--- old/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-03-30 14:43:11.599400000 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-03-30 14:43:11.355278000 +0530 @@ -1668,31 +1668,81 @@ } \ } 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); \ - } \ - Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ - PIXEL_INDEX, dst); \ + if (mixValSrc != 0xff) { \ + PromoteByteAlphaFor4ByteArgb(mixValSrc); \ + resA = MultiplyAlphaFor4ByteArgb(mixValSrc, SRC_PREFIX ## A); \ + MultiplyAndStore4ByteArgbComps(res, resA, SRC_PREFIX); \ } else { \ - Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ - FG_PIXEL, PREFIX); \ + if (SRC_PREFIX ## A == MaxValFor4ByteArgb) { \ + Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ + FG_PIXEL, PREFIX); \ + break; \ + } \ + resA = SRC_PREFIX ## A; \ + MultiplyAndStore4ByteArgbComps(res, \ + SRC_PREFIX ## A, \ + SRC_PREFIX); \ + } \ + if (resA != MaxValFor4ByteArgb) { \ + DeclareAndInvertAlphaVarFor4ByteArgb(dstF, resA) \ + DeclareAndClearAlphaVarFor4ByteArgb(dstA) \ + Declare ## DST ## AlphaLoadData(DstPix) \ + jint pixelOffset = PIXEL_INDEX * (DST ## PixelStride); \ + DST ## DataType *pixelAddress = PtrAddBytes(DST_PTR, \ + pixelOffset); \ + LoadAlphaFrom ## DST ## For4ByteArgb(pixelAddress, \ + DstPix, \ + dst); \ + dstA = MultiplyAlphaFor4ByteArgb(dstF, dstA); \ + if (!(DST ## IsPremultiplied)) { \ + dstF = dstA; \ + } \ + resA += dstA; \ + if (dstF) { \ + DeclareCompVarsFor4ByteArgb(tmp) \ + Postload4ByteArgbFrom ## DST(pixelAddress, \ + DstPix, \ + tmp); \ + if (dstF != MaxValFor4ByteArgb) { \ + MultiplyAndStore4ByteArgbComps(tmp, \ + dstF, \ + tmp); \ + } \ + Store4ByteArgbCompsUsingOp(res, +=, tmp); \ + } \ + } \ + if (!(DST ## IsOpaque) && \ + !(DST ## IsPremultiplied) && resA && \ + resA < MaxValFor4ByteArgb) \ + { \ + DivideAndStore4ByteArgbComps(res, res, resA); \ } \ + Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ + PIXEL_INDEX, res); \ } \ } while (0); --- /dev/null 2016-03-30 14:15:42.352014000 +0530 +++ new/test/java/awt/Graphics2D/DrawString/AntialiasedTextArtifact.java 2016-03-30 14:43:11.927564000 +0530 @@ -0,0 +1,130 @@ +/* + * 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 = 200; + private static final int TEST_IMAGE_HEIGHT = 100; + + /* + * 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. There + * are few other types as well that contain alpha channel. However, + * it would suffice to test on the most common formats TYPE_INT_ARGB + * and TYPE_INT_ARGB_PRE. + */ + private static final int[] TYPES = {BufferedImage.TYPE_INT_ARGB, + BufferedImage.TYPE_INT_ARGB_PRE}; + + 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("Save", 30, 65); + 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."); + } + } + } + } +}