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