JDK-4912220 : 1.4 REGRESSION: Flipping with asymmetric scaling often distorts fonts
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 1.4.1,1.4.2
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2003-08-26
  • Updated: 2006-03-22
  • Resolved: 2006-03-22
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 6
6 b77Fixed
Related Reports
Relates :  
Description
Name: jk109818			Date: 08/25/2003


FULL PRODUCT VERSION :
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06)
Java HotSpot(TM) Client VM (build 1.4.1_02-b06, mixed mode)

FULL OS VERSION :
Microsoft Windows XP [Version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Adapter Type	S3 Savage/IX, S3 Graphics, Inc. compatible
Adapter Description	S3 Graphics Savage/IX 103C
Adapter RAM	4.00 MB (4,194,304 bytes)


A DESCRIPTION OF THE PROBLEM :
Some combinations of scaling parameters and font sizes cause severe font distortion when applying a flip transform as well.  That is, some values of "xscale" and "yscale" used in the following transformation with some font sizes will cause severe font rendering problems:

    | xscale   0        0  |
    | 0       -yscale   0  |

where as the "non-flipped" transformation does not cause any problems:

    | xscale   0        0  |
    | 0        yscale   0  |

Examples are given in the sample program below.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the sample program below.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Flipped fonts should look like mirror images of non-flipped fonts when the same (assymetric) scaling is applied to both.
Fonts are severely distorted when flipped for many combinations of scaling factors and font sizes.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.geom.*;

public class TextFlipBug extends Component {
    static final float PAD = 15.0f;
    static final String STR = "J  Q  K";

    Font font1 = null;
    Font font2 = null;
    float xscale1, xscale2, yscale1, yscale2;
    static final Font IFONT = new Font("Dialog", Font.PLAIN, 12);

    TextFlipBug(float xs1, float ys1, int fs1,
              float xs2, float ys2, int fs2) {
        xscale1 = xs1;
        yscale1 = ys1;
        font1 = new Font("serif", Font.PLAIN, fs1);

        xscale2 = xs2;
        yscale2 = ys2;
        font2 = new Font("serif", Font.PLAIN, fs2);
    }

    private void draw_flip(Graphics2D g2,
                           Font font,                    // Font to use
                           String str,                   // String to point
                           float xscale, float yscale,   // Scale for X and Y
                           float midx, float midy,       // Mid point to draw around
                           boolean left)                 // Print to left or right of midx?
    {
        AffineTransform oldtx = g2.getTransform();

        g2.setFont(font);
        FontMetrics fm = g2.getFontMetrics();

        // If printing left of
        float len = xscale * (float) fm.stringWidth(str);
        float lshift = PAD;
        if (left) {
            lshift = -PAD - len;
        }
        // Scale X and Y and draw string:
        g2.translate(midx + lshift, midy - PAD);
        g2.scale(xscale, yscale);
        g2.drawString(str, 0f, 0f);

        // Flip about Y and draw string:
        g2.setTransform(oldtx);
        g2.translate(midx + lshift, midy + PAD);
        g2.scale(xscale, -yscale);
        g2.drawString(str, 0f, 0f);

        g2.setTransform(oldtx);

        g2.setFont(IFONT);
        String istr = "(XS=" + xscale + ", YS=" + yscale + ", FS=" + font.getSize() + ")";
        fm = g2.getFontMetrics();
        lshift = 2*PAD + len;
        if (left) {
            lshift = - 2*PAD - len - (float) fm.stringWidth(istr);
        }
        g2.drawString(istr, midx + lshift , midy - PAD);


    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;

        // Turning these off/on makes no difference in distortion:
        g2.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS,
                             RenderingHints.VALUE_FRACTIONALMETRICS_ON );
        g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON );
        g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
                             RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
        
        Dimension d = this.getSize();
        float midx = (float) d.getWidth()/2;
        float midy = (float) d.getHeight()/2;

        g2.drawLine((int) midx, 0, (int) midx, d.height);
        g2.drawLine(0, (int) midy, d.width, (int) midy);

        draw_flip(g2, font1, STR, xscale1, yscale1, midx, midy, true);
        draw_flip(g2, font2, STR, xscale2, yscale2, midx, midy, false);
    }

    public static void main(String args[]) {
        Frame f;
        int loc = 0;

        f = new Frame("Yscale under 10.1 is very bad for font size 10 (Xscale closer to 10 helps).");
        TextFlipBug tf = new TextFlipBug(2f, 10.1f, 10, 2f, 10.0f, 10);
        f.add("Center", tf);
        f.pack();
        f.setLocation(loc, loc);
        f.show();
        loc += 50;

        f = new Frame("Font size under 26 is bad for Xscale=2 and Yscale=4.");
        // Any font size below 26 is bad with xscale=2, yscale=4
        tf = new TextFlipBug(2f, 4f, 26, 2f, 4f, 25);
        f.add("Center", tf);
        f.pack();
        f.setLocation(loc, loc);
        f.show();
        loc += 50;

        f = new Frame("Xscale less than 0.9 is bad for Yscale=1.0.");
        //
        tf = new TextFlipBug(0.9f, 1f, 50, 0.5f, 1f, 50);
        f.add("Center", tf);
        f.pack();
        f.setLocation(loc, loc);
        f.show();
        loc += 50;
    }

    public Dimension getPreferredSize() {
        return new Dimension (700, 200);
    }

    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

}


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

CUSTOMER SUBMITTED WORKAROUND :
I couldn't find a satisfactory one.  In some cases drawing to a BufferedImage and then flipping the image with a BufferedImageOp might be sufficient, unless you are drawing within the rectangle which bounds the text.  I've tried making the image transparent, but this introduces other artifacts.

Release Regression From : 1.3.1_08
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Incident Review ID: 184900) 
======================================================================

Comments
EVALUATION This bug was fixed as part of changes for 4654540. Now we perform hinting with stretch transformation matrix that preserves proportions of the glyph. See attachments for before/after examples.
14-03-2006

EVALUATION Truetype hinting is designed without support for arbitrary transforms. To overcome this limitation we are trying to apply hints with "safe" identity matrix and then transform hinted outline when non-trival transform is requested. Reported problem is sideeffect of using identity transform - it does not preserve glyph proportions which are still available as ppem values to the hinting engine. This cause wrong results of computations. Another side of this bug is that glyphs are not symmetrical when user will expect them to be. E.g. using negative scale (say of size -10) will cause hinting with different proportions/ppem values and result will likely differ from flipped version of same glyph but with positive scale transform (size 10). Scan conversion may also contribute to these differences. In particular some of dropout control methods assume usage of "round to left/top" rules.
16-01-2006

EVALUATION Not for tiger. ###@###.### 2003-11-23
23-11-2003