Fullscreen Oddites

Well, I’m working on a small Java2D-AWT-based breakout clone and I have a few problems, maybe you can help me with them.

I designed the game engine with fullscreen or windowed mode in mind, but developed mainly in windowed. When I try to switch to fullscreen now, keyboard-input does not work, and my performance goes from somewhere in the 40-60FPS range to 5-15FPS. It’s very puzzling, as I thought fullscreen exclusive was supposedly faster than windowed, and there doesn’t seem to be any cause of the keyboard-input problem.

ScreenCanvas.java

/*
 *	ScreenCanvas Class
 *
 *	Used to encapsulate the process of setting up and showing frame renders,
 *	multi-buffering, and everything related to graphics.
 */
 
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;

public class ScreenCanvas extends Frame
{	
	protected static int width;
	protected static int height;
	protected static int fps;
	protected static boolean instance = false;
	
	public static int getScreenWidth()
	{
		return width;
	}
	
	public static int getScreenHeight()
	{
		return height;
	}
	
	public static int getFPS()
	{
		return fps;
	}

	public static ScreenCanvas getInstance()
	{
		if( !instance )
			return new ScreenCanvas();
		return null;
	}

	public static ScreenCanvas getInstance( String title )
	{
		if( !instance )
			return new ScreenCanvas( title );
		return null;
	}

	private GraphicsEnvironment env;
	private GraphicsDevice device;
	private GraphicsConfiguration gc;
	
	private BufferStrategy bufferStrategy;
	
	private DisplayMode fullscreenMode;
	private DisplayMode windowedMode;
	private DisplayMode displayMode;
	
	private int fps_max;
	
	//private double fps_avg = 0;
    
	private long startTime;
	private long endTime;
	private long wantedEndTime;
	
	private Canvas canvas;
	
	private Component raster;
	
	private boolean debug;
	
	protected ScreenCanvas()
	{
		this( "" );
	}
	
