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