--- old/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-03-22 14:12:15.678890569 +0530 +++ new/src/java.desktop/share/native/libawt/java2d/loops/LoopMacros.h 2016-03-22 14:12:15.430890569 +0530 @@ -1668,31 +1668,70 @@ } \ } 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 { \ + do { \ DeclareAlphaVarFor4ByteArgb(dstA) \ DeclareCompVarsFor4ByteArgb(dst) \ + DeclareAlphaVarFor4ByteArgb(resA) \ + DeclareCompVarsFor4ByteArgb(res) \ jint mixValSrc = GLYPH_PIXELS[PIXEL_INDEX]; \ if (mixValSrc) { \ - if (mixValSrc < 255) { \ - jint mixValDst = 255 - mixValSrc; \ + if (mixValSrc != 0xff) { \ + PromoteByteAlphaFor4ByteArgb(mixValSrc); \ + resA = MultiplyAlphaFor4ByteArgb(mixValSrc, SRC_PREFIX ## A); \ + MultiplyAndStore4ByteArgbComps(res, resA, SRC_PREFIX); \ + } else { \ + resA = SRC_PREFIX ## A; \ + MultiplyAndStore4ByteArgbComps(res, \ + SRC_PREFIX ## A, \ + SRC_PREFIX); \ + } \ + if (resA != MaxValFor4ByteArgb) { \ + DeclareAndInvertAlphaVarFor4ByteArgb(dstF, resA) \ 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); \ + dstA = MultiplyAlphaFor4ByteArgb(dstF, dstA); \ + if (!(IntArgbIsPremultiplied)) { \ + dstF = dstA; \ } \ - Store ## DST ## From4ByteArgbComps(DST_PTR, pix, \ - PIXEL_INDEX, dst); \ - } else { \ - Store ## DST ## PixelData(DST_PTR, PIXEL_INDEX, \ - FG_PIXEL, PREFIX); \ + resA += dstA; \ + if (dstF) { \ + DeclareCompVarsFor4ByteArgb(tmp) \ + Store4ByteArgbCompsUsingOp(tmp, =, dst); \ + 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-22 12:32:24.472499000 +0530 +++ new/test/java/awt/Graphics2D/DrawString/AADrawStringArtifact.java 2016-03-22 14:12:16.002890569 +0530 @@ -0,0 +1,109 @@ +/* + * 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. + */ +public class AADrawStringArtifact { + /* Image dimensions */ + static final int TEST_IMAGE_WIDTH = 200; + static final int TEST_IMAGE_HEIGHT = 100; + + public AADrawStringArtifact() throws IOException { + /* Create a Graphics2D object from a BufferedImage */ + BufferedImage testImg = new BufferedImage(TEST_IMAGE_WIDTH, + TEST_IMAGE_HEIGHT, + BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = (Graphics2D) testImg.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(); + + /* Check for artifacts */ + checkArtifacts(testImg); + } + + void checkArtifacts(BufferedImage testImage) 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 < testImage.getHeight(); rowIndex++) { + for (colIndex = 0; colIndex < testImage.getWidth(); colIndex++) { + /* + * API: getRGB(x,y) returns color in INT_ARGB color space. + * Extract individual color components with a simple mask. + */ + int colorValue = testImage.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."); + } + } + } + } + + public static void main(String[] args) throws IOException { + /* Mere instantiating this object will suffice for automated test */ + AADrawStringArtifact artifactTest = new AADrawStringArtifact(); + } +}