I didn’t believe how broken this was until I saw it with my own eyes.
With an Affine Transfrom drawImage does not work. Period. It is completely broken such that I can’t possibly imagine how it passes any tests that Sun might have. (Tested on Windows XP)
I’ve tested with 1.4.2_04 and 1.5.0 beta 3 build 56
same results on both.
Try this code, drag the windows over each other, off the screen edge etc. Note how the ‘stretched’ window simply doesn’t paint properly if at all, while the un-stretched window is fine.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* Created on 22-Jun-2004
*
* @author scott.palmer
*/
public class TransformTest
{
public static void main(String[] args)
{
JFrame frameNT = new JFrame("Transform Test - Plain");
MenuScreen screenNT = new MenuScreen(720,480,false);
frameNT.getContentPane().add( screenNT, BorderLayout.CENTER );
JFrame frameT = new JFrame("Transform Test - With Transform");
MenuScreen screenT = new MenuScreen(720,480,true);
frameT.getContentPane().add( screenT, BorderLayout.CENTER );
frameNT.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameT.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameNT.pack();
frameT.pack();
frameNT.show();
frameT.show();
}
}
class MenuScreen extends JPanel
{
public MenuScreen(int width, int height, boolean anamorphic)
{
compositeImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
size.width = width;
size.height = height;
this.anamorphic = anamorphic;
setDoubleBuffered(false);
setOpaque(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
AffineTransform savedAT = null;
if(anamorphic)
{
// 4:3 -> 16:9
savedAT = g2.getTransform();
g2.setTransform(AffineTransform.getScaleInstance(4.0 / 3.0, 1.0));
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
}
if( dirty )
rebuildCompositeImage(0);
g2.drawImage(compositeImage, 0, 0, null);
if(savedAT != null)
g2.setTransform(savedAT);
}
protected void rebuildCompositeImage(int fromItem)
{
// TODO account for insets
Graphics2D g2 = (Graphics2D) compositeImage.getGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, getWidth(), getHeight());
// leave some space around the edges
int safeTitleX = (int) ((size.width - (size.width* 0.8 )) / 2.0 + 0.5);
int safeTitleY = (int) ((size.height - (size.height* 0.8 )) / 2.0 + 0.5);
int safeWidth = size.width - safeTitleX * 2;
int safeHeight = size.height - safeTitleY * 2;
int titleLines = 1;
g2.setColor( textColor );
g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
g2.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON );
g2.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
// draw title
FontMetrics fm = g2.getFontMetrics(titleFont);
int tHeight = fm.getHeight();
if( title != null )
{
int tWidth = fm.stringWidth(title);
g2.setFont(titleFont);
g2.drawString(title, safeTitleX + (safeWidth - tWidth) / 2, safeTitleY + fm.getAscent());
}
// draw Menu items
fm = g2.getFontMetrics(chapterFont);
int menuThumbHeight = 60;
int menuRowHeight = fm.getHeight() + menuThumbHeight;
int chapsPerRow = 4;
int chapWidth = (safeWidth/chapsPerRow);
int zz = fromItem + 1;
for(int j = 0; j < 4; j++)
{
for(int i = 0; i < 4; i++)
{
String str = "Menu Item "+zz;
int ctPos = (chapWidth - fm.stringWidth(str))/2; // center chapter title
int x = safeTitleX + i * chapWidth + ctPos;
int y = safeTitleY + (tHeight * titleLines) + menuThumbHeight + fm.getAscent() + j * menuRowHeight;
g2.drawString(str,x,y);
zz++;
}
}
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.dispose();
dirty = false;
}
public Dimension getPreferredSize()
{
return anamorphic ? anamorphicSize : size;
}
public Dimension getMinimumSize() { return getPreferredSize(); }
public Dimension getMaximumSize() { return getPreferredSize(); }
private boolean dirty = true; // need to rebuild the image
private Dimension size = new Dimension(720, 480);
private Dimension anamorphicSize = new Dimension(720 * 4 / 3, 480);
private boolean anamorphic;
private BufferedImage compositeImage;
private Color textColor = Color.BLACK;
private String title = "Painting Bugs with AffineTransform";
private Font titleFont = new Font("SansSerif", Font.PLAIN, 20);
private Font chapterFont = new Font("SansSerif", Font.PLAIN, 18);
}