8145019: Exceptions from TIFFImageReader.read() when loading bit depth test images
authorbpb
Fri, 20 Jan 2017 15:09:54 -0800
changeset 43316 5285aed28df8
parent 43315 43ae29672739
child 43317 cbe67e782fce
8145019: Exceptions from TIFFImageReader.read() when loading bit depth test images Summary: Fix some problems reading unusual bit depth images relating to setting the correct ImageTypeSpecifier and reformatting discontiguous data Reviewed-by: prr
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java	Fri Jan 20 08:53:42 2017 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java	Fri Jan 20 15:09:54 2017 -0800
@@ -156,6 +156,11 @@
     protected boolean planar;
 
     /**
+     * The planar band to decode; ignored for chunky (interleaved) images.
+     */
+    protected int planarBand = 0;
+
+    /**
      * The value of the {@code SamplesPerPixel} tag.
      */
     protected int samplesPerPixel;
@@ -446,24 +451,19 @@
      * Create a {@code ComponentColorModel} for use in creating
      * an {@code ImageTypeSpecifier}.
      */
-    // This code was copied from javax.imageio.ImageTypeSpecifier.
+    // This code was inspired by the method of the same name in
+    // javax.imageio.ImageTypeSpecifier
     static ColorModel createComponentCM(ColorSpace colorSpace,
                                         int numBands,
+                                        int[] bitsPerSample,
                                         int dataType,
                                         boolean hasAlpha,
                                         boolean isAlphaPremultiplied) {
         int transparency =
             hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
 
-        int[] numBits = new int[numBands];
-        int bits = DataBuffer.getDataTypeSize(dataType);
-
-        for (int i = 0; i < numBands; i++) {
-            numBits[i] = bits;
-        }
-
         return new ComponentColorModel(colorSpace,
-                                       numBits,
+                                       bitsPerSample,
                                        hasAlpha,
                                        isAlphaPremultiplied,
                                        transparency,
@@ -581,14 +581,15 @@
      * Determines whether the {@code DataBuffer} is filled without
      * any interspersed padding bits.
      */
-    private static boolean isDataBufferBitContiguous(SampleModel sm)
+    private static boolean isDataBufferBitContiguous(SampleModel sm,
+        int[] bitsPerSample)
         throws IIOException {
         int dataTypeSize = getDataTypeSize(sm.getDataType());
 
         if(sm instanceof ComponentSampleModel) {
             int numBands = sm.getNumBands();
             for(int i = 0; i < numBands; i++) {
-                if(sm.getSampleSize(i) != dataTypeSize) {
+                if(bitsPerSample[i] != dataTypeSize) {
                     // Sample does not fill data element.
                     return false;
                 }
@@ -682,6 +683,7 @@
      * of the supplied {@code WritableRaster}.
      */
     private static void reformatDiscontiguousData(byte[] buf,
+                                                  int[] bitsPerSample,
                                                   int stride,
                                                   int w,
                                                   int h,
@@ -691,7 +693,6 @@
         // Get SampleModel info.
         SampleModel sm = raster.getSampleModel();
         int numBands = sm.getNumBands();
-        int[] sampleSize = sm.getSampleSize();
 
         // Initialize input stream.
         ByteArrayInputStream is = new ByteArrayInputStream(buf);
@@ -705,7 +706,7 @@
             int x = raster.getMinX();
             for(int i = 0; i < w; i++, x++) {
                 for(int b = 0; b < numBands; b++) {
-                    long bits = iis.readBits(sampleSize[b]);
+                    long bits = iis.readBits(bitsPerSample[b]);
                     raster.setSample(x, y, b, (int)bits);
                 }
             }
@@ -806,8 +807,15 @@
                     blueLut[i] = (byte)((colorMap[2*mapSize + i]*255)/65535);
                 }
 
-                int dataType = bitsPerSample[0] == 8 ?
-                    DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT;
+                int dataType;
+                if (bitsPerSample[0] <= 8) {
+                    dataType = DataBuffer.TYPE_BYTE;
+                } else if (sampleFormat[0] ==
+                    BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+                    dataType = DataBuffer.TYPE_SHORT;
+                } else {
+                    dataType = DataBuffer.TYPE_USHORT;
+                }
                 return ImageTypeSpecifier.createIndexed(redLut,
                                                         greenLut,
                                                         blueLut,
@@ -1082,6 +1090,7 @@
 
                         cm = createComponentCM(cs,
                                                samplesPerPixel,
+                                               bitsPerSample,
                                                dataType,
                                                hasAlpha,
                                                alphaPremultiplied);
@@ -1089,6 +1098,7 @@
                         ColorSpace cs = new BogusColorSpace(samplesPerPixel);
                         cm = createComponentCM(cs,
                                                samplesPerPixel,
+                                               bitsPerSample,
                                                dataType,
                                                false, // hasAlpha
                                                false); // alphaPremultiplied
@@ -1119,17 +1129,23 @@
                  BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER);
 
             // Grayscale
-            if(samplesPerPixel == 1) {
-                int dataType =
-                    getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+            if(samplesPerPixel == 1 &&
+                (bitsPerSample[0] == 1 || bitsPerSample[0] == 2 ||
+                 bitsPerSample[0] == 4 || bitsPerSample[0] == 8 ||
+                 bitsPerSample[0] == 16)) {
+                int dataType = getDataTypeFromNumBits(maxBitsPerSample, isSigned);
 
-                return ImageTypeSpecifier.createGrayscale(maxBitsPerSample,
+                return ImageTypeSpecifier.createGrayscale(bitsPerSample[0],
                                                           dataType,
                                                           isSigned);
             }
 
             // Gray-alpha
-            if (samplesPerPixel == 2) {
+            if (samplesPerPixel == 2 &&
+                bitsPerSample[0] == bitsPerSample[1] &&
+                (bitsPerSample[0] == 1 || bitsPerSample[0] == 2 ||
+                 bitsPerSample[0] == 4 || bitsPerSample[0] == 8 ||
+                 bitsPerSample[0] == 16)) {
                 boolean alphaPremultiplied = false;
                 if (extraSamples != null &&
                     extraSamples[0] ==
@@ -1147,6 +1163,13 @@
             }
 
             if (samplesPerPixel == 3 || samplesPerPixel == 4) {
+                int dataType = getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+                int dataTypeSize;
+                try {
+                    dataTypeSize = getDataTypeSize(dataType);
+                } catch (IIOException ignored) {
+                    dataTypeSize = maxBitsPerSample;
+                }
                 if(totalBits <= 32 && !isSigned) {
                     // Packed RGB or RGBA
                     int redMask = createMask(bitsPerSample, 0);
@@ -1169,21 +1192,24 @@
                                                            alphaMask,
                                                            transferType,
                                                            alphaPremultiplied);
-                } else if(samplesPerPixel == 3) {
+                } else if(samplesPerPixel == 3 &&
+                    dataTypeSize == bitsPerSample[0] &&
+                    bitsPerSample[0] == bitsPerSample[1] &&
+                    bitsPerSample[1] == bitsPerSample[2]) {
                     // Interleaved RGB
                     int[] bandOffsets = new int[] {0, 1, 2};
-                    int dataType =
-                        getDataTypeFromNumBits(maxBitsPerSample, isSigned);
                     return ImageTypeSpecifier.createInterleaved(rgb,
                                                                 bandOffsets,
                                                                 dataType,
                                                                 false,
                                                                 false);
-                } else if(samplesPerPixel == 4) {
+                } else if(samplesPerPixel == 4 &&
+                    dataTypeSize == bitsPerSample[0] &&
+                    bitsPerSample[0] == bitsPerSample[1] &&
+                    bitsPerSample[1] == bitsPerSample[2] &&
+                    bitsPerSample[2] == bitsPerSample[3]) {
                     // Interleaved RGBA
                     int[] bandOffsets = new int[] {0, 1, 2, 3};
-                    int dataType =
-                        getDataTypeFromNumBits(maxBitsPerSample, isSigned);
                     boolean alphaPremultiplied = false;
                     if (extraSamples != null &&
                         extraSamples[0] ==
@@ -1196,20 +1222,28 @@
                                                                 true,
                                                                 alphaPremultiplied);
                 }
+            }
+
+            // Arbitrary Interleaved.
+            int dataType =
+                getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+            SampleModel sm = createInterleavedSM(dataType,
+                                                 samplesPerPixel);
+            ColorSpace cs;
+            if (samplesPerPixel <= 2) {
+                cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+            } else if (samplesPerPixel <= 4) {
+                cs = rgb;
             } else {
-                // Arbitrary Interleaved.
-                int dataType =
-                    getDataTypeFromNumBits(maxBitsPerSample, isSigned);
-                SampleModel sm = createInterleavedSM(dataType,
-                                                     samplesPerPixel);
-                ColorSpace cs = new BogusColorSpace(samplesPerPixel);
-                ColorModel cm = createComponentCM(cs,
-                                                  samplesPerPixel,
-                                                  dataType,
-                                                  false, // hasAlpha
-                                                  false); // alphaPremultiplied
-                return new ImageTypeSpecifier(cm, sm);
+                cs = new BogusColorSpace(samplesPerPixel);
             }
+            ColorModel cm = createComponentCM(cs,
+                                              samplesPerPixel,
+                                              bitsPerSample,
+                                              dataType,
+                                              false, // hasAlpha
+                                              false); // alphaPremultiplied
+            return new ImageTypeSpecifier(cm, sm);
         }
 
         return null;
@@ -1285,6 +1319,14 @@
     }
 
     /**
+     * Sets the index of the planar configuration band to be decoded. This value
+     * is ignored for chunky (interleaved) images.
+     *
+     * @param the index of the planar band to decode
+     */
+    public void setPlanarBand(int planarBand) { this.planarBand = planarBand; }
+
+    /**
      * Sets the value of the {@code samplesPerPixel} field.
      *
      * <p> If this method is called, the {@code beginDecoding}
@@ -2488,7 +2530,7 @@
             // Branch based on whether data are bit-contiguous, i.e.,
             // data are packaed as tightly as possible leaving no unused
             // bits except at the end of a row.
-            if(isDataBufferBitContiguous(sm)) {
+            if(isDataBufferBitContiguous(sm, bitsPerSample)) {
                 // Use byte or float data directly.
                 if (byteData != null) {
                     decodeRaw(byteData, dstOffset,
@@ -2537,11 +2579,19 @@
             } else {
                 // Read discontiguous data into bytes and set the samples
                 // into the Raster.
-                int bpp = getBitsPerPixel(sm);
+                int bpp;
+                if (planar) {
+                    bpp = bitsPerSample[planarBand];
+                } else {
+                    bpp = 0;
+                    for (int bps : bitsPerSample) {
+                        bpp += bps;
+                    }
+                }
                 int bytesPerRow = (bpp*srcWidth + 7)/8;
                 byte[] buf = new byte[bytesPerRow*srcHeight];
                 decodeRaw(buf, 0, bpp, bytesPerRow);
-                reformatDiscontiguousData(buf, bytesPerRow,
+                reformatDiscontiguousData(buf, bitsPerSample, bytesPerRow,
                                           srcWidth, srcHeight,
                                           ras);
             }
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java	Fri Jan 20 08:53:42 2017 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java	Fri Jan 20 15:09:54 2017 -0800
@@ -1113,6 +1113,7 @@
         long offset = getTileOrStripOffset(tileIndex);
         long byteCount = getTileOrStripByteCount(tileIndex);
 
+        decompressor.setPlanarBand(band);
         decompressor.setStream(stream);
         decompressor.setOffset(offset);
         decompressor.setByteCount((int) byteCount);