< 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. 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) {
             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);
                     }
                 }

@@ -1072,10 +1083,36 @@
 
         int bitsPerRow = Math.multiplyExact((inputBands * bitDepth), passWidth);
         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++) {
                 // Update count of pixels read
                 updateImageProgress(passWidth);

@@ -1107,15 +1144,10 @@
         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();

@@ -1236,21 +1268,78 @@
             default:
                 throw new IIOException("Unknown row filter type (= " +
                                        filter + ")!");
             }
 
-            // Copy data into passRow byte by byte
+            /*
+             * 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 {
             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) &&

@@ -1420,13 +1509,20 @@
 
             // 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 (considerTransparentPixel()) {
+                checkReadParamBandSettings(param,
+                    4,
+                    theImage.getSampleModel().getNumBands());
+
+            } else {
             checkReadParamBandSettings(param,
                                        inputBandsForColorType[colorType],
                                       theImage.getSampleModel().getNumBands());
+            }
 
             clearAbortRequest();
             processImageStarted(0);
             if (abortRequested()) {
                 processReadAborted();

@@ -1446,10 +1542,27 @@
                 inf.end();
             }
         }
     }
 
+    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!");
         }
         if (seekForwardOnly && allowSearch) {

@@ -1512,11 +1625,22 @@
                                                      dataType,
                                                      false));
             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(
                           BufferedImage.TYPE_3BYTE_BGR));
 

@@ -1525,10 +1649,22 @@
 
                 l.add(ImageTypeSpecifier.createFromBufferedImageType(
                           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];
             bandOffsets[0] = 0;
             bandOffsets[1] = 1;
< prev index next >