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}