	protected ScreenCanvas( String title )
	{
		ScreenCanvas.instance = true;
		debug = Configuration.getBoolean("debug", false);
		ScreenCanvas.fps = 0;
		fps_max = Utility.clamp( Configuration.getInteger( "fps_max", 0 ), 0, 1000 );
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				cleanup();
			}
		});
		
		setBackground( Color.black );
		setIgnoreRepaint( true );
		setResizable( false );
		setFocusable( false );
		
        env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        device = env.getDefaultScreenDevice();
        gc = device.getDefaultConfiguration();
        
        int bitDepth     = device.getDisplayMode().getBitDepth();
        int refreshRate  = device.getDisplayMode().getRefreshRate();
        
        fullscreenMode = new DisplayMode( Configuration.getInteger("fs_res_w", 1024), Configuration.getInteger("fs_res_h", 768), bitDepth, refreshRate );
		windowedMode = new DisplayMode( Configuration.getInteger("wnd_res_w", 1024), Configuration.getInteger("wnd_res_h", 768), bitDepth, refreshRate );
		
		if( Configuration.getBoolean("fakefullscreen", false) )
		{
			//Setup fake-fullscreen
			setupFakeFullscreen();
		}
		else if( Configuration.getBoolean("fullscreen", false) && device.isFullScreenSupported() )
		{
			//Setup fullscreen
			setupFullscreen();
		}
		else
		{
			//Setup windowed
			setupWindowed();
		}
		
		if( !isVisible() )
			setVisible( true );
		
		if( canvas == null )
		{
	        createBufferStrategy( Configuration.getInteger("buffers", 2) );
	        bufferStrategy = getBufferStrategy();
		}
		else
		{
	        canvas.createBufferStrategy( Configuration.getInteger("buffers", 2) );
	        bufferStrategy = canvas.getBufferStrategy();
		}
		
		ScreenCanvas.width = raster.getWidth();
		ScreenCanvas.height = raster.getHeight();
		raster.addKeyListener( new Input() );
		raster.addMouseListener( new Input() );
		raster.addMouseMotionListener( new Input() );
		raster.addMouseWheelListener( new Input() );
		requestFocus();
        
        try{
        	Thread.sleep(375);
        } catch(Exception e) {}
        
    	Console.echo( Console.VERBOSE, "ScreenCanvas Initialized" );
    	setTitle( title );
	}
	
	public void cleanup()
	{
		if( device.getFullScreenWindow() != null )
			device.setFullScreenWindow(null);
			
		bufferStrategy = null;
		ScreenCanvas.instance = false;

		setVisible(false);	
		dispose();
	}
	
	private void setupWindowed()
	{
    	Console.echo( Console.NORMAL, "Using Windowed Mode" );
    	
    	Insets windowInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc ); //Use these insets to correct the 'odd' space left around borders...
		setSize( windowedMode.getWidth() + windowInsets.left + windowInsets.right, windowedMode.getHeight() + windowInsets.top + windowInsets.bottom );
		      
    	canvas = new Canvas();
        canvas.setBackground( Color.black );
        canvas.setIgnoreRepaint( true );
        canvas.setFocusable( true );
		canvas.setSize( windowedMode.getWidth(), windowedMode.getHeight() );
		canvas.setLocation( 0, 0 );
        add( canvas );
        
        raster = canvas;
        displayMode = windowedMode;
	}
	
	private void setupFullscreen()
	{
    	Console.echo( Console.NORMAL, "Using Fullscreen Mode" );
    	
		setUndecorated(true);
		if( device.isFullScreenSupported() )
			device.setFullScreenWindow(this);
		
		if( device.isDisplayChangeSupported() )
		{
			if( isDisplayModeSupported( fullscreenMode ) )
				device.setDisplayMode( fullscreenMode );
		}
		
		setSize( fullscreenMode.getWidth(), fullscreenMode.getHeight() );
        
        raster = this;
        canvas = null;
        displayMode = fullscreenMode;
	}
	
	private void setupFakeFullscreen()
	{
    	Console.echo( Console.NORMAL, "Using Fake Fullscreen Mode" );
    	
		setUndecorated( true );
		
		if( device.isDisplayChangeSupported() ) // Well, if we can change it without going into the
		{										// Fullscreen exclusive mode, do it. (Probably not though)
			if( isDisplayModeSupported( fullscreenMode ) )
				device.setDisplayMode( fullscreenMode );
		}
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		setSize( (int)screenSize.getWidth(), (int)screenSize.getHeight() );
		
    	canvas = new Canvas();
        canvas.setBackground( Color.black );
        canvas.setIgnoreRepaint( true );
        canvas.setFocusable( true );
		canvas.setSize( getSize() );
		canvas.setLocation( 0, 0 );
        add( canvas );
        
        raster = canvas;
        displayMode = new DisplayMode( (int)screenSize.getWidth(), (int)screenSize.getHeight(), fullscreenMode.getBitDepth(), fullscreenMode.getRefreshRate() );
	}
	
    private boolean isDisplayModeSupported( DisplayMode dm )
    {
        DisplayMode[] modes = device.getDisplayModes();
        for (int i = 0; i < modes.length; i++)
        {
            if (dm.getWidth() == modes[i].getWidth() && dm.getHeight() == modes[i].getHeight() && dm.getBitDepth() == modes[i].getBitDepth() && dm.getRefreshRate() == modes[i].getRefreshRate())
                return true;
        }
        return false;
    }
    
    public void setCursorVisible( boolean b )
    {
    	if( b )
    	{
	    	setCursor( Cursor.getDefaultCursor() );
	    }
	    else
	    {
			int[] pixels = new int[16 * 16];
			Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
			Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "");
	    	setCursor( transparentCursor );
	    }
    }
    
    public void setCustomCursor( ARGBImage img )
    {
		int[] pixels = img.toIntArray();
		Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
		Cursor cur = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "");
    	setCursor( cur );
    }

    public Graphics2D getGraphics()
    {
		if( bufferStrategy != null )
		{
			Graphics2D g = (Graphics2D)bufferStrategy.getDrawGraphics();
			g.setClip( new Rectangle( 0, 0, raster.getWidth(), raster.getHeight() ) );
			return g;
		}
		return null;
    }

    public void StartFrame()
    {
		if( bufferStrategy == null )
			return;
			
		startTime = Utility.currentTimeMillis();
		
		Graphics2D g = getGraphics();
		if(g == null)
			return;
			
		g.setColor( getBackground() );
		g.fillRect( 0, 0, raster.getWidth(), raster.getHeight() );
    }

    public void EndFrame()
    {
		if( bufferStrategy == null || bufferStrategy.contentsLost() )
			return;
		
		Graphics2D g = getGraphics();
		if(g == null)
			return;
			
		if( debug )
		{
			Console.render( g );
			DynamicGrid.render( g );
		}
		
		if( bufferStrategy != null && !bufferStrategy.contentsLost() && isVisible() )
			bufferStrategy.show();
			
		endTime = Utility.currentTimeMillis();
		if( fps_max > 0 )
		{
			wantedEndTime = ( 1000 / fps_max ) + startTime;
			while( wantedEndTime > endTime )
			{
				endTime = Utility.currentTimeMillis();
			}
		}
		
		if( endTime != startTime )
			ScreenCanvas.fps = Utility.clamp( (int)( 1000 / (endTime - startTime) ), 1, Integer.MAX_VALUE );
		else
			ScreenCanvas.fps = 1000;
			
		TimerSystem.update();
		
		/*if( fps_avg != 0)
		{
			fps_avg += ScreenCanvas.fps;
			fps_avg /= 2;
		}
		else
		{
			fps_avg = ScreenCanvas.fps;
		}*/
	}
	
	public void setDebug( boolean b )
	{
		debug = b;
	}
	
	public boolean getDebug()
	{
		return debug;
	}
	
	public void update( Graphics g ) {}
	public void paint( Graphics g ) {}
}

