Source Code Cross Referenced for TIFFImageWriter.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » com » sun » media » imageioimpl » plugins » tiff » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » Java Advanced Imaging » com.sun.media.imageioimpl.plugins.tiff 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: TIFFImageWriter.java,v $
0003:         *
0004:         * 
0005:         * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
0006:         * 
0007:         * Redistribution and use in source and binary forms, with or without
0008:         * modification, are permitted provided that the following conditions
0009:         * are met: 
0010:         * 
0011:         * - Redistribution of source code must retain the above copyright 
0012:         *   notice, this  list of conditions and the following disclaimer.
0013:         * 
0014:         * - Redistribution in binary form must reproduce the above copyright
0015:         *   notice, this list of conditions and the following disclaimer in 
0016:         *   the documentation and/or other materials provided with the
0017:         *   distribution.
0018:         * 
0019:         * Neither the name of Sun Microsystems, Inc. or the names of 
0020:         * contributors may be used to endorse or promote products derived 
0021:         * from this software without specific prior written permission.
0022:         * 
0023:         * This software is provided "AS IS," without a warranty of any 
0024:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
0025:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
0026:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027:         * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
0028:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
0029:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
0031:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035:         * POSSIBILITY OF SUCH DAMAGES. 
0036:         * 
0037:         * You acknowledge that this software is not designed or intended for 
0038:         * use in the design, construction, operation or maintenance of any 
0039:         * nuclear facility. 
0040:         *
0041:         * $Revision: 1.24 $
0042:         * $Date: 2007/09/01 00:27:20 $
0043:         * $State: Exp $
0044:         */
0045:        package com.sun.media.imageioimpl.plugins.tiff;
0046:
0047:        import java.awt.Point;
0048:        import java.awt.Rectangle;
0049:        import java.awt.color.ColorSpace;
0050:        import java.awt.color.ICC_ColorSpace;
0051:        import java.awt.image.BufferedImage;
0052:        import java.awt.image.ColorModel;
0053:        import java.awt.image.ComponentSampleModel;
0054:        import java.awt.image.DataBuffer;
0055:        import java.awt.image.DataBufferByte;
0056:        import java.awt.image.IndexColorModel;
0057:        import java.awt.image.MultiPixelPackedSampleModel;
0058:        import java.awt.image.RenderedImage;
0059:        import java.awt.image.Raster;
0060:        import java.awt.image.SampleModel;
0061:        import java.awt.image.WritableRaster;
0062:        import java.io.EOFException;
0063:        import java.io.IOException;
0064:        import java.nio.ByteOrder;
0065:        import java.util.ArrayList;
0066:        import java.util.Arrays;
0067:        import java.util.List;
0068:        import javax.imageio.IIOException;
0069:        import javax.imageio.IIOImage;
0070:        import javax.imageio.ImageWriteParam;
0071:        import javax.imageio.ImageWriter;
0072:        import javax.imageio.ImageTypeSpecifier;
0073:        import javax.imageio.metadata.IIOInvalidTreeException;
0074:        import javax.imageio.metadata.IIOMetadata;
0075:        import javax.imageio.metadata.IIOMetadataFormatImpl;
0076:        import javax.imageio.spi.ImageWriterSpi;
0077:        import javax.imageio.stream.ImageOutputStream;
0078:        import org.w3c.dom.Node;
0079:        import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
0080:        import com.sun.media.imageio.plugins.tiff.EXIFParentTIFFTagSet;
0081:        import com.sun.media.imageio.plugins.tiff.EXIFTIFFTagSet;
0082:        import com.sun.media.imageio.plugins.tiff.TIFFColorConverter;
0083:        import com.sun.media.imageio.plugins.tiff.TIFFCompressor;
0084:        import com.sun.media.imageio.plugins.tiff.TIFFField;
0085:        import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;
0086:        import com.sun.media.imageio.plugins.tiff.TIFFTag;
0087:        import com.sun.media.imageio.plugins.tiff.TIFFTagSet;
0088:        import com.sun.media.imageioimpl.common.ImageUtil;
0089:        import com.sun.media.imageioimpl.common.PackageUtil;
0090:        import com.sun.media.imageioimpl.common.SimpleRenderedImage;
0091:        import com.sun.media.imageioimpl.common.SingleTileRenderedImage;
0092:
0093:        public class TIFFImageWriter extends ImageWriter {
0094:
0095:            private static final boolean DEBUG = false; // XXX false for release!
0096:
0097:            static final String EXIF_JPEG_COMPRESSION_TYPE = "EXIF JPEG";
0098:
0099:            public static final int DEFAULT_BYTES_PER_STRIP = 8192;
0100:
0101:            /**
0102:             * Supported TIFF compression types.
0103:             */
0104:            public static final String[] TIFFCompressionTypes = { "CCITT RLE",
0105:                    "CCITT T.4", "CCITT T.6", "LZW",
0106:                    // "Old JPEG",
0107:                    "JPEG", "ZLib", "PackBits", "Deflate",
0108:                    EXIF_JPEG_COMPRESSION_TYPE };
0109:
0110:            //
0111:            // !!! The lengths of the arrays 'compressionTypes',
0112:            // !!! 'isCompressionLossless', and 'compressionNumbers'
0113:            // !!! must be equal.
0114:            //
0115:
0116:            /**
0117:             * Known TIFF compression types.
0118:             */
0119:            public static final String[] compressionTypes = { "CCITT RLE",
0120:                    "CCITT T.4", "CCITT T.6", "LZW", "Old JPEG", "JPEG",
0121:                    "ZLib", "PackBits", "Deflate", EXIF_JPEG_COMPRESSION_TYPE };
0122:
0123:            /**
0124:             * Lossless flag for known compression types.
0125:             */
0126:            public static final boolean[] isCompressionLossless = { true, // RLE
0127:                    true, // T.4
0128:                    true, // T.6
0129:                    true, // LZW
0130:                    false, // Old JPEG
0131:                    false, // JPEG
0132:                    true, // ZLib
0133:                    true, // PackBits
0134:                    true, // DEFLATE
0135:                    false // EXIF JPEG
0136:            };
0137:
0138:            /**
0139:             * Compression tag values for known compression types.
0140:             */
0141:            public static final int[] compressionNumbers = {
0142:                    BaselineTIFFTagSet.COMPRESSION_CCITT_RLE,
0143:                    BaselineTIFFTagSet.COMPRESSION_CCITT_T_4,
0144:                    BaselineTIFFTagSet.COMPRESSION_CCITT_T_6,
0145:                    BaselineTIFFTagSet.COMPRESSION_LZW,
0146:                    BaselineTIFFTagSet.COMPRESSION_OLD_JPEG,
0147:                    BaselineTIFFTagSet.COMPRESSION_JPEG,
0148:                    BaselineTIFFTagSet.COMPRESSION_ZLIB,
0149:                    BaselineTIFFTagSet.COMPRESSION_PACKBITS,
0150:                    BaselineTIFFTagSet.COMPRESSION_DEFLATE,
0151:                    BaselineTIFFTagSet.COMPRESSION_OLD_JPEG, // EXIF JPEG
0152:            };
0153:
0154:            ImageOutputStream stream;
0155:            long headerPosition;
0156:            RenderedImage image;
0157:            ImageTypeSpecifier imageType;
0158:            ByteOrder byteOrder;
0159:            ImageWriteParam param;
0160:            TIFFCompressor compressor;
0161:            TIFFColorConverter colorConverter;
0162:
0163:            TIFFStreamMetadata streamMetadata;
0164:            TIFFImageMetadata imageMetadata;
0165:
0166:            int sourceXOffset;
0167:            int sourceYOffset;
0168:            int sourceWidth;
0169:            int sourceHeight;
0170:            int[] sourceBands;
0171:            int periodX;
0172:            int periodY;
0173:
0174:            int bitDepth; // bits per channel
0175:            int numBands;
0176:            int tileWidth;
0177:            int tileLength;
0178:            int tilesAcross;
0179:            int tilesDown;
0180:
0181:            int[] sampleSize = null; // Input sample size per band, in bits
0182:            int scalingBitDepth = -1; // Output bit depth of the scaling tables
0183:            boolean isRescaling = false; // Whether rescaling is needed.
0184:
0185:            boolean isBilevel; // Whether image is bilevel
0186:            boolean isImageSimple; // Whether image can be copied into directly
0187:            boolean isInverted; // Whether photometric inversion is required
0188:
0189:            boolean isTiled; // Whether the image is tiled (true) or stipped (false).
0190:
0191:            int nativePhotometricInterpretation;
0192:            int photometricInterpretation;
0193:
0194:            char[] bitsPerSample; // Output sample size per band
0195:            int sampleFormat = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED; // Output sample format
0196:
0197:            // Tables for 1, 2, 4, or 8 bit output
0198:            byte[][] scale = null; // 8 bit table
0199:            byte[] scale0 = null; // equivalent to scale[0]
0200:
0201:            // Tables for 16 bit output
0202:            byte[][] scaleh = null; // High bytes of output
0203:            byte[][] scalel = null; // Low bytes of output
0204:
0205:            int compression;
0206:            int predictor;
0207:
0208:            int totalPixels;
0209:            int pixelsDone;
0210:
0211:            long nextIFDPointerPos;
0212:
0213:            // Next available space.
0214:            long nextSpace = 0L;
0215:
0216:            // Whether a sequence is being written.
0217:            boolean isWritingSequence = false;
0218:
0219:            /**
0220:             * Converts a pixel's X coordinate into a horizontal tile index
0221:             * relative to a given tile grid layout specified by its X offset
0222:             * and tile width.
0223:             *
0224:             * <p> If <code>tileWidth < 0</code>, the results of this method
0225:             * are undefined.  If <code>tileWidth == 0</code>, an
0226:             * <code>ArithmeticException</code> will be thrown.
0227:             *
0228:             * @throws ArithmeticException  If <code>tileWidth == 0</code>.
0229:             */
0230:            public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
0231:                x -= tileGridXOffset;
0232:                if (x < 0) {
0233:                    x += 1 - tileWidth; // force round to -infinity (ceiling)
0234:                }
0235:                return x / tileWidth;
0236:            }
0237:
0238:            /**
0239:             * Converts a pixel's Y coordinate into a vertical tile index
0240:             * relative to a given tile grid layout specified by its Y offset
0241:             * and tile height.
0242:             *
0243:             * <p> If <code>tileHeight < 0</code>, the results of this method
0244:             * are undefined.  If <code>tileHeight == 0</code>, an
0245:             * <code>ArithmeticException</code> will be thrown.
0246:             *
0247:             * @throws ArithmeticException  If <code>tileHeight == 0</code>.
0248:             */
0249:            public static int YToTileY(int y, int tileGridYOffset,
0250:                    int tileHeight) {
0251:                y -= tileGridYOffset;
0252:                if (y < 0) {
0253:                    y += 1 - tileHeight; // force round to -infinity (ceiling)
0254:                }
0255:                return y / tileHeight;
0256:            }
0257:
0258:            public TIFFImageWriter(ImageWriterSpi originatingProvider) {
0259:                super (originatingProvider);
0260:            }
0261:
0262:            public ImageWriteParam getDefaultWriteParam() {
0263:                return new TIFFImageWriteParam(getLocale());
0264:            }
0265:
0266:            public void setOutput(Object output) {
0267:                super .setOutput(output);
0268:
0269:                if (output != null) {
0270:                    if (!(output instanceof  ImageOutputStream)) {
0271:                        throw new IllegalArgumentException(
0272:                                "output not an ImageOutputStream!");
0273:                    }
0274:                    this .stream = (ImageOutputStream) output;
0275:
0276:                    //
0277:                    // The output is expected to be positioned at a TIFF header
0278:                    // or at some arbitrary location which may or may not be
0279:                    // the EOF. In the former case the writer should be able
0280:                    // either to overwrite the existing sequence or append to it.
0281:                    //
0282:
0283:                    // Set the position of the header and the next available space.
0284:                    try {
0285:                        headerPosition = this .stream.getStreamPosition();
0286:                        try {
0287:                            // Read byte order and magic number.
0288:                            byte[] b = new byte[4];
0289:                            stream.readFully(b);
0290:
0291:                            // Check bytes for TIFF header.
0292:                            if ((b[0] == (byte) 0x49 && b[1] == (byte) 0x49
0293:                                    && b[2] == (byte) 0x2a && b[3] == (byte) 0x00)
0294:                                    || (b[0] == (byte) 0x4d
0295:                                            && b[1] == (byte) 0x4d
0296:                                            && b[2] == (byte) 0x00 && b[3] == (byte) 0x2a)) {
0297:                                // TIFF header.
0298:                                this .nextSpace = stream.length();
0299:                            } else {
0300:                                // Neither TIFF header nor EOF: overwrite.
0301:                                this .nextSpace = headerPosition;
0302:                            }
0303:                        } catch (IOException io) { // thrown by readFully()
0304:                            // At EOF or not at a TIFF header.
0305:                            this .nextSpace = headerPosition;
0306:                        }
0307:                        stream.seek(headerPosition);
0308:                    } catch (IOException ioe) { // thrown by getStreamPosition()
0309:                        // Assume it's at zero.
0310:                        this .nextSpace = headerPosition = 0L;
0311:                    }
0312:                } else {
0313:                    this .stream = null;
0314:                }
0315:            }
0316:
0317:            public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
0318:                return new TIFFStreamMetadata();
0319:            }
0320:
0321:            public IIOMetadata getDefaultImageMetadata(
0322:                    ImageTypeSpecifier imageType, ImageWriteParam param) {
0323:
0324:                List tagSets = new ArrayList(1);
0325:                tagSets.add(BaselineTIFFTagSet.getInstance());
0326:                // XXX Should add Fax/EXIF/GeoTIFF TagSets?
0327:                TIFFImageMetadata imageMetadata = new TIFFImageMetadata(tagSets);
0328:
0329:                if (imageType != null) {
0330:                    TIFFImageMetadata im = (TIFFImageMetadata) convertImageMetadata(
0331:                            imageMetadata, imageType, param);
0332:                    if (im != null) {
0333:                        imageMetadata = im;
0334:                    }
0335:                }
0336:
0337:                return imageMetadata;
0338:            }
0339:
0340:            public IIOMetadata convertStreamMetadata(IIOMetadata inData,
0341:                    ImageWriteParam param) {
0342:                // Check arguments.
0343:                if (inData == null) {
0344:                    throw new IllegalArgumentException("inData == null!");
0345:                }
0346:
0347:                // Note: param is irrelevant as it does not contain byte order.
0348:
0349:                TIFFStreamMetadata outData = null;
0350:                if (inData instanceof  TIFFStreamMetadata) {
0351:                    outData = new TIFFStreamMetadata();
0352:                    outData.byteOrder = ((TIFFStreamMetadata) inData).byteOrder;
0353:                    return outData;
0354:                } else if (Arrays.asList(inData.getMetadataFormatNames())
0355:                        .contains(TIFFStreamMetadata.nativeMetadataFormatName)) {
0356:                    outData = new TIFFStreamMetadata();
0357:                    String format = TIFFStreamMetadata.nativeMetadataFormatName;
0358:                    try {
0359:                        outData.mergeTree(format, inData.getAsTree(format));
0360:                    } catch (IIOInvalidTreeException e) {
0361:                        // XXX Warning
0362:                    }
0363:                }
0364:
0365:                return outData;
0366:            }
0367:
0368:            public IIOMetadata convertImageMetadata(IIOMetadata inData,
0369:                    ImageTypeSpecifier imageType, ImageWriteParam param) {
0370:                // Check arguments.
0371:                if (inData == null) {
0372:                    throw new IllegalArgumentException("inData == null!");
0373:                }
0374:                if (imageType == null) {
0375:                    throw new IllegalArgumentException("imageType == null!");
0376:                }
0377:
0378:                TIFFImageMetadata outData = null;
0379:
0380:                // Obtain a TIFFImageMetadata object.
0381:                if (inData instanceof  TIFFImageMetadata) {
0382:                    // Create a new metadata object from a clone of the input IFD.
0383:                    TIFFIFD inIFD = ((TIFFImageMetadata) inData).getRootIFD();
0384:                    outData = new TIFFImageMetadata(inIFD.getShallowClone());
0385:                } else if (Arrays.asList(inData.getMetadataFormatNames())
0386:                        .contains(TIFFImageMetadata.nativeMetadataFormatName)) {
0387:                    // Initialize from the native metadata form of the input tree.
0388:                    try {
0389:                        outData = convertNativeImageMetadata(inData);
0390:                    } catch (IIOInvalidTreeException e) {
0391:                        // XXX Warning
0392:                    }
0393:                } else if (inData.isStandardMetadataFormatSupported()) {
0394:                    // Initialize from the standard metadata form of the input tree.
0395:                    try {
0396:                        outData = convertStandardImageMetadata(inData);
0397:                    } catch (IIOInvalidTreeException e) {
0398:                        // XXX Warning
0399:                    }
0400:                }
0401:
0402:                // Update the metadata per the image type and param.
0403:                if (outData != null) {
0404:                    TIFFImageWriter bogusWriter = new TIFFImageWriter(
0405:                            this .originatingProvider);
0406:                    bogusWriter.imageMetadata = outData;
0407:                    bogusWriter.param = param;
0408:                    SampleModel sm = imageType.getSampleModel();
0409:                    try {
0410:                        bogusWriter.setupMetadata(imageType.getColorModel(),
0411:                                sm, sm.getWidth(), sm.getHeight());
0412:                        return bogusWriter.imageMetadata;
0413:                    } catch (IIOException e) {
0414:                        // XXX Warning
0415:                        return null;
0416:                    }
0417:                }
0418:
0419:                return outData;
0420:            }
0421:
0422:            /**
0423:             * Converts a standard <code>javax_imageio_1.0</code> tree to a
0424:             * <code>TIFFImageMetadata</code> object.
0425:             *
0426:             * @param inData The metadata object.
0427:             * @return a <code>TIFFImageMetadata</code> or <code>null</code> if
0428:             * the standard tree derived from the input object is <code>null</code>.
0429:             * @throws IllegalArgumentException if <code>inData</code> is
0430:             * <code>null</code> or does not support the standard metadata format.
0431:             * @throws IIOInvalidTreeException if <code>inData</code> generates an
0432:             * invalid standard metadata tree.
0433:             */
0434:            private TIFFImageMetadata convertStandardImageMetadata(
0435:                    IIOMetadata inData) throws IIOInvalidTreeException {
0436:
0437:                if (inData == null) {
0438:                    throw new IllegalArgumentException("inData == null!");
0439:                } else if (!inData.isStandardMetadataFormatSupported()) {
0440:                    throw new IllegalArgumentException(
0441:                            "inData does not support standard metadata format!");
0442:                }
0443:
0444:                TIFFImageMetadata outData = null;
0445:
0446:                String formatName = IIOMetadataFormatImpl.standardMetadataFormatName;
0447:                Node tree = inData.getAsTree(formatName);
0448:                if (tree != null) {
0449:                    List tagSets = new ArrayList(1);
0450:                    tagSets.add(BaselineTIFFTagSet.getInstance());
0451:                    outData = new TIFFImageMetadata(tagSets);
0452:                    outData.setFromTree(formatName, tree);
0453:                }
0454:
0455:                return outData;
0456:            }
0457:
0458:            /**
0459:             * Converts a native
0460:             * <code>com_sun_media_imageio_plugins_tiff_image_1.0</code> tree to a
0461:             * <code>TIFFImageMetadata</code> object.
0462:             *
0463:             * @param inData The metadata object.
0464:             * @return a <code>TIFFImageMetadata</code> or <code>null</code> if
0465:             * the native tree derived from the input object is <code>null</code>.
0466:             * @throws IllegalArgumentException if <code>inData</code> is
0467:             * <code>null</code> or does not support the native metadata format.
0468:             * @throws IIOInvalidTreeException if <code>inData</code> generates an
0469:             * invalid native metadata tree.
0470:             */
0471:            private TIFFImageMetadata convertNativeImageMetadata(
0472:                    IIOMetadata inData) throws IIOInvalidTreeException {
0473:
0474:                if (inData == null) {
0475:                    throw new IllegalArgumentException("inData == null!");
0476:                } else if (!Arrays.asList(inData.getMetadataFormatNames())
0477:                        .contains(TIFFImageMetadata.nativeMetadataFormatName)) {
0478:                    throw new IllegalArgumentException(
0479:                            "inData does not support native metadata format!");
0480:                }
0481:
0482:                TIFFImageMetadata outData = null;
0483:
0484:                String formatName = TIFFImageMetadata.nativeMetadataFormatName;
0485:                Node tree = inData.getAsTree(formatName);
0486:                if (tree != null) {
0487:                    List tagSets = new ArrayList(1);
0488:                    tagSets.add(BaselineTIFFTagSet.getInstance());
0489:                    outData = new TIFFImageMetadata(tagSets);
0490:                    outData.setFromTree(formatName, tree);
0491:                }
0492:
0493:                return outData;
0494:            }
0495:
0496:            /**
0497:             * Sets up the output metadata adding, removing, and overriding fields
0498:             * as needed. The destination image dimensions are provided as parameters
0499:             * because these might differ from those of the source due to subsampling.
0500:             * 
0501:             * @param cm The <code>ColorModel</code> of the image being written.
0502:             * @param sm The <code>SampleModel</code> of the image being written.
0503:             * @param destWidth The width of the written image after subsampling.
0504:             * @param destHeight The height of the written image after subsampling.
0505:             */
0506:            void setupMetadata(ColorModel cm, SampleModel sm, int destWidth,
0507:                    int destHeight) throws IIOException {
0508:                // Get initial IFD from metadata
0509:
0510:                // Always emit these fields:
0511:                //
0512:                // Override values from metadata:
0513:                //
0514:                //  planarConfiguration -> chunky (planar not supported on output)
0515:                //
0516:                // Override values from metadata with image-derived values:
0517:                // 
0518:                //  bitsPerSample (if not bilivel)
0519:                //  colorMap (if palette color)
0520:                //  photometricInterpretation (derive from image)
0521:                //  imageLength
0522:                //  imageWidth
0523:                // 
0524:                //  rowsPerStrip     \      /   tileLength
0525:                //  stripOffsets      | OR |   tileOffsets
0526:                //  stripByteCounts  /     |   tileByteCounts
0527:                //                          \   tileWidth
0528:                //                    
0529:                //
0530:                // Override values from metadata with write param values:
0531:                //
0532:                //  compression
0533:
0534:                // Use values from metadata if present for these fields,
0535:                // otherwise use defaults:
0536:                //
0537:                //  resolutionUnit
0538:                //  XResolution (take from metadata if present)
0539:                //  YResolution
0540:                //  rowsPerStrip
0541:                //  sampleFormat
0542:
0543:                TIFFIFD rootIFD = imageMetadata.getRootIFD();
0544:
0545:                BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
0546:
0547:                // If PlanarConfiguration field present, set value to chunky.
0548:
0549:                TIFFField f = rootIFD
0550:                        .getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
0551:                if (f != null
0552:                        && f.getAsInt(0) != BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY) {
0553:                    // XXX processWarningOccurred()
0554:                    TIFFField planarConfigurationField = new TIFFField(
0555:                            base
0556:                                    .getTag(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION),
0557:                            BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY);
0558:                    rootIFD.addTIFFField(planarConfigurationField);
0559:                }
0560:
0561:                char[] extraSamples = null;
0562:
0563:                this .photometricInterpretation = -1;
0564:                boolean forcePhotometricInterpretation = false;
0565:
0566:                f = rootIFD
0567:                        .getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
0568:                if (f != null) {
0569:                    photometricInterpretation = f.getAsInt(0);
0570:                    if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR
0571:                            && !(cm instanceof  IndexColorModel)) {
0572:                        photometricInterpretation = -1;
0573:                    } else {
0574:                        forcePhotometricInterpretation = true;
0575:                    }
0576:                }
0577:
0578:                //         f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
0579:                //         if (f != null) {
0580:                //             extraSamples = f.getAsChars();
0581:                //         }
0582:
0583:                //         f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
0584:                //         if (f != null) {
0585:                //             bitsPerSample = f.getAsChars();
0586:                //         }
0587:
0588:                int[] sampleSize = sm.getSampleSize();
0589:
0590:                int numBands = sm.getNumBands();
0591:                int numExtraSamples = 0;
0592:
0593:                // Check that numBands > 1 here because TIFF requires that
0594:                // SamplesPerPixel = numBands + numExtraSamples and numBands
0595:                // cannot be zero.
0596:                if (numBands > 1 && cm != null && cm.hasAlpha()) {
0597:                    --numBands;
0598:                    numExtraSamples = 1;
0599:                    extraSamples = new char[1];
0600:                    if (cm.isAlphaPremultiplied()) {
0601:                        extraSamples[0] = BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA;
0602:                    } else {
0603:                        extraSamples[0] = BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA;
0604:                    }
0605:                }
0606:
0607:                if (numBands == 3) {
0608:                    this .nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
0609:                    if (photometricInterpretation == -1) {
0610:                        photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
0611:                    }
0612:                } else if (sm.getNumBands() == 1
0613:                        && cm instanceof  IndexColorModel) {
0614:                    IndexColorModel icm = (IndexColorModel) cm;
0615:                    int r0 = icm.getRed(0);
0616:                    int r1 = icm.getRed(1);
0617:                    if (icm.getMapSize() == 2 && (r0 == icm.getGreen(0))
0618:                            && (r0 == icm.getBlue(0))
0619:                            && (r1 == icm.getGreen(1))
0620:                            && (r1 == icm.getBlue(1)) && (r0 == 0 || r0 == 255)
0621:                            && (r1 == 0 || r1 == 255) && (r0 != r1)) {
0622:                        // Black/white image
0623:
0624:                        if (r0 == 0) {
0625:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
0626:                        } else {
0627:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
0628:                        }
0629:
0630:                        // If photometricInterpretation is already set to
0631:                        // WhiteIsZero or BlackIsZero, leave it alone
0632:                        if (photometricInterpretation != BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
0633:                                && photometricInterpretation != BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) {
0634:                            photometricInterpretation = r0 == 0 ? BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
0635:                                    : BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
0636:                        }
0637:                    } else {
0638:                        nativePhotometricInterpretation = photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
0639:                    }
0640:                } else {
0641:                    if (cm != null) {
0642:                        switch (cm.getColorSpace().getType()) {
0643:                        case ColorSpace.TYPE_Lab:
0644:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB;
0645:                            break;
0646:                        case ColorSpace.TYPE_YCbCr:
0647:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
0648:                            break;
0649:                        case ColorSpace.TYPE_CMYK:
0650:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK;
0651:                            break;
0652:                        default:
0653:                            nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
0654:                        }
0655:                    } else {
0656:                        nativePhotometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
0657:                    }
0658:                    if (photometricInterpretation == -1) {
0659:                        photometricInterpretation = nativePhotometricInterpretation;
0660:                    }
0661:                }
0662:
0663:                // Set the compressor and color converter.
0664:
0665:                this .compressor = null;
0666:                this .colorConverter = null;
0667:                if (param instanceof  TIFFImageWriteParam) {
0668:                    TIFFImageWriteParam tparam = (TIFFImageWriteParam) param;
0669:                    if (tparam.getCompressionMode() == tparam.MODE_EXPLICIT) {
0670:                        compressor = tparam.getTIFFCompressor();
0671:                        String compressionType = param.getCompressionType();
0672:                        if (compressor != null
0673:                                && !compressor.getCompressionType().equals(
0674:                                        compressionType)) {
0675:                            // Unset the TIFFCompressor if its compression type is
0676:                            // not the one selected.
0677:                            compressor = null;
0678:                        }
0679:                    } else {
0680:                        // Compression mode not MODE_EXPLICIT.
0681:                        compressor = null;
0682:                    }
0683:                    colorConverter = tparam.getColorConverter();
0684:                    if (colorConverter != null) {
0685:                        photometricInterpretation = tparam
0686:                                .getPhotometricInterpretation();
0687:                    }
0688:                }
0689:
0690:                // Emit compression tag
0691:
0692:                int compressionMode = param instanceof  TIFFImageWriteParam ? param
0693:                        .getCompressionMode()
0694:                        : ImageWriteParam.MODE_DEFAULT;
0695:                switch (compressionMode) {
0696:                case ImageWriteParam.MODE_EXPLICIT: {
0697:                    String compressionType = param.getCompressionType();
0698:                    if (compressionType == null) {
0699:                        this .compression = BaselineTIFFTagSet.COMPRESSION_NONE;
0700:                    } else {
0701:                        // Determine corresponding compression tag value.
0702:                        int len = compressionTypes.length;
0703:                        for (int i = 0; i < len; i++) {
0704:                            if (compressionType.equals(compressionTypes[i])) {
0705:                                this .compression = compressionNumbers[i];
0706:                            }
0707:                        }
0708:                    }
0709:
0710:                    // Ensure the compressor, if any, matches compression setting
0711:                    // with the precedence described in TIFFImageWriteParam.
0712:                    if (compressor != null
0713:                            && compressor.getCompressionTagValue() != this .compression) {
0714:                        // Does not match: unset the compressor.
0715:                        compressor = null;
0716:                    }
0717:                }
0718:                    break;
0719:                case ImageWriteParam.MODE_COPY_FROM_METADATA: {
0720:                    TIFFField compField = rootIFD
0721:                            .getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
0722:                    if (compField != null) {
0723:                        this .compression = compField.getAsInt(0);
0724:                        break;
0725:                    }
0726:                }
0727:                case ImageWriteParam.MODE_DEFAULT:
0728:                case ImageWriteParam.MODE_DISABLED:
0729:                default:
0730:                    this .compression = BaselineTIFFTagSet.COMPRESSION_NONE;
0731:                }
0732:
0733:                TIFFField predictorField = rootIFD
0734:                        .getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
0735:                if (predictorField != null) {
0736:                    this .predictor = predictorField.getAsInt(0);
0737:
0738:                    // We only support Horizontal Predictor for a bitDepth of 8
0739:                    if (sampleSize[0] != 8 ||
0740:                    // Check the value of the tag for validity
0741:                            (predictor != BaselineTIFFTagSet.PREDICTOR_NONE && predictor != BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING)) {
0742:                        // XXX processWarningOccured ???
0743:                        // Set to default
0744:                        predictor = BaselineTIFFTagSet.PREDICTOR_NONE;
0745:
0746:                        // Emit this changed predictor value to metadata
0747:                        TIFFField newPredictorField = new TIFFField(base
0748:                                .getTag(BaselineTIFFTagSet.TAG_PREDICTOR),
0749:                                predictor);
0750:                        rootIFD.addTIFFField(newPredictorField);
0751:                    }
0752:
0753:                    // XXX Do we need to ensure that predictor is not passed on if
0754:                    // the compression is not either Deflate or LZW?
0755:                }
0756:
0757:                TIFFField compressionField = new TIFFField(base
0758:                        .getTag(BaselineTIFFTagSet.TAG_COMPRESSION),
0759:                        compression);
0760:                rootIFD.addTIFFField(compressionField);
0761:
0762:                // Set EXIF flag. Note that there is no way to determine definitively
0763:                // when an uncompressed thumbnail is being written as the EXIF IFD
0764:                // pointer field is optional for thumbnails.
0765:                boolean isEXIF = false;
0766:                if (numBands == 3 && sampleSize[0] == 8 && sampleSize[1] == 8
0767:                        && sampleSize[2] == 8) {
0768:                    // Three bands with 8 bits per sample.
0769:                    if (rootIFD
0770:                            .getTIFFField(EXIFParentTIFFTagSet.TAG_EXIF_IFD_POINTER) != null) {
0771:                        // EXIF IFD pointer present.
0772:                        if (compression == BaselineTIFFTagSet.COMPRESSION_NONE
0773:                                && (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB || photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR)) {
0774:                            // Uncompressed RGB or YCbCr.
0775:                            isEXIF = true;
0776:                        } else if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
0777:                            // Compressed.
0778:                            isEXIF = true;
0779:                        }
0780:                    } else if (compressionMode == ImageWriteParam.MODE_EXPLICIT
0781:                            && EXIF_JPEG_COMPRESSION_TYPE.equals(param
0782:                                    .getCompressionType())) {
0783:                        // EXIF IFD pointer absent but EXIF JPEG compression set.
0784:                        isEXIF = true;
0785:                    }
0786:                }
0787:
0788:                // Initialize JPEG interchange format flag which is used to
0789:                // indicate that the image is stored as a single JPEG stream.
0790:                // This flag is separated from the 'isEXIF' flag in case JPEG
0791:                // interchange format is eventually supported for non-EXIF images.
0792:                boolean isJPEGInterchange = isEXIF
0793:                        && compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG;
0794:
0795:                if (compressor == null) {
0796:                    if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
0797:                        if (PackageUtil.isCodecLibAvailable()) {
0798:                            try {
0799:                                compressor = new TIFFCodecLibRLECompressor();
0800:                                if (DEBUG) {
0801:                                    System.out
0802:                                            .println("Using codecLib RLE compressor");
0803:                                }
0804:                            } catch (RuntimeException e) {
0805:                                if (DEBUG) {
0806:                                    System.out.println(e);
0807:                                }
0808:                            }
0809:                        }
0810:
0811:                        if (compressor == null) {
0812:                            compressor = new TIFFRLECompressor();
0813:                            if (DEBUG) {
0814:                                System.out.println("Using Java RLE compressor");
0815:                            }
0816:                        }
0817:
0818:                        if (!forcePhotometricInterpretation) {
0819:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
0820:                        }
0821:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
0822:                        if (PackageUtil.isCodecLibAvailable()) {
0823:                            try {
0824:                                compressor = new TIFFCodecLibT4Compressor();
0825:                                if (DEBUG) {
0826:                                    System.out
0827:                                            .println("Using codecLib T.4 compressor");
0828:                                }
0829:                            } catch (RuntimeException e) {
0830:                                if (DEBUG) {
0831:                                    System.out.println(e);
0832:                                }
0833:                            }
0834:                        }
0835:
0836:                        if (compressor == null) {
0837:                            compressor = new TIFFT4Compressor();
0838:                            if (DEBUG) {
0839:                                System.out.println("Using Java T.4 compressor");
0840:                            }
0841:                        }
0842:
0843:                        if (!forcePhotometricInterpretation) {
0844:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
0845:                        }
0846:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
0847:                        if (PackageUtil.isCodecLibAvailable()) {
0848:                            try {
0849:                                compressor = new TIFFCodecLibT6Compressor();
0850:                                if (DEBUG) {
0851:                                    System.out
0852:                                            .println("Using codecLib T.6 compressor");
0853:                                }
0854:                            } catch (RuntimeException e) {
0855:                                if (DEBUG) {
0856:                                    System.out.println(e);
0857:                                }
0858:                            }
0859:                        }
0860:
0861:                        if (compressor == null) {
0862:                            compressor = new TIFFT6Compressor();
0863:                            if (DEBUG) {
0864:                                System.out.println("Using Java T.6 compressor");
0865:                            }
0866:                        }
0867:
0868:                        if (!forcePhotometricInterpretation) {
0869:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
0870:                        }
0871:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_LZW) {
0872:                        compressor = new TIFFLZWCompressor(predictor);
0873:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
0874:                        if (isEXIF) {
0875:                            compressor = new TIFFEXIFJPEGCompressor(param);
0876:                        } else {
0877:                            throw new IIOException(
0878:                                    "Old JPEG compression not supported!");
0879:                        }
0880:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_JPEG) {
0881:                        if (numBands == 3 && sampleSize[0] == 8
0882:                                && sampleSize[1] == 8 && sampleSize[2] == 8) {
0883:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
0884:                        } else if (numBands == 1 && sampleSize[0] == 8) {
0885:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
0886:                        } else {
0887:                            throw new IIOException(
0888:                                    "JPEG compression supported for 1- and 3-band byte images only!");
0889:                        }
0890:                        compressor = new TIFFJPEGCompressor(param);
0891:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_ZLIB) {
0892:                        compressor = new TIFFZLibCompressor(param, predictor);
0893:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_PACKBITS) {
0894:                        compressor = new TIFFPackBitsCompressor();
0895:                    } else if (compression == BaselineTIFFTagSet.COMPRESSION_DEFLATE) {
0896:                        compressor = new TIFFDeflateCompressor(param, predictor);
0897:                    } else {
0898:                        // Determine inverse fill setting.
0899:                        f = rootIFD
0900:                                .getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
0901:                        boolean inverseFill = (f != null && f.getAsInt(0) == 2);
0902:
0903:                        if (inverseFill) {
0904:                            compressor = new TIFFLSBCompressor();
0905:                        } else {
0906:                            compressor = new TIFFNullCompressor();
0907:                        }
0908:                    } // compression == ?
0909:                } // compressor == null
0910:
0911:                if (DEBUG) {
0912:                    if (param != null
0913:                            && param.getCompressionMode() == param.MODE_EXPLICIT) {
0914:                        System.out.println("compressionType = "
0915:                                + param.getCompressionType());
0916:                    }
0917:                    if (compressor != null) {
0918:                        System.out.println("compressor = "
0919:                                + compressor.getClass().getName());
0920:                    }
0921:                }
0922:
0923:                if (colorConverter == null) {
0924:                    if (cm != null
0925:                            && cm.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
0926:                        //
0927:                        // Perform color conversion only if image has RGB color space.
0928:                        //
0929:                        if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
0930:                                && compression != BaselineTIFFTagSet.COMPRESSION_JPEG) {
0931:                            //
0932:                            // Convert RGB to YCbCr only if compression type is not
0933:                            // JPEG in which case this is handled implicitly by the
0934:                            // compressor.
0935:                            //
0936:                            colorConverter = new TIFFYCbCrColorConverter(
0937:                                    imageMetadata);
0938:                        } else if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB) {
0939:                            colorConverter = new TIFFCIELabColorConverter();
0940:                        }
0941:                    }
0942:                }
0943:
0944:                //
0945:                // Cannot at this time do YCbCr subsampling so set the
0946:                // YCbCrSubsampling field value to [1, 1] and the YCbCrPositioning
0947:                // field value to "cosited".
0948:                //
0949:                if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
0950:                        && compression != BaselineTIFFTagSet.COMPRESSION_JPEG) {
0951:                    // Remove old subsampling and positioning fields.
0952:                    rootIFD
0953:                            .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
0954:                    rootIFD
0955:                            .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
0956:
0957:                    // Add unity chrominance subsampling factors.
0958:                    rootIFD
0959:                            .addTIFFField(new TIFFField(
0960:                                    base
0961:                                            .getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING),
0962:                                    TIFFTag.TIFF_SHORT, 2, new char[] {
0963:                                            (char) 1, (char) 1 }));
0964:
0965:                    // Add cosited positioning.
0966:                    rootIFD
0967:                            .addTIFFField(new TIFFField(
0968:                                    base
0969:                                            .getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
0970:                                    TIFFTag.TIFF_SHORT,
0971:                                    1,
0972:                                    new char[] { (char) BaselineTIFFTagSet.Y_CB_CR_POSITIONING_COSITED }));
0973:                }
0974:
0975:                TIFFField photometricInterpretationField = new TIFFField(
0976:                        base
0977:                                .getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION),
0978:                        photometricInterpretation);
0979:                rootIFD.addTIFFField(photometricInterpretationField);
0980:
0981:                this .bitsPerSample = new char[numBands + numExtraSamples];
0982:                this .bitDepth = 0;
0983:                for (int i = 0; i < numBands; i++) {
0984:                    this .bitDepth = Math.max(bitDepth, sampleSize[i]);
0985:                }
0986:                if (bitDepth == 3) {
0987:                    bitDepth = 4;
0988:                } else if (bitDepth > 4 && bitDepth < 8) {
0989:                    bitDepth = 8;
0990:                } else if (bitDepth > 8 && bitDepth < 16) {
0991:                    bitDepth = 16;
0992:                } else if (bitDepth > 16) {
0993:                    bitDepth = 32;
0994:                }
0995:
0996:                for (int i = 0; i < bitsPerSample.length; i++) {
0997:                    bitsPerSample[i] = (char) bitDepth;
0998:                }
0999:
1000:                // Emit BitsPerSample. If the image is bilevel, emit if and only
1001:                // if already in the metadata and correct (count and value == 1).
1002:                if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
1003:                    TIFFField bitsPerSampleField = new TIFFField(base
1004:                            .getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE),
1005:                            TIFFTag.TIFF_SHORT, bitsPerSample.length,
1006:                            bitsPerSample);
1007:                    rootIFD.addTIFFField(bitsPerSampleField);
1008:                } else { // bitsPerSample.length == 1 && bitsPerSample[0] == 1
1009:                    TIFFField bitsPerSampleField = rootIFD
1010:                            .getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
1011:                    if (bitsPerSampleField != null) {
1012:                        int[] bps = bitsPerSampleField.getAsInts();
1013:                        if (bps == null || bps.length != 1 || bps[0] != 1) {
1014:                            rootIFD
1015:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
1016:                        }
1017:                    }
1018:                }
1019:
1020:                // Prepare SampleFormat field.
1021:                f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
1022:                if (f == null && (bitDepth == 16 || bitDepth == 32)) {
1023:                    // Set up default content for 16- and 32-bit cases.
1024:                    char sampleFormatValue;
1025:                    int dataType = sm.getDataType();
1026:                    if (bitDepth == 16 && dataType == DataBuffer.TYPE_USHORT) {
1027:                        sampleFormatValue = (char) BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
1028:                    } else if (bitDepth == 32
1029:                            && dataType == DataBuffer.TYPE_FLOAT) {
1030:                        sampleFormatValue = (char) BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT;
1031:                    } else {
1032:                        sampleFormatValue = BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER;
1033:                    }
1034:                    this .sampleFormat = (int) sampleFormatValue;
1035:                    char[] sampleFormatArray = new char[bitsPerSample.length];
1036:                    Arrays.fill(sampleFormatArray, sampleFormatValue);
1037:
1038:                    // Update the metadata.
1039:                    TIFFTag sampleFormatTag = base
1040:                            .getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
1041:
1042:                    TIFFField sampleFormatField = new TIFFField(
1043:                            sampleFormatTag, TIFFTag.TIFF_SHORT,
1044:                            sampleFormatArray.length, sampleFormatArray);
1045:
1046:                    rootIFD.addTIFFField(sampleFormatField);
1047:                } else if (f != null) {
1048:                    // Get whatever was provided.
1049:                    sampleFormat = f.getAsInt(0);
1050:                } else {
1051:                    // Set default value for internal use only.
1052:                    sampleFormat = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
1053:                }
1054:
1055:                if (extraSamples != null) {
1056:                    TIFFField extraSamplesField = new TIFFField(base
1057:                            .getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
1058:                            TIFFTag.TIFF_SHORT, extraSamples.length,
1059:                            extraSamples);
1060:                    rootIFD.addTIFFField(extraSamplesField);
1061:                } else {
1062:                    rootIFD
1063:                            .removeTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
1064:                }
1065:
1066:                TIFFField samplesPerPixelField = new TIFFField(base
1067:                        .getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL),
1068:                        bitsPerSample.length);
1069:                rootIFD.addTIFFField(samplesPerPixelField);
1070:
1071:                // Emit ColorMap if image is of palette color type
1072:                if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR
1073:                        && cm instanceof  IndexColorModel) {
1074:                    char[] colorMap = new char[3 * (1 << bitsPerSample[0])];
1075:
1076:                    IndexColorModel icm = (IndexColorModel) cm;
1077:
1078:                    // mapSize is determined by BitsPerSample, not by incoming ICM.
1079:                    int mapSize = 1 << bitsPerSample[0];
1080:                    int indexBound = Math.min(mapSize, icm.getMapSize());
1081:                    for (int i = 0; i < indexBound; i++) {
1082:                        colorMap[i] = (char) ((icm.getRed(i) * 65535) / 255);
1083:                        colorMap[mapSize + i] = (char) ((icm.getGreen(i) * 65535) / 255);
1084:                        colorMap[2 * mapSize + i] = (char) ((icm.getBlue(i) * 65535) / 255);
1085:                    }
1086:
1087:                    TIFFField colorMapField = new TIFFField(base
1088:                            .getTag(BaselineTIFFTagSet.TAG_COLOR_MAP),
1089:                            TIFFTag.TIFF_SHORT, colorMap.length, colorMap);
1090:                    rootIFD.addTIFFField(colorMapField);
1091:                } else {
1092:                    rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
1093:                }
1094:
1095:                // Emit ICCProfile if there is no ICCProfile field already in the
1096:                // metadata and the ColorSpace is non-standard ICC.
1097:                if (cm != null
1098:                        && rootIFD
1099:                                .getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE) == null
1100:                        && ImageUtil.isNonStandardICCColorSpace(cm
1101:                                .getColorSpace())) {
1102:                    ICC_ColorSpace iccColorSpace = (ICC_ColorSpace) cm
1103:                            .getColorSpace();
1104:                    byte[] iccProfileData = iccColorSpace.getProfile()
1105:                            .getData();
1106:                    TIFFField iccProfileField = new TIFFField(base
1107:                            .getTag(BaselineTIFFTagSet.TAG_ICC_PROFILE),
1108:                            TIFFTag.TIFF_UNDEFINED, iccProfileData.length,
1109:                            iccProfileData);
1110:                    rootIFD.addTIFFField(iccProfileField);
1111:                }
1112:
1113:                // Always emit XResolution and YResolution.
1114:
1115:                TIFFField XResolutionField = rootIFD
1116:                        .getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
1117:                TIFFField YResolutionField = rootIFD
1118:                        .getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
1119:
1120:                if (XResolutionField == null && YResolutionField == null) {
1121:                    long[][] resRational = new long[1][2];
1122:                    resRational[0] = new long[2];
1123:
1124:                    TIFFField ResolutionUnitField = rootIFD
1125:                            .getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
1126:
1127:                    // Don't force dimensionless if one of the other dimensional
1128:                    // quantities is present.
1129:                    if (ResolutionUnitField == null
1130:                            && rootIFD
1131:                                    .getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION) == null
1132:                            && rootIFD
1133:                                    .getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION) == null) {
1134:                        // Set resolution to unit and units to dimensionless.
1135:                        resRational[0][0] = 1;
1136:                        resRational[0][1] = 1;
1137:
1138:                        ResolutionUnitField = new TIFFField(
1139:                                rootIFD
1140:                                        .getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
1141:                                BaselineTIFFTagSet.RESOLUTION_UNIT_NONE);
1142:                        rootIFD.addTIFFField(ResolutionUnitField);
1143:                    } else {
1144:                        // Set resolution to a value which would make the maximum
1145:                        // image dimension equal to 4 inches as arbitrarily stated
1146:                        // in the description of ResolutionUnit in the TIFF 6.0
1147:                        // specification. If the ResolutionUnit field specifies
1148:                        // "none" then set the resolution to unity (1/1).
1149:                        int resolutionUnit = ResolutionUnitField != null ? ResolutionUnitField
1150:                                .getAsInt(0)
1151:                                : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
1152:                        int maxDimension = Math.max(destWidth, destHeight);
1153:                        switch (resolutionUnit) {
1154:                        case BaselineTIFFTagSet.RESOLUTION_UNIT_INCH:
1155:                            resRational[0][0] = maxDimension;
1156:                            resRational[0][1] = 4;
1157:                            break;
1158:                        case BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER:
1159:                            resRational[0][0] = 100L * maxDimension; // divide out 100
1160:                            resRational[0][1] = 4 * 254; // 2.54 cm/inch * 100
1161:                            break;
1162:                        default:
1163:                            resRational[0][0] = 1;
1164:                            resRational[0][1] = 1;
1165:                        }
1166:                    }
1167:
1168:                    XResolutionField = new TIFFField(rootIFD
1169:                            .getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
1170:                            TIFFTag.TIFF_RATIONAL, 1, resRational);
1171:                    rootIFD.addTIFFField(XResolutionField);
1172:
1173:                    YResolutionField = new TIFFField(rootIFD
1174:                            .getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
1175:                            TIFFTag.TIFF_RATIONAL, 1, resRational);
1176:                    rootIFD.addTIFFField(YResolutionField);
1177:                } else if (XResolutionField == null && YResolutionField != null) {
1178:                    // Set XResolution to YResolution.
1179:                    long[] yResolution = (long[]) YResolutionField
1180:                            .getAsRational(0).clone();
1181:                    XResolutionField = new TIFFField(rootIFD
1182:                            .getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
1183:                            TIFFTag.TIFF_RATIONAL, 1, yResolution);
1184:                    rootIFD.addTIFFField(XResolutionField);
1185:                } else if (XResolutionField != null && YResolutionField == null) {
1186:                    // Set YResolution to XResolution.
1187:                    long[] xResolution = (long[]) XResolutionField
1188:                            .getAsRational(0).clone();
1189:                    YResolutionField = new TIFFField(rootIFD
1190:                            .getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
1191:                            TIFFTag.TIFF_RATIONAL, 1, xResolution);
1192:                    rootIFD.addTIFFField(YResolutionField);
1193:                }
1194:
1195:                // Set mandatory fields, overriding metadata passed in
1196:
1197:                int width = destWidth;
1198:                TIFFField imageWidthField = new TIFFField(base
1199:                        .getTag(BaselineTIFFTagSet.TAG_IMAGE_WIDTH), width);
1200:                rootIFD.addTIFFField(imageWidthField);
1201:
1202:                int height = destHeight;
1203:                TIFFField imageLengthField = new TIFFField(base
1204:                        .getTag(BaselineTIFFTagSet.TAG_IMAGE_LENGTH), height);
1205:                rootIFD.addTIFFField(imageLengthField);
1206:
1207:                // Determine rowsPerStrip
1208:
1209:                int rowsPerStrip;
1210:
1211:                TIFFField rowsPerStripField = rootIFD
1212:                        .getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
1213:                if (rowsPerStripField != null) {
1214:                    rowsPerStrip = rowsPerStripField.getAsInt(0);
1215:                    if (rowsPerStrip < 0) {
1216:                        rowsPerStrip = height;
1217:                    }
1218:                } else {
1219:                    int bitsPerPixel = bitDepth * (numBands + numExtraSamples);
1220:                    int bytesPerRow = (bitsPerPixel * width + 7) / 8;
1221:                    rowsPerStrip = Math.max(Math.max(DEFAULT_BYTES_PER_STRIP
1222:                            / bytesPerRow, 1), 8);
1223:                }
1224:                rowsPerStrip = Math.min(rowsPerStrip, height);
1225:
1226:                // Tiling flag.
1227:                boolean useTiling = false;
1228:
1229:                // Analyze tiling parameters
1230:                int tilingMode = param instanceof  TIFFImageWriteParam ? param
1231:                        .getTilingMode() : ImageWriteParam.MODE_DEFAULT;
1232:                if (tilingMode == ImageWriteParam.MODE_DISABLED
1233:                        || tilingMode == ImageWriteParam.MODE_DEFAULT) {
1234:                    this .tileWidth = width;
1235:                    this .tileLength = rowsPerStrip;
1236:                    useTiling = false;
1237:                } else if (tilingMode == ImageWriteParam.MODE_EXPLICIT) {
1238:                    tileWidth = param.getTileWidth();
1239:                    tileLength = param.getTileHeight();
1240:                    useTiling = true;
1241:                } else if (tilingMode == ImageWriteParam.MODE_COPY_FROM_METADATA) {
1242:                    f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
1243:                    if (f == null) {
1244:                        tileWidth = width;
1245:                        useTiling = false;
1246:                    } else {
1247:                        tileWidth = f.getAsInt(0);
1248:                        useTiling = true;
1249:                    }
1250:
1251:                    f = rootIFD
1252:                            .getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
1253:                    if (f == null) {
1254:                        tileLength = rowsPerStrip;
1255:                    } else {
1256:                        tileLength = f.getAsInt(0);
1257:                        useTiling = true;
1258:                    }
1259:                } else {
1260:                    throw new IIOException("Illegal value of tilingMode!");
1261:                }
1262:
1263:                if (compression == BaselineTIFFTagSet.COMPRESSION_JPEG) {
1264:                    // Reset tile size per TTN2 spec for JPEG compression.
1265:                    int subX;
1266:                    int subY;
1267:                    if (numBands == 1) {
1268:                        subX = subY = 1;
1269:                    } else {
1270:                        subX = subY = TIFFJPEGCompressor.CHROMA_SUBSAMPLING;
1271:                    }
1272:                    if (useTiling) {
1273:                        int MCUMultipleX = 8 * subX;
1274:                        int MCUMultipleY = 8 * subY;
1275:                        tileWidth = Math
1276:                                .max(
1277:                                        MCUMultipleX
1278:                                                * ((tileWidth + MCUMultipleX / 2) / MCUMultipleX),
1279:                                        MCUMultipleX);
1280:                        tileLength = Math
1281:                                .max(
1282:                                        MCUMultipleY
1283:                                                * ((tileLength + MCUMultipleY / 2) / MCUMultipleY),
1284:                                        MCUMultipleY);
1285:                    } else if (rowsPerStrip < height) {
1286:                        int MCUMultiple = 8 * Math.max(subX, subY);
1287:                        rowsPerStrip = tileLength = Math
1288:                                .max(
1289:                                        MCUMultiple
1290:                                                * ((tileLength + MCUMultiple / 2) / MCUMultiple),
1291:                                        MCUMultiple);
1292:                    }
1293:                } else if (isJPEGInterchange) {
1294:                    // Force tile size to equal image size.
1295:                    tileWidth = width;
1296:                    tileLength = height;
1297:                } else if (useTiling) {
1298:                    // Round tile size to multiple of 16 per TIFF 6.0 specification
1299:                    // (see pages 67-68 of version 6.0.1 from Adobe).
1300:                    int tileWidthRemainder = tileWidth % 16;
1301:                    if (tileWidthRemainder != 0) {
1302:                        // Round to nearest multiple of 16 not less than 16.
1303:                        tileWidth = Math.max(16 * ((tileWidth + 8) / 16), 16);
1304:                        // XXX insert processWarningOccurred(int,String);
1305:                    }
1306:
1307:                    int tileLengthRemainder = tileLength % 16;
1308:                    if (tileLengthRemainder != 0) {
1309:                        // Round to nearest multiple of 16 not less than 16.
1310:                        tileLength = Math.max(16 * ((tileLength + 8) / 16), 16);
1311:                        // XXX insert processWarningOccurred(int,String);
1312:                    }
1313:                }
1314:
1315:                this .tilesAcross = (width + tileWidth - 1) / tileWidth;
1316:                this .tilesDown = (height + tileLength - 1) / tileLength;
1317:
1318:                if (!useTiling) {
1319:                    this .isTiled = false;
1320:
1321:                    rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
1322:                    rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
1323:                    rootIFD
1324:                            .removeTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
1325:                    rootIFD
1326:                            .removeTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
1327:
1328:                    rowsPerStripField = new TIFFField(base
1329:                            .getTag(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP),
1330:                            rowsPerStrip);
1331:                    rootIFD.addTIFFField(rowsPerStripField);
1332:
1333:                    TIFFField stripOffsetsField = new TIFFField(base
1334:                            .getTag(BaselineTIFFTagSet.TAG_STRIP_OFFSETS),
1335:                            TIFFTag.TIFF_LONG, tilesDown);
1336:                    rootIFD.addTIFFField(stripOffsetsField);
1337:
1338:                    TIFFField stripByteCountsField = new TIFFField(base
1339:                            .getTag(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS),
1340:                            TIFFTag.TIFF_LONG, tilesDown);
1341:                    rootIFD.addTIFFField(stripByteCountsField);
1342:                } else {
1343:                    this .isTiled = true;
1344:
1345:                    rootIFD
1346:                            .removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
1347:                    rootIFD
1348:                            .removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
1349:                    rootIFD
1350:                            .removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
1351:
1352:                    TIFFField tileWidthField = new TIFFField(base
1353:                            .getTag(BaselineTIFFTagSet.TAG_TILE_WIDTH),
1354:                            tileWidth);
1355:                    rootIFD.addTIFFField(tileWidthField);
1356:
1357:                    TIFFField tileLengthField = new TIFFField(base
1358:                            .getTag(BaselineTIFFTagSet.TAG_TILE_LENGTH),
1359:                            tileLength);
1360:                    rootIFD.addTIFFField(tileLengthField);
1361:
1362:                    TIFFField tileOffsetsField = new TIFFField(base
1363:                            .getTag(BaselineTIFFTagSet.TAG_TILE_OFFSETS),
1364:                            TIFFTag.TIFF_LONG, tilesDown * tilesAcross);
1365:                    rootIFD.addTIFFField(tileOffsetsField);
1366:
1367:                    TIFFField tileByteCountsField = new TIFFField(base
1368:                            .getTag(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS),
1369:                            TIFFTag.TIFF_LONG, tilesDown * tilesAcross);
1370:                    rootIFD.addTIFFField(tileByteCountsField);
1371:                }
1372:
1373:                if (isEXIF) {
1374:                    //
1375:                    // Ensure presence of mandatory fields and absence of prohibited
1376:                    // fields and those that duplicate information in JPEG marker
1377:                    // segments per tables 14-18 of the EXIF 2.2 specification.
1378:                    //
1379:
1380:                    // If an empty image is being written or inserted then infer
1381:                    // that the primary IFD is being set up.
1382:                    boolean isPrimaryIFD = isEncodingEmpty();
1383:
1384:                    // Handle TIFF fields in order of increasing tag number.
1385:                    if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
1386:                        // ImageWidth
1387:                        rootIFD
1388:                                .removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
1389:
1390:                        // ImageLength
1391:                        rootIFD
1392:                                .removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
1393:
1394:                        // BitsPerSample
1395:                        rootIFD
1396:                                .removeTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
1397:                        // Compression
1398:                        if (isPrimaryIFD) {
1399:                            rootIFD
1400:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
1401:                        }
1402:
1403:                        // PhotometricInterpretation
1404:                        rootIFD
1405:                                .removeTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
1406:
1407:                        // StripOffsets
1408:                        rootIFD
1409:                                .removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
1410:
1411:                        // SamplesPerPixel
1412:                        rootIFD
1413:                                .removeTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
1414:
1415:                        // RowsPerStrip
1416:                        rootIFD
1417:                                .removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
1418:
1419:                        // StripByteCounts
1420:                        rootIFD
1421:                                .removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
1422:                        // XResolution and YResolution are handled above for all TIFFs.
1423:
1424:                        // PlanarConfiguration
1425:                        rootIFD
1426:                                .removeTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
1427:
1428:                        // ResolutionUnit
1429:                        if (rootIFD
1430:                                .getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
1431:                            f = new TIFFField(
1432:                                    base
1433:                                            .getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
1434:                                    BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
1435:                            rootIFD.addTIFFField(f);
1436:                        }
1437:
1438:                        if (isPrimaryIFD) {
1439:                            // JPEGInterchangeFormat
1440:                            rootIFD
1441:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
1442:
1443:                            // JPEGInterchangeFormatLength
1444:                            rootIFD
1445:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
1446:
1447:                            // YCbCrSubsampling
1448:                            rootIFD
1449:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
1450:
1451:                            // YCbCrPositioning
1452:                            if (rootIFD
1453:                                    .getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING) == null) {
1454:                                f = new TIFFField(
1455:                                        base
1456:                                                .getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
1457:                                        TIFFTag.TIFF_SHORT,
1458:                                        1,
1459:                                        new char[] { (char) BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED });
1460:                                rootIFD.addTIFFField(f);
1461:                            }
1462:                        } else { // Thumbnail IFD
1463:                            // JPEGInterchangeFormat
1464:                            f = new TIFFField(
1465:                                    base
1466:                                            .getTag(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT),
1467:                                    TIFFTag.TIFF_LONG, 1);
1468:                            rootIFD.addTIFFField(f);
1469:
1470:                            // JPEGInterchangeFormatLength
1471:                            f = new TIFFField(
1472:                                    base
1473:                                            .getTag(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH),
1474:                                    TIFFTag.TIFF_LONG, 1);
1475:                            rootIFD.addTIFFField(f);
1476:
1477:                            // YCbCrSubsampling
1478:                            rootIFD
1479:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
1480:                        }
1481:                    } else { // Uncompressed
1482:                        // ImageWidth through PlanarConfiguration are set above.
1483:                        // XResolution and YResolution are handled above for all TIFFs.
1484:
1485:                        // ResolutionUnit
1486:                        if (rootIFD
1487:                                .getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
1488:                            f = new TIFFField(
1489:                                    base
1490:                                            .getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
1491:                                    BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
1492:                            rootIFD.addTIFFField(f);
1493:                        }
1494:
1495:                        // JPEGInterchangeFormat
1496:                        rootIFD
1497:                                .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
1498:
1499:                        // JPEGInterchangeFormatLength
1500:                        rootIFD
1501:                                .removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
1502:
1503:                        if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB) {
1504:                            // YCbCrCoefficients
1505:                            rootIFD
1506:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS);
1507:
1508:                            // YCbCrSubsampling
1509:                            rootIFD
1510:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
1511:
1512:                            // YCbCrPositioning
1513:                            rootIFD
1514:                                    .removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
1515:                        }
1516:                    }
1517:
1518:                    // Get EXIF tags.
1519:                    TIFFTagSet exifTags = EXIFTIFFTagSet.getInstance();
1520:
1521:                    // Retrieve or create the EXIF IFD.
1522:                    TIFFIFD exifIFD = null;
1523:                    f = rootIFD
1524:                            .getTIFFField(EXIFParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
1525:                    if (f != null) {
1526:                        // Retrieve the EXIF IFD.
1527:                        exifIFD = (TIFFIFD) f.getData();
1528:                    } else if (isPrimaryIFD) {
1529:                        // Create the EXIF IFD.
1530:                        List exifTagSets = new ArrayList(1);
1531:                        exifTagSets.add(exifTags);
1532:                        exifIFD = new TIFFIFD(exifTagSets);
1533:
1534:                        // Add it to the root IFD.
1535:                        TIFFTagSet tagSet = EXIFParentTIFFTagSet.getInstance();
1536:                        TIFFTag exifIFDTag = tagSet
1537:                                .getTag(EXIFParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
1538:                        rootIFD.addTIFFField(new TIFFField(exifIFDTag,
1539:                                TIFFTag.TIFF_LONG, 1, exifIFD));
1540:                    }
1541:
1542:                    if (exifIFD != null) {
1543:                        // Handle EXIF private fields in order of increasing
1544:                        // tag number.
1545:
1546:                        // ExifVersion
1547:                        if (exifIFD
1548:                                .getTIFFField(EXIFTIFFTagSet.TAG_EXIF_VERSION) == null) {
1549:                            f = new TIFFField(exifTags
1550:                                    .getTag(EXIFTIFFTagSet.TAG_EXIF_VERSION),
1551:                                    TIFFTag.TIFF_UNDEFINED, 4,
1552:                                    EXIFTIFFTagSet.EXIF_VERSION_2_2);
1553:                            exifIFD.addTIFFField(f);
1554:                        }
1555:
1556:                        if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
1557:                            // ComponentsConfiguration
1558:                            if (exifIFD
1559:                                    .getTIFFField(EXIFTIFFTagSet.TAG_COMPONENTS_CONFIGURATION) == null) {
1560:                                f = new TIFFField(
1561:                                        exifTags
1562:                                                .getTag(EXIFTIFFTagSet.TAG_COMPONENTS_CONFIGURATION),
1563:                                        TIFFTag.TIFF_UNDEFINED,
1564:                                        4,
1565:                                        new byte[] {
1566:                                                (byte) EXIFTIFFTagSet.COMPONENTS_CONFIGURATION_Y,
1567:                                                (byte) EXIFTIFFTagSet.COMPONENTS_CONFIGURATION_CB,
1568:                                                (byte) EXIFTIFFTagSet.COMPONENTS_CONFIGURATION_CR,
1569:                                                (byte) 0 });
1570:                                exifIFD.addTIFFField(f);
1571:                            }
1572:                        } else {
1573:                            // ComponentsConfiguration
1574:                            exifIFD
1575:                                    .removeTIFFField(EXIFTIFFTagSet.TAG_COMPONENTS_CONFIGURATION);
1576:
1577:                            // CompressedBitsPerPixel
1578:                            exifIFD
1579:                                    .removeTIFFField(EXIFTIFFTagSet.TAG_COMPRESSED_BITS_PER_PIXEL);
1580:                        }
1581:
1582:                        // FlashpixVersion
1583:                        if (exifIFD
1584:                                .getTIFFField(EXIFTIFFTagSet.TAG_FLASHPIX_VERSION) == null) {
1585:                            f = new TIFFField(
1586:                                    exifTags
1587:                                            .getTag(EXIFTIFFTagSet.TAG_FLASHPIX_VERSION),
1588:                                    TIFFTag.TIFF_UNDEFINED, 4, new byte[] {
1589:                                            (byte) '0', (byte) '1', (byte) '0',
1590:                                            (byte) '0' });
1591:                            exifIFD.addTIFFField(f);
1592:                        }
1593:
1594:                        // ColorSpace
1595:                        if (exifIFD
1596:                                .getTIFFField(EXIFTIFFTagSet.TAG_COLOR_SPACE) == null) {
1597:                            f = new TIFFField(
1598:                                    exifTags
1599:                                            .getTag(EXIFTIFFTagSet.TAG_COLOR_SPACE),
1600:                                    TIFFTag.TIFF_SHORT,
1601:                                    1,
1602:                                    new char[] { (char) EXIFTIFFTagSet.COLOR_SPACE_SRGB });
1603:                            exifIFD.addTIFFField(f);
1604:                        }
1605:
1606:                        if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
1607:                            // PixelXDimension
1608:                            if (exifIFD
1609:                                    .getTIFFField(EXIFTIFFTagSet.TAG_PIXEL_X_DIMENSION) == null) {
1610:                                f = new TIFFField(
1611:                                        exifTags
1612:                                                .getTag(EXIFTIFFTagSet.TAG_PIXEL_X_DIMENSION),
1613:                                        width);
1614:                                exifIFD.addTIFFField(f);
1615:                            }
1616:
1617:                            // PixelYDimension
1618:                            if (exifIFD
1619:                                    .getTIFFField(EXIFTIFFTagSet.TAG_PIXEL_Y_DIMENSION) == null) {
1620:                                f = new TIFFField(
1621:                                        exifTags
1622:                                                .getTag(EXIFTIFFTagSet.TAG_PIXEL_Y_DIMENSION),
1623:                                        height);
1624:                                exifIFD.addTIFFField(f);
1625:                            }
1626:                        } else {
1627:                            exifIFD
1628:                                    .removeTIFFField(EXIFTIFFTagSet.TAG_INTEROPERABILITY_IFD_POINTER);
1629:                        }
1630:                    }
1631:
1632:                } // if(isEXIF)
1633:            }
1634:
1635:            /**
1636:               @param tileRect The area to be written which might be outside the image.
1637:             */
1638:            private int writeTile(Rectangle tileRect, TIFFCompressor compressor)
1639:                    throws IOException {
1640:                // Determine the rectangle which will actually be written 
1641:                // and set the padding flag. Padding will occur only when the
1642:                // image is written as a tiled TIFF and the tile bounds are not
1643:                // contained within the image bounds.
1644:                Rectangle activeRect;
1645:                boolean isPadded;
1646:                Rectangle imageBounds = new Rectangle(image.getMinX(), image
1647:                        .getMinY(), image.getWidth(), image.getHeight());
1648:                if (!isTiled) {
1649:                    // Stripped
1650:                    activeRect = tileRect.intersection(imageBounds);
1651:                    tileRect = activeRect;
1652:                    isPadded = false;
1653:                } else if (imageBounds.contains(tileRect)) {
1654:                    // Tiled, tile within image bounds
1655:                    activeRect = tileRect;
1656:                    isPadded = false;
1657:                } else {
1658:                    // Tiled, tile not within image bounds
1659:                    activeRect = imageBounds.intersection(tileRect);
1660:                    isPadded = true;
1661:                }
1662:
1663:                // Shouldn't happen, but return early if empty intersection.
1664:                if (activeRect.isEmpty()) {
1665:                    return 0;
1666:                }
1667:
1668:                int minX = tileRect.x;
1669:                int minY = tileRect.y;
1670:                int width = tileRect.width;
1671:                int height = tileRect.height;
1672:
1673:                if (isImageSimple) {
1674:
1675:                    SampleModel sm = image.getSampleModel();
1676:
1677:                    // Read only data from the active rectangle.
1678:                    Raster raster = image.getData(activeRect);
1679:
1680:                    // If padding is required, create a larger Raster and fill
1681:                    // it from the active rectangle.
1682:                    if (isPadded) {
1683:                        WritableRaster wr = raster
1684:                                .createCompatibleWritableRaster(minX, minY,
1685:                                        width, height);
1686:                        wr.setRect(raster);
1687:                        raster = wr;
1688:                    }
1689:
1690:                    if (isBilevel) {
1691:                        /* XXX
1692:                        MultiPixelPackedSampleModel mppsm =
1693:                            (MultiPixelPackedSampleModel)raster.getSampleModel();
1694:
1695:                        byte[] buf;
1696:                        int off;
1697:                        int lineStride;
1698:                        if(mppsm.getDataBitOffset() == 0 &&
1699:                           raster.getDataBuffer() instanceof DataBufferByte) {
1700:                            buf = ((DataBufferByte)raster.getDataBuffer()).getData();
1701:                            off = mppsm.getOffset(tileRect.x -
1702:                                                  raster.getSampleModelTranslateX(),
1703:                                                  tileRect.y -
1704:                                                  raster.getSampleModelTranslateY());
1705:                            lineStride = mppsm.getScanlineStride();
1706:                        } else {
1707:                            buf = ImageUtil.getPackedBinaryData(raster,
1708:                                                                tileRect);
1709:                            off = 0;
1710:                            lineStride = (tileRect.width + 7)/8;
1711:                        }
1712:                         */
1713:                        byte[] buf = ImageUtil.getPackedBinaryData(raster,
1714:                                tileRect);
1715:
1716:                        if (isInverted) {
1717:                            DataBuffer dbb = raster.getDataBuffer();
1718:                            if (dbb instanceof  DataBufferByte
1719:                                    && buf == ((DataBufferByte) dbb).getData()) {
1720:                                byte[] bbuf = new byte[buf.length];
1721:                                int len = buf.length;
1722:                                for (int i = 0; i < len; i++) {
1723:                                    bbuf[i] = (byte) (buf[i] ^ 0xff);
1724:                                }
1725:                                buf = bbuf;
1726:                            } else {
1727:                                int len = buf.length;
1728:                                for (int i = 0; i < len; i++) {
1729:                                    buf[i] ^= 0xff;
1730:                                }
1731:                            }
1732:                        }
1733:
1734:                        if (DEBUG) {
1735:                            System.out.println("Optimized bilevel case");
1736:                        }
1737:
1738:                        return compressor.encode(buf, 0, width, height,
1739:                                sampleSize, (tileRect.width + 7) / 8);
1740:                    } else if (bitDepth == 8
1741:                            && sm.getDataType() == DataBuffer.TYPE_BYTE) {
1742:                        ComponentSampleModel csm = (ComponentSampleModel) raster
1743:                                .getSampleModel();
1744:
1745:                        byte[] buf = ((DataBufferByte) raster.getDataBuffer())
1746:                                .getData();
1747:
1748:                        int off = csm.getOffset(minX
1749:                                - raster.getSampleModelTranslateX(), minY
1750:                                - raster.getSampleModelTranslateY());
1751:
1752:                        if (DEBUG) {
1753:                            System.out.println("Optimized component case");
1754:                        }
1755:
1756:                        return compressor.encode(buf, off, width, height,
1757:                                sampleSize, csm.getScanlineStride());
1758:                    }
1759:                }
1760:
1761:                // Set offsets and skips based on source subsampling factors
1762:                int xOffset = minX;
1763:                int xSkip = periodX;
1764:                int yOffset = minY;
1765:                int ySkip = periodY;
1766:
1767:                // Early exit if no data for this pass
1768:                int hpixels = (width + xSkip - 1) / xSkip;
1769:                int vpixels = (height + ySkip - 1) / ySkip;
1770:                if (hpixels == 0 || vpixels == 0) {
1771:                    return 0;
1772:                }
1773:
1774:                // Convert X offset and skip from pixels to samples
1775:                xOffset *= numBands;
1776:                xSkip *= numBands;
1777:
1778:                // Initialize sizes
1779:                int samplesPerByte = 8 / bitDepth;
1780:                int numSamples = width * numBands;
1781:                int bytesPerRow = hpixels * numBands;
1782:
1783:                // Update number of bytes per row.
1784:                if (bitDepth < 8) {
1785:                    bytesPerRow = (bytesPerRow + samplesPerByte - 1)
1786:                            / samplesPerByte;
1787:                } else if (bitDepth == 16) {
1788:                    bytesPerRow *= 2;
1789:                } else if (bitDepth == 32) {
1790:                    bytesPerRow *= 4;
1791:                }
1792:
1793:                // Create row buffers
1794:                int[] samples = null;
1795:                float[] fsamples = null;
1796:                if (sampleFormat == BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
1797:                    fsamples = new float[numSamples];
1798:                } else {
1799:                    samples = new int[numSamples];
1800:                }
1801:
1802:                // Create tile buffer
1803:                byte[] currTile = new byte[bytesPerRow * vpixels];
1804:
1805:                // Sub-optimal case: shy of "isImageSimple" only by virtue of
1806:                // not being contiguous.
1807:                if (!isInverted && // no inversion
1808:                        !isRescaling && // no value rescaling
1809:                        sourceBands == null && // no subbanding
1810:                        periodX == 1 && periodY == 1 && // no subsampling
1811:                        colorConverter == null) {
1812:
1813:                    SampleModel sm = image.getSampleModel();
1814:
1815:                    if (sm instanceof  ComponentSampleModel && // component
1816:                            bitDepth == 8 && // 8 bits/sample
1817:                            sm.getDataType() == DataBuffer.TYPE_BYTE) { // byte type
1818:
1819:                        if (DEBUG) {
1820:                            System.out
1821:                                    .println("Sub-optimal byte component case");
1822:                            System.out.println(sm.getClass().getName());
1823:                        }
1824:
1825:                        // Read only data from the active rectangle.
1826:                        Raster raster = image.getData(activeRect);
1827:
1828:                        // If padding is required, create a larger Raster and fill
1829:                        // it from the active rectangle.
1830:                        if (isPadded) {
1831:                            WritableRaster wr = raster
1832:                                    .createCompatibleWritableRaster(minX, minY,
1833:                                            width, height);
1834:                            wr.setRect(raster);
1835:                            raster = wr;
1836:                        }
1837:
1838:                        // Get SampleModel info.
1839:                        ComponentSampleModel csm = (ComponentSampleModel) raster
1840:                                .getSampleModel();
1841:                        int[] bankIndices = csm.getBankIndices();
1842:                        byte[][] bankData = ((DataBufferByte) raster
1843:                                .getDataBuffer()).getBankData();
1844:                        int lineStride = csm.getScanlineStride();
1845:                        int pixelStride = csm.getPixelStride();
1846:
1847:                        // Copy the data into a contiguous pixel interleaved buffer.
1848:                        for (int k = 0; k < numBands; k++) {
1849:                            byte[] bandData = bankData[bankIndices[k]];
1850:                            int lineOffset = csm.getOffset(raster.getMinX()
1851:                                    - raster.getSampleModelTranslateX(), raster
1852:                                    .getMinY()
1853:                                    - raster.getSampleModelTranslateY(), k);
1854:                            int idx = k;
1855:                            for (int j = 0; j < vpixels; j++) {
1856:                                int offset = lineOffset;
1857:                                for (int i = 0; i < hpixels; i++) {
1858:                                    currTile[idx] = bandData[offset];
1859:                                    idx += numBands;
1860:                                    offset += pixelStride;
1861:                                }
1862:                                lineOffset += lineStride;
1863:                            }
1864:                        }
1865:
1866:                        // Compressor and return.
1867:                        return compressor.encode(currTile, 0, width, height,
1868:                                sampleSize, width * numBands);
1869:                    }
1870:                }
1871:
1872:                if (DEBUG) {
1873:                    System.out.println("Unoptimized case for bit depth "
1874:                            + bitDepth);
1875:                    SampleModel sm = image.getSampleModel();
1876:                    System.out.println("isRescaling = " + isRescaling);
1877:                    System.out.println("sourceBands = " + sourceBands);
1878:                    System.out.println("periodX = " + periodX);
1879:                    System.out.println("periodY = " + periodY);
1880:                    System.out.println(sm.getClass().getName());
1881:                    System.out.println(sm.getDataType());
1882:                    if (sm instanceof  ComponentSampleModel) {
1883:                        ComponentSampleModel csm = (ComponentSampleModel) sm;
1884:                        System.out.println(csm.getNumBands());
1885:                        System.out.println(csm.getPixelStride());
1886:                        int[] bankIndices = csm.getBankIndices();
1887:                        for (int b = 0; b < numBands; b++) {
1888:                            System.out.print(bankIndices[b] + " ");
1889:                        }
1890:                        int[] bandOffsets = csm.getBandOffsets();
1891:                        for (int b = 0; b < numBands; b++) {
1892:                            System.out.print(bandOffsets[b] + " ");
1893:                        }
1894:                        System.out.println("");
1895:                    }
1896:                }
1897:
1898:                int tcount = 0;
1899:
1900:                // Save active rectangle variables.
1901:                int activeMinX = activeRect.x;
1902:                int activeMinY = activeRect.y;
1903:                int activeMaxY = activeMinY + activeRect.height - 1;
1904:                int activeWidth = activeRect.width;
1905:
1906:                // Set a SampleModel for use in padding.
1907:                SampleModel rowSampleModel = null;
1908:                if (isPadded) {
1909:                    rowSampleModel = image.getSampleModel()
1910:                            .createCompatibleSampleModel(width, 1);
1911:                }
1912:
1913:                for (int row = yOffset; row < yOffset + height; row += ySkip) {
1914:                    Raster ras = null;
1915:                    if (isPadded) {
1916:                        // Create a raster for the entire row.
1917:                        WritableRaster wr = Raster.createWritableRaster(
1918:                                rowSampleModel, new Point(minX, row));
1919:
1920:                        // Populate the raster from the active sub-row, if any.
1921:                        if (row >= activeMinY && row <= activeMaxY) {
1922:                            Rectangle rect = new Rectangle(activeMinX, row,
1923:                                    activeWidth, 1);
1924:                            ras = image.getData(rect);
1925:                            wr.setRect(ras);
1926:                        }
1927:
1928:                        // Update the raster variable.
1929:                        ras = wr;
1930:                    } else {
1931:                        Rectangle rect = new Rectangle(minX, row, width, 1);
1932:                        ras = image.getData(rect);
1933:                    }
1934:                    if (sourceBands != null) {
1935:                        ras = ras.createChild(minX, row, width, 1, minX, row,
1936:                                sourceBands);
1937:                    }
1938:
1939:                    if (sampleFormat == BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
1940:                        ras.getPixels(minX, row, width, 1, fsamples);
1941:                    } else {
1942:                        ras.getPixels(minX, row, width, 1, samples);
1943:
1944:                        if ((nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO)
1945:                                || (nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO)) {
1946:                            int bitMask = (1 << bitDepth) - 1;
1947:                            for (int s = 0; s < numSamples; s++) {
1948:                                samples[s] ^= bitMask;
1949:                            }
1950:                        }
1951:                    }
1952:
1953:                    if (colorConverter != null) {
1954:                        int idx = 0;
1955:                        float[] result = new float[3];
1956:
1957:                        if (sampleFormat == BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
1958:                            for (int i = 0; i < width; i++) {
1959:                                float r = fsamples[idx];
1960:                                float g = fsamples[idx + 1];
1961:                                float b = fsamples[idx + 2];
1962:
1963:                                colorConverter.fromRGB(r, g, b, result);
1964:
1965:                                fsamples[idx] = result[0];
1966:                                fsamples[idx + 1] = result[1];
1967:                                fsamples[idx + 2] = result[2];
1968:
1969:                                idx += 3;
1970:                            }
1971:                        } else {
1972:                            for (int i = 0; i < width; i++) {
1973:                                float r = (float) samples[idx];
1974:                                float g = (float) samples[idx + 1];
1975:                                float b = (float) samples[idx + 2];
1976:
1977:                                colorConverter.fromRGB(r, g, b, result);
1978:
1979:                                samples[idx] = (int) (result[0]);
1980:                                samples[idx + 1] = (int) (result[1]);
1981:                                samples[idx + 2] = (int) (result[2]);
1982:
1983:                                idx += 3;
1984:                            }
1985:                        }
1986:                    }
1987:
1988:                    int tmp = 0;
1989:                    int pos = 0;
1990:
1991:                    switch (bitDepth) {
1992:                    case 1:
1993:                    case 2:
1994:                    case 4:
1995:                        // Image can only have a single band
1996:
1997:                        if (isRescaling) {
1998:                            for (int s = 0; s < numSamples; s += xSkip) {
1999:                                byte val = scale0[samples[s]];
2000:                                tmp = (tmp << bitDepth) | val;
2001:
2002:                                if (++pos == samplesPerByte) {
2003:                                    currTile[tcount++] = (byte) tmp;
2004:                                    tmp = 0;
2005:                                    pos = 0;
2006:                                }
2007:                            }
2008:                        } else {
2009:                            for (int s = 0; s < numSamples; s += xSkip) {
2010:                                byte val = (byte) samples[s];
2011:                                tmp = (tmp << bitDepth) | val;
2012:
2013:                                if (++pos == samplesPerByte) {
2014:                                    currTile[tcount++] = (byte) tmp;
2015:                                    tmp = 0;
2016:                                    pos = 0;
2017:                                }
2018:                            }
2019:                        }
2020:
2021:                        // Left shift the last byte
2022:                        if (pos != 0) {
2023:                            tmp <<= ((8 / bitDepth) - pos) * bitDepth;
2024:                            currTile[tcount++] = (byte) tmp;
2025:                        }
2026:                        break;
2027:
2028:                    case 8:
2029:                        if (numBands == 1) {
2030:                            if (isRescaling) {
2031:                                for (int s = 0; s < numSamples; s += xSkip) {
2032:                                    currTile[tcount++] = scale0[samples[s]];
2033:                                }
2034:                            } else {
2035:                                for (int s = 0; s < numSamples; s += xSkip) {
2036:                                    currTile[tcount++] = (byte) samples[s];
2037:                                }
2038:                            }
2039:                        } else {
2040:                            if (isRescaling) {
2041:                                for (int s = 0; s < numSamples; s += xSkip) {
2042:                                    for (int b = 0; b < numBands; b++) {
2043:                                        currTile[tcount++] = scale[b][samples[s
2044:                                                + b]];
2045:                                    }
2046:                                }
2047:                            } else {
2048:                                for (int s = 0; s < numSamples; s += xSkip) {
2049:                                    for (int b = 0; b < numBands; b++) {
2050:                                        currTile[tcount++] = (byte) samples[s
2051:                                                + b];
2052:                                    }
2053:                                }
2054:                            }
2055:                        }
2056:                        break;
2057:
2058:                    case 16:
2059:                        // XXX Need to verify this rescaling for signed vs. unsigned.
2060:                        if (isRescaling) {
2061:                            if (stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
2062:                                for (int s = 0; s < numSamples; s += xSkip) {
2063:                                    for (int b = 0; b < numBands; b++) {
2064:                                        int sample = samples[s + b];
2065:                                        currTile[tcount++] = scaleh[b][sample];
2066:                                        currTile[tcount++] = scalel[b][sample];
2067:                                    }
2068:                                }
2069:                            } else { // ByteOrder.LITLE_ENDIAN
2070:                                for (int s = 0; s < numSamples; s += xSkip) {
2071:                                    for (int b = 0; b < numBands; b++) {
2072:                                        int sample = samples[s + b];
2073:                                        currTile[tcount++] = scalel[b][sample];
2074:                                        currTile[tcount++] = scaleh[b][sample];
2075:                                    }
2076:                                }
2077:                            }
2078:                        } else {
2079:                            if (stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
2080:                                for (int s = 0; s < numSamples; s += xSkip) {
2081:                                    for (int b = 0; b < numBands; b++) {
2082:                                        int sample = samples[s + b];
2083:                                        currTile[tcount++] = (byte) ((sample >>> 8) & 0xff);
2084:                                        currTile[tcount++] = (byte) (sample & 0xff);
2085:                                    }
2086:                                }
2087:                            } else { // ByteOrder.LITLE_ENDIAN
2088:                                for (int s = 0; s < numSamples; s += xSkip) {
2089:                                    for (int b = 0; b < numBands; b++) {
2090:                                        int sample = samples[s + b];
2091:                                        currTile[tcount++] = (byte) (sample & 0xff);
2092:                                        currTile[tcount++] = (byte) ((sample >>> 8) & 0xff);
2093:                                    }
2094:                                }
2095:                            }
2096:                        }
2097:                        break;
2098:
2099:                    case 32:
2100:                        if (sampleFormat == BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
2101:                            if (stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
2102:                                for (int s = 0; s < numSamples; s += xSkip) {
2103:                                    for (int b = 0; b < numBands; b++) {
2104:                                        float fsample = fsamples[s + b];
2105:                                        int isample = Float
2106:                                                .floatToIntBits(fsample);
2107:                                        currTile[tcount++] = (byte) ((isample & 0xff000000) >> 24);
2108:                                        currTile[tcount++] = (byte) ((isample & 0x00ff0000) >> 16);
2109:                                        currTile[tcount++] = (byte) ((isample & 0x0000ff00) >> 8);
2110:                                        currTile[tcount++] = (byte) (isample & 0x000000ff);
2111:                                    }
2112:                                }
2113:                            } else { // ByteOrder.LITLE_ENDIAN
2114:                                for (int s = 0; s < numSamples; s += xSkip) {
2115:                                    for (int b = 0; b < numBands; b++) {
2116:                                        float fsample = fsamples[s + b];
2117:                                        int isample = Float
2118:                                                .floatToIntBits(fsample);
2119:                                        currTile[tcount++] = (byte) (isample & 0x000000ff);
2120:                                        currTile[tcount++] = (byte) ((isample & 0x0000ff00) >> 8);
2121:                                        currTile[tcount++] = (byte) ((isample & 0x00ff0000) >> 16);
2122:                                        currTile[tcount++] = (byte) ((isample & 0xff000000) >> 24);
2123:                                    }
2124:                                }
2125:                            }
2126:                        } else {
2127:                            if (isRescaling) {
2128:                                // XXX Need to verify this for signed vs. unsigned.
2129:                                // XXX The following gives saturated results when the
2130:                                // original data are in the signed integer range.
2131:                                long[] maxIn = new long[numBands];
2132:                                long[] halfIn = new long[numBands];
2133:                                long maxOut = (1L << (long) bitDepth) - 1L;
2134:
2135:                                for (int b = 0; b < numBands; b++) {
2136:                                    maxIn[b] = ((1L << (long) sampleSize[b]) - 1L);
2137:                                    halfIn[b] = maxIn[b] / 2;
2138:                                }
2139:
2140:                                if (stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
2141:                                    for (int s = 0; s < numSamples; s += xSkip) {
2142:                                        for (int b = 0; b < numBands; b++) {
2143:                                            long sampleOut = (samples[s + b]
2144:                                                    * maxOut + halfIn[b])
2145:                                                    / maxIn[b];
2146:                                            currTile[tcount++] = (byte) ((sampleOut & 0xff000000) >> 24);
2147:                                            currTile[tcount++] = (byte) ((sampleOut & 0x00ff0000) >> 16);
2148:                                            currTile[tcount++] = (byte) ((sampleOut & 0x0000ff00) >> 8);
2149:                                            currTile[tcount++] = (byte) (sampleOut & 0x000000ff);
2150:                                        }
2151:                                    }
2152:                                } else { // ByteOrder.LITLE_ENDIAN
2153:                                    for (int s = 0; s < numSamples; s += xSkip) {
2154:                                        for (int b = 0; b < numBands; b++) {
2155:                                            long sampleOut = (samples[s + b]
2156:                                                    * maxOut + halfIn[b])
2157:                                                    / maxIn[b];
2158:                                            currTile[tcount++] = (byte) (sampleOut & 0x000000ff);
2159:                                            currTile[tcount++] = (byte) ((sampleOut & 0x0000ff00) >> 8);
2160:                                            currTile[tcount++] = (byte) ((sampleOut & 0x00ff0000) >> 16);
2161:                                            currTile[tcount++] = (byte) ((sampleOut & 0xff000000) >> 24);
2162:                                        }
2163:                                    }
2164:                                }
2165:                            } else {
2166:                                if (stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
2167:                                    for (int s = 0; s < numSamples; s += xSkip) {
2168:                                        for (int b = 0; b < numBands; b++) {
2169:                                            int isample = samples[s + b];
2170:                                            currTile[tcount++] = (byte) ((isample & 0xff000000) >> 24);
2171:                                            currTile[tcount++] = (byte) ((isample & 0x00ff0000) >> 16);
2172:                                            currTile[tcount++] = (byte) ((isample & 0x0000ff00) >> 8);
2173:                                            currTile[tcount++] = (byte) (isample & 0x000000ff);
2174:                                        }
2175:                                    }
2176:                                } else { // ByteOrder.LITLE_ENDIAN
2177:                                    for (int s = 0; s < numSamples; s += xSkip) {
2178:                                        for (int b = 0; b < numBands; b++) {
2179:                                            int isample = samples[s + b];
2180:                                            currTile[tcount++] = (byte) (isample & 0x000000ff);
2181:                                            currTile[tcount++] = (byte) ((isample & 0x0000ff00) >> 8);
2182:                                            currTile[tcount++] = (byte) ((isample & 0x00ff0000) >> 16);
2183:                                            currTile[tcount++] = (byte) ((isample & 0xff000000) >> 24);
2184:                                        }
2185:                                    }
2186:                                }
2187:                            }
2188:                        }
2189:                        break;
2190:                    }
2191:                }
2192:
2193:                int[] bitsPerSample = new int[numBands];
2194:                for (int i = 0; i < bitsPerSample.length; i++) {
2195:                    bitsPerSample[i] = bitDepth;
2196:                }
2197:
2198:                int byteCount = compressor.encode(currTile, 0, hpixels,
2199:                        vpixels, bitsPerSample, bytesPerRow);
2200:                return byteCount;
2201:            }
2202:
2203:            // Check two int arrays for value equality, always returns false
2204:            // if either array is null
2205:            private boolean equals(int[] s0, int[] s1) {
2206:                if (s0 == null || s1 == null) {
2207:                    return false;
2208:                }
2209:                if (s0.length != s1.length) {
2210:                    return false;
2211:                }
2212:                for (int i = 0; i < s0.length; i++) {
2213:                    if (s0[i] != s1[i]) {
2214:                        return false;
2215:                    }
2216:                }
2217:                return true;
2218:            }
2219:
2220:            // Initialize the scale/scale0 or scaleh/scalel arrays to
2221:            // hold the results of scaling an input value to the desired
2222:            // output bit depth
2223:            // XXX Need to verify this rescaling for signed vs. unsigned.
2224:            private void initializeScaleTables(int[] sampleSize) {
2225:                // Save the sample size in the instance variable.
2226:
2227:                // If the existing tables are still valid, just return.
2228:                if (bitDepth == scalingBitDepth
2229:                        && equals(sampleSize, this .sampleSize)) {
2230:                    if (DEBUG) {
2231:                        System.out
2232:                                .println("Returning from initializeScaleTables()");
2233:                    }
2234:                    return;
2235:                }
2236:
2237:                // Reset scaling variables.
2238:                isRescaling = false;
2239:                scalingBitDepth = -1;
2240:                scale = scalel = scaleh = null;
2241:                scale0 = null;
2242:
2243:                // Set global sample size to parameter.
2244:                this .sampleSize = sampleSize;
2245:
2246:                // Check whether rescaling is called for.
2247:                if (bitDepth <= 16) {
2248:                    for (int b = 0; b < numBands; b++) {
2249:                        if (sampleSize[b] != bitDepth) {
2250:                            isRescaling = true;
2251:                            break;
2252:                        }
2253:                    }
2254:                }
2255:
2256:                if (DEBUG) {
2257:                    System.out.println("isRescaling = " + isRescaling);
2258:                }
2259:
2260:                // If not rescaling then return after saving the sample size.
2261:                if (!isRescaling) {
2262:                    return;
2263:                }
2264:
2265:                // Compute new tables
2266:                this .scalingBitDepth = bitDepth;
2267:                int maxOutSample = (1 << bitDepth) - 1;
2268:                if (bitDepth <= 8) {
2269:                    scale = new byte[numBands][];
2270:                    for (int b = 0; b < numBands; b++) {
2271:                        int maxInSample = (1 << sampleSize[b]) - 1;
2272:                        int halfMaxInSample = maxInSample / 2;
2273:                        scale[b] = new byte[maxInSample + 1];
2274:                        for (int s = 0; s <= maxInSample; s++) {
2275:                            scale[b][s] = (byte) ((s * maxOutSample + halfMaxInSample) / maxInSample);
2276:                        }
2277:                    }
2278:                    scale0 = scale[0];
2279:                    scaleh = scalel = null;
2280:                } else if (bitDepth <= 16) {
2281:                    // Divide scaling table into high and low bytes
2282:                    scaleh = new byte[numBands][];
2283:                    scalel = new byte[numBands][];
2284:
2285:                    for (int b = 0; b < numBands; b++) {
2286:                        int maxInSample = (1 << sampleSize[b]) - 1;
2287:                        int halfMaxInSample = maxInSample / 2;
2288:                        scaleh[b] = new byte[maxInSample + 1];
2289:                        scalel[b] = new byte[maxInSample + 1];
2290:                        for (int s = 0; s <= maxInSample; s++) {
2291:                            int val = (s * maxOutSample + halfMaxInSample)
2292:                                    / maxInSample;
2293:                            scaleh[b][s] = (byte) (val >> 8);
2294:                            scalel[b][s] = (byte) (val & 0xff);
2295:                        }
2296:                    }
2297:                    scale = null;
2298:                    scale0 = null;
2299:                }
2300:            }
2301:
2302:            public void write(IIOMetadata sm, IIOImage iioimage,
2303:                    ImageWriteParam p) throws IOException {
2304:                write(sm, iioimage, p, true, true);
2305:            }
2306:
2307:            private void writeHeader() throws IOException {
2308:                if (streamMetadata != null) {
2309:                    this .byteOrder = streamMetadata.byteOrder;
2310:                } else {
2311:                    this .byteOrder = ByteOrder.BIG_ENDIAN;
2312:                }
2313:
2314:                stream.setByteOrder(byteOrder);
2315:                if (byteOrder == ByteOrder.BIG_ENDIAN) {
2316:                    stream.writeShort(0x4d4d);
2317:                } else {
2318:                    stream.writeShort(0x4949);
2319:                }
2320:
2321:                stream.writeShort(42); // Magic number
2322:                stream.writeInt(0); // Offset of first IFD (0 == none)
2323:
2324:                nextSpace = stream.getStreamPosition();
2325:                headerPosition = nextSpace - 8;
2326:            }
2327:
2328:            private void write(IIOMetadata sm, IIOImage iioimage,
2329:                    ImageWriteParam p, boolean writeHeader, boolean writeData)
2330:                    throws IOException {
2331:                if (stream == null) {
2332:                    throw new IllegalStateException("output == null!");
2333:                }
2334:                if (iioimage == null) {
2335:                    throw new IllegalArgumentException("image == null!");
2336:                }
2337:                if (iioimage.hasRaster() && !canWriteRasters()) {
2338:                    throw new UnsupportedOperationException(
2339:                            "TIFF ImageWriter cannot write Rasters!");
2340:                }
2341:
2342:                this .image = iioimage.getRenderedImage();
2343:                SampleModel sampleModel = image.getSampleModel();
2344:
2345:                this .sourceXOffset = image.getMinX();
2346:                this .sourceYOffset = image.getMinY();
2347:                this .sourceWidth = image.getWidth();
2348:                this .sourceHeight = image.getHeight();
2349:
2350:                Rectangle imageBounds = new Rectangle(sourceXOffset,
2351:                        sourceYOffset, sourceWidth, sourceHeight);
2352:
2353:                ColorModel colorModel = null;
2354:                if (p == null) {
2355:                    this .param = getDefaultWriteParam();
2356:                    this .sourceBands = null;
2357:                    this .periodX = 1;
2358:                    this .periodY = 1;
2359:                    this .numBands = sampleModel.getNumBands();
2360:                    colorModel = image.getColorModel();
2361:                } else {
2362:                    this .param = p;
2363:
2364:                    // Get source region and subsampling factors
2365:                    Rectangle sourceRegion = param.getSourceRegion();
2366:                    if (sourceRegion != null) {
2367:                        // Clip to actual image bounds
2368:                        sourceRegion = sourceRegion.intersection(imageBounds);
2369:
2370:                        sourceXOffset = sourceRegion.x;
2371:                        sourceYOffset = sourceRegion.y;
2372:                        sourceWidth = sourceRegion.width;
2373:                        sourceHeight = sourceRegion.height;
2374:                    }
2375:
2376:                    // Adjust for subsampling offsets
2377:                    int gridX = param.getSubsamplingXOffset();
2378:                    int gridY = param.getSubsamplingYOffset();
2379:                    this .sourceXOffset += gridX;
2380:                    this .sourceYOffset += gridY;
2381:                    this .sourceWidth -= gridX;
2382:                    this .sourceHeight -= gridY;
2383:
2384:                    // Get subsampling factors
2385:                    this .periodX = param.getSourceXSubsampling();
2386:                    this .periodY = param.getSourceYSubsampling();
2387:
2388:                    int[] sBands = param.getSourceBands();
2389:                    if (sBands != null) {
2390:                        sourceBands = sBands;
2391:                        this .numBands = sourceBands.length;
2392:                    } else {
2393:                        this .numBands = sampleModel.getNumBands();
2394:                    }
2395:
2396:                    ImageTypeSpecifier destType = p.getDestinationType();
2397:                    if (destType != null) {
2398:                        ColorModel cm = destType.getColorModel();
2399:                        if (cm.getNumComponents() == numBands) {
2400:                            colorModel = cm;
2401:                        }
2402:                    }
2403:
2404:                    if (colorModel == null) {
2405:                        colorModel = image.getColorModel();
2406:                    }
2407:                }
2408:
2409:                this .imageType = new ImageTypeSpecifier(colorModel, sampleModel);
2410:
2411:                ImageUtil.canEncodeImage(this , this .imageType);
2412:
2413:                // Compute output dimensions
2414:                int destWidth = (sourceWidth + periodX - 1) / periodX;
2415:                int destHeight = (sourceHeight + periodY - 1) / periodY;
2416:                if (destWidth <= 0 || destHeight <= 0) {
2417:                    throw new IllegalArgumentException("Empty source region!");
2418:                }
2419:
2420:                // this.bitDepth = 8; // XXX fix?
2421:
2422:                clearAbortRequest();
2423:                processImageStarted(0);
2424:
2425:                // Optionally write the header.
2426:                if (writeHeader) {
2427:                    // Clear previous stream metadata.
2428:                    this .streamMetadata = null;
2429:
2430:                    // Try to convert non-null input stream metadata.
2431:                    if (sm != null) {
2432:                        this .streamMetadata = (TIFFStreamMetadata) convertStreamMetadata(
2433:                                sm, param);
2434:                    }
2435:
2436:                    // Set to default if not converted.
2437:                    if (this .streamMetadata == null) {
2438:                        this .streamMetadata = (TIFFStreamMetadata) getDefaultStreamMetadata(param);
2439:                    }
2440:
2441:                    // Write the header.
2442:                    writeHeader();
2443:
2444:                    // Seek to the position of the IFD pointer in the header.
2445:                    stream.seek(headerPosition + 4);
2446:
2447:                    // Ensure IFD is written on a word boundary
2448:                    nextSpace = (nextSpace + 3) & ~0x3;
2449:
2450:                    // Write the pointer to the first IFD after the header.
2451:                    stream.writeInt((int) nextSpace);
2452:                }
2453:
2454:                // Write out the IFD and any sub IFDs, followed by a zero
2455:
2456:                // Clear previous image metadata.
2457:                this .imageMetadata = null;
2458:
2459:                // Initialize the metadata object.
2460:                IIOMetadata im = iioimage.getMetadata();
2461:                if (im != null) {
2462:                    if (im instanceof  TIFFImageMetadata) {
2463:                        // Clone the one passed in.
2464:                        this .imageMetadata = ((TIFFImageMetadata) im)
2465:                                .getShallowClone();
2466:                    } else if (Arrays.asList(im.getMetadataFormatNames())
2467:                            .contains(
2468:                                    TIFFImageMetadata.nativeMetadataFormatName)) {
2469:                        this .imageMetadata = convertNativeImageMetadata(im);
2470:                    } else if (im.isStandardMetadataFormatSupported()) {
2471:                        try {
2472:                            // Convert standard metadata.
2473:                            this .imageMetadata = convertStandardImageMetadata(im);
2474:                        } catch (IIOInvalidTreeException e) {
2475:                            // XXX Warning
2476:                        }
2477:                    }
2478:                }
2479:
2480:                // Use default metadata if still null.
2481:                if (this .imageMetadata == null) {
2482:                    this .imageMetadata = (TIFFImageMetadata) getDefaultImageMetadata(
2483:                            this .imageType, this .param);
2484:                }
2485:
2486:                // Set or overwrite mandatory fields in the root IFD
2487:                setupMetadata(colorModel, sampleModel, destWidth, destHeight);
2488:
2489:                // Set compressor fields.
2490:                compressor.setWriter(this );
2491:                // Metadata needs to be set on the compressor before the IFD is
2492:                // written as the compressor could modify the metadata.
2493:                compressor.setMetadata(imageMetadata);
2494:                compressor.setStream(stream);
2495:
2496:                // Initialize scaling tables for this image
2497:                int[] sampleSize = sampleModel.getSampleSize();
2498:                initializeScaleTables(sampleModel.getSampleSize());
2499:
2500:                // Determine whether bilevel.
2501:                this .isBilevel = ImageUtil
2502:                        .isBinary(this .image.getSampleModel());
2503:
2504:                // Check for photometric inversion.
2505:                this .isInverted = (nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO)
2506:                        || (nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO);
2507:
2508:                // Analyze image data suitability for direct copy.
2509:                this .isImageSimple = (isBilevel || (!isInverted && ImageUtil
2510:                        .imageIsContiguous(this .image)))
2511:                        && !isRescaling && // no value rescaling
2512:                        sourceBands == null && // no subbanding
2513:                        periodX == 1 && periodY == 1 && // no subsampling
2514:                        colorConverter == null;
2515:
2516:                TIFFIFD rootIFD = imageMetadata.getRootIFD();
2517:
2518:                rootIFD.writeToStream(stream);
2519:
2520:                this .nextIFDPointerPos = stream.getStreamPosition();
2521:                stream.writeInt(0);
2522:
2523:                // Seek to end of IFD data
2524:                long lastIFDPosition = rootIFD.getLastPosition();
2525:                stream.seek(lastIFDPosition);
2526:                if (lastIFDPosition > this .nextSpace) {
2527:                    this .nextSpace = lastIFDPosition;
2528:                }
2529:
2530:                // If not writing the image data, i.e., if writing or inserting an
2531:                // empty image, return.
2532:                if (!writeData) {
2533:                    return;
2534:                }
2535:
2536:                // Get positions of fields within the IFD to update as we write
2537:                // each strip or tile
2538:                long stripOrTileByteCountsPosition = rootIFD
2539:                        .getStripOrTileByteCountsPosition();
2540:                long stripOrTileOffsetsPosition = rootIFD
2541:                        .getStripOrTileOffsetsPosition();
2542:
2543:                // Compute total number of pixels for progress notification
2544:                this .totalPixels = tileWidth * tileLength * tilesDown
2545:                        * tilesAcross;
2546:                this .pixelsDone = 0;
2547:
2548:                // Write the image, a strip or tile at a time
2549:                for (int tj = 0; tj < tilesDown; tj++) {
2550:                    for (int ti = 0; ti < tilesAcross; ti++) {
2551:                        long pos = stream.getStreamPosition();
2552:
2553:                        // Write the (possibly compressed) tile data
2554:
2555:                        Rectangle tileRect = new Rectangle(sourceXOffset + ti
2556:                                * tileWidth * periodX, sourceYOffset + tj
2557:                                * tileLength * periodY, tileWidth * periodX,
2558:                                tileLength * periodY);
2559:                        // tileRect = tileRect.intersection(imageBounds); // XXX
2560:
2561:                        try {
2562:                            int byteCount = writeTile(tileRect, compressor);
2563:
2564:                            if (pos + byteCount > nextSpace) {
2565:                                nextSpace = pos + byteCount;
2566:                            }
2567:
2568:                            pixelsDone += tileRect.width * tileRect.height;
2569:                            processImageProgress(100.0F * pixelsDone
2570:                                    / totalPixels);
2571:
2572:                            // Fill in the offset and byte count for the file
2573:                            stream.mark();
2574:                            stream.seek(stripOrTileOffsetsPosition);
2575:                            stream.writeInt((int) pos);
2576:                            stripOrTileOffsetsPosition += 4;
2577:
2578:                            stream.seek(stripOrTileByteCountsPosition);
2579:                            stream.writeInt(byteCount);
2580:                            stripOrTileByteCountsPosition += 4;
2581:                            stream.reset();
2582:                        } catch (IOException e) {
2583:                            throw new IIOException(
2584:                                    "I/O error writing TIFF file!", e);
2585:                        }
2586:
2587:                        if (abortRequested()) {
2588:                            processWriteAborted();
2589:                            return;
2590:                        }
2591:                    }
2592:                }
2593:
2594:                processImageComplete();
2595:            }
2596:
2597:            public boolean canWriteSequence() {
2598:                return true;
2599:            }
2600:
2601:            public void prepareWriteSequence(IIOMetadata streamMetadata)
2602:                    throws IOException {
2603:                if (getOutput() == null) {
2604:                    throw new IllegalStateException("getOutput() == null!");
2605:                }
2606:
2607:                // Set up stream metadata.
2608:                if (streamMetadata != null) {
2609:                    streamMetadata = convertStreamMetadata(streamMetadata, null);
2610:                }
2611:                if (streamMetadata == null) {
2612:                    streamMetadata = getDefaultStreamMetadata(null);
2613:                }
2614:                this .streamMetadata = (TIFFStreamMetadata) streamMetadata;
2615:
2616:                // Write the header.
2617:                writeHeader();
2618:
2619:                // Set the sequence flag.
2620:                this .isWritingSequence = true;
2621:            }
2622:
2623:            public void writeToSequence(IIOImage image, ImageWriteParam param)
2624:                    throws IOException {
2625:                // Check sequence flag.
2626:                if (!this .isWritingSequence) {
2627:                    throw new IllegalStateException(
2628:                            "prepareWriteSequence() has not been called!");
2629:                }
2630:
2631:                // Append image.
2632:                writeInsert(-1, image, param);
2633:            }
2634:
2635:            public void endWriteSequence() throws IOException {
2636:                // Check output.
2637:                if (getOutput() == null) {
2638:                    throw new IllegalStateException("getOutput() == null!");
2639:                }
2640:
2641:                // Check sequence flag.
2642:                if (!isWritingSequence) {
2643:                    throw new IllegalStateException(
2644:                            "prepareWriteSequence() has not been called!");
2645:                }
2646:
2647:                // Unset sequence flag.
2648:                this .isWritingSequence = false;
2649:            }
2650:
2651:            public boolean canInsertImage(int imageIndex) throws IOException {
2652:                if (getOutput() == null) {
2653:                    throw new IllegalStateException("getOutput() == null!");
2654:                }
2655:
2656:                // Mark position as locateIFD() will seek to IFD at imageIndex.
2657:                stream.mark();
2658:
2659:                // locateIFD() will throw an IndexOutOfBoundsException if
2660:                // imageIndex is < -1 or is too big thereby satisfying the spec.
2661:                long[] ifdpos = new long[1];
2662:                long[] ifd = new long[1];
2663:                locateIFD(imageIndex, ifdpos, ifd);
2664:
2665:                // Reset to position before locateIFD().
2666:                stream.reset();
2667:
2668:                return true;
2669:            }
2670:
2671:            // Locate start of IFD for image.
2672:            // Throws IIOException if not at a TIFF header and
2673:            // IndexOutOfBoundsException if imageIndex is < -1 or is too big.
2674:            private void locateIFD(int imageIndex, long[] ifdpos, long[] ifd)
2675:                    throws IOException {
2676:
2677:                if (imageIndex < -1) {
2678:                    throw new IndexOutOfBoundsException("imageIndex < -1!");
2679:                }
2680:
2681:                long startPos = stream.getStreamPosition();
2682:
2683:                stream.seek(headerPosition);
2684:                int byteOrder = stream.readUnsignedShort();
2685:                if (byteOrder == 0x4d4d) {
2686:                    stream.setByteOrder(ByteOrder.BIG_ENDIAN);
2687:                } else if (byteOrder == 0x4949) {
2688:                    stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
2689:                } else {
2690:                    stream.seek(startPos);
2691:                    throw new IIOException("Illegal byte order");
2692:                }
2693:                if (stream.readUnsignedShort() != 42) {
2694:                    stream.seek(startPos);
2695:                    throw new IIOException("Illegal magic number");
2696:                }
2697:
2698:                ifdpos[0] = stream.getStreamPosition();
2699:                ifd[0] = stream.readUnsignedInt();
2700:                if (ifd[0] == 0) {
2701:                    // imageIndex has to be >= -1 due to check above.
2702:                    if (imageIndex > 0) {
2703:                        stream.seek(startPos);
2704:                        throw new IndexOutOfBoundsException(
2705:                                "imageIndex is greater than the largest available index!");
2706:                    }
2707:                    return;
2708:                }
2709:                stream.seek(ifd[0]);
2710:
2711:                for (int i = 0; imageIndex == -1 || i < imageIndex; i++) {
2712:                    int numFields;
2713:                    try {
2714:                        numFields = stream.readShort();
2715:                    } catch (EOFException eof) {
2716:                        stream.seek(startPos);
2717:                        ifd[0] = 0;
2718:                        return;
2719:                    }
2720:
2721:                    stream.skipBytes(12 * numFields);
2722:
2723:                    ifdpos[0] = stream.getStreamPosition();
2724:                    ifd[0] = stream.readUnsignedInt();
2725:                    if (ifd[0] == 0) {
2726:                        if (imageIndex != -1 && i < imageIndex - 1) {
2727:                            stream.seek(startPos);
2728:                            throw new IndexOutOfBoundsException(
2729:                                    "imageIndex is greater than the largest available index!");
2730:                        }
2731:                        break;
2732:                    }
2733:                    stream.seek(ifd[0]);
2734:                }
2735:            }
2736:
2737:            public void writeInsert(int imageIndex, IIOImage image,
2738:                    ImageWriteParam param) throws IOException {
2739:                insert(imageIndex, image, param, true);
2740:            }
2741:
2742:            private void insert(int imageIndex, IIOImage image,
2743:                    ImageWriteParam param, boolean writeData)
2744:                    throws IOException {
2745:                if (stream == null) {
2746:                    throw new IllegalStateException("Output not set!");
2747:                }
2748:                if (image == null) {
2749:                    throw new IllegalArgumentException("image == null!");
2750:                }
2751:
2752:                // Locate the position of the old IFD (ifd) and the location
2753:                // of the pointer to that position (ifdpos).
2754:                long[] ifdpos = new long[1];
2755:                long[] ifd = new long[1];
2756:
2757:                // locateIFD() will throw an IndexOutOfBoundsException if
2758:                // imageIndex is < -1 or is too big thereby satisfying the spec.
2759:                locateIFD(imageIndex, ifdpos, ifd);
2760:
2761:                // Seek to the position containing the pointer to the old IFD.
2762:                stream.seek(ifdpos[0]);
2763:
2764:                // Update next space pointer in anticipation of next write.
2765:                if (ifdpos[0] + 4 > nextSpace) {
2766:                    nextSpace = ifdpos[0] + 4;
2767:                }
2768:
2769:                // Ensure IFD is written on a word boundary
2770:                nextSpace = (nextSpace + 3) & ~0x3;
2771:
2772:                // Update the value to point to the next available space.
2773:                stream.writeInt((int) nextSpace);
2774:
2775:                // Seek to the next available space.
2776:                stream.seek(nextSpace);
2777:
2778:                // Write the image (IFD and data).
2779:                write(null, image, param, false, writeData);
2780:
2781:                // Seek to the position containing the pointer in the new IFD.
2782:                stream.seek(nextIFDPointerPos);
2783:
2784:                // Update the new IFD to point to the old IFD.
2785:                stream.writeInt((int) ifd[0]);
2786:                // Don't need to update nextSpace here as already done in write().
2787:            }
2788:
2789:            // ----- BEGIN insert/writeEmpty methods -----
2790:
2791:            // XXX Move local variable(s) up.
2792:            private boolean isInsertingEmpty = false;
2793:            private boolean isWritingEmpty = false;
2794:
2795:            private boolean isEncodingEmpty() {
2796:                return isInsertingEmpty || isWritingEmpty;
2797:            }
2798:
2799:            public boolean canInsertEmpty(int imageIndex) throws IOException {
2800:                return canInsertImage(imageIndex);
2801:            }
2802:
2803:            public boolean canWriteEmpty() throws IOException {
2804:                if (getOutput() == null) {
2805:                    throw new IllegalStateException("getOutput() == null!");
2806:                }
2807:                return true;
2808:            }
2809:
2810:            // Check state and parameters for writing or inserting empty images.
2811:            private void checkParamsEmpty(ImageTypeSpecifier imageType,
2812:                    int width, int height, List thumbnails) {
2813:                if (getOutput() == null) {
2814:                    throw new IllegalStateException("getOutput() == null!");
2815:                }
2816:
2817:                if (imageType == null) {
2818:                    throw new IllegalArgumentException("imageType == null!");
2819:                }
2820:
2821:                if (width < 1 || height < 1) {
2822:                    throw new IllegalArgumentException(
2823:                            "width < 1 || height < 1!");
2824:                }
2825:
2826:                if (thumbnails != null) {
2827:                    int numThumbs = thumbnails.size();
2828:                    for (int i = 0; i < numThumbs; i++) {
2829:                        Object thumb = thumbnails.get(i);
2830:                        if (thumb == null || !(thumb instanceof  BufferedImage)) {
2831:                            throw new IllegalArgumentException(
2832:                                    "thumbnails contains null references or objects other than BufferedImages!");
2833:                        }
2834:                    }
2835:                }
2836:
2837:                if (this .isInsertingEmpty) {
2838:                    throw new IllegalStateException(
2839:                            "Previous call to prepareInsertEmpty() without corresponding call to endInsertEmpty()!");
2840:                }
2841:
2842:                if (this .isWritingEmpty) {
2843:                    throw new IllegalStateException(
2844:                            "Previous call to prepareWriteEmpty() without corresponding call to endWriteEmpty()!");
2845:                }
2846:            }
2847:
2848:            public void prepareInsertEmpty(int imageIndex,
2849:                    ImageTypeSpecifier imageType, int width, int height,
2850:                    IIOMetadata imageMetadata, List thumbnails,
2851:                    ImageWriteParam param) throws IOException {
2852:                checkParamsEmpty(imageType, width, height, thumbnails);
2853:
2854:                this .isInsertingEmpty = true;
2855:
2856:                SampleModel emptySM = imageType.getSampleModel();
2857:                RenderedImage emptyImage = new EmptyImage(0, 0, width, height,
2858:                        0, 0, emptySM.getWidth(), emptySM.getHeight(), emptySM,
2859:                        imageType.getColorModel());
2860:
2861:                insert(imageIndex,
2862:                        new IIOImage(emptyImage, null, imageMetadata), param,
2863:                        false);
2864:            }
2865:
2866:            public void prepareWriteEmpty(IIOMetadata streamMetadata,
2867:                    ImageTypeSpecifier imageType, int width, int height,
2868:                    IIOMetadata imageMetadata, List thumbnails,
2869:                    ImageWriteParam param) throws IOException {
2870:                checkParamsEmpty(imageType, width, height, thumbnails);
2871:
2872:                this .isWritingEmpty = true;
2873:
2874:                SampleModel emptySM = imageType.getSampleModel();
2875:                RenderedImage emptyImage = new EmptyImage(0, 0, width, height,
2876:                        0, 0, emptySM.getWidth(), emptySM.getHeight(), emptySM,
2877:                        imageType.getColorModel());
2878:
2879:                write(streamMetadata, new IIOImage(emptyImage, null,
2880:                        imageMetadata), param, true, false);
2881:            }
2882:
2883:            public void endInsertEmpty() throws IOException {
2884:                if (getOutput() == null) {
2885:                    throw new IllegalStateException("getOutput() == null!");
2886:                }
2887:
2888:                if (!this .isInsertingEmpty) {
2889:                    throw new IllegalStateException(
2890:                            "No previous call to prepareInsertEmpty()!");
2891:                }
2892:
2893:                if (this .isWritingEmpty) {
2894:                    throw new IllegalStateException(
2895:                            "Previous call to prepareWriteEmpty() without corresponding call to endWriteEmpty()!");
2896:                }
2897:
2898:                if (inReplacePixelsNest) {
2899:                    throw new IllegalStateException(
2900:                            "In nested call to prepareReplacePixels!");
2901:                }
2902:
2903:                this .isInsertingEmpty = false;
2904:            }
2905:
2906:            public void endWriteEmpty() throws IOException {
2907:                if (getOutput() == null) {
2908:                    throw new IllegalStateException("getOutput() == null!");
2909:                }
2910:
2911:                if (!this .isWritingEmpty) {
2912:                    throw new IllegalStateException(
2913:                            "No previous call to prepareWriteEmpty()!");
2914:                }
2915:
2916:                if (this .isInsertingEmpty) {
2917:                    throw new IllegalStateException(
2918:                            "Previous call to prepareInsertEmpty() without corresponding call to endInsertEmpty()!");
2919:                }
2920:
2921:                if (inReplacePixelsNest) {
2922:                    throw new IllegalStateException(
2923:                            "In nested call to prepareReplacePixels!");
2924:                }
2925:
2926:                this .isWritingEmpty = false;
2927:            }
2928:
2929:            // ----- END insert/writeEmpty methods -----
2930:
2931:            // ----- BEGIN replacePixels methods -----
2932:
2933:            private TIFFIFD readIFD(int imageIndex) throws IOException {
2934:                if (stream == null) {
2935:                    throw new IllegalStateException("Output not set!");
2936:                }
2937:                if (imageIndex < 0) {
2938:                    throw new IndexOutOfBoundsException("imageIndex < 0!");
2939:                }
2940:
2941:                stream.mark();
2942:                long[] ifdpos = new long[1];
2943:                long[] ifd = new long[1];
2944:                locateIFD(imageIndex, ifdpos, ifd);
2945:                if (ifd[0] == 0) {
2946:                    stream.reset();
2947:                    throw new IndexOutOfBoundsException(
2948:                            "imageIndex out of bounds!");
2949:                }
2950:
2951:                List tagSets = new ArrayList(1);
2952:                tagSets.add(BaselineTIFFTagSet.getInstance());
2953:                TIFFIFD rootIFD = new TIFFIFD(tagSets);
2954:                // XXX Ignore unknown fields in metadata presumably because
2955:                // any fields needed to write pixels would be known?
2956:                rootIFD.initialize(stream, true);
2957:                stream.reset();
2958:
2959:                return rootIFD;
2960:            }
2961:
2962:            public boolean canReplacePixels(int imageIndex) throws IOException {
2963:                if (getOutput() == null) {
2964:                    throw new IllegalStateException("getOutput() == null!");
2965:                }
2966:
2967:                TIFFIFD rootIFD = readIFD(imageIndex);
2968:                TIFFField f = rootIFD
2969:                        .getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
2970:                int compression = f.getAsInt(0);
2971:
2972:                return compression == BaselineTIFFTagSet.COMPRESSION_NONE;
2973:            }
2974:
2975:            private Object replacePixelsLock = new Object();
2976:
2977:            private int replacePixelsIndex = -1;
2978:            private TIFFImageMetadata replacePixelsMetadata = null;
2979:            private long[] replacePixelsTileOffsets = null;
2980:            private long[] replacePixelsByteCounts = null;
2981:            private long replacePixelsOffsetsPosition = 0L;
2982:            private long replacePixelsByteCountsPosition = 0L;
2983:            private Rectangle replacePixelsRegion = null;
2984:            private boolean inReplacePixelsNest = false;
2985:
2986:            private TIFFImageReader reader = null;
2987:
2988:            public void prepareReplacePixels(int imageIndex, Rectangle region)
2989:                    throws IOException {
2990:                synchronized (replacePixelsLock) {
2991:                    // Check state and parameters vis-a-vis ImageWriter specification.
2992:                    if (stream == null) {
2993:                        throw new IllegalStateException("Output not set!");
2994:                    }
2995:                    if (region == null) {
2996:                        throw new IllegalArgumentException("region == null!");
2997:                    }
2998:                    if (region.getWidth() < 1) {
2999:                        throw new IllegalArgumentException(
3000:                                "region.getWidth() < 1!");
3001:                    }
3002:                    if (region.getHeight() < 1) {
3003:                        throw new IllegalArgumentException(
3004:                                "region.getHeight() < 1!");
3005:                    }
3006:                    if (inReplacePixelsNest) {
3007:                        throw new IllegalStateException(
3008:                                "In nested call to prepareReplacePixels!");
3009:                    }
3010:
3011:                    // Read the IFD for the pixel replacement index.
3012:                    TIFFIFD replacePixelsIFD = readIFD(imageIndex);
3013:
3014:                    // Ensure that compression is "none".
3015:                    TIFFField f = replacePixelsIFD
3016:                            .getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
3017:                    int compression = f.getAsInt(0);
3018:                    if (compression != BaselineTIFFTagSet.COMPRESSION_NONE) {
3019:                        throw new UnsupportedOperationException(
3020:                                "canReplacePixels(imageIndex) == false!");
3021:                    }
3022:
3023:                    // Get the image dimensions.
3024:                    f = replacePixelsIFD
3025:                            .getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
3026:                    if (f == null) {
3027:                        throw new IIOException("Cannot read ImageWidth field.");
3028:                    }
3029:                    int w = f.getAsInt(0);
3030:
3031:                    f = replacePixelsIFD
3032:                            .getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
3033:                    if (f == null) {
3034:                        throw new IIOException("Cannot read ImageHeight field.");
3035:                    }
3036:                    int h = f.getAsInt(0);
3037:
3038:                    // Create image bounds.
3039:                    Rectangle bounds = new Rectangle(0, 0, w, h);
3040:
3041:                    // Intersect region with bounds.
3042:                    region = region.intersection(bounds);
3043:
3044:                    // Check for empty intersection.
3045:                    if (region.isEmpty()) {
3046:                        throw new IIOException(
3047:                                "Region does not intersect image bounds");
3048:                    }
3049:
3050:                    // Save the region.
3051:                    replacePixelsRegion = region;
3052:
3053:                    // Get the tile offsets.
3054:                    f = replacePixelsIFD
3055:                            .getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
3056:                    if (f == null) {
3057:                        f = replacePixelsIFD
3058:                                .getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
3059:                    }
3060:                    replacePixelsTileOffsets = f.getAsLongs();
3061:
3062:                    // Get the byte counts.
3063:                    f = replacePixelsIFD
3064:                            .getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
3065:                    if (f == null) {
3066:                        f = replacePixelsIFD
3067:                                .getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
3068:                    }
3069:                    replacePixelsByteCounts = f.getAsLongs();
3070:
3071:                    replacePixelsOffsetsPosition = replacePixelsIFD
3072:                            .getStripOrTileOffsetsPosition();
3073:                    replacePixelsByteCountsPosition = replacePixelsIFD
3074:                            .getStripOrTileByteCountsPosition();
3075:
3076:                    // Get the image metadata.
3077:                    replacePixelsMetadata = new TIFFImageMetadata(
3078:                            replacePixelsIFD);
3079:
3080:                    // Save the image index.
3081:                    replacePixelsIndex = imageIndex;
3082:
3083:                    // Set the pixel replacement flag.
3084:                    inReplacePixelsNest = true;
3085:                }
3086:            }
3087:
3088:            private Raster subsample(Raster raster, int[] sourceBands,
3089:                    int subOriginX, int subOriginY, int subPeriodX,
3090:                    int subPeriodY, int dstOffsetX, int dstOffsetY,
3091:                    Rectangle target) {
3092:
3093:                int x = raster.getMinX();
3094:                int y = raster.getMinY();
3095:                int w = raster.getWidth();
3096:                int h = raster.getHeight();
3097:                int b = raster.getSampleModel().getNumBands();
3098:                int t = raster.getSampleModel().getDataType();
3099:
3100:                int outMinX = XToTileX(x, subOriginX, subPeriodX) + dstOffsetX;
3101:                int outMinY = YToTileY(y, subOriginY, subPeriodY) + dstOffsetY;
3102:                int outMaxX = XToTileX(x + w - 1, subOriginX, subPeriodX)
3103:                        + dstOffsetX;
3104:                int outMaxY = YToTileY(y + h - 1, subOriginY, subPeriodY)
3105:                        + dstOffsetY;
3106:                int outWidth = outMaxX - outMinX + 1;
3107:                int outHeight = outMaxY - outMinY + 1;
3108:
3109:                if (outWidth <= 0 || outHeight <= 0)
3110:                    return null;
3111:
3112:                int inMinX = (outMinX - dstOffsetX) * subPeriodX + subOriginX;
3113:                int inMaxX = (outMaxX - dstOffsetX) * subPeriodX + subOriginX;
3114:                int inWidth = inMaxX - inMinX + 1;
3115:                int inMinY = (outMinY - dstOffsetY) * subPeriodY + subOriginY;
3116:                int inMaxY = (outMaxY - dstOffsetY) * subPeriodY + subOriginY;
3117:                int inHeight = inMaxY - inMinY + 1;
3118:
3119:                WritableRaster wr = raster.createCompatibleWritableRaster(
3120:                        outMinX, outMinY, outWidth, outHeight);
3121:
3122:                int jMax = inMinY + inHeight;
3123:
3124:                if (t == DataBuffer.TYPE_FLOAT || t == DataBuffer.TYPE_DOUBLE) {
3125:                    float[] fsamples = new float[inWidth];
3126:                    float[] fsubsamples = new float[outWidth];
3127:
3128:                    for (int k = 0; k < b; k++) {
3129:                        int outY = outMinY;
3130:                        for (int j = inMinY; j < jMax; j += subPeriodY) {
3131:                            raster.getSamples(inMinX, j, inWidth, 1, k,
3132:                                    fsamples);
3133:                            int s = 0;
3134:                            for (int i = 0; i < inWidth; i += subPeriodX) {
3135:                                fsubsamples[s++] = fsamples[i];
3136:                            }
3137:                            wr.setSamples(outMinX, outY++, outWidth, 1, k,
3138:                                    fsubsamples);
3139:                        }
3140:                    }
3141:                } else {
3142:                    int[] samples = new int[inWidth];
3143:                    int[] subsamples = new int[outWidth];
3144:
3145:                    for (int k = 0; k < b; k++) {
3146:                        int outY = outMinY;
3147:                        for (int j = inMinY; j < jMax; j += subPeriodY) {
3148:                            raster
3149:                                    .getSamples(inMinX, j, inWidth, 1, k,
3150:                                            samples);
3151:                            int s = 0;
3152:                            for (int i = 0; i < inWidth; i += subPeriodX) {
3153:                                subsamples[s++] = samples[i];
3154:                            }
3155:                            wr.setSamples(outMinX, outY++, outWidth, 1, k,
3156:                                    subsamples);
3157:                        }
3158:                    }
3159:                }
3160:
3161:                return wr.createChild(outMinX, outMinY, target.width,
3162:                        target.height, target.x, target.y, sourceBands);
3163:            }
3164:
3165:            public void replacePixels(RenderedImage image, ImageWriteParam param)
3166:                    throws IOException {
3167:
3168:                synchronized (replacePixelsLock) {
3169:                    // Check state and parameters vis-a-vis ImageWriter specification.
3170:                    if (stream == null) {
3171:                        throw new IllegalStateException("stream == null!");
3172:                    }
3173:
3174:                    if (image == null) {
3175:                        throw new IllegalArgumentException("image == null!");
3176:                    }
3177:
3178:                    if (!inReplacePixelsNest) {
3179:                        throw new IllegalStateException(
3180:                                "No previous call to prepareReplacePixels!");
3181:                    }
3182:
3183:                    // Subsampling values.
3184:                    int stepX = 1, stepY = 1, gridX = 0, gridY = 0;
3185:
3186:                    // Initialize the ImageWriteParam.
3187:                    if (param == null) {
3188:                        // Use the default.
3189:                        param = getDefaultWriteParam();
3190:                    } else {
3191:                        // Make a copy of the ImageWriteParam.
3192:                        ImageWriteParam paramCopy = getDefaultWriteParam();
3193:
3194:                        // Force uncompressed.
3195:                        paramCopy
3196:                                .setCompressionMode(ImageWriteParam.MODE_DISABLED);
3197:
3198:                        // Force tiling to remain as in the already written image.
3199:                        paramCopy
3200:                                .setTilingMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
3201:
3202:                        // Retain source and destination region and band settings.
3203:                        paramCopy.setDestinationOffset(param
3204:                                .getDestinationOffset());
3205:                        paramCopy.setSourceBands(param.getSourceBands());
3206:                        paramCopy.setSourceRegion(param.getSourceRegion());
3207:
3208:                        // Save original subsampling values for subsampling the
3209:                        // replacement data - not the data re-read from the image.
3210:                        stepX = param.getSourceXSubsampling();
3211:                        stepY = param.getSourceYSubsampling();
3212:                        gridX = param.getSubsamplingXOffset();
3213:                        gridY = param.getSubsamplingYOffset();
3214:
3215:                        // Replace the param.
3216:                        param = paramCopy;
3217:                    }
3218:
3219:                    // Check band count and bit depth compatibility.
3220:                    TIFFField f = replacePixelsMetadata
3221:                            .getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
3222:                    if (f == null) {
3223:                        throw new IIOException(
3224:                                "Cannot read destination BitsPerSample");
3225:                    }
3226:                    int[] dstBitsPerSample = f.getAsInts();
3227:                    int[] srcBitsPerSample = image.getSampleModel()
3228:                            .getSampleSize();
3229:                    int[] sourceBands = param.getSourceBands();
3230:                    if (sourceBands != null) {
3231:                        if (sourceBands.length != dstBitsPerSample.length) {
3232:                            throw new IIOException(
3233:                                    "Source and destination have different SamplesPerPixel");
3234:                        }
3235:                        for (int i = 0; i < sourceBands.length; i++) {
3236:                            if (dstBitsPerSample[i] != srcBitsPerSample[sourceBands[i]]) {
3237:                                throw new IIOException(
3238:                                        "Source and destination have different BitsPerSample");
3239:                            }
3240:                        }
3241:                    } else {
3242:                        int srcNumBands = image.getSampleModel().getNumBands();
3243:                        if (srcNumBands != dstBitsPerSample.length) {
3244:                            throw new IIOException(
3245:                                    "Source and destination have different SamplesPerPixel");
3246:                        }
3247:                        for (int i = 0; i < srcNumBands; i++) {
3248:                            if (dstBitsPerSample[i] != srcBitsPerSample[i]) {
3249:                                throw new IIOException(
3250:                                        "Source and destination have different BitsPerSample");
3251:                            }
3252:                        }
3253:                    }
3254:
3255:                    // Get the source image bounds.
3256:                    Rectangle srcImageBounds = new Rectangle(image.getMinX(),
3257:                            image.getMinY(), image.getWidth(), image
3258:                                    .getHeight());
3259:
3260:                    // Initialize the source rect.
3261:                    Rectangle srcRect = param.getSourceRegion();
3262:                    if (srcRect == null) {
3263:                        srcRect = srcImageBounds;
3264:                    }
3265:
3266:                    // Set subsampling grid parameters.
3267:                    int subPeriodX = stepX;
3268:                    int subPeriodY = stepY;
3269:                    int subOriginX = gridX + srcRect.x;
3270:                    int subOriginY = gridY + srcRect.y;
3271:
3272:                    // Intersect with the source bounds.
3273:                    if (!srcRect.equals(srcImageBounds)) {
3274:                        srcRect = srcRect.intersection(srcImageBounds);
3275:                        if (srcRect.isEmpty()) {
3276:                            throw new IllegalArgumentException(
3277:                                    "Source region does not intersect source image!");
3278:                        }
3279:                    }
3280:
3281:                    // Get the destination offset.
3282:                    Point dstOffset = param.getDestinationOffset();
3283:
3284:                    // Forward map source rectangle to determine destination width.
3285:                    int dMinX = XToTileX(srcRect.x, subOriginX, subPeriodX)
3286:                            + dstOffset.x;
3287:                    int dMinY = YToTileY(srcRect.y, subOriginY, subPeriodY)
3288:                            + dstOffset.y;
3289:                    int dMaxX = XToTileX(srcRect.x + srcRect.width, subOriginX,
3290:                            subPeriodX)
3291:                            + dstOffset.x;
3292:                    int dMaxY = YToTileY(srcRect.y + srcRect.height,
3293:                            subOriginY, subPeriodY)
3294:                            + dstOffset.y;
3295:
3296:                    // Initialize the destination rectangle.
3297:                    Rectangle dstRect = new Rectangle(dstOffset.x, dstOffset.y,
3298:                            dMaxX - dMinX, dMaxY - dMinY);
3299:
3300:                    // Intersect with the replacement region.
3301:                    dstRect = dstRect.intersection(replacePixelsRegion);
3302:                    if (dstRect.isEmpty()) {
3303:                        throw new IllegalArgumentException(
3304:                                "Forward mapped source region does not intersect destination region!");
3305:                    }
3306:
3307:                    // Backward map to the active source region.
3308:                    int activeSrcMinX = (dstRect.x - dstOffset.x) * subPeriodX
3309:                            + subOriginX;
3310:                    int sxmax = (dstRect.x + dstRect.width - 1 - dstOffset.x)
3311:                            * subPeriodX + subOriginX;
3312:                    int activeSrcWidth = sxmax - activeSrcMinX + 1;
3313:
3314:                    int activeSrcMinY = (dstRect.y - dstOffset.y) * subPeriodY
3315:                            + subOriginY;
3316:                    int symax = (dstRect.y + dstRect.height - 1 - dstOffset.y)
3317:                            * subPeriodY + subOriginY;
3318:                    int activeSrcHeight = symax - activeSrcMinY + 1;
3319:                    Rectangle activeSrcRect = new Rectangle(activeSrcMinX,
3320:                            activeSrcMinY, activeSrcWidth, activeSrcHeight);
3321:                    if (activeSrcRect.intersection(srcImageBounds).isEmpty()) {
3322:                        throw new IllegalArgumentException(
3323:                                "Backward mapped destination region does not intersect source image!");
3324:                    }
3325:
3326:                    if (reader == null) {
3327:                        reader = new TIFFImageReader(new TIFFImageReaderSpi());
3328:                    } else {
3329:                        reader.reset();
3330:                    }
3331:
3332:                    stream.mark();
3333:
3334:                    try {
3335:                        stream.seek(headerPosition);
3336:                        reader.setInput(stream);
3337:
3338:                        this .imageMetadata = replacePixelsMetadata;
3339:                        this .param = param;
3340:                        SampleModel sm = image.getSampleModel();
3341:                        ColorModel cm = image.getColorModel();
3342:                        this .numBands = sm.getNumBands();
3343:                        this .imageType = new ImageTypeSpecifier(image);
3344:                        this .periodX = param.getSourceXSubsampling();
3345:                        this .periodY = param.getSourceYSubsampling();
3346:                        this .sourceBands = null;
3347:                        int[] sBands = param.getSourceBands();
3348:                        if (sBands != null) {
3349:                            this .sourceBands = sBands;
3350:                            this .numBands = sourceBands.length;
3351:                        }
3352:                        setupMetadata(cm, sm, reader
3353:                                .getWidth(replacePixelsIndex), reader
3354:                                .getHeight(replacePixelsIndex));
3355:                        int[] scaleSampleSize = sm.getSampleSize();
3356:                        initializeScaleTables(scaleSampleSize);
3357:
3358:                        // Determine whether bilevel.
3359:                        this .isBilevel = ImageUtil.isBinary(image
3360:                                .getSampleModel());
3361:
3362:                        // Check for photometric inversion.
3363:                        this .isInverted = (nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO)
3364:                                || (nativePhotometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO && photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO);
3365:
3366:                        // Analyze image data suitability for direct copy.
3367:                        this .isImageSimple = (isBilevel || (!isInverted && ImageUtil
3368:                                .imageIsContiguous(image)))
3369:                                && !isRescaling && // no value rescaling
3370:                                sourceBands == null && // no subbanding
3371:                                periodX == 1 && periodY == 1 && // no subsampling
3372:                                colorConverter == null;
3373:
3374:                        int minTileX = XToTileX(dstRect.x, 0, tileWidth);
3375:                        int minTileY = YToTileY(dstRect.y, 0, tileLength);
3376:                        int maxTileX = XToTileX(dstRect.x + dstRect.width - 1,
3377:                                0, tileWidth);
3378:                        int maxTileY = YToTileY(dstRect.y + dstRect.height - 1,
3379:                                0, tileLength);
3380:
3381:                        TIFFCompressor encoder = new TIFFNullCompressor();
3382:                        encoder.setWriter(this );
3383:                        encoder.setStream(stream);
3384:                        encoder.setMetadata(this .imageMetadata);
3385:
3386:                        Rectangle tileRect = new Rectangle();
3387:                        for (int ty = minTileY; ty <= maxTileY; ty++) {
3388:                            for (int tx = minTileX; tx <= maxTileX; tx++) {
3389:                                int tileIndex = ty * tilesAcross + tx;
3390:                                boolean isEmpty = replacePixelsByteCounts[tileIndex] == 0L;
3391:                                WritableRaster raster;
3392:                                if (isEmpty) {
3393:                                    SampleModel tileSM = sm
3394:                                            .createCompatibleSampleModel(
3395:                                                    tileWidth, tileLength);
3396:                                    raster = Raster.createWritableRaster(
3397:                                            tileSM, null);
3398:                                } else {
3399:                                    BufferedImage tileImage = reader.readTile(
3400:                                            replacePixelsIndex, tx, ty);
3401:                                    raster = tileImage.getRaster();
3402:                                }
3403:
3404:                                tileRect.setLocation(tx * tileWidth, ty
3405:                                        * tileLength);
3406:                                tileRect.setSize(raster.getWidth(), raster
3407:                                        .getHeight());
3408:                                raster = raster.createWritableTranslatedChild(
3409:                                        tileRect.x, tileRect.y);
3410:
3411:                                Rectangle replacementRect = tileRect
3412:                                        .intersection(dstRect);
3413:
3414:                                int srcMinX = (replacementRect.x - dstOffset.x)
3415:                                        * subPeriodX + subOriginX;
3416:                                int srcXmax = (replacementRect.x
3417:                                        + replacementRect.width - 1 - dstOffset.x)
3418:                                        * subPeriodX + subOriginX;
3419:                                int srcWidth = srcXmax - srcMinX + 1;
3420:
3421:                                int srcMinY = (replacementRect.y - dstOffset.y)
3422:                                        * subPeriodY + subOriginY;
3423:                                int srcYMax = (replacementRect.y
3424:                                        + replacementRect.height - 1 - dstOffset.y)
3425:                                        * subPeriodY + subOriginY;
3426:                                int srcHeight = srcYMax - srcMinY + 1;
3427:                                Rectangle srcTileRect = new Rectangle(srcMinX,
3428:                                        srcMinY, srcWidth, srcHeight);
3429:
3430:                                Raster replacementData = image
3431:                                        .getData(srcTileRect);
3432:                                if (subPeriodX == 1 && subPeriodY == 1
3433:                                        && subOriginX == 0 && subOriginY == 0) {
3434:                                    replacementData = replacementData
3435:                                            .createChild(srcTileRect.x,
3436:                                                    srcTileRect.y,
3437:                                                    srcTileRect.width,
3438:                                                    srcTileRect.height,
3439:                                                    replacementRect.x,
3440:                                                    replacementRect.y,
3441:                                                    sourceBands);
3442:                                } else {
3443:                                    replacementData = subsample(
3444:                                            replacementData, sourceBands,
3445:                                            subOriginX, subOriginY, subPeriodX,
3446:                                            subPeriodY, dstOffset.x,
3447:                                            dstOffset.y, replacementRect);
3448:                                    if (replacementData == null) {
3449:                                        continue;
3450:                                    }
3451:                                }
3452:
3453:                                raster.setRect(replacementData);
3454:
3455:                                if (isEmpty) {
3456:                                    stream.seek(nextSpace);
3457:                                } else {
3458:                                    stream
3459:                                            .seek(replacePixelsTileOffsets[tileIndex]);
3460:                                }
3461:
3462:                                this .image = new SingleTileRenderedImage(
3463:                                        raster, cm);
3464:
3465:                                int numBytes = writeTile(tileRect, encoder);
3466:
3467:                                if (isEmpty) {
3468:                                    // Update Strip/TileOffsets and
3469:                                    // Strip/TileByteCounts fields.
3470:                                    stream.mark();
3471:                                    stream.seek(replacePixelsOffsetsPosition
3472:                                            + 4 * tileIndex);
3473:                                    stream.writeInt((int) nextSpace);
3474:                                    stream.seek(replacePixelsByteCountsPosition
3475:                                            + 4 * tileIndex);
3476:                                    stream.writeInt(numBytes);
3477:                                    stream.reset();
3478:
3479:                                    // Increment location of next available space.
3480:                                    nextSpace += numBytes;
3481:                                }
3482:                            }
3483:                        }
3484:
3485:                    } catch (IOException e) {
3486:                        throw e;
3487:                    } finally {
3488:                        stream.reset();
3489:                    }
3490:                }
3491:            }
3492:
3493:            public void replacePixels(Raster raster, ImageWriteParam param)
3494:                    throws IOException {
3495:                if (raster == null) {
3496:                    throw new IllegalArgumentException("raster == null!");
3497:                }
3498:
3499:                replacePixels(new SingleTileRenderedImage(raster, image
3500:                        .getColorModel()), param);
3501:            }
3502:
3503:            public void endReplacePixels() throws IOException {
3504:                synchronized (replacePixelsLock) {
3505:                    if (!this .inReplacePixelsNest) {
3506:                        throw new IllegalStateException(
3507:                                "No previous call to prepareReplacePixels()!");
3508:                    }
3509:                    replacePixelsIndex = -1;
3510:                    replacePixelsMetadata = null;
3511:                    replacePixelsTileOffsets = null;
3512:                    replacePixelsByteCounts = null;
3513:                    replacePixelsOffsetsPosition = 0L;
3514:                    replacePixelsByteCountsPosition = 0L;
3515:                    replacePixelsRegion = null;
3516:                    inReplacePixelsNest = false;
3517:                }
3518:            }
3519:
3520:            // ----- END replacePixels methods -----
3521:
3522:            public void reset() {
3523:                super .reset();
3524:
3525:                stream = null;
3526:                image = null;
3527:                imageType = null;
3528:                byteOrder = null;
3529:                param = null;
3530:                compressor = null;
3531:                colorConverter = null;
3532:                streamMetadata = null;
3533:                imageMetadata = null;
3534:
3535:                isWritingSequence = false;
3536:                isWritingEmpty = false;
3537:                isInsertingEmpty = false;
3538:
3539:                replacePixelsIndex = -1;
3540:                replacePixelsMetadata = null;
3541:                replacePixelsTileOffsets = null;
3542:                replacePixelsByteCounts = null;
3543:                replacePixelsOffsetsPosition = 0L;
3544:                replacePixelsByteCountsPosition = 0L;
3545:                replacePixelsRegion = null;
3546:                inReplacePixelsNest = false;
3547:            }
3548:
3549:            public void dispose() {
3550:                reset();
3551:                super .dispose();
3552:            }
3553:        }
3554:
3555:        class EmptyImage extends SimpleRenderedImage {
3556:            EmptyImage(int minX, int minY, int width, int height,
3557:                    int tileGridXOffset, int tileGridYOffset, int tileWidth,
3558:                    int tileHeight, SampleModel sampleModel,
3559:                    ColorModel colorModel) {
3560:                this .minX = minX;
3561:                this .minY = minY;
3562:                this .width = width;
3563:                this .height = height;
3564:                this .tileGridXOffset = tileGridXOffset;
3565:                this .tileGridYOffset = tileGridYOffset;
3566:                this .tileWidth = tileWidth;
3567:                this .tileHeight = tileHeight;
3568:                this .sampleModel = sampleModel;
3569:                this .colorModel = colorModel;
3570:            }
3571:
3572:            public Raster getTile(int tileX, int tileY) {
3573:                return null;
3574:            }
3575:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.