< prev index next >
src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java
Print this page
@@ -696,14 +696,16 @@
}
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. 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) {
@@ -715,14 +717,23 @@
}
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);
}
}
@@ -1264,11 +1275,21 @@
}
if (dstY > dstMaxY) {
break;
}
- if (useSetRect) {
+ /*
+ * 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;
for (int dstX = updateMinX;
@@ -1279,11 +1300,42 @@
if (adjustBitDepths) {
for (int b = 0; b < numBands; b++) {
ps[b] = scale[b][ps[b]];
}
}
+ if (tRNSTransparentPixelPresent) {
+ /*
+ * Create intermediate array to fill the extra
+ * alpha channel for each pixel.
+ */
+ int[] temp = new int[inputBands + 1];
+
+ int opaque = (bitDepth < 16) ? 255 : 65535;
+ 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,
@@ -1420,13 +1472,21 @@
// 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();
@@ -1504,19 +1564,44 @@
} 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));
@@ -1525,10 +1610,23 @@
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;
< prev index next >