Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
JDK 17 java.desktop.jmod - Desktop Module
JDK 17 java.desktop.jmod is the JMOD file for JDK 17 Desktop module.
JDK 17 Desktop module compiled class files are stored in \fyicenter\jdk-17.0.5\jmods\java.desktop.jmod.
JDK 17 Desktop module compiled class files are also linked and stored in the \fyicenter\jdk-17.0.5\lib\modules JImage file.
JDK 17 Desktop module source code files are stored in \fyicenter\jdk-17.0.5\lib\src.zip\java.desktop.
You can click and view the content of each source code file in the list below.
✍: FYIcenter
⏎ com/sun/imageio/plugins/png/PNGImageReader.java
/* * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ 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 } @Override public InputStream nextElement() { try { firstTime = false; ImageInputStream iis = new SubImageInputStream(stream, length); return new InputStreamAdapter(iis); } catch (IOException e) { return null; } } @Override 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); } @Override 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 = 0; int count = 0; while ((maxLen > count++) && ((b = stream.read()) != 0)) { if (b == -1) throw new EOFException(); baos.write(b); } if (b != 0) { throw new IIOException("Found non null terminated string"); } 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); int compressedProfileLength = chunkLength - keyword.length() - 2; if (compressedProfileLength <= 0) { throw new IIOException("iCCP chunk length is not proper"); } metadata.iCCP_profileName = keyword; metadata.iCCP_compressionMethod = stream.readUnsignedByte(); byte[] compressedProfile = new byte[compressedProfileLength]; 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)); long pos = stream.getStreamPosition(); int remainingLen = (int)(chunkStart + chunkLength - pos); String languageTag = readNullTerminatedString("UTF8", remainingLen); metadata.iTXt_languageTag.add(languageTag); pos = stream.getStreamPosition(); remainingLen = (int)(chunkStart + chunkLength - pos); if (remainingLen < 0) { throw new IIOException("iTXt chunk length is not proper"); } String translatedKeyword = readNullTerminatedString("UTF8", remainingLen); metadata.iTXt_translatedKeyword.add(translatedKeyword); String text; pos = stream.getStreamPosition(); int textLength = (int)(chunkStart + chunkLength - pos); if (textLength < 0) { throw new IIOException("iTXt chunk length is not proper"); } byte[] b = new byte[textLength]; 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); // Check if the text chunk contains image creation time if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) { // Update Standard/Document/ImageCreationTime from text chunk int index = metadata.iTXt_text.size() - 1; metadata.decodeImageCreationTimeFromTextChunk( metadata.iTXt_text.listIterator(index)); } } 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); int remainingChunkLength = chunkLength - (metadata.sPLT_paletteName.length() + 1); if (remainingChunkLength <= 0) { throw new IIOException("sPLT chunk length is not proper"); } int sampleDepth = stream.readUnsignedByte(); metadata.sPLT_sampleDepth = sampleDepth; int numEntries = remainingChunkLength/(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); int textLength = chunkLength - keyword.length() - 1; if (textLength < 0) { throw new IIOException("tEXt chunk length is not proper"); } metadata.tEXt_keyword.add(keyword); byte[] b = new byte[textLength]; stream.readFully(b); metadata.tEXt_text.add(new String(b, "ISO-8859-1")); // Check if the text chunk contains image creation time if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) { // Update Standard/Document/ImageCreationTime from text chunk int index = metadata.tEXt_text.size() - 1; metadata.decodeImageCreationTimeFromTextChunk( metadata.tEXt_text.listIterator(index)); } } 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 && maxEntries > 0) { 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); int textLength = chunkLength - keyword.length() - 2; if (textLength < 0) { throw new IIOException("zTXt chunk length is not proper"); } metadata.zTXt_keyword.add(keyword); int method = stream.readUnsignedByte(); metadata.zTXt_compressionMethod.add(method); byte[] b = new byte[textLength]; stream.readFully(b); metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1")); // Check if the text chunk contains image creation time if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) { // Update Standard/Document/ImageCreationTime from text chunk int index = metadata.zTXt_text.size() - 1; metadata.decodeImageCreationTimeFromTextChunk( metadata.zTXt_text.listIterator(index)); } } private void readMetadata() throws IIOException { if (gotMetadata) { return; } readHeader(); /* * Optimization: We can skip reading metadata if ignoreMetadata * flag is set and colorType is not PNG_COLOR_PALETTE. However, * we parse tRNS chunk to retrieve the transparent color from the * metadata. Doing so, helps PNGImageReader to appropriately * identify and set transparent pixels in the decoded image for * colorType PNG_COLOR_RGB and PNG_COLOR_GRAY. */ int colorType = metadata.IHDR_colorType; if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) { try { while (true) { int chunkLength = stream.readInt(); // verify the chunk length first if (chunkLength < 0 || chunkLength + 4 < 0) { throw new IIOException("Invalid chunk length " + chunkLength); } int chunkType = stream.readInt(); if (chunkType == IDAT_TYPE) { // We've reached the first IDAT chunk position stream.skipBytes(-8); imageStartPosition = stream.getStreamPosition(); /* * According to PNG specification tRNS chunk must * precede the first IDAT chunk. So we can stop * reading metadata. */ break; } else if (chunkType == tRNS_TYPE) { parse_tRNS_chunk(chunkLength); // After parsing tRNS chunk we will skip 4 CRC bytes stream.skipBytes(4); } 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(); // Initialize chunkCRC, value assigned has no significance int chunkCRC = -1; // verify the chunk length if (chunkLength < 0) { throw new IIOException("Invalid chunk length " + chunkLength); }; try { /* * As per PNG specification all chunks should have * 4 byte CRC. But there are some images where * CRC is not present/corrupt for IEND chunk. * And these type of images are supported by other * decoders. So as soon as we hit chunk type * for IEND chunk stop reading metadata. */ if (chunkType != IEND_TYPE) { 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. if (imageStartPosition == -1L) { /* * The PNG specification mandates that if colorType is * PNG_COLOR_PALETTE then the PLTE chunk should appear * before the first IDAT chunk. */ if (colorType == PNG_COLOR_PALETTE && !(metadata.PLTE_present)) { throw new IIOException("Required PLTE chunk" + " missing"); } /* * PNGs may contain multiple IDAT chunks containing * a portion of image data. We store the position of * the first IDAT chunk and continue with iteration * of other chunks that follow image data. */ imageStartPosition = stream.getStreamPosition() - 8; } // Move to the CRC byte location. stream.skipBytes(chunkLength); break; case IEND_TYPE: /* * If the chunk type is 'IEND', we've reached end of image. * Seek to the first IDAT chunk for subsequent decoding. */ stream.seek(imageStartPosition); /* * flushBefore discards the portion of the stream before * the indicated position. Hence this should be used after * we complete iteration over available chunks including * those that appear after the IDAT. */ stream.flushBefore(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: if (ignoreMetadata) { stream.skipBytes(chunkLength); } else { 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: if (ignoreMetadata) { stream.skipBytes(chunkLength); } else { 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); } } } 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 bitsPerRow = Math. multiplyExact((inputBands * metadata.IHDR_bitDepth), passWidth); int bytesPerRow = (bitsPerRow + 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); } } 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 bitsPerRow = Math.multiplyExact((inputBands * bitDepth), passWidth); int bytesPerRow = (bitsPerRow + 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); /* * If read has been aborted, just return * processReadAborted will be called later */ if (abortRequested()) { return; } // 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(); for (int b = 0; b < inputBands; 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[inputBands][]; for (int b = 0; b < inputBands; 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); /* * If read has been aborted, just return * processReadAborted will be called later */ if (abortRequested()) { return; } // 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; } /* * For PNG images of color type PNG_COLOR_RGB or PNG_COLOR_GRAY * that contain a specific transparent color (given by tRNS * chunk), we compare the decoded pixel color with the color * given by tRNS chunk to set the alpha on the destination. */ boolean tRNSTransparentPixelPresent = theImage.getSampleModel().getNumBands() == inputBands + 1 && metadata.hasTransparentColor(); if (useSetRect && !tRNSTransparentPixelPresent) { imRas.setRect(updateMinX, dstY, passRow); } else { int newSrcX = srcX; /* * Create intermediate array to fill the extra alpha * channel when tRNSTransparentPixelPresent is true. */ final int[] temp = new int[inputBands + 1]; final int opaque = (bitDepth < 16) ? 255 : 65535; for (int dstX = updateMinX; dstX < updateMinX + updateWidth; dstX += updateXStep) { passRow.getPixel(newSrcX, 0, ps); if (adjustBitDepths) { for (int b = 0; b < inputBands; b++) { ps[b] = scale[b][ps[b]]; } } if (tRNSTransparentPixelPresent) { if (metadata.tRNS_colorType == PNG_COLOR_RGB) { temp[0] = ps[0]; temp[1] = ps[1]; temp[2] = ps[2]; if (ps[0] == metadata.tRNS_red && ps[1] == metadata.tRNS_green && ps[2] == metadata.tRNS_blue) { temp[3] = 0; } else { temp[3] = opaque; } } else { // when tRNS_colorType is PNG_COLOR_GRAY temp[0] = ps[0]; if (ps[0] == metadata.tRNS_gray) { temp[1] = 0; } else { temp[1] = opaque; } } imRas.setPixel(dstX, dstY, temp); } else { imRas.setPixel(dstX, dstY, ps); } newSrcX += srcXStep; } } processImageUpdate(theImage, updateMinX, dstY, updateWidth, 1, updateXStep, updateYStep, destinationBands); } } 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; 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; if ((long)width * height > Integer.MAX_VALUE - 2) { // We are not able to properly decode image that has number // of pixels greater than Integer.MAX_VALUE - 2 throw new IIOException("Can not read image of the size " + width + " by " + 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); /* * 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. * * If the read operation triggers OutOfMemoryError, the same * will be wrapped in an IIOException at PNGImageReader.read * method. * * The recovery strategy for this case should be defined at * 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; if (theImage.getSampleModel().getNumBands() == inputBandsForColorType[colorType] + 1 && metadata.hasTransparentColor()) { checkReadParamBandSettings(param, inputBandsForColorType[colorType] + 1, theImage.getSampleModel().getNumBands()); } else { checkReadParamBandSettings(param, inputBandsForColorType[colorType], theImage.getSampleModel().getNumBands()); } clearAbortRequest(); processImageStarted(0); if (abortRequested()) { processReadAborted(); } else { decodeImage(); if (abortRequested()) { processReadAborted(); } else { processImageComplete(); } } } catch (IOException e) { throw new IIOException("Error reading PNG image data", e); } finally { if (inf != null) { inf.end(); } } } @Override 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; } @Override public int getWidth(int imageIndex) throws IIOException { if (imageIndex != 0) { throw new IndexOutOfBoundsException("imageIndex != 0!"); } readHeader(); return metadata.IHDR_width; } @Override public int getHeight(int imageIndex) throws IIOException { if (imageIndex != 0) { throw new IndexOutOfBoundsException("imageIndex != 0!"); } readHeader(); return metadata.IHDR_height; } @Override 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) { /* * For PNG images of color type PNG_COLOR_RGB or PNG_COLOR_GRAY that * contain a specific transparent color (given by tRNS chunk), we add * ImageTypeSpecifier(s) that support transparency to the list of * supported image types. */ case PNG_COLOR_GRAY: readMetadata(); // Need tRNS chunk if (metadata.hasTransparentColor()) { 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)); } // Packed grayscale l.add(ImageTypeSpecifier.createGrayscale(bitDepth, dataType, false)); break; case PNG_COLOR_RGB: readMetadata(); // Need tRNS chunk if (bitDepth == 8) { if (metadata.hasTransparentColor()) { l.add(ImageTypeSpecifier.createFromBufferedImageType( BufferedImage.TYPE_4BYTE_ABGR)); } // 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)); } if (metadata.hasTransparentColor()) { 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)); } // 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. */ @Override public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException { Iterator<ImageTypeSpecifier> types = getImageTypes(imageIndex); ImageTypeSpecifier raw = null; do { raw = types.next(); } while (types.hasNext()); return raw; } @Override public ImageReadParam getDefaultReadParam() { return new ImageReadParam(); } @Override public IIOMetadata getStreamMetadata() throws IIOException { return null; } @Override public IIOMetadata getImageMetadata(int imageIndex) throws IIOException { if (imageIndex != 0) { throw new IndexOutOfBoundsException("imageIndex != 0!"); } readMetadata(); return metadata; } @Override public BufferedImage read(int imageIndex, ImageReadParam param) throws IIOException { if (imageIndex != 0) { throw new IndexOutOfBoundsException("imageIndex != 0!"); } try { readImage(param); } catch (IOException | IllegalStateException | IllegalArgumentException e) { throw e; } catch (Throwable e) { throw new IIOException("Caught exception during read: ", e); } return theImage; } @Override public void reset() { super.reset(); resetStreamSettings(); } private void resetStreamSettings() { gotHeader = false; gotMetadata = false; metadata = null; pixelStream = null; imageStartPosition = -1L; } }
⏎ com/sun/imageio/plugins/png/PNGImageReader.java
Or download all of them as a single archive file:
File name: java.desktop-17.0.5-src.zip File size: 9152233 bytes Release date: 2022-09-13 Download
⇒ JDK 17 java.instrument.jmod - Instrument Module
2023-09-16, 81081👍, 0💬
Popular Posts:
Where Can I see Java Source Code files for Xerces Java 2.11.2? Here are Java Source Code files for X...
The JMX technology provides the tools for building distributed, Web-based, modular and dynamic solut...
JDK 11 jdk.jconsole.jmod is the JMOD file for JDK 11 JConsole tool, which can be invoked by the "jco...
Apache BCEL Source Code Files are inside the Apache BCEL source package file like bcel-6.5.0-src.zip...
JDK 11 jdk.jdi.jmod is the JMOD file for JDK 11 JDI (Java Debug Interface) tool. JDK 11 JDI tool com...