alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (PNGImageReader.java)

This example Java source code file (PNGImageReader.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, bad, bytearrayoutputstream, color, iioexception, image, imageio, indexoutofboundsexception, ioexception, iso\-8859\-1, plte, png_color_gray, png_color_gray_alpha, png_color_palette, png_color_rgb, png_color_rgb_alpha, point, string, util, zip

The PNGImageReader.java Java example source code

/*
 * Copyright (c) 2000, 2013, 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.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import com.sun.imageio.plugins.common.InputStreamAdapter;
import com.sun.imageio.plugins.common.ReaderUtil;
import com.sun.imageio.plugins.common.SubImageInputStream;
import java.io.ByteArrayOutputStream;
import sun.awt.image.ByteInterleavedRaster;

class PNGImageDataEnumeration implements Enumeration<InputStream> {

    boolean firstTime = true;
    ImageInputStream stream;
    int length;

    public PNGImageDataEnumeration(ImageInputStream stream)
        throws IOException {
        this.stream = stream;
        this.length = stream.readInt();
        int type = stream.readInt(); // skip chunk type
    }

    public InputStream nextElement() {
        try {
            firstTime = false;
            ImageInputStream iis = new SubImageInputStream(stream, length);
            return new InputStreamAdapter(iis);
        } catch (IOException e) {
            return null;
        }
    }

    public boolean hasMoreElements() {
        if (firstTime) {
            return true;
        }

        try {
            int crc = stream.readInt();
            this.length = stream.readInt();
            int type = stream.readInt();
            if (type == PNGImageReader.IDAT_TYPE) {
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            return false;
        }
    }
}

public class PNGImageReader extends ImageReader {

    /*
     * Note: The following chunk type constants are autogenerated.  Each
     * one is derived from the ASCII values of its 4-character name.  For
     * example, IHDR_TYPE is calculated as follows:
     *            ('I' << 24) | ('H' << 16) | ('D' << 8) | 'R'
     */

    // Critical chunks
    static final int IHDR_TYPE = 0x49484452;
    static final int PLTE_TYPE = 0x504c5445;
    static final int IDAT_TYPE = 0x49444154;
    static final int IEND_TYPE = 0x49454e44;

    // Ancillary chunks
    static final int bKGD_TYPE = 0x624b4744;
    static final int cHRM_TYPE = 0x6348524d;
    static final int gAMA_TYPE = 0x67414d41;
    static final int hIST_TYPE = 0x68495354;
    static final int iCCP_TYPE = 0x69434350;
    static final int iTXt_TYPE = 0x69545874;
    static final int pHYs_TYPE = 0x70485973;
    static final int sBIT_TYPE = 0x73424954;
    static final int sPLT_TYPE = 0x73504c54;
    static final int sRGB_TYPE = 0x73524742;
    static final int tEXt_TYPE = 0x74455874;
    static final int tIME_TYPE = 0x74494d45;
    static final int tRNS_TYPE = 0x74524e53;
    static final int zTXt_TYPE = 0x7a545874;

    static final int PNG_COLOR_GRAY = 0;
    static final int PNG_COLOR_RGB = 2;
    static final int PNG_COLOR_PALETTE = 3;
    static final int PNG_COLOR_GRAY_ALPHA = 4;
    static final int PNG_COLOR_RGB_ALPHA = 6;

    // The number of bands by PNG color type
    static final int[] inputBandsForColorType = {
         1, // gray
        -1, // unused
         3, // rgb
         1, // palette
         2, // gray + alpha
        -1, // unused
         4  // rgb + alpha
    };

    static final int PNG_FILTER_NONE = 0;
    static final int PNG_FILTER_SUB = 1;
    static final int PNG_FILTER_UP = 2;
    static final int PNG_FILTER_AVERAGE = 3;
    static final int PNG_FILTER_PAETH = 4;

    static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 };
    static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 };
    static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 };
    static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 };

    private static final boolean debug = true;

    ImageInputStream stream = null;

    boolean gotHeader = false;
    boolean gotMetadata = false;

    ImageReadParam lastParam = null;

    long imageStartPosition = -1L;

    Rectangle sourceRegion = null;
    int sourceXSubsampling = -1;
    int sourceYSubsampling = -1;
    int sourceMinProgressivePass = 0;
    int sourceMaxProgressivePass = 6;
    int[] sourceBands = null;
    int[] destinationBands = null;
    Point destinationOffset = new Point(0, 0);

    PNGMetadata metadata = new PNGMetadata();

    DataInputStream pixelStream = null;

    BufferedImage theImage = null;

    // The number of source pixels processed
    int pixelsDone = 0;

    // The total number of pixels in the source image
    int totalPixels;

    public PNGImageReader(ImageReaderSpi originatingProvider) {
        super(originatingProvider);
    }

    public void setInput(Object input,
                         boolean seekForwardOnly,
                         boolean ignoreMetadata) {
        super.setInput(input, seekForwardOnly, ignoreMetadata);
        this.stream = (ImageInputStream)input; // Always works

        // Clear all values based on the previous stream contents
        resetStreamSettings();
    }

    private String readNullTerminatedString(String charset, int maxLen) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int b;
        int count = 0;
        while ((maxLen > count++) && ((b = stream.read()) != 0)) {
            if (b == -1) throw new EOFException();
            baos.write(b);
        }
        return new String(baos.toByteArray(), charset);
    }

    private void readHeader() throws IIOException {
        if (gotHeader) {
            return;
        }
        if (stream == null) {
            throw new IllegalStateException("Input source not set!");
        }

        try {
            byte[] signature = new byte[8];
            stream.readFully(signature);

            if (signature[0] != (byte)137 ||
                signature[1] != (byte)80 ||
                signature[2] != (byte)78 ||
                signature[3] != (byte)71 ||
                signature[4] != (byte)13 ||
                signature[5] != (byte)10 ||
                signature[6] != (byte)26 ||
                signature[7] != (byte)10) {
                throw new IIOException("Bad PNG signature!");
            }

            int IHDR_length = stream.readInt();
            if (IHDR_length != 13) {
                throw new IIOException("Bad length for IHDR chunk!");
            }
            int IHDR_type = stream.readInt();
            if (IHDR_type != IHDR_TYPE) {
                throw new IIOException("Bad type for IHDR chunk!");
            }

            this.metadata = new PNGMetadata();

            int width = stream.readInt();
            int height = stream.readInt();

            // Re-use signature array to bulk-read these unsigned byte values
            stream.readFully(signature, 0, 5);
            int bitDepth          = signature[0] & 0xff;
            int colorType         = signature[1] & 0xff;
            int compressionMethod = signature[2] & 0xff;
            int filterMethod      = signature[3] & 0xff;
            int interlaceMethod   = signature[4] & 0xff;

            // Skip IHDR CRC
            stream.skipBytes(4);

            stream.flushBefore(stream.getStreamPosition());

            if (width == 0) {
                throw new IIOException("Image width == 0!");
            }
            if (height == 0) {
                throw new IIOException("Image height == 0!");
            }
            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
                bitDepth != 8 && bitDepth != 16) {
                throw new IIOException("Bit depth must be 1, 2, 4, 8, or 16!");
            }
            if (colorType != 0 && colorType != 2 && colorType != 3 &&
                colorType != 4 && colorType != 6) {
                throw new IIOException("Color type must be 0, 2, 3, 4, or 6!");
            }
            if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
                throw new IIOException("Bad color type/bit depth combination!");
            }
            if ((colorType == PNG_COLOR_RGB ||
                 colorType == PNG_COLOR_RGB_ALPHA ||
                 colorType == PNG_COLOR_GRAY_ALPHA) &&
                (bitDepth != 8 && bitDepth != 16)) {
                throw new IIOException("Bad color type/bit depth combination!");
            }
            if (compressionMethod != 0) {
                throw new IIOException("Unknown compression method (not 0)!");
            }
            if (filterMethod != 0) {
                throw new IIOException("Unknown filter method (not 0)!");
            }
            if (interlaceMethod != 0 && interlaceMethod != 1) {
                throw new IIOException("Unknown interlace method (not 0 or 1)!");
            }

            metadata.IHDR_present = true;
            metadata.IHDR_width = width;
            metadata.IHDR_height = height;
            metadata.IHDR_bitDepth = bitDepth;
            metadata.IHDR_colorType = colorType;
            metadata.IHDR_compressionMethod = compressionMethod;
            metadata.IHDR_filterMethod = filterMethod;
            metadata.IHDR_interlaceMethod = interlaceMethod;
            gotHeader = true;
        } catch (IOException e) {
            throw new IIOException("I/O error reading PNG header!", e);
        }
    }

    private void parse_PLTE_chunk(int chunkLength) throws IOException {
        if (metadata.PLTE_present) {
            processWarningOccurred(
"A PNG image may not contain more than one PLTE chunk.\n" +
"The chunk wil be ignored.");
            return;
        } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
                   metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
            processWarningOccurred(
"A PNG gray or gray alpha image cannot have a PLTE chunk.\n" +
"The chunk wil be ignored.");
            return;
        }

        byte[] palette = new byte[chunkLength];
        stream.readFully(palette);

        int numEntries = chunkLength/3;
        if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
            int maxEntries = 1 << metadata.IHDR_bitDepth;
            if (numEntries > maxEntries) {
                processWarningOccurred(
"PLTE chunk contains too many entries for bit depth, ignoring extras.");
                numEntries = maxEntries;
            }
            numEntries = Math.min(numEntries, maxEntries);
        }

        // Round array sizes up to 2^2^n
        int paletteEntries;
        if (numEntries > 16) {
            paletteEntries = 256;
        } else if (numEntries > 4) {
            paletteEntries = 16;
        } else if (numEntries > 2) {
            paletteEntries = 4;
        } else {
            paletteEntries = 2;
        }

        metadata.PLTE_present = true;
        metadata.PLTE_red = new byte[paletteEntries];
        metadata.PLTE_green = new byte[paletteEntries];
        metadata.PLTE_blue = new byte[paletteEntries];

        int index = 0;
        for (int i = 0; i < numEntries; i++) {
            metadata.PLTE_red[i] = palette[index++];
            metadata.PLTE_green[i] = palette[index++];
            metadata.PLTE_blue[i] = palette[index++];
        }
    }

    private void parse_bKGD_chunk() throws IOException {
        if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
            metadata.bKGD_colorType = PNG_COLOR_PALETTE;
            metadata.bKGD_index = stream.readUnsignedByte();
        } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
                   metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
            metadata.bKGD_colorType = PNG_COLOR_GRAY;
            metadata.bKGD_gray = stream.readUnsignedShort();
        } else { // RGB or RGB_ALPHA
            metadata.bKGD_colorType = PNG_COLOR_RGB;
            metadata.bKGD_red = stream.readUnsignedShort();
            metadata.bKGD_green = stream.readUnsignedShort();
            metadata.bKGD_blue = stream.readUnsignedShort();
        }

        metadata.bKGD_present = true;
    }

    private void parse_cHRM_chunk() throws IOException {
        metadata.cHRM_whitePointX = stream.readInt();
        metadata.cHRM_whitePointY = stream.readInt();
        metadata.cHRM_redX = stream.readInt();
        metadata.cHRM_redY = stream.readInt();
        metadata.cHRM_greenX = stream.readInt();
        metadata.cHRM_greenY = stream.readInt();
        metadata.cHRM_blueX = stream.readInt();
        metadata.cHRM_blueY = stream.readInt();

        metadata.cHRM_present = true;
    }

    private void parse_gAMA_chunk() throws IOException {
        int gamma = stream.readInt();
        metadata.gAMA_gamma = gamma;

        metadata.gAMA_present = true;
    }

    private void parse_hIST_chunk(int chunkLength) throws IOException,
        IIOException
    {
        if (!metadata.PLTE_present) {
            throw new IIOException("hIST chunk without prior PLTE chunk!");
        }

        /* According to PNG specification length of
         * hIST chunk is specified in bytes and
         * hIST chunk consists of 2 byte elements
         * (so we expect length is even).
         */
        metadata.hIST_histogram = new char[chunkLength/2];
        stream.readFully(metadata.hIST_histogram,
                         0, metadata.hIST_histogram.length);

        metadata.hIST_present = true;
    }

    private void parse_iCCP_chunk(int chunkLength) throws IOException {
        String keyword = readNullTerminatedString("ISO-8859-1", 80);
        metadata.iCCP_profileName = keyword;

        metadata.iCCP_compressionMethod = stream.readUnsignedByte();

        byte[] compressedProfile =
          new byte[chunkLength - keyword.length() - 2];
        stream.readFully(compressedProfile);
        metadata.iCCP_compressedProfile = compressedProfile;

        metadata.iCCP_present = true;
    }

    private void parse_iTXt_chunk(int chunkLength) throws IOException {
        long chunkStart = stream.getStreamPosition();

        String keyword = readNullTerminatedString("ISO-8859-1", 80);
        metadata.iTXt_keyword.add(keyword);

        int compressionFlag = stream.readUnsignedByte();
        metadata.iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag == 1));

        int compressionMethod = stream.readUnsignedByte();
        metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));

        String languageTag = readNullTerminatedString("UTF8", 80);
        metadata.iTXt_languageTag.add(languageTag);

        long pos = stream.getStreamPosition();
        int maxLen = (int)(chunkStart + chunkLength - pos);
        String translatedKeyword =
            readNullTerminatedString("UTF8", maxLen);
        metadata.iTXt_translatedKeyword.add(translatedKeyword);

        String text;
        pos = stream.getStreamPosition();
        byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
        stream.readFully(b);

        if (compressionFlag == 1) { // Decompress the text
            text = new String(inflate(b), "UTF8");
        } else {
            text = new String(b, "UTF8");
        }
        metadata.iTXt_text.add(text);
    }

    private void parse_pHYs_chunk() throws IOException {
        metadata.pHYs_pixelsPerUnitXAxis = stream.readInt();
        metadata.pHYs_pixelsPerUnitYAxis = stream.readInt();
        metadata.pHYs_unitSpecifier = stream.readUnsignedByte();

        metadata.pHYs_present = true;
    }

    private void parse_sBIT_chunk() throws IOException {
        int colorType = metadata.IHDR_colorType;
        if (colorType == PNG_COLOR_GRAY ||
            colorType == PNG_COLOR_GRAY_ALPHA) {
            metadata.sBIT_grayBits = stream.readUnsignedByte();
        } else if (colorType == PNG_COLOR_RGB ||
                   colorType == PNG_COLOR_PALETTE ||
                   colorType == PNG_COLOR_RGB_ALPHA) {
            metadata.sBIT_redBits = stream.readUnsignedByte();
            metadata.sBIT_greenBits = stream.readUnsignedByte();
            metadata.sBIT_blueBits = stream.readUnsignedByte();
        }

        if (colorType == PNG_COLOR_GRAY_ALPHA ||
            colorType == PNG_COLOR_RGB_ALPHA) {
            metadata.sBIT_alphaBits = stream.readUnsignedByte();
        }

        metadata.sBIT_colorType = colorType;
        metadata.sBIT_present = true;
    }

    private void parse_sPLT_chunk(int chunkLength)
        throws IOException, IIOException {
        metadata.sPLT_paletteName = readNullTerminatedString("ISO-8859-1", 80);
        chunkLength -= metadata.sPLT_paletteName.length() + 1;

        int sampleDepth = stream.readUnsignedByte();
        metadata.sPLT_sampleDepth = sampleDepth;

        int numEntries = chunkLength/(4*(sampleDepth/8) + 2);
        metadata.sPLT_red = new int[numEntries];
        metadata.sPLT_green = new int[numEntries];
        metadata.sPLT_blue = new int[numEntries];
        metadata.sPLT_alpha = new int[numEntries];
        metadata.sPLT_frequency = new int[numEntries];

        if (sampleDepth == 8) {
            for (int i = 0; i < numEntries; i++) {
                metadata.sPLT_red[i] = stream.readUnsignedByte();
                metadata.sPLT_green[i] = stream.readUnsignedByte();
                metadata.sPLT_blue[i] = stream.readUnsignedByte();
                metadata.sPLT_alpha[i] = stream.readUnsignedByte();
                metadata.sPLT_frequency[i] = stream.readUnsignedShort();
            }
        } else if (sampleDepth == 16) {
            for (int i = 0; i < numEntries; i++) {
                metadata.sPLT_red[i] = stream.readUnsignedShort();
                metadata.sPLT_green[i] = stream.readUnsignedShort();
                metadata.sPLT_blue[i] = stream.readUnsignedShort();
                metadata.sPLT_alpha[i] = stream.readUnsignedShort();
                metadata.sPLT_frequency[i] = stream.readUnsignedShort();
            }
        } else {
            throw new IIOException("sPLT sample depth not 8 or 16!");
        }

        metadata.sPLT_present = true;
    }

    private void parse_sRGB_chunk() throws IOException {
        metadata.sRGB_renderingIntent = stream.readUnsignedByte();

        metadata.sRGB_present = true;
    }

    private void parse_tEXt_chunk(int chunkLength) throws IOException {
        String keyword = readNullTerminatedString("ISO-8859-1", 80);
        metadata.tEXt_keyword.add(keyword);

        byte[] b = new byte[chunkLength - keyword.length() - 1];
        stream.readFully(b);
        metadata.tEXt_text.add(new String(b, "ISO-8859-1"));
    }

    private void parse_tIME_chunk() throws IOException {
        metadata.tIME_year = stream.readUnsignedShort();
        metadata.tIME_month = stream.readUnsignedByte();
        metadata.tIME_day = stream.readUnsignedByte();
        metadata.tIME_hour = stream.readUnsignedByte();
        metadata.tIME_minute = stream.readUnsignedByte();
        metadata.tIME_second = stream.readUnsignedByte();

        metadata.tIME_present = true;
    }

    private void parse_tRNS_chunk(int chunkLength) throws IOException {
        int colorType = metadata.IHDR_colorType;
        if (colorType == PNG_COLOR_PALETTE) {
            if (!metadata.PLTE_present) {
                processWarningOccurred(
"tRNS chunk without prior PLTE chunk, ignoring it.");
                return;
            }

            // Alpha table may have fewer entries than RGB palette
            int maxEntries = metadata.PLTE_red.length;
            int numEntries = chunkLength;
            if (numEntries > maxEntries) {
                processWarningOccurred(
"tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
                numEntries = maxEntries;
            }
            metadata.tRNS_alpha = new byte[numEntries];
            metadata.tRNS_colorType = PNG_COLOR_PALETTE;
            stream.read(metadata.tRNS_alpha, 0, numEntries);
            stream.skipBytes(chunkLength - numEntries);
        } else if (colorType == PNG_COLOR_GRAY) {
            if (chunkLength != 2) {
                processWarningOccurred(
"tRNS chunk for gray image must have length 2, ignoring chunk.");
                stream.skipBytes(chunkLength);
                return;
            }
            metadata.tRNS_gray = stream.readUnsignedShort();
            metadata.tRNS_colorType = PNG_COLOR_GRAY;
        } else if (colorType == PNG_COLOR_RGB) {
            if (chunkLength != 6) {
                processWarningOccurred(
"tRNS chunk for RGB image must have length 6, ignoring chunk.");
                stream.skipBytes(chunkLength);
                return;
            }
            metadata.tRNS_red = stream.readUnsignedShort();
            metadata.tRNS_green = stream.readUnsignedShort();
            metadata.tRNS_blue = stream.readUnsignedShort();
            metadata.tRNS_colorType = PNG_COLOR_RGB;
        } else {
            processWarningOccurred(
"Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it.");
            return;
        }

        metadata.tRNS_present = true;
    }

    private static byte[] inflate(byte[] b) throws IOException {
        InputStream bais = new ByteArrayInputStream(b);
        InputStream iis = new InflaterInputStream(bais);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        int c;
        try {
            while ((c = iis.read()) != -1) {
                baos.write(c);
            }
        } finally {
            iis.close();
        }
        return baos.toByteArray();
    }

    private void parse_zTXt_chunk(int chunkLength) throws IOException {
        String keyword = readNullTerminatedString("ISO-8859-1", 80);
        metadata.zTXt_keyword.add(keyword);

        int method = stream.readUnsignedByte();
        metadata.zTXt_compressionMethod.add(new Integer(method));

        byte[] b = new byte[chunkLength - keyword.length() - 2];
        stream.readFully(b);
        metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
    }

    private void readMetadata() throws IIOException {
        if (gotMetadata) {
            return;
        }

        readHeader();

        /*
         * Optimization: We can skip the remaining metadata if the
         * ignoreMetadata flag is set, and only if this is not a palette
         * image (in that case, we need to read the metadata to get the
         * tRNS chunk, which is needed for the getImageTypes() method).
         */
        int colorType = metadata.IHDR_colorType;
        if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) {
            try {
                while (true) {
                    int chunkLength = stream.readInt();
                    int chunkType = stream.readInt();

                    if (chunkType == IDAT_TYPE) {
                        // We've reached the image data
                        stream.skipBytes(-8);
                        imageStartPosition = stream.getStreamPosition();
                        break;
                    } else {
                        // Skip the chunk plus the 4 CRC bytes that follow
                        stream.skipBytes(chunkLength + 4);
                    }
                }
            } catch (IOException e) {
                throw new IIOException("Error skipping PNG metadata", e);
            }

            gotMetadata = true;
            return;
        }

        try {
            loop: while (true) {
                int chunkLength = stream.readInt();
                int chunkType = stream.readInt();
                int chunkCRC;

                // verify the chunk length
                if (chunkLength < 0) {
                    throw new IIOException("Invalid chunk lenght " + chunkLength);
                };

                try {
                    stream.mark();
                    stream.seek(stream.getStreamPosition() + chunkLength);
                    chunkCRC = stream.readInt();
                    stream.reset();
                } catch (IOException e) {
                    throw new IIOException("Invalid chunk length " + chunkLength);
                }

                switch (chunkType) {
                case IDAT_TYPE:
                    // If chunk type is 'IDAT', we've reached the image data.
                    stream.skipBytes(-8);
                    imageStartPosition = stream.getStreamPosition();
                    break loop;
                case PLTE_TYPE:
                    parse_PLTE_chunk(chunkLength);
                    break;
                case bKGD_TYPE:
                    parse_bKGD_chunk();
                    break;
                case cHRM_TYPE:
                    parse_cHRM_chunk();
                    break;
                case gAMA_TYPE:
                    parse_gAMA_chunk();
                    break;
                case hIST_TYPE:
                    parse_hIST_chunk(chunkLength);
                    break;
                case iCCP_TYPE:
                    parse_iCCP_chunk(chunkLength);
                    break;
                case iTXt_TYPE:
                    parse_iTXt_chunk(chunkLength);
                    break;
                case pHYs_TYPE:
                    parse_pHYs_chunk();
                    break;
                case sBIT_TYPE:
                    parse_sBIT_chunk();
                    break;
                case sPLT_TYPE:
                    parse_sPLT_chunk(chunkLength);
                    break;
                case sRGB_TYPE:
                    parse_sRGB_chunk();
                    break;
                case tEXt_TYPE:
                    parse_tEXt_chunk(chunkLength);
                    break;
                case tIME_TYPE:
                    parse_tIME_chunk();
                    break;
                case tRNS_TYPE:
                    parse_tRNS_chunk(chunkLength);
                    break;
                case zTXt_TYPE:
                    parse_zTXt_chunk(chunkLength);
                    break;
                default:
                    // Read an unknown chunk
                    byte[] b = new byte[chunkLength];
                    stream.readFully(b);

                    StringBuilder chunkName = new StringBuilder(4);
                    chunkName.append((char)(chunkType >>> 24));
                    chunkName.append((char)((chunkType >> 16) & 0xff));
                    chunkName.append((char)((chunkType >> 8) & 0xff));
                    chunkName.append((char)(chunkType & 0xff));

                    int ancillaryBit = chunkType >>> 28;
                    if (ancillaryBit == 0) {
                        processWarningOccurred(
"Encountered unknown chunk with critical bit set!");
                    }

                    metadata.unknownChunkType.add(chunkName.toString());
                    metadata.unknownChunkData.add(b);
                    break;
                }

                // double check whether all chunk data were consumed
                if (chunkCRC != stream.readInt()) {
                    throw new IIOException("Failed to read a chunk of type " +
                            chunkType);
                }
                stream.flushBefore(stream.getStreamPosition());
            }
        } catch (IOException e) {
            throw new IIOException("Error reading PNG metadata", e);
        }

        gotMetadata = true;
    }

    // Data filtering methods

    private static void decodeSubFilter(byte[] curr, int coff, int count,
                                        int bpp) {
        for (int i = bpp; i < count; i++) {
            int val;

            val = curr[i + coff] & 0xff;
            val += curr[i + coff - bpp] & 0xff;

            curr[i + coff] = (byte)val;
        }
    }

    private static void decodeUpFilter(byte[] curr, int coff,
                                       byte[] prev, int poff,
                                       int count) {
        for (int i = 0; i < count; i++) {
            int raw = curr[i + coff] & 0xff;
            int prior = prev[i + poff] & 0xff;

            curr[i + coff] = (byte)(raw + prior);
        }
    }

    private static void decodeAverageFilter(byte[] curr, int coff,
                                            byte[] prev, int poff,
                                            int count, int bpp) {
        int raw, priorPixel, priorRow;

        for (int i = 0; i < bpp; i++) {
            raw = curr[i + coff] & 0xff;
            priorRow = prev[i + poff] & 0xff;

            curr[i + coff] = (byte)(raw + priorRow/2);
        }

        for (int i = bpp; i < count; i++) {
            raw = curr[i + coff] & 0xff;
            priorPixel = curr[i + coff - bpp] & 0xff;
            priorRow = prev[i + poff] & 0xff;

            curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2);
        }
    }

    private static int paethPredictor(int a, int b, int c) {
        int p = a + b - c;
        int pa = Math.abs(p - a);
        int pb = Math.abs(p - b);
        int pc = Math.abs(p - c);

        if ((pa <= pb) && (pa <= pc)) {
            return a;
        } else if (pb <= pc) {
            return b;
        } else {
            return c;
        }
    }

    private static void decodePaethFilter(byte[] curr, int coff,
                                          byte[] prev, int poff,
                                          int count, int bpp) {
        int raw, priorPixel, priorRow, priorRowPixel;

        for (int i = 0; i < bpp; i++) {
            raw = curr[i + coff] & 0xff;
            priorRow = prev[i + poff] & 0xff;

            curr[i + coff] = (byte)(raw + priorRow);
        }

        for (int i = bpp; i < count; i++) {
            raw = curr[i + coff] & 0xff;
            priorPixel = curr[i + coff - bpp] & 0xff;
            priorRow = prev[i + poff] & 0xff;
            priorRowPixel = prev[i + poff - bpp] & 0xff;

            curr[i + coff] = (byte)(raw + paethPredictor(priorPixel,
                                                         priorRow,
                                                         priorRowPixel));
        }
    }

    private static final int[][] bandOffsets = {
        null,
        { 0 }, // G
        { 0, 1 }, // GA in GA order
        { 0, 1, 2 }, // RGB in RGB order
        { 0, 1, 2, 3 } // RGBA in RGBA order
    };

    private WritableRaster createRaster(int width, int height, int bands,
                                        int scanlineStride,
                                        int bitDepth) {

        DataBuffer dataBuffer;
        WritableRaster ras = null;
        Point origin = new Point(0, 0);
        if ((bitDepth < 8) && (bands == 1)) {
            dataBuffer = new DataBufferByte(height*scanlineStride);
            ras = Raster.createPackedRaster(dataBuffer,
                                            width, height,
                                            bitDepth,
                                            origin);
        } else if (bitDepth <= 8) {
            dataBuffer = new DataBufferByte(height*scanlineStride);
            ras = Raster.createInterleavedRaster(dataBuffer,
                                                 width, height,
                                                 scanlineStride,
                                                 bands,
                                                 bandOffsets[bands],
                                                 origin);
        } else {
            dataBuffer = new DataBufferUShort(height*scanlineStride);
            ras = Raster.createInterleavedRaster(dataBuffer,
                                                 width, height,
                                                 scanlineStride,
                                                 bands,
                                                 bandOffsets[bands],
                                                 origin);
        }

        return ras;
    }

    private void skipPass(int passWidth, int passHeight)
        throws IOException, IIOException  {
        if ((passWidth == 0) || (passHeight == 0)) {
            return;
        }

        int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
        int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8;

        // Read the image row-by-row
        for (int srcY = 0; srcY < passHeight; srcY++) {
            // Skip filter byte and the remaining row bytes
            pixelStream.skipBytes(1 + bytesPerRow);

            // If read has been aborted, just return
            // processReadAborted will be called later
            if (abortRequested()) {
                return;
            }
        }
    }

    private void updateImageProgress(int newPixels) {
        pixelsDone += newPixels;
        processImageProgress(100.0F*pixelsDone/totalPixels);
    }

    private void decodePass(int passNum,
                            int xStart, int yStart,
                            int xStep, int yStep,
                            int passWidth, int passHeight) throws IOException {

        if ((passWidth == 0) || (passHeight == 0)) {
            return;
        }

        WritableRaster imRas = theImage.getWritableTile(0, 0);
        int dstMinX = imRas.getMinX();
        int dstMaxX = dstMinX + imRas.getWidth() - 1;
        int dstMinY = imRas.getMinY();
        int dstMaxY = dstMinY + imRas.getHeight() - 1;

        // Determine which pixels will be updated in this pass
        int[] vals =
          ReaderUtil.computeUpdatedPixels(sourceRegion,
                                          destinationOffset,
                                          dstMinX, dstMinY,
                                          dstMaxX, dstMaxY,
                                          sourceXSubsampling,
                                          sourceYSubsampling,
                                          xStart, yStart,
                                          passWidth, passHeight,
                                          xStep, yStep);
        int updateMinX = vals[0];
        int updateMinY = vals[1];
        int updateWidth = vals[2];
        int updateXStep = vals[4];
        int updateYStep = vals[5];

        int bitDepth = metadata.IHDR_bitDepth;
        int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
        int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
        bytesPerPixel *= inputBands;

        int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
        int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;

        // If no pixels need updating, just skip the input data
        if (updateWidth == 0) {
            for (int srcY = 0; srcY < passHeight; srcY++) {
                // Update count of pixels read
                updateImageProgress(passWidth);
                // Skip filter byte and the remaining row bytes
                pixelStream.skipBytes(1 + bytesPerRow);
            }
            return;
        }

        // Backwards map from destination pixels
        // (dstX = updateMinX + k*updateXStep)
        // to source pixels (sourceX), and then
        // to offset and skip in passRow (srcX and srcXStep)
        int sourceX =
            (updateMinX - destinationOffset.x)*sourceXSubsampling +
            sourceRegion.x;
        int srcX = (sourceX - xStart)/xStep;

        // Compute the step factor in the source
        int srcXStep = updateXStep*sourceXSubsampling/xStep;

        byte[] byteData = null;
        short[] shortData = null;
        byte[] curr = new byte[bytesPerRow];
        byte[] prior = new byte[bytesPerRow];

        // Create a 1-row tall Raster to hold the data
        WritableRaster passRow = createRaster(passWidth, 1, inputBands,
                                              eltsPerRow,
                                              bitDepth);

        // Create an array suitable for holding one pixel
        int[] ps = passRow.getPixel(0, 0, (int[])null);

        DataBuffer dataBuffer = passRow.getDataBuffer();
        int type = dataBuffer.getDataType();
        if (type == DataBuffer.TYPE_BYTE) {
            byteData = ((DataBufferByte)dataBuffer).getData();
        } else {
            shortData = ((DataBufferUShort)dataBuffer).getData();
        }

        processPassStarted(theImage,
                           passNum,
                           sourceMinProgressivePass,
                           sourceMaxProgressivePass,
                           updateMinX, updateMinY,
                           updateXStep, updateYStep,
                           destinationBands);

        // Handle source and destination bands
        if (sourceBands != null) {
            passRow = passRow.createWritableChild(0, 0,
                                                  passRow.getWidth(), 1,
                                                  0, 0,
                                                  sourceBands);
        }
        if (destinationBands != null) {
            imRas = imRas.createWritableChild(0, 0,
                                              imRas.getWidth(),
                                              imRas.getHeight(),
                                              0, 0,
                                              destinationBands);
        }

        // Determine if all of the relevant output bands have the
        // same bit depth as the source data
        boolean adjustBitDepths = false;
        int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
        int numBands = outputSampleSize.length;
        for (int b = 0; b < numBands; b++) {
            if (outputSampleSize[b] != bitDepth) {
                adjustBitDepths = true;
                break;
            }
        }

        // If the bit depths differ, create a lookup table per band to perform
        // the conversion
        int[][] scale = null;
        if (adjustBitDepths) {
            int maxInSample = (1 << bitDepth) - 1;
            int halfMaxInSample = maxInSample/2;
            scale = new int[numBands][];
            for (int b = 0; b < numBands; b++) {
                int maxOutSample = (1 << outputSampleSize[b]) - 1;
                scale[b] = new int[maxInSample + 1];
                for (int s = 0; s <= maxInSample; s++) {
                    scale[b][s] =
                        (s*maxOutSample + halfMaxInSample)/maxInSample;
                }
            }
        }

        // Limit passRow to relevant area for the case where we
        // will can setRect to copy a contiguous span
        boolean useSetRect = srcXStep == 1 &&
            updateXStep == 1 &&
            !adjustBitDepths &&
            (imRas instanceof ByteInterleavedRaster);

        if (useSetRect) {
            passRow = passRow.createWritableChild(srcX, 0,
                                                  updateWidth, 1,
                                                  0, 0,
                                                  null);
        }

        // Decode the (sub)image row-by-row
        for (int srcY = 0; srcY < passHeight; srcY++) {
            // Update count of pixels read
            updateImageProgress(passWidth);

            // Read the filter type byte and a row of data
            int filter = pixelStream.read();
            try {
                // Swap curr and prior
                byte[] tmp = prior;
                prior = curr;
                curr = tmp;

                pixelStream.readFully(curr, 0, bytesPerRow);
            } catch (java.util.zip.ZipException ze) {
                // TODO - throw a more meaningful exception
                throw ze;
            }

            switch (filter) {
            case PNG_FILTER_NONE:
                break;
            case PNG_FILTER_SUB:
                decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
                break;
            case PNG_FILTER_UP:
                decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
                break;
            case PNG_FILTER_AVERAGE:
                decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
                                    bytesPerPixel);
                break;
            case PNG_FILTER_PAETH:
                decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
                                  bytesPerPixel);
                break;
            default:
                throw new IIOException("Unknown row filter type (= " +
                                       filter + ")!");
            }

            // Copy data into passRow byte by byte
            if (bitDepth < 16) {
                System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
            } else {
                int idx = 0;
                for (int j = 0; j < eltsPerRow; j++) {
                    shortData[j] =
                        (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
                    idx += 2;
                }
            }

            // True Y position in source
            int sourceY = srcY*yStep + yStart;
            if ((sourceY >= sourceRegion.y) &&
                (sourceY < sourceRegion.y + sourceRegion.height) &&
                (((sourceY - sourceRegion.y) %
                  sourceYSubsampling) == 0)) {

                int dstY = destinationOffset.y +
                    (sourceY - sourceRegion.y)/sourceYSubsampling;
                if (dstY < dstMinY) {
                    continue;
                }
                if (dstY > dstMaxY) {
                    break;
                }

                if (useSetRect) {
                    imRas.setRect(updateMinX, dstY, passRow);
                } else {
                    int newSrcX = srcX;

                    for (int dstX = updateMinX;
                         dstX < updateMinX + updateWidth;
                         dstX += updateXStep) {

                        passRow.getPixel(newSrcX, 0, ps);
                        if (adjustBitDepths) {
                            for (int b = 0; b < numBands; b++) {
                                ps[b] = scale[b][ps[b]];
                            }
                        }
                        imRas.setPixel(dstX, dstY, ps);
                        newSrcX += srcXStep;
                    }
                }

                processImageUpdate(theImage,
                                   updateMinX, dstY,
                                   updateWidth, 1,
                                   updateXStep, updateYStep,
                                   destinationBands);

                // If read has been aborted, just return
                // processReadAborted will be called later
                if (abortRequested()) {
                    return;
                }
            }
        }

        processPassComplete(theImage);
    }

    private void decodeImage()
        throws IOException, IIOException  {
        int width = metadata.IHDR_width;
        int height = metadata.IHDR_height;

        this.pixelsDone = 0;
        this.totalPixels = width*height;

        clearAbortRequest();

        if (metadata.IHDR_interlaceMethod == 0) {
            decodePass(0, 0, 0, 1, 1, width, height);
        } else {
            for (int i = 0; i <= sourceMaxProgressivePass; i++) {
                int XOffset = adam7XOffset[i];
                int YOffset = adam7YOffset[i];
                int XSubsampling = adam7XSubsampling[i];
                int YSubsampling = adam7YSubsampling[i];
                int xbump = adam7XSubsampling[i + 1] - 1;
                int ybump = adam7YSubsampling[i + 1] - 1;

                if (i >= sourceMinProgressivePass) {
                    decodePass(i,
                               XOffset,
                               YOffset,
                               XSubsampling,
                               YSubsampling,
                               (width + xbump)/XSubsampling,
                               (height + ybump)/YSubsampling);
                } else {
                    skipPass((width + xbump)/XSubsampling,
                             (height + ybump)/YSubsampling);
                }

                // If read has been aborted, just return
                // processReadAborted will be called later
                if (abortRequested()) {
                    return;
                }
            }
        }
    }

    private void readImage(ImageReadParam param) throws IIOException {
        readMetadata();

        int width = metadata.IHDR_width;
        int height = metadata.IHDR_height;

        // Init default values
        sourceXSubsampling = 1;
        sourceYSubsampling = 1;
        sourceMinProgressivePass = 0;
        sourceMaxProgressivePass = 6;
        sourceBands = null;
        destinationBands = null;
        destinationOffset = new Point(0, 0);

        // If an ImageReadParam is available, get values from it
        if (param != null) {
            sourceXSubsampling = param.getSourceXSubsampling();
            sourceYSubsampling = param.getSourceYSubsampling();

            sourceMinProgressivePass =
                Math.max(param.getSourceMinProgressivePass(), 0);
            sourceMaxProgressivePass =
                Math.min(param.getSourceMaxProgressivePass(), 6);

            sourceBands = param.getSourceBands();
            destinationBands = param.getDestinationBands();
            destinationOffset = param.getDestinationOffset();
        }
        Inflater inf = null;
        try {
            stream.seek(imageStartPosition);

            Enumeration<InputStream> e = new PNGImageDataEnumeration(stream);
            InputStream is = new SequenceInputStream(e);

           /* InflaterInputStream uses an Inflater instance which consumes
            * native (non-GC visible) resources. This is normally implicitly
            * freed when the stream is closed. However since the
            * InflaterInputStream wraps a client-supplied input stream,
            * we cannot close it.
            * But the app may depend on GC finalization to close the stream.
            * Therefore to ensure timely freeing of native resources we
            * explicitly create the Inflater instance and free its resources
            * when we are done with the InflaterInputStream by calling
            * inf.end();
            */
            inf = new Inflater();
            is = new InflaterInputStream(is, inf);
            is = new BufferedInputStream(is);
            this.pixelStream = new DataInputStream(is);

            /*
             * NB: the PNG spec declares that valid range for width
             * and height is [1, 2^31-1], so here we may fail to allocate
             * a buffer for destination image due to memory limitation.
             *
             * However, the recovery strategy for this case should be
             * defined on the level of application, so we will not
             * try to estimate the required amount of the memory and/or
             * handle OOM in any way.
             */
            theImage = getDestination(param,
                                      getImageTypes(0),
                                      width,
                                      height);

            Rectangle destRegion = new Rectangle(0, 0, 0, 0);
            sourceRegion = new Rectangle(0, 0, 0, 0);
            computeRegions(param, width, height,
                           theImage,
                           sourceRegion, destRegion);
            destinationOffset.setLocation(destRegion.getLocation());

            // At this point the header has been read and we know
            // how many bands are in the image, so perform checking
            // of the read param.
            int colorType = metadata.IHDR_colorType;
            checkReadParamBandSettings(param,
                                       inputBandsForColorType[colorType],
                                      theImage.getSampleModel().getNumBands());

            processImageStarted(0);
            decodeImage();
            if (abortRequested()) {
                processReadAborted();
            } else {
                processImageComplete();
            }
        } catch (IOException e) {
            throw new IIOException("Error reading PNG image data", e);
        } finally {
            if (inf != null) {
                inf.end();
            }
        }
    }

    public int getNumImages(boolean allowSearch) throws IIOException {
        if (stream == null) {
            throw new IllegalStateException("No input source set!");
        }
        if (seekForwardOnly && allowSearch) {
            throw new IllegalStateException
                ("seekForwardOnly and allowSearch can't both be true!");
        }
        return 1;
    }

    public int getWidth(int imageIndex) throws IIOException {
        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException("imageIndex != 0!");
        }

        readHeader();

        return metadata.IHDR_width;
    }

    public int getHeight(int imageIndex) throws IIOException {
        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException("imageIndex != 0!");
        }

        readHeader();

        return metadata.IHDR_height;
    }

    public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
      throws IIOException
    {
        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException("imageIndex != 0!");
        }

        readHeader();

        ArrayList<ImageTypeSpecifier> l =
            new ArrayList<ImageTypeSpecifier>(1);

        ColorSpace rgb;
        ColorSpace gray;
        int[] bandOffsets;

        int bitDepth = metadata.IHDR_bitDepth;
        int colorType = metadata.IHDR_colorType;

        int dataType;
        if (bitDepth <= 8) {
            dataType = DataBuffer.TYPE_BYTE;
        } else {
            dataType = DataBuffer.TYPE_USHORT;
        }

        switch (colorType) {
        case PNG_COLOR_GRAY:
            // Packed grayscale
            l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
                                                     dataType,
                                                     false));
            break;

        case PNG_COLOR_RGB:
            if (bitDepth == 8) {
                // some standard types of buffered images
                // which can be used as destination
                l.add(ImageTypeSpecifier.createFromBufferedImageType(
                          BufferedImage.TYPE_3BYTE_BGR));

                l.add(ImageTypeSpecifier.createFromBufferedImageType(
                          BufferedImage.TYPE_INT_RGB));

                l.add(ImageTypeSpecifier.createFromBufferedImageType(
                          BufferedImage.TYPE_INT_BGR));

            }
            // Component R, G, B
            rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            bandOffsets = new int[3];
            bandOffsets[0] = 0;
            bandOffsets[1] = 1;
            bandOffsets[2] = 2;
            l.add(ImageTypeSpecifier.createInterleaved(rgb,
                                                       bandOffsets,
                                                       dataType,
                                                       false,
                                                       false));
            break;

        case PNG_COLOR_PALETTE:
            readMetadata(); // Need tRNS chunk

            /*
             * The PLTE chunk spec says:
             *
             * The number of palette entries must not exceed the range that
             * can be represented in the image bit depth (for example, 2^4 = 16
             * for a bit depth of 4). It is permissible to have fewer entries
             * than the bit depth would allow. In that case, any out-of-range
             * pixel value found in the image data is an error.
             *
             * http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.PLTE
             *
             * Consequently, the case when the palette length is smaller than
             * 2^bitDepth is legal in the view of PNG spec.
             *
             * However the spec of createIndexed() method demands the exact
             * equality of the palette lengh and number of possible palette
             * entries (2^bitDepth).
             *
             * {@link javax.imageio.ImageTypeSpecifier.html#createIndexed}
             *
             * In order to avoid this contradiction we need to extend the
             * palette arrays to the limit defined by the bitDepth.
             */

            int plength = 1 << bitDepth;

            byte[] red = metadata.PLTE_red;
            byte[] green = metadata.PLTE_green;
            byte[] blue = metadata.PLTE_blue;

            if (metadata.PLTE_red.length < plength) {
                red = Arrays.copyOf(metadata.PLTE_red, plength);
                Arrays.fill(red, metadata.PLTE_red.length, plength,
                            metadata.PLTE_red[metadata.PLTE_red.length - 1]);

                green = Arrays.copyOf(metadata.PLTE_green, plength);
                Arrays.fill(green, metadata.PLTE_green.length, plength,
                            metadata.PLTE_green[metadata.PLTE_green.length - 1]);

                blue = Arrays.copyOf(metadata.PLTE_blue, plength);
                Arrays.fill(blue, metadata.PLTE_blue.length, plength,
                            metadata.PLTE_blue[metadata.PLTE_blue.length - 1]);

            }

            // Alpha from tRNS chunk may have fewer entries than
            // the RGB LUTs from the PLTE chunk; if so, pad with
            // 255.
            byte[] alpha = null;
            if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) {
                if (metadata.tRNS_alpha.length == red.length) {
                    alpha = metadata.tRNS_alpha;
                } else {
                    alpha = Arrays.copyOf(metadata.tRNS_alpha, red.length);
                    Arrays.fill(alpha,
                                metadata.tRNS_alpha.length,
                                red.length, (byte)255);
                }
            }

            l.add(ImageTypeSpecifier.createIndexed(red, green,
                                                   blue, alpha,
                                                   bitDepth,
                                                   DataBuffer.TYPE_BYTE));
            break;

        case PNG_COLOR_GRAY_ALPHA:
            // Component G, A
            gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            bandOffsets = new int[2];
            bandOffsets[0] = 0;
            bandOffsets[1] = 1;
            l.add(ImageTypeSpecifier.createInterleaved(gray,
                                                       bandOffsets,
                                                       dataType,
                                                       true,
                                                       false));
            break;

        case PNG_COLOR_RGB_ALPHA:
            if (bitDepth == 8) {
                // some standard types of buffered images
                // wich can be used as destination
                l.add(ImageTypeSpecifier.createFromBufferedImageType(
                          BufferedImage.TYPE_4BYTE_ABGR));

                l.add(ImageTypeSpecifier.createFromBufferedImageType(
                          BufferedImage.TYPE_INT_ARGB));
            }

            // Component R, G, B, A (non-premultiplied)
            rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            bandOffsets = new int[4];
            bandOffsets[0] = 0;
            bandOffsets[1] = 1;
            bandOffsets[2] = 2;
            bandOffsets[3] = 3;

            l.add(ImageTypeSpecifier.createInterleaved(rgb,
                                                       bandOffsets,
                                                       dataType,
                                                       true,
                                                       false));
            break;

        default:
            break;
        }

        return l.iterator();
    }

    /*
     * Super class implementation uses first element
     * of image types list as raw image type.
     *
     * Also, super implementation uses first element of this list
     * as default destination type image read param does not specify
     * anything other.
     *
     * However, in case of RGB and RGBA color types, raw image type
     * produces buffered image of custom type. It causes some
     * performance degradation of subsequent rendering operations.
     *
     * To resolve this contradiction we put standard image types
     * at the first positions of image types list (to produce standard
     * images by default) and put raw image type (which is custom)
     * at the last position of this list.
     *
     * After this changes we should override getRawImageType()
     * to return last element of image types list.
     */
    public ImageTypeSpecifier getRawImageType(int imageIndex)
      throws IOException {

        Iterator<ImageTypeSpecifier> types = getImageTypes(imageIndex);
        ImageTypeSpecifier raw = null;
        do {
            raw = types.next();
        } while (types.hasNext());
        return raw;
    }

    public ImageReadParam getDefaultReadParam() {
        return new ImageReadParam();
    }

    public IIOMetadata getStreamMetadata()
        throws IIOException {
        return null;
    }

    public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException("imageIndex != 0!");
        }
        readMetadata();
        return metadata;
    }

    public BufferedImage read(int imageIndex, ImageReadParam param)
        throws IIOException {
        if (imageIndex != 0) {
            throw new IndexOutOfBoundsException("imageIndex != 0!");
        }

        readImage(param);
        return theImage;
    }

    public void reset() {
        super.reset();
        resetStreamSettings();
    }

    private void resetStreamSettings() {
        gotHeader = false;
        gotMetadata = false;
        metadata = null;
        pixelStream = null;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java PNGImageReader.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.