提交 b3a81122 编写于 作者: S serb

7190349: [macosx] Text (Label) is incorrectly drawn with a rotated g2d

8013569: [macosx] JLabel preferred size incorrect on retina displays with non-default font size
Reviewed-by: prr
上级 3ffc5009
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2013, 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
......@@ -31,7 +31,7 @@ import java.util.*;
import sun.awt.SunHints;
public class CStrike extends FontStrike {
public final class CStrike extends FontStrike {
// Creates the native strike
private static native long createNativeStrikePtr(long nativeFontPtr,
......@@ -68,10 +68,10 @@ public class CStrike extends FontStrike {
Rectangle2D.Float result,
double x, double y);
private CFont nativeFont;
private final CFont nativeFont;
private AffineTransform invDevTx;
private GlyphInfoCache glyphInfoCache;
private GlyphAdvanceCache glyphAdvanceCache;
private final GlyphInfoCache glyphInfoCache;
private final GlyphAdvanceCache glyphAdvanceCache;
private long nativeStrikePtr;
CStrike(final CFont font, final FontStrikeDesc inDesc) {
......@@ -84,11 +84,11 @@ public class CStrike extends FontStrike {
// Normally the device transform should be the identity transform
// for screen operations. The device transform only becomes
// interesting when we are outputting between different dpi surfaces,
// like when we are printing to postscript.
// like when we are printing to postscript or use retina.
if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {
try {
invDevTx = inDesc.devTx.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (NoninvertibleTransformException ignored) {
// ignored, since device transforms should not be that
// complicated, and if they are - there is nothing we can do,
// so we won't worry about it.
......@@ -134,15 +134,13 @@ public class CStrike extends FontStrike {
nativeStrikePtr = 0;
}
// the fractional metrics default on our platform is OFF
private boolean useFractionalMetrics() {
return desc.fmHint == SunHints.INTVAL_FRACTIONALMETRICS_ON;
}
@Override
public int getNumGlyphs() {
return nativeFont.getNumGlyphs();
}
@Override
StrikeMetrics getFontMetrics() {
if (strikeMetrics == null) {
StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());
......@@ -155,74 +153,24 @@ public class CStrike extends FontStrike {
return strikeMetrics;
}
float getGlyphAdvance(int glyphCode) {
return getScaledAdvanceForAdvance(getCachedNativeGlyphAdvance(glyphCode));
}
float getCodePointAdvance(int cp) {
float advance = getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
double glyphScaleX = desc.glyphTx.getScaleX();
double devScaleX = desc.devTx.getScaleX();
if (devScaleX == 0) {
glyphScaleX = Math.sqrt(desc.glyphTx.getDeterminant());
devScaleX = Math.sqrt(desc.devTx.getDeterminant());
}
if (devScaleX == 0) {
devScaleX = Double.NaN; // this an undefined graphics state
}
advance = (float) (advance * glyphScaleX / devScaleX);
return useFractionalMetrics() ? advance : Math.round(advance);
}
// calculate an advance, and round if not using fractional metrics
private float getScaledAdvanceForAdvance(float advance) {
if (invDevTx != null) {
advance *= invDevTx.getScaleX();
}
advance *= desc.glyphTx.getScaleX();
return useFractionalMetrics() ? advance : Math.round(advance);
}
Point2D.Float getCharMetrics(char ch) {
return getScaledPointForAdvance(getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(ch)));
@Override
float getGlyphAdvance(final int glyphCode) {
return getCachedNativeGlyphAdvance(glyphCode);
}
Point2D.Float getGlyphMetrics(int glyphCode) {
return getScaledPointForAdvance(getCachedNativeGlyphAdvance(glyphCode));
@Override
float getCodePointAdvance(final int cp) {
return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
}
// calculate an advance point, and round if not using fractional metrics
private Point2D.Float getScaledPointForAdvance(float advance) {
Point2D.Float pt = new Point2D.Float(advance, 0);
if (!desc.glyphTx.isIdentity()) {
return scalePoint(pt);
}
if (!useFractionalMetrics()) {
pt.x = Math.round(pt.x);
}
return pt;
@Override
Point2D.Float getCharMetrics(final char ch) {
return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));
}
private Point2D.Float scalePoint(Point2D.Float pt) {
if (invDevTx != null) {
// transform the point out of the device space first
invDevTx.transform(pt, pt);
}
desc.glyphTx.transform(pt, pt);
pt.x -= desc.glyphTx.getTranslateX();
pt.y -= desc.glyphTx.getTranslateY();
if (!useFractionalMetrics()) {
pt.x = Math.round(pt.x);
pt.y = Math.round(pt.y);
}
return pt;
@Override
Point2D.Float getGlyphMetrics(final int glyphCode) {
return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);
}
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
......@@ -414,9 +362,7 @@ public class CStrike extends FontStrike {
private SparseBitShiftingTwoLayerArray secondLayerCache;
private HashMap<Integer, Long> generalCache;
public GlyphInfoCache(final Font2D nativeFont,
final FontStrikeDesc desc)
{
GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {
super(nativeFont, desc);
firstLayerCache = new long[FIRST_LAYER_SIZE];
}
......@@ -527,7 +473,7 @@ public class CStrike extends FontStrike {
final int shift;
final int secondLayerLength;
public SparseBitShiftingTwoLayerArray(final int size, final int shift) {
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
this.shift = shift;
this.cache = new long[1 << shift][];
this.secondLayerLength = size >> shift;
......@@ -559,6 +505,12 @@ public class CStrike extends FontStrike {
private SparseBitShiftingTwoLayerArray secondLayerCache;
private HashMap<Integer, Float> generalCache;
// Empty non private constructor was added because access to this
// class shouldn't be emulated by a synthetic accessor method.
GlyphAdvanceCache() {
super();
}
public synchronized float get(final int index) {
if (index < 0) {
if (-index < SECOND_LAYER_SIZE) {
......@@ -609,9 +561,7 @@ public class CStrike extends FontStrike {
final int shift;
final int secondLayerLength;
public SparseBitShiftingTwoLayerArray(final int size,
final int shift)
{
SparseBitShiftingTwoLayerArray(final int size, final int shift) {
this.shift = shift;
this.cache = new float[1 << shift][];
this.secondLayerLength = size >> shift;
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2013, 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
......@@ -31,11 +31,12 @@
@interface AWTStrike : NSObject {
@public
AWTFont * fAWTFont;
CGFloat fSize;
CGFloat fSize;
JRSFontRenderingStyle fStyle;
jint fAAStyle;
jint fAAStyle;
CGAffineTransform fTx;
CGAffineTransform fDevTx;
CGAffineTransform fAltTx; // alternate strike tx used for Sun2D
CGAffineTransform fFontTx;
}
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2013, 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
......@@ -65,6 +65,7 @@ static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 };
invDevTx.b *= -1;
invDevTx.c *= -1;
fFontTx = CGAffineTransformConcat(CGAffineTransformConcat(tx, invDevTx), sInverseTX);
fDevTx = CGAffineTransformInvert(invDevTx);
// the "font size" is the square root of the determinant of the matrix
fSize = sqrt(abs(fFontTx.a * fFontTx.d - fFontTx.b * fFontTx.c));
......@@ -148,7 +149,8 @@ Java_sun_font_CStrike_getNativeGlyphAdvance
{
CGSize advance;
JNF_COCOA_ENTER(env);
AWTFont *awtFont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont;
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtFont = awtStrike->fAWTFont;
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
......@@ -156,6 +158,10 @@ JNF_COCOA_ENTER(env);
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
CFRelease(fallback);
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
advance.width = round(advance.width);
}
JNF_COCOA_EXIT(env);
return advance.width;
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2013, 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
......@@ -455,6 +455,7 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
#define CGGI_GLYPH_BBOX_PADDING 2.0f
static inline GlyphInfo *
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
const AWTStrike *strike,
const CGGI_RenderingMode *mode)
{
size_t pixelSize = mode->glyphDescriptor->pixelSize;
......@@ -477,6 +478,12 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
width = 1;
height = 1;
}
advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
advance.width = round(advance.width);
advance.height = round(advance.height);
}
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
#ifdef USE_IMAGE_ALIGNED_MEMORY
// create separate memory
......@@ -564,10 +571,10 @@ CGGI_CreateImageForUnicode
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
CGSize advance;
JRSFontGetAdvancesForGlyphsAndStyle(fallback, &tx, strike->fStyle, &glyph, 1, &advance);
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
// create the Sun2D GlyphInfo we are going to strike into
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
// fix the context size, just in case the substituted character is unexpectedly large
CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
......@@ -715,7 +722,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
JRSFontGetAdvancesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, strike->fStyle, glyphs, len, advances);
CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
size_t maxWidth = 1;
size_t maxHeight = 1;
......@@ -732,7 +739,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
CGSize advance = advances[i];
CGRect bbox = bboxes[i];
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
......
/*
* Copyright (c) 2013, 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.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* @test
* @bug 7190349
* @summary Verifies that we get correct direction, when draw rotated string.
* @author Sergey Bylokhov
* @run main/othervm DrawRotatedString
*/
public final class DrawRotatedString {
private static final int SIZE = 500;
public static void main(final String[] args) throws IOException {
BufferedImage bi = createBufferedImage(true);
verify(bi);
bi = createBufferedImage(false);
verify(bi);
System.out.println("Passed");
}
private static void verify(BufferedImage bi) throws IOException {
for (int i = 0; i < SIZE; ++i) {
for (int j = 0; j < 99; ++j) {
//Text should not appear before 100
if (bi.getRGB(i, j) != Color.RED.getRGB()) {
ImageIO.write(bi, "png", new File("image.png"));
throw new RuntimeException("Failed: wrong text location");
}
}
}
}
private static BufferedImage createBufferedImage(final boolean aa) {
final BufferedImage bi = new BufferedImage(SIZE, SIZE,
BufferedImage.TYPE_INT_RGB);
final Graphics2D bg = bi.createGraphics();
bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
aa ? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF);
bg.setColor(Color.RED);
bg.fillRect(0, 0, SIZE, SIZE);
bg.translate(100, 100);
bg.rotate(Math.toRadians(90));
bg.setColor(Color.BLACK);
bg.setFont(bg.getFont().deriveFont(20.0f));
bg.drawString("MMMMMMMMMMMMMMMM", 0, 0);
bg.dispose();
return bi;
}
}
/*
* Copyright (c) 2013, 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.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* @test
* @bug 8013569
* @author Sergey Bylokhov
*/
public final class IncorrectTextSize {
static final int scale = 2;
static final int width = 1200;
static final int height = 100;
static BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
static final String TEXT = "The quick brown fox jumps over the lazy dog"
+ "The quick brown fox jumps over the lazy dog";
public static void main(final String[] args) throws IOException {
for (int point = 5; point < 11; ++point) {
Graphics2D g2d = bi.createGraphics();
g2d.setFont(new Font(Font.DIALOG, Font.PLAIN, point));
g2d.scale(scale, scale);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.green);
g2d.drawString(TEXT, 0, 20);
int length = g2d.getFontMetrics().stringWidth(TEXT);
if (length < 0) {
throw new RuntimeException("Negative length");
}
for (int i = (length + 1) * scale; i < width; ++i) {
for (int j = 0; j < height; ++j) {
if (bi.getRGB(i, j) != Color.white.getRGB()) {
g2d.drawLine(length, 0, length, height);
ImageIO.write(bi, "png", new File("image.png"));
System.out.println("length = " + length);
System.err.println("Wrong color at x=" + i + ",y=" + j);
System.err.println("Color is:" + new Color(bi.getRGB(i,
j)));
throw new RuntimeException("Test failed.");
}
}
}
g2d.dispose();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册