--- old/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-04-27 13:38:30.176321000 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-04-27 13:38:29.932198999 +0530 @@ -1668,31 +1668,84 @@ } \ } while (0); +/* + * Antialiased glyph drawing results in artifacts around the character edges + * when text is drawn ontop of translucent background color. The standard + * blending equation for two colors: + * destColor = srcColor * glyphAlpha + destColor * (1 - glyphAlpha) + * works only when srcColor and destColor are opaque. For translucent srcColor + * and destColor, the respective alpha components in each color will influence + * the visibility of the color and the visibility of the color below it. Hence + * the equation for blending is given as: + * resA = srcAlpha + dstAlpha * (1 - srcAlpha) + * resCol = (srcColor * srcAlpha + destColor * destAlpha * (1- srcAlpha))/resA + * In addition, srcAlpha is multiplied with the glyphAlpha- that indicates the + * grayscale mask value of the glyph being drawn. The combined result provides + * smooth antialiased text on the buffer without any artifacts. Since the + * logic is executed for every pixel in a glyph, the implementation is further + * optimized to reduce computation and improve execution time. + */ #define GlyphListAABlend4ByteArgb(DST, GLYPH_PIXELS, PIXEL_INDEX, DST_PTR, \ FG_PIXEL, PREFIX, SRC_PREFIX) \ - do { \ - DeclareAlphaVarFor4ByteArgb(dstA) \ - DeclareCompVarsFor4ByteArgb(dst) \ + do { \ + DeclareAlphaVarFor4ByteArgb(resA) \ + DeclareCompVarsFor4ByteArgb(res) \ jint mixValSrc = GLYPH_PIXELS[PIXEL_INDEX]; \ if (mixValSrc) { \ - if (mixValSrc < 255) { \ - jint mixValDst = 255 - mixValSrc; \ - Load ## DST ## To4ByteArgb(DST_PTR, pix, PIXEL_INDEX, \ - dstA, dstR, dstG, dstB); \ - dstA = MUL8(dstA, mixValDst) + \ - MUL8(SRC_PREFIX ## A, mixValSrc); \ - MultMultAddAndStore4ByteArgbComps(dst, mixValDst, dst, \ - mixValSrc, SRC_PREFIX); \ - if (!(DST ## IsOpaque) && \ - !(DST ## IsPremultiplied) && dstA && dstA < 255) { \ - DivideAndStore4ByteArgbComps(dst, dst, dstA); \ + if (mixValSrc != 0xff) { \ + PromoteByteAlphaFor4ByteArgb(mixValSrc); \ + resA = MultiplyAlphaFor4ByteArgb(mixValSrc, SRC_PREFIX ## A); \ + } else { \ + resA = SRC_PREFIX ## A; \ + } \ + if (resA != MaxValFor4ByteArgb) { \ + DeclareAndInvertAlphaVarFor4ByteArgb(dstF, resA) \ + DeclareAndClearAlphaVarFor4ByteArgb(dstA) \ + DeclareCompVarsFor4ByteArgb(dst) \ + DeclareCompVarsFor4ByteArgb(tmp) \ + MultiplyAndStore4ByteArgbComps(res, resA, SRC_PREFIX); \ + if (!(DST ## IsPremultiplied)) { \ + Load ## DST ## To4ByteArgb(DST_PTR, pix, PIXEL_INDEX, \ + dstA, dstR, dstG, dstB); \ + Store4ByteArgbCompsUsingOp(tmp, =, dst); \ + } else { \ + Declare ## DST ## AlphaLoadData(DstPix) \ + jint pixelOffset = PIXEL_INDEX * (DST ## PixelStride); \ + DST ## DataType *pixelAddress = PtrAddBytes(DST_PTR, \ + pixelOffset); \ + LoadAlphaFrom ## DST ## For4ByteArgb(pixelAddress, \ + DstPix, \ + dst); \ + Postload4ByteArgbFrom ## DST(pixelAddress, \ + DstPix, \ + tmp); \ + } \ + if (dstA) { \ + Declare ## DST ## SrcOverDstBlendFactor(blendF) \ + dstA = MultiplyAlphaFor4ByteArgb(dstF, dstA); \ + resA += dstA; \ + blendF = Store ## DST ## SrcOverDstBlendFactor(dstF, \ + dstA); \ + if (blendF != MaxValFor4ByteArgb) { \ + MultiplyAndStore4ByteArgbComps(tmp, \ + blendF, \ + tmp); \ + } \ + Store4ByteArgbCompsUsingOp(res, +=, tmp); \ } \ - Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ - PIXEL_INDEX, dst); \ } else { \ Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ FG_PIXEL, PREFIX); \ + break; \ + } \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && resA && \ + resA < MaxValFor4ByteArgb) \ + { \ + DivideAndStore4ByteArgbComps(res, res, resA); \ } \ + Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ + PIXEL_INDEX, res); \ } \ } while (0); --- old/src/java.desktop/share/native/libawt/java2d/loops/IntArgb.h 2016-04-27 13:38:30.740603000 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/IntArgb.h 2016-04-27 13:38:30.496481000 +0530 @@ -208,4 +208,19 @@ COMP_PREFIX ## A = (COMP_PREFIX ## A << 8) + COMP_PREFIX ## A; \ } while (0) +/* + * Declare ## TYPE ## SrcOverDstBlendFactor + * Declares blend factor variable to be used for blending destination color + * components with source color. + */ +#define DeclareIntArgbSrcOverDstBlendFactor(PREFIX) \ + jint PREFIX; + +/* + * Store ## TYPE ## SrcOverDstBlendFactor + * Returns appropriate blend value for use in blending calculations. + */ +#define StoreIntArgbSrcOverDstBlendFactor(dF, dA) \ + (dA) + #endif /* IntArgb_h_Included */ --- old/src/java.desktop/share/native/libawt/java2d/loops/IntArgbPre.h 2016-04-27 13:38:31.248856999 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/IntArgbPre.h 2016-04-27 13:38:31.040753000 +0530 @@ -216,4 +216,19 @@ COMP_PREFIX ## G, \ COMP_PREFIX ## B) +/* + * Declare ## TYPE ## SrcOverDstBlendFactor + * Declares blend factor variable to be used for blending destination color + * components with source color. + */ +#define DeclareIntArgbPreSrcOverDstBlendFactor(PREFIX) \ + jint PREFIX; + +/* + * Store ## TYPE ## SrcOverDstBlendFactor + * Returns appropriate blend value for use in blending calculations. + */ +#define StoreIntArgbPreSrcOverDstBlendFactor(dF, dA) \ + (dF) + #endif /* IntArgbPre_h_Included */ --- old/src/java.desktop/share/native/libawt/java2d/loops/IntArgbBm.h 2016-04-27 13:38:31.765115000 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/IntArgbBm.h 2016-04-27 13:38:31.557010999 +0530 @@ -206,4 +206,19 @@ COMP_PREFIX ## A = (COMP_PREFIX ## A << 8) + COMP_PREFIX ## A; \ } while (0) +/* + * Declare ## TYPE ## SrcOverDstBlendFactor + * Declares blend factor variable to be used for blending destination color + * components with source color. + */ +#define DeclareIntArgbBmSrcOverDstBlendFactor(PREFIX) \ + jint PREFIX; + +/* + * Store ## TYPE ## SrcOverDstBlendFactor + * Returns appropriate blend value for use in blending calculations. + */ +#define StoreIntArgbBmSrcOverDstBlendFactor(dF, dA) \ + (dA) + #endif /* IntArgbBm_h_Included */ --- old/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgr.h 2016-04-27 13:38:32.257360999 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgr.h 2016-04-27 13:38:32.045255000 +0530 @@ -191,4 +191,19 @@ COMP_PREFIX ## A, COMP_PREFIX ## R, \ COMP_PREFIX ## G, COMP_PREFIX ## B) +/* + * Declare ## TYPE ## SrcOverDstBlendFactor + * Declares blend factor variable to be used for blending destination color + * components with source color. + */ +#define DeclareFourByteAbgrSrcOverDstBlendFactor(PREFIX) \ + jint PREFIX; + +/* + * Store ## TYPE ## SrcOverDstBlendFactor + * Returns appropriate blend value for use in blending calculations. + */ +#define StoreFourByteAbgrSrcOverDstBlendFactor(dF, dA) \ + (dA) + #endif /* FourByteAbgr_h_Included */ --- old/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.h 2016-04-27 13:38:32.749607000 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/FourByteAbgrPre.h 2016-04-27 13:38:32.541503000 +0530 @@ -217,4 +217,19 @@ (pRas)[4*(x)+3] = (jubyte) COMP_PREFIX ## R; \ } while (0) +/* + * Declare ## TYPE ## SrcOverDstBlendFactor + * Declares blend factor variable to be used for blending destination color + * components with source color. + */ +#define DeclareFourByteAbgrPreSrcOverDstBlendFactor(PREFIX) \ + jint PREFIX; + +/* + * Store ## TYPE ## SrcOverDstBlendFactor + * Returns appropriate blend value for use in blending calculations. + */ +#define StoreFourByteAbgrPreSrcOverDstBlendFactor(dF, dA) \ + (dF) + #endif /* FourByteAbgrPre_h_Included */ --- /dev/null 2016-04-27 12:34:56.547689000 +0530 +++ new/test/java/awt/Graphics2D/DrawString/AntialiasedTextArtifact.java 2016-04-27 13:38:33.045755000 +0530 @@ -0,0 +1,135 @@ +/* + * 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. + */ +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * @test + * @bug 8015070 + * @summary Tests for artifacts around the edges of anti-aliased text + * drawn over translucent background color. + */ +public class AntialiasedTextArtifact { + /* Image dimensions */ + private static final int TEST_IMAGE_WIDTH = 2800; + private static final int TEST_IMAGE_HEIGHT = 100; + private static final String TEST_STRING = + "The quick brown fox jumps over the lazy dog. 0123456789."; + + /* + * The artifacts appear when text is drawn ontop of translucent + * background. In other words, a background with alpha channel. + * Hence we test the algorithm for image types that contain either + * straight alpha channel or pre-multiplied alpha channel. In + * addition we test the images with other common pixel formats. + */ + private static final int[] TYPES = {BufferedImage.TYPE_INT_ARGB, + BufferedImage.TYPE_INT_ARGB_PRE, + BufferedImage.TYPE_4BYTE_ABGR, + BufferedImage.TYPE_4BYTE_ABGR_PRE, + BufferedImage.TYPE_INT_RGB, + BufferedImage.TYPE_INT_BGR, + BufferedImage.TYPE_3BYTE_BGR}; + + public static void main(String[] args) throws IOException { + /* Iterate over different image types */ + for (int type : TYPES) { + BufferedImage testImg = getBufferedImage(type); + + /* Draw anti-aliased string and check for artifacts */ + drawAntialiasedString(testImg); + checkArtifact(testImg); + } + } + + private static BufferedImage getBufferedImage(int imageType) { + /* Create a Graphics2D object from the given image type */ + BufferedImage image = new BufferedImage(TEST_IMAGE_WIDTH, + TEST_IMAGE_HEIGHT, + imageType); + return image; + } + + private static void drawAntialiasedString(BufferedImage image) { + /* Create Graphics2D object */ + Graphics2D graphics = (Graphics2D) image.getGraphics(); + + /* Fill the image with translucent color */ + graphics.setColor(new Color(127, 127, 127, 127)); + graphics.fillRect(0, 0, TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT); + + /* Drawstring with Antialiasing hint */ + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Font font = new Font("Verdana" , Font.PLAIN, 60); + graphics.setFont(font); + graphics.setColor(new Color(255, 0, 0)); + graphics.drawString(TEST_STRING, 10, 75); + graphics.dispose(); + } + + private static void checkArtifact(BufferedImage image) throws IOException { + int componentMask = 0xff; + int colorThreshold = 200; + int rowIndex = 0; + int colIndex = 0; + + /* Loop through every pixel to check for possible artifact */ + for (rowIndex = 0; rowIndex < image.getHeight(); rowIndex++) { + for (colIndex = 0; colIndex < image.getWidth(); colIndex++) { + /* + * API: getRGB(x,y) returns color in INT_ARGB color space. + * Extract individual color components with a simple mask. + */ + int colorValue = image.getRGB(colIndex, rowIndex); + int colorComponent1 = colorValue & componentMask; + int colorComponent2 = (colorValue>>8) & componentMask; + int colorComponent3 = (colorValue>>16) & componentMask; + + /* + * Artifacts are predominantly a subjective decision based on + * the quality of the rendered image content. However, in the + * current use-case, the artifacts around the edges of the anti + * aliased text appear like spots of white pixels without any + * relation to the color of foreground text or the background + * translucent shape. + * + * To identify the artifact pixels, each color component from + * the testImage is compared with a constant threshold. The + * component threshold has been set based on observation from + * different experiments on mulitple Java versions. + */ + if (colorComponent1 >= colorThreshold + && colorComponent2 >= colorThreshold + && colorComponent3 >= colorThreshold) { + /* Artifact has been noticed. Report error. */ + throw new RuntimeException("Test Failed."); + } + } + } + } +}