From bdeefd43ff10f266d91d1ab215fa7ab7a1ad764c Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Fri, 15 Sep 2023 10:47:43 +0000 Subject: [PATCH 1/2] 8312555: Ideographic characters aren't stretched by AffineTransform.scale(2, 1) Ignore bitmaps embedded into fonts for non-uniform scales Backport-of: 62610203f18095cbd25b456f0622bad033a65a5d --- .../native/libfontmanager/freetypeScaler.c | 5 +- .../font/FontScaling/StretchedFontTest.java | 221 ++++++++++++++++++ 2 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/awt/font/FontScaling/StretchedFontTest.java diff --git a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c index 16dc9cd1e44..087169336c2 100644 --- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c +++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, 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 @@ -530,7 +530,8 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative( if ((aa != TEXT_AA_ON) && (fm != TEXT_FM_ON) && !context->doBold && !context->doItalize && (context->transform.yx == 0) && (context->transform.xy == 0) && - (context->transform.xx > 0) && (context->transform.yy > 0)) + (context->transform.xx > 0) && (context->transform.yy > 0) && + (context->transform.xx == context->transform.yy)) { context->useSbits = 1; } diff --git a/test/jdk/java/awt/font/FontScaling/StretchedFontTest.java b/test/jdk/java/awt/font/FontScaling/StretchedFontTest.java new file mode 100644 index 00000000000..da7c233f1c1 --- /dev/null +++ b/test/jdk/java/awt/font/FontScaling/StretchedFontTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023, 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.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Stream; + +import javax.imageio.ImageIO; + +import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment; +import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; +import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON; +import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR; + +/* + * @test + * @bug 8312555 + * @summary Verifies that hieroglyphs are stretched by AffineTransform.scale(2, 1) + * @run main StretchedFontTest + */ +public final class StretchedFontTest { + private static final String TEXT = "\u6F22"; + private static final int FONT_SIZE = 20; + + private static final Color BACKGROUND = Color.WHITE; + private static final Color[] FOREGROUNDS = { + new Color(0xFF000000, true), + new Color(0x7F000000, true) + }; + + private static final AffineTransform STRETCH_TRANSFORM = + AffineTransform.getScaleInstance(2.0, 1.0); + + public static void main(String[] args) { + List errors = + Arrays.stream(getLocalGraphicsEnvironment() + .getAvailableFontFamilyNames(Locale.ENGLISH)) + .map(family -> new Font(family, Font.PLAIN, FONT_SIZE)) + .filter(font -> font.canDisplay(TEXT.codePointAt(0))) + .map(font -> font.deriveFont(STRETCH_TRANSFORM)) + .flatMap(StretchedFontTest::testFont) + .filter(Objects::nonNull) + .toList(); + + if (!errors.isEmpty()) { + errors.forEach(System.err::println); + throw new Error(errors.size() + " failure(s) found;" + + " the first one: " + errors.get(0)); + } + } + + /** + * Tests the font with a set of text antialiasing hints. + * + * @param font the font to test + * @return a stream of test results + * @see #testFont(Font, Object) + */ + private static Stream testFont(final Font font) { + return Stream.of(VALUE_TEXT_ANTIALIAS_OFF, + VALUE_TEXT_ANTIALIAS_ON, + VALUE_TEXT_ANTIALIAS_LCD_HRGB) + .flatMap(hint -> testFont(font, hint)); + } + + /** + * Tests the font with the specified text antialiasing hint and a set of + * foreground colors. + * + * @param font the font to test + * @param hint the text antialiasing hint to test + * @return a stream of test results + * @see #testFont(Font, Object, Color) + */ + private static Stream testFont(final Font font, final Object hint) { + return Stream.of(FOREGROUNDS) + .map(foreground -> testFont(font, hint, foreground)); + } + + /** + * Tests the font with the specified text antialiasing hint and + * foreground color. In case of failure, it saves the rendered + * image to a file. + * + * @param font the font to test + * @param hint the text antialiasing hint to test + * @param foreground the foreground color to use + * @return {@code null} if the text rendered correctly; otherwise, + * a {@code String} with the font family name, the value of + * the rendering hint and the color in hex + */ + private static String testFont(final Font font, + final Object hint, + final Color foreground) { + final Dimension size = getTextSize(font); + final BufferedImage image = + new BufferedImage(size.width, size.height, TYPE_3BYTE_BGR); + + final Graphics2D g2d = image.createGraphics(); + try { + g2d.setColor(BACKGROUND); + g2d.fillRect(0, 0, size.width, size.height); + + g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, hint); + g2d.setColor(foreground); + g2d.setFont(font); + g2d.drawString(TEXT, 0, g2d.getFontMetrics(font).getAscent()); + } finally { + g2d.dispose(); + } + + if (verifyImage(image)) { + return null; + } + String fontName = font.getFontName(Locale.ENGLISH); + String hintValue = getHintString(hint); + String hexColor = String.format("0x%08x", foreground.getRGB()); + saveImage(image, fontName + "-" + hintValue + "-" + hexColor); + return "Font: " + fontName + ", Hint: " + hintValue + ", Color: " + hexColor; + } + + /** + * Verifies the rendered image of the hieroglyph. The hieroglyph + * should be stretched across the entire width of the image. + * If the right half of the image contains only pixels of the background + * color, the hieroglyph isn't stretched correctly + * — it's a failure. + * + * @param image the image to verify + * @return {@code true} if the hieroglyph is stretched correctly; or + * {@code false} if right half of the image contains only + * background-colored pixels, which means the hieroglyph isn't + * stretched. + */ + private static boolean verifyImage(final BufferedImage image) { + final int width = image.getWidth(); + final int height = image.getHeight(); + for (int x = width / 2; x < width; x++) { + for (int y = 0; y < height; y++) { + if (image.getRGB(x, y) != BACKGROUND.getRGB()) { + // Any other color but background means the glyph is stretched + return true; + } + } + } + + // The right side of the image is filled with the background color only, + // the glyph isn't stretched. + return false; + } + + private static String getHintString(final Object hint) { + if (hint == VALUE_TEXT_ANTIALIAS_OFF) { + return "off"; + } else if (hint == VALUE_TEXT_ANTIALIAS_ON) { + return "on"; + } else if (hint == VALUE_TEXT_ANTIALIAS_LCD_HRGB) { + return "lcd"; + } else { + throw new IllegalArgumentException("Unexpected hint: " + hint); + } + } + + private static final BufferedImage dummyImage = + new BufferedImage(5, 5, TYPE_3BYTE_BGR); + + private static Dimension getTextSize(final Font font) { + final Graphics g = dummyImage.getGraphics(); + try { + return g.getFontMetrics(font) + .getStringBounds(TEXT, g) + .getBounds() + .getSize(); + } finally { + g.dispose(); + } + } + + private static void saveImage(final BufferedImage image, + final String fileName) { + try { + ImageIO.write(image, + "png", + new File(fileName + ".png")); + } catch (IOException ignored) { + } + } +} From 73b1959732ca554a6aa8ec596a7ce0baca848d57 Mon Sep 17 00:00:00 2001 From: J9 Build Date: Wed, 20 Sep 2023 13:28:06 +0000 Subject: [PATCH 2/2] Update OPENJDK_TAG to merged level jdk-17.0.9+7 Signed-off-by: J9 Build --- closed/openjdk-tag.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/closed/openjdk-tag.gmk b/closed/openjdk-tag.gmk index c33cfa1e752..83a1691a802 100644 --- a/closed/openjdk-tag.gmk +++ b/closed/openjdk-tag.gmk @@ -1 +1 @@ -OPENJDK_TAG := jdk-17.0.9+6 +OPENJDK_TAG := jdk-17.0.9+7