Input.java


/*
 *	Input Class
 *
 *	Allows for polling-input rather than event-driven input. This is an
 *	esspecially important concept of games that is missing in vanilla-Java.
 */

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Input implements MouseMotionListener, MouseListener, KeyListener, MouseWheelListener
{
	public static void init()
	{
		typedKeys = new ArrayList<Integer>();
		wheelUnits = 0;
		mouseBtns = new ArrayList<Integer>();
		mousePosition = new Point(0, 0);
	}
	
	private static ArrayList<Integer> typedKeys;
	
	protected static synchronized void  addKey( int key )
	{
		if( !isKeyDown(key) )
			typedKeys.add( new Integer(key) );
	}
	
	protected static synchronized void removeKey( int key )
	{
		for( int i = 0; i < typedKeys.size(); i++ )
		{
			if( typedKeys.get(i).intValue() == key )
			{
				typedKeys.remove(i);
				return;
			}
		}
	}
	
	public static boolean isKeyDown( int key )
	{
		for( int i = 0; i < typedKeys.size(); i++ )
		{
			if( typedKeys.get(i).intValue() == key )
				return true;
		}
		return false;
	}
	
	private static int wheelUnits;
	
	protected static synchronized final void setWheelMoved( int i )
	{
		wheelUnits = i;
	}
	
	public static final int getWheelMoved()
	{	// Read-Once method, for reacting to the mouse wheel properly
		int i = wheelUnits;
		wheelUnits = 0;
		return i;
	}
	
	private static ArrayList<Integer> mouseBtns;
	
	protected static synchronized void addMouseButton( int key )
	{
		if( !isMouseButtonDown(key) )
			mouseBtns.add( new Integer(key) );
	}
	
	protected static synchronized void removeMouseButton( int key )
	{
		for( int i = 0; i < mouseBtns.size(); i++ )
		{
			if( mouseBtns.get(i).intValue() == key )
			{
				mouseBtns.remove(i);
				return;
			}
		}
	}
	
	public static boolean isMouseButtonDown( int key )
	{
		for( int i = 0; i < mouseBtns.size(); i++ )
		{
			if( mouseBtns.get(i).intValue() == key )
				return true;
		}
		return false;
	}
	
	private static Point mousePosition;
	
	protected static synchronized void setMousePosition( Point p )
	{
		mousePosition = p;
	}
	
	public static final Point getMousePosition()
	{
		return new Point( (int)mousePosition.getX(), (int)mousePosition.getY() );
	}
	
	public static final Vector2D getMouseVector2D()
	{
		return new Vector2D( (int)mousePosition.getX(), (int)mousePosition.getY() );
	}
	
	public static final int getMouseX()
	{
		return (int)mousePosition.getX();
	}
	
	public static final int getMouseY()
	{
		return (int)mousePosition.getY();
	}

	/*
	 *	KeyListener Methods
	 */
	 
	public void keyPressed( KeyEvent evt )
	{
		Input.addKey( evt.getKeyCode() );
	}
	
	public void keyReleased( KeyEvent evt )
	{
		Input.removeKey( evt.getKeyCode() );
	}
	
	public void keyTyped( KeyEvent evt ) {}
	
	/*
	 *	MouseListener Methods
	 */
	
	public void mouseClicked( MouseEvent evt ) {}
	
	public void mouseEntered( MouseEvent evt )
	{
		Input.setMousePosition( evt.getPoint() );
	}
	
	public void mouseExited( MouseEvent evt )
	{
		Input.setMousePosition( evt.getPoint() );
	}
	
	public void mousePressed( MouseEvent evt )
	{
		Input.addMouseButton( evt.getButton() );
	}
	
	public void mouseReleased( MouseEvent evt )
	{
		Input.removeMouseButton( evt.getButton() );
	}
	
	/*
	 *	MouseMotionListener Methods
	 */
	
	public void mouseDragged( MouseEvent evt )
	{
		Input.setMousePosition( evt.getPoint() );
	}
	
	public void mouseMoved( MouseEvent evt )
	{
		Input.setMousePosition( evt.getPoint() );
	}
	
	/*
	 *	MouseWheelListener Methods
	 */
	
	public void mouseWheelMoved( MouseWheelEvent evt )
	{
		Input.setWheelMoved( evt.getWheelRotation() );
	}
}

