Where are my pixels!!???

Hi, I have the following code to display a game in a window (Frame) or applet mode:


import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Game extends Applet {

	private boolean isApplet;

	public Game() {
		this.isApplet = true;
	}

	public void init() {
	}

	public void paint(Graphics g) {
		g.setColor(Color.black);
		g.fillRect(0, 0, 640, 480);

		g.setColor(Color.red);
		g.drawRect(0, 0, 640, 480);

		// g.setColor(Color.green);
		// g.drawRect(0, 0, 640 - 3, 480 - 33);
	}

	public void update(Graphics g) {
	}

	public void buildFrame() {
		Frame f = new Frame("Game");
		// Add an window adapter to allow user close it.
		f.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		f.setSize(640, 480);
		f.setResizable(false);
		// Get current screen resolution to adjust the frame in the middle
		// of the user desktop.
		Dimension dimension = getToolkit().getScreenSize();
		Rectangle rectangle = f.getBounds();
		// Put the frame centered in the user desktop.
		f.setLocation((dimension.width - rectangle.width) / 2,
				(dimension.height - rectangle.height) / 2);
		// Add the applet to frame.
		f.add(this);
		// Show it!
		f.setVisible(true);
		this.isApplet = false;
		// Call applet init method.
		this.init();
	}

	public static void main(String[] args) {
		Game g = new Game();
		g.buildFrame();
	}
}

When running as application it starts in the main method that calls the buildFrame method to put the applet inside of it.

I have created a 640x480 pixels window and to make sure the size is correct I have drawn a rectangle with (0,0,640,480) but for my surprise it does not appear in the right and bottom edges.

To make the full rectangle appear in the screen I had to put the following code:


g.drawRect(0, 0, 640 - 3, 480 - 33);

Why this happens? The same problem happens when running in the browser (applet mode).

I also noticed that the result is different when running in Windows (I’m using Linux). I had to put (-5) in the right corner to make it appear.

The question is: is there a way to make the code above display a real 640x480 display for both applet/application and plataform independent?

Thanks!

The problem is that you make the actual frame (including the borders and the title bar) to be 640x480. This makes the content pane (the area where you can draw stuff) a lot smaller.

Instead do this:

applet.setPreferredSize(new Dimension(640, 480));
f.add(applet);
f.pack();

f.setVisible(true);

If you want to center the frame, all you have to do is this:

f.setLocationRelativeTo(null);

Hope that helps :wink:

Thanks Captain, but even using the code you provided I still have the same problem.

There shouldn’t be a problem in Applet mode, since you’ve got the entire viewing area to paint.

However, in application mode, the top bar (with the close, maximize, and minimize buttons) and the thin border around the window are part of the viewing area. When you add the applet to the Frame, it is not using the full 640x480 because of the border.
What exactly happens when you use Captain Awesome’s code?


this.setPreferredSize(new Dimension(640,480));
f.add(this);
f.pack();

the problem is that my red rectangle right and bottom lines does not appear, as my original code in the first post.

The best I could do was:


