Hi,
I’m finding that Graphics2D.fill(Shape) gives different results to Graphics2D.setClip(Shape) then filling with Graphics2D.fillRect(0 ,0, width, height).
Setting the clip then filling results in a bigger filled area, or sometimes the same size filled area, depending on the floating point dimensions of the shape and the AffineTransform applied to the Graphics2D object.
It’s a slight annoyance since I’m getting stray pixels that aren’t coloured properly in my game. Just wondering if this is an intentional feature or a strange bug?
Thanks
Keith
Here’s a little test case illustrationg my point:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;
import java.awt.image.*;
/**
*
* @author Keith
*/
public class FillVsClipTest extends JFrame{
final ViewPane v;
volatile boolean keepRunning = true;
long lastUpdateNanos;
public FillVsClipTest(){
setTitle("Graphics2D Fill vs Clip Test");
setSize(400, 400);
setLocationRelativeTo(null);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
keepRunning = false;
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
v = new ViewPane(this);
this.setLayout(new BorderLayout());
add(v);
setVisible(true);
Thread renderThread = new Thread(){
public void run(){
lastUpdateNanos = System.nanoTime();
while(keepRunning){
long currentNanos = System.nanoTime();
double secondsSinceLastUpdate = (currentNanos - lastUpdateNanos)/1000000000.0;
lastUpdateNanos = currentNanos;
v.update(secondsSinceLastUpdate);
v.render();
try{Thread.sleep(1);}catch(InterruptedException e){}
}
}
};
renderThread.setDaemon(true);
renderThread.start();
}
public class ViewPane extends JComponent{
FillVsClipTest frame;
VolatileImage backImage;
Graphics2D g;
double x = 0;
boolean xIncreasing = true;
double y = 0;
boolean yIncreasing = true;
public ViewPane(FillVsClipTest frame){
this.frame = frame;
}
protected VolatileImage createVolatileImage() {
return createVolatileImage(getWidth(), getHeight(), Transparency.OPAQUE);
}
protected VolatileImage createVolatileImage(int width, int height, int transparency) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
VolatileImage image = null;
image = gc.createCompatibleVolatileImage(width, height, transparency);
int valid = image.validate(gc);
if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
image = this.createVolatileImage(width, height, transparency);
}
//System.out.println(this.getClass().getSimpleName() + ": initiated VolatileImage backImage for quick rendering");
return image;
}
public void render() {
if (getWidth() <= 0 || getHeight() <= 0) {
System.out.println(this.getClass().getSimpleName() + ": width &/or height <= 0!!!");
return;
}
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
if (backImage == null || getWidth() != backImage.getWidth() || getHeight() != backImage.getHeight() || backImage.validate(gc) != VolatileImage.IMAGE_OK) {
backImage = createVolatileImage();
}
do {
int valid = backImage.validate(gc);
if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
backImage = createVolatileImage();
}
g = backImage.createGraphics();
renderWorld();
// It's always best to dispose of your Graphics objects.
g.dispose();
} while (backImage.contentsLost());
if (getGraphics() != null) {
getGraphics().drawImage(backImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync(); // to flush the graphics commands to the graphics card. see http://www.java-gaming.org/forums/index.php?topic=15000.msg119601;topicseen#msg119601
}
}
public void update(double seconds){
double speed = 1; // pixels per second
double minY = 0;
double maxY = 20;
double yIncrement = speed*seconds;
if (yIncreasing){
y += yIncrement;
}else{
y -= yIncrement;
}
if (y >= maxY){
yIncreasing = false;
}else if (y <= minY){
yIncreasing = true;
}
double minX = 0;
double maxX = 20;
double xIncrement = speed*seconds;
if (xIncreasing){
x += xIncrement;
}else{
x -= xIncrement;
}
if (x >= maxX){
xIncreasing = false;
}else if (x <= minX){
xIncreasing = true;
}
}
public void renderWorld(){
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
Shape oldClip = g.getClip();
g.drawString("Graphics2D.fill(rect) is in red", 40, 30);
g.setColor(Color.WHITE);
g.drawString("Graphics2D.setClip(rect) is in white", 40, 50);
//g.scale(4, 4);
g.translate(x, y);
{
float w = 80f;
float h = 80f;
Rectangle2D.Float rect = new Rectangle2D.Float(10, 70, w, h);
Rectangle2D.Float rect2 = new Rectangle2D.Float(10, 180, w, h);
g.setColor(Color.WHITE);
g.setClip(rect);
g.fillRect(0, 0, getWidth(), getHeight());
g.setClip(oldClip);
g.setColor(Color.RED);
g.fill(rect);
g.setColor(Color.RED);
g.fill(rect2);
g.setColor(Color.WHITE);
g.setClip(rect2);
g.fillRect(0, 0, getWidth(), getHeight());
g.setClip(oldClip);
}
{
float w = 80.3f;
float h = 80.3f;
Rectangle2D.Float rect = new Rectangle2D.Float(150, 70, w, h);
Rectangle2D.Float rect2 = new Rectangle2D.Float(150, 180, w, h);
g.setColor(Color.WHITE);
g.setClip(rect);
g.fillRect(0, 0, getWidth(), getHeight());
g.setClip(oldClip);
g.setColor(Color.RED);
g.fill(rect);
g.setColor(Color.RED);
g.fill(rect2);
g.setColor(Color.WHITE);
g.setClip(rect2);
g.fillRect(0, 0, getWidth(), getHeight());
g.setClip(oldClip);
}
}
}
public static void main(String[] args){
new FillVsClipTest();
}
}