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

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


0001:        /*
0002:         * $RCSfile: CLibPNGMetadata.java,v $
0003:         *
0004:         * 
0005:         * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
0006:         * 
0007:         * Redistribution and use in source and binary forms, with or without
0008:         * modification, are permitted provided that the following conditions
0009:         * are met: 
0010:         * 
0011:         * - Redistribution of source code must retain the above copyright 
0012:         *   notice, this  list of conditions and the following disclaimer.
0013:         * 
0014:         * - Redistribution in binary form must reproduce the above copyright
0015:         *   notice, this list of conditions and the following disclaimer in 
0016:         *   the documentation and/or other materials provided with the
0017:         *   distribution.
0018:         * 
0019:         * Neither the name of Sun Microsystems, Inc. or the names of 
0020:         * contributors may be used to endorse or promote products derived 
0021:         * from this software without specific prior written permission.
0022:         * 
0023:         * This software is provided "AS IS," without a warranty of any 
0024:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
0025:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
0026:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027:         * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
0028:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
0029:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
0031:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035:         * POSSIBILITY OF SUCH DAMAGES. 
0036:         * 
0037:         * You acknowledge that this software is not designed or intended for 
0038:         * use in the design, construction, operation or maintenance of any 
0039:         * nuclear facility. 
0040:         *
0041:         * $Revision: 1.3 $
0042:         * $Date: 2006/02/27 17:25:04 $
0043:         * $State: Exp $
0044:         */
0045:
0046:        package com.sun.media.imageioimpl.plugins.png;
0047:
0048:        import java.awt.image.ColorModel;
0049:        import java.awt.image.IndexColorModel;
0050:        import java.awt.image.SampleModel;
0051:        import java.io.ByteArrayInputStream;
0052:        import java.io.InputStream;
0053:        import java.io.IOException;
0054:        import java.io.UnsupportedEncodingException;
0055:        import java.util.ArrayList;
0056:        import java.util.Arrays;
0057:        import java.util.Calendar;
0058:        import java.util.GregorianCalendar;
0059:        import java.util.Iterator;
0060:        import java.util.List;
0061:        import java.util.StringTokenizer;
0062:        import java.util.zip.DataFormatException;
0063:        import java.util.zip.Deflater;
0064:        import java.util.zip.Inflater;
0065:        import javax.imageio.IIOException;
0066:        import javax.imageio.ImageTypeSpecifier;
0067:        import javax.imageio.ImageWriteParam;
0068:        import javax.imageio.metadata.IIOInvalidTreeException;
0069:        import javax.imageio.metadata.IIOMetadata;
0070:        import javax.imageio.metadata.IIOMetadataFormat;
0071:        import javax.imageio.metadata.IIOMetadataFormatImpl;
0072:        import javax.imageio.metadata.IIOMetadataNode;
0073:        import javax.imageio.stream.ImageInputStream;
0074:        import javax.imageio.stream.MemoryCacheImageInputStream;
0075:        import org.w3c.dom.Node;
0076:        import com.sun.medialib.codec.png.Decoder;
0077:        import com.sun.medialib.codec.png.Encoder;
0078:        import com.sun.medialib.codec.png.PNGChunk;
0079:        import com.sun.medialib.codec.png.PNGTextualData;
0080:        import com.sun.medialib.codec.jiio.mediaLibImage;
0081:
0082:        //
0083:        // Core J2SE problems fixed in this package:
0084:        // 5109146:
0085:        // PNG: Background color initialization from standard metadata is incomplete
0086:        // 5109114:
0087:        // PNG: Cannot set IHDR_bitDepth from standard metadata /Data/BitsPerSample
0088:        // 5106305:
0089:        // PNG standard to native image metadata conversion incorrect for pixel size
0090:        // 5106550:
0091:        // PNG writer merge standard metadata fails for TextEntry sans #IMPLIED
0092:        // attributes
0093:        // 5082756:
0094:        // Image I/O plug-ins set metadata boolean attributes to "true" or "false"
0095:        // 5105068:
0096:        // PNGImageWriter.convertImageMetadata() broken for non-PNGMetadata
0097:        //
0098:
0099:        /**
0100:         */
0101:        public class CLibPNGMetadata extends IIOMetadata implements  Cloneable {
0102:
0103:            // package scope
0104:            public static final String nativeMetadataFormatName = "javax_imageio_png_1.0";
0105:
0106:            protected static final String nativeMetadataFormatClassName = "com.sun.media.imageioimpl.plugins.png.CLibPNGMetadataFormat";
0107:
0108:            // Color types for IHDR chunk
0109:            public static final String[] IHDR_colorTypeNames = { "Grayscale",
0110:                    null, "RGB", "Palette", "GrayAlpha", null, "RGBAlpha" };
0111:
0112:            public static final int[] IHDR_numChannels = { 1, 0, 3, 3, 2, 0, 4 };
0113:
0114:            // Bit depths for IHDR chunk
0115:            public static final String[] IHDR_bitDepths = { "1", "2", "4", "8",
0116:                    "16" };
0117:
0118:            // Compression methods for IHDR chunk
0119:            public static final String[] IHDR_compressionMethodNames = { "deflate" };
0120:
0121:            // Filter methods for IHDR chunk
0122:            public static final String[] IHDR_filterMethodNames = { "adaptive" };
0123:
0124:            // Interlace methods for IHDR chunk
0125:            public static final String[] IHDR_interlaceMethodNames = { "none",
0126:                    "adam7" };
0127:
0128:            // Compression methods for iCCP chunk
0129:            public static final String[] iCCP_compressionMethodNames = { "deflate" };
0130:
0131:            // Compression methods for zTXt chunk
0132:            public static final String[] zTXt_compressionMethodNames = { "deflate" };
0133:
0134:            // "Unknown" unit for pHYs chunk
0135:            public static final int PHYS_UNIT_UNKNOWN = 0;
0136:
0137:            // "Meter" unit for pHYs chunk
0138:            public static final int PHYS_UNIT_METER = 1;
0139:
0140:            // Unit specifiers for pHYs chunk
0141:            public static final String[] unitSpecifierNames = { "unknown",
0142:                    "meter" };
0143:
0144:            // Rendering intents for sRGB chunk
0145:            public static final String[] renderingIntentNames = { "Perceptual", // 0
0146:                    "Relative colorimetric", // 1
0147:                    "Saturation", // 2
0148:                    "Absolute colorimetric" // 3
0149:
0150:            };
0151:
0152:            // Color space types for Chroma->ColorSpaceType node
0153:            public static final String[] colorSpaceTypeNames = { "GRAY", null,
0154:                    "RGB", "RGB", "GRAY", null, "RGB" };
0155:
0156:            // BEGIN Definitions required for reading.
0157:
0158:            // Critical chunks
0159:            static final int IHDR_TYPE = chunkType("IHDR");
0160:            static final int PLTE_TYPE = chunkType("PLTE");
0161:            static final int IDAT_TYPE = chunkType("IDAT");
0162:            static final int IEND_TYPE = chunkType("IEND");
0163:
0164:            // Ancillary chunks
0165:            static final int bKGD_TYPE = chunkType("bKGD");
0166:            static final int cHRM_TYPE = chunkType("cHRM");
0167:            static final int gAMA_TYPE = chunkType("gAMA");
0168:            static final int hIST_TYPE = chunkType("hIST");
0169:            static final int iCCP_TYPE = chunkType("iCCP");
0170:            static final int iTXt_TYPE = chunkType("iTXt");
0171:            static final int pHYs_TYPE = chunkType("pHYs");
0172:            static final int sBIT_TYPE = chunkType("sBIT");
0173:            static final int sPLT_TYPE = chunkType("sPLT");
0174:            static final int sRGB_TYPE = chunkType("sRGB");
0175:            static final int tEXt_TYPE = chunkType("tEXt");
0176:            static final int tIME_TYPE = chunkType("tIME");
0177:            static final int tRNS_TYPE = chunkType("tRNS");
0178:            static final int zTXt_TYPE = chunkType("zTXt");
0179:
0180:            static final int PNG_COLOR_GRAY = 0;
0181:            static final int PNG_COLOR_RGB = 2;
0182:            static final int PNG_COLOR_PALETTE = 3;
0183:            static final int PNG_COLOR_GRAY_ALPHA = 4;
0184:            static final int PNG_COLOR_RGB_ALPHA = 6;
0185:
0186:            // END Definitions required for reading.
0187:
0188:            // IHDR chunk
0189:            public boolean IHDR_present;
0190:            public int IHDR_width;
0191:            public int IHDR_height;
0192:            public int IHDR_bitDepth;
0193:            public int IHDR_colorType;
0194:            public int IHDR_compressionMethod;
0195:            public int IHDR_filterMethod;
0196:            public int IHDR_interlaceMethod; // 0 == none, 1 == adam7
0197:
0198:            // PLTE chunk
0199:            public boolean PLTE_present;
0200:            public byte[] PLTE_red;
0201:            public byte[] PLTE_green;
0202:            public byte[] PLTE_blue;
0203:
0204:            // bKGD chunk
0205:            // If external (non-PNG sourced) data has red = green = blue,
0206:            // always store it as gray and promote when writing
0207:            public boolean bKGD_present;
0208:            public int bKGD_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE
0209:            public int bKGD_index;
0210:            public int bKGD_gray;
0211:            public int bKGD_red;
0212:            public int bKGD_green;
0213:            public int bKGD_blue;
0214:
0215:            // cHRM chunk
0216:            public boolean cHRM_present;
0217:            public int cHRM_whitePointX;
0218:            public int cHRM_whitePointY;
0219:            public int cHRM_redX;
0220:            public int cHRM_redY;
0221:            public int cHRM_greenX;
0222:            public int cHRM_greenY;
0223:            public int cHRM_blueX;
0224:            public int cHRM_blueY;
0225:
0226:            // gAMA chunk
0227:            public boolean gAMA_present;
0228:            public int gAMA_gamma;
0229:
0230:            // hIST chunk
0231:            public boolean hIST_present;
0232:            public char[] hIST_histogram;
0233:
0234:            // iCCP chunk
0235:            public boolean iCCP_present;
0236:            public String iCCP_profileName;
0237:            public int iCCP_compressionMethod;
0238:            public byte[] iCCP_compressedProfile;
0239:
0240:            // iTXt chunk
0241:            public ArrayList iTXt_keyword = new ArrayList(); // Strings
0242:            public ArrayList iTXt_compressionFlag = new ArrayList(); // Integers
0243:            public ArrayList iTXt_compressionMethod = new ArrayList(); // Integers
0244:            public ArrayList iTXt_languageTag = new ArrayList(); // Strings
0245:            public ArrayList iTXt_translatedKeyword = new ArrayList(); // Strings
0246:            public ArrayList iTXt_text = new ArrayList(); // Strings
0247:
0248:            // pHYs chunk
0249:            public boolean pHYs_present;
0250:            public int pHYs_pixelsPerUnitXAxis;
0251:            public int pHYs_pixelsPerUnitYAxis;
0252:            public int pHYs_unitSpecifier; // 0 == unknown, 1 == meter
0253:
0254:            // sBIT chunk
0255:            public boolean sBIT_present;
0256:            public int sBIT_colorType; // PNG_COLOR_GRAY, _GRAY_ALPHA, _RGB, _RGB_ALPHA
0257:            public int sBIT_grayBits;
0258:            public int sBIT_redBits;
0259:            public int sBIT_greenBits;
0260:            public int sBIT_blueBits;
0261:            public int sBIT_alphaBits;
0262:
0263:            // sPLT chunk
0264:            public boolean sPLT_present;
0265:            public String sPLT_paletteName; // 1-79 characters
0266:            public int sPLT_sampleDepth; // 8 or 16
0267:            public int[] sPLT_red;
0268:            public int[] sPLT_green;
0269:            public int[] sPLT_blue;
0270:            public int[] sPLT_alpha;
0271:            public int[] sPLT_frequency;
0272:
0273:            // sRGB chunk
0274:            public boolean sRGB_present;
0275:            public int sRGB_renderingIntent;
0276:
0277:            // tEXt chunk
0278:            public ArrayList tEXt_keyword = new ArrayList(); // 1-79 char Strings
0279:            public ArrayList tEXt_text = new ArrayList(); // Strings
0280:
0281:            // tIME chunk
0282:            public boolean tIME_present;
0283:            public int tIME_year;
0284:            public int tIME_month;
0285:            public int tIME_day;
0286:            public int tIME_hour;
0287:            public int tIME_minute;
0288:            public int tIME_second;
0289:
0290:            // tRNS chunk
0291:            // If external (non-PNG sourced) data has red = green = blue,
0292:            // always store it as gray and promote when writing
0293:            public boolean tRNS_present;
0294:            public int tRNS_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE
0295:            public byte[] tRNS_alpha; // May have fewer entries than PLTE_red, etc.
0296:            public int tRNS_gray;
0297:            public int tRNS_red;
0298:            public int tRNS_green;
0299:            public int tRNS_blue;
0300:
0301:            // zTXt chunk
0302:            public ArrayList zTXt_keyword = new ArrayList(); // Strings
0303:            public ArrayList zTXt_compressionMethod = new ArrayList(); // Integers
0304:            public ArrayList zTXt_text = new ArrayList(); // Strings
0305:
0306:            // Unknown chunks
0307:            public ArrayList unknownChunkType = new ArrayList(); // Strings
0308:            public ArrayList unknownChunkData = new ArrayList(); // byte arrays
0309:
0310:            /**
0311:             * Converts its parameter to another <code>String</code> which contains
0312:             * only printable Latin-1 characters but not leading, trailing, or
0313:             * consecutive spaces.
0314:             *
0315:             * @param s the <code>String</code> to convert.
0316:             * @return a printable Latin-1 <code>String</code> sans superfluous spaces.
0317:             */
0318:            static String toPrintableLatin1(String s) {
0319:                // Pass a null right back.
0320:                if (s == null)
0321:                    return null;
0322:
0323:                // Get Latin-1 characters.
0324:                byte[] data = null;
0325:                try {
0326:                    data = s.getBytes("ISO-8859-1");
0327:                } catch (UnsupportedEncodingException e) {
0328:                    // In theory this should not happen (assert).
0329:                    data = s.getBytes();
0330:                }
0331:
0332:                // Copy printable characters omitting leading spaces and
0333:                // all but first trailing space.
0334:                int len = 0;
0335:                int prev = 0;
0336:                for (int i = 0; i < data.length; i++) {
0337:                    int d = data[i] & 0xFF;
0338:                    if (prev == 32 && d == 32)
0339:                        continue;
0340:                    if ((d > 32 && d <= 126) || (d >= 161 && d <= 255)
0341:                            || (d == 32 && len != 0))
0342:                        data[len++] = (byte) d;
0343:                    prev = d;
0344:                }
0345:
0346:                // Return an empty string if no acceptable characters.
0347:                if (len == 0)
0348:                    return "";
0349:
0350:                // Omit trailing space, if any.
0351:                if (data[len - 1] == 32)
0352:                    len--;
0353:
0354:                return new String(data, 0, len);
0355:            }
0356:
0357:            public CLibPNGMetadata() {
0358:                super (true, nativeMetadataFormatName,
0359:                        nativeMetadataFormatClassName, null, null);
0360:            }
0361:
0362:            public CLibPNGMetadata(IIOMetadata metadata)
0363:                    throws IIOInvalidTreeException {
0364:
0365:                this ();
0366:
0367:                if (metadata != null) {
0368:                    List formats = Arrays.asList(metadata
0369:                            .getMetadataFormatNames());
0370:
0371:                    if (formats.contains(nativeMetadataFormatName)) {
0372:                        // Initialize from native image metadata format.
0373:                        String format = nativeMetadataFormatName;
0374:                        setFromTree(format, metadata.getAsTree(format));
0375:                    } else if (metadata.isStandardMetadataFormatSupported()) {
0376:                        // Initialize from standard metadata form of the input tree.
0377:                        String format = IIOMetadataFormatImpl.standardMetadataFormatName;
0378:                        setFromTree(format, metadata.getAsTree(format));
0379:                    }
0380:                }
0381:            }
0382:
0383:            /**
0384:             * Sets the instance variables of the IHDR and if necessary PLTE and
0385:             * tRNS chunks. The <code>numBands</code> parameter is necessary since
0386:             * we may only be writing a subset of the image bands.
0387:             */
0388:            public void initialize(ImageTypeSpecifier imageType, int numBands,
0389:                    ImageWriteParam param, int interlaceMethod) {
0390:                ColorModel colorModel = imageType.getColorModel();
0391:                SampleModel sampleModel = imageType.getSampleModel();
0392:
0393:                // Intialize IHDR_width and IHDR_height
0394:                IHDR_width = sampleModel.getWidth();
0395:                IHDR_height = sampleModel.getHeight();
0396:
0397:                // Initialize IHDR_bitDepth
0398:                int[] sampleSize = sampleModel.getSampleSize();
0399:                int bitDepth = sampleSize[0];
0400:                // Choose max bit depth over all channels
0401:                // Fixes bug 4413109
0402:                for (int i = 1; i < sampleSize.length; i++) {
0403:                    if (sampleSize[i] > bitDepth) {
0404:                        bitDepth = sampleSize[i];
0405:                    }
0406:                }
0407:                // Multi-channel images must have a bit depth of 8 or 16
0408:                if (sampleSize.length > 1 && bitDepth < 8) {
0409:                    bitDepth = 8;
0410:                }
0411:
0412:                // Round bit depth up to a power of 2
0413:                if (bitDepth > 2 && bitDepth < 4) {
0414:                    bitDepth = 4;
0415:                } else if (bitDepth > 4 && bitDepth < 8) {
0416:                    bitDepth = 8;
0417:                } else if (bitDepth > 8 && bitDepth < 16) {
0418:                    bitDepth = 16;
0419:                } else if (bitDepth > 16) {
0420:                    throw new RuntimeException("bitDepth > 16!");
0421:                }
0422:                IHDR_bitDepth = bitDepth;
0423:
0424:                // Initialize IHDR_colorType
0425:                if (colorModel instanceof  IndexColorModel) {
0426:                    IndexColorModel icm = (IndexColorModel) colorModel;
0427:                    int size = icm.getMapSize();
0428:
0429:                    byte[] reds = new byte[size];
0430:                    icm.getReds(reds);
0431:                    byte[] greens = new byte[size];
0432:                    icm.getGreens(greens);
0433:                    byte[] blues = new byte[size];
0434:                    icm.getBlues(blues);
0435:
0436:                    // Determine whether the color tables are actually a gray ramp
0437:                    // if the color type has not been set previously
0438:                    boolean isGray = false;
0439:                    if (!IHDR_present || (IHDR_colorType != PNG_COLOR_PALETTE)) {
0440:                        isGray = true;
0441:                        int scale = 255 / ((1 << IHDR_bitDepth) - 1);
0442:                        for (int i = 0; i < size; i++) {
0443:                            byte red = reds[i];
0444:                            if ((red != (byte) (i * scale))
0445:                                    || (red != greens[i]) || (red != blues[i])) {
0446:                                isGray = false;
0447:                                break;
0448:                            }
0449:                        }
0450:                    }
0451:
0452:                    // Determine whether transparency exists
0453:                    boolean hasAlpha = colorModel.hasAlpha();
0454:
0455:                    byte[] alpha = null;
0456:                    if (hasAlpha) {
0457:                        alpha = new byte[size];
0458:                        icm.getAlphas(alpha);
0459:                    }
0460:
0461:                    if (isGray && hasAlpha) {
0462:                        IHDR_colorType = PNG_COLOR_GRAY_ALPHA;
0463:                    } else if (isGray) {
0464:                        IHDR_colorType = PNG_COLOR_GRAY;
0465:                    } else {
0466:                        IHDR_colorType = PNG_COLOR_PALETTE;
0467:
0468:                        // Initialize PLTE chunk
0469:                        PLTE_present = true;
0470:                        PLTE_red = (byte[]) reds.clone();
0471:                        PLTE_green = (byte[]) greens.clone();
0472:                        PLTE_blue = (byte[]) blues.clone();
0473:
0474:                        if (hasAlpha) {
0475:                            // Initialize tRNS chunk
0476:                            tRNS_present = true;
0477:                            tRNS_colorType = PNG_COLOR_PALETTE;
0478:                            tRNS_alpha = (byte[]) alpha.clone();
0479:                        }
0480:                    }
0481:                } else {
0482:                    if (numBands == 1) {
0483:                        IHDR_colorType = PNG_COLOR_GRAY;
0484:                    } else if (numBands == 2) {
0485:                        IHDR_colorType = PNG_COLOR_GRAY_ALPHA;
0486:                    } else if (numBands == 3) {
0487:                        IHDR_colorType = PNG_COLOR_RGB;
0488:                    } else if (numBands == 4) {
0489:                        IHDR_colorType = PNG_COLOR_RGB_ALPHA;
0490:                    } else {
0491:                        throw new RuntimeException("Number of bands not 1-4!");
0492:                    }
0493:                }
0494:
0495:                // Initialize IHDR_compressionMethod and IHDR_filterMethod
0496:                IHDR_compressionMethod = IHDR_filterMethod = 0; // Only supported value
0497:
0498:                // Initialize IHDR_interlaceMethod
0499:                if (param != null
0500:                        && param.getProgressiveMode() == ImageWriteParam.MODE_DISABLED) {
0501:                    IHDR_interlaceMethod = 0; // No interlacing.
0502:                } else if (param != null
0503:                        && param.getProgressiveMode() == ImageWriteParam.MODE_DEFAULT) {
0504:                    IHDR_interlaceMethod = 1; // Adam7
0505:                } else {
0506:                    // param == null ||
0507:                    // param.getProgressiveMode() ==
0508:                    // ImageWriteParam.MODE_COPY_FROM_METADATA
0509:                    IHDR_interlaceMethod = interlaceMethod;
0510:                }
0511:
0512:                IHDR_present = true;
0513:            }
0514:
0515:            public boolean isReadOnly() {
0516:                return false;
0517:            }
0518:
0519:            private ArrayList cloneBytesArrayList(ArrayList in) {
0520:                if (in == null) {
0521:                    return null;
0522:                } else {
0523:                    ArrayList list = new ArrayList(in.size());
0524:                    Iterator iter = in.iterator();
0525:                    while (iter.hasNext()) {
0526:                        Object o = iter.next();
0527:                        if (o == null) {
0528:                            list.add(null);
0529:                        } else {
0530:                            list.add(((byte[]) o).clone());
0531:                        }
0532:                    }
0533:
0534:                    return list;
0535:                }
0536:            }
0537:
0538:            // Deep clone
0539:            public Object clone() {
0540:                CLibPNGMetadata metadata;
0541:                try {
0542:                    metadata = (CLibPNGMetadata) super .clone();
0543:                } catch (CloneNotSupportedException e) {
0544:                    return null;
0545:                }
0546:
0547:                // unknownChunkData needs deep clone
0548:                metadata.unknownChunkData = cloneBytesArrayList(this .unknownChunkData);
0549:
0550:                return metadata;
0551:            }
0552:
0553:            public Node getAsTree(String formatName) {
0554:                if (formatName.equals(nativeMetadataFormatName)) {
0555:                    return getNativeTree();
0556:                } else if (formatName
0557:                        .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0558:                    return getStandardTree();
0559:                } else {
0560:                    throw new IllegalArgumentException(
0561:                            "Not a recognized format!");
0562:                }
0563:            }
0564:
0565:            private Node getNativeTree() {
0566:                IIOMetadataNode node = null; // scratch node
0567:                IIOMetadataNode root = new IIOMetadataNode(
0568:                        nativeMetadataFormatName);
0569:
0570:                // IHDR
0571:                if (IHDR_present) {
0572:                    IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR");
0573:                    IHDR_node.setAttribute("width", Integer
0574:                            .toString(IHDR_width));
0575:                    IHDR_node.setAttribute("height", Integer
0576:                            .toString(IHDR_height));
0577:                    IHDR_node.setAttribute("bitDepth", Integer
0578:                            .toString(IHDR_bitDepth));
0579:                    IHDR_node.setAttribute("colorType",
0580:                            IHDR_colorTypeNames[IHDR_colorType]);
0581:                    // IHDR_compressionMethod must be 0 in PNG 1.1
0582:                    IHDR_node
0583:                            .setAttribute(
0584:                                    "compressionMethod",
0585:                                    IHDR_compressionMethodNames[IHDR_compressionMethod]);
0586:                    // IHDR_filterMethod must be 0 in PNG 1.1
0587:                    IHDR_node.setAttribute("filterMethod",
0588:                            IHDR_filterMethodNames[IHDR_filterMethod]);
0589:                    IHDR_node.setAttribute("interlaceMethod",
0590:                            IHDR_interlaceMethodNames[IHDR_interlaceMethod]);
0591:                    root.appendChild(IHDR_node);
0592:                }
0593:
0594:                // PLTE
0595:                if (PLTE_present) {
0596:                    IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE");
0597:                    int numEntries = PLTE_red.length;
0598:                    for (int i = 0; i < numEntries; i++) {
0599:                        IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry");
0600:                        entry.setAttribute("index", Integer.toString(i));
0601:                        entry.setAttribute("red", Integer
0602:                                .toString(PLTE_red[i] & 0xff));
0603:                        entry.setAttribute("green", Integer
0604:                                .toString(PLTE_green[i] & 0xff));
0605:                        entry.setAttribute("blue", Integer
0606:                                .toString(PLTE_blue[i] & 0xff));
0607:                        PLTE_node.appendChild(entry);
0608:                    }
0609:
0610:                    root.appendChild(PLTE_node);
0611:                }
0612:
0613:                // bKGD
0614:                if (bKGD_present) {
0615:                    IIOMetadataNode bKGD_node = new IIOMetadataNode("bKGD");
0616:
0617:                    if (bKGD_colorType == PNG_COLOR_PALETTE) {
0618:                        node = new IIOMetadataNode("bKGD_Palette");
0619:                        node
0620:                                .setAttribute("index", Integer
0621:                                        .toString(bKGD_index));
0622:                    } else if (bKGD_colorType == PNG_COLOR_GRAY) {
0623:                        node = new IIOMetadataNode("bKGD_Grayscale");
0624:                        node.setAttribute("gray", Integer.toString(bKGD_gray));
0625:                    } else if (bKGD_colorType == PNG_COLOR_RGB) {
0626:                        node = new IIOMetadataNode("bKGD_RGB");
0627:                        node.setAttribute("red", Integer.toString(bKGD_red));
0628:                        node
0629:                                .setAttribute("green", Integer
0630:                                        .toString(bKGD_green));
0631:                        node.setAttribute("blue", Integer.toString(bKGD_blue));
0632:                    }
0633:                    bKGD_node.appendChild(node);
0634:
0635:                    root.appendChild(bKGD_node);
0636:                }
0637:
0638:                // cHRM
0639:                if (cHRM_present) {
0640:                    IIOMetadataNode cHRM_node = new IIOMetadataNode("cHRM");
0641:                    cHRM_node.setAttribute("whitePointX", Integer
0642:                            .toString(cHRM_whitePointX));
0643:                    cHRM_node.setAttribute("whitePointY", Integer
0644:                            .toString(cHRM_whitePointY));
0645:                    cHRM_node.setAttribute("redX", Integer.toString(cHRM_redX));
0646:                    cHRM_node.setAttribute("redY", Integer.toString(cHRM_redY));
0647:                    cHRM_node.setAttribute("greenX", Integer
0648:                            .toString(cHRM_greenX));
0649:                    cHRM_node.setAttribute("greenY", Integer
0650:                            .toString(cHRM_greenY));
0651:                    cHRM_node.setAttribute("blueX", Integer
0652:                            .toString(cHRM_blueX));
0653:                    cHRM_node.setAttribute("blueY", Integer
0654:                            .toString(cHRM_blueY));
0655:
0656:                    root.appendChild(cHRM_node);
0657:                }
0658:
0659:                // gAMA
0660:                if (gAMA_present) {
0661:                    IIOMetadataNode gAMA_node = new IIOMetadataNode("gAMA");
0662:                    gAMA_node.setAttribute("value", Integer
0663:                            .toString(gAMA_gamma));
0664:
0665:                    root.appendChild(gAMA_node);
0666:                }
0667:
0668:                // hIST
0669:                if (hIST_present) {
0670:                    IIOMetadataNode hIST_node = new IIOMetadataNode("hIST");
0671:
0672:                    for (int i = 0; i < hIST_histogram.length; i++) {
0673:                        IIOMetadataNode hist = new IIOMetadataNode("hISTEntry");
0674:                        hist.setAttribute("index", Integer.toString(i));
0675:                        hist.setAttribute("value", Integer
0676:                                .toString(hIST_histogram[i]));
0677:                        hIST_node.appendChild(hist);
0678:                    }
0679:
0680:                    root.appendChild(hIST_node);
0681:                }
0682:
0683:                // iCCP
0684:                if (iCCP_present) {
0685:                    IIOMetadataNode iCCP_node = new IIOMetadataNode("iCCP");
0686:                    iCCP_node.setAttribute("profileName", iCCP_profileName);
0687:                    iCCP_node
0688:                            .setAttribute(
0689:                                    "compressionMethod",
0690:                                    iCCP_compressionMethodNames[iCCP_compressionMethod]);
0691:
0692:                    Object profile = iCCP_compressedProfile;
0693:                    if (profile != null) {
0694:                        profile = ((byte[]) profile).clone();
0695:                    }
0696:                    iCCP_node.setUserObject(profile);
0697:
0698:                    root.appendChild(iCCP_node);
0699:                }
0700:
0701:                // iTXt
0702:                if (iTXt_keyword.size() > 0) {
0703:                    IIOMetadataNode iTXt_parent = new IIOMetadataNode("iTXt");
0704:                    for (int i = 0; i < iTXt_keyword.size(); i++) {
0705:                        Integer val;
0706:
0707:                        IIOMetadataNode iTXt_node = new IIOMetadataNode(
0708:                                "iTXtEntry");
0709:                        iTXt_node.setAttribute("keyword", (String) iTXt_keyword
0710:                                .get(i));
0711:                        val = (Integer) iTXt_compressionFlag.get(i);
0712:                        iTXt_node.setAttribute("compressionFlag", val
0713:                                .toString());
0714:                        val = (Integer) iTXt_compressionMethod.get(i);
0715:                        iTXt_node.setAttribute("compressionMethod", val
0716:                                .toString());
0717:                        iTXt_node.setAttribute("languageTag",
0718:                                (String) iTXt_languageTag.get(i));
0719:                        iTXt_node.setAttribute("translatedKeyword",
0720:                                (String) iTXt_translatedKeyword.get(i));
0721:                        iTXt_node.setAttribute("text", (String) iTXt_text
0722:                                .get(i));
0723:
0724:                        iTXt_parent.appendChild(iTXt_node);
0725:                    }
0726:
0727:                    root.appendChild(iTXt_parent);
0728:                }
0729:
0730:                // pHYs
0731:                if (pHYs_present) {
0732:                    IIOMetadataNode pHYs_node = new IIOMetadataNode("pHYs");
0733:                    pHYs_node.setAttribute("pixelsPerUnitXAxis", Integer
0734:                            .toString(pHYs_pixelsPerUnitXAxis));
0735:                    pHYs_node.setAttribute("pixelsPerUnitYAxis", Integer
0736:                            .toString(pHYs_pixelsPerUnitYAxis));
0737:                    pHYs_node.setAttribute("unitSpecifier",
0738:                            unitSpecifierNames[pHYs_unitSpecifier]);
0739:
0740:                    root.appendChild(pHYs_node);
0741:                }
0742:
0743:                // sBIT
0744:                if (sBIT_present) {
0745:                    IIOMetadataNode sBIT_node = new IIOMetadataNode("sBIT");
0746:
0747:                    if (sBIT_colorType == PNG_COLOR_GRAY) {
0748:                        node = new IIOMetadataNode("sBIT_Grayscale");
0749:                        node.setAttribute("gray", Integer
0750:                                .toString(sBIT_grayBits));
0751:                    } else if (sBIT_colorType == PNG_COLOR_GRAY_ALPHA) {
0752:                        node = new IIOMetadataNode("sBIT_GrayAlpha");
0753:                        node.setAttribute("gray", Integer
0754:                                .toString(sBIT_grayBits));
0755:                        node.setAttribute("alpha", Integer
0756:                                .toString(sBIT_alphaBits));
0757:                    } else if (sBIT_colorType == PNG_COLOR_RGB) {
0758:                        node = new IIOMetadataNode("sBIT_RGB");
0759:                        node
0760:                                .setAttribute("red", Integer
0761:                                        .toString(sBIT_redBits));
0762:                        node.setAttribute("green", Integer
0763:                                .toString(sBIT_greenBits));
0764:                        node.setAttribute("blue", Integer
0765:                                .toString(sBIT_blueBits));
0766:                    } else if (sBIT_colorType == PNG_COLOR_RGB_ALPHA) {
0767:                        node = new IIOMetadataNode("sBIT_RGBAlpha");
0768:                        node
0769:                                .setAttribute("red", Integer
0770:                                        .toString(sBIT_redBits));
0771:                        node.setAttribute("green", Integer
0772:                                .toString(sBIT_greenBits));
0773:                        node.setAttribute("blue", Integer
0774:                                .toString(sBIT_blueBits));
0775:                        node.setAttribute("alpha", Integer
0776:                                .toString(sBIT_alphaBits));
0777:                    } else if (sBIT_colorType == PNG_COLOR_PALETTE) {
0778:                        node = new IIOMetadataNode("sBIT_Palette");
0779:                        node
0780:                                .setAttribute("red", Integer
0781:                                        .toString(sBIT_redBits));
0782:                        node.setAttribute("green", Integer
0783:                                .toString(sBIT_greenBits));
0784:                        node.setAttribute("blue", Integer
0785:                                .toString(sBIT_blueBits));
0786:                    }
0787:                    sBIT_node.appendChild(node);
0788:
0789:                    root.appendChild(sBIT_node);
0790:                }
0791:
0792:                // sPLT
0793:                if (sPLT_present) {
0794:                    IIOMetadataNode sPLT_node = new IIOMetadataNode("sPLT");
0795:
0796:                    sPLT_node.setAttribute("name", sPLT_paletteName);
0797:                    sPLT_node.setAttribute("sampleDepth", Integer
0798:                            .toString(sPLT_sampleDepth));
0799:
0800:                    int numEntries = sPLT_red.length;
0801:                    for (int i = 0; i < numEntries; i++) {
0802:                        IIOMetadataNode entry = new IIOMetadataNode("sPLTEntry");
0803:                        entry.setAttribute("index", Integer.toString(i));
0804:                        entry
0805:                                .setAttribute("red", Integer
0806:                                        .toString(sPLT_red[i]));
0807:                        entry.setAttribute("green", Integer
0808:                                .toString(sPLT_green[i]));
0809:                        entry.setAttribute("blue", Integer
0810:                                .toString(sPLT_blue[i]));
0811:                        entry.setAttribute("alpha", Integer
0812:                                .toString(sPLT_alpha[i]));
0813:                        entry.setAttribute("frequency", Integer
0814:                                .toString(sPLT_frequency[i]));
0815:                        sPLT_node.appendChild(entry);
0816:                    }
0817:
0818:                    root.appendChild(sPLT_node);
0819:                }
0820:
0821:                // sRGB
0822:                if (sRGB_present) {
0823:                    IIOMetadataNode sRGB_node = new IIOMetadataNode("sRGB");
0824:                    sRGB_node.setAttribute("renderingIntent",
0825:                            renderingIntentNames[sRGB_renderingIntent]);
0826:
0827:                    root.appendChild(sRGB_node);
0828:                }
0829:
0830:                // tEXt
0831:                if (tEXt_keyword.size() > 0) {
0832:                    IIOMetadataNode tEXt_parent = new IIOMetadataNode("tEXt");
0833:                    for (int i = 0; i < tEXt_keyword.size(); i++) {
0834:                        IIOMetadataNode tEXt_node = new IIOMetadataNode(
0835:                                "tEXtEntry");
0836:                        tEXt_node.setAttribute("keyword", (String) tEXt_keyword
0837:                                .get(i));
0838:                        tEXt_node.setAttribute("value", (String) tEXt_text
0839:                                .get(i));
0840:
0841:                        tEXt_parent.appendChild(tEXt_node);
0842:                    }
0843:
0844:                    root.appendChild(tEXt_parent);
0845:                }
0846:
0847:                // tIME
0848:                if (tIME_present) {
0849:                    IIOMetadataNode tIME_node = new IIOMetadataNode("tIME");
0850:                    tIME_node.setAttribute("year", Integer.toString(tIME_year));
0851:                    tIME_node.setAttribute("month", Integer
0852:                            .toString(tIME_month));
0853:                    tIME_node.setAttribute("day", Integer.toString(tIME_day));
0854:                    tIME_node.setAttribute("hour", Integer.toString(tIME_hour));
0855:                    tIME_node.setAttribute("minute", Integer
0856:                            .toString(tIME_minute));
0857:                    tIME_node.setAttribute("second", Integer
0858:                            .toString(tIME_second));
0859:
0860:                    root.appendChild(tIME_node);
0861:                }
0862:
0863:                // tRNS
0864:                if (tRNS_present) {
0865:                    IIOMetadataNode tRNS_node = new IIOMetadataNode("tRNS");
0866:
0867:                    if (tRNS_colorType == PNG_COLOR_PALETTE) {
0868:                        node = new IIOMetadataNode("tRNS_Palette");
0869:
0870:                        for (int i = 0; i < tRNS_alpha.length; i++) {
0871:                            IIOMetadataNode entry = new IIOMetadataNode(
0872:                                    "tRNS_PaletteEntry");
0873:                            entry.setAttribute("index", Integer.toString(i));
0874:                            entry.setAttribute("alpha", Integer
0875:                                    .toString(tRNS_alpha[i] & 0xff));
0876:                            node.appendChild(entry);
0877:                        }
0878:                    } else if (tRNS_colorType == PNG_COLOR_GRAY) {
0879:                        node = new IIOMetadataNode("tRNS_Grayscale");
0880:                        node.setAttribute("gray", Integer.toString(tRNS_gray));
0881:                    } else if (tRNS_colorType == PNG_COLOR_RGB) {
0882:                        node = new IIOMetadataNode("tRNS_RGB");
0883:                        node.setAttribute("red", Integer.toString(tRNS_red));
0884:                        node
0885:                                .setAttribute("green", Integer
0886:                                        .toString(tRNS_green));
0887:                        node.setAttribute("blue", Integer.toString(tRNS_blue));
0888:                    }
0889:                    tRNS_node.appendChild(node);
0890:
0891:                    root.appendChild(tRNS_node);
0892:                }
0893:
0894:                // zTXt
0895:                if (zTXt_keyword.size() > 0) {
0896:                    IIOMetadataNode zTXt_parent = new IIOMetadataNode("zTXt");
0897:                    for (int i = 0; i < zTXt_keyword.size(); i++) {
0898:                        IIOMetadataNode zTXt_node = new IIOMetadataNode(
0899:                                "zTXtEntry");
0900:                        zTXt_node.setAttribute("keyword", (String) zTXt_keyword
0901:                                .get(i));
0902:
0903:                        int cm = ((Integer) zTXt_compressionMethod.get(i))
0904:                                .intValue();
0905:                        zTXt_node.setAttribute("compressionMethod",
0906:                                zTXt_compressionMethodNames[cm]);
0907:
0908:                        zTXt_node.setAttribute("text", (String) zTXt_text
0909:                                .get(i));
0910:
0911:                        zTXt_parent.appendChild(zTXt_node);
0912:                    }
0913:
0914:                    root.appendChild(zTXt_parent);
0915:                }
0916:
0917:                // Unknown chunks
0918:                if (unknownChunkType.size() > 0) {
0919:                    IIOMetadataNode unknown_parent = new IIOMetadataNode(
0920:                            "UnknownChunks");
0921:                    for (int i = 0; i < unknownChunkType.size(); i++) {
0922:                        IIOMetadataNode unknown_node = new IIOMetadataNode(
0923:                                "UnknownChunk");
0924:                        unknown_node.setAttribute("type",
0925:                                (String) unknownChunkType.get(i));
0926:                        unknown_node.setUserObject((byte[]) unknownChunkData
0927:                                .get(i));
0928:
0929:                        unknown_parent.appendChild(unknown_node);
0930:                    }
0931:
0932:                    root.appendChild(unknown_parent);
0933:                }
0934:
0935:                return root;
0936:            }
0937:
0938:            private int getNumChannels() {
0939:                // Determine number of channels
0940:                // Be careful about palette color with transparency
0941:                int numChannels = IHDR_numChannels[IHDR_colorType];
0942:                if (IHDR_colorType == PNG_COLOR_PALETTE && tRNS_present
0943:                        && tRNS_colorType == IHDR_colorType) {
0944:                    numChannels = 4;
0945:                }
0946:                return numChannels;
0947:            }
0948:
0949:            public IIOMetadataNode getStandardChromaNode() {
0950:                IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
0951:                IIOMetadataNode node = null; // scratch node
0952:
0953:                node = new IIOMetadataNode("ColorSpaceType");
0954:                node.setAttribute("name", colorSpaceTypeNames[IHDR_colorType]);
0955:                chroma_node.appendChild(node);
0956:
0957:                node = new IIOMetadataNode("NumChannels");
0958:                node.setAttribute("value", Integer.toString(getNumChannels()));
0959:                chroma_node.appendChild(node);
0960:
0961:                if (gAMA_present) {
0962:                    node = new IIOMetadataNode("Gamma");
0963:                    node.setAttribute("value", Float
0964:                            .toString(gAMA_gamma * 1.0e-5F));
0965:                    chroma_node.appendChild(node);
0966:                }
0967:
0968:                node = new IIOMetadataNode("BlackIsZero");
0969:                node.setAttribute("value", "TRUE");
0970:                chroma_node.appendChild(node);
0971:
0972:                if (PLTE_present) {
0973:                    boolean hasAlpha = tRNS_present
0974:                            && (tRNS_colorType == PNG_COLOR_PALETTE);
0975:
0976:                    node = new IIOMetadataNode("Palette");
0977:                    for (int i = 0; i < PLTE_red.length; i++) {
0978:                        IIOMetadataNode entry = new IIOMetadataNode(
0979:                                "PaletteEntry");
0980:                        entry.setAttribute("index", Integer.toString(i));
0981:                        entry.setAttribute("red", Integer
0982:                                .toString(PLTE_red[i] & 0xff));
0983:                        entry.setAttribute("green", Integer
0984:                                .toString(PLTE_green[i] & 0xff));
0985:                        entry.setAttribute("blue", Integer
0986:                                .toString(PLTE_blue[i] & 0xff));
0987:                        if (hasAlpha) {
0988:                            int alpha = (i < tRNS_alpha.length) ? (tRNS_alpha[i] & 0xff)
0989:                                    : 255;
0990:                            entry
0991:                                    .setAttribute("alpha", Integer
0992:                                            .toString(alpha));
0993:                        }
0994:                        node.appendChild(entry);
0995:                    }
0996:                    chroma_node.appendChild(node);
0997:                }
0998:
0999:                if (bKGD_present) {
1000:                    if (bKGD_colorType == PNG_COLOR_PALETTE) {
1001:                        node = new IIOMetadataNode("BackgroundIndex");
1002:                        node
1003:                                .setAttribute("value", Integer
1004:                                        .toString(bKGD_index));
1005:                    } else {
1006:                        node = new IIOMetadataNode("BackgroundColor");
1007:                        int r, g, b;
1008:
1009:                        if (bKGD_colorType == PNG_COLOR_GRAY) {
1010:                            r = g = b = bKGD_gray;
1011:                        } else {
1012:                            r = bKGD_red;
1013:                            g = bKGD_green;
1014:                            b = bKGD_blue;
1015:                        }
1016:                        node.setAttribute("red", Integer.toString(r));
1017:                        node.setAttribute("green", Integer.toString(g));
1018:                        node.setAttribute("blue", Integer.toString(b));
1019:                    }
1020:                    chroma_node.appendChild(node);
1021:                }
1022:
1023:                return chroma_node;
1024:            }
1025:
1026:            public IIOMetadataNode getStandardCompressionNode() {
1027:                IIOMetadataNode compression_node = new IIOMetadataNode(
1028:                        "Compression");
1029:                IIOMetadataNode node = null; // scratch node
1030:
1031:                node = new IIOMetadataNode("CompressionTypeName");
1032:                node.setAttribute("value", "deflate");
1033:                compression_node.appendChild(node);
1034:
1035:                node = new IIOMetadataNode("Lossless");
1036:                node.setAttribute("value", "TRUE");
1037:                compression_node.appendChild(node);
1038:
1039:                node = new IIOMetadataNode("NumProgressiveScans");
1040:                node.setAttribute("value", (IHDR_interlaceMethod == 0) ? "1"
1041:                        : "7");
1042:                compression_node.appendChild(node);
1043:
1044:                return compression_node;
1045:            }
1046:
1047:            private String repeat(String s, int times) {
1048:                if (times == 1) {
1049:                    return s;
1050:                }
1051:                StringBuffer sb = new StringBuffer((s.length() + 1) * times - 1);
1052:                sb.append(s);
1053:                for (int i = 1; i < times; i++) {
1054:                    sb.append(" ");
1055:                    sb.append(s);
1056:                }
1057:                return sb.toString();
1058:            }
1059:
1060:            public IIOMetadataNode getStandardDataNode() {
1061:                IIOMetadataNode data_node = new IIOMetadataNode("Data");
1062:                IIOMetadataNode node = null; // scratch node
1063:
1064:                node = new IIOMetadataNode("PlanarConfiguration");
1065:                node.setAttribute("value", "PixelInterleaved");
1066:                data_node.appendChild(node);
1067:
1068:                node = new IIOMetadataNode("SampleFormat");
1069:                node.setAttribute("value",
1070:                        IHDR_colorType == PNG_COLOR_PALETTE ? "Index"
1071:                                : "UnsignedIntegral");
1072:                data_node.appendChild(node);
1073:
1074:                String bitDepth = Integer.toString(IHDR_bitDepth);
1075:                node = new IIOMetadataNode("BitsPerSample");
1076:                node.setAttribute("value", repeat(bitDepth, getNumChannels()));
1077:                data_node.appendChild(node);
1078:
1079:                if (sBIT_present) {
1080:                    node = new IIOMetadataNode("SignificantBitsPerSample");
1081:                    String sbits;
1082:                    if (sBIT_colorType == PNG_COLOR_GRAY
1083:                            || sBIT_colorType == PNG_COLOR_GRAY_ALPHA) {
1084:                        sbits = Integer.toString(sBIT_grayBits);
1085:                    } else { // sBIT_colorType == PNG_COLOR_RGB ||
1086:                        // sBIT_colorType == PNG_COLOR_RGB_ALPHA
1087:                        sbits = Integer.toString(sBIT_redBits) + " "
1088:                                + Integer.toString(sBIT_greenBits) + " "
1089:                                + Integer.toString(sBIT_blueBits);
1090:                    }
1091:
1092:                    if (sBIT_colorType == PNG_COLOR_GRAY_ALPHA
1093:                            || sBIT_colorType == PNG_COLOR_RGB_ALPHA) {
1094:                        sbits += " " + Integer.toString(sBIT_alphaBits);
1095:                    }
1096:
1097:                    node.setAttribute("value", sbits);
1098:                    data_node.appendChild(node);
1099:                }
1100:
1101:                // SampleMSB
1102:
1103:                return data_node;
1104:            }
1105:
1106:            public IIOMetadataNode getStandardDimensionNode() {
1107:                IIOMetadataNode dimension_node = new IIOMetadataNode(
1108:                        "Dimension");
1109:                IIOMetadataNode node = null; // scratch node
1110:
1111:                node = new IIOMetadataNode("PixelAspectRatio");
1112:                // aspect ratio is pixel width/height which is the ratio of the
1113:                // inverses of pixels per unit length.
1114:                float ratio = pHYs_present ? (float) pHYs_pixelsPerUnitYAxis
1115:                        / pHYs_pixelsPerUnitXAxis : 1.0F;
1116:                node.setAttribute("value", Float.toString(ratio));
1117:                dimension_node.appendChild(node);
1118:
1119:                node = new IIOMetadataNode("ImageOrientation");
1120:                node.setAttribute("value", "Normal");
1121:                dimension_node.appendChild(node);
1122:
1123:                if (pHYs_present && pHYs_unitSpecifier == PHYS_UNIT_METER) {
1124:                    node = new IIOMetadataNode("HorizontalPixelSize");
1125:                    node.setAttribute("value", Float
1126:                            .toString(1000.0F / pHYs_pixelsPerUnitXAxis));
1127:                    dimension_node.appendChild(node);
1128:
1129:                    node = new IIOMetadataNode("VerticalPixelSize");
1130:                    node.setAttribute("value", Float
1131:                            .toString(1000.0F / pHYs_pixelsPerUnitYAxis));
1132:                    dimension_node.appendChild(node);
1133:                }
1134:
1135:                return dimension_node;
1136:            }
1137:
1138:            public IIOMetadataNode getStandardDocumentNode() {
1139:                if (!tIME_present) {
1140:                    return null;
1141:                }
1142:
1143:                IIOMetadataNode document_node = new IIOMetadataNode("Document");
1144:                IIOMetadataNode node = null; // scratch node
1145:
1146:                node = new IIOMetadataNode("ImageModificationTime");
1147:                node.setAttribute("year", Integer.toString(tIME_year));
1148:                node.setAttribute("month", Integer.toString(tIME_month));
1149:                node.setAttribute("day", Integer.toString(tIME_day));
1150:                node.setAttribute("hour", Integer.toString(tIME_hour));
1151:                node.setAttribute("minute", Integer.toString(tIME_minute));
1152:                node.setAttribute("second", Integer.toString(tIME_second));
1153:                document_node.appendChild(node);
1154:
1155:                return document_node;
1156:            }
1157:
1158:            public IIOMetadataNode getStandardTextNode() {
1159:                int numEntries = tEXt_keyword.size() + iTXt_keyword.size()
1160:                        + zTXt_keyword.size();
1161:                if (numEntries == 0) {
1162:                    return null;
1163:                }
1164:
1165:                IIOMetadataNode text_node = new IIOMetadataNode("Text");
1166:                IIOMetadataNode node = null; // scratch node
1167:
1168:                for (int i = 0; i < tEXt_keyword.size(); i++) {
1169:                    node = new IIOMetadataNode("TextEntry");
1170:                    node.setAttribute("keyword", (String) tEXt_keyword.get(i));
1171:                    node.setAttribute("value", (String) tEXt_text.get(i));
1172:                    node.setAttribute("encoding", "ISO-8859-1");
1173:                    node.setAttribute("compression", "none");
1174:
1175:                    text_node.appendChild(node);
1176:                }
1177:
1178:                for (int i = 0; i < iTXt_keyword.size(); i++) {
1179:                    node = new IIOMetadataNode("TextEntry");
1180:                    node.setAttribute("keyword", (String) iTXt_keyword.get(i));
1181:                    node.setAttribute("value", (String) iTXt_text.get(i));
1182:                    node.setAttribute("language", (String) iTXt_languageTag
1183:                            .get(i));
1184:                    if (((Integer) iTXt_compressionFlag.get(i)).intValue() == 1) {
1185:                        node.setAttribute("compression", "deflate");
1186:                    } else {
1187:                        node.setAttribute("compression", "none");
1188:                    }
1189:
1190:                    text_node.appendChild(node);
1191:                }
1192:
1193:                for (int i = 0; i < zTXt_keyword.size(); i++) {
1194:                    node = new IIOMetadataNode("TextEntry");
1195:                    node.setAttribute("keyword", (String) zTXt_keyword.get(i));
1196:                    node.setAttribute("value", (String) zTXt_text.get(i));
1197:                    node.setAttribute("compression", "deflate");
1198:
1199:                    text_node.appendChild(node);
1200:                }
1201:
1202:                return text_node;
1203:            }
1204:
1205:            public IIOMetadataNode getStandardTransparencyNode() {
1206:                IIOMetadataNode transparency_node = new IIOMetadataNode(
1207:                        "Transparency");
1208:                IIOMetadataNode node = null; // scratch node
1209:
1210:                node = new IIOMetadataNode("Alpha");
1211:                boolean hasAlpha = (IHDR_colorType == PNG_COLOR_RGB_ALPHA)
1212:                        || (IHDR_colorType == PNG_COLOR_GRAY_ALPHA)
1213:                        || (IHDR_colorType == PNG_COLOR_PALETTE && tRNS_present
1214:                                && (tRNS_colorType == IHDR_colorType) && (tRNS_alpha != null));
1215:                node.setAttribute("value", hasAlpha ? "nonpremultiplied"
1216:                        : "none");
1217:                transparency_node.appendChild(node);
1218:
1219:                if (tRNS_present) {
1220:                    if (tRNS_colorType == PNG_COLOR_RGB
1221:                            || tRNS_colorType == PNG_COLOR_GRAY) {
1222:                        node = new IIOMetadataNode("TransparentColor");
1223:                        if (tRNS_colorType == PNG_COLOR_RGB) {
1224:                            node.setAttribute("value", Integer
1225:                                    .toString(tRNS_red)
1226:                                    + " "
1227:                                    + Integer.toString(tRNS_green)
1228:                                    + " "
1229:                                    + Integer.toString(tRNS_blue));
1230:                        } else if (tRNS_colorType == PNG_COLOR_GRAY) {
1231:                            node.setAttribute("value", Integer
1232:                                    .toString(tRNS_gray));
1233:                        }
1234:                        transparency_node.appendChild(node);
1235:                    }
1236:                }
1237:
1238:                return transparency_node;
1239:            }
1240:
1241:            // Shorthand for throwing an IIOInvalidTreeException
1242:            private void fatal(Node node, String reason)
1243:                    throws IIOInvalidTreeException {
1244:                throw new IIOInvalidTreeException(reason, node);
1245:            }
1246:
1247:            // Get an integer-valued attribute
1248:            private int getIntAttribute(Node node, String name,
1249:                    int defaultValue, boolean required)
1250:                    throws IIOInvalidTreeException {
1251:                String value = getAttribute(node, name, null, required);
1252:                if (value == null) {
1253:                    return defaultValue;
1254:                }
1255:                return Integer.parseInt(value);
1256:            }
1257:
1258:            // Get a float-valued attribute
1259:            private float getFloatAttribute(Node node, String name,
1260:                    float defaultValue, boolean required)
1261:                    throws IIOInvalidTreeException {
1262:                String value = getAttribute(node, name, null, required);
1263:                if (value == null) {
1264:                    return defaultValue;
1265:                }
1266:                return Float.parseFloat(value);
1267:            }
1268:
1269:            // Get a required integer-valued attribute
1270:            private int getIntAttribute(Node node, String name)
1271:                    throws IIOInvalidTreeException {
1272:                return getIntAttribute(node, name, -1, true);
1273:            }
1274:
1275:            // Get a required float-valued attribute
1276:            private float getFloatAttribute(Node node, String name)
1277:                    throws IIOInvalidTreeException {
1278:                return getFloatAttribute(node, name, -1.0F, true);
1279:            }
1280:
1281:            // Get a boolean-valued attribute
1282:            private boolean getBooleanAttribute(Node node, String name,
1283:                    boolean defaultValue, boolean required)
1284:                    throws IIOInvalidTreeException {
1285:                Node attr = node.getAttributes().getNamedItem(name);
1286:                if (attr == null) {
1287:                    if (!required) {
1288:                        return defaultValue;
1289:                    } else {
1290:                        fatal(node, "Required attribute " + name
1291:                                + " not present!");
1292:                    }
1293:                }
1294:
1295:                String value = attr.getNodeValue();
1296:
1297:                if (value.equalsIgnoreCase("true")) {
1298:                    return true;
1299:                } else if (value.equalsIgnoreCase("false")) {
1300:                    return false;
1301:                } else {
1302:                    fatal(node, "Attribute " + name
1303:                            + " must be 'true' or 'false'!");
1304:                    return false;
1305:                }
1306:            }
1307:
1308:            // Get a required boolean-valued attribute
1309:            private boolean getBooleanAttribute(Node node, String name)
1310:                    throws IIOInvalidTreeException {
1311:                return getBooleanAttribute(node, name, false, true);
1312:            }
1313:
1314:            // Get an enumerated attribute as an index into a String array
1315:            private int getEnumeratedAttribute(Node node, String name,
1316:                    String[] legalNames, int defaultValue, boolean required)
1317:                    throws IIOInvalidTreeException {
1318:                Node attr = node.getAttributes().getNamedItem(name);
1319:                if (attr == null) {
1320:                    if (!required) {
1321:                        return defaultValue;
1322:                    } else {
1323:                        fatal(node, "Required attribute " + name
1324:                                + " not present!");
1325:                    }
1326:                }
1327:
1328:                String value = attr.getNodeValue();
1329:
1330:                for (int i = 0; i < legalNames.length; i++) {
1331:                    if (value.equals(legalNames[i])) {
1332:                        return i;
1333:                    }
1334:                }
1335:
1336:                fatal(node, "Illegal value for attribute " + name + "!");
1337:                return -1;
1338:            }
1339:
1340:            // Get a required enumerated attribute as an index into a String array
1341:            private int getEnumeratedAttribute(Node node, String name,
1342:                    String[] legalNames) throws IIOInvalidTreeException {
1343:                return getEnumeratedAttribute(node, name, legalNames, -1, true);
1344:            }
1345:
1346:            // Get a String-valued attribute
1347:            private String getAttribute(Node node, String name,
1348:                    String defaultValue, boolean required)
1349:                    throws IIOInvalidTreeException {
1350:                Node attr = node.getAttributes().getNamedItem(name);
1351:                if (attr == null) {
1352:                    if (!required) {
1353:                        return defaultValue;
1354:                    } else {
1355:                        fatal(node, "Required attribute " + name
1356:                                + " not present!");
1357:                    }
1358:                }
1359:                return attr.getNodeValue();
1360:            }
1361:
1362:            // Get a required String-valued attribute
1363:            private String getAttribute(Node node, String name)
1364:                    throws IIOInvalidTreeException {
1365:                return getAttribute(node, name, null, true);
1366:            }
1367:
1368:            public void mergeTree(String formatName, Node root)
1369:                    throws IIOInvalidTreeException {
1370:                if (formatName.equals(nativeMetadataFormatName)) {
1371:                    if (root == null) {
1372:                        throw new IllegalArgumentException("root == null!");
1373:                    }
1374:                    mergeNativeTree(root);
1375:                } else if (formatName
1376:                        .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
1377:                    if (root == null) {
1378:                        throw new IllegalArgumentException("root == null!");
1379:                    }
1380:                    mergeStandardTree(root);
1381:                } else {
1382:                    throw new IllegalArgumentException(
1383:                            "Not a recognized format!");
1384:                }
1385:            }
1386:
1387:            private void mergeNativeTree(Node root)
1388:                    throws IIOInvalidTreeException {
1389:                Node node = root;
1390:                if (!node.getNodeName().equals(nativeMetadataFormatName)) {
1391:                    fatal(node, "Root must be " + nativeMetadataFormatName);
1392:                }
1393:
1394:                node = node.getFirstChild();
1395:                while (node != null) {
1396:                    String name = node.getNodeName();
1397:
1398:                    if (name.equals("IHDR")) {
1399:                        IHDR_width = getIntAttribute(node, "width");
1400:                        IHDR_height = getIntAttribute(node, "height");
1401:                        IHDR_bitDepth = getEnumeratedAttribute(node,
1402:                                "bitDepth", IHDR_bitDepths);
1403:                        IHDR_colorType = getEnumeratedAttribute(node,
1404:                                "colorType", IHDR_colorTypeNames);
1405:                        IHDR_compressionMethod = getEnumeratedAttribute(node,
1406:                                "compressionMethod",
1407:                                IHDR_compressionMethodNames);
1408:                        IHDR_filterMethod = getEnumeratedAttribute(node,
1409:                                "filterMethod", IHDR_filterMethodNames);
1410:                        IHDR_interlaceMethod = getEnumeratedAttribute(node,
1411:                                "interlaceMethod", IHDR_interlaceMethodNames);
1412:                        IHDR_present = true;
1413:                    } else if (name.equals("PLTE")) {
1414:                        byte[] red = new byte[256];
1415:                        byte[] green = new byte[256];
1416:                        byte[] blue = new byte[256];
1417:                        int maxindex = -1;
1418:
1419:                        Node PLTE_entry = node.getFirstChild();
1420:                        if (PLTE_entry == null) {
1421:                            fatal(node, "Palette has no entries!");
1422:                        }
1423:
1424:                        while (PLTE_entry != null) {
1425:                            if (!PLTE_entry.getNodeName().equals("PLTEEntry")) {
1426:                                fatal(node,
1427:                                        "Only a PLTEEntry may be a child of a PLTE!");
1428:                            }
1429:
1430:                            int index = getIntAttribute(PLTE_entry, "index");
1431:                            if (index < 0 || index > 255) {
1432:                                fatal(node,
1433:                                        "Bad value for PLTEEntry attribute index!");
1434:                            }
1435:                            if (index > maxindex) {
1436:                                maxindex = index;
1437:                            }
1438:                            red[index] = (byte) getIntAttribute(PLTE_entry,
1439:                                    "red");
1440:                            green[index] = (byte) getIntAttribute(PLTE_entry,
1441:                                    "green");
1442:                            blue[index] = (byte) getIntAttribute(PLTE_entry,
1443:                                    "blue");
1444:
1445:                            PLTE_entry = PLTE_entry.getNextSibling();
1446:                        }
1447:
1448:                        int numEntries = maxindex + 1;
1449:                        PLTE_red = new byte[numEntries];
1450:                        PLTE_green = new byte[numEntries];
1451:                        PLTE_blue = new byte[numEntries];
1452:                        System.arraycopy(red, 0, PLTE_red, 0, numEntries);
1453:                        System.arraycopy(green, 0, PLTE_green, 0, numEntries);
1454:                        System.arraycopy(blue, 0, PLTE_blue, 0, numEntries);
1455:                        PLTE_present = true;
1456:                    } else if (name.equals("bKGD")) {
1457:                        bKGD_present = false; // Guard against partial overwrite
1458:                        Node bKGD_node = node.getFirstChild();
1459:                        if (bKGD_node == null) {
1460:                            fatal(node, "bKGD node has no children!");
1461:                        }
1462:                        String bKGD_name = bKGD_node.getNodeName();
1463:                        if (bKGD_name.equals("bKGD_Palette")) {
1464:                            bKGD_index = getIntAttribute(bKGD_node, "index");
1465:                            bKGD_colorType = PNG_COLOR_PALETTE;
1466:                        } else if (bKGD_name.equals("bKGD_Grayscale")) {
1467:                            bKGD_gray = getIntAttribute(bKGD_node, "gray");
1468:                            bKGD_colorType = PNG_COLOR_GRAY;
1469:                        } else if (bKGD_name.equals("bKGD_RGB")) {
1470:                            bKGD_red = getIntAttribute(bKGD_node, "red");
1471:                            bKGD_green = getIntAttribute(bKGD_node, "green");
1472:                            bKGD_blue = getIntAttribute(bKGD_node, "blue");
1473:                            bKGD_colorType = PNG_COLOR_RGB;
1474:                        } else {
1475:                            fatal(node, "Bad child of a bKGD node!");
1476:                        }
1477:                        if (bKGD_node.getNextSibling() != null) {
1478:                            fatal(node, "bKGD node has more than one child!");
1479:                        }
1480:
1481:                        bKGD_present = true;
1482:                    } else if (name.equals("cHRM")) {
1483:                        cHRM_whitePointX = getIntAttribute(node, "whitePointX");
1484:                        cHRM_whitePointY = getIntAttribute(node, "whitePointY");
1485:                        cHRM_redX = getIntAttribute(node, "redX");
1486:                        cHRM_redY = getIntAttribute(node, "redY");
1487:                        cHRM_greenX = getIntAttribute(node, "greenX");
1488:                        cHRM_greenY = getIntAttribute(node, "greenY");
1489:                        cHRM_blueX = getIntAttribute(node, "blueX");
1490:                        cHRM_blueY = getIntAttribute(node, "blueY");
1491:
1492:                        cHRM_present = true;
1493:                    } else if (name.equals("gAMA")) {
1494:                        gAMA_gamma = getIntAttribute(node, "value");
1495:                        gAMA_present = true;
1496:                    } else if (name.equals("hIST")) {
1497:                        char[] hist = new char[256];
1498:                        int maxindex = -1;
1499:
1500:                        Node hIST_entry = node.getFirstChild();
1501:                        if (hIST_entry == null) {
1502:                            fatal(node, "hIST node has no children!");
1503:                        }
1504:
1505:                        while (hIST_entry != null) {
1506:                            if (!hIST_entry.getNodeName().equals("hISTEntry")) {
1507:                                fatal(node,
1508:                                        "Only a hISTEntry may be a child of a hIST!");
1509:                            }
1510:
1511:                            int index = getIntAttribute(hIST_entry, "index");
1512:                            if (index < 0 || index > 255) {
1513:                                fatal(node,
1514:                                        "Bad value for histEntry attribute index!");
1515:                            }
1516:                            if (index > maxindex) {
1517:                                maxindex = index;
1518:                            }
1519:                            hist[index] = (char) getIntAttribute(hIST_entry,
1520:                                    "value");
1521:
1522:                            hIST_entry = hIST_entry.getNextSibling();
1523:                        }
1524:
1525:                        int numEntries = maxindex + 1;
1526:                        hIST_histogram = new char[numEntries];
1527:                        System
1528:                                .arraycopy(hist, 0, hIST_histogram, 0,
1529:                                        numEntries);
1530:
1531:                        hIST_present = true;
1532:                    } else if (name.equals("iCCP")) {
1533:                        iCCP_profileName = toPrintableLatin1(getAttribute(node,
1534:                                "profileName"));
1535:                        iCCP_compressionMethod = getEnumeratedAttribute(node,
1536:                                "compressionMethod",
1537:                                iCCP_compressionMethodNames);
1538:                        Object compressedProfile = ((IIOMetadataNode) node)
1539:                                .getUserObject();
1540:                        if (compressedProfile == null) {
1541:                            fatal(node,
1542:                                    "No ICCP profile present in user object!");
1543:                        }
1544:                        if (!(compressedProfile instanceof  byte[])) {
1545:                            fatal(node, "User object not a byte array!");
1546:                        }
1547:
1548:                        iCCP_compressedProfile = (byte[]) ((byte[]) compressedProfile)
1549:                                .clone();
1550:
1551:                        iCCP_present = true;
1552:                    } else if (name.equals("iTXt")) {
1553:                        Node iTXt_node = node.getFirstChild();
1554:                        while (iTXt_node != null) {
1555:                            if (!iTXt_node.getNodeName().equals("iTXtEntry")) {
1556:                                fatal(node,
1557:                                        "Only an iTXtEntry may be a child of an iTXt!");
1558:                            }
1559:
1560:                            String keyword = toPrintableLatin1(getAttribute(
1561:                                    iTXt_node, "keyword"));
1562:                            iTXt_keyword.add(keyword);
1563:
1564:                            boolean compressionFlag = getBooleanAttribute(
1565:                                    iTXt_node, "compressionFlag");
1566:                            iTXt_compressionFlag.add(new Boolean(
1567:                                    compressionFlag));
1568:
1569:                            String compressionMethod = getAttribute(iTXt_node,
1570:                                    "compressionMethod");
1571:                            iTXt_compressionMethod.add(compressionMethod);
1572:
1573:                            String languageTag = getAttribute(iTXt_node,
1574:                                    "languageTag");
1575:                            iTXt_languageTag.add(languageTag);
1576:
1577:                            String translatedKeyword = getAttribute(iTXt_node,
1578:                                    "translatedKeyword");
1579:                            iTXt_translatedKeyword.add(translatedKeyword);
1580:
1581:                            String text = getAttribute(iTXt_node, "text");
1582:                            iTXt_text.add(text);
1583:
1584:                            iTXt_node = iTXt_node.getNextSibling();
1585:                        }
1586:                    } else if (name.equals("pHYs")) {
1587:                        pHYs_pixelsPerUnitXAxis = getIntAttribute(node,
1588:                                "pixelsPerUnitXAxis");
1589:                        pHYs_pixelsPerUnitYAxis = getIntAttribute(node,
1590:                                "pixelsPerUnitYAxis");
1591:                        pHYs_unitSpecifier = getEnumeratedAttribute(node,
1592:                                "unitSpecifier", unitSpecifierNames);
1593:
1594:                        pHYs_present = true;
1595:                    } else if (name.equals("sBIT")) {
1596:                        sBIT_present = false; // Guard against partial overwrite
1597:                        Node sBIT_node = node.getFirstChild();
1598:                        if (sBIT_node == null) {
1599:                            fatal(node, "sBIT node has no children!");
1600:                        }
1601:                        String sBIT_name = sBIT_node.getNodeName();
1602:                        if (sBIT_name.equals("sBIT_Grayscale")) {
1603:                            sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
1604:                            sBIT_colorType = PNG_COLOR_GRAY;
1605:                        } else if (sBIT_name.equals("sBIT_GrayAlpha")) {
1606:                            sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
1607:                            sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
1608:                            sBIT_colorType = PNG_COLOR_GRAY_ALPHA;
1609:                        } else if (sBIT_name.equals("sBIT_RGB")) {
1610:                            sBIT_redBits = getIntAttribute(sBIT_node, "red");
1611:                            sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1612:                            sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1613:                            sBIT_colorType = PNG_COLOR_RGB;
1614:                        } else if (sBIT_name.equals("sBIT_RGBAlpha")) {
1615:                            sBIT_redBits = getIntAttribute(sBIT_node, "red");
1616:                            sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1617:                            sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1618:                            sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
1619:                            sBIT_colorType = PNG_COLOR_RGB_ALPHA;
1620:                        } else if (sBIT_name.equals("sBIT_Palette")) {
1621:                            sBIT_redBits = getIntAttribute(sBIT_node, "red");
1622:                            sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1623:                            sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1624:                            sBIT_colorType = PNG_COLOR_PALETTE;
1625:                        } else {
1626:                            fatal(node, "Bad child of an sBIT node!");
1627:                        }
1628:                        if (sBIT_node.getNextSibling() != null) {
1629:                            fatal(node, "sBIT node has more than one child!");
1630:                        }
1631:
1632:                        sBIT_present = true;
1633:                    } else if (name.equals("sPLT")) {
1634:                        sPLT_paletteName = toPrintableLatin1(getAttribute(node,
1635:                                "name"));
1636:                        sPLT_sampleDepth = getIntAttribute(node, "sampleDepth");
1637:
1638:                        int[] red = new int[256];
1639:                        int[] green = new int[256];
1640:                        int[] blue = new int[256];
1641:                        int[] alpha = new int[256];
1642:                        int[] frequency = new int[256];
1643:                        int maxindex = -1;
1644:
1645:                        Node sPLT_entry = node.getFirstChild();
1646:                        if (sPLT_entry == null) {
1647:                            fatal(node, "sPLT node has no children!");
1648:                        }
1649:
1650:                        while (sPLT_entry != null) {
1651:                            if (!sPLT_entry.getNodeName().equals("sPLTEntry")) {
1652:                                fatal(node,
1653:                                        "Only an sPLTEntry may be a child of an sPLT!");
1654:                            }
1655:
1656:                            int index = getIntAttribute(sPLT_entry, "index");
1657:                            if (index < 0 || index > 255) {
1658:                                fatal(node,
1659:                                        "Bad value for PLTEEntry attribute index!");
1660:                            }
1661:                            if (index > maxindex) {
1662:                                maxindex = index;
1663:                            }
1664:                            red[index] = getIntAttribute(sPLT_entry, "red");
1665:                            green[index] = getIntAttribute(sPLT_entry, "green");
1666:                            blue[index] = getIntAttribute(sPLT_entry, "blue");
1667:                            alpha[index] = getIntAttribute(sPLT_entry, "alpha");
1668:                            frequency[index] = getIntAttribute(sPLT_entry,
1669:                                    "frequency");
1670:
1671:                            sPLT_entry = sPLT_entry.getNextSibling();
1672:                        }
1673:
1674:                        int numEntries = maxindex + 1;
1675:                        sPLT_red = new int[numEntries];
1676:                        sPLT_green = new int[numEntries];
1677:                        sPLT_blue = new int[numEntries];
1678:                        sPLT_alpha = new int[numEntries];
1679:                        sPLT_frequency = new int[numEntries];
1680:                        System.arraycopy(red, 0, sPLT_red, 0, numEntries);
1681:                        System.arraycopy(green, 0, sPLT_green, 0, numEntries);
1682:                        System.arraycopy(blue, 0, sPLT_blue, 0, numEntries);
1683:                        System.arraycopy(alpha, 0, sPLT_alpha, 0, numEntries);
1684:                        System.arraycopy(frequency, 0, sPLT_frequency, 0,
1685:                                numEntries);
1686:
1687:                        sPLT_present = true;
1688:                    } else if (name.equals("sRGB")) {
1689:                        sRGB_renderingIntent = getEnumeratedAttribute(node,
1690:                                "renderingIntent", renderingIntentNames);
1691:
1692:                        sRGB_present = true;
1693:                    } else if (name.equals("tEXt")) {
1694:                        Node tEXt_node = node.getFirstChild();
1695:                        while (tEXt_node != null) {
1696:                            if (!tEXt_node.getNodeName().equals("tEXtEntry")) {
1697:                                fatal(node,
1698:                                        "Only an tEXtEntry may be a child of an tEXt!");
1699:                            }
1700:
1701:                            String keyword = toPrintableLatin1(getAttribute(
1702:                                    tEXt_node, "keyword"));
1703:                            tEXt_keyword.add(keyword);
1704:
1705:                            String text = getAttribute(tEXt_node, "value");
1706:                            tEXt_text.add(text);
1707:
1708:                            tEXt_node = tEXt_node.getNextSibling();
1709:                        }
1710:                    } else if (name.equals("tIME")) {
1711:                        tIME_year = getIntAttribute(node, "year");
1712:                        tIME_month = getIntAttribute(node, "month");
1713:                        tIME_day = getIntAttribute(node, "day");
1714:                        tIME_hour = getIntAttribute(node, "hour");
1715:                        tIME_minute = getIntAttribute(node, "minute");
1716:                        tIME_second = getIntAttribute(node, "second");
1717:
1718:                        tIME_present = true;
1719:                    } else if (name.equals("tRNS")) {
1720:                        tRNS_present = false; // Guard against partial overwrite
1721:                        Node tRNS_node = node.getFirstChild();
1722:                        if (tRNS_node == null) {
1723:                            fatal(node, "tRNS node has no children!");
1724:                        }
1725:                        String tRNS_name = tRNS_node.getNodeName();
1726:                        if (tRNS_name.equals("tRNS_Palette")) {
1727:                            byte[] alpha = new byte[256];
1728:                            int maxindex = -1;
1729:
1730:                            Node tRNS_paletteEntry = tRNS_node.getFirstChild();
1731:                            if (tRNS_paletteEntry == null) {
1732:                                fatal(node,
1733:                                        "tRNS_Palette node has no children!");
1734:                            }
1735:                            while (tRNS_paletteEntry != null) {
1736:                                if (!tRNS_paletteEntry.getNodeName().equals(
1737:                                        "tRNS_PaletteEntry")) {
1738:                                    fatal(node,
1739:                                            "Only a tRNS_PaletteEntry may be a child of a tRNS_Palette!");
1740:                                }
1741:                                int index = getIntAttribute(tRNS_paletteEntry,
1742:                                        "index");
1743:                                if (index < 0 || index > 255) {
1744:                                    fatal(node,
1745:                                            "Bad value for tRNS_PaletteEntry attribute index!");
1746:                                }
1747:                                if (index > maxindex) {
1748:                                    maxindex = index;
1749:                                }
1750:                                alpha[index] = (byte) getIntAttribute(
1751:                                        tRNS_paletteEntry, "alpha");
1752:
1753:                                tRNS_paletteEntry = tRNS_paletteEntry
1754:                                        .getNextSibling();
1755:                            }
1756:
1757:                            int numEntries = maxindex + 1;
1758:                            tRNS_alpha = new byte[numEntries];
1759:                            tRNS_colorType = PNG_COLOR_PALETTE;
1760:                            System.arraycopy(alpha, 0, tRNS_alpha, 0,
1761:                                    numEntries);
1762:                        } else if (tRNS_name.equals("tRNS_Grayscale")) {
1763:                            tRNS_gray = getIntAttribute(tRNS_node, "gray");
1764:                            tRNS_colorType = PNG_COLOR_GRAY;
1765:                        } else if (tRNS_name.equals("tRNS_RGB")) {
1766:                            tRNS_red = getIntAttribute(tRNS_node, "red");
1767:                            tRNS_green = getIntAttribute(tRNS_node, "green");
1768:                            tRNS_blue = getIntAttribute(tRNS_node, "blue");
1769:                            tRNS_colorType = PNG_COLOR_RGB;
1770:                        } else {
1771:                            fatal(node, "Bad child of a tRNS node!");
1772:                        }
1773:                        if (tRNS_node.getNextSibling() != null) {
1774:                            fatal(node, "tRNS node has more than one child!");
1775:                        }
1776:
1777:                        tRNS_present = true;
1778:                    } else if (name.equals("zTXt")) {
1779:                        Node zTXt_node = node.getFirstChild();
1780:                        while (zTXt_node != null) {
1781:                            if (!zTXt_node.getNodeName().equals("zTXtEntry")) {
1782:                                fatal(node,
1783:                                        "Only an zTXtEntry may be a child of an zTXt!");
1784:                            }
1785:
1786:                            String keyword = toPrintableLatin1(getAttribute(
1787:                                    zTXt_node, "keyword"));
1788:                            zTXt_keyword.add(keyword);
1789:
1790:                            int compressionMethod = getEnumeratedAttribute(
1791:                                    zTXt_node, "compressionMethod",
1792:                                    zTXt_compressionMethodNames);
1793:                            zTXt_compressionMethod.add(new Integer(
1794:                                    compressionMethod));
1795:
1796:                            String text = getAttribute(zTXt_node, "text");
1797:                            zTXt_text.add(text);
1798:
1799:                            zTXt_node = zTXt_node.getNextSibling();
1800:                        }
1801:                    } else if (name.equals("UnknownChunks")) {
1802:                        Node unknown_node = node.getFirstChild();
1803:                        while (unknown_node != null) {
1804:                            if (!unknown_node.getNodeName().equals(
1805:                                    "UnknownChunk")) {
1806:                                fatal(node,
1807:                                        "Only an UnknownChunk may be a child of an UnknownChunks!");
1808:                            }
1809:                            String chunkType = getAttribute(unknown_node,
1810:                                    "type");
1811:                            Object chunkData = ((IIOMetadataNode) unknown_node)
1812:                                    .getUserObject();
1813:
1814:                            if (chunkType.length() != 4) {
1815:                                fatal(unknown_node,
1816:                                        "Chunk type must be 4 characters!");
1817:                            }
1818:                            if (chunkData == null) {
1819:                                fatal(unknown_node,
1820:                                        "No chunk data present in user object!");
1821:                            }
1822:                            if (!(chunkData instanceof  byte[])) {
1823:                                fatal(unknown_node,
1824:                                        "User object not a byte array!");
1825:                            }
1826:                            unknownChunkType.add(chunkType);
1827:                            unknownChunkData.add(((byte[]) chunkData).clone());
1828:
1829:                            unknown_node = unknown_node.getNextSibling();
1830:                        }
1831:                    } else {
1832:                        fatal(node, "Unknown child of root node!");
1833:                    }
1834:
1835:                    node = node.getNextSibling();
1836:                }
1837:            }
1838:
1839:            private boolean isISOLatin(String s) {
1840:                int len = s.length();
1841:                for (int i = 0; i < len; i++) {
1842:                    if (s.charAt(i) > 255) {
1843:                        return false;
1844:                    }
1845:                }
1846:                return true;
1847:            }
1848:
1849:            private void mergeStandardTree(Node root)
1850:                    throws IIOInvalidTreeException {
1851:                Node node = root;
1852:                if (!node.getNodeName().equals(
1853:                        IIOMetadataFormatImpl.standardMetadataFormatName)) {
1854:                    fatal(node, "Root must be "
1855:                            + IIOMetadataFormatImpl.standardMetadataFormatName);
1856:                }
1857:
1858:                node = node.getFirstChild();
1859:                while (node != null) {
1860:                    String name = node.getNodeName();
1861:
1862:                    if (name.equals("Chroma")) {
1863:                        Node child = node.getFirstChild();
1864:                        while (child != null) {
1865:                            String childName = child.getNodeName();
1866:                            if (childName.equals("Gamma")) {
1867:                                float gamma = getFloatAttribute(child, "value");
1868:                                gAMA_present = true;
1869:                                gAMA_gamma = (int) (gamma * 100000 + 0.5);
1870:                            } else if (childName.equals("Palette")) {
1871:                                byte[] red = new byte[256];
1872:                                byte[] green = new byte[256];
1873:                                byte[] blue = new byte[256];
1874:                                int maxindex = -1;
1875:
1876:                                Node entry = child.getFirstChild();
1877:                                while (entry != null) {
1878:                                    String entryName = entry.getNodeName();
1879:                                    if (entryName.equals("PaletteEntry")) {
1880:                                        int index = getIntAttribute(entry,
1881:                                                "index");
1882:                                        if (index >= 0 && index <= 255) {
1883:                                            red[index] = (byte) getIntAttribute(
1884:                                                    entry, "red");
1885:                                            green[index] = (byte) getIntAttribute(
1886:                                                    entry, "green");
1887:                                            blue[index] = (byte) getIntAttribute(
1888:                                                    entry, "blue");
1889:                                            if (index > maxindex) {
1890:                                                maxindex = index;
1891:                                            }
1892:                                        }
1893:                                    }
1894:                                    entry = entry.getNextSibling();
1895:                                }
1896:
1897:                                int numEntries = maxindex + 1;
1898:                                PLTE_red = new byte[numEntries];
1899:                                PLTE_green = new byte[numEntries];
1900:                                PLTE_blue = new byte[numEntries];
1901:                                System.arraycopy(red, 0, PLTE_red, 0,
1902:                                        numEntries);
1903:                                System.arraycopy(green, 0, PLTE_green, 0,
1904:                                        numEntries);
1905:                                System.arraycopy(blue, 0, PLTE_blue, 0,
1906:                                        numEntries);
1907:                                PLTE_present = true;
1908:                            } else if (childName.equals("BackgroundIndex")) {
1909:                                bKGD_present = true;
1910:                                bKGD_colorType = PNG_COLOR_PALETTE;
1911:                                bKGD_index = getIntAttribute(child, "value");
1912:                            } else if (childName.equals("BackgroundColor")) {
1913:                                int red = getIntAttribute(child, "red");
1914:                                int green = getIntAttribute(child, "green");
1915:                                int blue = getIntAttribute(child, "blue");
1916:                                if (red == green && red == blue) {
1917:                                    bKGD_colorType = PNG_COLOR_GRAY;
1918:                                    bKGD_gray = red;
1919:                                } else {
1920:                                    bKGD_colorType = PNG_COLOR_RGB;
1921:                                    bKGD_red = red;
1922:                                    bKGD_green = green;
1923:                                    bKGD_blue = blue;
1924:                                }
1925:                                bKGD_present = true;
1926:                            }
1927:                            // } else if (childName.equals("ColorSpaceType")) {
1928:                            // } else if (childName.equals("NumChannels")) {
1929:
1930:                            child = child.getNextSibling();
1931:                        }
1932:                    } else if (name.equals("Compression")) {
1933:                        Node child = node.getFirstChild();
1934:                        while (child != null) {
1935:                            String childName = child.getNodeName();
1936:                            if (childName.equals("NumProgressiveScans")) {
1937:                                // Use Adam7 if NumProgressiveScans > 1
1938:                                int scans = getIntAttribute(child, "value");
1939:                                IHDR_interlaceMethod = (scans > 1) ? 1 : 0;
1940:                                //                  } else if (childName.equals("CompressionTypeName")) {
1941:                                //                  } else if (childName.equals("Lossless")) {
1942:                                //                  } else if (childName.equals("BitRate")) {
1943:                            }
1944:                            child = child.getNextSibling();
1945:                        }
1946:                    } else if (name.equals("Data")) {
1947:                        Node child = node.getFirstChild();
1948:                        while (child != null) {
1949:                            String childName = child.getNodeName();
1950:                            if (childName.equals("BitsPerSample")) {
1951:                                String s = getAttribute(child, "value");
1952:                                StringTokenizer t = new StringTokenizer(s);
1953:                                int maxBits = -1;
1954:                                while (t.hasMoreTokens()) {
1955:                                    int bits = Integer.parseInt(t.nextToken());
1956:                                    if (bits > maxBits) {
1957:                                        maxBits = bits;
1958:                                    }
1959:                                }
1960:                                if (maxBits < 1) {
1961:                                    maxBits = 1;
1962:                                } else if (maxBits == 3) {
1963:                                    maxBits = 4;
1964:                                } else if (maxBits > 4 && maxBits < 8) {
1965:                                    maxBits = 8;
1966:                                } else if (maxBits > 8) {
1967:                                    maxBits = 16;
1968:                                }
1969:                                IHDR_bitDepth = maxBits;
1970:                            } else if (childName
1971:                                    .equals("SignificantBitsPerSample")) {
1972:                                String s = getAttribute(child, "value");
1973:                                StringTokenizer t = new StringTokenizer(s);
1974:                                int numTokens = t.countTokens();
1975:                                if (numTokens == 1) {
1976:                                    sBIT_colorType = PNG_COLOR_GRAY;
1977:                                    sBIT_grayBits = Integer.parseInt(t
1978:                                            .nextToken());
1979:                                } else if (numTokens == 2) {
1980:                                    sBIT_colorType = PNG_COLOR_GRAY_ALPHA;
1981:                                    sBIT_grayBits = Integer.parseInt(t
1982:                                            .nextToken());
1983:                                    sBIT_alphaBits = Integer.parseInt(t
1984:                                            .nextToken());
1985:                                } else if (numTokens == 3) {
1986:                                    sBIT_colorType = PNG_COLOR_RGB;
1987:                                    sBIT_redBits = Integer.parseInt(t
1988:                                            .nextToken());
1989:                                    sBIT_greenBits = Integer.parseInt(t
1990:                                            .nextToken());
1991:                                    sBIT_blueBits = Integer.parseInt(t
1992:                                            .nextToken());
1993:                                } else if (numTokens == 4) {
1994:                                    sBIT_colorType = PNG_COLOR_RGB_ALPHA;
1995:                                    sBIT_redBits = Integer.parseInt(t
1996:                                            .nextToken());
1997:                                    sBIT_greenBits = Integer.parseInt(t
1998:                                            .nextToken());
1999:                                    sBIT_blueBits = Integer.parseInt(t
2000:                                            .nextToken());
2001:                                    sBIT_alphaBits = Integer.parseInt(t
2002:                                            .nextToken());
2003:                                }
2004:                                if (numTokens >= 1 && numTokens <= 4) {
2005:                                    sBIT_present = true;
2006:                                }
2007:                                // } else if (childName.equals("PlanarConfiguration")) {
2008:                                // } else if (childName.equals("SampleFormat")) {
2009:                                // } else if (childName.equals("SampleMSB")) {
2010:                            }
2011:                            child = child.getNextSibling();
2012:                        }
2013:                    } else if (name.equals("Dimension")) {
2014:                        boolean gotWidth = false;
2015:                        boolean gotHeight = false;
2016:                        boolean gotAspectRatio = false;
2017:
2018:                        float width = -1.0F;
2019:                        float height = -1.0F;
2020:                        float aspectRatio = -1.0F;
2021:
2022:                        Node child = node.getFirstChild();
2023:                        while (child != null) {
2024:                            String childName = child.getNodeName();
2025:                            if (childName.equals("PixelAspectRatio")) {
2026:                                aspectRatio = getFloatAttribute(child, "value");
2027:                                gotAspectRatio = true;
2028:                            } else if (childName.equals("HorizontalPixelSize")) {
2029:                                width = getFloatAttribute(child, "value");
2030:                                gotWidth = true;
2031:                            } else if (childName.equals("VerticalPixelSize")) {
2032:                                height = getFloatAttribute(child, "value");
2033:                                gotHeight = true;
2034:                                // } else if (childName.equals("ImageOrientation")) {
2035:                                // } else if
2036:                                //     (childName.equals("HorizontalPhysicalPixelSpacing")) {
2037:                                // } else if
2038:                                //     (childName.equals("VerticalPhysicalPixelSpacing")) {
2039:                                // } else if (childName.equals("HorizontalPosition")) {
2040:                                // } else if (childName.equals("VerticalPosition")) {
2041:                                // } else if (childName.equals("HorizontalPixelOffset")) {
2042:                                // } else if (childName.equals("VerticalPixelOffset")) {
2043:                            }
2044:                            child = child.getNextSibling();
2045:                        }
2046:
2047:                        if (gotWidth && gotHeight) {
2048:                            pHYs_present = true;
2049:                            pHYs_unitSpecifier = 1;
2050:                            pHYs_pixelsPerUnitXAxis = (int) (1000.0F / width + 0.5F);
2051:                            pHYs_pixelsPerUnitYAxis = (int) (1000.0F / height + 0.5F);
2052:                        } else if (gotAspectRatio) {
2053:                            pHYs_present = true;
2054:                            pHYs_unitSpecifier = 0;
2055:
2056:                            // Find a reasonable rational approximation
2057:                            int denom = 1;
2058:                            for (; denom < 100; denom++) {
2059:                                int num = (int) (aspectRatio * denom);
2060:                                if (Math.abs(num / denom - aspectRatio) < 0.001) {
2061:                                    break;
2062:                                }
2063:                            }
2064:                            pHYs_pixelsPerUnitXAxis = (int) (aspectRatio * denom);
2065:                            pHYs_pixelsPerUnitYAxis = denom;
2066:                        }
2067:                    } else if (name.equals("Document")) {
2068:                        Node child = node.getFirstChild();
2069:                        while (child != null) {
2070:                            String childName = child.getNodeName();
2071:                            if (childName.equals("ImageModificationTime")) {
2072:                                tIME_present = true;
2073:                                tIME_year = getIntAttribute(child, "year");
2074:                                tIME_month = getIntAttribute(child, "month");
2075:                                tIME_day = getIntAttribute(child, "day");
2076:                                tIME_hour = getIntAttribute(child, "hour", 0,
2077:                                        false);
2078:                                tIME_minute = getIntAttribute(child, "minute",
2079:                                        0, false);
2080:                                tIME_second = getIntAttribute(child, "second",
2081:                                        0, false);
2082:                                // } else if (childName.equals("SubimageInterpretation")) {
2083:                                // } else if (childName.equals("ImageCreationTime")) {
2084:                            }
2085:                            child = child.getNextSibling();
2086:                        }
2087:                    } else if (name.equals("Text")) {
2088:                        Node child = node.getFirstChild();
2089:                        while (child != null) {
2090:                            String childName = child.getNodeName();
2091:                            if (childName.equals("TextEntry")) {
2092:                                String keyword = getAttribute(child, "keyword",
2093:                                        "text", false);
2094:                                String value = getAttribute(child, "value");
2095:                                String encoding = getAttribute(child,
2096:                                        "encoding", "unknown", false);
2097:                                String language = getAttribute(child,
2098:                                        "language", "unknown", false);
2099:                                String compression = getAttribute(child,
2100:                                        "compression", "other", false);
2101:
2102:                                if (isISOLatin(value)) {
2103:                                    if (compression.equals("zip")) {
2104:                                        // Use a zTXt node
2105:                                        zTXt_keyword
2106:                                                .add(toPrintableLatin1(keyword));
2107:                                        zTXt_text.add(value);
2108:                                        zTXt_compressionMethod.add(new Integer(
2109:                                                0));
2110:                                    } else {
2111:                                        // Use a tEXt node
2112:                                        tEXt_keyword
2113:                                                .add(toPrintableLatin1(keyword));
2114:                                        tEXt_text.add(value);
2115:                                    }
2116:                                } else {
2117:                                    int flag = compression.equals("zip") ? 1
2118:                                            : 0;
2119:
2120:                                    // Use an iTXt node
2121:                                    iTXt_keyword
2122:                                            .add(toPrintableLatin1(keyword));
2123:                                    iTXt_compressionFlag.add(new Integer(flag));
2124:                                    iTXt_compressionMethod.add(new Integer(0));
2125:                                    iTXt_languageTag.add(language);
2126:                                    iTXt_translatedKeyword.add(keyword); // fake it
2127:                                    iTXt_text.add(value);
2128:                                }
2129:                            }
2130:                            child = child.getNextSibling();
2131:                        }
2132:                        //          } else if (name.equals("Transparency")) {
2133:                        //              Node child = node.getFirstChild();
2134:                        //              while (child != null) {
2135:                        //                  String childName = child.getNodeName();
2136:                        //                  if (childName.equals("Alpha")) {
2137:                        //                  } else if (childName.equals("TransparentIndex")) {
2138:                        //                  } else if (childName.equals("TransparentColor")) {
2139:                        //                  } else if (childName.equals("TileTransparencies")) {
2140:                        //                  } else if (childName.equals("TileOpacities")) {
2141:                        //                  }
2142:                        //                  child = child.getNextSibling();
2143:                        //              }
2144:                        //          } else {
2145:                        //              // fatal(node, "Unknown child of root node!");
2146:                    }
2147:
2148:                    node = node.getNextSibling();
2149:                }
2150:            }
2151:
2152:            // Reset all instance variables to their initial state
2153:            public void reset() {
2154:                IHDR_present = false;
2155:                PLTE_present = false;
2156:                bKGD_present = false;
2157:                cHRM_present = false;
2158:                gAMA_present = false;
2159:                hIST_present = false;
2160:                iCCP_present = false;
2161:                iTXt_keyword = new ArrayList();
2162:                iTXt_compressionFlag = new ArrayList();
2163:                iTXt_compressionMethod = new ArrayList();
2164:                iTXt_languageTag = new ArrayList();
2165:                iTXt_translatedKeyword = new ArrayList();
2166:                iTXt_text = new ArrayList();
2167:                pHYs_present = false;
2168:                sBIT_present = false;
2169:                sPLT_present = false;
2170:                sRGB_present = false;
2171:                tEXt_keyword = new ArrayList();
2172:                tEXt_text = new ArrayList();
2173:                tIME_present = false;
2174:                tRNS_present = false;
2175:                zTXt_keyword = new ArrayList();
2176:                zTXt_compressionMethod = new ArrayList();
2177:                zTXt_text = new ArrayList();
2178:                unknownChunkType = new ArrayList();
2179:                unknownChunkData = new ArrayList();
2180:            }
2181:
2182:            // BEGIN metadata reading section.
2183:
2184:            private boolean gotHeader = false;
2185:            private boolean gotMetadata = false;
2186:
2187:            private Decoder decoder = null;
2188:            private CLibPNGImageReader reader = null;
2189:
2190:            private static int chunkType(String typeString) {
2191:                char c0 = typeString.charAt(0);
2192:                char c1 = typeString.charAt(1);
2193:                char c2 = typeString.charAt(2);
2194:                char c3 = typeString.charAt(3);
2195:
2196:                int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
2197:                return type;
2198:            }
2199:
2200:            private String readNullTerminatedString(ImageInputStream stream)
2201:                    throws IOException {
2202:                StringBuffer b = new StringBuffer();
2203:                int c;
2204:
2205:                while ((c = stream.read()) != 0) {
2206:                    b.append((char) c);
2207:                }
2208:                return b.toString();
2209:            }
2210:
2211:            private void readHeader() throws IIOException {
2212:                if (gotHeader) {
2213:                    return;
2214:                }
2215:
2216:                try {
2217:                    mediaLibImage mlibImage = decoder.getImage();
2218:                    int width = mlibImage.getWidth();
2219:                    int height = mlibImage.getHeight();
2220:                    int bitDepth = decoder.getBitDepth();
2221:                    int colorType;
2222:                    switch (mlibImage.getChannels()) {
2223:                    case 1:
2224:                        colorType = decoder.getPalette() == null ? PNG_COLOR_GRAY
2225:                                : PNG_COLOR_PALETTE;
2226:                        break;
2227:                    case 2:
2228:                        colorType = PNG_COLOR_GRAY_ALPHA;
2229:                        break;
2230:                    case 3:
2231:                        colorType = PNG_COLOR_RGB;
2232:                        break;
2233:                    case 4:
2234:                        colorType = PNG_COLOR_RGB_ALPHA;
2235:                        break;
2236:                    default:
2237:                        throw new IIOException("Unsupported image type.");
2238:                    }
2239:
2240:                    // Compression method 0 (deflate/inflate) is only supported type.
2241:                    int compressionMethod = 0;
2242:
2243:                    // Filter method 0 (adaptive filtering) is only supported type.
2244:                    int filterMethod = 0;
2245:
2246:                    int interlaceMethod = decoder.getInterlaceMethod();
2247:
2248:                    if (width == 0) {
2249:                        throw new IIOException("Image width == 0!");
2250:                    }
2251:                    if (height == 0) {
2252:                        throw new IIOException("Image height == 0!");
2253:                    }
2254:                    if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4
2255:                            && bitDepth != 8 && bitDepth != 16) {
2256:                        throw new IIOException(
2257:                                "Bit depth must be 1, 2, 4, 8, or 16!");
2258:                    }
2259:                    if (colorType != 0 && colorType != 2 && colorType != 3
2260:                            && colorType != 4 && colorType != 6) {
2261:                        throw new IIOException(
2262:                                "Color type must be 0, 2, 3, 4, or 6!");
2263:                    }
2264:                    if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
2265:                        throw new IIOException(
2266:                                "Bad color type/bit depth combination!");
2267:                    }
2268:                    if ((colorType == PNG_COLOR_RGB
2269:                            || colorType == PNG_COLOR_RGB_ALPHA || colorType == PNG_COLOR_GRAY_ALPHA)
2270:                            && (bitDepth != 8 && bitDepth != 16)) {
2271:                        throw new IIOException(
2272:                                "Bad color type/bit depth combination!");
2273:                    }
2274:                    if (compressionMethod != 0) {
2275:                        throw new IIOException(
2276:                                "Unknown compression method (not 0)!");
2277:                    }
2278:                    if (filterMethod != 0) {
2279:                        throw new IIOException("Unknown filter method (not 0)!");
2280:                    }
2281:                    if (interlaceMethod != 0 && interlaceMethod != 1) {
2282:                        throw new IIOException(
2283:                                "Unknown interlace method (not 0 or 1)!");
2284:                    }
2285:
2286:                    IHDR_present = true;
2287:                    IHDR_width = width;
2288:                    IHDR_height = height;
2289:                    IHDR_bitDepth = bitDepth;
2290:                    IHDR_colorType = colorType;
2291:                    IHDR_compressionMethod = compressionMethod;
2292:                    IHDR_filterMethod = filterMethod;
2293:                    IHDR_interlaceMethod = interlaceMethod;
2294:                    gotHeader = true;
2295:                } catch (IOException e) {
2296:                    throw new IIOException("I/O error reading PNG header!", e);
2297:                }
2298:            }
2299:
2300:            private void parse_PLTE_chunk() throws IOException {
2301:                if (PLTE_present) {
2302:                    processWarningOccurred("A PNG image may not contain more than one PLTE chunk.\n"
2303:                            + "The chunk will be ignored.");
2304:                    return;
2305:                } else if (IHDR_colorType == PNG_COLOR_GRAY
2306:                        || IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
2307:                    processWarningOccurred("A PNG gray or gray alpha image cannot have a PLTE chunk.\n"
2308:                            + "The chunk will be ignored.");
2309:                    return;
2310:                }
2311:
2312:                byte[] palette = decoder.getPalette();
2313:
2314:                if (palette != null) {
2315:                    int numEntries = palette.length / 3;
2316:                    if (IHDR_colorType == PNG_COLOR_PALETTE) {
2317:                        int maxEntries = 1 << IHDR_bitDepth;
2318:                        if (numEntries > maxEntries) {
2319:                            processWarningOccurred("PLTE chunk contains too many entries for bit depth, ignoring extras.");
2320:                            numEntries = maxEntries;
2321:                        }
2322:                    }
2323:
2324:                    // Round array sizes up to 2^2^n
2325:                    int paletteEntries;
2326:                    if (numEntries > 16) {
2327:                        paletteEntries = 256;
2328:                    } else if (numEntries > 4) {
2329:                        paletteEntries = 16;
2330:                    } else if (numEntries > 2) {
2331:                        paletteEntries = 4;
2332:                    } else {
2333:                        paletteEntries = 2;
2334:                    }
2335:
2336:                    PLTE_present = true;
2337:                    PLTE_red = new byte[paletteEntries];
2338:                    PLTE_green = new byte[paletteEntries];
2339:                    PLTE_blue = new byte[paletteEntries];
2340:
2341:                    int index = 0;
2342:                    for (int i = 0; i < numEntries; i++) {
2343:                        PLTE_red[i] = palette[index++];
2344:                        PLTE_green[i] = palette[index++];
2345:                        PLTE_blue[i] = palette[index++];
2346:                    }
2347:                }
2348:            }
2349:
2350:            private void parse_bKGD_chunk() throws IOException {
2351:                int[] background = decoder.getBackground();
2352:                if (background != null) {
2353:                    if (IHDR_colorType == PNG_COLOR_PALETTE) {
2354:                        bKGD_colorType = PNG_COLOR_PALETTE;
2355:                        bKGD_index = background[0];
2356:                    } else if (IHDR_colorType == PNG_COLOR_GRAY
2357:                            || IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
2358:                        bKGD_colorType = PNG_COLOR_GRAY;
2359:                        bKGD_gray = background[0];
2360:                    } else { // RGB or RGB_ALPHA
2361:                        bKGD_colorType = PNG_COLOR_RGB;
2362:                        bKGD_red = background[0];
2363:                        bKGD_green = background[1];
2364:                        bKGD_blue = background[2];
2365:                    }
2366:
2367:                    bKGD_present = true;
2368:                }
2369:            }
2370:
2371:            private void parse_cHRM_chunk() throws IOException {
2372:                int[] chrm = decoder.getAllPrimaryChromaticities();
2373:                if (chrm != null) {
2374:                    int i = 0;
2375:                    cHRM_whitePointX = chrm[i++];
2376:                    cHRM_whitePointY = chrm[i++];
2377:                    cHRM_redX = chrm[i++];
2378:                    cHRM_redY = chrm[i++];
2379:                    cHRM_greenX = chrm[i++];
2380:                    cHRM_greenY = chrm[i++];
2381:                    cHRM_blueX = chrm[i++];
2382:                    cHRM_blueY = chrm[i++];
2383:
2384:                    cHRM_present = true;
2385:                }
2386:            }
2387:
2388:            private void parse_gAMA_chunk() throws IOException {
2389:                int gamma = decoder.getImageGamma();
2390:                if (gamma != decoder.PNG_gAMA_DEFAULT) {
2391:                    gAMA_gamma = gamma;
2392:
2393:                    gAMA_present = true;
2394:                }
2395:            }
2396:
2397:            private void parse_hIST_chunk() throws IOException, IIOException {
2398:                short[] histogram = decoder.getHistogram();
2399:                if (histogram != null) {
2400:                    if (!PLTE_present) {
2401:                        throw new IIOException(
2402:                                "hIST chunk without prior PLTE chunk!");
2403:                    }
2404:
2405:                    int length = Math.min(PLTE_red.length, histogram.length);
2406:                    hIST_histogram = new char[length];
2407:                    for (int i = 0; i < length; i++) {
2408:                        hIST_histogram[i] = (char) histogram[i];
2409:                    }
2410:
2411:                    hIST_present = true;
2412:                }
2413:            }
2414:
2415:            private void parse_iCCP_chunk() throws IOException {
2416:                String profileName = decoder.getEmbeddedICCProfileName();
2417:
2418:                if (profileName != null) {
2419:                    iCCP_profileName = profileName;
2420:
2421:                    byte[] uncompressedProfile = decoder
2422:                            .getEmbeddedICCProfile();
2423:
2424:                    // Need to compress this profile to match metadata specification.
2425:                    Deflater compressor = new Deflater(
2426:                            Deflater.BEST_COMPRESSION);
2427:                    compressor.setInput(uncompressedProfile);
2428:                    compressor.finish();
2429:
2430:                    int off = 0;
2431:                    int len = uncompressedProfile.length;
2432:                    byte[] compressedProfile = new byte[uncompressedProfile.length];
2433:                    do {
2434:                        int count = compressor.deflate(compressedProfile, off,
2435:                                len);
2436:                        off += count;
2437:                        len -= count;
2438:                    } while (!compressor.finished());
2439:
2440:                    int compressedDataLength = off;
2441:
2442:                    iCCP_compressedProfile = new byte[compressedDataLength];
2443:                    System.arraycopy(compressedProfile, 0,
2444:                            iCCP_compressedProfile, 0, compressedDataLength);
2445:
2446:                    iCCP_present = true;
2447:                }
2448:            }
2449:
2450:            private void parse_pHYs_chunk() throws IOException {
2451:                int unitSpecifier = decoder
2452:                        .getPhysicalPixelDimensions(decoder.PNG_PIXELS_UNIT_SPECIFIER);
2453:                if (unitSpecifier != decoder.PNG_pHYs_NOT_DEFINED) {
2454:                    pHYs_pixelsPerUnitXAxis = decoder
2455:                            .getPhysicalPixelDimensions(decoder.PNG_PIXELS_UNIT_X);
2456:                    pHYs_pixelsPerUnitYAxis = decoder
2457:                            .getPhysicalPixelDimensions(decoder.PNG_PIXELS_UNIT_Y);
2458:                    pHYs_unitSpecifier = unitSpecifier;
2459:
2460:                    pHYs_present = true;
2461:                }
2462:            }
2463:
2464:            private void parse_sBIT_chunk() throws IOException {
2465:                byte[] sBits = decoder.getSignificantBits();
2466:                if (sBits != null) {
2467:                    int i = 0;
2468:                    int colorType = IHDR_colorType;
2469:                    if (colorType == PNG_COLOR_GRAY
2470:                            || colorType == PNG_COLOR_GRAY_ALPHA) {
2471:                        sBIT_grayBits = sBits[i++];
2472:                    } else if (colorType == PNG_COLOR_RGB
2473:                            || colorType == PNG_COLOR_PALETTE
2474:                            || colorType == PNG_COLOR_RGB_ALPHA) {
2475:                        sBIT_redBits = sBits[i++];
2476:                        sBIT_greenBits = sBits[i++];
2477:                        sBIT_blueBits = sBits[i++];
2478:                    }
2479:
2480:                    if (colorType == PNG_COLOR_GRAY_ALPHA
2481:                            || colorType == PNG_COLOR_RGB_ALPHA) {
2482:                        sBIT_alphaBits = sBits[i++];
2483:                    }
2484:
2485:                    sBIT_colorType = colorType;
2486:                    sBIT_present = true;
2487:                }
2488:            }
2489:
2490:            private void parse_sPLT_chunk() throws IOException, IIOException {
2491:
2492:                PNGChunk[] sPLTChunks = decoder.getSuggestedPalette();
2493:
2494:                if (sPLTChunks != null && sPLTChunks.length > 0
2495:                        && sPLTChunks[0] != null) {
2496:                    PNGChunk sPLTChunk = sPLTChunks[0];
2497:                    byte[] chunkData = sPLTChunk.getData();
2498:                    int chunkLength = chunkData.length;
2499:
2500:                    InputStream is = new ByteArrayInputStream(sPLTChunk
2501:                            .getData());
2502:                    ImageInputStream stream = new MemoryCacheImageInputStream(
2503:                            is);
2504:
2505:                    sPLT_paletteName = readNullTerminatedString(stream);
2506:                    chunkLength -= sPLT_paletteName.length() + 1;
2507:
2508:                    int sampleDepth = stream.readUnsignedByte();
2509:                    sPLT_sampleDepth = sampleDepth;
2510:
2511:                    int numEntries = chunkLength / (4 * (sampleDepth / 8) + 2);
2512:                    sPLT_red = new int[numEntries];
2513:                    sPLT_green = new int[numEntries];
2514:                    sPLT_blue = new int[numEntries];
2515:                    sPLT_alpha = new int[numEntries];
2516:                    sPLT_frequency = new int[numEntries];
2517:
2518:                    if (sampleDepth == 8) {
2519:                        for (int i = 0; i < numEntries; i++) {
2520:                            sPLT_red[i] = stream.readUnsignedByte();
2521:                            sPLT_green[i] = stream.readUnsignedByte();
2522:                            sPLT_blue[i] = stream.readUnsignedByte();
2523:                            sPLT_alpha[i] = stream.readUnsignedByte();
2524:                            sPLT_frequency[i] = stream.readUnsignedShort();
2525:                        }
2526:                    } else if (sampleDepth == 16) {
2527:                        for (int i = 0; i < numEntries; i++) {
2528:                            sPLT_red[i] = stream.readUnsignedShort();
2529:                            sPLT_green[i] = stream.readUnsignedShort();
2530:                            sPLT_blue[i] = stream.readUnsignedShort();
2531:                            sPLT_alpha[i] = stream.readUnsignedShort();
2532:                            sPLT_frequency[i] = stream.readUnsignedShort();
2533:                        }
2534:                    } else {
2535:                        throw new IIOException("sPLT sample depth not 8 or 16!");
2536:                    }
2537:
2538:                    sPLT_present = true;
2539:                }
2540:            }
2541:
2542:            private void parse_sRGB_chunk() throws IOException {
2543:                int renderingIntent = decoder.getStandardRGB();
2544:                if (renderingIntent != decoder.PNG_sRGB_NOT_DEFINED) {
2545:                    sRGB_renderingIntent = renderingIntent;
2546:                    sRGB_present = true;
2547:                }
2548:            }
2549:
2550:            private void parse_tIME_chunk() throws IOException {
2551:                Calendar cal = decoder.getLastModificationTime();
2552:                if (cal != null) {
2553:                    tIME_year = cal.get(Calendar.YEAR);
2554:                    tIME_month = cal.get(Calendar.MONTH) + 1;
2555:                    tIME_day = cal.get(Calendar.DAY_OF_MONTH);
2556:                    tIME_hour = cal.get(Calendar.HOUR_OF_DAY);
2557:                    tIME_minute = cal.get(Calendar.MINUTE);
2558:                    tIME_second = cal.get(Calendar.SECOND);
2559:
2560:                    tIME_present = true;
2561:                }
2562:            }
2563:
2564:            private void parse_tRNS_chunk() throws IOException {
2565:                int[] transparency = decoder.getTransparency();
2566:
2567:                if (transparency == null) {
2568:                    return;
2569:                }
2570:
2571:                int colorType = IHDR_colorType;
2572:                if (colorType == PNG_COLOR_PALETTE) {
2573:                    if (!PLTE_present) {
2574:                        processWarningOccurred("tRNS chunk without prior PLTE chunk, ignoring it.");
2575:                        return;
2576:                    }
2577:
2578:                    // Alpha table may have fewer entries than RGB palette
2579:                    int maxEntries = PLTE_red.length;
2580:                    int numEntries = transparency.length;
2581:                    if (numEntries > maxEntries) {
2582:                        processWarningOccurred("tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
2583:                        numEntries = maxEntries;
2584:                    }
2585:                    tRNS_alpha = new byte[numEntries];
2586:                    tRNS_colorType = PNG_COLOR_PALETTE;
2587:                    for (int i = 0; i < numEntries; i++) {
2588:                        tRNS_alpha[i] = (byte) transparency[i];
2589:                    }
2590:                } else if (colorType == PNG_COLOR_GRAY) {
2591:                    if (transparency.length != 1) {
2592:                        processWarningOccurred("tRNS chunk for gray image must have length 2, ignoring chunk.");
2593:                        return;
2594:                    }
2595:                    tRNS_gray = transparency[0];
2596:                    tRNS_colorType = PNG_COLOR_GRAY;
2597:                } else if (colorType == PNG_COLOR_RGB) {
2598:                    if (transparency.length != 3) {
2599:                        processWarningOccurred("tRNS chunk for RGB image must have length 6, ignoring chunk.");
2600:                        return;
2601:                    }
2602:                    tRNS_red = transparency[0];
2603:                    tRNS_green = transparency[1];
2604:                    tRNS_blue = transparency[2];
2605:                    tRNS_colorType = PNG_COLOR_RGB;
2606:                } else {
2607:                    processWarningOccurred("Gray+Alpha and RGBA images may not have a tRNS chunk, ignoring it.");
2608:                    return;
2609:                }
2610:
2611:                tRNS_present = true;
2612:            }
2613:
2614:            // Parse all iTXt, tEXt, and zTXt chunks.
2615:            private void parseTextChunk() throws IOException {
2616:                PNGTextualData[] textualData = decoder.getTextualData();
2617:
2618:                if (textualData != null) {
2619:                    for (int i = 0; i < textualData.length; i++) {
2620:                        PNGTextualData textData = textualData[i];
2621:                        String keyword = textData.getKeyword();
2622:                        String text = textData.getText();
2623:                        String translatedKeyword = textData
2624:                                .getTranslatedKeyword();
2625:
2626:                        // No way to detect a zTXt chunk to use tEXt for zTXt.
2627:                        // Also, all text is already decompressed.
2628:                        if (keyword.equals(translatedKeyword)) { // tEXt and zTXt
2629:                            tEXt_keyword.add(keyword);
2630:                            tEXt_text.add(text);
2631:                        } else { // iTXt
2632:                            iTXt_keyword.add(keyword);
2633:                            iTXt_text.add(text);
2634:                            iTXt_translatedKeyword.add(translatedKeyword);
2635:
2636:                            // XXX No access to compression flag so set to 'false'
2637:                            // as text is decompressed by codecLib.
2638:                            int compressionFlag = 0;
2639:                            iTXt_compressionFlag.add(new Integer(
2640:                                    compressionFlag));
2641:
2642:                            // No access to compression method but only specified
2643:                            // one is '0' (deflate compression with ZLib data stream).
2644:                            int compressionMethod = 0;
2645:                            iTXt_compressionMethod.add(new Integer(
2646:                                    compressionMethod));
2647:
2648:                            String languageTag = textData.getEncoding();
2649:                            iTXt_languageTag.add(languageTag);
2650:                        }
2651:                    }
2652:                }
2653:            }
2654:
2655:            synchronized void readMetadata(CLibPNGImageReader reader,
2656:                    Decoder decoder) throws IIOException {
2657:                if (gotMetadata) {
2658:                    return;
2659:                }
2660:
2661:                this .reader = reader;
2662:                this .decoder = decoder;
2663:
2664:                readHeader();
2665:
2666:                try {
2667:                    parse_PLTE_chunk();
2668:                    parse_bKGD_chunk();
2669:                    parse_cHRM_chunk();
2670:                    parse_gAMA_chunk();
2671:                    parse_hIST_chunk();
2672:                    parse_iCCP_chunk();
2673:                    parse_pHYs_chunk();
2674:                    parse_sBIT_chunk();
2675:                    parse_sPLT_chunk();
2676:                    parse_sRGB_chunk();
2677:                    parse_tIME_chunk();
2678:                    parse_tRNS_chunk();
2679:
2680:                    parseTextChunk();
2681:
2682:                    PNGChunk[] userChunks = decoder.getUserData();
2683:                    if (userChunks != null) {
2684:                        for (int i = 0; i < userChunks.length; i++) {
2685:                            // Read an unknown chunk
2686:                            PNGChunk userChunk = userChunks[i];
2687:
2688:                            int chunkType = userChunk.getID();
2689:                            byte[] b = userChunk.getData();
2690:
2691:                            StringBuffer chunkName = new StringBuffer(4);
2692:                            chunkName.append((char) (chunkType >>> 24));
2693:                            chunkName.append((char) ((chunkType >> 16) & 0xff));
2694:                            chunkName.append((char) ((chunkType >> 8) & 0xff));
2695:                            chunkName.append((char) (chunkType & 0xff));
2696:
2697:                            int ancillaryBit = chunkType >>> 28;
2698:                            if (ancillaryBit == 0) {
2699:                                processWarningOccurred("Encountered unknown chunk with critical bit set!");
2700:                            }
2701:
2702:                            unknownChunkType.add(chunkName.toString());
2703:                            unknownChunkData.add(b);
2704:                        }
2705:                    }
2706:                } catch (IOException e) {
2707:                    throw new IIOException("Error reading PNG metadata", e);
2708:                } finally {
2709:                    this .reader = null;
2710:                    this .decoder = null;
2711:                }
2712:
2713:                gotMetadata = true;
2714:            }
2715:
2716:            void processWarningOccurred(String warning) {
2717:                if (reader != null) {
2718:                    reader.forwardWarningMessage(warning);
2719:                }
2720:            }
2721:
2722:            // END metadata reading methods.
2723:
2724:            // BEGIN metadata writing methods.
2725:
2726:            synchronized void writeMetadata(Encoder encoder)
2727:                    throws IIOException {
2728:                if (IHDR_present) {
2729:                    encoder.setBitDepth(IHDR_bitDepth);
2730:                    encoder
2731:                            .setInterlaceMethod(IHDR_interlaceMethod == 0 ? Encoder.PNG_INTERLACE_METHOD_DEFAULT
2732:                                    : Encoder.PNG_INTERLACE_METHOD_ADAM7);
2733:                }
2734:
2735:                if (PLTE_present) {
2736:                    int paletteLength = PLTE_red.length;
2737:                    byte[] palette = new byte[3 * paletteLength];
2738:                    for (int i = 0, j = 0; i < paletteLength; i++) {
2739:                        palette[j++] = PLTE_red[i];
2740:                        palette[j++] = PLTE_green[i];
2741:                        palette[j++] = PLTE_blue[i];
2742:                    }
2743:                    encoder.setPalette(palette);
2744:                }
2745:
2746:                if (bKGD_present) {
2747:                    int[] color;
2748:                    switch (bKGD_colorType) {
2749:                    case PNG_COLOR_GRAY:
2750:                        color = new int[] { bKGD_gray };
2751:                        break;
2752:                    case PNG_COLOR_PALETTE:
2753:                        color = new int[] { bKGD_index };
2754:                        break;
2755:                    default:
2756:                        color = new int[] { bKGD_red, bKGD_green, bKGD_blue };
2757:                    }
2758:                    encoder.setBackground(color);
2759:                }
2760:
2761:                if (cHRM_present) {
2762:                    encoder.setPrimaryChromaticities(cHRM_whitePointX,
2763:                            cHRM_whitePointY, cHRM_redX, cHRM_redY,
2764:                            cHRM_greenX, cHRM_greenY, cHRM_blueX, cHRM_blueY);
2765:                }
2766:
2767:                if (gAMA_present) {
2768:                    encoder.setImageGamma(gAMA_gamma);
2769:                }
2770:
2771:                if (hIST_present) {
2772:                    int histogramLength = hIST_histogram.length;
2773:                    short[] histogram = new short[histogramLength];
2774:                    for (int i = 0; i < histogramLength; i++) {
2775:                        histogram[i] = (short) hIST_histogram[i];
2776:                    }
2777:                    encoder.setHistogram(histogram);
2778:                }
2779:
2780:                if (iCCP_present) {
2781:                    // Encoder expects an uncompressed profile so decompress.
2782:                    Inflater decompresser = new Inflater();
2783:                    decompresser.setInput(iCCP_compressedProfile);
2784:                    byte[] result = new byte[2 * decompresser.getRemaining()];
2785:
2786:                    int off = 0;
2787:                    try {
2788:                        do {
2789:                            off += decompresser.inflate(result, off,
2790:                                    result.length - off);
2791:                            if (off == result.length
2792:                                    && !decompresser.finished()) {
2793:                                byte[] tmpbuf = new byte[2 * result.length];
2794:                                System.arraycopy(result, 0, tmpbuf, 0,
2795:                                        result.length);
2796:                                result = tmpbuf;
2797:                            }
2798:                        } while (!decompresser.finished());
2799:                        decompresser.end();
2800:
2801:                        byte[] uncompressedProfile;
2802:                        if (off == result.length) {
2803:                            uncompressedProfile = result;
2804:                        } else {
2805:                            uncompressedProfile = new byte[off];
2806:                            System.arraycopy(result, 0, uncompressedProfile, 0,
2807:                                    off);
2808:                        }
2809:
2810:                        String iCCPName = toPrintableLatin1(iCCP_profileName);
2811:                        encoder.setEmbeddedICCProfile(iCCPName,
2812:                                uncompressedProfile);
2813:                    } catch (DataFormatException e) {
2814:                        // XXX warning message?
2815:                    }
2816:                }
2817:
2818:                if (iTXt_keyword.size() > 0) {
2819:                    int numChunks = iTXt_keyword.size();
2820:                    for (int i = 0; i < numChunks; i++) {
2821:                        Integer compressionFlag = Integer
2822:                                .valueOf((String) iTXt_compressionFlag.get(i));
2823:                        encoder.setUnicodeTextualData((String) iTXt_keyword
2824:                                .get(i),
2825:                                (String) iTXt_translatedKeyword.get(i),
2826:                                (String) iTXt_languageTag.get(i),
2827:                                (String) iTXt_text.get(i), compressionFlag
2828:                                        .intValue() == 1);
2829:                    }
2830:                }
2831:
2832:                if (pHYs_present) {
2833:                    encoder.setPhysicalPixelDimensions(pHYs_pixelsPerUnitXAxis,
2834:                            pHYs_pixelsPerUnitYAxis, pHYs_unitSpecifier);
2835:                }
2836:
2837:                if (sBIT_present) {
2838:                    byte[] bits;
2839:                    switch (sBIT_colorType) {
2840:                    case PNG_COLOR_GRAY:
2841:                        bits = new byte[] { (byte) (sBIT_grayBits & 0xff) };
2842:                        break;
2843:                    case PNG_COLOR_GRAY_ALPHA:
2844:                        bits = new byte[] { (byte) (sBIT_grayBits & 0xff),
2845:                                (byte) (sBIT_alphaBits & 0xff) };
2846:                        break;
2847:                    case PNG_COLOR_RGB_ALPHA:
2848:                        bits = new byte[] { (byte) (sBIT_redBits & 0xff),
2849:                                (byte) (sBIT_greenBits & 0xff),
2850:                                (byte) (sBIT_blueBits & 0xff),
2851:                                (byte) (sBIT_alphaBits & 0xff) };
2852:                        break;
2853:                    default: // RGB and PALETTE
2854:                        bits = new byte[] { (byte) (sBIT_redBits & 0xff),
2855:                                (byte) (sBIT_greenBits & 0xff),
2856:                                (byte) (sBIT_blueBits & 0xff) };
2857:                        break;
2858:                    }
2859:                    encoder.setSignificantBits(bits);
2860:                }
2861:
2862:                if (sPLT_present) {
2863:                    if (sPLT_sampleDepth == 8) {
2864:                        byte[] red = new byte[sPLT_red.length];
2865:                        byte[] green = new byte[sPLT_green.length];
2866:                        byte[] blue = new byte[sPLT_blue.length];
2867:                        byte[] alpha = new byte[sPLT_alpha.length];
2868:                        short[] frequency = new short[sPLT_frequency.length];
2869:
2870:                        int length = red.length;
2871:                        for (int i = 0; i < length; i++) {
2872:                            red[i] = (byte) (sPLT_red[i] & 0xff);
2873:                            green[i] = (byte) (sPLT_green[i] & 0xff);
2874:                            blue[i] = (byte) (sPLT_blue[i] & 0xff);
2875:                            alpha[i] = (byte) (sPLT_alpha[i] & 0xff);
2876:                            frequency[i] = (short) (sPLT_frequency[i] & 0xffff);
2877:                        }
2878:
2879:                        String sPLTName = toPrintableLatin1(sPLT_paletteName);
2880:                        encoder.setSuggestedPalette(sPLTName, red, green, blue,
2881:                                alpha, frequency);
2882:                    } else {
2883:                        short[] red = new short[sPLT_red.length];
2884:                        short[] green = new short[sPLT_green.length];
2885:                        short[] blue = new short[sPLT_blue.length];
2886:                        short[] alpha = new short[sPLT_alpha.length];
2887:                        short[] frequency = new short[sPLT_frequency.length];
2888:
2889:                        int length = red.length;
2890:                        for (int i = 0; i < length; i++) {
2891:                            red[i] = (short) (sPLT_red[i] & 0xffff);
2892:                            green[i] = (short) (sPLT_green[i] & 0xffff);
2893:                            blue[i] = (short) (sPLT_blue[i] & 0xffff);
2894:                            alpha[i] = (short) (sPLT_alpha[i] & 0xffff);
2895:                            frequency[i] = (short) (sPLT_frequency[i] & 0xffff);
2896:                        }
2897:
2898:                        String sPLTName = toPrintableLatin1(sPLT_paletteName);
2899:                        encoder.setSuggestedPalette(sPLTName, red, green, blue,
2900:                                alpha, frequency);
2901:                    }
2902:                }
2903:
2904:                if (sRGB_present) {
2905:                    encoder.setStandardRGB(sRGB_renderingIntent);
2906:                }
2907:
2908:                if (tEXt_keyword.size() > 0) {
2909:                    int numChunks = tEXt_keyword.size();
2910:                    for (int i = 0; i < numChunks; i++) {
2911:                        encoder.setTextualData((String) tEXt_keyword.get(i),
2912:                                (String) tEXt_text.get(i), false);
2913:                    }
2914:                }
2915:
2916:                if (tIME_present) {
2917:                    encoder.setLastModificationTime(new GregorianCalendar(
2918:                            tIME_year, tIME_month - 1, tIME_day, tIME_hour,
2919:                            tIME_minute, tIME_second));
2920:                }
2921:
2922:                if (tRNS_present) {
2923:                    if (tRNS_colorType == PNG_COLOR_GRAY) {
2924:                        encoder
2925:                                .setTransparency(tRNS_gray, tRNS_gray,
2926:                                        tRNS_gray);
2927:                    } else if (tRNS_colorType == PNG_COLOR_PALETTE) {
2928:                        int length = tRNS_alpha.length;
2929:                        int[] color = new int[length];
2930:                        for (int i = 0; i < length; i++) {
2931:                            color[i] = tRNS_alpha[i] & 0xff;
2932:                        }
2933:                        encoder.setTransparency(color);
2934:                    } else {
2935:                        encoder
2936:                                .setTransparency(tRNS_red, tRNS_green,
2937:                                        tRNS_blue);
2938:                    }
2939:                }
2940:
2941:                if (zTXt_keyword.size() > 0) {
2942:                    int numChunks = zTXt_keyword.size();
2943:                    for (int i = 0; i < numChunks; i++) {
2944:                        encoder.setTextualData((String) zTXt_keyword.get(i),
2945:                                (String) zTXt_text.get(i), true);
2946:                    }
2947:                }
2948:
2949:                if (unknownChunkType.size() > 0) {
2950:                    int numChunks = unknownChunkType.size();
2951:                    for (int i = 0; i < numChunks; i++) {
2952:                        encoder.setUserData((String) unknownChunkType.get(i),
2953:                                (byte[]) unknownChunkData.get(i),
2954:                                Encoder.PNG_SAVE_BEFORE_IMAGE_DATA);
2955:                    }
2956:                }
2957:            }
2958:
2959:            // END metadata writing methods.
2960:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.