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