001/* 002 * $RCSfile: TIFFOldJPEGDecompressor.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.4 $ 042 * $Date: 2007/09/14 01:14:56 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.plugins.tiff; 046 047import java.awt.Point; 048import java.awt.image.BufferedImage; 049import java.io.IOException; 050import java.io.ByteArrayInputStream; 051import java.io.ByteArrayOutputStream; 052import java.util.Iterator; 053 054import javax.imageio.IIOException; 055import javax.imageio.ImageIO; 056import javax.imageio.ImageReader; 057import javax.imageio.ImageReadParam; 058import javax.imageio.plugins.jpeg.JPEGHuffmanTable; 059import javax.imageio.plugins.jpeg.JPEGImageReadParam; 060import javax.imageio.plugins.jpeg.JPEGQTable; 061import javax.imageio.stream.MemoryCacheImageInputStream; 062import javax.imageio.stream.ImageInputStream; 063 064import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet; 065import com.github.jaiimageio.plugins.tiff.TIFFDecompressor; 066import com.github.jaiimageio.plugins.tiff.TIFFField; 067 068/** 069 * <code>TIFFDecompressor</code> for "Old JPEG" compression. 070 */ 071public class TIFFOldJPEGDecompressor extends TIFFJPEGDecompressor { 072 073 private static final boolean DEBUG = false; // XXX 'false' for release 074 075 // Start of Image 076 // private static final int SOI = 0xD8; // now defined in superclass 077 078 // Define Huffman Tables 079 private static final int DHT = 0xC4; 080 081 // Define Quantisation Tables 082 private static final int DQT = 0xDB; 083 084 // Define Restart Interval 085 private static final int DRI = 0xDD; 086 087 // Baseline DCT 088 private static final int SOF0 = 0xC0; 089 090 // Start of Scan 091 private static final int SOS = 0xDA; 092 093 // End of Image 094 // private static final int EOI = 0xD9; // now defined in superclass 095 096 // Whether the decompressor has been initialized. 097 private boolean isInitialized = false; 098 099 // 100 // Instance variables set by the initialize() method. 101 // 102 // Offset to a complete, contiguous JPEG stream. 103 private Long JPEGStreamOffset = null; 104 // Offset to the SOF marker. 105 private int SOFPosition = -1; 106 // Value of the SOS marker. 107 private byte[] SOSMarker = null; 108 109 // Horizontal chroma subsampling factor. 110 private int subsamplingX = 2; 111 112 // Vertical chroma subsampling factor. 113 private int subsamplingY = 2; 114 115 public TIFFOldJPEGDecompressor() {} 116 117 // 118 // Intialize instance variables according to an analysis of the 119 // TIFF field content. See bug 4929147 for test image information. 120 // 121 // Case 1: Image contains a single strip or tile and the offset to 122 // that strip or tile points to an SOI marker. 123 // 124 // Example: 125 // "Visionshape Inc. Compression Software, version 2.5" 126 // ColorTiffWithBarcode.tif 127 // Color2.tif (pages 2-5 (indexes 1-4) 128 // color3.tif (pages 2-5 (indexes 1-4) 129 // 130 // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000" 131 // 01.tif (pages 1 and 3(indexes 0 and 2)) 132 // 133 // Instance variables set: JPEGStreamOffset 134 // 135 // Case 2: Image contains a single strip or tile and a 136 // JPEGInterchangeFormat field is present but the 137 // JPEGInterchangeFormatLength is erroneously missing. 138 // 139 // Example: 140 // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000" 141 // 01.tif (pages 1 and 3(indexes 0 and 2)) 142 // (but this example also satisfies case 1) 143 // 144 // Instance variables set: JPEGStreamOffset 145 // 146 // Case 3: Image contains a single strip or tile, the 147 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 148 // fields are both present, the value of JPEGInterchangeFormat 149 // is less than the offset to the strip or tile, and the sum 150 // of the values of JPEGInterchangeFormat and 151 // JPEGInterchangeFormatLength is greater than the offset to 152 // the strip or tile. 153 // 154 // Instance variables set: JPEGStreamOffset 155 // 156 // Example: 157 // "HP IL v1.1" 158 // smallliz.tif from libtiff test data. 159 // 160 // Instance variables set: JPEGStreamOffset 161 // 162 // Cases 4-5 apply if none of cases 1-3 applies or the image has multiple 163 // strips or tiles. 164 // 165 // Case 4: JPEGInterchangeFormat and JPEGInterchangeFormatLength are 166 // present, the value of JPEGInterchangeFormatLength is at least 2, 167 // and the sum of the values of these two fields is at most the 168 // value of the offset to the first strip or tile. 169 // 170 // Instance variables set: tables, SOFPosition, SOSMarker 171 // 172 // Example: 173 // "Oi/GFS, writer v00.06.00P, (c) Wang Labs, Inc. 1990, 1991" 174 // 03.tif (pages 1 and 3(indexes 0 and 2)) 175 // 176 // "Oi/GFS, writer v00.06.02" 177 // Color2.tif (page 1 (index 0)) 178 // color3.tif (page 1 (index 0)) 179 // 180 // Case 5: If none of the foregoing cases apply. For this case the 181 // JPEGQTables, JPEGACTables, and JPEGDCTables must be valid. 182 // 183 // Instance variables set: tables, SOFPosition, SOSMarker 184 // 185 // Example: 186 // "NeXT" 187 // zackthecat.tif from libtiff test data. 188 // 189 private synchronized void initialize() throws IOException { 190 if(isInitialized) { 191 return; 192 } 193 194 // Get the TIFF metadata object. 195 TIFFImageMetadata tim = (TIFFImageMetadata)metadata; 196 197 // Get the JPEGInterchangeFormat field. 198 TIFFField JPEGInterchangeFormatField = 199 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 200 201 // Get the tile or strip offsets. 202 TIFFField segmentOffsetField = 203 tim.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 204 if(segmentOffsetField == null) { 205 segmentOffsetField = 206 tim.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 207 if(segmentOffsetField == null) { 208 segmentOffsetField = JPEGInterchangeFormatField; 209 } 210 } 211 long[] segmentOffsets = segmentOffsetField.getAsLongs(); 212 213 // Determine whether the image has more than one strip or tile. 214 boolean isTiled = segmentOffsets.length > 1; 215 216 if(!isTiled) { 217 // 218 // If the image has only a single strip or tile and it looks 219 // as if a complete JPEG stream is present then set the value 220 // of JPEGStreamOffset to the offset of the JPEG stream; 221 // otherwise leave JPEGStreamOffset set to null. 222 // 223 224 stream.seek(offset); 225 stream.mark(); 226 if(stream.read() == 0xff && stream.read() == SOI) { 227 // Tile or strip offset points to SOI. 228 JPEGStreamOffset = new Long(offset); 229 230 // Set initialization flag and return. 231 if(DEBUG) System.out.println("OLD JPEG CASE 1"); 232 ((TIFFImageReader)reader).forwardWarningMessage("SOI marker detected at start of strip or tile."); 233 isInitialized = true; 234 stream.reset(); 235 return; 236 } 237 stream.reset(); 238 239 if(JPEGInterchangeFormatField != null) { 240 // Get the value of JPEGInterchangeFormat. 241 long jpegInterchangeOffset = 242 JPEGInterchangeFormatField.getAsLong(0); 243 244 // Check that the value of JPEGInterchangeFormat points to SOI. 245 stream.mark(); 246 stream.seek(jpegInterchangeOffset); 247 if(stream.read() == 0xff && stream.read() == SOI) 248 // JPEGInterchangeFormat offset points to SOI. 249 JPEGStreamOffset = new Long(jpegInterchangeOffset); 250 else 251 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat does not point to SOI"); 252 stream.reset(); 253 254 // Get the JPEGInterchangeFormatLength field. 255 TIFFField JPEGInterchangeFormatLengthField = 256 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 257 258 if(JPEGInterchangeFormatLengthField == null) { 259 if(DEBUG) System.out.println("OLD JPEG CASE 2"); 260 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field is missing"); 261 } else { 262 // Get the JPEGInterchangeFormatLength field's value. 263 long jpegInterchangeLength = 264 JPEGInterchangeFormatLengthField.getAsLong(0); 265 266 if(jpegInterchangeOffset < segmentOffsets[0] && 267 (jpegInterchangeOffset + jpegInterchangeLength) > 268 segmentOffsets[0]) { 269 if(DEBUG) System.out.println("OLD JPEG CASE 3"); 270 } else { 271 if(DEBUG) System.out.println("OLD JPEG CASE 3A"); 272 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field value is invalid"); 273 } 274 } 275 276 // Return if JPEGInterchangeFormat pointed to SOI. 277 if(JPEGStreamOffset != null) { 278 isInitialized = true; 279 return; 280 } 281 } 282 } 283 284 // Get the subsampling factors. 285 TIFFField YCbCrSubsamplingField = 286 tim.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING); 287 if(YCbCrSubsamplingField != null) { 288 subsamplingX = YCbCrSubsamplingField.getAsChars()[0]; 289 subsamplingY = YCbCrSubsamplingField.getAsChars()[1]; 290 } 291 292 // 293 // Initialize the 'tables' instance variable either for later 294 // use in prepending to individual abbreviated strips or tiles. 295 // 296 if(JPEGInterchangeFormatField != null) { 297 // Get the value of JPEGInterchangeFormat. 298 long jpegInterchangeOffset = 299 JPEGInterchangeFormatField.getAsLong(0); 300 301 // Get the JPEGInterchangeFormatLength field. 302 TIFFField JPEGInterchangeFormatLengthField = 303 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 304 305 if(JPEGInterchangeFormatLengthField != null) { 306 // Get the JPEGInterchangeFormatLength field's value. 307 long jpegInterchangeLength = 308 JPEGInterchangeFormatLengthField.getAsLong(0); 309 310 if(jpegInterchangeLength >= 2 && 311 jpegInterchangeOffset + jpegInterchangeLength <= 312 segmentOffsets[0]) { 313 // Determine the length excluding any terminal EOI marker 314 // and allocate table memory. 315 stream.mark(); 316 stream.seek(jpegInterchangeOffset+jpegInterchangeLength-2); 317 if(stream.read() == 0xff && stream.read() == EOI) { 318 this.tables = new byte[(int)(jpegInterchangeLength-2)]; 319 } else { 320 this.tables = new byte[(int)jpegInterchangeLength]; 321 } 322 stream.reset(); 323 324 // Read the tables. 325 stream.mark(); 326 stream.seek(jpegInterchangeOffset); 327 stream.readFully(tables); 328 stream.reset(); 329 330 if(DEBUG) System.out.println("OLD JPEG CASE 4"); 331 ((TIFFImageReader)reader).forwardWarningMessage("Incorrect JPEG interchange format: using JPEGInterchangeFormat offset to derive tables."); 332 } else { 333 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat+JPEGInterchangeFormatLength > offset to first strip or tile."); 334 } 335 } 336 } 337 338 if(this.tables == null) { 339 // 340 // Create tables-only stream in tables[] consisting of 341 // SOI+DQTs+DHTs 342 // 343 344 ByteArrayOutputStream baos = 345 new ByteArrayOutputStream();//XXX length 346 347 // Save stream length; 348 long streamLength = stream.length(); 349 350 // SOI 351 baos.write(0xff); 352 baos.write(SOI); 353 354 // Quantization Tables 355 TIFFField f = 356 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 357 if(f == null) { 358 throw new IIOException("JPEGQTables field missing!"); 359 } 360 long[] off = f.getAsLongs(); 361 362 for(int i = 0; i < off.length; i++) { 363 baos.write(0xff); // Marker ID 364 baos.write(DQT); 365 366 char markerLength = (char)67; 367 baos.write((markerLength >>> 8) & 0xff); // Length 368 baos.write(markerLength & 0xff); 369 370 baos.write(i); // Table ID and precision 371 372 byte[] qtable = new byte[64]; 373 if(streamLength != -1 && off[i] > streamLength) { 374 throw new IIOException("JPEGQTables offset for index "+ 375 i+" is not in the stream!"); 376 } 377 stream.seek(off[i]); 378 stream.readFully(qtable); 379 380 baos.write(qtable); // Table data 381 } 382 383 // Huffman Tables (k == 0 ? DC : AC). 384 for(int k = 0; k < 2; k++) { 385 int tableTagNumber = k == 0 ? 386 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES : 387 BaselineTIFFTagSet.TAG_JPEG_AC_TABLES; 388 f = tim.getTIFFField(tableTagNumber); 389 String fieldName = 390 tableTagNumber == 391 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES ? 392 "JPEGDCTables" : "JPEGACTables"; 393 394 if(f == null) { 395 throw new IIOException(fieldName+" field missing!"); 396 } 397 off = f.getAsLongs(); 398 399 for(int i = 0; i < off.length; i++) { 400 baos.write(0xff); // Marker ID 401 baos.write(DHT); 402 403 byte[] blengths = new byte[16]; 404 if(streamLength != -1 && off[i] > streamLength) { 405 throw new IIOException(fieldName+" offset for index "+ 406 i+" is not in the stream!"); 407 } 408 stream.seek(off[i]); 409 stream.readFully(blengths); 410 int numCodes = 0; 411 for(int j = 0; j < 16; j++) { 412 numCodes += blengths[j]&0xff; 413 } 414 415 char markerLength = (char)(19 + numCodes); 416 417 baos.write((markerLength >>> 8) & 0xff); // Length 418 baos.write(markerLength & 0xff); 419 420 baos.write(i | (k << 4)); // Table ID and type 421 422 baos.write(blengths); // Number of codes 423 424 byte[] bcodes = new byte[numCodes]; 425 stream.readFully(bcodes); 426 baos.write(bcodes); // Codes 427 } 428 } 429 430 // SOF0 431 baos.write((byte)0xff); // Marker identifier 432 baos.write((byte)SOF0); 433 short sval = (short)(8 + 3*samplesPerPixel); // Length 434 baos.write((byte)((sval >>> 8) & 0xff)); 435 baos.write((byte)(sval & 0xff)); 436 baos.write((byte)8); // Data precision 437 sval = (short)srcHeight; // Tile/strip height 438 baos.write((byte)((sval >>> 8) & 0xff)); 439 baos.write((byte)(sval & 0xff)); 440 sval = (short)srcWidth; // Tile/strip width 441 baos.write((byte)((sval >>> 8) & 0xff)); 442 baos.write((byte)(sval & 0xff)); 443 baos.write((byte)samplesPerPixel); // Number of components 444 if(samplesPerPixel == 1) { 445 baos.write((byte)1); // Component ID 446 baos.write((byte)0x11); // Subsampling factor 447 baos.write((byte)0); // Quantization table ID 448 } else { // 3 449 for(int i = 0; i < 3; i++) { 450 baos.write((byte)(i + 1)); // Component ID 451 baos.write((i != 0) ? 452 (byte)0x11 : 453 (byte)(((subsamplingX & 0x0f) << 4) | 454 (subsamplingY & 0x0f))); 455 456 baos.write((byte)i); // Quantization table ID 457 } 458 }; 459 460 461 // DRI (optional). 462 f = tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL); 463 if(f != null) { 464 char restartInterval = f.getAsChars()[0]; 465 466 if(restartInterval != 0) { 467 baos.write((byte)0xff); // Marker identifier 468 baos.write((byte)DRI); 469 470 sval = 4; 471 baos.write((byte)((sval >>> 8) & 0xff)); // Length 472 baos.write((byte)(sval & 0xff)); 473 474 // RestartInterval 475 baos.write((byte)((restartInterval >>> 8) & 0xff)); 476 baos.write((byte)(restartInterval & 0xff)); 477 } 478 } 479 480 tables = baos.toByteArray(); 481 482 if(DEBUG) System.out.println("OLD JPEG CASE 5"); 483 } 484 485 // 486 // Check for presence of SOF marker and save its position. 487 // 488 int idx = 0; 489 int idxMax = tables.length - 1; 490 while(idx < idxMax) { 491 if((tables[idx]&0xff) == 0xff && 492 (tables[idx+1]&0xff) == SOF0) { 493 SOFPosition = idx; 494 break; 495 } 496 idx++; 497 } 498 499 // 500 // If no SOF marker, add one. 501 // 502 if(SOFPosition == -1) { 503 byte[] tmpTables = 504 new byte[tables.length + 10 + 3*samplesPerPixel]; 505 System.arraycopy(tables, 0, tmpTables, 0, tables.length); 506 int tmpOffset = tables.length; 507 SOFPosition = tables.length; 508 tables = tmpTables; 509 510 tables[tmpOffset++] = (byte)0xff; // Marker identifier 511 tables[tmpOffset++] = (byte)SOF0; 512 short sval = (short)(8 + 3*samplesPerPixel); // Length 513 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 514 tables[tmpOffset++] = (byte)(sval & 0xff); 515 tables[tmpOffset++] = (byte)8; // Data precision 516 sval = (short)srcHeight; // Tile/strip height 517 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 518 tables[tmpOffset++] = (byte)(sval & 0xff); 519 sval = (short)srcWidth; // Tile/strip width 520 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 521 tables[tmpOffset++] = (byte)(sval & 0xff); 522 tables[tmpOffset++] = (byte)samplesPerPixel; // Number of components 523 if(samplesPerPixel == 1) { 524 tables[tmpOffset++] = (byte)1; // Component ID 525 tables[tmpOffset++] = (byte)0x11; // Subsampling factor 526 tables[tmpOffset++] = (byte)0; // Quantization table ID 527 } else { // 3 528 for(int i = 0; i < 3; i++) { 529 tables[tmpOffset++] = (byte)(i + 1); // Component ID 530 tables[tmpOffset++] = (i != 0) ? 531 (byte)0x11 : 532 (byte)(((subsamplingX & 0x0f) << 4) | 533 (subsamplingY & 0x0f)); 534 535 tables[tmpOffset++] = (byte)i; // Quantization table ID 536 } 537 }; 538 } 539 540 // 541 // Initialize SOSMarker. 542 // 543 stream.mark(); 544 stream.seek(segmentOffsets[0]); 545 if(stream.read() == 0xff && stream.read() == SOS) { 546 // 547 // If the first segment starts with an SOS marker save it. 548 // 549 int SOSLength = (stream.read()<<8)|stream.read(); 550 SOSMarker = new byte[SOSLength+2]; 551 SOSMarker[0] = (byte)0xff; 552 SOSMarker[1] = (byte)SOS; 553 SOSMarker[2] = (byte)((SOSLength & 0xff00) >> 8); 554 SOSMarker[3] = (byte)(SOSLength & 0xff); 555 stream.readFully(SOSMarker, 4, SOSLength - 2); 556 } else { 557 // 558 // Manufacture an SOS marker. 559 // 560 SOSMarker = new byte[2 + 6 + 2*samplesPerPixel]; 561 int SOSMarkerIndex = 0; 562 SOSMarker[SOSMarkerIndex++] = (byte)0xff; // Marker identifier 563 SOSMarker[SOSMarkerIndex++] = (byte)SOS; 564 short sval = (short)(6 + 2*samplesPerPixel); // Length 565 SOSMarker[SOSMarkerIndex++] = (byte)((sval >>> 8) & 0xff); 566 SOSMarker[SOSMarkerIndex++] = (byte)(sval & 0xff); 567 // Number of components in scan 568 SOSMarker[SOSMarkerIndex++] = (byte)samplesPerPixel; 569 if(samplesPerPixel == 1) { 570 SOSMarker[SOSMarkerIndex++] = (byte)1; // Component ID 571 SOSMarker[SOSMarkerIndex++] = (byte)0; // Huffman table ID 572 } else { // 3 573 for(int i = 0; i < 3; i++) { 574 SOSMarker[SOSMarkerIndex++] = 575 (byte)(i + 1); // Component ID 576 SOSMarker[SOSMarkerIndex++] = 577 (byte)((i << 4) | i); // Huffman table IDs 578 } 579 }; 580 SOSMarker[SOSMarkerIndex++] = (byte)0; 581 SOSMarker[SOSMarkerIndex++] = (byte)0x3f; 582 SOSMarker[SOSMarkerIndex++] = (byte)0; 583 } 584 stream.reset(); 585 586 // Set initialization flag. 587 isInitialized = true; 588 } 589 590 // 591 // The strategy for reading cases 1-3 is to treat the data as a complete 592 // JPEG interchange stream located at JPEGStreamOffset. 593 // 594 // The strategy for cases 4-5 is to concatenate a tables stream created 595 // in initialize() with the entropy coded data in each strip or tile. 596 // 597 public void decodeRaw(byte[] b, 598 int dstOffset, 599 int bitsPerPixel, 600 int scanlineStride) throws IOException { 601 602 initialize(); 603 604 TIFFImageMetadata tim = (TIFFImageMetadata)metadata; 605 606 if(JPEGStreamOffset != null) { 607 stream.seek(JPEGStreamOffset.longValue()); 608 JPEGReader.setInput(stream, false, true); 609 } else { 610 // Determine buffer length and allocate. 611 int tableLength = tables.length; 612 int bufLength = 613 tableLength + SOSMarker.length + byteCount + 2; // 2 for EOI. 614 byte[] buf = new byte[bufLength]; 615 if(tables != null) { 616 System.arraycopy(tables, 0, buf, 0, tableLength); 617 } 618 int bufOffset = tableLength; 619 620 // Update the SOF dimensions. 621 short sval = (short)srcHeight; // Tile/strip height 622 buf[SOFPosition + 5] = (byte)((sval >>> 8) & 0xff); 623 buf[SOFPosition + 6] = (byte)(sval & 0xff); 624 sval = (short)srcWidth; // Tile/strip width 625 buf[SOFPosition + 7] = (byte)((sval >>> 8) & 0xff); 626 buf[SOFPosition + 8] = (byte)(sval & 0xff); 627 628 // Seek to data. 629 stream.seek(offset); 630 631 // Add SOS marker if data segment does not start with one. 632 byte[] twoBytes = new byte[2]; 633 stream.readFully(twoBytes); 634 if(!((twoBytes[0]&0xff) == 0xff && (twoBytes[1]&0xff) == SOS)) { 635 // Segment does not start with SOS marker; 636 // use the pre-calculated SOS marker. 637 System.arraycopy(SOSMarker, 0, buf, bufOffset, 638 SOSMarker.length); 639 bufOffset += SOSMarker.length; 640 } 641 642 // Copy the segment data into the buffer. 643 buf[bufOffset++] = twoBytes[0]; 644 buf[bufOffset++] = twoBytes[1]; 645 stream.readFully(buf, bufOffset, byteCount - 2); 646 bufOffset += byteCount - 2; 647 648 // EOI. 649 buf[bufOffset++] = (byte)0xff; // Marker identifier 650 buf[bufOffset++] = (byte)EOI; 651 652 ByteArrayInputStream bais = 653 new ByteArrayInputStream(buf, 0, bufOffset); 654 ImageInputStream is = new MemoryCacheImageInputStream(bais); 655 656 JPEGReader.setInput(is, true, true); 657 } 658 659 // Read real image 660 JPEGParam.setDestination(rawImage); 661 JPEGReader.read(0, JPEGParam); 662 } 663 664 protected void finalize() throws Throwable { 665 super.finalize(); 666 JPEGReader.dispose(); 667 } 668}