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