import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Game extends Applet {

	private boolean isApplet;

	private Image image;

	public Game() {
		this.isApplet = true;
		this.setPreferredSize(new Dimension(640, 480));
	}

	public void init() {
		this.setPreferredSize(new Dimension(640, 480));
		this.setSize(new Dimension(640, 480));
	}

	public void paint(Graphics g) {
		g.setColor(Color.black);
		g.fillRect(0, 0, 640, 480);

		g.setColor(Color.red);
		g.drawRect(0, 0, 640, 480);

		g.drawImage(image, 0, 0, null);
		g.setColor(Color.green);
		g.drawLine(0, 0, 640, 480);
	}

	public void update(Graphics g) {
	}

	public void buildFrame() {
		Frame f = new Frame("Game");
		f.add(this);
		f.pack();
		f.setVisible(true);

		f.setLocationRelativeTo(null);

		System.out.println(f.getInsets().bottom);
		System.out.println(f.getInsets().top);
		System.out.println(f.getInsets().left);
		System.out.println(f.getInsets().right);
		System.out.println(f.getWidth());

		// 640+11
		// 480+31
		// f.setSize(640 + 11, 480 + 31);
		f.setSize(640 + f.getInsets().left + f.getInsets().right + 1, 480
				+ f.getInsets().top + f.getInsets().bottom + 1);

		try {
			image = ImageIO.read(new File("image.png"));
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		this.isApplet = false;
		// Call applet init method.
		this.init();
	}

	public static void main(String[] args) {
		Game g = new Game();
		g.buildFrame();
	}
}

Using those insets() worked a little bit better but I still had to add a magic number (+1) there. Now running application mode in both Linux and Windows get almost the same result.

Have you tried my code and run as applet? Even running as applet in both Windows and Linux I’m not able to see right and bottom lines of the red rectangle.


<applet code=Game width="640" height="480"></applet>

This is really weird, in fact, I haven’t stopped to check this before… but it’d be nice to have one solution for both applet/application modes… any other tips?

thanks

You need to read the specification for drawRect(…); the extent of the touched pixels is not the same as fillRect(…).

fillRect(0,0,10,2); will paint a 10x2 rectangle of pixels.
drawRect(0,0,9,1); will paint the same 10x2 rectangle of pixels.

i.e. drawRect touches the pixels at width and height, fillRect touches the pixels upto width and height.

Ofcourse, if you ever work on embedded Java you’ll find all manner of broken implementations that fail to follow this simple specification.
Samsung, SonyEricsson and Nokia have in the past all managed to produce phones with J2ME implementations that failed in this regard; Sun certified my ass.

When you tried my code, did you remove the f.setSize… part? Since f.pack() automatically calculates (and sets) the size of the frame needed to properly display all components. Also try what Abuse just said.

Yeah I just did some playing around and Abuse is right. It seems as if you go 1 extra pixel when you do drawRect. Weird…


import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;

public class Game extends Applet {

	private boolean isApplet;

	private Image image;

	public Game() {
		this.isApplet = true;
		this.setPreferredSize(new Dimension(640, 480));
		this.setMaximumSize(new Dimension(640, 480));
		this.setSize(new Dimension(640, 480));
	}

	public void init() {
		this.setPreferredSize(new Dimension(640, 480));
		this.setMaximumSize(new Dimension(640, 480));
		this.setSize(new Dimension(640, 480));
	}

	public void paint(Graphics g) {
		g.setColor(Color.black);
		g.fillRect(0, 0, 640, 480);

		g.setColor(Color.red);
		g.drawRect(0, 0, 640, 480);

		g.drawImage(image, 0, 0, null);
		g.setColor(Color.green);
		g.drawLine(0, 0, 640, 480);
	}

	public void update(Graphics g) {
	}

	public void buildFrame() {
		Frame f = new Frame("Game");
		f.add(this);
		f.pack();
		f.setVisible(true);
		f.setResizable(false);

		f.setLocationRelativeTo(null);

		System.out.println(f.getInsets().bottom);
		System.out.println(f.getInsets().top);
		System.out.println(f.getInsets().left);
		System.out.println(f.getInsets().right);
		System.out.println(f.getWidth());
		System.out.println(f.getHeight());

		// 640+11
		// 480+31
		// f.setSize(640 + 11, 480 + 31);
		f.setSize(640 + f.getInsets().left + f.getInsets().right + 1, 480
				+ f.getInsets().top + f.getInsets().bottom + 1);

		this.isApplet = false;
		// Call applet init method.
		this.init();
	}

	public static void main(String[] args) {
		Game g = new Game();
		g.buildFrame();
	}
}

Under linux it’s really really weird. See the output of the code above:

How it’s possible to run exactly the same code multiple times and get different values for the inset() values???!? ???


asses$ java Game 
5
27
1
1
650
510
^Cluisoft@luisoft-Inspiron-N5010:/luisoft/development/games/BoulderDash4k/build/asses$ java Game 
5
27
1
1
642
512
^Cluisoft@luisoft-Inspiron-N5010:/luisoft/development/games/BoulderDash4k/build/asses$ java Game 
5
27
1
1
642
512
^Cluisoft@luisoft-Inspiron-N5010:/luisoft/development/games/BoulderDash4k/build/asses$ java Game 
5
27
1
1
640
480

Under Windows I ran multiple times the code above and the result was always consistent:



Z:\development\games\BoulderDash4k\build\classes>java Game
3
29
3
3
648
514

Z:\development\games\BoulderDash4k\build\classes>java Game
3
29
3
3
648
514

Z:\development\games\BoulderDash4k\build\classes>java Game
3
29
3
3
648
514

I had to put the following code just after the setVisible:


Toolkit.getDefaultToolkit().sync();

And now it’s working on Linux in a consistent manner.

what does the sync() method do?

from the API:

Synchronizes this toolkit’s graphics state. Some window systems may do buffering of graphics events.

This method ensures that the display is up-to-date. It is useful for animation.

Yes I looked at the API but…I don’t understand what it says :stuck_out_tongue:
What does it synchronize the graphics with??

when I was back on the java2d I always put that method on repaint() method but still don’t know what it is about. The reason I used it because it makes everything goes fine :smiley:

Basically it synchronizes your renderer with your screen’s refresh rate. If your renderer wants to update your screen faster than the screen can refresh itself, you might experience one screen-refresh displaying content from more than one frame. This is known as http://en.wikipedia.org/wiki/Screen_tearing

Oh so that’s for v-sync? Where should I put it in my code, before or after repaint()?

Based on my example, I put it on paint(g) method, at the end.


while(true) {
    //game code
    
    paint(g);
    Toolkit.getDefaultToolkit().sync();
}

;D