|
|
|
Contents: |
|
|
|
Related content: |
|
|
|
Subscriptions: |
|
|
| Fullscreen Exclusive Mode API lets you change display modes
and draw directly to video RAM
John
Zukowski (mailto:jaz@zukowski.net?cc=&subject=Exercise
complete control over your graphics display) President, JZ
Ventures, Inc. 29 April 2003
Game developers and computer speed nuts
alike will love Merlin's new Fullscreen Exclusive Mode (FEM) API, with
which you can write directly to video memory with complete control over
the graphics display. In this latest Magic with Merlin
installment, Java programming expert John Zukowski introduces the power
of this new Merlin feature. Share your thoughts on this article with the
author and other readers in the accompanying discussion forum. (You can
also click Discuss at the top or bottom of the article to access the
forum.)
Do you enjoy upsetting people by making your programs do annoying
things? If you answered "yes," this month's tip is sure to be a pleaser.
With J2SE 1.4, your Java programs can now change video modes and seize
absolute control of the screen. You need not let anyone else play nicely;
you literally can possess total control. For this god-like power, thank
the new Fullscreen Exclusive Mode (FEM) API.
Even if you answered "no" and you don't get your kicks by annoying
others, you'll find the FEM API has much to offer. The FEM API delivers
complete control of the display by writing directly to video RAM --
perfect for game development, although other uses abound. Some programs,
for instance, simply look and work better with a certain size screen. Read
on to discover your inner control freak.
Change display
modes Let's begin by examining the FEM API's
java.awt.DislayMode class, which wraps the screen dimensions
and refresh rate for a particular display mode. The modes supported are
specific to a system's hardware support.
To find out a specific system's supported modes, ask the
GraphicsEnvironment . From that environment you get the
default screen device, a GraphicDevice , from which you get
the display modes, as seen in Listing 1: Listing 1.
Look up display modes
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] =
graphicsDevice.getDisplayModes();
|
You can also discover the current display mode with the
getDisplayMode() method, as shown in Listing 2: Listing 2. Get the current display mode
DisplayMode originalDisplayMode =
graphicsDevice.getDisplayMode();
|
Changing modes proves relatively easy, but you must first ask if the
involved graphics device supports changing with the
GraphicsDevice 's isDisplayChangeSupported()
method.
Once that's known, to change modes use the
setDisplayMode() method, passing in the new mode. The
display-mode change typically occurs in a
try /finally block such that the finally block
resets code to the original mode. While that process is not absolutely
required, it ensures a safe mode upon the program's completion. Listing 3
shows a typical pattern for changing display modes: Listing 3. Change modes
GraphicsDevice graphicsDevice = ...
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
DisplayMode newDisplayMode = ...
try {
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(newDisplayMode);
}
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
}
|
Use fullscreen
windows With the FEM API, entering fullscreen windows is a
snap: just pass a window to the GraphicsDevice 's
setFullScreenWindow() method, as shown in Listing 4. When you
want to return to nonfullscreen mode, pass null into the
method. Of course, first check whether the GraphicsDevice
supports fullscreen mode by employing the
isFullScreenSupported() method. Listing
4. Enter fullscreen mode
GraphicsDevice graphicsDevice = ...
Window window = ...
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(window);
}
} finally {
graphicsDevice.setFullScreenWindow(null);
}
|
To illustrate everything you've learned so far, Listing 5 contains a
complete example. The code in Listing 5 gets the display modes, picks one
at random to change to, then displays a fullscreen window with the text
"Hello, World!" The new display mode's characteristics display so you can
see the specific screen size, refresh rate, and bit depth. Listing 5. Mode changing example
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class DisplayModes {
public static void main(String args[]) {
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
JWindow window = new JWindow() {
public void paint(Graphics g) {
g.setColor(Color.blue);
g.drawString("Hello, World!", 50, 50);
}
};
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(window);
}
Random random = new Random();
int mode = random.nextInt(displayModes.length);
DisplayMode displayMode = displayModes[mode];
System.out.println(displayMode.getWidth() + "x" +
displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
" / " + displayMode.getBitDepth());
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(displayMode);
}
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
graphicsDevice.setFullScreenWindow(null);
}
System.exit(0);
}
}
|
Fullscreen
rendering Notice in Listing 5 that the window used for the
fullscreen includes a perfectly functional paint() method.
However, because the window is in fullscreen mode, the overhead required
for the paint() method, how the method deals with clipping,
and other associated display-handling behaviors are unnecessary. Indeed,
the standard passive rendering proves overkill, slowing fullscreen
application down. You needn't deal with such tasks as overlapping or
resizing a window, for instance. Instead, take a more active approach and
create a tight loop that handles the rendering itself.
If you're familiar with double buffering, you know it manages
two Image objects in memory and swaps between them based upon
which possesses the current display information. While one
Image displays, you draw to the other and swap
Image objects between each scene.
Graphics cards employ a similar concept, but instead of working with
actual Java Image objects, they swap memory pages. The
display changes when you swap memory pages so you need not copy an
Image object from program memory to video memory; you merely
change video pointers and the display changes. While the double-buffering
concept still exists, instead of writing to an Image in
program space, you draw directly to video memory space.
The BufferStrategy class masks which of the two
aforementioned double-buffering methodologies is used and lets you take
advantage of any hardware-based buffering the system offers. To create a
BufferStrategy , tell the system the number of buffers you
desire with the createBufferStrategy() method and swap
between buffers with the getDrawGraphics() method, which
returns the next buffer to work with. Conceptually that's all there is to
it, but, as Listing 6 shows, the working code requires a bit more
effort: Listing 6. Use a BufferStrategy
JFrame frame = ...
frame.createBufferStrategy(2); // Number of buffers to have
BufferStrategy bufferStrategy = frame.getBufferStrategy();
while (!done()) { // Some condition to end
Graphics g = null;
try {
g = bufferStrategy.getDrawGraphics();
drawNextScene(g); // Method to draw to
} finally {
// Free resources
if (g != null) {
g.dispose();
}
}
bufferStrategy.show();
}
|
When using a BufferStrategy , you can't assume the last
item you drew to the buffer remains valid; you must ask with the
contentsLost() method. If lost, you must redraw the entire
buffer. Otherwise, you need draw only what's changed since its last
usage.
In addition to buffer support with BufferStrategy , the
BufferCapabilities class lets you discover what capabilities
-- such as full screen mode -- a strategy supports with the
getCapabilities() method.
A working
example Putting all the pieces together produces the example
program in Listing 7. Because of my limited artistic skills, don't look
for a sophisticated game. You will, however, find a complete working
example employing a BufferStrategy and fullscreen drawing
modes. The example program purposely does not synchronize buffers
by remembering what wasn't drawn to the current buffer -- letting you see
multiple buffers at work more clearly. The program does randomly draw 100
rectangles to the screen, with a 1/10th-of-a-second delay between
draws. Listing 7. A working example
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Random;
public class MultipleBuffers {
public static void main(String args[]) {
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(frame);
}
Random random = new Random();
int mode = random.nextInt(displayModes.length);
DisplayMode displayMode = displayModes[mode];
System.out.println(displayMode.getWidth() + "x" +
displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
" / " + displayMode.getBitDepth());
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(displayMode);
}
frame.createBufferStrategy(2);
BufferStrategy bufferStrategy = frame.getBufferStrategy();
int width = frame.getWidth();
int height = frame.getHeight();
for (int i=0; i<100; i++) {
Graphics g = bufferStrategy.getDrawGraphics();
g.setColor(new Color(random.nextInt()));
g.fillRect(random.nextInt(width),
random.nextInt(height), 100, 100);
bufferStrategy.show();
g.dispose();
Thread.sleep(100);
}
} catch (InterruptedException e) {
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
graphicsDevice.setFullScreenWindow(null);
}
System.exit(0);
}
}
|
The example program would benefit from such enhancements as keeping the
buffers synchronized or checking if the complete buffer must be redrawn
when contents are lost. The former task requires remembering only the last
rectangle drawn (and the color), while the latter requires remembering
them all.
First the screen, then the known
universe! With the new Fullscreen Exclusive Mode API, Java
development becomes a mainstream option for game development. The API
replaces the need to work with platform-specific APIs like DirectX or
OpenGL, instead relying on a standard API across all Java-enabled
platforms. It's more than an aspiring programming despot could
imagine.
Resources
- Participate in the discussion forum on this
article. (You can also click Discuss at the top or bottom of the
article to access the forum.)
- Peruse John Zukowski's Magic
with Merlin column archive for J2SE 1.4 tips.
- In "Introduction
to the Retained Graphic Object API" (developerWorks, November
2001), Barry Feigenbaum describes the Retained Graphic Objects (RGO)
API, which enables the creation of reusable graphic objects for the Java
platform.
- The AlphaComposite class, which previously supported only 8 of the
12 Porter-Duff rules for digital image compositing, now supports all 12.
In this earlier installment of Magic with Merlin, Porter-Duff
rules! (developerWorks, September 2001), John Zukowski
describes all 12 rules and offers an interactive program to demonstrate
how they operate.
- For more background on the FEM API, read the New
Fullscreen Exclusive Mode API documentation from Sun
Microsystems.
- Bugtraq
Report # 4189326 started it all (free login required).
- Learn more about the
DisplayMode
class.
- You'll also need more detail on the
GraphicsDevice
class.
- The Java Tutorial offers more information
about the FEM API.
- You'll find hundreds of articles about every aspect of Java
programming in the developerWorks
Java technology zone.
|
|