Source Code Cross Referenced for TIFFImageEncoder.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » com » sun » media » jai » codecimpl » 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.jai.codecimpl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: TIFFImageEncoder.java,v $
0003:         *
0004:         * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Use is subject to license terms.
0007:         *
0008:         * $Revision: 1.3 $
0009:         * $Date: 2006/02/22 23:03:30 $
0010:         * $State: Exp $
0011:         */
0012:        package com.sun.media.jai.codecimpl;
0013:
0014:        import java.io.ByteArrayOutputStream;
0015:        import java.io.File;
0016:        import java.io.FileInputStream;
0017:        import java.io.IOException;
0018:        import java.io.OutputStream;
0019:        import java.io.RandomAccessFile;
0020:        import java.awt.Point;
0021:        import java.awt.Rectangle;
0022:        import java.awt.color.ColorSpace;
0023:        import java.awt.image.BufferedImage;
0024:        import java.awt.image.ColorModel;
0025:        import java.awt.image.ComponentSampleModel;
0026:        import java.awt.image.DataBuffer;
0027:        import java.awt.image.DataBufferByte;
0028:        import java.awt.image.IndexColorModel;
0029:        import java.awt.image.MultiPixelPackedSampleModel;
0030:        import java.awt.image.Raster;
0031:        import java.awt.image.RenderedImage;
0032:        import java.awt.image.SampleModel;
0033:        import java.awt.image.WritableRaster;
0034:        import java.util.ArrayList;
0035:        import java.util.Iterator;
0036:        import java.util.SortedSet;
0037:        import java.util.TreeSet;
0038:        import java.util.zip.Deflater;
0039:        import com.sun.media.jai.codec.ImageEncoderImpl;
0040:        import com.sun.media.jai.codec.ImageEncodeParam;
0041:        import com.sun.media.jai.codec.JPEGEncodeParam;
0042:        import com.sun.media.jai.codec.SeekableOutputStream;
0043:        import com.sun.media.jai.codec.TIFFEncodeParam;
0044:        import com.sun.media.jai.codec.TIFFField;
0045:        import com.sun.media.jai.codecimpl.util.RasterFactory;
0046:
0047:        /**
0048:         * A baseline TIFF writer. The writer outputs TIFF images in either Bilevel,
0049:         * Greyscale, Palette color or Full Color modes.
0050:         * 
0051:         * @since EA4
0052:         */
0053:        public class TIFFImageEncoder extends ImageEncoderImpl {
0054:
0055:            // Image Types
0056:            private static final int TIFF_UNSUPPORTED = -1;
0057:            private static final int TIFF_BILEVEL_WHITE_IS_ZERO = 0;
0058:            private static final int TIFF_BILEVEL_BLACK_IS_ZERO = 1;
0059:            private static final int TIFF_GRAY = 2;
0060:            private static final int TIFF_PALETTE = 3;
0061:            private static final int TIFF_RGB = 4;
0062:            private static final int TIFF_CMYK = 5;
0063:            private static final int TIFF_YCBCR = 6;
0064:            private static final int TIFF_CIELAB = 7;
0065:            private static final int TIFF_GENERIC = 8;
0066:
0067:            // Compression types
0068:            private static final int COMP_NONE = TIFFEncodeParam.COMPRESSION_NONE;
0069:            private static final int COMP_GROUP3_1D = TIFFEncodeParam.COMPRESSION_GROUP3_1D;
0070:            private static final int COMP_GROUP3_2D = TIFFEncodeParam.COMPRESSION_GROUP3_2D;
0071:            private static final int COMP_GROUP4 = TIFFEncodeParam.COMPRESSION_GROUP4;
0072:            private static final int COMP_JPEG_TTN2 = TIFFEncodeParam.COMPRESSION_JPEG_TTN2;
0073:            private static final int COMP_PACKBITS = TIFFEncodeParam.COMPRESSION_PACKBITS;
0074:            private static final int COMP_DEFLATE = TIFFEncodeParam.COMPRESSION_DEFLATE;
0075:
0076:            // Incidental tags
0077:            private static final int TIFF_JPEG_TABLES = 347;
0078:            private static final int TIFF_YCBCR_SUBSAMPLING = 530;
0079:            private static final int TIFF_YCBCR_POSITIONING = 531;
0080:            private static final int TIFF_REF_BLACK_WHITE = 532;
0081:
0082:            // ExtraSamples types
0083:            private static final int EXTRA_SAMPLE_UNSPECIFIED = 0;
0084:            private static final int EXTRA_SAMPLE_ASSOCIATED_ALPHA = 1;
0085:            private static final int EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 2;
0086:
0087:            // Default values
0088:            private static final int DEFAULT_ROWS_PER_STRIP = 8;
0089:
0090:            // Little endian flag
0091:            private boolean isLittleEndian = false;
0092:
0093:            private static final char[] intsToChars(int[] intArray) {
0094:                int arrayLength = intArray.length;
0095:                char[] charArray = new char[arrayLength];
0096:                for (int i = 0; i < arrayLength; i++) {
0097:                    charArray[i] = (char) (intArray[i] & 0x0000ffff);
0098:                }
0099:                return charArray;
0100:            }
0101:
0102:            public TIFFImageEncoder(OutputStream output, ImageEncodeParam param) {
0103:                super (output, param);
0104:                if (this .param == null) {
0105:                    this .param = new TIFFEncodeParam();
0106:                }
0107:            }
0108:
0109:            /**
0110:             * Encodes a RenderedImage and writes the output to the
0111:             * OutputStream associated with this ImageEncoder.
0112:             */
0113:            public void encode(RenderedImage im) throws IOException {
0114:                // Get the encoding parameters.
0115:                TIFFEncodeParam encodeParam = (TIFFEncodeParam) param;
0116:
0117:                // Set the byte order flag before any data are written.
0118:                isLittleEndian = encodeParam.getLittleEndian();
0119:
0120:                // Write the file header (8 bytes).
0121:                writeFileHeader();
0122:
0123:                Iterator iter = encodeParam.getExtraImages();
0124:                if (iter != null) {
0125:                    int ifdOffset = 8;
0126:                    RenderedImage nextImage = im;
0127:                    TIFFEncodeParam nextParam = encodeParam;
0128:                    boolean hasNext;
0129:                    do {
0130:                        hasNext = iter.hasNext();
0131:                        ifdOffset = encode(nextImage, nextParam, ifdOffset,
0132:                                !hasNext);
0133:                        if (hasNext) {
0134:                            Object obj = iter.next();
0135:                            if (obj instanceof  RenderedImage) {
0136:                                nextImage = (RenderedImage) obj;
0137:                                nextParam = encodeParam;
0138:                            } else if (obj instanceof  Object[]) {
0139:                                Object[] o = (Object[]) obj;
0140:                                nextImage = (RenderedImage) o[0];
0141:                                nextParam = (TIFFEncodeParam) o[1];
0142:                            }
0143:                        }
0144:                    } while (hasNext);
0145:                } else {
0146:                    encode(im, encodeParam, 8, true);
0147:                }
0148:            }
0149:
0150:            private int encode(RenderedImage im, TIFFEncodeParam encodeParam,
0151:                    int ifdOffset, boolean isLast) throws IOException {
0152:                // Cannot store a packed byte image directly so reformat it.
0153:                if (CodecUtils.isPackedByteImage(im)) {
0154:                    // Get the source ColorModel.
0155:                    ColorModel sourceCM = im.getColorModel();
0156:
0157:                    // Create an equivalent ComponentColorModel.
0158:                    ColorModel destCM = RasterFactory
0159:                            .createComponentColorModel(DataBuffer.TYPE_BYTE,
0160:                                    sourceCM.getColorSpace(), sourceCM
0161:                                            .hasAlpha(), sourceCM
0162:                                            .isAlphaPremultiplied(), sourceCM
0163:                                            .getTransparency());
0164:
0165:                    // Create a raster which can contain the entire source.
0166:                    Point origin = new Point(im.getMinX(), im.getMinY());
0167:                    WritableRaster raster = Raster.createWritableRaster(destCM
0168:                            .createCompatibleSampleModel(im.getWidth(), im
0169:                                    .getHeight()), origin);
0170:
0171:                    // Copy the source data.
0172:                    raster.setRect(im.getData());
0173:
0174:                    // Replace the source reference with the new image.
0175:                    im = new SingleTileRenderedImage(raster, destCM);
0176:                }
0177:
0178:                // Currently all images are stored uncompressed.
0179:                int compression = encodeParam.getCompression();
0180:
0181:                // Get tiled output preference.
0182:                boolean isTiled = encodeParam.getWriteTiled();
0183:
0184:                // Set bounds.
0185:                int minX = im.getMinX();
0186:                int minY = im.getMinY();
0187:                int width = im.getWidth();
0188:                int height = im.getHeight();
0189:
0190:                // Get SampleModel.
0191:                SampleModel sampleModel = im.getSampleModel();
0192:
0193:                // Retrieve and verify sample size.
0194:                int sampleSize[] = sampleModel.getSampleSize();
0195:                for (int i = 1; i < sampleSize.length; i++) {
0196:                    if (sampleSize[i] != sampleSize[0]) {
0197:                        throw new RuntimeException(JaiI18N
0198:                                .getString("TIFFImageEncoder0"));
0199:                    }
0200:                }
0201:
0202:                // Check low bit limits.
0203:                int numBands = sampleModel.getNumBands();
0204:                if ((sampleSize[0] == 1 || sampleSize[0] == 4) && numBands != 1) {
0205:                    throw new RuntimeException(JaiI18N
0206:                            .getString("TIFFImageEncoder1"));
0207:                }
0208:
0209:                // Retrieve and verify data type.
0210:                int dataType = sampleModel.getDataType();
0211:                switch (dataType) {
0212:                case DataBuffer.TYPE_BYTE:
0213:                    if (sampleSize[0] != 1 && sampleSize[0] != 4
0214:                            && sampleSize[0] != 8) {
0215:                        throw new RuntimeException(JaiI18N
0216:                                .getString("TIFFImageEncoder2"));
0217:                    }
0218:                    break;
0219:                case DataBuffer.TYPE_SHORT:
0220:                case DataBuffer.TYPE_USHORT:
0221:                    if (sampleSize[0] != 16) {
0222:                        throw new RuntimeException(JaiI18N
0223:                                .getString("TIFFImageEncoder3"));
0224:                    }
0225:                    break;
0226:                case DataBuffer.TYPE_INT:
0227:                case DataBuffer.TYPE_FLOAT:
0228:                    if (sampleSize[0] != 32) {
0229:                        throw new RuntimeException(JaiI18N
0230:                                .getString("TIFFImageEncoder4"));
0231:                    }
0232:                    break;
0233:                default:
0234:                    throw new RuntimeException(JaiI18N
0235:                            .getString("TIFFImageEncoder5"));
0236:                }
0237:
0238:                boolean dataTypeIsShort = dataType == DataBuffer.TYPE_SHORT
0239:                        || dataType == DataBuffer.TYPE_USHORT;
0240:
0241:                ColorModel colorModel = im.getColorModel();
0242:                if (colorModel != null && colorModel instanceof  IndexColorModel
0243:                        && dataType != DataBuffer.TYPE_BYTE) {
0244:                    // Don't support (unsigned) short palette-color images.
0245:                    throw new RuntimeException(JaiI18N
0246:                            .getString("TIFFImageEncoder6"));
0247:                }
0248:                IndexColorModel icm = null;
0249:                int sizeOfColormap = 0;
0250:                int colormap[] = null;
0251:
0252:                // Set image type.
0253:                int imageType = TIFF_UNSUPPORTED;
0254:                int numExtraSamples = 0;
0255:                int extraSampleType = EXTRA_SAMPLE_UNSPECIFIED;
0256:                if (colorModel instanceof  IndexColorModel) { // Bilevel or palette
0257:                    icm = (IndexColorModel) colorModel;
0258:                    int mapSize = icm.getMapSize();
0259:
0260:                    if (sampleSize[0] == 1 && numBands == 1) { // Bilevel image
0261:
0262:                        if (mapSize != 2) {
0263:                            throw new IllegalArgumentException(JaiI18N
0264:                                    .getString("TIFFImageEncoder7"));
0265:                        }
0266:
0267:                        byte r[] = new byte[mapSize];
0268:                        icm.getReds(r);
0269:                        byte g[] = new byte[mapSize];
0270:                        icm.getGreens(g);
0271:                        byte b[] = new byte[mapSize];
0272:                        icm.getBlues(b);
0273:
0274:                        if ((r[0] & 0xff) == 0 && (r[1] & 0xff) == 255
0275:                                && (g[0] & 0xff) == 0 && (g[1] & 0xff) == 255
0276:                                && (b[0] & 0xff) == 0 && (b[1] & 0xff) == 255) {
0277:
0278:                            imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
0279:
0280:                        } else if ((r[0] & 0xff) == 255 && (r[1] & 0xff) == 0
0281:                                && (g[0] & 0xff) == 255 && (g[1] & 0xff) == 0
0282:                                && (b[0] & 0xff) == 255 && (b[1] & 0xff) == 0) {
0283:
0284:                            imageType = TIFF_BILEVEL_WHITE_IS_ZERO;
0285:
0286:                        } else {
0287:                            imageType = TIFF_PALETTE;
0288:                        }
0289:
0290:                    } else if (numBands == 1) { // Non-bilevel image.
0291:                        // Palette color image.
0292:                        imageType = TIFF_PALETTE;
0293:                    }
0294:                } else if (colorModel == null) {
0295:
0296:                    if (sampleSize[0] == 1 && numBands == 1) { // bilevel
0297:                        imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
0298:                    } else { // generic image
0299:                        imageType = TIFF_GENERIC;
0300:                        if (numBands > 1) {
0301:                            numExtraSamples = numBands - 1;
0302:                        }
0303:                    }
0304:
0305:                } else { // colorModel is non-null but not an IndexColorModel
0306:                    ColorSpace colorSpace = colorModel.getColorSpace();
0307:
0308:                    switch (colorSpace.getType()) {
0309:                    case ColorSpace.TYPE_CMYK:
0310:                        imageType = TIFF_CMYK;
0311:                        break;
0312:                    case ColorSpace.TYPE_GRAY:
0313:                        imageType = TIFF_GRAY;
0314:                        break;
0315:                    case ColorSpace.TYPE_Lab:
0316:                        imageType = TIFF_CIELAB;
0317:                        break;
0318:                    case ColorSpace.TYPE_RGB:
0319:                        if (compression == COMP_JPEG_TTN2
0320:                                && encodeParam.getJPEGCompressRGBToYCbCr()) {
0321:                            imageType = TIFF_YCBCR;
0322:                        } else {
0323:                            imageType = TIFF_RGB;
0324:                        }
0325:                        break;
0326:                    case ColorSpace.TYPE_YCbCr:
0327:                        imageType = TIFF_YCBCR;
0328:                        break;
0329:                    default:
0330:                        imageType = TIFF_GENERIC; // generic
0331:                        break;
0332:                    }
0333:
0334:                    if (imageType == TIFF_GENERIC) {
0335:                        numExtraSamples = numBands - 1;
0336:                    } else if (numBands > 1) {
0337:                        numExtraSamples = numBands
0338:                                - colorSpace.getNumComponents();
0339:                    }
0340:
0341:                    if (numExtraSamples == 1 && colorModel.hasAlpha()) {
0342:                        extraSampleType = colorModel.isAlphaPremultiplied() ? EXTRA_SAMPLE_ASSOCIATED_ALPHA
0343:                                : EXTRA_SAMPLE_UNASSOCIATED_ALPHA;
0344:                    }
0345:                }
0346:
0347:                if (imageType == TIFF_UNSUPPORTED) {
0348:                    throw new RuntimeException(JaiI18N
0349:                            .getString("TIFFImageEncoder8"));
0350:                }
0351:
0352:                // Check JPEG compatibility.
0353:                if (compression == COMP_JPEG_TTN2) {
0354:                    if (imageType == TIFF_PALETTE) {
0355:                        throw new RuntimeException(JaiI18N
0356:                                .getString("TIFFImageEncoder11"));
0357:                    } else if (!(sampleSize[0] == 8 && (imageType == TIFF_GRAY
0358:                            || imageType == TIFF_RGB || imageType == TIFF_YCBCR))) {
0359:                        throw new RuntimeException(JaiI18N
0360:                                .getString("TIFFImageEncoder9"));
0361:                    }
0362:                }
0363:
0364:                // Check bilevel encoding compatibility.
0365:                if ((imageType != TIFF_BILEVEL_WHITE_IS_ZERO && imageType != TIFF_BILEVEL_BLACK_IS_ZERO)
0366:                        && (compression == COMP_GROUP3_1D
0367:                                || compression == COMP_GROUP3_2D || compression == COMP_GROUP4)) {
0368:                    throw new RuntimeException(JaiI18N
0369:                            .getString("TIFFImageEncoder12"));
0370:                }
0371:
0372:                int photometricInterpretation = -1;
0373:                switch (imageType) {
0374:
0375:                case TIFF_BILEVEL_WHITE_IS_ZERO:
0376:                    photometricInterpretation = 0;
0377:                    break;
0378:
0379:                case TIFF_BILEVEL_BLACK_IS_ZERO:
0380:                    photometricInterpretation = 1;
0381:                    break;
0382:
0383:                case TIFF_GRAY:
0384:                case TIFF_GENERIC:
0385:                    // Since the CS_GRAY colorspace is always of type black_is_zero
0386:                    photometricInterpretation = 1;
0387:                    break;
0388:
0389:                case TIFF_PALETTE:
0390:                    photometricInterpretation = 3;
0391:
0392:                    icm = (IndexColorModel) colorModel;
0393:                    sizeOfColormap = icm.getMapSize();
0394:
0395:                    byte r[] = new byte[sizeOfColormap];
0396:                    icm.getReds(r);
0397:                    byte g[] = new byte[sizeOfColormap];
0398:                    icm.getGreens(g);
0399:                    byte b[] = new byte[sizeOfColormap];
0400:                    icm.getBlues(b);
0401:
0402:                    int redIndex = 0,
0403:                    greenIndex = sizeOfColormap;
0404:                    int blueIndex = 2 * sizeOfColormap;
0405:                    colormap = new int[sizeOfColormap * 3];
0406:                    for (int i = 0; i < sizeOfColormap; i++) {
0407:                        colormap[redIndex++] = (r[i] << 8) & 0xffff;
0408:                        colormap[greenIndex++] = (g[i] << 8) & 0xffff;
0409:                        colormap[blueIndex++] = (b[i] << 8) & 0xffff;
0410:                    }
0411:
0412:                    sizeOfColormap *= 3;
0413:
0414:                    break;
0415:
0416:                case TIFF_RGB:
0417:                    photometricInterpretation = 2;
0418:                    break;
0419:
0420:                case TIFF_CMYK:
0421:                    photometricInterpretation = 5;
0422:                    break;
0423:
0424:                case TIFF_YCBCR:
0425:                    photometricInterpretation = 6;
0426:                    break;
0427:
0428:                case TIFF_CIELAB:
0429:                    photometricInterpretation = 8;
0430:                    break;
0431:
0432:                default:
0433:                    throw new RuntimeException(JaiI18N
0434:                            .getString("TIFFImageEncoder8"));
0435:                }
0436:
0437:                // Initialize tile dimensions.
0438:                int tileWidth;
0439:                int tileHeight;
0440:                if (isTiled) {
0441:                    tileWidth = encodeParam.getTileWidth() > 0 ? encodeParam
0442:                            .getTileWidth() : im.getTileWidth();
0443:                    tileHeight = encodeParam.getTileHeight() > 0 ? encodeParam
0444:                            .getTileHeight() : im.getTileHeight();
0445:                } else {
0446:                    tileWidth = width;
0447:                    // XXX Set rows per strip based on memory value if not specified?
0448:                    tileHeight = encodeParam.getTileHeight() > 0 ? encodeParam
0449:                            .getTileHeight() : DEFAULT_ROWS_PER_STRIP;
0450:                }
0451:
0452:                // Re-tile for JPEG conformance if needed.
0453:                JPEGEncodeParam jep = null;
0454:                if (compression == COMP_JPEG_TTN2) {
0455:                    // Get JPEGEncodeParam from encodeParam.
0456:                    jep = encodeParam.getJPEGEncodeParam();
0457:
0458:                    // Determine maximum subsampling.
0459:                    int maxSubH = jep.getHorizontalSubsampling(0);
0460:                    int maxSubV = jep.getVerticalSubsampling(0);
0461:                    for (int i = 1; i < numBands; i++) {
0462:                        int subH = jep.getHorizontalSubsampling(i);
0463:                        if (subH > maxSubH) {
0464:                            maxSubH = subH;
0465:                        }
0466:                        int subV = jep.getVerticalSubsampling(i);
0467:                        if (subV > maxSubV) {
0468:                            maxSubV = subV;
0469:                        }
0470:                    }
0471:
0472:                    int factorV = 8 * maxSubV;
0473:                    tileHeight = (int) ((float) tileHeight / (float) factorV + 0.5F)
0474:                            * factorV;
0475:                    if (tileHeight < factorV) {
0476:                        tileHeight = factorV;
0477:                    }
0478:
0479:                    if (isTiled) {
0480:                        int factorH = 8 * maxSubH;
0481:                        tileWidth = (int) ((float) tileWidth / (float) factorH + 0.5F)
0482:                                * factorH;
0483:                        if (tileWidth < factorH) {
0484:                            tileWidth = factorH;
0485:                        }
0486:                    }
0487:                }
0488:
0489:                int numTiles;
0490:                if (isTiled) {
0491:                    // NB: Parentheses are used in this statement for correct rounding.
0492:                    numTiles = ((width + tileWidth - 1) / tileWidth)
0493:                            * ((height + tileHeight - 1) / tileHeight);
0494:                } else {
0495:                    numTiles = (int) Math.ceil((double) height
0496:                            / (double) tileHeight);
0497:                }
0498:
0499:                long tileByteCounts[] = new long[numTiles];
0500:
0501:                long bytesPerRow = (long) Math.ceil((sampleSize[0] / 8.0)
0502:                        * tileWidth * numBands);
0503:
0504:                long bytesPerTile = bytesPerRow * tileHeight;
0505:
0506:                for (int i = 0; i < numTiles; i++) {
0507:                    tileByteCounts[i] = bytesPerTile;
0508:                }
0509:
0510:                if (!isTiled) {
0511:                    // Last strip may have lesser rows
0512:                    long lastStripRows = height - (tileHeight * (numTiles - 1));
0513:                    tileByteCounts[numTiles - 1] = lastStripRows * bytesPerRow;
0514:                }
0515:
0516:                long totalBytesOfData = bytesPerTile * (numTiles - 1)
0517:                        + tileByteCounts[numTiles - 1];
0518:
0519:                // The data will be written after the IFD: create the array here
0520:                // but fill it in later.
0521:                long tileOffsets[] = new long[numTiles];
0522:
0523:                // Basic fields - have to be in increasing numerical order.
0524:                // ImageWidth                     256
0525:                // ImageLength                    257
0526:                // BitsPerSample                  258
0527:                // Compression                    259
0528:                // PhotoMetricInterpretation      262
0529:                // StripOffsets                   273
0530:                // RowsPerStrip                   278
0531:                // StripByteCounts                279
0532:                // XResolution                    282
0533:                // YResolution                    283
0534:                // ResolutionUnit                 296	
0535:
0536:                // Create Directory
0537:                SortedSet fields = new TreeSet();
0538:
0539:                // Image Width
0540:                fields.add(new TIFFField(TIFFImageDecoder.TIFF_IMAGE_WIDTH,
0541:                        TIFFField.TIFF_LONG, 1,
0542:                        (Object) (new long[] { (long) width })));
0543:
0544:                // Image Length
0545:                fields.add(new TIFFField(TIFFImageDecoder.TIFF_IMAGE_LENGTH,
0546:                        TIFFField.TIFF_LONG, 1, new long[] { (long) height }));
0547:
0548:                fields
0549:                        .add(new TIFFField(
0550:                                TIFFImageDecoder.TIFF_BITS_PER_SAMPLE,
0551:                                TIFFField.TIFF_SHORT, numBands,
0552:                                intsToChars(sampleSize)));
0553:
0554:                fields.add(new TIFFField(TIFFImageDecoder.TIFF_COMPRESSION,
0555:                        TIFFField.TIFF_SHORT, 1,
0556:                        new char[] { (char) compression }));
0557:
0558:                fields.add(new TIFFField(
0559:                        TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION,
0560:                        TIFFField.TIFF_SHORT, 1,
0561:                        new char[] { (char) photometricInterpretation }));
0562:
0563:                if (!isTiled) {
0564:                    fields
0565:                            .add(new TIFFField(
0566:                                    TIFFImageDecoder.TIFF_STRIP_OFFSETS,
0567:                                    TIFFField.TIFF_LONG, numTiles,
0568:                                    (long[]) tileOffsets));
0569:                }
0570:
0571:                fields
0572:                        .add(new TIFFField(
0573:                                TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL,
0574:                                TIFFField.TIFF_SHORT, 1,
0575:                                new char[] { (char) numBands }));
0576:
0577:                if (!isTiled) {
0578:                    fields.add(new TIFFField(
0579:                            TIFFImageDecoder.TIFF_ROWS_PER_STRIP,
0580:                            TIFFField.TIFF_LONG, 1,
0581:                            new long[] { (long) tileHeight }));
0582:
0583:                    fields.add(new TIFFField(
0584:                            TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS,
0585:                            TIFFField.TIFF_LONG, numTiles,
0586:                            (long[]) tileByteCounts));
0587:                }
0588:
0589:                if (colormap != null) {
0590:                    fields.add(new TIFFField(TIFFImageDecoder.TIFF_COLORMAP,
0591:                            TIFFField.TIFF_SHORT, sizeOfColormap,
0592:                            intsToChars(colormap)));
0593:                }
0594:
0595:                if (isTiled) {
0596:                    fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_WIDTH,
0597:                            TIFFField.TIFF_LONG, 1,
0598:                            new long[] { (long) tileWidth }));
0599:
0600:                    fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_LENGTH,
0601:                            TIFFField.TIFF_LONG, 1,
0602:                            new long[] { (long) tileHeight }));
0603:
0604:                    fields
0605:                            .add(new TIFFField(
0606:                                    TIFFImageDecoder.TIFF_TILE_OFFSETS,
0607:                                    TIFFField.TIFF_LONG, numTiles,
0608:                                    (long[]) tileOffsets));
0609:
0610:                    fields.add(new TIFFField(
0611:                            TIFFImageDecoder.TIFF_TILE_BYTE_COUNTS,
0612:                            TIFFField.TIFF_LONG, numTiles,
0613:                            (long[]) tileByteCounts));
0614:                }
0615:
0616:                if (numExtraSamples > 0) {
0617:                    int[] extraSamples = new int[numExtraSamples];
0618:                    for (int i = 0; i < numExtraSamples; i++) {
0619:                        extraSamples[i] = extraSampleType;
0620:                    }
0621:                    fields.add(new TIFFField(
0622:                            TIFFImageDecoder.TIFF_EXTRA_SAMPLES,
0623:                            TIFFField.TIFF_SHORT, numExtraSamples,
0624:                            intsToChars(extraSamples)));
0625:                }
0626:
0627:                // Data Sample Format Extension fields.
0628:                if (dataType != DataBuffer.TYPE_BYTE) {
0629:                    // SampleFormat
0630:                    int[] sampleFormat = new int[numBands];
0631:                    if (dataType == DataBuffer.TYPE_FLOAT) {
0632:                        sampleFormat[0] = 3;
0633:                    } else if (dataType == DataBuffer.TYPE_USHORT) {
0634:                        sampleFormat[0] = 1;
0635:                    } else {
0636:                        sampleFormat[0] = 2;
0637:                    }
0638:                    for (int b = 1; b < numBands; b++) {
0639:                        sampleFormat[b] = sampleFormat[0];
0640:                    }
0641:                    fields.add(new TIFFField(
0642:                            TIFFImageDecoder.TIFF_SAMPLE_FORMAT,
0643:                            TIFFField.TIFF_SHORT, numBands,
0644:                            intsToChars(sampleFormat)));
0645:
0646:                    // NOTE: We don't bother setting the SMinSampleValue and
0647:                    // SMaxSampleValue fields as these both default to the
0648:                    // extrema of the respective data types.  Probably we should
0649:                    // check for the presence of the "extrema" property and
0650:                    // use it if available.
0651:                }
0652:
0653:                // Bilevel compression variables.
0654:                boolean inverseFill = encodeParam.getReverseFillOrder();
0655:                boolean T4encode2D = encodeParam.getT4Encode2D();
0656:                boolean T4PadEOLs = encodeParam.getT4PadEOLs();
0657:                TIFFFaxEncoder faxEncoder = null;
0658:
0659:                // Add bilevel compression fields.
0660:                if ((imageType == TIFF_BILEVEL_BLACK_IS_ZERO || imageType == TIFF_BILEVEL_WHITE_IS_ZERO)
0661:                        && (compression == COMP_GROUP3_1D
0662:                                || compression == COMP_GROUP3_2D || compression == COMP_GROUP4)) {
0663:
0664:                    // Create the encoder.
0665:                    faxEncoder = new TIFFFaxEncoder(inverseFill);
0666:
0667:                    // FillOrder field.
0668:                    fields.add(new TIFFField(TIFFImageDecoder.TIFF_FILL_ORDER,
0669:                            TIFFField.TIFF_SHORT, 1,
0670:                            new char[] { inverseFill ? (char) 2 : (char) 1 }));
0671:
0672:                    if (compression == COMP_GROUP3_2D) {
0673:                        // T4Options field.
0674:                        long T4Options = 0x00000000;
0675:                        if (T4encode2D) {
0676:                            T4Options |= 0x00000001;
0677:                        }
0678:                        if (T4PadEOLs) {
0679:                            T4Options |= 0x00000004;
0680:                        }
0681:                        fields.add(new TIFFField(
0682:                                TIFFImageDecoder.TIFF_T4_OPTIONS,
0683:                                TIFFField.TIFF_LONG, 1,
0684:                                new long[] { T4Options }));
0685:                    } else if (compression == COMP_GROUP4) {
0686:                        // T6Options field.
0687:                        fields.add(new TIFFField(
0688:                                TIFFImageDecoder.TIFF_T6_OPTIONS,
0689:                                TIFFField.TIFF_LONG, 1,
0690:                                new long[] { (long) 0x00000000 }));
0691:                    }
0692:                }
0693:
0694:                // Initialize some JPEG variables.
0695:                com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = null;
0696:                com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = null;
0697:                int jpegColorID = 0;
0698:
0699:                if (compression == COMP_JPEG_TTN2) {
0700:
0701:                    // Initialize JPEG color ID.
0702:                    jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_UNKNOWN;
0703:                    switch (imageType) {
0704:                    case TIFF_GRAY:
0705:                    case TIFF_PALETTE:
0706:                        jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_GRAY;
0707:                        break;
0708:                    case TIFF_RGB:
0709:                        jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_RGB;
0710:                        break;
0711:                    case TIFF_YCBCR:
0712:                        jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_YCbCr;
0713:                        break;
0714:                    }
0715:
0716:                    // Get the JDK encoding parameters.
0717:                    Raster tile00 = im.getTile(im.getMinTileX(), im
0718:                            .getMinTileY());
0719:                    jpegEncodeParam = com.sun.image.codec.jpeg.JPEGCodec
0720:                            .getDefaultJPEGEncodeParam(tile00, jpegColorID);
0721:
0722:                    // Modify per values passed in.
0723:                    JPEGImageEncoder.modifyEncodeParam(jep, jpegEncodeParam,
0724:                            numBands);
0725:
0726:                    // JPEGTables field.
0727:                    if (jep.getWriteImageOnly()) {
0728:                        // Write an abbreviated tables-only stream to JPEGTables field.
0729:                        jpegEncodeParam.setImageInfoValid(false);
0730:                        jpegEncodeParam.setTableInfoValid(true);
0731:                        ByteArrayOutputStream tableStream = new ByteArrayOutputStream();
0732:                        jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec
0733:                                .createJPEGEncoder(tableStream, jpegEncodeParam);
0734:                        jpegEncoder.encode(tile00);
0735:                        byte[] tableData = tableStream.toByteArray();
0736:                        fields.add(new TIFFField(TIFF_JPEG_TABLES,
0737:                                TIFFField.TIFF_UNDEFINED, tableData.length,
0738:                                tableData));
0739:
0740:                        // Reset encoder so it's recreated below.
0741:                        jpegEncoder = null;
0742:                    }
0743:                }
0744:
0745:                if (imageType == TIFF_YCBCR) {
0746:                    // YCbCrSubSampling: 2 is the default so we must write 1 as
0747:                    // we do not (yet) do any subsampling.
0748:                    int subsampleH = 1;
0749:                    int subsampleV = 1;
0750:
0751:                    // If JPEG, update values.
0752:                    if (compression == COMP_JPEG_TTN2) {
0753:                        // Determine maximum subsampling.
0754:                        subsampleH = jep.getHorizontalSubsampling(0);
0755:                        subsampleV = jep.getVerticalSubsampling(0);
0756:                        for (int i = 1; i < numBands; i++) {
0757:                            int subH = jep.getHorizontalSubsampling(i);
0758:                            if (subH > subsampleH) {
0759:                                subsampleH = subH;
0760:                            }
0761:                            int subV = jep.getVerticalSubsampling(i);
0762:                            if (subV > subsampleV) {
0763:                                subsampleV = subV;
0764:                            }
0765:                        }
0766:                    }
0767:
0768:                    fields.add(new TIFFField(TIFF_YCBCR_SUBSAMPLING,
0769:                            TIFFField.TIFF_SHORT, 2, new char[] {
0770:                                    (char) subsampleH, (char) subsampleV }));
0771:
0772:                    // YCbCr positioning.
0773:                    fields
0774:                            .add(new TIFFField(
0775:                                    TIFF_YCBCR_POSITIONING,
0776:                                    TIFFField.TIFF_SHORT,
0777:                                    1,
0778:                                    new char[] { compression == COMP_JPEG_TTN2 ? (char) 1
0779:                                            : (char) 2 }));
0780:
0781:                    // Reference black/white.
0782:                    long[][] refbw;
0783:                    if (compression == COMP_JPEG_TTN2) {
0784:                        refbw = new long[][] { // no headroon/footroom
0785:                        { 0, 1 }, { 255, 1 }, { 128, 1 }, { 255, 1 },
0786:                                { 128, 1 }, { 255, 1 } };
0787:                    } else {
0788:                        refbw = new long[][] { // CCIR 601.1 headroom/footroom (presumptive)
0789:                        { 15, 1 }, { 235, 1 }, { 128, 1 }, { 240, 1 },
0790:                                { 128, 1 }, { 240, 1 } };
0791:                    }
0792:                    fields.add(new TIFFField(TIFF_REF_BLACK_WHITE,
0793:                            TIFFField.TIFF_RATIONAL, 6, refbw));
0794:                }
0795:
0796:                // ---- No more automatically generated fields should be added
0797:                //      after this point. ----
0798:
0799:                // Add extra fields specified via the encoding parameters.
0800:                TIFFField[] extraFields = encodeParam.getExtraFields();
0801:                if (extraFields != null) {
0802:                    ArrayList extantTags = new ArrayList(fields.size());
0803:                    Iterator fieldIter = fields.iterator();
0804:                    while (fieldIter.hasNext()) {
0805:                        TIFFField fld = (TIFFField) fieldIter.next();
0806:                        extantTags.add(new Integer(fld.getTag()));
0807:                    }
0808:
0809:                    int numExtraFields = extraFields.length;
0810:                    for (int i = 0; i < numExtraFields; i++) {
0811:                        TIFFField fld = extraFields[i];
0812:                        Integer tagValue = new Integer(fld.getTag());
0813:                        if (!extantTags.contains(tagValue)) {
0814:                            fields.add(fld);
0815:                            extantTags.add(tagValue);
0816:                        }
0817:                    }
0818:                }
0819:
0820:                // ---- No more fields of any type should be added after this. ----
0821:
0822:                // Determine the size of the IFD which is written after the header
0823:                // of the stream or after the data of the previous image in a
0824:                // multi-page stream.
0825:                int dirSize = getDirectorySize(fields);
0826:
0827:                // The first data segment is written after the field overflow
0828:                // following the IFD so initialize the first offset accordingly.
0829:                tileOffsets[0] = ifdOffset + dirSize;
0830:
0831:                // Branch here depending on whether data are being comrpressed.
0832:                // If not, then the IFD is written immediately.
0833:                // If so then there are three possibilities:
0834:                // A) the OutputStream is a SeekableOutputStream (outCache null);
0835:                // B) the OutputStream is not a SeekableOutputStream and a file cache
0836:                //    is used (outCache non-null, tempFile non-null);
0837:                // C) the OutputStream is not a SeekableOutputStream and a memory cache
0838:                //    is used (outCache non-null, tempFile null).
0839:
0840:                OutputStream outCache = null;
0841:                byte[] compressBuf = null;
0842:                File tempFile = null;
0843:
0844:                int nextIFDOffset = 0;
0845:                boolean skipByte = false;
0846:
0847:                Deflater deflater = null;
0848:                int deflateLevel = Deflater.DEFAULT_COMPRESSION;
0849:
0850:                boolean jpegRGBToYCbCr = false;
0851:
0852:                if (compression == COMP_NONE) {
0853:                    // Determine the number of bytes of padding necessary between
0854:                    // the end of the IFD and the first data segment such that the
0855:                    // alignment of the data conforms to the specification (required
0856:                    // for uncompressed data only).
0857:                    int numBytesPadding = 0;
0858:                    if (sampleSize[0] == 16 && tileOffsets[0] % 2 != 0) {
0859:                        numBytesPadding = 1;
0860:                        tileOffsets[0]++;
0861:                    } else if (sampleSize[0] == 32 && tileOffsets[0] % 4 != 0) {
0862:                        numBytesPadding = (int) (4 - tileOffsets[0] % 4);
0863:                        tileOffsets[0] += numBytesPadding;
0864:                    }
0865:
0866:                    // Update the data offsets (which TIFFField stores by reference).
0867:                    for (int i = 1; i < numTiles; i++) {
0868:                        tileOffsets[i] = tileOffsets[i - 1]
0869:                                + tileByteCounts[i - 1];
0870:                    }
0871:
0872:                    if (!isLast) {
0873:                        // Determine the offset of the next IFD.
0874:                        nextIFDOffset = (int) (tileOffsets[0] + totalBytesOfData);
0875:
0876:                        // IFD offsets must be on a word boundary.
0877:                        if (nextIFDOffset % 2 != 0) {
0878:                            nextIFDOffset++;
0879:                            skipByte = true;
0880:                        }
0881:                    }
0882:
0883:                    // Write the IFD and field overflow before the image data.
0884:                    writeDirectory(ifdOffset, fields, nextIFDOffset);
0885:
0886:                    // Write any padding bytes needed between the end of the IFD
0887:                    // and the start of the actual image data.
0888:                    if (numBytesPadding != 0) {
0889:                        for (int padding = 0; padding < numBytesPadding; padding++) {
0890:                            output.write((byte) 0);
0891:                        }
0892:                    }
0893:                } else {
0894:                    // If compressing, the cannot be written yet as the size of the
0895:                    // data segments is unknown.
0896:
0897:                    if ((output instanceof  SeekableOutputStream)) {
0898:                        // Simply seek to the first data segment position.
0899:                        ((SeekableOutputStream) output).seek(tileOffsets[0]);
0900:                    } else {
0901:                        // Cache the original OutputStream.
0902:                        outCache = output;
0903:
0904:                        try {
0905:                            // Attempt to create a temporary file.
0906:                            tempFile = File.createTempFile("jai-SOS-", ".tmp");
0907:                            tempFile.deleteOnExit();
0908:                            RandomAccessFile raFile = new RandomAccessFile(
0909:                                    tempFile, "rw");
0910:                            output = new SeekableOutputStream(raFile);
0911:                            // XXX Be sure that this file is deleted no matter how
0912:                            // this method is exited!
0913:                        } catch (Exception e) {
0914:                            tempFile = null;
0915:                            // Allocate memory for the entire image data (!).
0916:                            output = new ByteArrayOutputStream(
0917:                                    (int) totalBytesOfData);
0918:                        }
0919:                    }
0920:
0921:                    int bufSize = 0;
0922:                    switch (compression) {
0923:                    case COMP_GROUP3_1D:
0924:                        // This initial buffer size is based on an alternating 1-0
0925:                        // pattern generating the most bits when converted to code
0926:                        // words: 9 bits out for each pair of bits in. So the number
0927:                        // of bit pairs is determined, multiplied by 9, converted to
0928:                        // bytes, and a ceil() is taken to account for fill bits at the
0929:                        // end of each line.  The "2" addend accounts for the case
0930:                        // of the pattern beginning with black.  The buffer is intended
0931:                        // to hold only a single row.
0932:                        bufSize = (int) Math
0933:                                .ceil((((tileWidth + 1) / 2) * 9 + 2) / 8.0);
0934:                        break;
0935:                    case COMP_GROUP3_2D:
0936:                    case COMP_GROUP4:
0937:                        // Calculate the maximum row as the G3-1D size plus the EOL,
0938:                        // multiply this by the number of rows in the tile, and add
0939:                        // 6 EOLs for the RTC (return to control).
0940:                        bufSize = (int) Math
0941:                                .ceil((((tileWidth + 1) / 2) * 9 + 2) / 8.0);
0942:                        bufSize = tileHeight * (bufSize + 2) + 12;
0943:                        break;
0944:                    case COMP_PACKBITS:
0945:                        bufSize = (int) (bytesPerTile + ((bytesPerRow + 127) / 128)
0946:                                * tileHeight);
0947:                        break;
0948:                    case COMP_JPEG_TTN2:
0949:                        bufSize = 0;
0950:
0951:                        // Set color conversion flag.
0952:                        if (imageType == TIFF_YCBCR
0953:                                && colorModel != null
0954:                                && colorModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
0955:                            jpegRGBToYCbCr = true;
0956:                        }
0957:                        break;
0958:                    case COMP_DEFLATE:
0959:                        bufSize = (int) bytesPerTile;
0960:                        deflater = new Deflater(encodeParam.getDeflateLevel());
0961:                        break;
0962:                    default:
0963:                        bufSize = 0;
0964:                    }
0965:                    if (bufSize != 0) {
0966:                        compressBuf = new byte[bufSize];
0967:                    }
0968:                }
0969:
0970:                // ---- Writing of actual image data ----
0971:
0972:                // Buffer for up to tileHeight rows of pixels
0973:                int[] pixels = null;
0974:                float[] fpixels = null;
0975:
0976:                // Whether to test for contiguous data.
0977:                boolean checkContiguous = ((sampleSize[0] == 1
0978:                        && sampleModel instanceof  MultiPixelPackedSampleModel && dataType == DataBuffer.TYPE_BYTE) || (sampleSize[0] == 8 && sampleModel instanceof  ComponentSampleModel));
0979:
0980:                // Also create a buffer to hold tileHeight lines of the
0981:                // data to be written to the file, so we can use array writes.
0982:                byte[] bpixels = null;
0983:                if (compression != COMP_JPEG_TTN2) {
0984:                    if (dataType == DataBuffer.TYPE_BYTE) {
0985:                        bpixels = new byte[tileHeight * tileWidth * numBands];
0986:                    } else if (dataTypeIsShort) {
0987:                        bpixels = new byte[2 * tileHeight * tileWidth
0988:                                * numBands];
0989:                    } else if (dataType == DataBuffer.TYPE_INT
0990:                            || dataType == DataBuffer.TYPE_FLOAT) {
0991:                        bpixels = new byte[4 * tileHeight * tileWidth
0992:                                * numBands];
0993:                    }
0994:                }
0995:
0996:                // Process tileHeight rows at a time
0997:                int lastRow = minY + height;
0998:                int lastCol = minX + width;
0999:                int tileNum = 0;
1000:                for (int row = minY; row < lastRow; row += tileHeight) {
1001:                    int rows = isTiled ? tileHeight : Math.min(tileHeight,
1002:                            lastRow - row);
1003:                    int size = rows * tileWidth * numBands;
1004:
1005:                    for (int col = minX; col < lastCol; col += tileWidth) {
1006:                        // Grab the pixels
1007:                        Raster src = im.getData(new Rectangle(col, row,
1008:                                tileWidth, rows));
1009:
1010:                        boolean useDataBuffer = false;
1011:                        if (compression != COMP_JPEG_TTN2) { // JPEG access Raster
1012:                            if (checkContiguous) {
1013:                                if (sampleSize[0] == 8) { // 8-bit
1014:                                    ComponentSampleModel csm = (ComponentSampleModel) src
1015:                                            .getSampleModel();
1016:                                    int[] bankIndices = csm.getBankIndices();
1017:                                    int[] bandOffsets = csm.getBandOffsets();
1018:                                    int pixelStride = csm.getPixelStride();
1019:                                    int lineStride = csm.getScanlineStride();
1020:
1021:                                    if (pixelStride != numBands
1022:                                            || lineStride != bytesPerRow) {
1023:                                        useDataBuffer = false;
1024:                                    } else {
1025:                                        useDataBuffer = true;
1026:                                        for (int i = 0; useDataBuffer
1027:                                                && i < numBands; i++) {
1028:                                            if (bankIndices[i] != 0
1029:                                                    || bandOffsets[i] != i) {
1030:                                                useDataBuffer = false;
1031:                                            }
1032:                                        }
1033:                                    }
1034:                                } else { // 1-bit
1035:                                    MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) src
1036:                                            .getSampleModel();
1037:                                    if (mpp.getNumBands() == 1
1038:                                            && mpp.getDataBitOffset() == 0
1039:                                            && mpp.getPixelBitStride() == 1) {
1040:                                        useDataBuffer = true;
1041:                                    }
1042:                                }
1043:                            }
1044:
1045:                            if (!useDataBuffer) {
1046:                                if (dataType == DataBuffer.TYPE_FLOAT) {
1047:                                    fpixels = src.getPixels(col, row,
1048:                                            tileWidth, rows, fpixels);
1049:                                } else {
1050:                                    pixels = src.getPixels(col, row, tileWidth,
1051:                                            rows, pixels);
1052:                                }
1053:                            }
1054:                        }
1055:
1056:                        int index;
1057:
1058:                        int pixel = 0;
1059:                        ;
1060:                        int k = 0;
1061:                        switch (sampleSize[0]) {
1062:
1063:                        case 1:
1064:
1065:                            if (useDataBuffer) {
1066:                                byte[] btmp = ((DataBufferByte) src
1067:                                        .getDataBuffer()).getData();
1068:                                MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel) src
1069:                                        .getSampleModel();
1070:                                int lineStride = mpp.getScanlineStride();
1071:                                int inOffset = mpp.getOffset(col
1072:                                        - src.getSampleModelTranslateX(), row
1073:                                        - src.getSampleModelTranslateY());
1074:                                if (lineStride == (int) bytesPerRow) {
1075:                                    System.arraycopy(btmp, inOffset, bpixels,
1076:                                            0, (int) bytesPerRow * rows);
1077:                                } else {
1078:                                    int outOffset = 0;
1079:                                    for (int j = 0; j < rows; j++) {
1080:                                        System.arraycopy(btmp, inOffset,
1081:                                                bpixels, outOffset,
1082:                                                (int) bytesPerRow);
1083:                                        inOffset += lineStride;
1084:                                        outOffset += (int) bytesPerRow;
1085:                                    }
1086:                                }
1087:                            } else {
1088:                                index = 0;
1089:
1090:                                // For each of the rows in a strip
1091:                                for (int i = 0; i < rows; i++) {
1092:
1093:                                    // Write number of pixels exactly divisible by 8
1094:                                    for (int j = 0; j < tileWidth / 8; j++) {
1095:
1096:                                        pixel = (pixels[index++] << 7)
1097:                                                | (pixels[index++] << 6)
1098:                                                | (pixels[index++] << 5)
1099:                                                | (pixels[index++] << 4)
1100:                                                | (pixels[index++] << 3)
1101:                                                | (pixels[index++] << 2)
1102:                                                | (pixels[index++] << 1)
1103:                                                | pixels[index++];
1104:                                        bpixels[k++] = (byte) pixel;
1105:                                    }
1106:
1107:                                    // Write the pixels remaining after division by 8
1108:                                    if (tileWidth % 8 > 0) {
1109:                                        pixel = 0;
1110:                                        for (int j = 0; j < tileWidth % 8; j++) {
1111:                                            pixel |= (pixels[index++] << (7 - j));
1112:                                        }
1113:                                        bpixels[k++] = (byte) pixel;
1114:                                    }
1115:                                }
1116:                            }
1117:
1118:                            if (compression == COMP_NONE) {
1119:                                output.write(bpixels, 0, rows
1120:                                        * ((tileWidth + 7) / 8));
1121:                            } else if (compression == COMP_GROUP3_1D) {
1122:                                int rowStride = (tileWidth + 7) / 8;
1123:                                int rowOffset = 0;
1124:                                int numCompressedBytes = 0;
1125:                                for (int tileRow = 0; tileRow < rows; tileRow++) {
1126:                                    int numCompressedBytesInRow = faxEncoder
1127:                                            .encodeRLE(bpixels, rowOffset, 0,
1128:                                                    tileWidth, compressBuf);
1129:                                    output.write(compressBuf, 0,
1130:                                            numCompressedBytesInRow);
1131:                                    rowOffset += rowStride;
1132:                                    numCompressedBytes += numCompressedBytesInRow;
1133:                                }
1134:                                tileByteCounts[tileNum++] = numCompressedBytes;
1135:                            } else if (compression == COMP_GROUP3_2D) {
1136:                                int numCompressedBytes = faxEncoder.encodeT4(
1137:                                        !T4encode2D,// 1D == !2D
1138:                                        T4PadEOLs, bpixels,
1139:                                        (tileWidth + 7) / 8, 0, tileWidth,
1140:                                        rows, compressBuf);
1141:                                tileByteCounts[tileNum++] = numCompressedBytes;
1142:                                output
1143:                                        .write(compressBuf, 0,
1144:                                                numCompressedBytes);
1145:                            } else if (compression == COMP_GROUP4) {
1146:                                int numCompressedBytes = faxEncoder.encodeT6(
1147:                                        bpixels, (tileWidth + 7) / 8, 0,
1148:                                        tileWidth, rows, compressBuf);
1149:                                tileByteCounts[tileNum++] = numCompressedBytes;
1150:                                output
1151:                                        .write(compressBuf, 0,
1152:                                                numCompressedBytes);
1153:                            } else if (compression == COMP_PACKBITS) {
1154:                                int numCompressedBytes = compressPackBits(
1155:                                        bpixels, rows, (int) bytesPerRow,
1156:                                        compressBuf);
1157:                                tileByteCounts[tileNum++] = numCompressedBytes;
1158:                                output
1159:                                        .write(compressBuf, 0,
1160:                                                numCompressedBytes);
1161:                            } else if (compression == COMP_DEFLATE) {
1162:                                int numCompressedBytes = deflate(deflater,
1163:                                        bpixels, compressBuf);
1164:                                tileByteCounts[tileNum++] = numCompressedBytes;
1165:                                output
1166:                                        .write(compressBuf, 0,
1167:                                                numCompressedBytes);
1168:                            }
1169:
1170:                            break;
1171:
1172:                        case 4:
1173:
1174:                            index = 0;
1175:
1176:                            // For each of the rows in a strip
1177:                            for (int i = 0; i < rows; i++) {
1178:
1179:                                // Write  the number of pixels that will fit into an 
1180:                                // even number of nibbles.
1181:                                for (int j = 0; j < tileWidth / 2; j++) {
1182:                                    pixel = (pixels[index++] << 4)
1183:                                            | pixels[index++];
1184:                                    bpixels[k++] = (byte) pixel;
1185:                                }
1186:
1187:                                // Last pixel for odd-length lines
1188:                                if ((tileWidth % 2) == 1) {
1189:                                    pixel = pixels[index++] << 4;
1190:                                    bpixels[k++] = (byte) pixel;
1191:                                }
1192:                            }
1193:
1194:                            if (compression == COMP_NONE) {
1195:                                output.write(bpixels, 0, rows
1196:                                        * ((tileWidth + 1) / 2));
1197:                            } else if (compression == COMP_PACKBITS) {
1198:                                int numCompressedBytes = compressPackBits(
1199:                                        bpixels, rows, (int) bytesPerRow,
1200:                                        compressBuf);
1201:                                tileByteCounts[tileNum++] = numCompressedBytes;
1202:                                output
1203:                                        .write(compressBuf, 0,
1204:                                                numCompressedBytes);
1205:                            } else if (compression == COMP_DEFLATE) {
1206:                                int numCompressedBytes = deflate(deflater,
1207:                                        bpixels, compressBuf);
1208:                                tileByteCounts[tileNum++] = numCompressedBytes;
1209:                                output
1210:                                        .write(compressBuf, 0,
1211:                                                numCompressedBytes);
1212:                            }
1213:                            break;
1214:
1215:                        case 8:
1216:
1217:                            if (compression != COMP_JPEG_TTN2) {
1218:                                if (useDataBuffer) {
1219:                                    byte[] btmp = ((DataBufferByte) src
1220:                                            .getDataBuffer()).getData();
1221:                                    ComponentSampleModel csm = (ComponentSampleModel) src
1222:                                            .getSampleModel();
1223:                                    int inOffset = csm
1224:                                            .getOffset(
1225:                                                    col
1226:                                                            - src
1227:                                                                    .getSampleModelTranslateX(),
1228:                                                    row
1229:                                                            - src
1230:                                                                    .getSampleModelTranslateY());
1231:                                    int lineStride = csm.getScanlineStride();
1232:                                    if (lineStride == (int) bytesPerRow) {
1233:                                        System.arraycopy(btmp, inOffset,
1234:                                                bpixels, 0, (int) bytesPerRow
1235:                                                        * rows);
1236:                                    } else {
1237:                                        int outOffset = 0;
1238:                                        for (int j = 0; j < rows; j++) {
1239:                                            System.arraycopy(btmp, inOffset,
1240:                                                    bpixels, outOffset,
1241:                                                    (int) bytesPerRow);
1242:                                            inOffset += lineStride;
1243:                                            outOffset += (int) bytesPerRow;
1244:                                        }
1245:                                    }
1246:                                } else {
1247:                                    for (int i = 0; i < size; i++) {
1248:                                        bpixels[i] = (byte) pixels[i];
1249:                                    }
1250:                                }
1251:                            }
1252:
1253:                            if (compression == COMP_NONE) {
1254:                                output.write(bpixels, 0, size);
1255:                            } else if (compression == COMP_PACKBITS) {
1256:                                int numCompressedBytes = compressPackBits(
1257:                                        bpixels, rows, (int) bytesPerRow,
1258:                                        compressBuf);
1259:                                tileByteCounts[tileNum++] = numCompressedBytes;
1260:                                output
1261:                                        .write(compressBuf, 0,
1262:                                                numCompressedBytes);
1263:                            } else if (compression == COMP_JPEG_TTN2) {
1264:                                long startPos = getOffset(output);
1265:
1266:                                // Recreate encoder and parameters if the encoder
1267:                                // is null (first data segment) or if its size
1268:                                // doesn't match the current data segment.
1269:                                if (jpegEncoder == null
1270:                                        || jpegEncodeParam.getWidth() != src
1271:                                                .getWidth()
1272:                                        || jpegEncodeParam.getHeight() != src
1273:                                                .getHeight()) {
1274:
1275:                                    jpegEncodeParam = com.sun.image.codec.jpeg.JPEGCodec
1276:                                            .getDefaultJPEGEncodeParam(src,
1277:                                                    jpegColorID);
1278:
1279:                                    JPEGImageEncoder.modifyEncodeParam(jep,
1280:                                            jpegEncodeParam, numBands);
1281:
1282:                                    jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec
1283:                                            .createJPEGEncoder(output,
1284:                                                    jpegEncodeParam);
1285:                                }
1286:
1287:                                if (jpegRGBToYCbCr) {
1288:                                    WritableRaster wRas = null;
1289:                                    if (src instanceof  WritableRaster) {
1290:                                        wRas = (WritableRaster) src;
1291:                                    } else {
1292:                                        wRas = src
1293:                                                .createCompatibleWritableRaster();
1294:                                        wRas.setRect(src);
1295:                                    }
1296:
1297:                                    if (wRas.getMinX() != 0
1298:                                            || wRas.getMinY() != 0) {
1299:                                        wRas = wRas
1300:                                                .createWritableTranslatedChild(
1301:                                                        0, 0);
1302:                                    }
1303:                                    BufferedImage bi = new BufferedImage(
1304:                                            colorModel, wRas, false, null);
1305:                                    jpegEncoder.encode(bi);
1306:                                } else {
1307:                                    jpegEncoder.encode(src
1308:                                            .createTranslatedChild(0, 0));
1309:                                }
1310:
1311:                                long endPos = getOffset(output);
1312:                                tileByteCounts[tileNum++] = (int) (endPos - startPos);
1313:                            } else if (compression == COMP_DEFLATE) {
1314:                                int numCompressedBytes = deflate(deflater,
1315:                                        bpixels, compressBuf);
1316:                                tileByteCounts[tileNum++] = numCompressedBytes;
1317:                                output
1318:                                        .write(compressBuf, 0,
1319:                                                numCompressedBytes);
1320:                            }
1321:                            break;
1322:
1323:                        case 16:
1324:
1325:                            int ls = 0;
1326:                            for (int i = 0; i < size; i++) {
1327:                                short value = (short) pixels[i];
1328:                                bpixels[ls++] = (byte) ((value & 0xff00) >> 8);
1329:                                bpixels[ls++] = (byte) (value & 0x00ff);
1330:                            }
1331:
1332:                            if (compression == COMP_NONE) {
1333:                                output.write(bpixels, 0, size * 2);
1334:                            } else if (compression == COMP_PACKBITS) {
1335:                                int numCompressedBytes = compressPackBits(
1336:                                        bpixels, rows, (int) bytesPerRow,
1337:                                        compressBuf);
1338:                                tileByteCounts[tileNum++] = numCompressedBytes;
1339:                                output
1340:                                        .write(compressBuf, 0,
1341:                                                numCompressedBytes);
1342:                            } else if (compression == COMP_DEFLATE) {
1343:                                int numCompressedBytes = deflate(deflater,
1344:                                        bpixels, compressBuf);
1345:                                tileByteCounts[tileNum++] = numCompressedBytes;
1346:                                output
1347:                                        .write(compressBuf, 0,
1348:                                                numCompressedBytes);
1349:                            }
1350:                            break;
1351:
1352:                        case 32:
1353:                            if (dataType == DataBuffer.TYPE_INT) {
1354:                                int li = 0;
1355:                                for (int i = 0; i < size; i++) {
1356:                                    int value = pixels[i];
1357:                                    bpixels[li++] = (byte) ((value & 0xff000000) >> 24);
1358:                                    bpixels[li++] = (byte) ((value & 0x00ff0000) >> 16);
1359:                                    bpixels[li++] = (byte) ((value & 0x0000ff00) >> 8);
1360:                                    bpixels[li++] = (byte) (value & 0x000000ff);
1361:                                }
1362:                            } else { // DataBuffer.TYPE_FLOAT
1363:                                int lf = 0;
1364:                                for (int i = 0; i < size; i++) {
1365:                                    int value = Float
1366:                                            .floatToIntBits(fpixels[i]);
1367:                                    bpixels[lf++] = (byte) ((value & 0xff000000) >> 24);
1368:                                    bpixels[lf++] = (byte) ((value & 0x00ff0000) >> 16);
1369:                                    bpixels[lf++] = (byte) ((value & 0x0000ff00) >> 8);
1370:                                    bpixels[lf++] = (byte) (value & 0x000000ff);
1371:                                }
1372:                            }
1373:                            if (compression == COMP_NONE) {
1374:                                output.write(bpixels, 0, size * 4);
1375:                            } else if (compression == COMP_PACKBITS) {
1376:                                int numCompressedBytes = compressPackBits(
1377:                                        bpixels, rows, (int) bytesPerRow,
1378:                                        compressBuf);
1379:                                tileByteCounts[tileNum++] = numCompressedBytes;
1380:                                output
1381:                                        .write(compressBuf, 0,
1382:                                                numCompressedBytes);
1383:                            } else if (compression == COMP_DEFLATE) {
1384:                                int numCompressedBytes = deflate(deflater,
1385:                                        bpixels, compressBuf);
1386:                                tileByteCounts[tileNum++] = numCompressedBytes;
1387:                                output
1388:                                        .write(compressBuf, 0,
1389:                                                numCompressedBytes);
1390:                            }
1391:                            break;
1392:
1393:                        }
1394:                    }
1395:                }
1396:
1397:                if (compression == COMP_NONE) {
1398:                    // Write an extra byte for IFD word alignment if needed.
1399:                    if (skipByte) {
1400:                        output.write((byte) 0);
1401:                    }
1402:                } else {
1403:                    // Recompute tile offsets from the size of the compressed tiles.
1404:                    int totalBytes = 0;
1405:                    for (int i = 1; i < numTiles; i++) {
1406:                        int numBytes = (int) tileByteCounts[i - 1];
1407:                        totalBytes += numBytes;
1408:                        tileOffsets[i] = tileOffsets[i - 1] + numBytes;
1409:                    }
1410:                    totalBytes += (int) tileByteCounts[numTiles - 1];
1411:
1412:                    nextIFDOffset = isLast ? 0 : ifdOffset + dirSize
1413:                            + totalBytes;
1414:
1415:                    // IFD offsets must be on a word boundary.
1416:                    if (nextIFDOffset % 2 != 0) {
1417:                        nextIFDOffset++;
1418:                        skipByte = true;
1419:                    }
1420:
1421:                    if (outCache == null) {
1422:                        // Original OutputStream must be a SeekableOutputStream.
1423:
1424:                        // Write an extra byte for IFD word alignment if needed.
1425:                        if (skipByte) {
1426:                            output.write((byte) 0);
1427:                        }
1428:
1429:                        SeekableOutputStream sos = (SeekableOutputStream) output;
1430:
1431:                        // Save current position.
1432:                        long savePos = sos.getFilePointer();
1433:
1434:                        // Seek backward to the IFD offset and write IFD.
1435:                        sos.seek(ifdOffset);
1436:                        writeDirectory(ifdOffset, fields, nextIFDOffset);
1437:
1438:                        // Seek forward to position after data.
1439:                        sos.seek(savePos);
1440:                    } else if (tempFile != null) {
1441:
1442:                        // Using a file cache for the image data.
1443:
1444:                        // Close the original SeekableOutputStream.
1445:                        output.close();
1446:
1447:                        // Open a FileInputStream from which to copy the data.
1448:                        FileInputStream fileStream = new FileInputStream(
1449:                                tempFile);
1450:
1451:                        // Reset variable to the original OutputStream.
1452:                        output = outCache;
1453:
1454:                        // Write the IFD.
1455:                        writeDirectory(ifdOffset, fields, nextIFDOffset);
1456:
1457:                        // Write the image data.
1458:                        byte[] copyBuffer = new byte[8192];
1459:                        int bytesCopied = 0;
1460:                        while (bytesCopied < totalBytes) {
1461:                            int bytesRead = fileStream.read(copyBuffer);
1462:                            if (bytesRead == -1) {
1463:                                break;
1464:                            }
1465:                            output.write(copyBuffer, 0, bytesRead);
1466:                            bytesCopied += bytesRead;
1467:                        }
1468:
1469:                        // Delete the temporary file.
1470:                        fileStream.close();
1471:                        tempFile.delete();
1472:
1473:                        // Write an extra byte for IFD word alignment if needed.
1474:                        if (skipByte) {
1475:                            output.write((byte) 0);
1476:                        }
1477:                    } else if (output instanceof  ByteArrayOutputStream) {
1478:
1479:                        // Using a memory cache for the image data.
1480:
1481:                        ByteArrayOutputStream memoryStream = (ByteArrayOutputStream) output;
1482:
1483:                        // Reset variable to the original OutputStream.
1484:                        output = outCache;
1485:
1486:                        // Write the IFD.
1487:                        writeDirectory(ifdOffset, fields, nextIFDOffset);
1488:
1489:                        // Write the image data.
1490:                        memoryStream.writeTo(output);
1491:
1492:                        // Write an extra byte for IFD word alignment if needed.
1493:                        if (skipByte) {
1494:                            output.write((byte) 0);
1495:                        }
1496:                    } else {
1497:                        // This should never happen.
1498:                        throw new IllegalStateException();
1499:                    }
1500:                }
1501:
1502:                return nextIFDOffset;
1503:            }
1504:
1505:            /**
1506:             * Calculates the size of the IFD.
1507:             */
1508:            private int getDirectorySize(SortedSet fields) {
1509:                // Get the number of entries.
1510:                int numEntries = fields.size();
1511:
1512:                // Initialize the size excluding that of any values > 4 bytes.
1513:                int dirSize = 2 + numEntries * 12 + 4;
1514:
1515:                // Loop over fields adding the size of all values > 4 bytes.
1516:                Iterator iter = fields.iterator();
1517:                while (iter.hasNext()) {
1518:                    // Get the field.	    
1519:                    TIFFField field = (TIFFField) iter.next();
1520:
1521:                    // Determine the size of the field value.
1522:                    int valueSize = getValueSize(field);
1523:
1524:                    // Add any excess size.
1525:                    if (valueSize > 4) {
1526:                        dirSize += valueSize;
1527:                    }
1528:                }
1529:
1530:                return dirSize;
1531:            }
1532:
1533:            private void writeFileHeader() throws IOException {
1534:                // 8 byte image file header
1535:
1536:                // Byte order used within the file
1537:                if (isLittleEndian) {
1538:                    // Little Endian
1539:                    output.write('I');
1540:                    output.write('I');
1541:                } else {
1542:                    // Big Endian
1543:                    output.write('M');
1544:                    output.write('M');
1545:                }
1546:
1547:                // Magic value
1548:                writeUnsignedShort(42);
1549:
1550:                // Offset in bytes of the first IFD.
1551:                writeLong(8);
1552:            }
1553:
1554:            private void writeDirectory(int this IFDOffset, SortedSet fields,
1555:                    int nextIFDOffset) throws IOException {
1556:
1557:                // 2 byte count of number of directory entries (fields)
1558:                int numEntries = fields.size();
1559:
1560:                long offsetBeyondIFD = this IFDOffset + 12 * numEntries + 4 + 2;
1561:                ArrayList tooBig = new ArrayList();
1562:
1563:                // Write number of fields in the IFD
1564:                writeUnsignedShort(numEntries);
1565:
1566:                Iterator iter = fields.iterator();
1567:                while (iter.hasNext()) {
1568:
1569:                    // 12 byte field entry TIFFField	    
1570:                    TIFFField field = (TIFFField) iter.next();
1571:
1572:                    // byte 0-1 Tag that identifies a field
1573:                    int tag = field.getTag();
1574:                    writeUnsignedShort(tag);
1575:
1576:                    // byte 2-3 The field type
1577:                    int type = field.getType();
1578:                    writeUnsignedShort(type);
1579:
1580:                    // bytes 4-7 the number of values of the indicated type except
1581:                    // ASCII-valued fields which require the total number of bytes.
1582:                    int count = field.getCount();
1583:                    int valueSize = getValueSize(field);
1584:                    writeLong(type == TIFFField.TIFF_ASCII ? valueSize : count);
1585:
1586:                    // bytes 8 - 11 the value or value offset
1587:                    if (valueSize > 4) {
1588:
1589:                        // We need an offset as data won't fit into 4 bytes
1590:                        writeLong(offsetBeyondIFD);
1591:                        offsetBeyondIFD += valueSize;
1592:                        tooBig.add(field);
1593:
1594:                    } else {
1595:
1596:                        writeValuesAsFourBytes(field);
1597:                    }
1598:
1599:                }
1600:
1601:                // Address of next IFD
1602:                writeLong(nextIFDOffset);
1603:
1604:                // Write the tag values that did not fit into 4 bytes
1605:                for (int i = 0; i < tooBig.size(); i++) {
1606:                    writeValues((TIFFField) tooBig.get(i));
1607:                }
1608:            }
1609:
1610:            /**
1611:             * Determine the number of bytes in the value portion of the field.
1612:             */
1613:            private static final int getValueSize(TIFFField field) {
1614:                int type = field.getType();
1615:                int count = field.getCount();
1616:                int valueSize = 0;
1617:                if (type == TIFFField.TIFF_ASCII) {
1618:                    for (int i = 0; i < count; i++) {
1619:                        byte[] stringBytes = field.getAsString(i).getBytes();
1620:                        valueSize += stringBytes.length;
1621:                        if (stringBytes[stringBytes.length - 1] != (byte) 0) {
1622:                            valueSize++;
1623:                        }
1624:                    }
1625:                } else {
1626:                    valueSize = count * sizeOfType[type];
1627:                }
1628:                return valueSize;
1629:            }
1630:
1631:            private static final int[] sizeOfType = { 0, //  0 = n/a
1632:                    1, //  1 = byte
1633:                    1, //  2 = ascii
1634:                    2, //  3 = short
1635:                    4, //  4 = long
1636:                    8, //  5 = rational
1637:                    1, //  6 = sbyte
1638:                    1, //  7 = undefined
1639:                    2, //  8 = sshort
1640:                    4, //  9 = slong
1641:                    8, // 10 = srational
1642:                    4, // 11 = float
1643:                    8 // 12 = double 
1644:            };
1645:
1646:            private void writeValuesAsFourBytes(TIFFField field)
1647:                    throws IOException {
1648:
1649:                int dataType = field.getType();
1650:                int count = field.getCount();
1651:
1652:                switch (dataType) {
1653:
1654:                // 8 bits
1655:                case TIFFField.TIFF_BYTE:
1656:                case TIFFField.TIFF_SBYTE:
1657:                case TIFFField.TIFF_UNDEFINED:
1658:                    byte bytes[] = field.getAsBytes();
1659:
1660:                    for (int i = 0; i < count; i++) {
1661:                        output.write(bytes[i]);
1662:                    }
1663:
1664:                    for (int i = 0; i < (4 - count); i++) {
1665:                        output.write(0);
1666:                    }
1667:
1668:                    break;
1669:
1670:                // unsigned 16 bits
1671:                case TIFFField.TIFF_SHORT:
1672:                    char shorts[] = field.getAsChars();
1673:
1674:                    for (int i = 0; i < count; i++) {
1675:                        writeUnsignedShort(shorts[i]);
1676:                    }
1677:
1678:                    for (int i = 0; i < (2 - count); i++) {
1679:                        writeUnsignedShort(0);
1680:                    }
1681:
1682:                    break;
1683:
1684:                // signed 16 bits
1685:                case TIFFField.TIFF_SSHORT:
1686:                    short sshorts[] = field.getAsShorts();
1687:
1688:                    for (int i = 0; i < count; i++) {
1689:                        writeUnsignedShort(sshorts[i]);
1690:                    }
1691:
1692:                    for (int i = 0; i < (2 - count); i++) {
1693:                        writeUnsignedShort(0);
1694:                    }
1695:
1696:                    break;
1697:
1698:                // unsigned 32 bits
1699:                case TIFFField.TIFF_LONG:
1700:                    writeLong(field.getAsLong(0));
1701:                    break;
1702:
1703:                // signed 32 bits
1704:                case TIFFField.TIFF_SLONG:
1705:                    writeLong(field.getAsInt(0));
1706:                    break;
1707:
1708:                case TIFFField.TIFF_FLOAT:
1709:                    writeLong(Float.floatToIntBits(field.getAsFloat(0)));
1710:                    break;
1711:
1712:                case TIFFField.TIFF_ASCII:
1713:                    int asciiByteCount = 0;
1714:                    for (int i = 0; i < count; i++) {
1715:                        byte[] stringBytes = field.getAsString(i).getBytes();
1716:                        output.write(stringBytes);
1717:                        asciiByteCount += stringBytes.length;
1718:                        if (stringBytes[stringBytes.length - 1] != (byte) 0) {
1719:                            output.write(0);
1720:                            asciiByteCount++;
1721:                        }
1722:                    }
1723:                    for (int i = 0; i < (4 - asciiByteCount); i++) {
1724:                        output.write(0);
1725:                    }
1726:                    break;
1727:
1728:                default:
1729:                    throw new RuntimeException(JaiI18N
1730:                            .getString("TIFFImageEncoder10"));
1731:                }
1732:
1733:            }
1734:
1735:            private void writeValues(TIFFField field) throws IOException {
1736:
1737:                int dataType = field.getType();
1738:                int count = field.getCount();
1739:
1740:                switch (dataType) {
1741:
1742:                // unsigned 8 bits
1743:                case TIFFField.TIFF_BYTE:
1744:                case TIFFField.TIFF_SBYTE:
1745:                case TIFFField.TIFF_UNDEFINED:
1746:                    byte bytes[] = field.getAsBytes();
1747:                    for (int i = 0; i < count; i++) {
1748:                        output.write(bytes[i]);
1749:                    }
1750:                    break;
1751:
1752:                // unsigned 16 bits
1753:                case TIFFField.TIFF_SHORT:
1754:                    char shorts[] = field.getAsChars();
1755:                    for (int i = 0; i < count; i++) {
1756:                        writeUnsignedShort(shorts[i]);
1757:                    }
1758:                    break;
1759:
1760:                // signed 16 bits
1761:                case TIFFField.TIFF_SSHORT:
1762:                    short sshorts[] = field.getAsShorts();
1763:                    for (int i = 0; i < count; i++) {
1764:                        writeUnsignedShort(sshorts[i]);
1765:                    }
1766:                    break;
1767:
1768:                // unsigned 32 bits
1769:                case TIFFField.TIFF_LONG:
1770:                    long longs[] = field.getAsLongs();
1771:                    for (int i = 0; i < count; i++) {
1772:                        writeLong(longs[i]);
1773:                    }
1774:                    break;
1775:
1776:                // signed 32 bits
1777:                case TIFFField.TIFF_SLONG:
1778:                    int slongs[] = field.getAsInts();
1779:                    for (int i = 0; i < count; i++) {
1780:                        writeLong(slongs[i]);
1781:                    }
1782:                    break;
1783:
1784:                case TIFFField.TIFF_FLOAT:
1785:                    float[] floats = field.getAsFloats();
1786:                    for (int i = 0; i < count; i++) {
1787:                        int intBits = Float.floatToIntBits(floats[i]);
1788:                        writeLong(intBits);
1789:                    }
1790:                    break;
1791:
1792:                case TIFFField.TIFF_DOUBLE:
1793:                    double[] doubles = field.getAsDoubles();
1794:                    for (int i = 0; i < count; i++) {
1795:                        long longBits = Double.doubleToLongBits(doubles[i]);
1796:                        writeLong((int) (longBits >> 32));
1797:                        writeLong((int) (longBits & 0xffffffff));
1798:                    }
1799:                    break;
1800:
1801:                // unsigned rationals
1802:                case TIFFField.TIFF_RATIONAL:
1803:                    long rationals[][] = field.getAsRationals();
1804:                    for (int i = 0; i < count; i++) {
1805:                        writeLong(rationals[i][0]);
1806:                        writeLong(rationals[i][1]);
1807:                    }
1808:                    break;
1809:
1810:                // signed rationals
1811:                case TIFFField.TIFF_SRATIONAL:
1812:                    int srationals[][] = field.getAsSRationals();
1813:                    for (int i = 0; i < count; i++) {
1814:                        writeLong(srationals[i][0]);
1815:                        writeLong(srationals[i][1]);
1816:                    }
1817:                    break;
1818:
1819:                case TIFFField.TIFF_ASCII:
1820:                    for (int i = 0; i < count; i++) {
1821:                        byte[] stringBytes = field.getAsString(i).getBytes();
1822:                        output.write(stringBytes);
1823:                        if (stringBytes[stringBytes.length - 1] != (byte) 0) {
1824:                            output.write(0);
1825:                        }
1826:                    }
1827:                    break;
1828:
1829:                default:
1830:                    throw new RuntimeException(JaiI18N
1831:                            .getString("TIFFImageEncoder10"));
1832:
1833:                }
1834:
1835:            }
1836:
1837:            // Here s is never expected to have value greater than what can be 
1838:            // stored in 2 bytes.
1839:            private void writeUnsignedShort(int s) throws IOException {
1840:                if (isLittleEndian) {
1841:                    output.write(s & 0x00ff);
1842:                    output.write((s & 0xff00) >>> 8);
1843:                } else {
1844:                    output.write((s & 0xff00) >>> 8);
1845:                    output.write(s & 0x00ff);
1846:                }
1847:            }
1848:
1849:            private void writeLong(long l) throws IOException {
1850:                if (isLittleEndian) {
1851:                    output.write(((int) l & 0x000000ff));
1852:                    output.write((int) ((l & 0x0000ff00) >>> 8));
1853:                    output.write((int) ((l & 0x00ff0000) >>> 16));
1854:                    output.write((int) ((l & 0xff000000) >>> 24));
1855:                } else {
1856:                    output.write((int) ((l & 0xff000000) >>> 24));
1857:                    output.write((int) ((l & 0x00ff0000) >>> 16));
1858:                    output.write((int) ((l & 0x0000ff00) >>> 8));
1859:                    output.write(((int) l & 0x000000ff));
1860:                }
1861:            }
1862:
1863:            /**
1864:             * Returns the current offset in the supplied OutputStream.
1865:             * This method should only be used if compressing data.
1866:             */
1867:            private long getOffset(OutputStream out) throws IOException {
1868:                if (out instanceof  ByteArrayOutputStream) {
1869:                    return ((ByteArrayOutputStream) out).size();
1870:                } else if (out instanceof  SeekableOutputStream) {
1871:                    return ((SeekableOutputStream) out).getFilePointer();
1872:                } else {
1873:                    // Shouldn't happen.
1874:                    throw new IllegalStateException();
1875:                }
1876:            }
1877:
1878:            /**
1879:             * Performs PackBits compression on a tile of data.
1880:             */
1881:            private static int compressPackBits(byte[] data, int numRows,
1882:                    int bytesPerRow, byte[] compData) {
1883:                int inOffset = 0;
1884:                int outOffset = 0;
1885:
1886:                for (int i = 0; i < numRows; i++) {
1887:                    outOffset = packBits(data, inOffset, bytesPerRow, compData,
1888:                            outOffset);
1889:                    inOffset += bytesPerRow;
1890:                }
1891:
1892:                return outOffset;
1893:            }
1894:
1895:            /**
1896:             * Performs PackBits compression for a single buffer of data.
1897:             * This should be called for each row of each tile. The returned
1898:             * value is the offset into the output buffer after compression.
1899:             */
1900:            private static int packBits(byte[] input, int inOffset,
1901:                    int inCount, byte[] output, int outOffset) {
1902:                int inMax = inOffset + inCount - 1;
1903:                int inMaxMinus1 = inMax - 1;
1904:
1905:                while (inOffset <= inMax) {
1906:                    int run = 1;
1907:                    byte replicate = input[inOffset];
1908:                    while (run < 127 && inOffset < inMax
1909:                            && input[inOffset] == input[inOffset + 1]) {
1910:                        run++;
1911:                        inOffset++;
1912:                    }
1913:                    if (run > 1) {
1914:                        inOffset++;
1915:                        output[outOffset++] = (byte) (-(run - 1));
1916:                        output[outOffset++] = replicate;
1917:                    }
1918:
1919:                    run = 0;
1920:                    int saveOffset = outOffset;
1921:                    while (run < 128
1922:                            && ((inOffset < inMax && input[inOffset] != input[inOffset + 1]) || (inOffset < inMaxMinus1 && input[inOffset] != input[inOffset + 2]))) {
1923:                        run++;
1924:                        output[++outOffset] = input[inOffset++];
1925:                    }
1926:                    if (run > 0) {
1927:                        output[saveOffset] = (byte) (run - 1);
1928:                        outOffset++;
1929:                    }
1930:
1931:                    if (inOffset == inMax) {
1932:                        if (run > 0 && run < 128) {
1933:                            output[saveOffset]++;
1934:                            output[outOffset++] = input[inOffset++];
1935:                        } else {
1936:                            output[outOffset++] = (byte) 0;
1937:                            output[outOffset++] = input[inOffset++];
1938:                        }
1939:                    }
1940:                }
1941:
1942:                return outOffset;
1943:            }
1944:
1945:            private static int deflate(Deflater deflater, byte[] inflated,
1946:                    byte[] deflated) {
1947:                deflater.setInput(inflated);
1948:                deflater.finish();
1949:                int numCompressedBytes = deflater.deflate(deflated);
1950:                deflater.reset();
1951:                return numCompressedBytes;
1952:            }
1953:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.