001/* 002 * $RCSfile: TIFFImageWriteParam.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/28 01:01:59 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.plugins.tiff; 046 047import java.util.Locale; 048 049import javax.imageio.ImageWriteParam; 050 051import com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter; 052 053/** 054 * A subclass of {@link ImageWriteParam <code>ImageWriteParam</code>} 055 * allowing control over the TIFF writing process. The set of innately 056 * supported compression types is listed in the following table: 057 * 058 * <p> 059 * <table border=1> 060 * <caption><b>Supported Compression Types</b></caption> 061 * <tr><th>Compression Type</th> <th>Description</th> <th>Reference</th></tr> 062 * <tr> 063 * <td>CCITT RLE</td> 064 * <td>Modified Huffman compression</td> 065 * <td>TIFF 6.0 Specification, Section 10</td> 066 * </tr> 067 * <tr> 068 * <td>CCITT T.4</td> 069 * <td>CCITT T.4 bilevel encoding/Group 3 facsimile compression</td> 070 * <td>TIFF 6.0 Specification, Section 11</td> 071 * </tr> 072 * <tr> 073 * <td>CCITT T.6</td> 074 * <td>CCITT T.6 bilevel encoding/Group 4 facsimile compression</td> 075 * <td>TIFF 6.0 Specification, Section 11</td></tr> 076 * <tr> 077 * <td>LZW</td> 078 * <td>LZW compression</td> 079 * <td>TIFF 6.0 Specification, Section 13</td></tr> 080 * <tr> 081 * <td>JPEG</td> 082 * <td>"New" JPEG-in-TIFF compression</td> 083 * <td><a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">TIFF 084 * Technical Note #2</a></td> 085 * </tr> 086 * <tr> 087 * <td>ZLib</td> 088 * <td>"Deflate/Inflate" compression (see note following this table)</td> 089 * <td><a href="http://partners.adobe.com/asn/developer/pdfs/tn/TIFFphotoshop.pdf"> 090 * Adobe Photoshop® TIFF Technical Notes</a> (PDF)</td> 091 * </tr> 092 * <tr> 093 * <td>PackBits</td> 094 * <td>Byte-oriented, run length compression</td> 095 * <td>TIFF 6.0 Specification, Section 9</td> 096 * </tr> 097 * <tr> 098 * <td>Deflate</td> 099 * <td>"Zip-in-TIFF" compression (see note following this table)</td> 100 * <td><a href="http://www.isi.edu/in-notes/rfc1950.txt"> 101 * ZLIB Compressed Data Format Specification</a>, 102 * <a href="http://www.isi.edu/in-notes/rfc1951.txt"> 103 * DEFLATE Compressed Data Format Specification</a></td> 104 * </tr> 105 * <tr> 106 * <td>EXIF JPEG</td> 107 * <td>EXIF-specific JPEG compression (see note following this table)</td> 108 * <td><a href="http://www.exif.org/Exif2-2.PDF">EXIF 2.2 Specification</a> 109 * (PDF), section 4.5.5, "Basic Structure of Thumbnail Data"</td> 110 * </table> 111 * </p> 112 * <p> 113 * Old-style JPEG compression as described in section 22 of the TIFF 6.0 114 * Specification is <i>not</i> supported. 115 * </p> 116 * 117 * <p> The CCITT compression types are applicable to bilevel (1-bit) 118 * images only. The JPEG compression type is applicable to byte 119 * grayscale (1-band) and RGB (3-band) images only.</p> 120 * 121 * <p> 122 * ZLib and Deflate compression are identical except for the value of the 123 * TIFF Compression field: for ZLib the Compression field has value 8 124 * whereas for Deflate it has value 32946 (0x80b2). In both cases each 125 * image segment (strip or tile) is written as a single complete zlib data 126 * stream. 127 * </p> 128 * 129 * <p> 130 * "EXIF JPEG" is a compression type used when writing the contents of an 131 * APP1 EXIF marker segment for inclusion in a JPEG native image metadata 132 * tree. The contents appended to the output when this compression type is 133 * used are a function of whether an empty or non-empty image is written. 134 * If the image is empty, then a TIFF IFD adhering to the specification of 135 * a compressed EXIF primary IFD is appended. If the image is non-empty, 136 * then a complete IFD and image adhering to the specification of a 137 * compressed EXIF thumbnail IFD and image are appended. Note that the 138 * data of the empty image may <i>not</i> later be appended using the pixel 139 * replacement capability of the TIFF writer. 140 * </p> 141 * 142 * <p> If ZLib/Deflate or JPEG compression is used, the compression quality 143 * may be set. For ZLib/Deflate the supplied floating point quality value is 144 * rescaled to the range <tt>[1, 9]</tt> and truncated to an integer 145 * to derive the Deflate compression level. For JPEG the floating point 146 * quality value is passed directly to the JPEG writer plug-in which 147 * interprets it in the usual way.</p> 148 * 149 * <p> The <code>canWriteTiles</code> and 150 * <code>canWriteCompressed</code> methods will return 151 * <code>true</code>; the <code>canOffsetTiles</code> and 152 * <code>canWriteProgressive</code> methods will return 153 * <code>false</code>.</p> 154 * 155 * <p> If tiles are being written, then each of their dimensions will be 156 * rounded to the nearest multiple of 16 per the TIFF specification. If 157 * JPEG-in-TIFF compression is being used, and tiles are being written 158 * each tile dimension will be rounded to the nearest multiple of 8 times 159 * the JPEG minimum coded unit (MCU) in that dimension. If JPEG-in-TIFF 160 * compression is being used and strips are being written, the number of 161 * rows per strip is rounded to a multiple of 8 times the maximum MCU over 162 * both dimensions.</p> 163 */ 164public class TIFFImageWriteParam extends ImageWriteParam { 165 166 TIFFCompressor compressor = null; 167 168 TIFFColorConverter colorConverter = null; 169 int photometricInterpretation; 170 171 private boolean appendedCompressionType = false; 172 173 /** 174 * Constructs a <code>TIFFImageWriteParam</code> instance 175 * for a given <code>Locale</code>. 176 * 177 * @param locale the <code>Locale</code> for which messages 178 * should be localized. 179 */ 180 public TIFFImageWriteParam(Locale locale) { 181 super(locale); 182 this.canWriteCompressed = true; 183 this.canWriteTiles = true; 184 this.compressionTypes = TIFFImageWriter.TIFFCompressionTypes; 185 }; 186 187 public boolean isCompressionLossless() { 188 if (getCompressionMode() != MODE_EXPLICIT) { 189 throw new IllegalStateException 190 ("Compression mode not MODE_EXPLICIT!"); 191 } 192 193 if (compressionType == null) { 194 throw new IllegalStateException("No compression type set!"); 195 } 196 197 if(compressor != null && 198 compressionType.equals(compressor.getCompressionType())) { 199 return compressor.isCompressionLossless(); 200 } 201 202 for (int i = 0; i < compressionTypes.length; i++) { 203 if (compressionType.equals(compressionTypes[i])) { 204 return TIFFImageWriter.isCompressionLossless[i]; 205 } 206 } 207 208 return false; 209 } 210 211 /** 212 * Sets the <code>TIFFCompressor</code> object to be used by the 213 * <code>ImageWriter</code> to encode each image strip or tile. 214 * A value of <code>null</code> allows the writer to choose its 215 * own TIFFCompressor. 216 * 217 * <p>Note that invoking this method is not sufficient to set 218 * the compression type: 219 * {@link ImageWriteParam#setCompressionType(String) <code>setCompressionType()</code>} 220 * must be invoked explicitly for this purpose. The following 221 * code illustrates the correct procedure: 222 * <pre> 223 * TIFFImageWriteParam writeParam; 224 * TIFFCompressor compressor; 225 * writeParam.setCompressionMode(writeParam.MODE_EXPLICIT); 226 * writeParam.setTIFFCompressor(compressor); 227 * writeParam.setCompressionType(compressor.getCompressionType()); 228 * </pre> 229 * If <code>compressionType</code> is set to a value different from 230 * that supported by the <code>TIFFCompressor</code> then the 231 * compressor object will not be used. 232 * </p> 233 * 234 * <p>If the compression type supported by the supplied 235 * <code>TIFFCompressor</code> is not among those in 236 * {@link ImageWriteParam#compressionTypes <code>compressionTypes</code>}, 237 * then it will be appended to this array after removing any previously 238 * appended compression type. If <code>compressor</code> is 239 * <code>null</code> this will also cause any previously appended 240 * type to be removed from the array.</p> 241 * 242 * @param compressor the <code>TIFFCompressor</code> to be 243 * used for encoding, or <code>null</code> to allow the writer to 244 * choose its own. 245 * 246 * @throws IllegalStateException if the compression mode is not 247 * <code>MODE_EXPLICIT</code>. 248 * 249 * @see #getTIFFCompressor 250 */ 251 public void setTIFFCompressor(TIFFCompressor compressor) { 252 if (getCompressionMode() != MODE_EXPLICIT) { 253 throw new IllegalStateException 254 ("Compression mode not MODE_EXPLICIT!"); 255 } 256 257 this.compressor = compressor; 258 259 if(appendedCompressionType) { 260 // Remove previously appended compression type. 261 int len = compressionTypes.length - 1; 262 String[] types = new String[len]; 263 System.arraycopy(compressionTypes, 0, types, 0, len); 264 compressionTypes = types; 265 appendedCompressionType = false; 266 } 267 268 if(compressor != null) { 269 // Check whether compressor's type is already in the list. 270 String compressorType = compressor.getCompressionType(); 271 int len = compressionTypes.length; 272 boolean appendCompressionType = true; 273 for(int i = 0; i < len; i++) { 274 if(compressorType.equals(compressionTypes[i])) { 275 appendCompressionType = false; 276 break; 277 } 278 } 279 280 if(appendCompressionType) { 281 // Compressor's compression type not in the list; append it. 282 String[] types = new String[len + 1]; 283 System.arraycopy(compressionTypes, 0, types, 0, len); 284 types[len] = compressorType; 285 compressionTypes = types; 286 appendedCompressionType = true; 287 } 288 } 289 } 290 291 /** 292 * Returns the <code>TIFFCompressor</code> that is currently set 293 * to be used by the <code>ImageWriter</code> to encode each image 294 * strip or tile, or <code>null</code> if none has been set. 295 * 296 * @return compressor the <code>TIFFCompressor</code> to be 297 * used for encoding, or <code>null</code> if none has been set 298 * (allowing the writer to choose its own). 299 * 300 * @throws IllegalStateException if the compression mode is not 301 * <code>MODE_EXPLICIT</code>. 302 * 303 * @see #setTIFFCompressor(TIFFCompressor) 304 */ 305 public TIFFCompressor getTIFFCompressor() { 306 if (getCompressionMode() != MODE_EXPLICIT) { 307 throw new IllegalStateException 308 ("Compression mode not MODE_EXPLICIT!"); 309 } 310 return this.compressor; 311 } 312 313 /** 314 * Sets the <code>TIFFColorConverter</code> object describing the 315 * color space to which the input data should be converted for 316 * storage in the input stream. In addition, the value to be 317 * written to the <code>PhotometricInterpretation</code> tag is 318 * supplied. 319 * 320 * @param colorConverter a <code>TIFFColorConverter</code> object, 321 * or <code>null</code>. 322 * @param photometricInterpretation the value to be written to the 323 * <code>PhotometricInterpretation</code> tag in the root IFD. 324 * 325 * @see #getColorConverter 326 * @see #getPhotometricInterpretation 327 */ 328 public void setColorConverter(TIFFColorConverter colorConverter, 329 int photometricInterpretation) { 330 this.colorConverter = colorConverter; 331 this.photometricInterpretation = photometricInterpretation; 332 } 333 334 /** 335 * Returns the current <code>TIFFColorConverter</code> object that 336 * will be used to perform color conversion when writing the 337 * image, or <code>null</code> if none is set. 338 * 339 * @return a <code>TIFFColorConverter</code> object, or 340 * <code>null</code>. 341 * 342 * @see #setColorConverter(TIFFColorConverter, int) 343 */ 344 public TIFFColorConverter getColorConverter() { 345 return colorConverter; 346 } 347 348 /** 349 * Returns the current value that will be written to the 350 * <code>Photometricinterpretation</code> tag. This method should 351 * only be called if a value has been set using the 352 * <code>setColorConverter</code> method. 353 * 354 * @return an <code>int</code> to be used as the value of the 355 * <code>PhotometricInterpretation</code> tag. 356 * 357 * @see #setColorConverter(TIFFColorConverter, int) 358 * 359 * @throws IllegalStateException if no value is set. 360 */ 361 public int getPhotometricInterpretation() { 362 if (colorConverter == null) { 363 throw new IllegalStateException("Color converter not set!"); 364 } 365 return photometricInterpretation; 366 } 367 368 /** 369 * Removes any currently set <code>ColorConverter</code> object and 370 * <code>PhotometricInterpretation</code> tag value. 371 * 372 * @see #setColorConverter(TIFFColorConverter, int) 373 */ 374 public void unsetColorConverter() { 375 this.colorConverter = null; 376 } 377}