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