8233006: freetype incorrectly adjusts advances when emboldening rotated glyphs
Reviewed-by: serb, jdv
This commit is contained in:
parent
62a2354299
commit
9ad39392db
@ -50,7 +50,6 @@
|
||||
#define FloatToFTFixed(f) (FT_Fixed)((f) * (float)(ftFixed1))
|
||||
#define FTFixedToFloat(x) ((x) / (float)(ftFixed1))
|
||||
#define FT26Dot6ToFloat(x) ((x) / ((float) (1<<6)))
|
||||
#define FT26Dot6ToInt(x) (((int)(x)) >> 6)
|
||||
|
||||
typedef struct {
|
||||
/* Important note:
|
||||
@ -296,6 +295,71 @@ static void setInterpreterVersion(FT_Library library) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(upem, y_scale) / 24
|
||||
* I prefer something a little less bold, so using 32 instead of 24.
|
||||
*/
|
||||
#define BOLD_DIVISOR (32)
|
||||
#define BOLD_FACTOR(units_per_EM, y_scale) \
|
||||
((FT_MulFix(units_per_EM, y_scale) / BOLD_DIVISOR ))
|
||||
|
||||
#define BOLD_MODIFIER(units_per_EM, y_scale) \
|
||||
(context->doBold ? BOLD_FACTOR(units_per_EM, y_scale) : 0)
|
||||
|
||||
static void GlyphSlot_Embolden(FT_GlyphSlot slot, FT_Matrix transform) {
|
||||
FT_Pos extra = 0;
|
||||
|
||||
/*
|
||||
* Does it make sense to embolden an empty image, such as SPACE ?
|
||||
* We'll say no. A fixed width font might be the one case, but
|
||||
* nothing in freetype made provision for this. And freetype would also
|
||||
* have adjusted the metrics of zero advance glyphs (we won't, see below).
|
||||
*/
|
||||
if (!slot ||
|
||||
slot->format != FT_GLYPH_FORMAT_OUTLINE ||
|
||||
slot->metrics.width == 0 ||
|
||||
slot->metrics.height == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
extra = BOLD_FACTOR(slot->face->units_per_EM,
|
||||
slot->face->size->metrics.y_scale);
|
||||
|
||||
/*
|
||||
* It should not matter that the outline is rotated already,
|
||||
* since we are applying the strength equally in X and Y.
|
||||
* If that changes, then it might.
|
||||
*/
|
||||
FT_Outline_Embolden(&slot->outline, extra);
|
||||
slot->metrics.width += extra;
|
||||
slot->metrics.height += extra;
|
||||
|
||||
// Some glyphs are meant to be used as marks or diacritics, so
|
||||
// have a shape but do not have an advance.
|
||||
// Let's not adjust the metrics of any glyph that is zero advance.
|
||||
if (slot->linearHoriAdvance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot->advance.x) {
|
||||
slot->advance.x += FT_MulFix(extra, transform.xx);
|
||||
}
|
||||
|
||||
if (slot->advance.y) {
|
||||
slot->advance.y += FT_MulFix(extra, transform.yx);
|
||||
}
|
||||
|
||||
// The following need to be adjusted but no rotation
|
||||
// linear advance is in 16.16 format, extra is 26.6
|
||||
slot->linearHoriAdvance += extra << 10;
|
||||
// these are pixel values stored in 26.6 format.
|
||||
slot->metrics.horiAdvance += extra;
|
||||
slot->metrics.vertAdvance += extra;
|
||||
slot->metrics.horiBearingY += extra;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
* Method: initNativeScaler
|
||||
@ -523,13 +587,6 @@ static int setupFTContext(JNIEnv *env,
|
||||
// using same values as for the transformation matrix
|
||||
#define OBLIQUE_MODIFIER(y) (context->doItalize ? ((y)*FT_MATRIX_OBLIQUE_XY/FT_MATRIX_ONE) : 0)
|
||||
|
||||
/* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
|
||||
* strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
|
||||
* been taken from libfreetype version 2.6 and remain valid at least up to
|
||||
* 2.9.1. */
|
||||
#define BOLD_MODIFIER(units_per_EM, y_scale) \
|
||||
(context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
|
||||
|
||||
/*
|
||||
* Class: sun_font_FreetypeFontScaler
|
||||
* Method: getFontMetricsNative
|
||||
@ -902,7 +959,7 @@ static jlong
|
||||
|
||||
/* apply styles */
|
||||
if (context->doBold) { /* if bold style */
|
||||
FT_GlyphSlot_Embolden(ftglyph);
|
||||
GlyphSlot_Embolden(ftglyph, context->transform);
|
||||
}
|
||||
|
||||
/* generate bitmap if it is not done yet
|
||||
@ -973,13 +1030,11 @@ static jlong
|
||||
(float) - (advh * FTFixedToFloat(context->transform.yx));
|
||||
} else {
|
||||
if (!ftglyph->advance.y) {
|
||||
glyphInfo->advanceX =
|
||||
(float) FT26Dot6ToInt(ftglyph->advance.x);
|
||||
glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
|
||||
glyphInfo->advanceY = 0;
|
||||
} else if (!ftglyph->advance.x) {
|
||||
glyphInfo->advanceX = 0;
|
||||
glyphInfo->advanceY =
|
||||
(float) FT26Dot6ToInt(-ftglyph->advance.y);
|
||||
glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
|
||||
} else {
|
||||
glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
|
||||
glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
|
||||
@ -1153,7 +1208,7 @@ static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
|
||||
|
||||
/* apply styles */
|
||||
if (context->doBold) { /* if bold style */
|
||||
FT_GlyphSlot_Embolden(ftglyph);
|
||||
GlyphSlot_Embolden(ftglyph, context->transform);
|
||||
}
|
||||
|
||||
FT_Outline_Translate(&ftglyph->outline,
|
||||
|
168
test/jdk/java/awt/font/Rotate/RotatedSyntheticBoldTest.java
Normal file
168
test/jdk/java/awt/font/Rotate/RotatedSyntheticBoldTest.java
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 RotatedSyntheticBoldTest
|
||||
* @bug 8233006
|
||||
* @summary This test verifies that rotated synthetically bolded fonts
|
||||
* do not have a wandering baseline
|
||||
* @run main RotatedSyntheticBoldTest
|
||||
*
|
||||
* Note this is designed to be run headless. The creation of the UI
|
||||
* is meant to be run outside the harness as an visualisaton aid to
|
||||
* debugging any failure.
|
||||
*/
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public class RotatedSyntheticBoldTest extends JPanel {
|
||||
|
||||
static String TEXT = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLM";
|
||||
static int SZ = 1000;
|
||||
static Font font;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length > 1) {
|
||||
if (args[0].equals("-family")) {
|
||||
font = new Font(args[1], Font.BOLD, 20);
|
||||
} else if (args[0].equals("-file")) {
|
||||
font = Font.createFont(Font.TRUETYPE_FONT, new File(args[1]));
|
||||
} else {
|
||||
font = new Font(Font.DIALOG, Font.BOLD, 20);
|
||||
}
|
||||
System.out.println("Using " + font);
|
||||
createUI();
|
||||
} else {
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
static void createUI() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JFrame frame = new JFrame("Synthetic Text Test");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.add(new RotatedSyntheticBoldTest());
|
||||
frame.setSize(SZ, SZ);
|
||||
frame.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
public void paint(Graphics g) {
|
||||
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
||||
RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
|
||||
|
||||
AffineTransform tx = g2d.getTransform();
|
||||
int x = 40, y = 0;
|
||||
for (int i=0; i<360; i+=10) {
|
||||
|
||||
g2d.translate(SZ/2, SZ/2);
|
||||
g2d.rotate((double)i/360.0 * Math.PI*2.0);
|
||||
g2d.setFont(font);
|
||||
|
||||
g2d.setColor(Color.BLUE);
|
||||
g2d.drawString(TEXT, x, y);
|
||||
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
GlyphVector gv = font.createGlyphVector(frc, TEXT);
|
||||
Rectangle2D r = gv.getVisualBounds();
|
||||
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
if (r.getHeight() > 1.1 * fm.getHeight()) {
|
||||
System.out.println("FAIL : r= " + r + " hgt=" + fm.getHeight());
|
||||
}
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.translate(x, y);
|
||||
g2d.draw(r);
|
||||
|
||||
g2d.setTransform(tx);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(Graphics2D g2d, Font font) {
|
||||
int x = 40, y = 0;
|
||||
g2d.setFont(font);
|
||||
|
||||
g2d.setColor(Color.BLUE);
|
||||
g2d.drawString(TEXT, x, y);
|
||||
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
GlyphVector gv = font.createGlyphVector(frc, TEXT);
|
||||
Rectangle2D r = gv.getVisualBounds();
|
||||
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
if (r.getHeight() > 1.2 * fm.getHeight()) {
|
||||
System.out.println("FAIL : " + r);
|
||||
}
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.translate(x, y);
|
||||
g2d.draw(r);
|
||||
}
|
||||
|
||||
static void doTest() {
|
||||
GraphicsEnvironment ge =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
String[] families = ge.getAvailableFontFamilyNames();
|
||||
BufferedImage bi =
|
||||
new BufferedImage(SZ, SZ, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = bi.createGraphics();
|
||||
g2d.rotate( Math.PI/4.0);
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
|
||||
boolean failed = false;
|
||||
for (String s : families) {
|
||||
Font font = new Font(s, Font.BOLD, 20);
|
||||
g2d.setFont(font);
|
||||
GlyphVector gv = font.createGlyphVector(frc, TEXT);
|
||||
Rectangle2D r = gv.getVisualBounds();
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
if (r.getHeight() > 1.2 * fm.getHeight()) {
|
||||
failed = true;
|
||||
System.out.println("FAIL : r= " + r + " hgt=" + fm.getHeight() +
|
||||
" font=" + font);
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("test failed");
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test RotatedTextTest
|
||||
* @bug 8203485
|
||||
* @bug 8203485 8233006
|
||||
* @summary This test verifies that rotated text preserves the width.
|
||||
* @run main RotatedTextTest
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user