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