JDK-6479365 : Combining negative scale and negative shear in Font Transform produced incorrect results in JDK 6
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2006-10-06
  • Updated: 2011-03-08
  • Resolved: 2011-03-08
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 JDK 7
6u1Fixed 7 b03Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b86, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Results produced from the shear() operation do not match results produced in JDK 1.2 thru 1.5.  shear() was used to produce a "inverted, slanted" font which looked like a shadow to the original font.  The original font was flipped, then sheared which produced the shadow affect.

	fontTransform = AffineTransform.getScaleInstance(1.0, -1.0);
 	fontTransform.shear(-1.0, 0.0);
	reflectionFont = textFont.deriveFont(fontTransform);

Up through JDK 1.5, this produced a "shadow" of the original font, In JDK 1.6B2, the shear inverts the font across the Y axis instead of slanting the original font.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The following code was used to illustrate affine transforms and rendering hints in a class I teach.  This code worked correctly until JDK 6.

Sample code is included below that shows this behavior.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The generated font should appear below the original font, not to the left of the font.
ACTUAL -
The new "shadow" font is no longer correct.  If you just scale the font, you get the correct results, if you just shear the font, you get the correct results, if you scale the font, then shear it, it appears as though axis values are flipped.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class Affine1 extends JPanel{
    // our arrow shape
    GeneralPath arrow = new GeneralPath();
    // rotate transform
    AffineTransform arrowRotate;
    // shadow transform
    AffineTransform shadowScale;
    // shear transform
    AffineTransform shadowShear;
    // font transform
    AffineTransform fontTransform;
    Shape rotatedArrow;
    Shape scaledArrow;
    Shape skewedArrow;
    static RenderingHints hints1;
    static RenderingHints hints2;
    Font textFont = new Font("Arial", Font.PLAIN, 40);
    // derived fonts...
    Font reflectionFont;
    Font reflectionFont2;

    public Affine1 () {
	super();
	setLayout(null);

	// get the graphics environment
	GraphicsEnvironment env =
	    GraphicsEnvironment.getLocalGraphicsEnvironment();
	env.getAvailableFontFamilyNames();

	hints1 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
			   RenderingHints.VALUE_ANTIALIAS_ON);
	hints2 = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
			   RenderingHints.VALUE_ANTIALIAS_OFF);

	// build our arrow, but it is pointing to the right
	arrow.moveTo(0f, -10f);
	arrow.lineTo(20f, -10f);
	arrow.lineTo(20f, 0f);
	arrow.lineTo(40, -20f);
	arrow.lineTo(20f, -40f);
	arrow.lineTo(20f, -30f);
	arrow.lineTo(0f, -30f);
	arrow.closePath();

	// rotate the arrow -90 around its center
	arrowRotate = AffineTransform.getRotateInstance(- Math.PI/2, 20, -20);

	shadowScale = AffineTransform.getScaleInstance(1.0, 0.4);
	shadowShear = AffineTransform.getShearInstance(-1.25, 0.0);
	rotatedArrow = arrowRotate.createTransformedShape(arrow);
	scaledArrow = shadowScale.createTransformedShape(rotatedArrow);
	skewedArrow = shadowShear.createTransformedShape(scaledArrow);

	// This is the code that no longer works
	fontTransform = AffineTransform.getScaleInstance(1.0, -1.0);
  	fontTransform.shear(-1.0, 0.0);

	// if you try to just shear the font, it works, if you just scaled
	// it above it works as well.
