7116979: Unexpected pixel colour when converting images to TYPE_BYTE_INDEXED
authorjdv
Fri, 06 May 2016 06:52:13 +0000
changeset 37798 e1de34b6475c
parent 37797 b84e375f071d
child 37799 635c430d5a99
child 37807 78e5cb21ca13
7116979: Unexpected pixel colour when converting images to TYPE_BYTE_INDEXED Summary: Unexpected pixel colour when converting images to TYPE_BYTE_INDEXED Reviewed-by: prr, flar
jdk/make/data/x11wrappergen/sizes.32
jdk/make/data/x11wrappergen/sizes.64
jdk/make/data/x11wrappergen/sizes.64-solaris-i386
jdk/make/data/x11wrappergen/xlibtypes.txt
jdk/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c
jdk/src/java.desktop/share/native/libawt/java2d/SurfaceData.h
jdk/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.h
jdk/src/java.desktop/unix/native/common/awt/colordata.h
jdk/src/java.desktop/windows/native/libawt/windows/colordata.h
jdk/test/sun/java2d/loops/ConvertToByteIndexedTest.java
--- a/jdk/make/data/x11wrappergen/sizes.32	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/make/data/x11wrappergen/sizes.32	Fri May 06 06:52:13 2016 +0000
@@ -206,7 +206,8 @@
 ColorData.img_oda_blue	32
 ColorData.pGrayInverseLutData	36
 ColorData.screendata	40
-ColorData	44
+ColorData.representsPrimaries	44
+ColorData	48
 XFontStruct.ext_data	0
 XFontStruct.fid	4
 XFontStruct.direction	8
--- a/jdk/make/data/x11wrappergen/sizes.64	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/make/data/x11wrappergen/sizes.64	Fri May 06 06:52:13 2016 +0000
@@ -206,6 +206,7 @@
 ColorData.img_oda_blue	64
 ColorData.pGrayInverseLutData	72
 ColorData.screendata	80
+ColorData.representsPrimaries	84
 ColorData	88
 XFontStruct.ext_data	0
 XFontStruct.fid	8
--- a/jdk/make/data/x11wrappergen/sizes.64-solaris-i386	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/make/data/x11wrappergen/sizes.64-solaris-i386	Fri May 06 06:52:13 2016 +0000
@@ -206,6 +206,7 @@
 ColorData.img_oda_blue	64
 ColorData.pGrayInverseLutData	72
 ColorData.screendata	80
+ColorData.representsPrimaries	84
 ColorData	88
 XFontStruct.ext_data	0
 XFontStruct.fid	8
--- a/jdk/make/data/x11wrappergen/xlibtypes.txt	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/make/data/x11wrappergen/xlibtypes.txt	Fri May 06 06:52:13 2016 +0000
@@ -749,6 +749,7 @@
     img_oda_blue pointer byte
     pGrayInverseLutData pointer int
     screendata int
+    representsPrimaries int
 
 AwtGraphicsConfigData
     awt_depth int
--- a/jdk/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c	Fri May 06 06:52:13 2016 +0000
@@ -231,12 +231,14 @@
         pRasInfo->redErrTable = NULL;
         pRasInfo->grnErrTable = NULL;
         pRasInfo->bluErrTable = NULL;
+        pRasInfo->representsPrimaries = 0;
     } else {
         pRasInfo->invColorTable = bipriv->cData->img_clr_tbl;
         pRasInfo->redErrTable = bipriv->cData->img_oda_red;
         pRasInfo->grnErrTable = bipriv->cData->img_oda_green;
         pRasInfo->bluErrTable = bipriv->cData->img_oda_blue;
         pRasInfo->invGrayTable = bipriv->cData->pGrayInverseLutData;
+        pRasInfo->representsPrimaries = bipriv->cData->representsPrimaries;
     }
 }
 
@@ -259,6 +261,59 @@
     }
 }
 
