8177393: Result of RescaleOp for 4BYTE_ABGR images may be 25% black
authorprr
Fri, 19 May 2017 14:57:51 -0700
changeset 45349 b8f80bc16972
parent 45348 383dbc53e342
child 45350 849ddafa3642
8177393: Result of RescaleOp for 4BYTE_ABGR images may be 25% black Reviewed-by: flar, psadhukhan
jdk/src/java.desktop/share/classes/java/awt/image/RescaleOp.java
jdk/test/java/awt/image/RescaleOp/ImageRescaleOpTest.java
--- a/jdk/src/java.desktop/share/classes/java/awt/image/RescaleOp.java	Fri May 19 07:06:28 2017 -0700
+++ b/jdk/src/java.desktop/share/classes/java/awt/image/RescaleOp.java	Fri May 19 14:57:51 2017 -0700
@@ -27,6 +27,8 @@
 
 import java.awt.color.ColorSpace;
 import java.awt.geom.Rectangle2D;
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.geom.Point2D;
 import java.awt.RenderingHints;
@@ -193,9 +195,10 @@
                                           int   nBands,
                                           int   nElems) {
 
-        byte[][]        lutData = new byte[scale.length][nElems];
+        byte[][]        lutData = new byte[nBands][nElems];
+        int band;
 
-        for (int band=0; band<scale.length; band++) {
+        for (band=0; band<scale.length; band++) {
             float  bandScale   = scale[band];
             float  bandOff     = off[band];
             byte[] bandLutData = lutData[band];
@@ -212,6 +215,17 @@
             }
 
         }
+        int maxToCopy = (nBands == 4 && scale.length == 4) ? 4 : 3;
+        while (band < lutData.length && band < maxToCopy) {
+           System.arraycopy(lutData[band-1], 0, lutData[band], 0, nElems);
+           band++;
+        }
+        if (nBands == 4 && band < nBands) {
+           byte[] bandLutData = lutData[band];
+           for (int i=0; i<nElems; i++) {
+              bandLutData[i] = (byte)i;
+           }
+        }
 
         return new ByteLookupTable(0, lutData);
     }
@@ -228,9 +242,10 @@
                                             int   nBands,
                                             int   nElems) {
 
-        short[][]        lutData = new short[scale.length][nElems];
+        short[][]        lutData = new short[nBands][nElems];
+        int band = 0;
 
-        for (int band=0; band<scale.length; band++) {
+        for (band=0; band<scale.length; band++) {
             float   bandScale   = scale[band];
             float   bandOff     = off[band];
             short[] bandLutData = lutData[band];
@@ -246,6 +261,17 @@
                 bandLutData[i] = (short)val;
             }
         }
+        int maxToCopy = (nBands == 4 && scale.length == 4) ? 4 : 3;
+        while (band < lutData.length && band < maxToCopy) {
+           System.arraycopy(lutData[band-1], 0, lutData[band], 0, nElems);
+           band++;
+        }
+        if (nBands == 4 && band < nBands) {
+           short[] bandLutData = lutData[band];
+           for (int i=0; i<nElems; i++) {
+              bandLutData[i] = (short)i;
+           }
+        }
 
         return new ShortLookupTable(0, lutData);
     }
@@ -300,6 +326,19 @@
             }
         }
 
+      if (dstSM instanceof ComponentSampleModel) {
+           ComponentSampleModel dsm = (ComponentSampleModel)dstSM;
+           if (dsm.getPixelStride() != dst.getNumBands()) {
+               return false;
+           }
+        }
+        if (srcSM instanceof ComponentSampleModel) {
+           ComponentSampleModel csm = (ComponentSampleModel)srcSM;
+           if (csm.getPixelStride() != src.getNumBands()) {
+               return false;
+           }
+        }
+
         return true;
     }
 
@@ -344,6 +383,7 @@
         }
 
         boolean needToConvert = false;
+        boolean needToDraw = false;
 
         // Include alpha
         if (scaleConst > numSrcColorComp && srcCM.hasAlpha()) {
@@ -374,102 +414,41 @@
 
             dstCM = dst.getColorModel();
             if(srcCM.getColorSpace().getType() !=
-               dstCM.getColorSpace().getType()) {
+                 dstCM.getColorSpace().getType()) {
                 needToConvert = true;
                 dst = createCompatibleDestImage(src, null);
             }
 
         }
 
