FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
OS Name Microsoft Windows XP Home Edition
Version 5.1.2600 Service Pack 2 Build 2600
EXTRA RELEVANT SYSTEM CONFIGURATION :
Installed JDK/JRE with Netbeans 5.5. Running as standalone.
A DESCRIPTION OF THE PROBLEM :
When drawing a String and the Graphic's AffineTransform represented by entries m00, m10, m01, m11, m02, m12 is such that m01<0 and m11<0, the String is incorrectly rendered.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the SSCCE provided below.
Or: set the Graphic's transform by
g2.transform(new AffineTransform(1.0, 0.0, -0.5, -1.0, 0.0, 0.0));
or to to any AffineTransform, where the 2nd column has all entries negative, and draw a String by:
g2.drawString("hello", x, y);
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When running the SSCCE I was expecting that the String would remain within the containing rectangle and not "jump" out for certain values of the AffineTransform.
ACTUAL -
Running the code will show exactly what I saw.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error messages.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
<code>
/*
* TransformedStringBugDemo.java
*
* Created on February 13, 2007, 7:23 AM
*/
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JPanel;
import static java.lang.Math.*;
import javax.swing.Timer;
/**
*
* @author James Vagabond
*
* This class is written to show that there is a bug in java.swing. It draws
* a string inside a rectangle subject to various affine
* transformations. The affine transformations should keep the text within the
* rectangle, but for some affine transformations, the text makes a 180 degree
* rotation out of the rectangle. This occurs when m01 and m11 components are
* both negative, where m01 and m11 are defined as in the javadoc of
* AffineTransform.
*/
public class TransformedStringBugDemo extends javax.swing.JFrame {
/**
* TransformedStringBugDemo Constructor;
*/
public TransformedStringBugDemo() {
initComponents();
Graphics2D g2 = (Graphics2D) panel.getGraphics();
// Calculate the size of text and set dimension to allow for 4 pts around text
g2.setFont(font);
TextLayout layout = new TextLayout(text, g2.getFont(), g2.getFontRenderContext());
Rectangle2D r = layout.getBounds();
rectW = (int) (r.getWidth() + 8.5);
rectH = (int) (r.getHeight() + 8.5);
}
private String text = "Hello, World!";
private int rectW;
private int rectH;
private AffineTransform at = new AffineTransform();
private double theta;
private double delta = PI/100;
private Font font = new Font("Helvetica", Font.BOLD, 24);
private JPanel panel = new JPanel() {
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
int h = getHeight();
int w = getWidth();
//Clear the panel:
g2.setColor(Color.WHITE);
g2.fillRect(0,0,w,h);
g2.setColor(Color.BLACK);
g2.setFont(font);
//Save the user space transform:
AffineTransform savedAt = g2.getTransform();
//Move the origin to the center of the panel:
g2.translate(w/2, h/2);
//Draw coordinate axes:
g2.drawLine(-w/2, 0, w/2, 0);
g2.drawLine(0, -h/2, 0, h/2);
//Change the affine transformation of the graphics object and draw a
//text string inside a rectangle.
g2.transform(at);
drawBoxedText(g2); // Draws text *inside* a box.
//Reset the transform:
g2.setTransform(savedAt);
}
};
private Button startButton = new Button("Start"); //Used to start and stop animation
/**
* Updates the affine transformation. The transformation is defined by
* keeping the e0 unchanged and rotating e1 by theta.
*/
private Action updateTransformAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
at.setTransform(1.0, 0.0, -sin(theta), cos(theta), 0.0, 0.0);
panel.repaint();
theta += delta;
}
};
private Timer t = new Timer(50, updateTransformAction);
private boolean running = false;
/** This method is called from within the constructor to
* initialize the form.
*/
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setMinimumSize(new java.awt.Dimension(400, 400));
pack();
getContentPane().add(panel, java.awt.BorderLayout.CENTER);
getContentPane().add(startButton, java.awt.BorderLayout.SOUTH);
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!running) {
t.start();
running = true;
startButton.setLabel("Stop");
} else {
t.stop();
running = false;
startButton.setLabel("Start");
}
}
});
}
/**
* Draws text centered inside a rectangle.
* Note: Although the rectangle and the text may be subject to an
* affine transformation, the affine transformation should keep the text
* within the rectangle/parallelogramme.
*
*/
private void drawBoxedText(Graphics2D g2) {
//Draw a rectangle that is 8 pts wider and taller than text with lower left
//corner at the origin.
g2.drawRect(0, -rectH, rectW, rectH );
//Draw text centered within the rectangle.
g2.drawString(text, 4, -4);
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TransformedStringBugDemo().setVisible(true);
}
});
}
}
</code>
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None known.