--- old/src/java.desktop/share/native/libawt/java2d/loops/AlphaMacros.h 2016-04-20 14:20:31.654727059 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/AlphaMacros.h 2016-04-20 14:20:31.450727059 +0530 @@ -87,6 +87,7 @@ jint srcScan = pSrcInfo->scanStride; \ jint dstScan = pDstInfo->scanStride; \ jboolean loadsrc, loaddst; \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ SRC ## DataType *pSrc = (SRC ## DataType *) (srcBase); \ DST ## DataType *pDst = (DST ## DataType *) (dstBase); \ Declare ## SRC ## AlphaLoadData(SrcPix) \ @@ -514,6 +515,7 @@ DeclareAlphaVarFor ## STRATEGY(dstFbase) \ jint rasScan = pRasInfo->scanStride; \ jboolean loaddst; \ + jboolean representsPrimary = (pRasInfo)->representsPrimary; \ TYPE ## DataType *pRas = (TYPE ## DataType *) (rasBase); \ Declare ## TYPE ## AlphaLoadData(DstPix) \ Declare ## TYPE ## StoreVars(DstWrite) \ --- old/src/java.desktop/windows/native/libawt/windows/colordata.h 2016-04-20 14:20:32.234727059 +0530 +++ new/src/java.desktop/windows/native/libawt/windows/colordata.h 2016-04-20 14:20:32.038727059 +0530 @@ -33,6 +33,7 @@ char* img_oda_blue; unsigned char* img_clr_tbl; int *pGrayInverseLutData; + int representsPrimary; } ColorData; #define CANFREE(pData) (pData) --- old/src/java.desktop/unix/native/common/awt/colordata.h 2016-04-20 14:20:32.778727059 +0530 +++ new/src/java.desktop/unix/native/common/awt/colordata.h 2016-04-20 14:20:32.582727059 +0530 @@ -44,6 +44,7 @@ char* img_oda_blue; int *pGrayInverseLutData; int screendata; + int representsPrimary; } ColorData; --- old/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-04-20 14:20:33.342727059 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-04-20 14:20:33.134727059 +0530 @@ -907,6 +907,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## SRC ## LoadVars(SrcRead) \ Declare ## DST ## StoreVars(DstWrite) \ \ @@ -932,6 +933,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## LUT_STRATEGY ## Lut(SRC, DST, pixLut) \ \ @@ -963,6 +965,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## SRC ## LoadVars(SrcRead) \ Declare ## DST ## StoreVars(DstWrite) \ \ @@ -992,6 +995,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## LUT_STRATEGY ## Lut(SRC, DST, pixLut) \ \ @@ -1022,6 +1026,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## SRC ## LoadVars(SrcRead) \ Declare ## DST ## StoreVars(DstWrite) \ \ @@ -1049,6 +1054,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## LUT_STRATEGY ## XparLut(SRC, DST, pixLut) \ \ @@ -1081,6 +1087,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## LUT_STRATEGY ## XparLut(SRC, DST, pixLut) \ \ @@ -1115,6 +1122,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## SRC ## LoadVars(SrcRead) \ Declare ## DST ## StoreVars(DstWrite) \ \ @@ -1145,6 +1153,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## SRC ## LoadVars(SrcRead) \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## DST ## PixelData(bgdata) \ @@ -1175,6 +1184,7 @@ NativePrimitive *pPrim, \ CompositeInfo *pCompInfo) \ { \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DST ## StoreVars(DstWrite) \ Declare ## LUT_STRATEGY ## BgLut(SRC, DST, pixLut) \ \ @@ -1208,6 +1218,7 @@ { \ jint xorpixel = pCompInfo->details.xorPixel; \ juint alphamask = pCompInfo->alphaMask; \ + jboolean representsPrimary = (pDstInfo)->representsPrimary; \ Declare ## DSTANYTYPE ## PixelData(xor) \ Declare ## DSTANYTYPE ## PixelData(mask) \ Declare ## SRC ## LoadVars(SrcRead) \ @@ -1752,6 +1763,8 @@ { \ jint glyphCounter; \ jint scan = pRasInfo->scanStride; \ + jboolean representsPrimary = (pRasInfo)->representsPrimary; \ +\ DST ## DataType *pPix; \ Declare ## DST ## PixelData(solidpix) \ DeclareAlphaVarFor ## STRATEGY(srcA) \ @@ -1898,6 +1911,7 @@ { \ jint glyphCounter, bpp; \ jint scan = pRasInfo->scanStride; \ + jboolean representsPrimary = (pRasInfo)->representsPrimary; \ DST ## DataType *pPix; \ Declare ## DST ## PixelData(solidpix) \ DeclareAlphaVarFor ## STRATEGY(srcA) \ --- old/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c 2016-04-20 14:20:33.946727059 +0530 +++ new/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c 2016-04-20 14:20:33.746727059 +0530 @@ -231,12 +231,14 @@ pRasInfo->redErrTable = NULL; pRasInfo->grnErrTable = NULL; pRasInfo->bluErrTable = NULL; + pRasInfo->representsPrimary = 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->representsPrimary = bipriv->cData->representsPrimary; } } @@ -259,6 +261,88 @@ } } +static jboolean calculatePrimaryColorsAprroximation(int* cmap, unsigned char* cube, int cube_size) { + int i, j, k; + jboolean represents_primary = JNI_TRUE; + int index, value, color; + // maximum variaton allowed for r,g,b values + int dr, dg, db; + // values calculated from cmap + int r, g, b; + // maximum 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)& 0xff; + (g) = ((color) >> 8) & 0xff; + (b) = ((color) >> 16) & 0xff; + /* based on value of i, j, k we can set expected values for r, g, b + * if i,j,k is 0 then r,g,b is 0, if i,j,k is 31 then r,g,b is 255 + */ + dr = (i % (cube_size - 2)) * 255; + dg = (j % (cube_size - 2)) * 255; + db = (k % (cube_size - 2)) * 255; + + // if i,j,k values are 31 delta will be -5 and if i,j,k values are 0 + // delta will be +5. if r,g,b values from cmap vary more than dr,dg,db + // then they dont represent primary colors properly. + if (i % (cube_size - 2)) { + dr -= delta; + if (r < dr) { + represents_primary = JNI_FALSE; + break; + } + } + else { + dr += delta; + if (r > dr) { + represents_primary = JNI_FALSE; + break; + } + } + if (j % (cube_size - 2)) { + dg -= delta; + if (g < dg) { + represents_primary = JNI_FALSE; + break; + } + } + else { + dg += delta; + if (g > dg) { + represents_primary = JNI_FALSE; + break; + } + } + if (k % (cube_size - 2)) { + db -= delta; + if (b < db) { + represents_primary = JNI_FALSE; + break; + } + } + else { + db += delta; + if (b > db) { + represents_primary = JNI_FALSE; + break; + } + } + } + } + } + return represents_primary; +} + static ColorData *BufImg_SetupICM(JNIEnv *env, BufImgSDOps *bisdo) { @@ -298,6 +382,7 @@ } cData->img_clr_tbl = initCubemap(pRgb, bisdo->lutsize, 32); + cData->representsPrimary = calculatePrimaryColorsAprroximation(pRgb, cData->img_clr_tbl, 32); if (allGray == JNI_TRUE) { initInverseGrayLut(pRgb, bisdo->lutsize, cData); } --- old/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.c 2016-04-20 14:20:34.462727059 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.c 2016-04-20 14:20:34.294727059 +0530 @@ -163,6 +163,7 @@ DeclareByteIndexedLoadVars(DstRead) jint srcScan = pSrcInfo->scanStride; jint dstScan = pDstInfo->scanStride; + jboolean representsPrimary = (pDstInfo)->representsPrimary; InitByteIndexedLoadVars(SrcRead, pSrcInfo); InitByteIndexedLoadVars(DstRead, pDstInfo); @@ -207,6 +208,7 @@ DeclareByteIndexedLoadVars(DstRead) jint srcScan = pSrcInfo->scanStride; jint dstScan = pDstInfo->scanStride; + jboolean representsPrimary = (pDstInfo)->representsPrimary; DeclareByteIndexedStoreVars(DstWrite) InitByteIndexedLoadVars(SrcRead, pSrcInfo); --- old/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.h 2016-04-20 14:20:35.022727059 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/ByteIndexed.h 2016-04-20 14:20:34.794727059 +0530 @@ -168,9 +168,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)) && \ + (representsPrimary == 1))) { \ + 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) --- old/src/java.desktop/share/native/libawt/java2d/SurfaceData.h 2016-04-20 14:20:35.562727059 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/SurfaceData.h 2016-04-20 14:20:35.342727059 +0530 @@ -163,6 +163,7 @@ char *grnErrTable; /* Green ordered dither table */ char *bluErrTable; /* Blue ordered dither table */ int *invGrayTable; /* Inverse gray table */ + int representsPrimary; /* whether cmap represents primary colors */ union { void *align; /* ensures strict alignment */ char data[SD_RASINFO_PRIVATE_SIZE]; --- old/make/data/x11wrappergen/xlibtypes.txt 2016-04-20 14:20:36.114727059 +0530 +++ new/make/data/x11wrappergen/xlibtypes.txt 2016-04-20 14:20:35.914727059 +0530 @@ -749,6 +749,7 @@ img_oda_blue pointer byte pGrayInverseLutData pointer int screendata int + representsPrimary int AwtGraphicsConfigData awt_depth int --- old/make/data/x11wrappergen/sizes.32 2016-04-20 14:20:36.682727059 +0530 +++ new/make/data/x11wrappergen/sizes.32 2016-04-20 14:20:36.490727059 +0530 @@ -206,7 +206,8 @@ ColorData.img_oda_blue 32 ColorData.pGrayInverseLutData 36 ColorData.screendata 40 -ColorData 44 +ColorData.representsPrimary 44 +ColorData 48 XFontStruct.ext_data 0 XFontStruct.fid 4 XFontStruct.direction 8 --- old/make/data/x11wrappergen/sizes.64 2016-04-20 14:20:37.290727059 +0530 +++ new/make/data/x11wrappergen/sizes.64 2016-04-20 14:20:37.082727059 +0530 @@ -206,6 +206,7 @@ ColorData.img_oda_blue 64 ColorData.pGrayInverseLutData 72 ColorData.screendata 80 +ColorData.representsPrimary 84 ColorData 88 XFontStruct.ext_data 0 XFontStruct.fid 8 --- old/make/data/x11wrappergen/sizes.64-solaris-i386 2016-04-20 14:20:37.870727059 +0530 +++ new/make/data/x11wrappergen/sizes.64-solaris-i386 2016-04-20 14:20:37.682727059 +0530 @@ -206,6 +206,7 @@ ColorData.img_oda_blue 64 ColorData.pGrayInverseLutData 72 ColorData.screendata 80 +ColorData.representsPrimary 84 ColorData 88 XFontStruct.ext_data 0 XFontStruct.fid 8 --- /dev/null 2016-04-20 11:11:33.642641000 +0530 +++ new/test/sun/java2d/loops/ConvertToByteIndexedTest.java 2016-04-20 14:20:38.226727059 +0530 @@ -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 TYPE_TABLE = + new HashMap(); + + 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); + } + } + } +}