TJDecompressor.java 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. /*
  2. * Copyright (C)2011-2015, 2018 D. R. Commander. All Rights Reserved.
  3. * Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * - Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. * - Redistributions in binary form must reproduce the above copyright notice,
  11. * this list of conditions and the following disclaimer in the documentation
  12. * and/or other materials provided with the distribution.
  13. * - Neither the name of the libjpeg-turbo Project nor the names of its
  14. * contributors may be used to endorse or promote products derived from this
  15. * software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  21. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. * POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. package org.libjpegturbo.turbojpeg;
  30. import java.awt.image.*;
  31. import java.nio.*;
  32. import java.io.*;
  33. /**
  34. * TurboJPEG decompressor
  35. */
  36. public class TJDecompressor implements Closeable {
  37. private static final String NO_ASSOC_ERROR =
  38. "No JPEG image is associated with this instance";
  39. /**
  40. * Create a TurboJPEG decompresssor instance.
  41. */
  42. public TJDecompressor() throws TJException {
  43. init();
  44. }
  45. /**
  46. * Create a TurboJPEG decompressor instance and associate the JPEG source
  47. * image stored in <code>jpegImage</code> with the newly created instance.
  48. *
  49. * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to
  50. * be the length of the array.) This buffer is not modified.
  51. */
  52. public TJDecompressor(byte[] jpegImage) throws TJException {
  53. init();
  54. setSourceImage(jpegImage, jpegImage.length);
  55. }
  56. /**
  57. * Create a TurboJPEG decompressor instance and associate the JPEG source
  58. * image of length <code>imageSize</code> bytes stored in
  59. * <code>jpegImage</code> with the newly created instance.
  60. *
  61. * @param jpegImage JPEG image buffer. This buffer is not modified.
  62. *
  63. * @param imageSize size of the JPEG image (in bytes)
  64. */
  65. public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException {
  66. init();
  67. setSourceImage(jpegImage, imageSize);
  68. }
  69. /**
  70. * Create a TurboJPEG decompressor instance and associate the YUV planar
  71. * source image stored in <code>yuvImage</code> with the newly created
  72. * instance.
  73. *
  74. * @param yuvImage {@link YUVImage} instance containing a YUV planar
  75. * image to be decoded. This image is not modified.
  76. */
  77. @SuppressWarnings("checkstyle:HiddenField")
  78. public TJDecompressor(YUVImage yuvImage) throws TJException {
  79. init();
  80. setSourceImage(yuvImage);
  81. }
  82. /**
  83. * Associate the JPEG image of length <code>imageSize</code> bytes stored in
  84. * <code>jpegImage</code> with this decompressor instance. This image will
  85. * be used as the source image for subsequent decompress operations.
  86. *
  87. * @param jpegImage JPEG image buffer. This buffer is not modified.
  88. *
  89. * @param imageSize size of the JPEG image (in bytes)
  90. */
  91. public void setSourceImage(byte[] jpegImage, int imageSize)
  92. throws TJException {
  93. if (jpegImage == null || imageSize < 1)
  94. throw new IllegalArgumentException("Invalid argument in setSourceImage()");
  95. jpegBuf = jpegImage;
  96. jpegBufSize = imageSize;
  97. decompressHeader(jpegBuf, jpegBufSize);
  98. yuvImage = null;
  99. }
  100. /**
  101. * @deprecated Use {@link #setSourceImage(byte[], int)} instead.
  102. */
  103. @SuppressWarnings("checkstyle:JavadocMethod")
  104. @Deprecated
  105. public void setJPEGImage(byte[] jpegImage, int imageSize)
  106. throws TJException {
  107. setSourceImage(jpegImage, imageSize);
  108. }
  109. /**
  110. * Associate the specified YUV planar source image with this decompressor
  111. * instance. Subsequent decompress operations will decode this image into an
  112. * RGB or grayscale destination image.
  113. *
  114. * @param srcImage {@link YUVImage} instance containing a YUV planar image to
  115. * be decoded. This image is not modified.
  116. */
  117. public void setSourceImage(YUVImage srcImage) {
  118. if (srcImage == null)
  119. throw new IllegalArgumentException("Invalid argument in setSourceImage()");
  120. yuvImage = srcImage;
  121. jpegBuf = null;
  122. jpegBufSize = 0;
  123. }
  124. /**
  125. * Returns the width of the source image (JPEG or YUV) associated with this
  126. * decompressor instance.
  127. *
  128. * @return the width of the source image (JPEG or YUV) associated with this
  129. * decompressor instance.
  130. */
  131. public int getWidth() {
  132. if (yuvImage != null)
  133. return yuvImage.getWidth();
  134. if (jpegWidth < 1)
  135. throw new IllegalStateException(NO_ASSOC_ERROR);
  136. return jpegWidth;
  137. }
  138. /**
  139. * Returns the height of the source image (JPEG or YUV) associated with this
  140. * decompressor instance.
  141. *
  142. * @return the height of the source image (JPEG or YUV) associated with this
  143. * decompressor instance.
  144. */
  145. public int getHeight() {
  146. if (yuvImage != null)
  147. return yuvImage.getHeight();
  148. if (jpegHeight < 1)
  149. throw new IllegalStateException(NO_ASSOC_ERROR);
  150. return jpegHeight;
  151. }
  152. /**
  153. * Returns the level of chrominance subsampling used in the source image
  154. * (JPEG or YUV) associated with this decompressor instance. See
  155. * {@link TJ#SAMP_444 TJ.SAMP_*}.
  156. *
  157. * @return the level of chrominance subsampling used in the source image
  158. * (JPEG or YUV) associated with this decompressor instance.
  159. */
  160. public int getSubsamp() {
  161. if (yuvImage != null)
  162. return yuvImage.getSubsamp();
  163. if (jpegSubsamp < 0)
  164. throw new IllegalStateException(NO_ASSOC_ERROR);
  165. if (jpegSubsamp >= TJ.NUMSAMP)
  166. throw new IllegalStateException("JPEG header information is invalid");
  167. return jpegSubsamp;
  168. }
  169. /**
  170. * Returns the colorspace used in the source image (JPEG or YUV) associated
  171. * with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the
  172. * source image is YUV, then this always returns {@link TJ#CS_YCbCr}.
  173. *
  174. * @return the colorspace used in the source image (JPEG or YUV) associated
  175. * with this decompressor instance.
  176. */
  177. public int getColorspace() {
  178. if (yuvImage != null)
  179. return TJ.CS_YCbCr;
  180. if (jpegColorspace < 0)
  181. throw new IllegalStateException(NO_ASSOC_ERROR);
  182. if (jpegColorspace >= TJ.NUMCS)
  183. throw new IllegalStateException("JPEG header information is invalid");
  184. return jpegColorspace;
  185. }
  186. /**
  187. * Returns the JPEG image buffer associated with this decompressor instance.
  188. *
  189. * @return the JPEG image buffer associated with this decompressor instance.
  190. */
  191. public byte[] getJPEGBuf() {
  192. if (jpegBuf == null)
  193. throw new IllegalStateException(NO_ASSOC_ERROR);
  194. return jpegBuf;
  195. }
  196. /**
  197. * Returns the size of the JPEG image (in bytes) associated with this
  198. * decompressor instance.
  199. *
  200. * @return the size of the JPEG image (in bytes) associated with this
  201. * decompressor instance.
  202. */
  203. public int getJPEGSize() {
  204. if (jpegBufSize < 1)
  205. throw new IllegalStateException(NO_ASSOC_ERROR);
  206. return jpegBufSize;
  207. }
  208. /**
  209. * Returns the width of the largest scaled-down image that the TurboJPEG
  210. * decompressor can generate without exceeding the desired image width and
  211. * height.
  212. *
  213. * @param desiredWidth desired width (in pixels) of the decompressed image.
  214. * Setting this to 0 is the same as setting it to the width of the JPEG image
  215. * (in other words, the width will not be considered when determining the
  216. * scaled image size.)
  217. *
  218. * @param desiredHeight desired height (in pixels) of the decompressed image.
  219. * Setting this to 0 is the same as setting it to the height of the JPEG
  220. * image (in other words, the height will not be considered when determining
  221. * the scaled image size.)
  222. *
  223. * @return the width of the largest scaled-down image that the TurboJPEG
  224. * decompressor can generate without exceeding the desired image width and
  225. * height.
  226. */
  227. public int getScaledWidth(int desiredWidth, int desiredHeight) {
  228. if (jpegWidth < 1 || jpegHeight < 1)
  229. throw new IllegalStateException(NO_ASSOC_ERROR);
  230. if (desiredWidth < 0 || desiredHeight < 0)
  231. throw new IllegalArgumentException("Invalid argument in getScaledWidth()");
  232. TJScalingFactor[] sf = TJ.getScalingFactors();
  233. if (desiredWidth == 0)
  234. desiredWidth = jpegWidth;
  235. if (desiredHeight == 0)
  236. desiredHeight = jpegHeight;
  237. int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
  238. for (int i = 0; i < sf.length; i++) {
  239. scaledWidth = sf[i].getScaled(jpegWidth);
  240. scaledHeight = sf[i].getScaled(jpegHeight);
  241. if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
  242. break;
  243. }
  244. if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
  245. throw new IllegalArgumentException("Could not scale down to desired image dimensions");
  246. return scaledWidth;
  247. }
  248. /**
  249. * Returns the height of the largest scaled-down image that the TurboJPEG
  250. * decompressor can generate without exceeding the desired image width and
  251. * height.
  252. *
  253. * @param desiredWidth desired width (in pixels) of the decompressed image.
  254. * Setting this to 0 is the same as setting it to the width of the JPEG image
  255. * (in other words, the width will not be considered when determining the
  256. * scaled image size.)
  257. *
  258. * @param desiredHeight desired height (in pixels) of the decompressed image.
  259. * Setting this to 0 is the same as setting it to the height of the JPEG
  260. * image (in other words, the height will not be considered when determining
  261. * the scaled image size.)
  262. *
  263. * @return the height of the largest scaled-down image that the TurboJPEG
  264. * decompressor can generate without exceeding the desired image width and
  265. * height.
  266. */
  267. public int getScaledHeight(int desiredWidth, int desiredHeight) {
  268. if (jpegWidth < 1 || jpegHeight < 1)
  269. throw new IllegalStateException(NO_ASSOC_ERROR);
  270. if (desiredWidth < 0 || desiredHeight < 0)
  271. throw new IllegalArgumentException("Invalid argument in getScaledHeight()");
  272. TJScalingFactor[] sf = TJ.getScalingFactors();
  273. if (desiredWidth == 0)
  274. desiredWidth = jpegWidth;
  275. if (desiredHeight == 0)
  276. desiredHeight = jpegHeight;
  277. int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
  278. for (int i = 0; i < sf.length; i++) {
  279. scaledWidth = sf[i].getScaled(jpegWidth);
  280. scaledHeight = sf[i].getScaled(jpegHeight);
  281. if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
  282. break;
  283. }
  284. if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
  285. throw new IllegalArgumentException("Could not scale down to desired image dimensions");
  286. return scaledHeight;
  287. }
  288. /**
  289. * Decompress the JPEG source image or decode the YUV source image associated
  290. * with this decompressor instance and output a grayscale, RGB, or CMYK image
  291. * to the given destination buffer.
  292. * <p>
  293. * NOTE: The output image is fully recoverable if this method throws a
  294. * non-fatal {@link TJException} (unless
  295. * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
  296. *
  297. * @param dstBuf buffer that will receive the decompressed/decoded image.
  298. * If the source image is a JPEG image, then this buffer should normally be
  299. * <code>pitch * scaledHeight</code> bytes in size, where
  300. * <code>scaledHeight</code> can be determined by calling <code>
  301. * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight)
  302. * </code> with one of the scaling factors returned from {@link
  303. * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the
  304. * source image is a YUV image, then this buffer should normally be
  305. * <code>pitch * height</code> bytes in size, where <code>height</code> is
  306. * the height of the YUV image. However, the buffer may also be larger than
  307. * the dimensions of the source image, in which case the <code>x</code>,
  308. * <code>y</code>, and <code>pitch</code> parameters can be used to specify
  309. * the region into which the source image should be decompressed/decoded.
  310. *
  311. * @param x x offset (in pixels) of the region in the destination image into
  312. * which the source image should be decompressed/decoded
  313. *
  314. * @param y y offset (in pixels) of the region in the destination image into
  315. * which the source image should be decompressed/decoded
  316. *
  317. * @param desiredWidth If the source image is a JPEG image, then this
  318. * specifies the desired width (in pixels) of the decompressed image (or
  319. * image region.) If the desired destination image dimensions are different
  320. * than the source image dimensions, then TurboJPEG will use scaling in the
  321. * JPEG decompressor to generate the largest possible image that will fit
  322. * within the desired dimensions. Setting this to 0 is the same as setting
  323. * it to the width of the JPEG image (in other words, the width will not be
  324. * considered when determining the scaled image size.) This parameter is
  325. * ignored if the source image is a YUV image.
  326. *
  327. * @param pitch bytes per line of the destination image. Normally, this
  328. * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if
  329. * the destination image is unpadded, but you can use this to, for instance,
  330. * pad each line of the destination image to a 4-byte boundary or to
  331. * decompress/decode the source image into a region of a larger image. NOTE:
  332. * if the source image is a JPEG image, then <code>scaledWidth</code> can be
  333. * determined by calling <code>
  334. * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
  335. * </code> or by calling {@link #getScaledWidth}. If the source image is a
  336. * YUV image, then <code>scaledWidth</code> is the width of the YUV image.
  337. * Setting this parameter to 0 is the equivalent of setting it to
  338. * <code>scaledWidth * TJ.pixelSize(pixelFormat)</code>.
  339. *
  340. * @param desiredHeight If the source image is a JPEG image, then this
  341. * specifies the desired height (in pixels) of the decompressed image (or
  342. * image region.) If the desired destination image dimensions are different
  343. * than the source image dimensions, then TurboJPEG will use scaling in the
  344. * JPEG decompressor to generate the largest possible image that will fit
  345. * within the desired dimensions. Setting this to 0 is the same as setting
  346. * it to the height of the JPEG image (in other words, the height will not be
  347. * considered when determining the scaled image size.) This parameter is
  348. * ignored if the source image is a YUV image.
  349. *
  350. * @param pixelFormat pixel format of the decompressed/decoded image (one of
  351. * {@link TJ#PF_RGB TJ.PF_*})
  352. *
  353. * @param flags the bitwise OR of one or more of
  354. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  355. */
  356. public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
  357. int pitch, int desiredHeight, int pixelFormat,
  358. int flags) throws TJException {
  359. if (jpegBuf == null && yuvImage == null)
  360. throw new IllegalStateException(NO_ASSOC_ERROR);
  361. if (dstBuf == null || x < 0 || y < 0 || pitch < 0 ||
  362. (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
  363. pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
  364. throw new IllegalArgumentException("Invalid argument in decompress()");
  365. if (yuvImage != null)
  366. decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
  367. yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
  368. yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat,
  369. flags);
  370. else {
  371. if (x > 0 || y > 0)
  372. decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch,
  373. desiredHeight, pixelFormat, flags);
  374. else
  375. decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch,
  376. desiredHeight, pixelFormat, flags);
  377. }
  378. }
  379. /**
  380. * @deprecated Use
  381. * {@link #decompress(byte[], int, int, int, int, int, int, int)} instead.
  382. */
  383. @SuppressWarnings("checkstyle:JavadocMethod")
  384. @Deprecated
  385. public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
  386. int desiredHeight, int pixelFormat, int flags)
  387. throws TJException {
  388. decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat,
  389. flags);
  390. }
  391. /**
  392. * Decompress the JPEG source image associated with this decompressor
  393. * instance and return a buffer containing the decompressed image.
  394. *
  395. * @param desiredWidth see
  396. * {@link #decompress(byte[], int, int, int, int, int, int, int)}
  397. * for description
  398. *
  399. * @param pitch see
  400. * {@link #decompress(byte[], int, int, int, int, int, int, int)}
  401. * for description
  402. *
  403. * @param desiredHeight see
  404. * {@link #decompress(byte[], int, int, int, int, int, int, int)}
  405. * for description
  406. *
  407. * @param pixelFormat pixel format of the decompressed image (one of
  408. * {@link TJ#PF_RGB TJ.PF_*})
  409. *
  410. * @param flags the bitwise OR of one or more of
  411. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  412. *
  413. * @return a buffer containing the decompressed image.
  414. */
  415. public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
  416. int pixelFormat, int flags) throws TJException {
  417. if (pitch < 0 ||
  418. (yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
  419. pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
  420. throw new IllegalArgumentException("Invalid argument in decompress()");
  421. int pixelSize = TJ.getPixelSize(pixelFormat);
  422. int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
  423. int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
  424. if (pitch == 0)
  425. pitch = scaledWidth * pixelSize;
  426. byte[] buf = new byte[pitch * scaledHeight];
  427. decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags);
  428. return buf;
  429. }
  430. /**
  431. * Decompress the JPEG source image associated with this decompressor
  432. * instance into a YUV planar image and store it in the given
  433. * <code>YUVImage</code> instance. This method performs JPEG decompression
  434. * but leaves out the color conversion step, so a planar YUV image is
  435. * generated instead of an RGB or grayscale image. This method cannot be
  436. * used to decompress JPEG source images with the CMYK or YCCK colorspace.
  437. * <p>
  438. * NOTE: The YUV planar output image is fully recoverable if this method
  439. * throws a non-fatal {@link TJException} (unless
  440. * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
  441. *
  442. * @param dstImage {@link YUVImage} instance that will receive the YUV planar
  443. * image. The level of subsampling specified in this <code>YUVImage</code>
  444. * instance must match that of the JPEG image, and the width and height
  445. * specified in the <code>YUVImage</code> instance must match one of the
  446. * scaled image sizes that TurboJPEG is capable of generating from the JPEG
  447. * source image.
  448. *
  449. * @param flags the bitwise OR of one or more of
  450. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  451. */
  452. public void decompressToYUV(YUVImage dstImage, int flags)
  453. throws TJException {
  454. if (jpegBuf == null)
  455. throw new IllegalStateException(NO_ASSOC_ERROR);
  456. if (dstImage == null || flags < 0)
  457. throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
  458. int scaledWidth = getScaledWidth(dstImage.getWidth(),
  459. dstImage.getHeight());
  460. int scaledHeight = getScaledHeight(dstImage.getWidth(),
  461. dstImage.getHeight());
  462. if (scaledWidth != dstImage.getWidth() ||
  463. scaledHeight != dstImage.getHeight())
  464. throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
  465. if (jpegSubsamp != dstImage.getSubsamp())
  466. throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image");
  467. decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(),
  468. dstImage.getOffsets(), dstImage.getWidth(),
  469. dstImage.getStrides(), dstImage.getHeight(), flags);
  470. }
  471. /**
  472. * @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead.
  473. */
  474. @SuppressWarnings("checkstyle:JavadocMethod")
  475. @Deprecated
  476. public void decompressToYUV(byte[] dstBuf, int flags) throws TJException {
  477. YUVImage dstYUVImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight,
  478. jpegSubsamp);
  479. decompressToYUV(dstYUVImage, flags);
  480. }
  481. /**
  482. * Decompress the JPEG source image associated with this decompressor
  483. * instance into a set of Y, U (Cb), and V (Cr) image planes and return a
  484. * <code>YUVImage</code> instance containing the decompressed image planes.
  485. * This method performs JPEG decompression but leaves out the color
  486. * conversion step, so a planar YUV image is generated instead of an RGB or
  487. * grayscale image. This method cannot be used to decompress JPEG source
  488. * images with the CMYK or YCCK colorspace.
  489. *
  490. * @param desiredWidth desired width (in pixels) of the YUV image. If the
  491. * desired image dimensions are different than the dimensions of the JPEG
  492. * image being decompressed, then TurboJPEG will use scaling in the JPEG
  493. * decompressor to generate the largest possible image that will fit within
  494. * the desired dimensions. Setting this to 0 is the same as setting it to
  495. * the width of the JPEG image (in other words, the width will not be
  496. * considered when determining the scaled image size.)
  497. *
  498. * @param strides an array of integers, each specifying the number of bytes
  499. * per line in the corresponding plane of the output image. Setting the
  500. * stride for any plane to 0 is the same as setting it to the scaled
  501. * component width of the plane. If <tt>strides</tt> is NULL, then the
  502. * strides for all planes will be set to their respective scaled component
  503. * widths. You can adjust the strides in order to add an arbitrary amount of
  504. * line padding to each plane.
  505. *
  506. * @param desiredHeight desired height (in pixels) of the YUV image. If the
  507. * desired image dimensions are different than the dimensions of the JPEG
  508. * image being decompressed, then TurboJPEG will use scaling in the JPEG
  509. * decompressor to generate the largest possible image that will fit within
  510. * the desired dimensions. Setting this to 0 is the same as setting it to
  511. * the height of the JPEG image (in other words, the height will not be
  512. * considered when determining the scaled image size.)
  513. *
  514. * @param flags the bitwise OR of one or more of
  515. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  516. *
  517. * @return a YUV planar image.
  518. */
  519. public YUVImage decompressToYUV(int desiredWidth, int[] strides,
  520. int desiredHeight,
  521. int flags) throws TJException {
  522. if (flags < 0)
  523. throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
  524. if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
  525. throw new IllegalStateException(NO_ASSOC_ERROR);
  526. if (jpegSubsamp >= TJ.NUMSAMP)
  527. throw new IllegalStateException("JPEG header information is invalid");
  528. if (yuvImage != null)
  529. throw new IllegalStateException("Source image is the wrong type");
  530. int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
  531. int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
  532. YUVImage dstYUVImage = new YUVImage(scaledWidth, null, scaledHeight,
  533. jpegSubsamp);
  534. decompressToYUV(dstYUVImage, flags);
  535. return dstYUVImage;
  536. }
  537. /**
  538. * Decompress the JPEG source image associated with this decompressor
  539. * instance into a unified YUV planar image buffer and return a
  540. * <code>YUVImage</code> instance containing the decompressed image. This
  541. * method performs JPEG decompression but leaves out the color conversion
  542. * step, so a planar YUV image is generated instead of an RGB or grayscale
  543. * image. This method cannot be used to decompress JPEG source images with
  544. * the CMYK or YCCK colorspace.
  545. *
  546. * @param desiredWidth desired width (in pixels) of the YUV image. If the
  547. * desired image dimensions are different than the dimensions of the JPEG
  548. * image being decompressed, then TurboJPEG will use scaling in the JPEG
  549. * decompressor to generate the largest possible image that will fit within
  550. * the desired dimensions. Setting this to 0 is the same as setting it to
  551. * the width of the JPEG image (in other words, the width will not be
  552. * considered when determining the scaled image size.)
  553. *
  554. * @param pad the width of each line in each plane of the YUV image will be
  555. * padded to the nearest multiple of this number of bytes (must be a power of
  556. * 2.)
  557. *
  558. * @param desiredHeight desired height (in pixels) of the YUV image. If the
  559. * desired image dimensions are different than the dimensions of the JPEG
  560. * image being decompressed, then TurboJPEG will use scaling in the JPEG
  561. * decompressor to generate the largest possible image that will fit within
  562. * the desired dimensions. Setting this to 0 is the same as setting it to
  563. * the height of the JPEG image (in other words, the height will not be
  564. * considered when determining the scaled image size.)
  565. *
  566. * @param flags the bitwise OR of one or more of
  567. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  568. *
  569. * @return a YUV planar image.
  570. */
  571. public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight,
  572. int flags) throws TJException {
  573. if (flags < 0)
  574. throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
  575. if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
  576. throw new IllegalStateException(NO_ASSOC_ERROR);
  577. if (jpegSubsamp >= TJ.NUMSAMP)
  578. throw new IllegalStateException("JPEG header information is invalid");
  579. if (yuvImage != null)
  580. throw new IllegalStateException("Source image is the wrong type");
  581. int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
  582. int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
  583. YUVImage dstYUVImage = new YUVImage(scaledWidth, pad, scaledHeight,
  584. jpegSubsamp);
  585. decompressToYUV(dstYUVImage, flags);
  586. return dstYUVImage;
  587. }
  588. /**
  589. * @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead.
  590. */
  591. @SuppressWarnings("checkstyle:JavadocMethod")
  592. @Deprecated
  593. public byte[] decompressToYUV(int flags) throws TJException {
  594. YUVImage dstYUVImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp);
  595. decompressToYUV(dstYUVImage, flags);
  596. return dstYUVImage.getBuf();
  597. }
  598. /**
  599. * Decompress the JPEG source image or decode the YUV source image associated
  600. * with this decompressor instance and output a grayscale, RGB, or CMYK image
  601. * to the given destination buffer.
  602. * <p>
  603. * NOTE: The output image is fully recoverable if this method throws a
  604. * non-fatal {@link TJException} (unless
  605. * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
  606. *
  607. * @param dstBuf buffer that will receive the decompressed/decoded image.
  608. * If the source image is a JPEG image, then this buffer should normally be
  609. * <code>stride * scaledHeight</code> pixels in size, where
  610. * <code>scaledHeight</code> can be determined by calling <code>
  611. * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight)
  612. * </code> with one of the scaling factors returned from {@link
  613. * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the
  614. * source image is a YUV image, then this buffer should normally be
  615. * <code>stride * height</code> pixels in size, where <code>height</code> is
  616. * the height of the YUV image. However, the buffer may also be larger than
  617. * the dimensions of the JPEG image, in which case the <code>x</code>,
  618. * <code>y</code>, and <code>stride</code> parameters can be used to specify
  619. * the region into which the source image should be decompressed.
  620. *
  621. * @param x x offset (in pixels) of the region in the destination image into
  622. * which the source image should be decompressed/decoded
  623. *
  624. * @param y y offset (in pixels) of the region in the destination image into
  625. * which the source image should be decompressed/decoded
  626. *
  627. * @param desiredWidth If the source image is a JPEG image, then this
  628. * specifies the desired width (in pixels) of the decompressed image (or
  629. * image region.) If the desired destination image dimensions are different
  630. * than the source image dimensions, then TurboJPEG will use scaling in the
  631. * JPEG decompressor to generate the largest possible image that will fit
  632. * within the desired dimensions. Setting this to 0 is the same as setting
  633. * it to the width of the JPEG image (in other words, the width will not be
  634. * considered when determining the scaled image size.) This parameter is
  635. * ignored if the source image is a YUV image.
  636. *
  637. * @param stride pixels per line of the destination image. Normally, this
  638. * should be set to <code>scaledWidth</code>, but you can use this to, for
  639. * instance, decompress the JPEG image into a region of a larger image.
  640. * NOTE: if the source image is a JPEG image, then <code>scaledWidth</code>
  641. * can be determined by calling <code>
  642. * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
  643. * </code> or by calling {@link #getScaledWidth}. If the source image is a
  644. * YUV image, then <code>scaledWidth</code> is the width of the YUV image.
  645. * Setting this parameter to 0 is the equivalent of setting it to
  646. * <code>scaledWidth</code>.
  647. *
  648. * @param desiredHeight If the source image is a JPEG image, then this
  649. * specifies the desired height (in pixels) of the decompressed image (or
  650. * image region.) If the desired destination image dimensions are different
  651. * than the source image dimensions, then TurboJPEG will use scaling in the
  652. * JPEG decompressor to generate the largest possible image that will fit
  653. * within the desired dimensions. Setting this to 0 is the same as setting
  654. * it to the height of the JPEG image (in other words, the height will not be
  655. * considered when determining the scaled image size.) This parameter is
  656. * ignored if the source image is a YUV image.
  657. *
  658. * @param pixelFormat pixel format of the decompressed image (one of
  659. * {@link TJ#PF_RGB TJ.PF_*})
  660. *
  661. * @param flags the bitwise OR of one or more of
  662. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  663. */
  664. public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
  665. int stride, int desiredHeight, int pixelFormat,
  666. int flags) throws TJException {
  667. if (jpegBuf == null && yuvImage == null)
  668. throw new IllegalStateException(NO_ASSOC_ERROR);
  669. if (dstBuf == null || x < 0 || y < 0 || stride < 0 ||
  670. (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
  671. pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
  672. throw new IllegalArgumentException("Invalid argument in decompress()");
  673. if (yuvImage != null)
  674. decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
  675. yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
  676. yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat,
  677. flags);
  678. else
  679. decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride,
  680. desiredHeight, pixelFormat, flags);
  681. }
  682. /**
  683. * Decompress the JPEG source image or decode the YUV source image associated
  684. * with this decompressor instance and output a decompressed/decoded image to
  685. * the given <code>BufferedImage</code> instance.
  686. * <p>
  687. * NOTE: The output image is fully recoverable if this method throws a
  688. * non-fatal {@link TJException} (unless
  689. * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
  690. *
  691. * @param dstImage a <code>BufferedImage</code> instance that will receive
  692. * the decompressed/decoded image. If the source image is a JPEG image, then
  693. * the width and height of the <code>BufferedImage</code> instance must match
  694. * one of the scaled image sizes that TurboJPEG is capable of generating from
  695. * the JPEG image. If the source image is a YUV image, then the width and
  696. * height of the <code>BufferedImage</code> instance must match the width and
  697. * height of the YUV image.
  698. *
  699. * @param flags the bitwise OR of one or more of
  700. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  701. */
  702. public void decompress(BufferedImage dstImage, int flags)
  703. throws TJException {
  704. if (dstImage == null || flags < 0)
  705. throw new IllegalArgumentException("Invalid argument in decompress()");
  706. int desiredWidth = dstImage.getWidth();
  707. int desiredHeight = dstImage.getHeight();
  708. int scaledWidth, scaledHeight;
  709. if (yuvImage != null) {
  710. if (desiredWidth != yuvImage.getWidth() ||
  711. desiredHeight != yuvImage.getHeight())
  712. throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image.");
  713. scaledWidth = yuvImage.getWidth();
  714. scaledHeight = yuvImage.getHeight();
  715. } else {
  716. scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
  717. scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
  718. if (scaledWidth != desiredWidth || scaledHeight != desiredHeight)
  719. throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
  720. }
  721. int pixelFormat; boolean intPixels = false;
  722. if (byteOrder == null)
  723. byteOrder = ByteOrder.nativeOrder();
  724. switch (dstImage.getType()) {
  725. case BufferedImage.TYPE_3BYTE_BGR:
  726. pixelFormat = TJ.PF_BGR; break;
  727. case BufferedImage.TYPE_4BYTE_ABGR:
  728. case BufferedImage.TYPE_4BYTE_ABGR_PRE:
  729. pixelFormat = TJ.PF_XBGR; break;
  730. case BufferedImage.TYPE_BYTE_GRAY:
  731. pixelFormat = TJ.PF_GRAY; break;
  732. case BufferedImage.TYPE_INT_BGR:
  733. if (byteOrder == ByteOrder.BIG_ENDIAN)
  734. pixelFormat = TJ.PF_XBGR;
  735. else
  736. pixelFormat = TJ.PF_RGBX;
  737. intPixels = true; break;
  738. case BufferedImage.TYPE_INT_RGB:
  739. if (byteOrder == ByteOrder.BIG_ENDIAN)
  740. pixelFormat = TJ.PF_XRGB;
  741. else
  742. pixelFormat = TJ.PF_BGRX;
  743. intPixels = true; break;
  744. case BufferedImage.TYPE_INT_ARGB:
  745. case BufferedImage.TYPE_INT_ARGB_PRE:
  746. if (byteOrder == ByteOrder.BIG_ENDIAN)
  747. pixelFormat = TJ.PF_ARGB;
  748. else
  749. pixelFormat = TJ.PF_BGRA;
  750. intPixels = true; break;
  751. default:
  752. throw new IllegalArgumentException("Unsupported BufferedImage format");
  753. }
  754. WritableRaster wr = dstImage.getRaster();
  755. if (intPixels) {
  756. SinglePixelPackedSampleModel sm =
  757. (SinglePixelPackedSampleModel)dstImage.getSampleModel();
  758. int stride = sm.getScanlineStride();
  759. DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
  760. int[] buf = db.getData();
  761. if (yuvImage != null)
  762. decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
  763. yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0,
  764. yuvImage.getWidth(), stride, yuvImage.getHeight(),
  765. pixelFormat, flags);
  766. else {
  767. if (jpegBuf == null)
  768. throw new IllegalStateException(NO_ASSOC_ERROR);
  769. decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride,
  770. scaledHeight, pixelFormat, flags);
  771. }
  772. } else {
  773. ComponentSampleModel sm =
  774. (ComponentSampleModel)dstImage.getSampleModel();
  775. int pixelSize = sm.getPixelStride();
  776. if (pixelSize != TJ.getPixelSize(pixelFormat))
  777. throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
  778. int pitch = sm.getScanlineStride();
  779. DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
  780. byte[] buf = db.getData();
  781. decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat,
  782. flags);
  783. }
  784. }
  785. /**
  786. * Decompress the JPEG source image or decode the YUV source image associated
  787. * with this decompressor instance and return a <code>BufferedImage</code>
  788. * instance containing the decompressed/decoded image.
  789. *
  790. * @param desiredWidth see
  791. * {@link #decompress(byte[], int, int, int, int, int, int, int)} for
  792. * description
  793. *
  794. * @param desiredHeight see
  795. * {@link #decompress(byte[], int, int, int, int, int, int, int)} for
  796. * description
  797. *
  798. * @param bufferedImageType the image type of the <code>BufferedImage</code>
  799. * instance that will be created (for instance,
  800. * <code>BufferedImage.TYPE_INT_RGB</code>)
  801. *
  802. * @param flags the bitwise OR of one or more of
  803. * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
  804. *
  805. * @return a <code>BufferedImage</code> instance containing the
  806. * decompressed/decoded image.
  807. */
  808. public BufferedImage decompress(int desiredWidth, int desiredHeight,
  809. int bufferedImageType, int flags)
  810. throws TJException {
  811. if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
  812. flags < 0)
  813. throw new IllegalArgumentException("Invalid argument in decompress()");
  814. int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
  815. int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
  816. BufferedImage img = new BufferedImage(scaledWidth, scaledHeight,
  817. bufferedImageType);
  818. decompress(img, flags);
  819. return img;
  820. }
  821. /**
  822. * Free the native structures associated with this decompressor instance.
  823. */
  824. @Override
  825. public void close() throws TJException {
  826. if (handle != 0)
  827. destroy();
  828. }
  829. @SuppressWarnings("checkstyle:DesignForExtension")
  830. @Override
  831. protected void finalize() throws Throwable {
  832. try {
  833. close();
  834. } catch (TJException e) {
  835. } finally {
  836. super.finalize();
  837. }
  838. };
  839. private native void init() throws TJException;
  840. private native void destroy() throws TJException;
  841. private native void decompressHeader(byte[] srcBuf, int size)
  842. throws TJException;
  843. @Deprecated
  844. private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
  845. int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
  846. throws TJException;
  847. private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x,
  848. int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat,
  849. int flags) throws TJException;
  850. @Deprecated
  851. private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
  852. int desiredWidth, int stride, int desiredHeight, int pixelFormat,
  853. int flags) throws TJException;
  854. private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x,
  855. int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat,
  856. int flags) throws TJException;
  857. @Deprecated
  858. private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
  859. int flags) throws TJException;
  860. private native void decompressToYUV(byte[] srcBuf, int size,
  861. byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides,
  862. int desiredheight, int flags) throws TJException;
  863. private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
  864. int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width,
  865. int pitch, int height, int pixelFormat, int flags) throws TJException;
  866. private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
  867. int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width,
  868. int stride, int height, int pixelFormat, int flags) throws TJException;
  869. static {
  870. TJLoader.load();
  871. }
  872. protected long handle = 0;
  873. protected byte[] jpegBuf = null;
  874. protected int jpegBufSize = 0;
  875. protected YUVImage yuvImage = null;
  876. protected int jpegWidth = 0;
  877. protected int jpegHeight = 0;
  878. protected int jpegSubsamp = -1;
  879. protected int jpegColorspace = -1;
  880. private ByteOrder byteOrder = null;
  881. }