TJDecompressor.java revision 67ce3b2352fe1f7511edbfed74ec6960e41e97dc
1/* 2 * Copyright (C)2011 D. R. Commander. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * - Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * - Neither the name of the libjpeg-turbo Project nor the names of its 13 * contributors may be used to endorse or promote products derived from this 14 * software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.libjpegturbo.turbojpeg; 30 31import java.awt.image.*; 32import java.nio.*; 33 34/** 35 * TurboJPEG decompressor 36 */ 37public class TJDecompressor { 38 39 private final static String NO_ASSOC_ERROR = 40 "No JPEG image is associated with this instance"; 41 42 /** 43 * Create a TurboJPEG decompresssor instance. 44 */ 45 public TJDecompressor() throws Exception { 46 init(); 47 } 48 49 /** 50 * Create a TurboJPEG decompressor instance and associate the JPEG image 51 * stored in <code>jpegImage</code> with the newly-created instance. 52 * 53 * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to 54 * be the length of the array) 55 */ 56 public TJDecompressor(byte[] jpegImage) throws Exception { 57 init(); 58 setJPEGImage(jpegImage, jpegImage.length); 59 } 60 61 /** 62 * Create a TurboJPEG decompressor instance and associate the JPEG image 63 * of length <code>imageSize</code> bytes stored in <code>jpegImage</code> 64 * with the newly-created instance. 65 * 66 * @param jpegImage JPEG image buffer 67 * 68 * @param imageSize size of the JPEG image (in bytes) 69 */ 70 public TJDecompressor(byte[] jpegImage, int imageSize) throws Exception { 71 init(); 72 setJPEGImage(jpegImage, imageSize); 73 } 74 75 /** 76 * Associate the JPEG image of length <code>imageSize</code> bytes stored in 77 * <code>jpegImage</code> with this decompressor instance. This image will 78 * be used as the source image for subsequent decompress operations. 79 * 80 * @param jpegImage JPEG image buffer 81 * 82 * @param imageSize size of the JPEG image (in bytes) 83 */ 84 public void setJPEGImage(byte[] jpegImage, int imageSize) throws Exception { 85 if(jpegImage == null || imageSize < 1) 86 throw new Exception("Invalid argument in setJPEGImage()"); 87 jpegBuf = jpegImage; 88 jpegBufSize = imageSize; 89 decompressHeader(jpegBuf, jpegBufSize); 90 } 91 92 /** 93 * Returns the width of the JPEG image associated with this decompressor 94 * instance. 95 * 96 * @return the width of the JPEG image associated with this decompressor 97 * instance 98 */ 99 public int getWidth() throws Exception { 100 if(jpegWidth < 1) throw new Exception(NO_ASSOC_ERROR); 101 return jpegWidth; 102 } 103 104 /** 105 * Returns the height of the JPEG image associated with this decompressor 106 * instance. 107 * 108 * @return the height of the JPEG image associated with this decompressor 109 * instance 110 */ 111 public int getHeight() throws Exception { 112 if(jpegHeight < 1) throw new Exception(NO_ASSOC_ERROR); 113 return jpegHeight; 114 } 115 116 /** 117 * Returns the level of chrominance subsampling used in the JPEG image 118 * associated with this decompressor instance. 119 * 120 * @return the level of chrominance subsampling used in the JPEG image 121 * associated with this decompressor instance 122 */ 123 public int getSubsamp() throws Exception { 124 if(jpegSubsamp < 0) throw new Exception(NO_ASSOC_ERROR); 125 if(jpegSubsamp >= TJ.NUMSAMP) 126 throw new Exception("JPEG header information is invalid"); 127 return jpegSubsamp; 128 } 129 130 /** 131 * Returns the JPEG image buffer associated with this decompressor instance. 132 * 133 * @return the JPEG image buffer associated with this decompressor instance 134 */ 135 public byte[] getJPEGBuf() throws Exception { 136 if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); 137 return jpegBuf; 138 } 139 140 /** 141 * Returns the size of the JPEG image (in bytes) associated with this 142 * decompressor instance. 143 * 144 * @return the size of the JPEG image (in bytes) associated with this 145 * decompressor instance 146 */ 147 public int getJPEGSize() throws Exception { 148 if(jpegBufSize < 1) throw new Exception(NO_ASSOC_ERROR); 149 return jpegBufSize; 150 } 151 152 153 /** 154 * Returns the width of the largest scaled down image that the TurboJPEG 155 * decompressor can generate without exceeding the desired image width and 156 * height. 157 * 158 * @param desiredWidth desired width (in pixels) of the decompressed image. 159 * Setting this to 0 is the same as setting it to the width of the JPEG image 160 * (in other words, the width will not be considered when determining the 161 * scaled image size.) 162 * 163 * @param desiredHeight desired height (in pixels) of the decompressed image. 164 * Setting this to 0 is the same as setting it to the height of the JPEG 165 * image (in other words, the height will not be considered when determining 166 * the scaled image size.) 167 * 168 * @return the width of the largest scaled down image that the TurboJPEG 169 * decompressor can generate without exceeding the desired image width and 170 * height 171 */ 172 public int getScaledWidth(int desiredWidth, int desiredHeight) 173 throws Exception { 174 if(jpegWidth < 1 || jpegHeight < 1) 175 throw new Exception(NO_ASSOC_ERROR); 176 if(desiredWidth < 0 || desiredHeight < 0) 177 throw new Exception("Invalid argument in getScaledWidth()"); 178 TJScalingFactor sf[] = TJ.getScalingFactors(); 179 if(desiredWidth == 0) desiredWidth = jpegWidth; 180 if(desiredHeight == 0) desiredHeight = jpegHeight; 181 int scaledWidth = jpegWidth, scaledHeight = jpegHeight; 182 for(int i = 0; i < sf.length; i++) { 183 scaledWidth = sf[i].getScaled(jpegWidth); 184 scaledHeight = sf[i].getScaled(jpegHeight); 185 if(scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) 186 break; 187 } 188 if(scaledWidth > desiredWidth || scaledHeight > desiredHeight) 189 throw new Exception("Could not scale down to desired image dimensions"); 190 return scaledWidth; 191 } 192 193 /** 194 * Returns the height of the largest scaled down image that the TurboJPEG 195 * decompressor can generate without exceeding the desired image width and 196 * height. 197 * 198 * @param desiredWidth desired width (in pixels) of the decompressed image. 199 * Setting this to 0 is the same as setting it to the width of the JPEG image 200 * (in other words, the width will not be considered when determining the 201 * scaled image size.) 202 * 203 * @param desiredHeight desired height (in pixels) of the decompressed image. 204 * Setting this to 0 is the same as setting it to the height of the JPEG 205 * image (in other words, the height will not be considered when determining 206 * the scaled image size.) 207 * 208 * @return the height of the largest scaled down image that the TurboJPEG 209 * decompressor can generate without exceeding the desired image width and 210 * height 211 */ 212 public int getScaledHeight(int desiredWidth, int desiredHeight) 213 throws Exception { 214 if(jpegWidth < 1 || jpegHeight < 1) 215 throw new Exception(NO_ASSOC_ERROR); 216 if(desiredWidth < 0 || desiredHeight < 0) 217 throw new Exception("Invalid argument in getScaledHeight()"); 218 TJScalingFactor sf[] = TJ.getScalingFactors(); 219 if(desiredWidth == 0) desiredWidth = jpegWidth; 220 if(desiredHeight == 0) desiredHeight = jpegHeight; 221 int scaledWidth = jpegWidth, scaledHeight = jpegHeight; 222 for(int i = 0; i < sf.length; i++) { 223 scaledWidth = sf[i].getScaled(jpegWidth); 224 scaledHeight = sf[i].getScaled(jpegHeight); 225 if(scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) 226 break; 227 } 228 if(scaledWidth > desiredWidth || scaledHeight > desiredHeight) 229 throw new Exception("Could not scale down to desired image dimensions"); 230 return scaledHeight; 231 } 232 233 /** 234 * Decompress the JPEG source image associated with this decompressor 235 * instance and output a decompressed image to the given destination buffer. 236 * 237 * @param dstBuf buffer that will receive the decompressed image. This 238 * buffer should normally be <code>pitch * scaledHeight</code> bytes in size, 239 * where <code>scaledHeight</code> can be determined by calling <code> 240 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) 241 * </code> with one of the scaling factors returned from {@link 242 * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. 243 * 244 * @param desiredWidth desired width (in pixels) of the decompressed image. 245 * If the desired image dimensions are smaller than the dimensions of the 246 * JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG 247 * decompressor to generate the largest possible image that will fit within 248 * the desired dimensions. Setting this to 0 is the same as setting it to 249 * the width of the JPEG image (in other words, the width will not be 250 * considered when determining the scaled image size.) 251 * 252 * @param pitch bytes per line of the destination image. Normally, this 253 * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if 254 * the decompressed image is unpadded, but you can use this to, for instance, 255 * pad each line of the decompressed image to a 4-byte boundary. NOTE: 256 * <code>scaledWidth</code> can be determined by calling <code> 257 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) 258 * </code> or by calling {@link #getScaledWidth}. Setting this parameter to 259 * 0 is the equivalent of setting it to <code>scaledWidth * 260 * TJ.pixelSize(pixelFormat)</code>. 261 * 262 * @param desiredHeight desired height (in pixels) of the decompressed image. 263 * If the desired image dimensions are smaller than the dimensions of the 264 * JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG 265 * decompressor to generate the largest possible image that will fit within 266 * the desired dimensions. Setting this to 0 is the same as setting it to 267 * the height of the JPEG image (in other words, the height will not be 268 * considered when determining the scaled image size.) 269 * 270 * @param pixelFormat pixel format of the decompressed image (one of 271 * {@link TJ TJ.PF_*}) 272 * 273 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 274 */ 275 public void decompress(byte[] dstBuf, int desiredWidth, int pitch, 276 int desiredHeight, int pixelFormat, int flags) throws Exception { 277 if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); 278 if(dstBuf == null || desiredWidth < 0 || pitch < 0 || desiredHeight < 0 279 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) 280 throw new Exception("Invalid argument in decompress()"); 281 decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch, 282 desiredHeight, pixelFormat, flags); 283 } 284 285 /** 286 * Decompress the JPEG source image associated with this decompressor 287 * instance and return a buffer containing the decompressed image. 288 * 289 * @param desiredWidth see 290 * {@link #decompress(byte[], int, int, int, int, int)} for description 291 * 292 * @param pitch see 293 * {@link #decompress(byte[], int, int, int, int, int)} for description 294 * 295 * @param desiredHeight see 296 * {@link #decompress(byte[], int, int, int, int, int)} for description 297 * 298 * @param pixelFormat pixel format of the decompressed image (one of 299 * {@link TJ TJ.PF_*}) 300 * 301 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 302 * 303 * @return a buffer containing the decompressed image 304 */ 305 public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, 306 int pixelFormat, int flags) throws Exception { 307 if(desiredWidth < 0 || pitch < 0 || desiredHeight < 0 308 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) 309 throw new Exception("Invalid argument in decompress()"); 310 int pixelSize = TJ.getPixelSize(pixelFormat); 311 int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 312 int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 313 if(pitch == 0) pitch = scaledWidth * pixelSize; 314 byte[] buf = new byte[pitch * scaledHeight]; 315 decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags); 316 return buf; 317 } 318 319 /** 320 * Decompress the JPEG source image associated with this decompressor 321 * instance and output a YUV planar image to the given destination buffer. 322 * This method performs JPEG decompression but leaves out the color 323 * conversion step, so a planar YUV image is generated instead of an RGB 324 * image. The padding of the planes in this image is the same as the images 325 * generated by {@link TJCompressor#encodeYUV(byte[], int)}. Note that, if 326 * the width or height of the image is not an even multiple of the MCU block 327 * size (see {@link TJ#getMCUWidth} and {@link TJ#getMCUHeight}), then an 328 * intermediate buffer copy will be performed within TurboJPEG. 329 * 330 * @param dstBuf buffer that will receive the YUV planar image. Use 331 * {@link TJ#bufSizeYUV} to determine the appropriate size for this buffer 332 * based on the image width, height, and level of chrominance subsampling. 333 * 334 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 335 */ 336 public void decompressToYUV(byte[] dstBuf, int flags) throws Exception { 337 if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); 338 if(dstBuf == null || flags < 0) 339 throw new Exception("Invalid argument in decompressToYUV()"); 340 decompressToYUV(jpegBuf, jpegBufSize, dstBuf, flags); 341 } 342 343 344 /** 345 * Decompress the JPEG source image associated with this decompressor 346 * instance and return a buffer containing a YUV planar image. See {@link 347 * #decompressToYUV(byte[], int)} for more detail. 348 * 349 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 350 * 351 * @return a buffer containing a YUV planar image 352 */ 353 public byte[] decompressToYUV(int flags) throws Exception { 354 if(flags < 0) 355 throw new Exception("Invalid argument in decompressToYUV()"); 356 if(jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) 357 throw new Exception(NO_ASSOC_ERROR); 358 if(jpegSubsamp >= TJ.NUMSAMP) 359 throw new Exception("JPEG header information is invalid"); 360 byte[] buf = new byte[TJ.bufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp)]; 361 decompressToYUV(buf, flags); 362 return buf; 363 } 364 365 /** 366 * Decompress the JPEG source image associated with this decompressor 367 * instance and output a decompressed image to the given 368 * <code>BufferedImage</code> instance. 369 * 370 * @param dstImage a <code>BufferedImage</code> instance that will receive 371 * the decompressed image 372 * 373 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 374 */ 375 public void decompress(BufferedImage dstImage, int flags) throws Exception { 376 if(dstImage == null || flags < 0) 377 throw new Exception("Invalid argument in decompress()"); 378 int desiredWidth = dstImage.getWidth(); 379 int desiredHeight = dstImage.getHeight(); 380 int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 381 int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 382 if(scaledWidth != desiredWidth || scaledHeight != desiredHeight) 383 throw new Exception("BufferedImage dimensions do not match a scaled image size that TurboJPEG is capable of generating."); 384 int pixelFormat; boolean intPixels = false; 385 if(byteOrder == null) 386 byteOrder = ByteOrder.nativeOrder(); 387 switch(dstImage.getType()) { 388 case BufferedImage.TYPE_3BYTE_BGR: 389 pixelFormat = TJ.PF_BGR; break; 390 case BufferedImage.TYPE_4BYTE_ABGR: 391 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 392 pixelFormat = TJ.PF_XBGR; break; 393 case BufferedImage.TYPE_BYTE_GRAY: 394 pixelFormat = TJ.PF_GRAY; break; 395 case BufferedImage.TYPE_INT_BGR: 396 if(byteOrder == ByteOrder.BIG_ENDIAN) 397 pixelFormat = TJ.PF_XBGR; 398 else 399 pixelFormat = TJ.PF_RGBX; 400 intPixels = true; break; 401 case BufferedImage.TYPE_INT_RGB: 402 if(byteOrder == ByteOrder.BIG_ENDIAN) 403 pixelFormat = TJ.PF_XRGB; 404 else 405 pixelFormat = TJ.PF_BGRX; 406 intPixels = true; break; 407 case BufferedImage.TYPE_INT_ARGB: 408 case BufferedImage.TYPE_INT_ARGB_PRE: 409 if(byteOrder == ByteOrder.BIG_ENDIAN) 410 pixelFormat = TJ.PF_ARGB; 411 else 412 pixelFormat = TJ.PF_BGRA; 413 intPixels = true; break; 414 default: 415 throw new Exception("Unsupported BufferedImage format"); 416 } 417 WritableRaster wr = dstImage.getRaster(); 418 if(intPixels) { 419 SinglePixelPackedSampleModel sm = 420 (SinglePixelPackedSampleModel)dstImage.getSampleModel(); 421 int pitch = sm.getScanlineStride(); 422 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 423 int[] buf = db.getData(); 424 if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); 425 decompress(jpegBuf, jpegBufSize, buf, scaledWidth, pitch, scaledHeight, 426 pixelFormat, flags); 427 } 428 else { 429 ComponentSampleModel sm = 430 (ComponentSampleModel)dstImage.getSampleModel(); 431 int pixelSize = sm.getPixelStride(); 432 if(pixelSize != TJ.getPixelSize(pixelFormat)) 433 throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage"); 434 int pitch = sm.getScanlineStride(); 435 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 436 byte[] buf = db.getData(); 437 decompress(buf, scaledWidth, pitch, scaledHeight, pixelFormat, flags); 438 } 439 } 440 441 /** 442 * Decompress the JPEG source image associated with this decompressor 443 * instance and return a <code>BufferedImage</code> instance containing the 444 * decompressed image. 445 * 446 * @param desiredWidth see 447 * {@link #decompress(byte[], int, int, int, int, int)} for description 448 * 449 * @param desiredHeight see 450 * {@link #decompress(byte[], int, int, int, int, int)} for description 451 * 452 * @param bufferedImageType the image type of the newly-created 453 * <code>BufferedImage</code> instance (for instance, 454 * <code>BufferedImage.TYPE_INT_RGB</code>) 455 * 456 * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} 457 * 458 * @return a <code>BufferedImage</code> instance containing the 459 * decompressed image 460 */ 461 public BufferedImage decompress(int desiredWidth, int desiredHeight, 462 int bufferedImageType, int flags) throws Exception { 463 if(desiredWidth < 0 || desiredHeight < 0 || flags < 0) 464 throw new Exception("Invalid argument in decompress()"); 465 int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 466 int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 467 BufferedImage img = new BufferedImage(scaledWidth, scaledHeight, 468 bufferedImageType); 469 decompress(img, flags); 470 return img; 471 } 472 473 /** 474 * Free the native structures associated with this decompressor instance. 475 */ 476 public void close() throws Exception { 477 destroy(); 478 } 479 480 protected void finalize() throws Throwable { 481 try { 482 close(); 483 } 484 catch(Exception e) {} 485 finally { 486 super.finalize(); 487 } 488 }; 489 490 private native void init() throws Exception; 491 492 private native void destroy() throws Exception; 493 494 private native void decompressHeader(byte[] srcBuf, int size) 495 throws Exception; 496 497 private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, 498 int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) 499 throws Exception; 500 501 private native void decompress(byte[] srcBuf, int size, int[] dstBuf, 502 int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) 503 throws Exception; 504 505 private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, 506 int flags) 507 throws Exception; 508 509 static { 510 TJLoader.load(); 511 } 512 513 protected long handle = 0; 514 protected byte[] jpegBuf = null; 515 protected int jpegBufSize = 0; 516 protected int jpegWidth = 0; 517 protected int jpegHeight = 0; 518 protected int jpegSubsamp = -1; 519 private ByteOrder byteOrder = null; 520}; 521