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