< 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,26 +1275,66 @@
                 }
                 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;
 
+                    /*
+                     * 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 < numBands; 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,

@@ -1420,13 +1471,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 +1563,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 +1609,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 >