There is more, of course, but I don’t think it’s relevant.

I dont find anything. Maybe you should take some timing along the main loop to see where
you lose much time and to compare window and fullscreen. Maybe you can spot specific
lines of code which make the difference.

Just take some System.nanoTime() here and there and always log the difference between
two measure points, and check the results. I hope this would give us a hint.

-JAW

Is this lInux, windows or Mac?

Mac used to have a really really bad fullscreen implementation :frowning:

True, but it’s better now.

I’ll try and do that this weekend. Thanks for the responses.

This is Windows, XP to be exact.

The problem seems to only occur when I have something with an alpha other than 0 or 255 being drawn. This includes BufferedImages and AlphaComposites. Otherwise, I get a 2x increase in framerate. Any idea? This slowdown doesn’t occur so severely outside of fullscreen.

Also, there doesn’t seem to be any leads about the KeyListener problem. I might have to switch over to jinput if I want to continue fullscreen support here, I suppose.

The problem seems to only occur when I have something with an alpha other than 0 or 255 being drawn. This
includes BufferedImages and AlphaComposites. Otherwise, I get a 2x increase in framerate. Any idea? This
slowdown doesn’t occur so severely outside of fullscreen.

Thats because full alpha is slow (software rendering). There are is a flag for acceleration and there is the opengl pipeline… both options arent really perfect right now. You might want to consider switching to some opengl binding like lwjgl or jogl.

Also the speed difference you’re seeing is most likely way bigger actually. If your game runs in fullscreen mode you cant exceed the monitor’s refresh rate (framerate wise I mean… so its effectively capped).

Well, my breakout game thingy runs at about 400fps (bitmask transparency only… drawing area approx 600x600) with a 500mhz cpu and a gf2mx.

And KeyListener… you could try requestFocus() and the listener itself… it looks horrible inefficient with all those loops (just use a flag array… you dont need a loop for that).

Roger. :wink: Thanks for that.

Well, I’ve started converting the project over to LWJGL. OpenGL doesn’t seem that hard, and I’ve already got a ‘wire-frame’ with alpha-blending and LWJGL’s jinput wrapper working. The new OpenGL coordinate system annoys me though, as I’m so ingrained in the Java2D way of doing things… That and I have no idea how to load an OpenGL texture from a BufferedImage… Back to the red-book :slight_smile: !

