I am writing a Net Meeting clone for java. I have the entire thing working right now and I have finally declared version 1.0. I have noticed that the Java2D paint method consumes a lot of CPU usage. So I thought why not try using an OpenGL library instead.
I need to know how to display a BufferedImage onto one Quad or if that is even possible.
I know that textures have to be a power of two. Most desktops are going to be in a 4:3 or 16:9 ratio, so this may not work at all, unless someone has ideas. I was thinking about just taking a 1280x1024 image and placing it on a 1280x1280 texture, then moving the camera over just 1280x1024. Possible?
The whole idea is to get rid of that Java2D so I can conserve some resources. :) This is what I have so far - a mutated version of the FullScreenWindowedTest. Comments have been removed to save post space...not to ignore their respective owners:
package org.lwjgl.test.opengl;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;
public class FullScreenWindowedTest
{
public static void main ( String[ ] args )
{
FullScreenWindowedTest fswTest = new FullScreenWindowedTest( );
fswTest.execute( );
}
private DisplayMode mode;
private long startTime = System.currentTimeMillis( );
private int texture;
/**
* Creates a FullScreenWindowedTest
*/
public FullScreenWindowedTest ( )
{
}
/**
* Executes the test
*/
public void execute ( )
{
initialize( );
mainLoop( );
}
/**
* Retrieves a displaymode, if one such is available
*
* @param width Required width
* @param height Required height
* @param bpp Minimum required bits per pixel
* @return
*/
private DisplayMode findDisplayMode ( int width, int height, int bpp )
{
DisplayMode[ ] modes = Display.getAvailableDisplayModes( );
for ( int i = 0; i < modes.length; i++ )
{
if ( modes[i].getWidth( ) == width && modes[i].getHeight( ) == height && modes[i].getBitsPerPixel( ) >= bpp && modes[i].getFrequency( ) <= 60 )
{
try
{
Display.setDisplayMode( modes[i] );
}
catch ( LWJGLException e )
{
e.printStackTrace( );
}
return modes[i];
}
}
return null;
}
/**
* Initializes OGL
*/
private void glInit ( )
{
// Go into orthographic projection mode.
GL11.glMatrixMode( GL11.GL_PROJECTION );
GL11.glLoadIdentity( );
GLU.gluOrtho2D( 0, mode.getWidth( ), 0, mode.getHeight( ) );
GL11.glMatrixMode( GL11.GL_MODELVIEW );
GL11.glLoadIdentity( );
GL11.glViewport( 0, 0, mode.getWidth( ), mode.getHeight( ) );
GL11.glHint( GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST );
GL11.glEnable( GL11.GL_DEPTH_TEST );
GL11.glDepthFunc( GL11.GL_LEQUAL );
GL11.glShadeModel( GL11.GL_SMOOTH );
// enable texture mapping - me
GL11.glEnable( GL11.GL_TEXTURE_2D );
//set clear color to black
GL11.glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
//sync frame (only works on windows)
Display.setVSyncEnabled( false );
System.err.println( "GL_VENDOR: " + GL11.glGetString( GL11.GL_VENDOR ) );
System.err.println( "GL_RENDERER: " + GL11.glGetString( GL11.GL_RENDERER ) );
System.err.println( "GL_VERSION: " + GL11.glGetString( GL11.GL_VERSION ) );
texture = loadTexture( "test.png" );
System.out.println( texture + "t" );
}
/**
* Initializes the test
*/
private void initialize ( )
{
try
{
//find displaymode
mode = findDisplayMode( 800, 600, 32 );
// start of in windowed mode
Display.create( );
glInit( );
}
catch ( Exception e )
{
e.printStackTrace( );
}
}
private final int loadTexture ( String path )
{
Image image = ( new javax.swing.ImageIcon( path ) ).getImage( );
// Exctract The Image
BufferedImage tex = new BufferedImage( image.getWidth( null ), image.getHeight( null ), BufferedImage.TYPE_3BYTE_BGR );
Graphics2D g = ( Graphics2D ) tex.getGraphics( );
g.drawImage( image, null, null );
g.dispose( );
// Flip Image
AffineTransform tx = AffineTransform.getScaleInstance( 1, -1 );
tx.translate( 0, -image.getHeight( null ) );
AffineTransformOp op = new AffineTransformOp( tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR );
tex = op.filter( tex, null );
// Put Image In Memory
ByteBuffer scratch = ByteBuffer.allocateDirect( 4 * tex.getWidth( ) * tex.getHeight( ) );
byte data[] = ( byte[ ] ) tex.getRaster( ).getDataElements( 0, 0, tex.getWidth( ), tex.getHeight( ), null );
scratch.clear( );
scratch.put( data );
scratch.rewind( );
// Create A IntBuffer For Image Address In Memory
IntBuffer buf = ByteBuffer.allocateDirect( 4 ).order( ByteOrder.nativeOrder( ) ).asIntBuffer( );
GL11.glGenTextures( buf ); // Create Texture In OpenGL
GL11.glBindTexture( GL11.GL_TEXTURE_2D, buf.get( 0 ) );
// Typical Texture Generation Using Data From The Image
// Linear Filtering
GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR );
// Linear Filtering
GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR );
// Generate The Texture
GL11.glTexImage2D( GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, tex.getWidth( ), tex.getHeight( ), 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, scratch );
return buf.get( 0 ); // Return Image Address In Memory
}
/**
* Runs the main loop of the "test"
*/
private void mainLoop ( )
{
int fpsCount = 0;
while ( !Keyboard.isKeyDown( Keyboard.KEY_ESCAPE ) && !Display.isCloseRequested( ) )
{
if ( Display.isVisible( ) )
{
// check keyboard input
processKeyboard( );
// render it
render( );
}
else
{
// no need to render/paint if nothing has changed (ie.
// window
// dragged over)
if ( Display.isDirty( ) )
{
render( );
}
// don't waste cpu time, sleep more
try
{
Thread.sleep( 100 );
}
catch ( InterruptedException inte )
{
}
}
// Update window
Display.update( );
fpsCount++;
if ( System.currentTimeMillis( ) - startTime > 1000 )
{
System.out.println( fpsCount );
fpsCount = 0;
startTime = System.currentTimeMillis( );
}
}
}
/**
* Processes keyboard input
*/
private void processKeyboard ( )
{
//check for fullscreen key
if ( Keyboard.isKeyDown( Keyboard.KEY_F ) )
{
try
{
Display.setFullscreen( true );
}
catch ( Exception e )
{
e.printStackTrace( );
}
}
//check for window key
if ( Keyboard.isKeyDown( Keyboard.KEY_W ) )
{
try
{
Display.setFullscreen( false );
}
catch ( Exception e )
{
e.printStackTrace( );
}
}
}
private void render ( )
{
//clear background
GL11.glClear( GL11.GL_COLOR_BUFFER_BIT );
// draw white quad
GL11.glPushMatrix( );
{
// color of quad
//GL11.glColor3f( 1.0f, 1.0f, 1.0f );
GL11.glBindTexture( GL11.GL_TEXTURE_2D, texture );
GL11.glBegin( GL11.GL_QUADS );
{
// bottom left
GL11.glVertex2i( 0, 0 );
// bottom right
GL11.glVertex2i( 512, 0 );
// top right
GL11.glVertex2i( 512, 512 );
// top left
GL11.glVertex2i( 0, 512 );
}
GL11.glEnd( );
}
GL11.glPopMatrix( );
}
}
[/code]
I don't believe 1280x1280 is a legal texture size, since it is not a power of 2. You can expect most cards to be able to do 512x512. 1024x1024 is not available even a lot, even now. You can do what you describe in ortho mode. What you are suggesting however might not look very good. What you might want to do instead is use a 512x512 image as a texture, set ortho mode to 1024x768 then draw a quad 512x512 centered on the screen using the image as its texture.
You can use ARB_texture_non_power_of_two if it's available to save a bit of RAM.
Cas :)
ARB_texture_non_power_of_two is available only on 6800s. EXT_texture_rectangle (or NV_texture_rectangle, it's the same thing) should do the job. Both are widely available. 2048 max texture size is also available on most (all?) modern cards.
I am more concnered with how to just load one quad onto the screen with a BufferedImage textured onto it. I could easily resize and reposition the camera node to display what I want. :) Thank you for your replies thus far!
Here's a class to use an Image as a texture, I assume it's not much different to use a BufferedImage instead of Image.
http://potatoland.com/code/gl/GLImage.java
The function loadImage() grabs pixels from java Image and prepares them for use as a texture (I'm not sure if the BufferedImage pixel format is different).
You can use the resulting pixelBuffer to create a texture:
makeTexture(glimg.pixelBuffer, glimg.w, glimg.h);
...
public int makeTexture(ByteBuffer pixels, int w, int h)
{
// get a new empty texture
int textureHandle = allocateTexture();
// 'select' the new texture by it's handle
GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
// set texture parameters
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
// Create the texture from pixels
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, w, h,
0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
return textureHandle;
}
Then the texture can be applied to a quad in ortho mode.