-        boolean scaleAlpha = true;
-
-        //
-        // The number of sets of scaling constants may be one,
-        // in which case the same constants are applied to all color
-        // (but NOT alpha) components. Otherwise, the number of sets
-        // of scaling constants may equal the number of Source color
-        // components, in which case NO rescaling of the alpha component
-        // (if present) is performed.
-        //
-        if (numSrcColorComp == scaleConst || scaleConst == 1) {
-            scaleAlpha = false;
-        }
-
         //
         // Try to use a native BI rescale operation first
         //
         if (ImagingLib.filter(this, src, dst) == null) {
+            if (src.getRaster().getNumBands() !=
+                dst.getRaster().getNumBands()) {
+                needToDraw = true;
+                dst = createCompatibleDestImage(src, null);
+            }
+
             //
             // Native BI rescale failed - convert to rasters
             //
             WritableRaster srcRaster = src.getRaster();
             WritableRaster dstRaster = dst.getRaster();
 
-            if (!scaleAlpha) {
-                if (srcCM.hasAlpha()) {
-                    // Do not rescale Alpha component
-                    int minx = srcRaster.getMinX();
-                    int miny = srcRaster.getMinY();
-                    int[] bands = new int[numSrcColorComp];
-                    for (int i=0; i < numSrcColorComp; i++) {
-                        bands[i] = i;
-                    }
-                    srcRaster =
-                        srcRaster.createWritableChild(minx, miny,
-                                                      srcRaster.getWidth(),
-                                                      srcRaster.getHeight(),
-                                                      minx, miny,
-                                                      bands);
-                }
-                if (dstCM.hasAlpha()) {
-                    int minx = dstRaster.getMinX();
-                    int miny = dstRaster.getMinY();
-                    int[] bands = new int[numSrcColorComp];
-                    for (int i=0; i < numSrcColorComp; i++) {
-                        bands[i] = i;
-                    }
-                    dstRaster =
-                        dstRaster.createWritableChild(minx, miny,
-                                                      dstRaster.getWidth(),
-                                                      dstRaster.getHeight(),
-                                                      minx, miny,
-                                                      bands);
-                }
-            }
-
             //
             // Call the raster filter method
             //
-            filterRasterImpl(srcRaster, dstRaster, scaleConst);
-
-            //
-            // here copy the unscaled src alpha to destination alpha channel
-            //
-            if (!scaleAlpha) {
-                Raster srcAlphaRaster = null;
-                WritableRaster dstAlphaRaster = null;
-
-                if (srcCM.hasAlpha()) {
-                    srcAlphaRaster = src.getAlphaRaster();
-                }
-                if (dstCM.hasAlpha()) {
-                    dstAlphaRaster = dst.getAlphaRaster();
-                    if (srcAlphaRaster != null) {
-                        dstAlphaRaster.setRect(srcAlphaRaster);
-                    } else {
-                        int alpha = 0xff << 24;
-                        for (int cy=0; cy < dst.getHeight(); cy++) {
-                            for (int cx=0; cx < dst.getWidth(); cx++) {
-                                int color = dst.getRGB(cx, cy);
-
-                                dst.setRGB(cx, cy, color | alpha);
-                            }
-                        }
-                    }
-                }
-            }
+            filterRasterImpl(srcRaster, dstRaster, scaleConst, false);
         }
 