// 	fontTransform = AffineTransform.getShearInstance(-1.0, 0.0);

	reflectionFont = textFont.deriveFont(fontTransform);

	shadowScale.shear(-1.25, 0.0);
	reflectionFont2 = textFont.deriveFont(shadowScale);
    }

    public void paintComponent(Graphics g) {
	super.paintComponent(g);
	Graphics2D g2d = (Graphics2D)g;


	// draw Java2D with shadow in back
	g2d.translate(100, 80);
	g2d.setFont(reflectionFont2);
	g2d.setPaint(Color.gray);
	g2d.drawString("Aliased", 0, 0);
	g2d.setPaint(Color.blue);
	g2d.setFont(textFont);
	g2d.drawString("Aliased", 0, 0);

	// draw Java2D with shadow in back
	g2d.translate(300, 0);
	g2d.setRenderingHints(hints1);
	g2d.setPaint(Color.gray);
	g2d.setFont(reflectionFont2);
	g2d.drawString("Anti-Aliased", 0, 0);
	g2d.setPaint(Color.blue);
	g2d.setFont(textFont);
	g2d.drawString("Anti-Aliased", 0, 0);

	// Now draw arrow with shadow
  	g2d.translate(-260, 80);
	g2d.setColor(Color.gray);
	g2d.fill(skewedArrow);
	g2d.setColor(Color.blue);
	g2d.fill(rotatedArrow);
	
	// arrow with shadow and anti-aliasing
	g2d.setRenderingHints(hints1);
	g2d.translate(350, 0);
	g2d.setColor(Color.gray);
	g2d.fill(skewedArrow);
	g2d.setColor(Color.blue);
	g2d.fill(rotatedArrow);
	g2d.setRenderingHints(hints2);
	g2d.translate(-400, 0);

	// Now draw Java2D with shadow in front
	g2d.translate(-80, 60);
	g2d.setFont(textFont);
	g2d.drawString("Java 2D 1 2 3 4 5", 0, 0);
	g2d.setPaint(Color.gray);
	// note the use of using the transformed font like any other
	// font. Its transformation placed its 0,0 location at the top
	// left of its bounds
	g2d.setFont(reflectionFont);
	g2d.drawString("Java 2D 1 2 3 4 5", 0, 0);

	// Now draw Java2D with shadow in front and anti-aliasing
	g2d.translate(350, 0);
	g2d.setColor(Color.blue);
	g2d.setRenderingHints(hints1);
	g2d.setFont(textFont);
	g2d.drawString("Java 2D 1 2 3 4 5", 0, 0);
	g2d.setPaint(Color.gray);
	g2d.setFont(reflectionFont);
	g2d.drawString("Java 2D 1 2 3 4 5", 0, 0);
	g2d.setRenderingHints(hints2);
	g2d.translate(-200, 0);

    }

    public static void main(String argv[]) {
	JFrame j = new JFrame("Affine1.java");
	j.setContentPane(new Affine1());
	j.setSize(500, 400);
	j.setVisible(true);
	j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

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

CUSTOMER SUBMITTED WORKAROUND :
None known

Release Regression From : mustang
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION It seems to be stupid mistake in matrix decomposition logic. Flipping matrix M can be implemented by inverting signs of values in one of the columns. But if matrix M is decomposed into two matrices A and B (M = A*B). then flipping M is slightly more complicated. it is sign invertion in one of the columns of B and also sign inversion of one of the A's ROWs (by mistake it was column too).
11-10-2006

EVALUATION Here is a simplified version of the test case import java.awt.*; import java.awt.geom.*; import javax.swing.*; public class Affine2 extends JPanel { public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; Font textFont = new Font("Arial", Font.PLAIN, 40); // This is the code that no longer works // if you try to just shear the font, it works, if you just scale // it works as well. AffineTransform fontTransform = AffineTransform.getScaleInstance(1.0, -1.0); // With this shear its OK //fontTransform.shear(0.1, 0.0); // With this shear it fails. fontTransform.shear(-0.1, 0.0); Font reflectionFont = textFont.deriveFont(fontTransform); g2d.translate(400, 60); g2d.setFont(textFont); g2d.drawString("Java 2D 1 2 3 4 5", 0, 0); g2d.setPaint(Color.gray); g2d.setFont(reflectionFont); g2d.drawString("Java 2D 1 2 3 4 5", 0, 0); } public static void main(String argv[]) { JFrame j = new JFrame("Affine2.java"); j.setContentPane(new Affine2()); j.setSize(800, 200); j.setVisible(true); j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } Notice that this reproduces the bug : fontTransform.shear(-0.1, 0.0); but this does not : fontTransform.shear(0.1, 0.0); ie the negative X shear combined with the negative Y scale some how tickles a bug in the code that handles sepating those aspects of the font transform for rasterisation. We should fix this in an early update release of JDK6
10-10-2006

EVALUATION Looks like this changed in build 77 I see these fixes that were really 'one' fix and look related 6182443: Rotated antialiased text is too light gray 4654540: need hinting support for text rendering with scaled/flipped matrix 4908407: 1.4 REGRESSION: Rotated Sans Serif font doesn't render properly when scaled 4912220: 1.4 REGRESSION: Flipping with asymmetric scaling often distorts fonts
06-10-2006