Source Code Cross Referenced for TIFFImageEncoder.java in  » Graphic-Library » xmlgraphics-commons-1.2 » org » apache » xmlgraphics » image » codec » tiff » Java Source Code / Java DocumentationJava Source Code and Java Documentation

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


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