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}