You might want to take a look at this thread:

http://www.java-gaming.org/forums/index.php?topic=11410.0;topicseen

Btw the space invaders example comes with some very basic texture loader.

Btw#2 putting texture data first into some BufferedImage and making then a texture out of it is unecessarly complicated (and thus slow). The loader in that other topic is tga only and very fast. Thanks to zip (jar) compression tgas end up having about the same size as pngs. Just give it a try. :wink:

Great link! Again, Thanks.

Acctually, I prefer Targa to PNG, I acctually chose PNG as ImageIO doesn’t have a Targa-reader (that I’ve found).

Only one thing that I don’t like is pre-game/in-game image manipulation. Maybe it’s just bad practice, but in this game I split a single image, namely a brick image, into several smaller parts that I use as a particle system to ‘spray out’ when a brick is hit. Cool effect, but I don’t see how this could be possible with that loader. I also pre-rotate the paddle-image to -45 and 45 degrees of movement for some very smooth movement effects. (I saw something about a ‘rotated-quad’, somewhere, but I don’t want to have to detect collisions for that, and leave it as a rectangle-pixel perfect) So, if you have any leads on how to get the best of both worlds, please let me know.

Maybe it’s just bad practice, but in this game I split a single image, namely a brick image, into several smaller
parts that I use as a particle system to ‘spray out’ when a brick is hit.

http://kaioa.com/k/TGATextureManager.java
http://kaioa.com/k/Texture.java

Take a look at the addSlicesWH and addSlicesCR methods. They allow you to create a bunch of Texture objects, which all reference the same (physical) texture.

I also pre-rotate the paddle-image to -45 and 45 degrees of movement for some very smooth movement effects.

In opengl you can just rotate around… together with the right texture filtering mode it looks nice n smooth.

With my loader it would be something like…

private TGATextureManager gfx=TGATextureManager.getInstance();
private Texture foo;
[…]
gfx.setLinear();
foo=gfx.add("/foo/bar/foo.tga");
[…]
and then translate (to the desired position), rotate, draw a quad as usual (but centered around 0/0) and then load identity. Like…

GL11.glTranslatef(x,y,0.0f);
GL11.glRotatef(angle, 0.0f, 0.0f, 1.0f);
drawImage(foo,-foo.width/2,-foo.height/2);
GL11.glLoadIdentity();

I’ve made lots of progress with LWJGL, but I have just one question. And a rather puzzling one at that (to an OpenGL-beginner anyway). When I draw images with OpenGL, it looks … crappier, for lack of a better word, than it does in the picture. It almost looks like it was dithered to 256 colors and then brought back up to 32-bit. Any idea? Images:

Good:
http://modx.ath.cx:1337/images/good.png

Bad:
http://modx.ath.cx:1337/images/bad.png

	public static void drawImage( QTexture tex, int x, int y )
	{
		if( color[3] == 0 )
			return;
		// store the current model matrix
		GL11.glPushMatrix();
		
		// translate to the right location and prepare to draw
		GL11.glTranslatef(x, y, 0);	
		GL11.glColor4f( color[0], color[1], color[2], color[3] );
		
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex.getTextureId() );
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f( 0, 0 );
			GL11.glVertex2f( 0, 0 );
			
			GL11.glTexCoord2f( tex.getWidthRatio(), 0 );
			GL11.glVertex2f( tex.getWidth(), 0 );
			
			GL11.glTexCoord2f( tex.getWidthRatio(), tex.getHeightRatio() );
			GL11.glVertex2f( tex.getWidth(), tex.getHeight() );
			
			GL11.glTexCoord2f( 0, tex.getHeightRatio() );
			GL11.glVertex2f( 0, tex.getHeight() );
		}
		GL11.glEnd();
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0 );

		// restore the model view matrix to prevent contamination
		GL11.glPopMatrix();
	}
import java.nio.*;
 
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.devil.*;
import org.lwjgl.*;

public class QTexture implements QDestroyable
{
	//...
	
