Well…
I tried the scaling on the fly but I didn’t like the results.
Here is the code I tried
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.MemoryImageSource;
import java.awt.image.VolatileImage;
public class FakeFullscreen {
static boolean isRunning;
public static void main(String[] args) {
final Toolkit toolkit = Toolkit.getDefaultToolkit();
final int[] pixels = new int[16 * 16];
final Image image = toolkit.createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
final Cursor invisibleCursor = toolkit.createCustomCursor(image, new Point(0, 0), "invisibleCursor");
final Dimension screenSize = toolkit.getScreenSize();
final Dimension gameSize = new Dimension(640, 480);
final Rectangle gameArea = new Rectangle(0, 80, 640, 400);
final Canvas canvas = new Canvas();
canvas.setIgnoreRepaint(true);
canvas.setSize(screenSize);
final Frame frame = new Frame();
frame.setAlwaysOnTop(true);
frame.setCursor(invisibleCursor);
frame.setIgnoreRepaint(true);
frame.setLocation(0, 0);
frame.setResizable(false);
frame.setSize(screenSize);
frame.setUndecorated(true);
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
isRunning = false;
}
}
});
frame.add(canvas);
frame.pack();
frame.setVisible(true);
canvas.createBufferStrategy(2);
final BufferStrategy buffer = canvas.getBufferStrategy();
final VolatileImage offScreen = canvas.createVolatileImage(gameSize.width, gameSize.height);
Graphics2D offScreenGraphics = null;
Graphics2D graphics = null;
isRunning = true;
int fps = 0;
int frames = 0;
long totalTime = 0;
long curTime = System.nanoTime() / 1000000L;
long lastTime = curTime;
final int TICKS_PER_SECOND = 25;
final int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
final int MAX_FRAMESKIP = 5;
long nextGameTick = System.nanoTime() / 1000000L;
int loops;
float interpolation;
int ballWidth = 8;
int ballHeight = 8;
int ballX = gameArea.width / 2 - ballWidth / 2;
int ballY = gameArea.y + (gameArea.height / 2 - ballHeight / 2);
int ballDx = 10;
int ballDy = 10;
while (isRunning) {
loops = 0;
while ((System.nanoTime() / 1000000L) > nextGameTick && loops < MAX_FRAMESKIP) {
ballX += ballDx;
ballY += ballDy;
if (ballX <= gameArea.x) {
ballX = gameArea.x;
ballDx = -ballDx;
} else if (ballX >= gameArea.width - ballWidth) {
ballX = gameArea.width - ballWidth;
ballDx = -ballDx;
}
if (ballY <= gameArea.y) {
ballY = gameArea.y;
ballDy = -ballDy;
} else if (ballY >= gameArea.y + gameArea.height - ballHeight) {
ballY = gameArea.y + gameArea.height - ballHeight;
ballDy = -ballDy;
}
nextGameTick += SKIP_TICKS;
loops++;
}
lastTime = curTime;
curTime = System.nanoTime() / 1000000L;
totalTime += curTime - lastTime;
if (totalTime > 1000) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
++frames;
interpolation = (float) ((System.nanoTime() / 1000000L) + SKIP_TICKS - nextGameTick) / (float) (SKIP_TICKS);
try {
offScreenGraphics = offScreen.createGraphics();
offScreenGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
offScreenGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
offScreenGraphics.setColor(Color.BLACK);
offScreenGraphics.fillRect(0, 0, screenSize.width, screenSize.height);
offScreenGraphics.setColor(Color.WHITE);
offScreenGraphics.drawString(String.format("FPS: %s", fps), 20, 20);
int renderX = (int) (ballX + (ballDx * interpolation));
int renderY = (int) (ballY + (ballDy * interpolation));
offScreenGraphics.drawLine(gameArea.x, gameArea.y - ballHeight, gameArea.width, gameArea.y - ballHeight);
offScreenGraphics.fillRect(renderX, renderY, ballWidth, ballHeight);
graphics = (Graphics2D) buffer.getDrawGraphics();
if (!offScreen.contentsLost()) {
graphics.drawImage(offScreen, 0, 0, screenSize.width, screenSize.height, 0, 0, gameSize.width, gameSize.height, null);
}
if (!buffer.contentsLost()) {
buffer.show();
}
Thread.yield();
} finally {
if (offScreenGraphics != null) {
offScreenGraphics.dispose();
}
if (graphics != null) {
graphics.dispose();
}
}
}
frame.setVisible(false);
System.exit(0);
}
}
I get about 200FPS on my laptop which runs on Windows XP but I get jerky move.
I haven’t tested it yet on my desktop which has Linux, but I don’t expect too much of a difference.
If I replace
final Dimension screenSize = toolkit.getScreenSize();
with
final Dimension screenSize = new Dimension(640, 480);
i.e. so as to have a normal frame and not a ‘fake’ fullscreen, I get ~700FPS and much better movement.
Either there is something wrong in my code or I didn’t fully understand how to use scaling on the fly.
Perhaps giving out instructions on how to change xorg.conf so Java can use the Full Screen Exclusive Mode API might be a better idea than faking fullscreen mode?
Regards,
Charalampos