001: /*
002: * $RCSfile: TIFFJPEGCompressor.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.3 $
042: * $Date: 2006/04/11 22:10:36 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.tiff;
046:
047: import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
048: import com.sun.media.imageio.plugins.tiff.TIFFField;
049: import com.sun.media.imageio.plugins.tiff.TIFFTag;
050: import java.io.ByteArrayInputStream;
051: import java.io.ByteArrayOutputStream;
052: import java.util.Iterator;
053: import javax.imageio.ImageReader;
054: import javax.imageio.ImageWriteParam;
055: import javax.imageio.metadata.IIOMetadata;
056: import javax.imageio.spi.IIORegistry;
057: import javax.imageio.spi.ImageReaderSpi;
058: import javax.imageio.spi.ServiceRegistry;
059: import javax.imageio.stream.MemoryCacheImageInputStream;
060: import javax.imageio.stream.MemoryCacheImageOutputStream;
061:
062: /**
063: * Compressor for encoding compression type 7, TTN2/Adobe JPEG-in-TIFF.
064: */
065: public class TIFFJPEGCompressor extends TIFFBaseJPEGCompressor {
066:
067: private static final boolean DEBUG = false; // XXX false for release.
068:
069: // Subsampling factor for chroma bands (Cb Cr).
070: static final int CHROMA_SUBSAMPLING = 2;
071:
072: /**
073: * A filter which identifies the ImageReaderSpi of a JPEG reader
074: * which supports JPEG native stream metadata.
075: */
076: private static class JPEGSPIFilter implements
077: ServiceRegistry.Filter {
078: JPEGSPIFilter() {
079: }
080:
081: public boolean filter(Object provider) {
082: ImageReaderSpi readerSPI = (ImageReaderSpi) provider;
083:
084: if (readerSPI != null) {
085: String streamMetadataName = readerSPI
086: .getNativeStreamMetadataFormatName();
087: if (streamMetadataName != null) {
088: return streamMetadataName
089: .equals(STREAM_METADATA_NAME);
090: } else {
091: return false;
092: }
093: }
094:
095: return false;
096: }
097: }
098:
099: /**
100: * Retrieves a JPEG reader which supports native JPEG stream metadata.
101: */
102: private static ImageReader getJPEGTablesReader() {
103: ImageReader jpegReader = null;
104:
105: try {
106: IIORegistry registry = IIORegistry.getDefaultInstance();
107: Class imageReaderClass = Class
108: .forName("javax.imageio.spi.ImageReaderSpi");
109: Iterator readerSPIs = registry.getServiceProviders(
110: imageReaderClass, new JPEGSPIFilter(), true);
111: if (readerSPIs.hasNext()) {
112: ImageReaderSpi jpegReaderSPI = (ImageReaderSpi) readerSPIs
113: .next();
114: jpegReader = jpegReaderSPI.createReaderInstance();
115: }
116: } catch (Exception e) {
117: // Ignore it ...
118: }
119:
120: return jpegReader;
121: }
122:
123: public TIFFJPEGCompressor(ImageWriteParam param) {
124: super ("JPEG", BaselineTIFFTagSet.COMPRESSION_JPEG, false, param);
125: }
126:
127: /**
128: * Sets the value of the <code>metadata</code> field.
129: *
130: * <p>The implementation in this class also adds the TIFF fields
131: * JPEGTables, YCbCrSubSampling, YCbCrPositioning, and
132: * ReferenceBlackWhite superseding any prior settings of those
133: * fields.</p>
134: *
135: * @param metadata the <code>IIOMetadata</code> object for the
136: * image being written.
137: *
138: * @see #getMetadata()
139: */
140: public void setMetadata(IIOMetadata metadata) {
141: super .setMetadata(metadata);
142:
143: if (metadata instanceof TIFFImageMetadata) {
144: TIFFImageMetadata tim = (TIFFImageMetadata) metadata;
145: TIFFIFD rootIFD = tim.getRootIFD();
146: BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
147:
148: TIFFField f = tim
149: .getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
150: int numBands = f.getAsInt(0);
151:
152: if (numBands == 1) {
153: // Remove YCbCr fields not relevant for grayscale.
154:
155: rootIFD
156: .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
157: rootIFD
158: .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
159: rootIFD
160: .removeTIFFField(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE);
161: } else { // numBands == 3
162: // Replace YCbCr fields.
163:
164: // YCbCrSubSampling
165: TIFFField YCbCrSubSamplingField = new TIFFField(
166: base
167: .getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING),
168: TIFFTag.TIFF_SHORT, 2,
169: new char[] { CHROMA_SUBSAMPLING,
170: CHROMA_SUBSAMPLING });
171: rootIFD.addTIFFField(YCbCrSubSamplingField);
172:
173: // YCbCrPositioning
174: TIFFField YCbCrPositioningField = new TIFFField(
175: base
176: .getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
177: TIFFTag.TIFF_SHORT,
178: 1,
179: new char[] { BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED });
180: rootIFD.addTIFFField(YCbCrPositioningField);
181:
182: // ReferenceBlackWhite
183: TIFFField referenceBlackWhiteField = new TIFFField(
184: base
185: .getTag(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE),
186: TIFFTag.TIFF_RATIONAL, 6, new long[][] { // no headroon/footroom
187: { 0, 1 }, { 255, 1 }, { 128, 1 }, { 255, 1 },
188: { 128, 1 }, { 255, 1 } });
189: rootIFD.addTIFFField(referenceBlackWhiteField);
190: }
191:
192: // JPEGTables field is written if and only if one is
193: // already present in the metadata. If one is present
194: // and has either zero length or does not represent a
195: // valid tables-only stream, then a JPEGTables field
196: // will be written initialized to the standard tables-
197: // only stream written by the JPEG writer.
198:
199: // Retrieve the JPEGTables field.
200: TIFFField JPEGTablesField = tim
201: .getTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
202:
203: // Initialize JPEG writer to one supporting abbreviated streams.
204: if (JPEGTablesField != null) {
205: // Intialize the JPEG writer to one that supports stream
206: // metadata, i.e., abbreviated streams, and may or may not
207: // support image metadata.
208: initJPEGWriter(true, false);
209: }
210:
211: // Write JPEGTables field if a writer supporting abbreviated
212: // streams was available.
213: if (JPEGTablesField != null && JPEGWriter != null) {
214: if (DEBUG)
215: System.out.println("Has JPEGTables ...");
216:
217: // Set the abbreviated stream flag.
218: this .writeAbbreviatedStream = true;
219:
220: //Branch based on field value count.
221: if (JPEGTablesField.getCount() > 0) {
222: if (DEBUG)
223: System.out.println("JPEGTables > 0");
224:
225: // Derive the stream metadata from the field.
226:
227: // Get the field values.
228: byte[] tables = JPEGTablesField.getAsBytes();
229:
230: // Create an input stream for the tables.
231: ByteArrayInputStream bais = new ByteArrayInputStream(
232: tables);
233: MemoryCacheImageInputStream iis = new MemoryCacheImageInputStream(
234: bais);
235:
236: // Read the tables stream using the JPEG reader.
237: ImageReader jpegReader = getJPEGTablesReader();
238: jpegReader.setInput(iis);
239:
240: // Initialize the stream metadata object.
241: try {
242: JPEGStreamMetadata = jpegReader
243: .getStreamMetadata();
244: } catch (Exception e) {
245: // Fall back to default tables.
246: JPEGStreamMetadata = null;
247: } finally {
248: jpegReader.reset();
249: }
250: if (DEBUG)
251: System.out.println(JPEGStreamMetadata);
252: }
253:
254: if (JPEGStreamMetadata == null) {
255: if (DEBUG)
256: System.out.println("JPEGTables == 0");
257:
258: // Derive the field from default stream metadata.
259:
260: // Get default stream metadata.
261: JPEGStreamMetadata = JPEGWriter
262: .getDefaultStreamMetadata(JPEGParam);
263:
264: // Create an output stream for the tables.
265: ByteArrayOutputStream tableByteStream = new ByteArrayOutputStream();
266: MemoryCacheImageOutputStream tableStream = new MemoryCacheImageOutputStream(
267: tableByteStream);
268:
269: // Write a tables-only stream.
270: JPEGWriter.setOutput(tableStream);
271: try {
272: JPEGWriter
273: .prepareWriteSequence(JPEGStreamMetadata);
274: tableStream.flush();
275: JPEGWriter.endWriteSequence();
276:
277: // Get the tables-only stream content.
278: byte[] tables = tableByteStream.toByteArray();
279: if (DEBUG)
280: System.out.println("tables.length = "
281: + tables.length);
282:
283: // Add the JPEGTables field.
284: JPEGTablesField = new TIFFField(
285: base
286: .getTag(BaselineTIFFTagSet.TAG_JPEG_TABLES),
287: TIFFTag.TIFF_UNDEFINED, tables.length,
288: tables);
289: rootIFD.addTIFFField(JPEGTablesField);
290: } catch (Exception e) {
291: // Do not write JPEGTables field.
292: rootIFD
293: .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
294: this .writeAbbreviatedStream = false;
295: }
296: }
297: } else { // Do not write JPEGTables field.
298: // Remove any field present.
299: rootIFD
300: .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
301:
302: // Initialize the writer preferring codecLib.
303: initJPEGWriter(false, false);
304: }
305: }
306: }
307: }
|