+static int calculatePrimaryColorsApproximation(int* cmap, unsigned char* cube, int cube_size) {
+    int i, j, k;
+    int index, value, color;
+    // values calculated from cmap
+    int r, g, b;
+    // maximum positive/negative variation allowed for r, g, b values for primary colors
+    int delta = 5;
+    // get the primary color cmap indices from corner of inverse color table
+    for (i = 0; i < cube_size; i += (cube_size - 1)) {
+        for (j = 0; j < cube_size; j += (cube_size - 1)) {
+            for (k = 0; k < cube_size; k += (cube_size - 1)) {
+                // calculate inverse color table index
+                index = i + cube_size * (j + cube_size * k);
+                // get value present in corners of inverse color table
+                value = cube[index];
+                // use the corner values as index for cmap
+                color = cmap[value];
+                // extract r,g,b values from cmap value
+                r = ((color) >> 16) & 0xff;
+                g = ((color) >> 8) & 0xff;
+                b = color & 0xff;
+                /*
+                 * If i/j/k value is 0 optimum value of b/g/r should be 0 but we allow
+                 * maximum positive variation of 5. If i/j/k value is 31 optimum value
+                 * of b/g/r should be 255 but we allow maximum negative variation of 5.
+                 */
+                if (i == 0) {
+                    if (b > delta)
+                        return 0;
+                } else {
+                    if (b < (255 - delta))
+                        return 0;
+                }
+                if (j == 0) {
+                    if (g > delta)
+                        return 0;
+                } else {
+                    if (g < (255 - delta))
+                        return 0;
+                }
+                if (k == 0) {
+                    if (r > delta)
+                        return 0;
+                } else {
+                    if (r < (255 - delta))
+                        return 0;
+                }
+            }
+        }
+    }
+    return 1;
+}
+
 static ColorData *BufImg_SetupICM(JNIEnv *env,
                                   BufImgSDOps *bisdo)
 {
@@ -298,6 +353,7 @@
         }
 
         cData->img_clr_tbl = initCubemap(pRgb, bisdo->lutsize, 32);
+        cData->representsPrimaries = calculatePrimaryColorsApproximation(pRgb, cData->img_clr_tbl, 32);
         if (allGray == JNI_TRUE) {
             initInverseGrayLut(pRgb, bisdo->lutsize, cData);
         }
--- a/jdk/src/java.desktop/share/native/libawt/java2d/SurfaceData.h	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/src/java.desktop/share/native/libawt/java2d/SurfaceData.h	Fri May 06 06:52:13 2016 +0000
@@ -163,6 +163,7 @@
     char                *grnErrTable;           /* Green ordered dither table */
     char                *bluErrTable;           /* Blue ordered dither table */
     int                 *invGrayTable;          /* Inverse gray table */
