1/* 2 * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.imageio.plugins.tiff; 26 27import java.io.IOException; 28import java.io.ByteArrayInputStream; 29import java.io.ByteArrayOutputStream; 30import javax.imageio.IIOException; 31import javax.imageio.stream.MemoryCacheImageInputStream; 32import javax.imageio.stream.ImageInputStream; 33import javax.imageio.plugins.tiff.BaselineTIFFTagSet; 34import javax.imageio.plugins.tiff.TIFFField; 35 36/** 37 * {@code TIFFDecompressor} for "Old JPEG" compression. 38 */ 39public class TIFFOldJPEGDecompressor extends TIFFJPEGDecompressor { 40 41 // Define Huffman Tables 42 private static final int DHT = 0xC4; 43 44 // Define Quantisation Tables 45 private static final int DQT = 0xDB; 46 47 // Define Restart Interval 48 private static final int DRI = 0xDD; 49 50 // Baseline DCT 51 private static final int SOF0 = 0xC0; 52 53 // Start of Scan 54 private static final int SOS = 0xDA; 55 56 // End of Image 57 // private static final int EOI = 0xD9; // now defined in superclass 58 59 // Whether the decompressor has been initialized. 60 private boolean isInitialized = false; 61 62 // 63 // Instance variables set by the initialize() method. 64 // 65 // Offset to a complete, contiguous JPEG stream. 66 private Long JPEGStreamOffset = null; 67 // Offset to the SOF marker. 68 private int SOFPosition = -1; 69 // Value of the SOS marker. 70 private byte[] SOSMarker = null; 71 72 // Horizontal chroma subsampling factor. 73 private int subsamplingX = 2; 74 75 // Vertical chroma subsampling factor. 76 private int subsamplingY = 2; 77 78 public TIFFOldJPEGDecompressor() {} 79 80 // 81 // Intialize instance variables according to an analysis of the 82 // TIFF field content. See bug 4929147 for test image information. 83 // 84 // Case 1: Image contains a single strip or tile and the offset to 85 // that strip or tile points to an SOI marker. 86 // 87 // Example: 88 // "Visionshape Inc. Compression Software, version 2.5" 89 // ColorTiffWithBarcode.tif 90 // Color2.tif (pages 2-5 (indexes 1-4) 91 // color3.tif (pages 2-5 (indexes 1-4) 92 // 93 // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000" 94 // 01.tif (pages 1 and 3(indexes 0 and 2)) 95 // 96 // Instance variables set: JPEGStreamOffset 97 // 98 // Case 2: Image contains a single strip or tile and a 99 // JPEGInterchangeFormat field is present but the 100 // JPEGInterchangeFormatLength is erroneously missing. 101 // 102 // Example: 103 // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000" 104 // 01.tif (pages 1 and 3(indexes 0 and 2)) 105 // (but this example also satisfies case 1) 106 // 107 // Instance variables set: JPEGStreamOffset 108 // 109 // Case 3: Image contains a single strip or tile, the 110 // JPEGInterchangeFormat and JPEGInterchangeFormatLength 111 // fields are both present, the value of JPEGInterchangeFormat 112 // is less than the offset to the strip or tile, and the sum 113 // of the values of JPEGInterchangeFormat and 114 // JPEGInterchangeFormatLength is greater than the offset to 115 // the strip or tile. 116 // 117 // Instance variables set: JPEGStreamOffset 118 // 119 // Example: 120 // "HP IL v1.1" 121 // smallliz.tif from libtiff test data. 122 // 123 // Instance variables set: JPEGStreamOffset 124 // 125 // Cases 4-5 apply if none of cases 1-3 applies or the image has multiple 126 // strips or tiles. 127 // 128 // Case 4: JPEGInterchangeFormat and JPEGInterchangeFormatLength are 129 // present, the value of JPEGInterchangeFormatLength is at least 2, 130 // and the sum of the values of these two fields is at most the 131 // value of the offset to the first strip or tile. 132 // 133 // Instance variables set: tables, SOFPosition, SOSMarker 134 // 135 // Example: 136 // "Oi/GFS, writer v00.06.00P, (c) Wang Labs, Inc. 1990, 1991" 137 // 03.tif (pages 1 and 3(indexes 0 and 2)) 138 // 139 // "Oi/GFS, writer v00.06.02" 140 // Color2.tif (page 1 (index 0)) 141 // color3.tif (page 1 (index 0)) 142 // 143 // Case 5: If none of the foregoing cases apply. For this case the 144 // JPEGQTables, JPEGACTables, and JPEGDCTables must be valid. 145 // 146 // Instance variables set: tables, SOFPosition, SOSMarker 147 // 148 // Example: 149 // "NeXT" 150 // zackthecat.tif from libtiff test data. 151 // 152 private synchronized void initialize() throws IOException { 153 if(isInitialized) { 154 return; 155 } 156 157 // Get the TIFF metadata object. 158 TIFFImageMetadata tim = (TIFFImageMetadata)metadata; 159 160 // Get the JPEGInterchangeFormat field. 161 TIFFField JPEGInterchangeFormatField = 162 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 163 164 // Get the tile or strip offsets. 165 TIFFField segmentOffsetField = 166 tim.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 167 if(segmentOffsetField == null) { 168 segmentOffsetField = 169 tim.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 170 if(segmentOffsetField == null) { 171 segmentOffsetField = JPEGInterchangeFormatField; 172 } 173 } 174 long[] segmentOffsets = segmentOffsetField.getAsLongs(); 175 176 // Determine whether the image has more than one strip or tile. 177 boolean isTiled = segmentOffsets.length > 1; 178 179 if(!isTiled) { 180 // 181 // If the image has only a single strip or tile and it looks 182 // as if a complete JPEG stream is present then set the value 183 // of JPEGStreamOffset to the offset of the JPEG stream; 184 // otherwise leave JPEGStreamOffset set to null. 185 // 186 187 stream.seek(offset); 188 stream.mark(); 189 if(stream.read() == 0xff && stream.read() == SOI) { 190 // Tile or strip offset points to SOI. 191 JPEGStreamOffset = Long.valueOf(offset); 192 193 // Set initialization flag and return. 194 ((TIFFImageReader)reader).forwardWarningMessage("SOI marker detected at start of strip or tile."); 195 isInitialized = true; 196 return; 197 } 198 stream.reset(); 199 200 if(JPEGInterchangeFormatField != null) { 201 // Get the value of JPEGInterchangeFormat. 202 long jpegInterchangeOffset = 203 JPEGInterchangeFormatField.getAsLong(0); 204 205 // Get the JPEGInterchangeFormatLength field. 206 TIFFField JPEGInterchangeFormatLengthField = 207 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 208 209 if(JPEGInterchangeFormatLengthField == null) { 210 // JPEGInterchangeFormat stream is of indeterminate 211 // length so use it as a complete JPEG stream. 212 JPEGStreamOffset = Long.valueOf(jpegInterchangeOffset); 213 214 // Set initialization flag and return. 215 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field is missing"); 216 isInitialized = true; 217 return; 218 } else { 219 // Get the JPEGInterchangeFormatLength field's value. 220 long jpegInterchangeLength = 221 JPEGInterchangeFormatLengthField.getAsLong(0); 222 223 if(jpegInterchangeOffset < segmentOffsets[0] && 224 (jpegInterchangeOffset + jpegInterchangeLength) > 225 segmentOffsets[0]) { 226 // JPEGInterchangeFormat points to a position 227 // below the segment start position and ends at 228 // a position after the segment start position. 229 JPEGStreamOffset = Long.valueOf(jpegInterchangeOffset); 230 231 // Set initialization flag and return. 232 isInitialized = true; 233 return; 234 } 235 } 236 } 237 } 238 239 // Get the subsampling factors. 240 TIFFField YCbCrSubsamplingField = 241 tim.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING); 242 if(YCbCrSubsamplingField != null) { 243 subsamplingX = YCbCrSubsamplingField.getAsChars()[0]; 244 subsamplingY = YCbCrSubsamplingField.getAsChars()[1]; 245 } 246 247 // 248 // Initialize the 'tables' instance variable either for later 249 // use in prepending to individual abbreviated strips or tiles. 250 // 251 if(JPEGInterchangeFormatField != null) { 252 // Get the value of JPEGInterchangeFormat. 253 long jpegInterchangeOffset = 254 JPEGInterchangeFormatField.getAsLong(0); 255 256 // Get the JPEGInterchangeFormatLength field. 257 TIFFField JPEGInterchangeFormatLengthField = 258 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 259 260 if(JPEGInterchangeFormatLengthField != null) { 261 // Get the JPEGInterchangeFormatLength field's value. 262 long jpegInterchangeLength = 263 JPEGInterchangeFormatLengthField.getAsLong(0); 264 265 if(jpegInterchangeLength >= 2 && 266 jpegInterchangeOffset + jpegInterchangeLength <= 267 segmentOffsets[0]) { 268 // Determine the length excluding any terminal EOI marker 269 // and allocate table memory. 270 stream.mark(); 271 stream.seek(jpegInterchangeOffset+jpegInterchangeLength-2); 272 if(stream.read() == 0xff && stream.read() == EOI) { 273 this.tables = new byte[(int)(jpegInterchangeLength-2)]; 274 } else { 275 this.tables = new byte[(int)jpegInterchangeLength]; 276 } 277 stream.reset(); 278 279 // Read the tables. 280 stream.mark(); 281 stream.seek(jpegInterchangeOffset); 282 stream.readFully(tables); 283 stream.reset(); 284 285 ((TIFFImageReader)reader).forwardWarningMessage("Incorrect JPEG interchange format: using JPEGInterchangeFormat offset to derive tables."); 286 } else { 287 ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat+JPEGInterchangeFormatLength > offset to first strip or tile."); 288 } 289 } 290 } 291 292 if(this.tables == null) { 293 // 294 // Create tables-only stream in tables[] consisting of 295 // SOI+DQTs+DHTs 296 // 297 298 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 299 300 // Save stream length; 301 long streamLength = stream.length(); 302 303 // SOI 304 baos.write(0xff); 305 baos.write(SOI); 306 307 // Quantization Tables 308 TIFFField f = 309 tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES); 310 if(f == null) { 311 throw new IIOException("JPEGQTables field missing!"); 312 } 313 long[] off = f.getAsLongs(); 314 315 for(int i = 0; i < off.length; i++) { 316 baos.write(0xff); // Marker ID 317 baos.write(DQT); 318 319 char markerLength = (char)67; 320 baos.write((markerLength >>> 8) & 0xff); // Length 321 baos.write(markerLength & 0xff); 322 323 baos.write(i); // Table ID and precision 324 325 byte[] qtable = new byte[64]; 326 if(streamLength != -1 && off[i] > streamLength) { 327 throw new IIOException("JPEGQTables offset for index "+ 328 i+" is not in the stream!"); 329 } 330 stream.seek(off[i]); 331 stream.readFully(qtable); 332 333 baos.write(qtable); // Table data 334 } 335 336 // Huffman Tables (k == 0 ? DC : AC). 337 for(int k = 0; k < 2; k++) { 338 int tableTagNumber = k == 0 ? 339 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES : 340 BaselineTIFFTagSet.TAG_JPEG_AC_TABLES; 341 f = tim.getTIFFField(tableTagNumber); 342 String fieldName = 343 tableTagNumber == 344 BaselineTIFFTagSet.TAG_JPEG_DC_TABLES ? 345 "JPEGDCTables" : "JPEGACTables"; 346 347 if(f == null) { 348 throw new IIOException(fieldName+" field missing!"); 349 } 350 off = f.getAsLongs(); 351 352 for(int i = 0; i < off.length; i++) { 353 baos.write(0xff); // Marker ID 354 baos.write(DHT); 355 356 byte[] blengths = new byte[16]; 357 if(streamLength != -1 && off[i] > streamLength) { 358 throw new IIOException(fieldName+" offset for index "+ 359 i+" is not in the stream!"); 360 } 361 stream.seek(off[i]); 362 stream.readFully(blengths); 363 int numCodes = 0; 364 for(int j = 0; j < 16; j++) { 365 numCodes += blengths[j]&0xff; 366 } 367 368 char markerLength = (char)(19 + numCodes); 369 370 baos.write((markerLength >>> 8) & 0xff); // Length 371 baos.write(markerLength & 0xff); 372 373 baos.write(i | (k << 4)); // Table ID and type 374 375 baos.write(blengths); // Number of codes 376 377 byte[] bcodes = new byte[numCodes]; 378 stream.readFully(bcodes); 379 baos.write(bcodes); // Codes 380 } 381 } 382 383 // SOF0 384 baos.write((byte)0xff); // Marker identifier 385 baos.write((byte)SOF0); 386 short sval = (short)(8 + 3*samplesPerPixel); // Length 387 baos.write((byte)((sval >>> 8) & 0xff)); 388 baos.write((byte)(sval & 0xff)); 389 baos.write((byte)8); // Data precision 390 sval = (short)srcHeight; // Tile/strip height 391 baos.write((byte)((sval >>> 8) & 0xff)); 392 baos.write((byte)(sval & 0xff)); 393 sval = (short)srcWidth; // Tile/strip width 394 baos.write((byte)((sval >>> 8) & 0xff)); 395 baos.write((byte)(sval & 0xff)); 396 baos.write((byte)samplesPerPixel); // Number of components 397 if(samplesPerPixel == 1) { 398 baos.write((byte)1); // Component ID 399 baos.write((byte)0x11); // Subsampling factor 400 baos.write((byte)0); // Quantization table ID 401 } else { // 3 402 for(int i = 0; i < 3; i++) { 403 baos.write((byte)(i + 1)); // Component ID 404 baos.write((i != 0) ? 405 (byte)0x11 : 406 (byte)(((subsamplingX & 0x0f) << 4) | 407 (subsamplingY & 0x0f))); 408 409 baos.write((byte)i); // Quantization table ID 410 } 411 }; 412 413 414 // DRI (optional). 415 f = tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL); 416 if(f != null) { 417 char restartInterval = f.getAsChars()[0]; 418 419 if(restartInterval != 0) { 420 baos.write((byte)0xff); // Marker identifier 421 baos.write((byte)DRI); 422 423 sval = 4; 424 baos.write((byte)((sval >>> 8) & 0xff)); // Length 425 baos.write((byte)(sval & 0xff)); 426 427 // RestartInterval 428 baos.write((byte)((restartInterval >>> 8) & 0xff)); 429 baos.write((byte)(restartInterval & 0xff)); 430 } 431 } 432 433 tables = baos.toByteArray(); 434 } 435 436 // 437 // Check for presence of SOF marker and save its position. 438 // 439 int idx = 0; 440 int idxMax = tables.length - 1; 441 while(idx < idxMax) { 442 if((tables[idx]&0xff) == 0xff && 443 (tables[idx+1]&0xff) == SOF0) { 444 SOFPosition = idx; 445 break; 446 } 447 idx++; 448 } 449 450 // 451 // If no SOF marker, add one. 452 // 453 if(SOFPosition == -1) { 454 byte[] tmpTables = 455 new byte[tables.length + 10 + 3*samplesPerPixel]; 456 System.arraycopy(tables, 0, tmpTables, 0, tables.length); 457 int tmpOffset = tables.length; 458 SOFPosition = tables.length; 459 tables = tmpTables; 460 461 tables[tmpOffset++] = (byte)0xff; // Marker identifier 462 tables[tmpOffset++] = (byte)SOF0; 463 short sval = (short)(8 + 3*samplesPerPixel); // Length 464 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 465 tables[tmpOffset++] = (byte)(sval & 0xff); 466 tables[tmpOffset++] = (byte)8; // Data precision 467 sval = (short)srcHeight; // Tile/strip height 468 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 469 tables[tmpOffset++] = (byte)(sval & 0xff); 470 sval = (short)srcWidth; // Tile/strip width 471 tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff); 472 tables[tmpOffset++] = (byte)(sval & 0xff); 473 tables[tmpOffset++] = (byte)samplesPerPixel; // Number of components 474 if(samplesPerPixel == 1) { 475 tables[tmpOffset++] = (byte)1; // Component ID 476 tables[tmpOffset++] = (byte)0x11; // Subsampling factor 477 tables[tmpOffset++] = (byte)0; // Quantization table ID 478 } else { // 3 479 for(int i = 0; i < 3; i++) { 480 tables[tmpOffset++] = (byte)(i + 1); // Component ID 481 tables[tmpOffset++] = (i != 0) ? 482 (byte)0x11 : 483 (byte)(((subsamplingX & 0x0f) << 4) | 484 (subsamplingY & 0x0f)); 485 486 tables[tmpOffset++] = (byte)i; // Quantization table ID 487 } 488 }; 489 } 490 491 // 492 // Initialize SOSMarker. 493 // 494 stream.mark(); 495 stream.seek(segmentOffsets[0]); 496 if(stream.read() == 0xff && stream.read() == SOS) { 497 // 498 // If the first segment starts with an SOS marker save it. 499 // 500 int SOSLength = (stream.read()<<8)|stream.read(); 501 SOSMarker = new byte[SOSLength+2]; 502 SOSMarker[0] = (byte)0xff; 503 SOSMarker[1] = (byte)SOS; 504 SOSMarker[2] = (byte)((SOSLength & 0xff00) >> 8); 505 SOSMarker[3] = (byte)(SOSLength & 0xff); 506 stream.readFully(SOSMarker, 4, SOSLength - 2); 507 } else { 508 // 509 // Manufacture an SOS marker. 510 // 511 SOSMarker = new byte[2 + 6 + 2*samplesPerPixel]; 512 int SOSMarkerIndex = 0; 513 SOSMarker[SOSMarkerIndex++] = (byte)0xff; // Marker identifier 514 SOSMarker[SOSMarkerIndex++] = (byte)SOS; 515 short sval = (short)(6 + 2*samplesPerPixel); // Length 516 SOSMarker[SOSMarkerIndex++] = (byte)((sval >>> 8) & 0xff); 517 SOSMarker[SOSMarkerIndex++] = (byte)(sval & 0xff); 518 // Number of components in scan 519 SOSMarker[SOSMarkerIndex++] = (byte)samplesPerPixel; 520 if(samplesPerPixel == 1) { 521 SOSMarker[SOSMarkerIndex++] = (byte)1; // Component ID 522 SOSMarker[SOSMarkerIndex++] = (byte)0; // Huffman table ID 523 } else { // 3 524 for(int i = 0; i < 3; i++) { 525 SOSMarker[SOSMarkerIndex++] = 526 (byte)(i + 1); // Component ID 527 SOSMarker[SOSMarkerIndex++] = 528 (byte)((i << 4) | i); // Huffman table IDs 529 } 530 }; 531 SOSMarker[SOSMarkerIndex++] = (byte)0; 532 SOSMarker[SOSMarkerIndex++] = (byte)0x3f; 533 SOSMarker[SOSMarkerIndex++] = (byte)0; 534 } 535 stream.reset(); 536 537 // Set initialization flag. 538 isInitialized = true; 539 } 540 541 // 542 // The strategy for reading cases 1-3 is to treat the data as a complete 543 // JPEG interchange stream located at JPEGStreamOffset. 544 // 545 // The strategy for cases 4-5 is to concatenate a tables stream created 546 // in initialize() with the entropy coded data in each strip or tile. 547 // 548 public void decodeRaw(byte[] b, 549 int dstOffset, 550 int bitsPerPixel, 551 int scanlineStride) throws IOException { 552 553 initialize(); 554 555 TIFFImageMetadata tim = (TIFFImageMetadata)metadata; 556 557 if(JPEGStreamOffset != null) { 558 stream.seek(JPEGStreamOffset.longValue()); 559 JPEGReader.setInput(stream, false, true); 560 } else { 561 // Determine buffer length and allocate. 562 int tableLength = tables.length; 563 int bufLength = 564 tableLength + SOSMarker.length + byteCount + 2; // 2 for EOI. 565 byte[] buf = new byte[bufLength]; 566 System.arraycopy(tables, 0, buf, 0, tableLength); 567 int bufOffset = tableLength; 568 569 // Update the SOF dimensions. 570 short sval = (short)srcHeight; // Tile/strip height 571 buf[SOFPosition + 5] = (byte)((sval >>> 8) & 0xff); 572 buf[SOFPosition + 6] = (byte)(sval & 0xff); 573 sval = (short)srcWidth; // Tile/strip width 574 buf[SOFPosition + 7] = (byte)((sval >>> 8) & 0xff); 575 buf[SOFPosition + 8] = (byte)(sval & 0xff); 576 577 // Seek to data. 578 stream.seek(offset); 579 580 // Add SOS marker if data segment does not start with one. 581 byte[] twoBytes = new byte[2]; 582 stream.readFully(twoBytes); 583 if(!((twoBytes[0]&0xff) == 0xff && (twoBytes[1]&0xff) == SOS)) { 584 // Segment does not start with SOS marker; 585 // use the pre-calculated SOS marker. 586 System.arraycopy(SOSMarker, 0, buf, bufOffset, 587 SOSMarker.length); 588 bufOffset += SOSMarker.length; 589 } 590 591 // Copy the segment data into the buffer. 592 buf[bufOffset++] = twoBytes[0]; 593 buf[bufOffset++] = twoBytes[1]; 594 stream.readFully(buf, bufOffset, byteCount - 2); 595 bufOffset += byteCount - 2; 596 597 // EOI. 598 buf[bufOffset++] = (byte)0xff; // Marker identifier 599 buf[bufOffset++] = (byte)EOI; 600 601 ByteArrayInputStream bais = 602 new ByteArrayInputStream(buf, 0, bufOffset); 603 ImageInputStream is = new MemoryCacheImageInputStream(bais); 604 605 JPEGReader.setInput(is, true, true); 606 } 607 608 // Read real image 609 JPEGParam.setDestination(rawImage); 610 JPEGReader.read(0, JPEGParam); 611 } 612 613 @SuppressWarnings("deprecation") 614 protected void finalize() throws Throwable { 615 super.finalize(); 616 JPEGReader.dispose(); 617 } 618} 619