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