JDK-8065373 : [macosx] jdk8, jdk7u60 Regression in Graphics2D drawing of derived Fonts
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 7u60,8,9
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86
  • Submitted: 2014-11-19
  • Updated: 2015-09-29
  • Resolved: 2014-12-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u79Fixed 8u45Fixed 9 b45Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Mac OSX 10.9.5

A DESCRIPTION OF THE PROBLEM :
Rotating text is broken when using Font#deriveFont to draw String.
See sample.
It seems regression appeared after Java 7 u40.
It affects currently Apache JMeter project, see:
https://issues.apache.org/bugzilla/show_bug.cgi?id=57221

Thanks for help

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run on Java 7u40, you get a Frame with correclty drawn text
Run it with Java8 or Java7u71, you get a strange rotation of characters

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Correct rotation of text
ACTUAL -
Strange rotation of text which does not comply with previous behaviour

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error message

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test extends JPanel {
    Rectangle2D.Float rect = new Rectangle2D.Float(200, 200, 220, 35);
    // float theta = 1.1748778437843f;
    double theta = Math.PI / 6;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);
        g2.setPaint(Color.blue);
        Font font = g2.getFont().deriveFont(18f);
        g2.setFont(font);
        FontRenderContext frc = g2.getFontRenderContext();
        String s = "This text should be rotated";
        float width = (float) font.getStringBounds(s, frc).getWidth();
        LineMetrics lm = font.getLineMetrics(s, frc);
        float height = lm.getAscent() + lm.getDescent();
        // Scale text into rect.
        float xScale = rect.width / width;
        float yScale = rect.height / height;
        float scale = (xScale > yScale) ? yScale : xScale;
        // Locate string origin.
        double x = rect.x;
        double y = rect.y + (rect.height + scale * height) / 2 - scale
                * lm.getDescent();
        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
        at.scale(scale, scale);
        AffineTransform rotator = new AffineTransform();
        rotator.rotate(theta, rect.getCenterX(), rect.getCenterY());
        GeneralPath rect2 = new GeneralPath(
                rotator.createTransformedShape(rect));
        // Draw with no rotation.
        g2.draw(rect);
        g2.setPaint(Color.red);
        g2.setFont(font.deriveFont(at));
        g2.drawString(s, 0, 0);
        // Rotate once.
        g2.setPaint(Color.blue);
        g2.draw(rect2);
        rotator.concatenate(at);
        g2.setFont(font.deriveFont(rotator));
        g2.setPaint(Color.red);
        g2.drawString(s, 0, 0);
        // Rotate again.
        rotator.setToIdentity();
        rotator.rotate(2 * theta, rect.getCenterX(), rect.getCenterY());
        rect2 = new GeneralPath(rotator.createTransformedShape(rect));
        g2.setPaint(Color.blue);
        g2.draw(rect2);
        rotator.concatenate(at);
        g2.setFont(font.deriveFont(rotator));
        g2.setPaint(Color.red);
        g2.drawString(s, 0, 0);
        // Check scaled string bounds.
        // this was handled by the fractional metrics rendering hint
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.setPreferredSize(new Dimension(800, 600));
        JFrame f = new JFrame();
        f.getContentPane().add(test);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None found


Comments
There is no jdk7u-dev, so I need an approval to push the fix to jdk7u-cpu directly Lana Steuck: "Please note that 7u-dev is now completely closed. If you accidentally push anything there - it won't get integrated. After today's deadline, if you have a fix for 7u80, it should go via the approval process for critical fixes and push your fix into 7u80 (all in closed). There won't be 7u80-dev for 7u80."
19-01-2015

The issue affects jdk7. What about the backport to 7? SQE OK to take the fix into CPU15_02.
19-01-2015

CPU15_02-critical-request: issue impact: Rotated text is drawn in the wrong direction. Fix rational: regression, which was introduced in jdk7/8/9 Suggested testing: jck, regression, functional testing in fonts & 2d area. Webrev can be found at: http://cr.openjdk.java.net/~serb/8065373/webrev.00 jdk9 changeset: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/5d13458da243 Review: http://mail.openjdk.java.net/pipermail/2d-dev/2014-December/004972.html Reviewers: Phil Race, Andrew Brygin
16-01-2015

This is not a showstopper for 8u40 as an issue introduced in 7u60 and 8 GA.
12-12-2014

philippe.mouawad@gmail.com confirmed that the bug is NOt reproducible on Windows 7. felix.schumacher@internetallee.de confirmed that the bug is NOt reproducible on ubuntu 14.04.
20-11-2014

Here is a comment from the submitter about suggested workaround: On 11/19/14 08:53 PM, Philippe Mouawad wrote: > Thanks for fast answer and workaround although as it's a third party library it will be a problem. I tested workaround it is not enough as with it test is moved from its location.
20-11-2014

Can this be read as a regression from jdk 7 to jdk 8?
20-11-2014

Not a regression in jdk8
19-11-2014

The fix above does not solve printing, so the problem somewhere deeper.
19-11-2014

Suggested fix: diff -r 67ad12a0cbaa src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m --- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m Tue Nov 18 17:10:34 2014 +0400 +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m Wed Nov 19 20:54:18 2014 +0300 @@ -65,7 +65,7 @@ invDevTx.b *= -1; invDevTx.c *= -1; fFontTx = CGAffineTransformConcat(CGAffineTransformConcat(tx, invDevTx), sInverseTX); - fDevTx = CGAffineTransformInvert(invDevTx); + fDevTx = CGAffineTransformInvert(CGAffineTransformConcat(invDevTx, sInverseTX)); // the "font size" is the square root of the determinant of the matrix fSize = sqrt(abs(fFontTx.a * fFontTx.d - fFontTx.b * fFontTx.c));
19-11-2014

Workaround is to apply a transfrom to the graphics instead of font: + g2.transform(rotator); - g2.setFont(font.deriveFont(rotator));
19-11-2014

Possibly related to JDK-7190349
19-11-2014