/* * Copyright (c) 2000, 2006, 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();Lines 1–500 of 1592. Load more
/* * Copyright (c) 2000, 2006, 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();