+    int                 representsPrimaries;    /* whether cmap represents primary colors */
     union {
         void            *align;                 /* ensures strict alignment */
         char            data[SD_RASINFO_PRIVATE_SIZE];
--- a/jdk/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.h	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.h	Fri May 06 06:52:13 2016 +0000
@@ -43,7 +43,7 @@
     jint *PREFIX ## Lut;
 
 #define DeclareByteIndexedStoreVars(PREFIX) \
-    int PREFIX ## XDither, PREFIX ## YDither; \
+    int PREFIX ## XDither, PREFIX ## YDither, PREFIX ## RepPrims; \
     char *PREFIX ## rerr, *PREFIX ## gerr, *PREFIX ## berr; \
     unsigned char *PREFIX ## InvLut;
 
@@ -70,6 +70,7 @@
     do { \
         SetByteIndexedStoreVarsYPos(PREFIX, pRasInfo, (pRasInfo)->bounds.y1); \
         PREFIX ## InvLut = (pRasInfo)->invColorTable; \
+        PREFIX ## RepPrims = (pRasInfo)->representsPrimaries; \
     } while (0)
 
 #define InitByteIndexedStoreVarsX(PREFIX, pRasInfo) \
@@ -168,9 +169,14 @@
 
 #define StoreByteIndexedFrom3ByteRgb(pRas, PREFIX, x, r, g, b) \
     do { \
-        r += PREFIX ## rerr[PREFIX ## XDither]; \
-        g += PREFIX ## gerr[PREFIX ## XDither]; \
-        b += PREFIX ## berr[PREFIX ## XDither]; \
+        if (!(((r == 0) || (r == 255)) && \
+              ((g == 0) || (g == 255)) && \
+              ((b == 0) || (b == 255)) && \
+              PREFIX ## RepPrims)) { \
+            r += PREFIX ## rerr[PREFIX ## XDither]; \
+            g += PREFIX ## gerr[PREFIX ## XDither]; \
+            b += PREFIX ## berr[PREFIX ## XDither]; \
+        } \
         ByteClamp3Components(r, g, b); \
         (pRas)[x] = SurfaceData_InvColorMap(PREFIX ## InvLut, r, g, b); \
     } while (0)
--- a/jdk/src/java.desktop/unix/native/common/awt/colordata.h	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/src/java.desktop/unix/native/common/awt/colordata.h	Fri May 06 06:52:13 2016 +0000
@@ -44,6 +44,7 @@
     char* img_oda_blue;
     int *pGrayInverseLutData;
     int screendata;
+    int representsPrimaries;
 } ColorData;
 
 
--- a/jdk/src/java.desktop/windows/native/libawt/windows/colordata.h	Thu May 05 22:39:36 2016 -0700
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/colordata.h	Fri May 06 06:52:13 2016 +0000
@@ -33,6 +33,7 @@
     char* img_oda_blue;
     unsigned char* img_clr_tbl;
     int *pGrayInverseLutData;
+    int representsPrimaries;
 } ColorData;
 
 #define CANFREE(pData) (pData)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/java2d/loops/ConvertToByteIndexedTest.java	Fri May 06 06:52:13 2016 +0000
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016, 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     7116979
+ * @summary Test verifies whether BufferedImage with primary colors are
+ *          stored properly when we draw into ByteIndexed BufferedImage.
+ * @run     main ConvertToByteIndexedTest
+ */
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+
+public class ConvertToByteIndexedTest {
+    static final int[] SRC_TYPES = new int[] {
+        BufferedImage.TYPE_INT_RGB,
+        BufferedImage.TYPE_INT_ARGB,
+        BufferedImage.TYPE_INT_ARGB_PRE,
+        BufferedImage.TYPE_INT_BGR,
+        BufferedImage.TYPE_3BYTE_BGR,
+        BufferedImage.TYPE_4BYTE_ABGR,
+        BufferedImage.TYPE_4BYTE_ABGR_PRE,
+        BufferedImage.TYPE_USHORT_565_RGB,
+        BufferedImage.TYPE_USHORT_555_RGB,
+        BufferedImage.TYPE_BYTE_INDEXED};
+
+    static final String[] TYPE_NAME = new String[] {
+        "INT_RGB",
+        "INT_ARGB",
+        "INT_ARGB_PRE",
+        "INT_BGR",
+        "3BYTE_BGR",
+        "4BYTE_ABGR",
+        "4BYTE_ABGR_PRE",
+        "USHORT_565_RGB",
+        "USHORT_555_RGB",
+        "BYTE_INDEXED"};
+
+    static final Color[] COLORS = new Color[] {
+        //Color.WHITE,
+        Color.BLACK,
+        Color.RED,
+        Color.YELLOW,
+        Color.GREEN,
+        Color.MAGENTA,
+        Color.CYAN,
+        Color.BLUE};
+
+    static final HashMap<Integer,String> TYPE_TABLE =
+            new HashMap<Integer,String>();
+
+    static {
+        for (int i = 0; i < SRC_TYPES.length; i++) {
+            TYPE_TABLE.put(new Integer(SRC_TYPES[i]), TYPE_NAME[i]);
+        }
+    }
+
+    static int width = 50;
+    static int height = 50;
+
+    public static void ConvertToByteIndexed(Color color, int srcType) {
+        // setup source image and graphics for conversion.
+        BufferedImage srcImage = new BufferedImage(width, height, srcType);
+        Graphics2D srcG2D = srcImage.createGraphics();
+        srcG2D.setColor(color);
+        srcG2D.fillRect(0, 0, width, height);
+
+        // setup destination image and graphics for conversion.
+        int dstType = BufferedImage.TYPE_BYTE_INDEXED;
+        BufferedImage dstImage = new BufferedImage(width, height, dstType);
+        Graphics2D dstG2D = (Graphics2D)dstImage.getGraphics();
+        // draw source image into Byte Indexed destination
+        dstG2D.drawImage(srcImage, 0, 0, null);
+
+        // draw into ARGB image to verify individual pixel value.
+        BufferedImage argbImage = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        Graphics2D argbG2D = (Graphics2D)argbImage.getGraphics();
+        argbG2D.drawImage(dstImage, 0, 0, null);
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < height; j++) {
+                if (color.getRGB() != argbImage.getRGB(i, j)) {
+                    throw new RuntimeException("Conversion from " +
+                            TYPE_TABLE.get(srcType) + " to BYTE_INDEXED is not"
+                            + " done properly for " + color);
+                }
+            }
+        }
+    }
+
+    public static void main(String args[]) {
+        for (int srcType : SRC_TYPES) {
+            for (Color color : COLORS) {
+                ConvertToByteIndexed(color, srcType);
+            }
+        }
+    }
+}