+        if (needToDraw) {
+             Graphics2D g = origDst.createGraphics();
+             g.setComposite(AlphaComposite.Src);
+             g.drawImage(dst, 0, 0, width, height, null);
+             g.dispose();
+        }
         if (needToConvert) {
             // ColorModels are not the same
             ColorConvertOp ccop = new ColorConvertOp(hints);
@@ -497,10 +476,11 @@
      *         stated in the class comments.
      */
     public final WritableRaster filter (Raster src, WritableRaster dst)  {
-        return filterRasterImpl(src, dst, length);
+        return filterRasterImpl(src, dst, length, true);
     }
 
-    private WritableRaster filterRasterImpl(Raster src, WritableRaster dst, int scaleConst) {
+    private WritableRaster filterRasterImpl(Raster src, WritableRaster dst,
+                                            int scaleConst, boolean sCheck) {
         int numBands = src.getNumBands();
         int width  = src.getWidth();
         int height = src.getHeight();
@@ -527,7 +507,7 @@
 
         // Make sure that the arrays match
         // Make sure that the low/high/constant arrays match
-        if (scaleConst != 1 && scaleConst != src.getNumBands()) {
+        if (sCheck && scaleConst != 1 && scaleConst != src.getNumBands()) {
             throw new IllegalArgumentException("Number of scaling constants "+
                                                "does not equal the number of"+
                                                " of bands in the src raster");
@@ -598,8 +578,14 @@
                     srcPix = src.getPixel(sX, sY, srcPix);
                     tidx = 0;
                     for (int z=0; z<numBands; z++, tidx += step) {
-                        val = (int)(srcPix[z]*scaleFactors[tidx]
-                                          + offsets[tidx]);
+                        if ((scaleConst == 1 || scaleConst == 3) &&
+                            (z == 3) && (numBands == 4)) {
+                           val = srcPix[z];
+                        } else {
+                            val = (int)(srcPix[z]*scaleFactors[tidx]
+                                              + offsets[tidx]);
+
+                        }
                         // Clamp
                         if ((val & dstMask[z]) != 0) {
                             if (val < 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/RescaleOp/ImageRescaleOpTest.java	Fri May 19 14:57:51 2017 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8177393
+ * @summary Verify RescaleOp applied to BufferedImages.
+ * @run main ImageRescaleOpTest
+ */
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import static java.awt.image.BufferedImage.*;
+import java.awt.image.RescaleOp;
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+
+public class ImageRescaleOpTest {
+
+    int w = 10, h = 10;
+    float scaleFactor = 0.5f;
+    float offset = 0.0f;
+    static boolean saveImage = false;
+
+    public static void main(String[] args) throws Exception {
+        saveImage = args.length > 0;
+        ImageRescaleOpTest test = new ImageRescaleOpTest();
+        test.startTest();
+    }
+
+    String getFileName(int s, int d) {
+        return textFor(s)+"_to_"+textFor(d)+".png";
+    }
+
+    String getMsgText(int s, int d) {
+        return textFor(s)+"->"+textFor(d)+": ";
+    }
+
+    String textFor(int t) {
+       switch (t) {
+           case TYPE_INT_ARGB        : return "ARGB";
+           case TYPE_INT_RGB         : return "RGB";
+           case TYPE_4BYTE_ABGR      : return "4BYTEABGR";
+           case TYPE_3BYTE_BGR       : return "3BYTEBGR";
+           case TYPE_USHORT_555_RGB  : return "USHORT_555_RGB";
+           case TYPE_USHORT_565_RGB  : return "USHORT_565_RGB";
+           case TYPE_USHORT_GRAY     : return "USHORT_GRAY";
+           default                   : return "OTHER";
+       }
+    }
+
+    private void startTest() throws Exception {
+
+        int expect = 0xff7f7f7f;
+        runTest(TYPE_INT_RGB, TYPE_INT_RGB, expect);
+        runTest(TYPE_INT_ARGB, TYPE_INT_ARGB, expect);
+        runTest(TYPE_INT_ARGB, TYPE_INT_RGB, expect);
+        runTest(TYPE_INT_RGB, TYPE_INT_ARGB, expect);
+
+        runTest(TYPE_3BYTE_BGR, TYPE_3BYTE_BGR, expect);
+        runTest(TYPE_3BYTE_BGR, TYPE_4BYTE_ABGR, expect);
+        runTest(TYPE_4BYTE_ABGR, TYPE_3BYTE_BGR, expect);
+        runTest(TYPE_4BYTE_ABGR, TYPE_4BYTE_ABGR, expect);
+
+        /* Slightly different values here due to limited precision */
+        runTest(TYPE_USHORT_555_RGB, TYPE_USHORT_555_RGB, 0xff7b7b7b);
+        runTest(TYPE_USHORT_565_RGB, TYPE_USHORT_565_RGB, 0xff7b7d7b);
+
+        /* 565->555 and 555->565 results are wrong as the slow code
+         * path used is not accounting for the difference in the range.
+         */
+        //runTest(TYPE_USHORT_555_RGB, TYPE_USHORT_565_RGB, expect);
+        //runTest(TYPE_USHORT_565_RGB, TYPE_USHORT_555_RGB, expect);
+
+        runTest(TYPE_USHORT_GRAY, TYPE_USHORT_GRAY, 0xffbcbcbc);
+
+    }
+
+   private void check(BufferedImage bi, int expect, String msg) {
+        int argb = bi.getRGB(w-1, h-1);
+        System.out.println(msg + Integer.toHexString(argb));
+        if (argb != expect) {
+            throw new RuntimeException(msg +
+                   " expected " + Integer.toHexString(expect) +
+                   " but got " + Integer.toHexString(argb));
+        }
+    }
+
+    private void runTest(int sType, int dType, int expect) {
+
+        BufferedImage src  = new BufferedImage(w, h, sType);
+        BufferedImage dst  = new BufferedImage(w, h, dType);
+        String msg = getMsgText(sType, dType);
+
+        Graphics2D g2d = src.createGraphics();
+        g2d.setColor(Color.WHITE);
+        g2d.fillRect(0, 0, w, h);
+        RescaleOp res = new RescaleOp(scaleFactor, offset, null);
+        res.filter(src, dst);
+        if (saveImage) {
+            try {
+               String fname = getFileName(sType, dType);
+               ImageIO.write(dst, "png", new File(fname));
+            } catch (IOException e) {
+            }
+        }
+        check(dst, expect, msg);
+   }
+}