2046 lines · 83.9 KB · Java
RawDownload
/* * 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.