/* * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.png; import java.awt.image.ColorModel;import java.awt.image.IndexColorModel;import java.awt.image.SampleModel;import java.util.ArrayList;import java.util.Iterator;import java.util.StringTokenizer;import javax.imageio.ImageTypeSpecifier;import javax.imageio.metadata.IIOInvalidTreeException;import javax.imageio.metadata.IIOMetadata;import javax.imageio.metadata.IIOMetadataFormat;import javax.imageio.metadata.IIOMetadataFormatImpl;import javax.imageio.metadata.IIOMetadataNode;import org.w3c.dom.Node; public class PNGMetadata extends IIOMetadata implements Cloneable { // package scope public static final String nativeMetadataFormatName = "javax_imageio_png_1.0"; protected static final String nativeMetadataFormatClassName = "com.sun.imageio.plugins.png.PNGMetadataFormat"; // Color types for IHDR chunk public static final String[] IHDR_colorTypeNames = { "Grayscale", null, "RGB", "Palette", "GrayAlpha", null, "RGBAlpha" }; public static final int[] IHDR_numChannels = { 1, 0, 3, 3, 2, 0, 4 }; // Bit depths for IHDR chunk public static final String[] IHDR_bitDepths = { "1", "2", "4", "8", "16" }; // Compression methods for IHDR chunk public static final String[] IHDR_compressionMethodNames = { "deflate" }; // Filter methods for IHDR chunk public static final String[] IHDR_filterMethodNames = { "adaptive" }; // Interlace methods for IHDR chunk public static final String[] IHDR_interlaceMethodNames = { "none", "adam7" }; // Compression methods for iCCP chunk public static final String[] iCCP_compressionMethodNames = { "deflate" }; // Compression methods for zTXt chunk public static final String[] zTXt_compressionMethodNames = { "deflate" }; // "Unknown" unit for pHYs chunk public static final int PHYS_UNIT_UNKNOWN = 0; // "Meter" unit for pHYs chunk public static final int PHYS_UNIT_METER = 1; // Unit specifiers for pHYs chunk public static final String[] unitSpecifierNames = { "unknown", "meter" }; // Rendering intents for sRGB chunk public static final String[] renderingIntentNames = { "Perceptual", // 0 "Relative colorimetric", // 1 "Saturation", // 2 "Absolute colorimetric" // 3 }; // Color space types for Chroma->ColorSpaceType node public static final String[] colorSpaceTypeNames = { "GRAY", null, "RGB", "RGB", "GRAY", null, "RGB" }; // IHDR chunk public boolean IHDR_present; public int IHDR_width; public int IHDR_height; public int IHDR_bitDepth; public int IHDR_colorType; public int IHDR_compressionMethod; public int IHDR_filterMethod; public int IHDR_interlaceMethod; // 0 == none, 1 == adam7 // PLTE chunk public boolean PLTE_present; public byte[] PLTE_red; public byte[] PLTE_green; public byte[] PLTE_blue; // If non-null, used to reorder palette entries during encoding in // order to minimize the size of the tRNS chunk. Thus an index of // 'i' in the source should be encoded as index 'PLTE_order[i]'. // PLTE_order will be null unless 'initialize' is called with an // IndexColorModel image type. public int[] PLTE_order = null; // bKGD chunk // If external (non-PNG sourced) data has red = green = blue, // always store it as gray and promote when writing public boolean bKGD_present; public int bKGD_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE public int bKGD_index; public int bKGD_gray; public int bKGD_red; public int bKGD_green; public int bKGD_blue; // cHRM chunk public boolean cHRM_present; public int cHRM_whitePointX; public int cHRM_whitePointY; public int cHRM_redX; public int cHRM_redY; public int cHRM_greenX; public int cHRM_greenY; public int cHRM_blueX; public int cHRM_blueY; // gAMA chunk public boolean gAMA_present; public int gAMA_gamma; // hIST chunk public boolean hIST_present; public char[] hIST_histogram; // iCCP chunk public boolean iCCP_present; public String iCCP_profileName; public int iCCP_compressionMethod; public byte[] iCCP_compressedProfile; // iTXt chunk public ArrayList<String> iTXt_keyword = new ArrayList<String>(); public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>(); public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>(); public ArrayList<String> iTXt_languageTag = new ArrayList<String>(); public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>(); public ArrayList<String> iTXt_text = new ArrayList<String>(); // pHYs chunk public boolean pHYs_present; public int pHYs_pixelsPerUnitXAxis; public int pHYs_pixelsPerUnitYAxis; public int pHYs_unitSpecifier; // 0 == unknown, 1 == meter // sBIT chunk public boolean sBIT_present; public int sBIT_colorType; // PNG_COLOR_GRAY, _GRAY_ALPHA, _RGB, _RGB_ALPHA public int sBIT_grayBits; public int sBIT_redBits; public int sBIT_greenBits; public int sBIT_blueBits; public int sBIT_alphaBits; // sPLT chunk public boolean sPLT_present; public String sPLT_paletteName; // 1-79 characters public int sPLT_sampleDepth; // 8 or 16 public int[] sPLT_red; public int[] sPLT_green; public int[] sPLT_blue; public int[] sPLT_alpha; public int[] sPLT_frequency; // sRGB chunk public boolean sRGB_present; public int sRGB_renderingIntent; // tEXt chunk public ArrayList<String> tEXt_keyword = new ArrayList<String>(); // 1-79 characters public ArrayList<String> tEXt_text = new ArrayList<String>(); // tIME chunk public boolean tIME_present; public int tIME_year; public int tIME_month; public int tIME_day; public int tIME_hour; public int tIME_minute; public int tIME_second; // tRNS chunk // If external (non-PNG sourced) data has red = green = blue, // always store it as gray and promote when writing public boolean tRNS_present; public int tRNS_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE public byte[] tRNS_alpha; // May have fewer entries than PLTE_red, etc. public int tRNS_gray; public int tRNS_red; public int tRNS_green; public int tRNS_blue; // zTXt chunk public ArrayList<String> zTXt_keyword = new ArrayList<String>(); public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>(); public ArrayList<String> zTXt_text = new ArrayList<String>(); // Unknown chunks public ArrayList<String> unknownChunkType = new ArrayList<String>(); public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>(); public PNGMetadata() { super(true, nativeMetadataFormatName, nativeMetadataFormatClassName, null, null); } public PNGMetadata(IIOMetadata metadata) { // TODO -- implement } /** * Sets the IHDR_bitDepth and IHDR_colorType variables. * The <code>numBands</code> parameter is necessary since * we may only be writing a subset of the image bands. */ public void initialize(ImageTypeSpecifier imageType, int numBands) { ColorModel colorModel = imageType.getColorModel(); SampleModel sampleModel = imageType.getSampleModel(); // Initialize IHDR_bitDepth int[] sampleSize = sampleModel.getSampleSize(); int bitDepth = sampleSize[0]; // Choose max bit depth over all channels // Fixes bug 4413109 for (int i = 1; i < sampleSize.length; i++) { if (sampleSize[i] > bitDepth) { bitDepth = sampleSize[i]; } } // Multi-channel images must have a bit depth of 8 or 16 if (sampleSize.length > 1 && bitDepth < 8) { bitDepth = 8; } // Round bit depth up to a power of 2 if (bitDepth > 2 && bitDepth < 4) { bitDepth = 4; } else if (bitDepth > 4 && bitDepth < 8) { bitDepth = 8; } else if (bitDepth > 8 && bitDepth < 16) { bitDepth = 16; } else if (bitDepth > 16) { throw new RuntimeException("bitDepth > 16!"); } IHDR_bitDepth = bitDepth; // Initialize IHDR_colorType if (colorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)colorModel; int size = icm.getMapSize(); byte[] reds = new byte[size]; icm.getReds(reds); byte[] greens = new byte[size]; icm.getGreens(greens); byte[] blues = new byte[size]; icm.getBlues(blues); // Determine whether the color tables are actually a gray ramp // if the color type has not been set previously boolean isGray = false; if (!IHDR_present || (IHDR_colorType != PNGImageReader.PNG_COLOR_PALETTE)) { isGray = true; int scale = 255/((1 << IHDR_bitDepth) - 1); for (int i = 0; i < size; i++) { byte red = reds[i]; if ((red != (byte)(i*scale)) || (red != greens[i]) || (red != blues[i])) { isGray = false; break; } } } // Determine whether transparency exists boolean hasAlpha = colorModel.hasAlpha(); byte[] alpha = null; if (hasAlpha) { alpha = new byte[size]; icm.getAlphas(alpha); } /* * NB: PNG_COLOR_GRAY_ALPHA color type may be not optimal for images * contained more than 1024 pixels (or even than 768 pixels in case of * single transparent pixel in palette). * For such images alpha samples in raster will occupy more space than * it is required to store palette so it could be reasonable to * use PNG_COLOR_PALETTE color type for large images. */ if (isGray && hasAlpha && (bitDepth == 8 || bitDepth == 16)) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; } else if (isGray && !hasAlpha) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; } else { IHDR_colorType = PNGImageReader.PNG_COLOR_PALETTE; PLTE_present = true; PLTE_order = null; PLTE_red = (byte[])reds.clone(); PLTE_green = (byte[])greens.clone(); PLTE_blue = (byte[])blues.clone(); if (hasAlpha) { tRNS_present = true; tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE; PLTE_order = new int[alpha.length]; // Reorder the palette so that non-opaque entries // come first. Since the tRNS chunk does not have // to store trailing 255's, this can save a // considerable amount of space when encoding // images with only one transparent pixel value, // e.g., images from GIF sources. byte[] newAlpha = new byte[alpha.length]; // Scan for non-opaque entries and assign them // positions starting at 0. int newIndex = 0; for (int i = 0; i < alpha.length; i++) { if (alpha[i] != (byte)255) { PLTE_order[i] = newIndex; newAlpha[newIndex] = alpha[i]; ++newIndex; } } int numTransparent = newIndex; // Scan for opaque entries and assign them // positions following the non-opaque entries. for (int i = 0; i < alpha.length; i++) { if (alpha[i] == (byte)255) { PLTE_order[i] = newIndex++; } } // Reorder the palettes byte[] oldRed = PLTE_red; byte[] oldGreen = PLTE_green; byte[] oldBlue = PLTE_blue; int len = oldRed.length; // All have the same length PLTE_red = new byte[len]; PLTE_green = new byte[len]; PLTE_blue = new byte[len]; for (int i = 0; i < len; i++) { PLTE_red[PLTE_order[i]] = oldRed[i]; PLTE_green[PLTE_order[i]] = oldGreen[i]; PLTE_blue[PLTE_order[i]] = oldBlue[i]; } // Copy only the transparent entries into tRNS_alpha tRNS_alpha = new byte[numTransparent]; System.arraycopy(newAlpha, 0, tRNS_alpha, 0, numTransparent); } } } else { if (numBands == 1) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; } else if (numBands == 2) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; } else if (numBands == 3) { IHDR_colorType = PNGImageReader.PNG_COLOR_RGB; } else if (numBands == 4) { IHDR_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA; } else { throw new RuntimeException("Number of bands not 1-4!"); } } IHDR_present = true; } public boolean isReadOnly() { return false; } private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) { if (in == null) { return null; } else { ArrayList<byte[]> list = new ArrayList<byte[]>(in.size()); for (byte[] b: in) { list.add((b == null) ? null : (byte[])b.clone()); } return list; } } // Deep clone public Object clone() { PNGMetadata metadata; try { metadata = (PNGMetadata)super.clone(); } catch (CloneNotSupportedException e) { return null; } // unknownChunkData needs deep clone metadata.unknownChunkData = cloneBytesArrayList(this.unknownChunkData); return metadata; } public Node getAsTree(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return getNativeTree(); } else if (formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) { return getStandardTree(); } else { throw new IllegalArgumentException("Not a recognized format!"); } } private Node getNativeTree() { IIOMetadataNode node = null; // scratch node IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); // IHDR if (IHDR_present) { IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR"); IHDR_node.setAttribute("width", Integer.toString(IHDR_width)); IHDR_node.setAttribute("height", Integer.toString(IHDR_height)); IHDR_node.setAttribute("bitDepth", Integer.toString(IHDR_bitDepth)); IHDR_node.setAttribute("colorType", IHDR_colorTypeNames[IHDR_colorType]); // IHDR_compressionMethod must be 0 in PNG 1.1 IHDR_node.setAttribute("compressionMethod", IHDR_compressionMethodNames[IHDR_compressionMethod]); // IHDR_filterMethod must be 0 in PNG 1.1 IHDR_node.setAttribute("filterMethod", IHDR_filterMethodNames[IHDR_filterMethod]); IHDR_node.setAttribute("interlaceMethod", IHDR_interlaceMethodNames[IHDR_interlaceMethod]); root.appendChild(IHDR_node); } // PLTE if (PLTE_present) { IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE"); int numEntries = PLTE_red.length; for (int i = 0; i < numEntries; i++) { IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry"); entry.setAttribute("index", Integer.toString(i)); entry.setAttribute("red", Integer.toString(PLTE_red[i] & 0xff));Lines 1–500 of 2046. Load more
/* * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.png; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; import java.util.ArrayList; import java.util.Iterator; import java.util.StringTokenizer; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOMetadataNode; import org.w3c.dom.Node; public class PNGMetadata extends IIOMetadata implements Cloneable { // package scope public static final String nativeMetadataFormatName = "javax_imageio_png_1.0"; protected static final String nativeMetadataFormatClassName = "com.sun.imageio.plugins.png.PNGMetadataFormat"; // Color types for IHDR chunk public static final String[] IHDR_colorTypeNames = { "Grayscale", null, "RGB", "Palette", "GrayAlpha", null, "RGBAlpha" }; public static final int[] IHDR_numChannels = { 1, 0, 3, 3, 2, 0, 4 }; // Bit depths for IHDR chunk public static final String[] IHDR_bitDepths = { "1", "2", "4", "8", "16" }; // Compression methods for IHDR chunk public static final String[] IHDR_compressionMethodNames = { "deflate" }; // Filter methods for IHDR chunk public static final String[] IHDR_filterMethodNames = { "adaptive" }; // Interlace methods for IHDR chunk public static final String[] IHDR_interlaceMethodNames = { "none", "adam7" }; // Compression methods for iCCP chunk public static final String[] iCCP_compressionMethodNames = { "deflate" }; // Compression methods for zTXt chunk public static final String[] zTXt_compressionMethodNames = { "deflate" }; // "Unknown" unit for pHYs chunk public static final int PHYS_UNIT_UNKNOWN = 0; // "Meter" unit for pHYs chunk public static final int PHYS_UNIT_METER = 1; // Unit specifiers for pHYs chunk public static final String[] unitSpecifierNames = { "unknown", "meter" }; // Rendering intents for sRGB chunk public static final String[] renderingIntentNames = { "Perceptual", // 0 "Relative colorimetric", // 1 "Saturation", // 2 "Absolute colorimetric" // 3 }; // Color space types for Chroma->ColorSpaceType node public static final String[] colorSpaceTypeNames = { "GRAY", null, "RGB", "RGB", "GRAY", null, "RGB" }; // IHDR chunk public boolean IHDR_present; public int IHDR_width; public int IHDR_height; public int IHDR_bitDepth; public int IHDR_colorType; public int IHDR_compressionMethod; public int IHDR_filterMethod; public int IHDR_interlaceMethod; // 0 == none, 1 == adam7 // PLTE chunk public boolean PLTE_present; public byte[] PLTE_red; public byte[] PLTE_green; public byte[] PLTE_blue; // If non-null, used to reorder palette entries during encoding in // order to minimize the size of the tRNS chunk. Thus an index of // 'i' in the source should be encoded as index 'PLTE_order[i]'. // PLTE_order will be null unless 'initialize' is called with an // IndexColorModel image type. public int[] PLTE_order = null; // bKGD chunk // If external (non-PNG sourced) data has red = green = blue, // always store it as gray and promote when writing public boolean bKGD_present; public int bKGD_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE public int bKGD_index; public int bKGD_gray; public int bKGD_red; public int bKGD_green; public int bKGD_blue; // cHRM chunk public boolean cHRM_present; public int cHRM_whitePointX; public int cHRM_whitePointY; public int cHRM_redX; public int cHRM_redY; public int cHRM_greenX; public int cHRM_greenY; public int cHRM_blueX; public int cHRM_blueY; // gAMA chunk public boolean gAMA_present; public int gAMA_gamma; // hIST chunk public boolean hIST_present; public char[] hIST_histogram; // iCCP chunk public boolean iCCP_present; public String iCCP_profileName; public int iCCP_compressionMethod; public byte[] iCCP_compressedProfile; // iTXt chunk public ArrayList<String> iTXt_keyword = new ArrayList<String>(); public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>(); public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>(); public ArrayList<String> iTXt_languageTag = new ArrayList<String>(); public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>(); public ArrayList<String> iTXt_text = new ArrayList<String>(); // pHYs chunk public boolean pHYs_present; public int pHYs_pixelsPerUnitXAxis; public int pHYs_pixelsPerUnitYAxis; public int pHYs_unitSpecifier; // 0 == unknown, 1 == meter // sBIT chunk public boolean sBIT_present; public int sBIT_colorType; // PNG_COLOR_GRAY, _GRAY_ALPHA, _RGB, _RGB_ALPHA public int sBIT_grayBits; public int sBIT_redBits; public int sBIT_greenBits; public int sBIT_blueBits; public int sBIT_alphaBits; // sPLT chunk public boolean sPLT_present; public String sPLT_paletteName; // 1-79 characters public int sPLT_sampleDepth; // 8 or 16 public int[] sPLT_red; public int[] sPLT_green; public int[] sPLT_blue; public int[] sPLT_alpha; public int[] sPLT_frequency; // sRGB chunk public boolean sRGB_present; public int sRGB_renderingIntent; // tEXt chunk public ArrayList<String> tEXt_keyword = new ArrayList<String>(); // 1-79 characters public ArrayList<String> tEXt_text = new ArrayList<String>(); // tIME chunk public boolean tIME_present; public int tIME_year; public int tIME_month; public int tIME_day; public int tIME_hour; public int tIME_minute; public int tIME_second; // tRNS chunk // If external (non-PNG sourced) data has red = green = blue, // always store it as gray and promote when writing public boolean tRNS_present; public int tRNS_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE public byte[] tRNS_alpha; // May have fewer entries than PLTE_red, etc. public int tRNS_gray; public int tRNS_red; public int tRNS_green; public int tRNS_blue; // zTXt chunk public ArrayList<String> zTXt_keyword = new ArrayList<String>(); public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>(); public ArrayList<String> zTXt_text = new ArrayList<String>(); // Unknown chunks public ArrayList<String> unknownChunkType = new ArrayList<String>(); public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>(); public PNGMetadata() { super(true, nativeMetadataFormatName, nativeMetadataFormatClassName, null, null); } public PNGMetadata(IIOMetadata metadata) { // TODO -- implement } /** * Sets the IHDR_bitDepth and IHDR_colorType variables. * The <code>numBands</code> parameter is necessary since * we may only be writing a subset of the image bands. */ public void initialize(ImageTypeSpecifier imageType, int numBands) { ColorModel colorModel = imageType.getColorModel(); SampleModel sampleModel = imageType.getSampleModel(); // Initialize IHDR_bitDepth int[] sampleSize = sampleModel.getSampleSize(); int bitDepth = sampleSize[0]; // Choose max bit depth over all channels // Fixes bug 4413109 for (int i = 1; i < sampleSize.length; i++) { if (sampleSize[i] > bitDepth) { bitDepth = sampleSize[i]; } } // Multi-channel images must have a bit depth of 8 or 16 if (sampleSize.length > 1 && bitDepth < 8) { bitDepth = 8; } // Round bit depth up to a power of 2 if (bitDepth > 2 && bitDepth < 4) { bitDepth = 4; } else if (bitDepth > 4 && bitDepth < 8) { bitDepth = 8; } else if (bitDepth > 8 && bitDepth < 16) { bitDepth = 16; } else if (bitDepth > 16) { throw new RuntimeException("bitDepth > 16!"); } IHDR_bitDepth = bitDepth; // Initialize IHDR_colorType if (colorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)colorModel; int size = icm.getMapSize(); byte[] reds = new byte[size]; icm.getReds(reds); byte[] greens = new byte[size]; icm.getGreens(greens); byte[] blues = new byte[size]; icm.getBlues(blues); // Determine whether the color tables are actually a gray ramp // if the color type has not been set previously boolean isGray = false; if (!IHDR_present || (IHDR_colorType != PNGImageReader.PNG_COLOR_PALETTE)) { isGray = true; int scale = 255/((1 << IHDR_bitDepth) - 1); for (int i = 0; i < size; i++) { byte red = reds[i]; if ((red != (byte)(i*scale)) || (red != greens[i]) || (red != blues[i])) { isGray = false; break; } } } // Determine whether transparency exists boolean hasAlpha = colorModel.hasAlpha(); byte[] alpha = null; if (hasAlpha) { alpha = new byte[size]; icm.getAlphas(alpha); } /* * NB: PNG_COLOR_GRAY_ALPHA color type may be not optimal for images * contained more than 1024 pixels (or even than 768 pixels in case of * single transparent pixel in palette). * For such images alpha samples in raster will occupy more space than * it is required to store palette so it could be reasonable to * use PNG_COLOR_PALETTE color type for large images. */ if (isGray && hasAlpha && (bitDepth == 8 || bitDepth == 16)) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; } else if (isGray && !hasAlpha) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; } else { IHDR_colorType = PNGImageReader.PNG_COLOR_PALETTE; PLTE_present = true; PLTE_order = null; PLTE_red = (byte[])reds.clone(); PLTE_green = (byte[])greens.clone(); PLTE_blue = (byte[])blues.clone(); if (hasAlpha) { tRNS_present = true; tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE; PLTE_order = new int[alpha.length]; // Reorder the palette so that non-opaque entries // come first. Since the tRNS chunk does not have // to store trailing 255's, this can save a // considerable amount of space when encoding // images with only one transparent pixel value, // e.g., images from GIF sources. byte[] newAlpha = new byte[alpha.length]; // Scan for non-opaque entries and assign them // positions starting at 0. int newIndex = 0; for (int i = 0; i < alpha.length; i++) { if (alpha[i] != (byte)255) { PLTE_order[i] = newIndex; newAlpha[newIndex] = alpha[i]; ++newIndex; } } int numTransparent = newIndex; // Scan for opaque entries and assign them // positions following the non-opaque entries. for (int i = 0; i < alpha.length; i++) { if (alpha[i] == (byte)255) { PLTE_order[i] = newIndex++; } } // Reorder the palettes byte[] oldRed = PLTE_red; byte[] oldGreen = PLTE_green; byte[] oldBlue = PLTE_blue; int len = oldRed.length; // All have the same length PLTE_red = new byte[len]; PLTE_green = new byte[len]; PLTE_blue = new byte[len]; for (int i = 0; i < len; i++) { PLTE_red[PLTE_order[i]] = oldRed[i]; PLTE_green[PLTE_order[i]] = oldGreen[i]; PLTE_blue[PLTE_order[i]] = oldBlue[i]; } // Copy only the transparent entries into tRNS_alpha tRNS_alpha = new byte[numTransparent]; System.arraycopy(newAlpha, 0, tRNS_alpha, 0, numTransparent); } } } else { if (numBands == 1) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; } else if (numBands == 2) { IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; } else if (numBands == 3) { IHDR_colorType = PNGImageReader.PNG_COLOR_RGB; } else if (numBands == 4) { IHDR_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA; } else { throw new RuntimeException("Number of bands not 1-4!"); } } IHDR_present = true; } public boolean isReadOnly() { return false; } private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) { if (in == null) { return null; } else { ArrayList<byte[]> list = new ArrayList<byte[]>(in.size()); for (byte[] b: in) { list.add((b == null) ? null : (byte[])b.clone()); } return list; } } // Deep clone public Object clone() { PNGMetadata metadata; try { metadata = (PNGMetadata)super.clone(); } catch (CloneNotSupportedException e) { return null; } // unknownChunkData needs deep clone metadata.unknownChunkData = cloneBytesArrayList(this.unknownChunkData); return metadata; } public Node getAsTree(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return getNativeTree(); } else if (formatName.equals (IIOMetadataFormatImpl.standardMetadataFormatName)) { return getStandardTree(); } else { throw new IllegalArgumentException("Not a recognized format!"); } } private Node getNativeTree() { IIOMetadataNode node = null; // scratch node IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); // IHDR if (IHDR_present) { IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR"); IHDR_node.setAttribute("width", Integer.toString(IHDR_width)); IHDR_node.setAttribute("height", Integer.toString(IHDR_height)); IHDR_node.setAttribute("bitDepth", Integer.toString(IHDR_bitDepth)); IHDR_node.setAttribute("colorType", IHDR_colorTypeNames[IHDR_colorType]); // IHDR_compressionMethod must be 0 in PNG 1.1 IHDR_node.setAttribute("compressionMethod", IHDR_compressionMethodNames[IHDR_compressionMethod]); // IHDR_filterMethod must be 0 in PNG 1.1 IHDR_node.setAttribute("filterMethod", IHDR_filterMethodNames[IHDR_filterMethod]); IHDR_node.setAttribute("interlaceMethod", IHDR_interlaceMethodNames[IHDR_interlaceMethod]); root.appendChild(IHDR_node); } // PLTE if (PLTE_present) { IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE"); int numEntries = PLTE_red.length; for (int i = 0; i < numEntries; i++) { IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry"); entry.setAttribute("index", Integer.toString(i)); entry.setAttribute("red", Integer.toString(PLTE_red[i] & 0xff));