	public static QTexture fromFile( String name, boolean flip )
	{
		QTexture texture = null;
		ByteBuffer imageData = null;
		int ilImageHandle;
		int oglImageHandle;
		IntBuffer scratch = BufferUtils.createIntBuffer(1);
		
		try{
			IL.create();
			ILU.create();
			ILUT.create();
		} catch (LWJGLException ex) {
			
		}
		// create image in DevIL and bind it
		IL.ilGenImages(scratch);
		IL.ilBindImage(scratch.get(0));
		ilImageHandle = scratch.get(0);
		
		// load the image
		//if(!IL.ilLoadFromURL(IL.class.getClassLoader().getResource(name)))
		//	return null;
		if( !IL.ilLoadImage( name ) )
			return null;
		
		// convert image to RGBA
		IL.ilConvertImage(IL.IL_RGBA, IL.IL_BYTE);
		
		// flip
		if( flip )
			ILU.iluFlipImage();
		
		// get image attributes
		int width = IL.ilGetInteger(IL.IL_IMAGE_WIDTH);
		int height = IL.ilGetInteger(IL.IL_IMAGE_HEIGHT);
		int textureWidth = QUtil.getNextPowerOfTwo(width);
		int textureHeight = QUtil.getNextPowerOfTwo(height);
		
		// resize image according to poweroftwo
		if (textureWidth != width || textureHeight != height) 
		{
			imageData = BufferUtils.createByteBuffer(textureWidth * textureHeight * 4);
			IL.ilCopyPixels(0, 0, 0, textureWidth, textureHeight, 1, IL.IL_RGBA, IL.IL_BYTE, imageData);
		} 
		else 
			imageData = IL.ilGetData();
		
		// create OpenGL counterpart
		GL11.glGenTextures(scratch);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, scratch.get(0));
		oglImageHandle = scratch.get(0);
		
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, textureWidth, textureHeight, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
		
		// Create image (either resized by copying, else directly from IL)
		if (textureWidth != width || textureHeight != height) 
			texture = new QTexture(oglImageHandle, width, height, (width / (float) textureWidth), (height / (float) textureHeight), textureWidth, textureHeight);
		else
			texture = new QTexture(oglImageHandle, width, height);
		
		// delete Image in DevIL
		scratch.put(0, ilImageHandle);
		IL.ilDeleteImages(scratch);
		IL.destroy();
		ILU.destroy();
		ILUT.destroy();
		
		// revert the gl state back to the default so that accidental texture binding doesn't occur
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
		
		// return OpenGL texture handle
		return texture;
	}
}

http://kaioa.com/k/vagecmp.png

Looks more like 16bit to me. Are you sure your screen is 24 or 32bit?

I’m running in Windowed mode at 32-bit, according to Windows.

EDIT:
I’m going to repost the issue I’m having with OpenGL at the LWJGL forum.

To answer the original question of Keyboard input not working…

I fiddled around with this mess for hours, and it seems that if you add your KeyListeners before entering full-screen mode, that they do indeed work. We’ll see if that stands up throughout development. What a pain in the ass.

Ahh, I’m sure someone else will be happy for that.

Right now though, I’ve completely transferred over to LWJGL. The only reason I might go back and revisit Java2D is if I want to make a Java2D wrapper that is also compatible with my OpenGL wrapper.

I’d pay at least $75 USD for a Java2D-like interface to OpenGL. Give me the old “drawImage()”, with support for text, lines, shapes, colors, etc. Call it GL2D or something with the acceleration of a dedicated card. I’d definitely go for it.

Of course it’s probably not that simple. Different image formats/loaders, and a whole lot of other stuff. I should just learn OpenGL, I’ve got a copy of the Red Book.

I guess I should check to see if the before/after fullscreen listener thingy is already on the bug database, or if it’s off somewhere in some really obscure documentation…

A proper Java2D interface to OpenGL looks remarkably like what is being shipped with Java 6.0…

Cas :slight_smile:

Oh, really?

Well, cas, given your experience, and my history of reading many of your posts over the years, when you say “looks remarkably like” what I hear is “it’s close, but here’s critical feature 1, 2, and 3 that they’re still missing, and isn’t planned until 8.0.” Is this a fair statement?

I’m off to read about the coming Java2D goodness.