--- old/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java 2018-03-27 17:14:13.700892160 +0530 +++ new/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java 2018-03-27 17:14:13.252892160 +0530 @@ -698,10 +698,12 @@ 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). + * Optimization: We can skip reading metadata if ignoreMetadata + * flag is set and colorType is not PNG_COLOR_PALETTE. But we need + * to parse only the tRNS chunk even in the case where IHDR colortype + * is not PNG_COLOR_PALETTE, because we need tRNS chunk transparent + * pixel information for PNG_COLOR_RGB while storing the pixel data + * in decodePass(). */ int colorType = metadata.IHDR_colorType; if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) { @@ -717,10 +719,19 @@ int chunkType = stream.readInt(); if (chunkType == IDAT_TYPE) { - // We've reached the image data + // 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); @@ -1074,6 +1085,32 @@ int bytesPerRow = (bitsPerRow + 7) / 8; int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow; + WritableRaster passRow; + if (considerTransparentPixel()) { + /* + * When we have tRNS chunk for image type PNG_COLOR_RGB, we need + * extra alpha channel to represent transparency. In getImageTypes() + * we create a 4 channel destination image, so we need to calculate + * destEltsPerRow and create appropriate Raster. + */ + int destBands = 4; + int destBytesPerPixel = (bitDepth == 16) ? 2 : 1; + destBytesPerPixel *= destBands; + int destBitsPerRow = + Math.multiplyExact((destBands * bitDepth), passWidth); + int destBytesPerRow = (destBitsPerRow + 7) / 8; + int destEltsPerRow = + (bitDepth == 16) ? destBytesPerRow/2 : destBytesPerRow; + + // Create a 1-row tall Raster to hold the data + passRow = createRaster(passWidth, 1, destBands, + destEltsPerRow, bitDepth); + } else { + // Create a 1-row tall Raster to hold the data + passRow = createRaster(passWidth, 1, inputBands, + eltsPerRow, bitDepth); + } + // If no pixels need updating, just skip the input data if (updateWidth == 0) { for (int srcY = 0; srcY < passHeight; srcY++) { @@ -1109,11 +1146,6 @@ 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); @@ -1238,15 +1270,72 @@ filter + ")!"); } - // Copy data into passRow byte by byte - if (bitDepth < 16) { - System.arraycopy(curr, 0, byteData, 0, bytesPerRow); + /* + * Copy data into passRow byte by byte. In case of colortype + * PNG_COLOR_RGB if we have transparent pixel information from + * tRNS chunk we need to consider that and store proper information + * in alpha channel. + */ + if (considerTransparentPixel()) { + if (bitDepth < 16) { + int srcidx = 0; + int destidx = 0; + for (int i = 0; i < passWidth; i++) { + if (curr[srcidx] == (byte)metadata.tRNS_red && + curr[srcidx + 1] == (byte)metadata.tRNS_green && + curr[srcidx + 2] == (byte)metadata.tRNS_blue) + { + byteData[destidx] = curr[srcidx]; + byteData[destidx + 1] = curr[srcidx + 1]; + byteData[destidx + 2] = curr[srcidx + 2]; + byteData[destidx + 3] = (byte)0; + } else { + byteData[destidx] = curr[srcidx]; + byteData[destidx + 1] = curr[srcidx + 1]; + byteData[destidx + 2] = curr[srcidx + 2]; + byteData[destidx + 3] = (byte)255; + } + srcidx += 3; + destidx += 4; + } + } else { + int srcidx = 0; + int destidx = 0; + for (int i = 0; i < passWidth; i++) { + short red = (short) + ((curr[srcidx] << 8) | (curr[srcidx + 1] & 0xff)); + short green = (short) + ((curr[srcidx + 2] << 8) | (curr[srcidx + 3] & 0xff)); + short blue = (short) + ((curr[srcidx + 4] << 8) | (curr[srcidx + 5] & 0xff)); + if (red == (short)metadata.tRNS_red && + green == (short)metadata.tRNS_green && + blue == (short)metadata.tRNS_blue) + { + shortData[destidx] = red; + shortData[destidx + 1] = green; + shortData[destidx + 2] = blue; + shortData[destidx + 3] = (short)0; + } else { + shortData[destidx] = red; + shortData[destidx + 1] = green; + shortData[destidx + 2] = blue; + shortData[destidx + 3] = (short)65535; + } + srcidx += 6; + destidx += 4; + } + } } else { - int idx = 0; - for (int j = 0; j < eltsPerRow; j++) { - shortData[j] = + 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; + idx += 2; + } } } @@ -1422,9 +1511,16 @@ // 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()); + if (considerTransparentPixel()) { + checkReadParamBandSettings(param, + 4, + theImage.getSampleModel().getNumBands()); + + } else { + checkReadParamBandSettings(param, + inputBandsForColorType[colorType], + theImage.getSampleModel().getNumBands()); + } clearAbortRequest(); processImageStarted(0); @@ -1448,6 +1544,23 @@ } } + private boolean considerTransparentPixel(){ + /* + * In case of non-indexed PNG_COLOR_RGB images if we have tRNS chunk, + * we need to consider transparent pixel values and store alpha channel + * also. If user explicitly provides ImageReadParam with + * destinationBands as 3 when we have tRNS chunk, only then we will + * ignore tRNS chunk values. + */ + if ((destinationBands == null || + destinationBands.length == 4) && + metadata.tRNS_colorType == PNG_COLOR_RGB) + { + return true; + } + return false; + } + public int getNumImages(boolean allowSearch) throws IIOException { if (stream == null) { throw new IllegalStateException("No input source set!"); @@ -1514,7 +1627,18 @@ break; case PNG_COLOR_RGB: + readMetadata(); // Need tRNS chunk + + /* + * In case of PNG_COLOR_RGB colortype if we have transparent + * pixel information in tRNS chunk we create destination + * image with 4 channels. + */ if (bitDepth == 8) { + if (considerTransparentPixel()) { + l.add(ImageTypeSpecifier.createFromBufferedImageType( + BufferedImage.TYPE_4BYTE_ABGR)); + } // some standard types of buffered images // which can be used as destination l.add(ImageTypeSpecifier.createFromBufferedImageType( @@ -1527,6 +1651,18 @@ BufferedImage.TYPE_INT_BGR)); } + if (considerTransparentPixel()) { + 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];