package gl4java.awt; import gl4java.*; import gl4java.drawable.*; import gl4java.drawable.utils.*; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; import java.awt.event.*; /** * This is meant as an base class writing * easy offscreen render functions. * *

* * If you are interessting in further Documentation and/or * the history of GL4Java follow the following link. * *

	The GL4Java Documentation
 * 
*

* * There are two ways of using a GLOffScreenDrawable: the {@link * gl4java.drawable.GLEventListener} model or the subclassing model. Earlier * versions of the system only supported the subclassing model. The * default implementations of {@link #init}, {@link #display}, * {@link #reshape} and {@link #doCleanup} * now send events to GLEventListeners; they can * still be overridden as before to support the subclassing model. * *

* If using the subclassing model, you should override the following * methods for your needs: *

        preInit - initialisation before creating GLContext
        init - 1st initialisation after creating GLContext
        doCleanup - OGL cleanup prior to context deletion
        display - render your frame
	reshape - to reshape (window resize), gljResize() is allready invoked !
 * 
* * To check if you can use the GLContext and GL and GLU methods, * use the function *
        cvsIsInit
 * 
*

* IF you remove/release a GLOffScreenDrawable, * e.g. you want to close/dispose it´s Window (which contains this GLOffScreenDrawable), * you HAVE TO call: * *

        cvsDispose
 * 
* You should call this before releasing/dispose this Window ! * Also you can overwrite this class, * to dispose your own elements, e.g. a Frame etc. - * but be shure that you call * cvsDispose implementation call this one ! * *

* We do offer the following Canvas methods. * *

        repaint
 * 
*

* * @version 2.0, 21. April 1999 * @author Sven Goethel * */ public class GLOffScreenDrawable implements GLEnum, GLUEnum, GLDrawable { protected GLContext glj = null; public GLFunc gl = null; public GLUFunc glu = null; protected boolean cvsInitialized=false; protected boolean needCvsDispose = false; /** * Visual pre-set for stencil-bit number, default: 0 * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see gl4java.awt.GLOffScreenDrawable#preInit * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected int stencilBits = 0; /** * Visual pre-set for accumulator buffer size, default: 0 * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see gl4java.awt.GLOffScreenDrawable#preInit * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected int accumSize = 0; /** * Visual pre-set for stereoView, default: false * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see gl4java.awt.GLOffScreenDrawable#preInit * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected boolean stereoView = false; /** * Visual pre-set for RGBA usage, default: true - of course ;-) * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see gl4java.awt.GLOffScreenDrawable#preInit * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected boolean rgba = true; /** * The context with witch display lists and textures will be shared. * * @see gl4java.awt.GLOffScreenDrawable#preInit * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected GLContext sharedGLContext; /** * The data to hold the offscreen pixels on the java side ! * * @see gl4java.awt.GLOffScreenDrawable#repaint */ protected BufferedImage offImage = null; protected int glFormat=0; protected int glType=0; protected int glComps=0; protected int awtFormat=0; /** * The custom set offscreen Size * * If this is set to != null, * the offscreen pixmap is used in this size, * not in the components-size (-> faster if smaller) * * @see gl4java.awt.GLOffScreenDrawable#setOffScreenSize * @see gl4java.awt.GLOffScreenDrawable#getOffScreenSize */ protected Dimension offScrnSize = null; protected boolean offScrnSizeChanged=false; // The list of GLEventListeners private GLEventListenerList listeners = new GLEventListenerList(); static { if(GLContext.doLoadNativeLibraries(null, null, null)==false) System.out.println("GLOffScreenDrawable could not load def. native libs."); } /** * * Constructor * * @param gl_Name The name of the GLFunc implementation * If gl_Name==null, the default class will be used ! * * @param glu_Name The name of the GLUFunc implementation * If gl_LibName==null, the default class will be used ! * * @param size The offscreen Size */ public GLOffScreenDrawable( String gl_Name, String glu_Name, Dimension size ) { if( (gl=GLContext.createGLFunc(gl_Name)) ==null) { System.out.println("GLFunc implementation "+gl_Name+" not created"); } if( (glu=GLContext.createGLUFunc(glu_Name)) ==null) { System.out.println("GLUFunc implementation "+glu_Name+" not created"); } offScrnSizeChanged=true; offScrnSize=size; } /** * * Constructor * * @param size The offscreen Size */ public GLOffScreenDrawable(Dimension size ) { this(null, null, size); } /** * Used to return the created GLContext */ public final GLContext getGLContext() { return glj; } /** * Get the customers offscreen Size * * If this is set, * the offscreen pixmap is used in this size, * not in the components-size (-> faster if smaller) * * @see gl4java.awt.GLOffScreenDrawable#offScrnSize * @see gl4java.awt.GLOffScreenDrawable#setSize */ public Dimension getSize() { return offScrnSize; } /** * The customers offscreen Size * * If this is set, * the offscreen pixmap is used in this size, * not in the components-size (-> faster if smaller) * * @see gl4java.awt.GLOffScreenDrawable#offScrnSize * @see gl4java.awt.GLOffScreenDrawable#getOffScreenSize */ public void setSize(Dimension size) { if((size!=null && size.equals(offScrnSize)==false) || size!=offScrnSize ) { offScrnSizeChanged=true; offScrnSize=size; initDrawable(); } } /** * The data to hold the offscreen pixels on the java side ! * * @see gl4java.awt.GLOffScreenDrawable#repaint */ public BufferedImage getImage() { return offImage; } /** * The data to hold the offscreen pixels on the java side ! * * @param gr the Graphics which should draw * @param x the destination x pos to draw * @param y the destination y pos to draw * @param width the destination width to draw * @param height the destination height to draw * @param notifiedObject the notified Object for proceding */ public void drawImage(Graphics gr, int x, int y, int width, int height, ImageObserver notifiedObject) { gr.drawImage(offImage, x, y, width, height, notifiedObject); } /** * The format of GL pixel data * * @see gl4java.GLFunc#glReadPixels */ public int getGLFormat() { return glFormat; } /** * The type of GL pixel data * * @see gl4java.GLFunc#glReadPixels */ public int getGLType() { return glType; } /** * The number of byte components per pixel * * @see gl4java.GLFunc#glReadPixels */ public int getGLComponents() { return glComps; } /** * The AWT BufferedImage Type */ public int getAwtFormat() { return awtFormat; } /** * The initialisation procedure * * must be called by the user at least once, * best after adding the GLEventListener's ! * * @see gl4java.awt.GLOffScreenDrawable#repaint */ public synchronized final void initDrawable() { if(glj == null || offScrnSizeChanged ) { cvsDispose(); preInit(); glj = GLContext.createOffScreenCtx ( gl, glu, stereoView, rgba, stencilBits, accumSize, sharedGLContext, offScrnSize ); if(glj!=null) { stencilBits = glj.getStencilBitNumber(); accumSize = glj.getAccumSize(); stereoView = glj.isStereoView(); rgba = glj.isRGBA(); } if(offImage!=null) offImage.flush(); offImage=null; offScrnSizeChanged=false; init(); Dimension size = getSize(); reshape(size.width, size.height); if(glj!=null && glj.gljIsInit()) cvsInitialized=true; } repaint(); } Graphics gr = null; DataBufferInt dbInt = null; DataBufferUShort dbUShort = null; DataBufferByte dbByte = null; /** * this function can be called to force a repaint * * Repaints this component. * * This method causes a call to this component's update method * as soon as possible. * * This is the thread save rendering-method called by yourself. * The actual thread will be set to highes priority befor calling * 'display'. After 'display' the priority will be reset ! * * 'gljFree' will be NOT called after 'display'. * * We tested the above to use multi-threading and * for the demonstration 'glDemos' it works ;-)) ! * * BE SURE, if you want to call 'display' by yourself * (e.g. in the run method for animation) * YOU HAVE TO CALL sDisplay -- OR YOU MUST KNOW WHAT YOU ARE DOING THEN ! * * @return void * * @see gl4java.awt.GLOffScreenDrawable#display * @see gl4java.awt.GLEventListener#preDisplay * @see gl4java.awt.GLEventListener#display * @see gl4java.awt.GLEventListener#postDisplay */ public synchronized final void repaint() { boolean ok = true; long _s = System.currentTimeMillis(); if(!cvsIsInit()) return; listeners.sendPreDisplayEvent(this); if( glj.gljMakeCurrent() == false ) { System.out.println("GLOffScreenDrawable: problem in use() method"); return; } if(ok) { display(); _f_dur_self = System.currentTimeMillis()-_s; if(GLContext.gljClassDebug) { _f_dur_self_sum+=_f_dur_self; glj.gljCheckGL(); } Dimension size = offScrnSize; int w=size.width; int h=size.height; long _s_tst = System.currentTimeMillis(); if(offImage==null || offImage.getHeight()!=h || offImage.getWidth()!=w) { GLCapabilities caps = glj.getGLCapabilities(); switch ( GLContext.getNativeOSType() ) { case GLContext.OsWindoof: if(caps.getAlphaBits()>0) awtFormat = BufferedImage.TYPE_INT_ARGB; else awtFormat = BufferedImage.TYPE_INT_RGB; break; case GLContext.OsX11: case GLContext.OsMac9: case GLContext.OsMacX: case GLContext.OsUnknown: default: if(caps.getAlphaBits()>0) awtFormat = BufferedImage.TYPE_INT_ARGB; else awtFormat = BufferedImage.TYPE_3BYTE_BGR; break; } if(offImage!=null) offImage.flush(); offImage = new BufferedImage(w,h,awtFormat); dbByte=null; dbUShort=null; dbInt=null; switch (awtFormat) { case BufferedImage.TYPE_3BYTE_BGR: if(GLContext.gljClassDebug) System.out.println("awt=3BYTE_BGR, gl=BGR,UNSIGNED_BYTE"); glFormat = GL_BGR; glType = GL_UNSIGNED_BYTE; glComps = 3; dbByte = (DataBufferByte) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_4BYTE_ABGR: if(GLContext.gljClassDebug) System.out.println("awt=4BYTE_ABGR, gl=BGRA,UNSIGNED_INT_8_8_8_8"); glFormat = GL_BGRA; glType = GL_UNSIGNED_BYTE; glComps = 4; dbByte = (DataBufferByte) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_RGB: if(GLContext.gljClassDebug) System.out.println("awt=INT_RGB, gl=BGRA,UNSIGNED_BYTE"); glFormat = GL_BGRA; glType = GL_UNSIGNED_BYTE; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_ARGB: if(GLContext.gljClassDebug) System.out.println("awt=INT_ARGB, gl=BGRA,UNSIGNED_BYTE"); glFormat = GL_BGRA; glType = GL_UNSIGNED_BYTE; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_BGR: if(GLContext.gljClassDebug) System.out.println("awt=INT_BGR, gl=BGRA,UNSIGNED_INT_8_8_8_8"); glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_USHORT_555_RGB: if(GLContext.gljClassDebug) System.out.println("awt=USHORT_555_RGB, gl=RGBA,UNSIGNED_INT_5_5_5_1"); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; glComps = 2; dbUShort = (DataBufferUShort) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_USHORT_565_RGB: if(GLContext.gljClassDebug) System.out.println("awt=USHORT_565_RGB, gl=RGB,UNSIGNED_INT_5_6_5"); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; glComps = 2; dbUShort = (DataBufferUShort) offImage.getRaster().getDataBuffer(); break; } } if(dbByte!=null) glj.gljReadPixelGL2AWT(w, 0,0, 0, 0, w, h, glFormat, glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, dbByte.getData()); else if(dbUShort!=null) glj.gljReadPixelGL2AWT(w, 0,0, 0, 0, w, h, glFormat, glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, dbUShort.getData()); else if(dbInt!=null) glj.gljReadPixelGL2AWT(w, 0,0, 0, 0, w, h, glFormat,glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, dbInt.getData()); //glj.gljSwap(); // no true swapping with offscreen buffers .. if(GLContext.gljClassDebug) _f_dur_tst_sum+=System.currentTimeMillis()-_s_tst; if(GLContext.gljClassDebug) glj.gljCheckGL(); glj.gljFree(); // enable ctx for threads ... _f_dur_total = System.currentTimeMillis()-_s; if(GLContext.gljClassDebug) { _f_dur_total_sum+=_f_dur_total; if(++_f_dur_times==100) { System.out.println("self p 100: "+ (double)(_f_dur_self_sum/100)/1000.0+" s"); System.out.println("tst p 100: "+ (double)(_f_dur_tst_sum/100)/1000.0+" s"); System.out.println("gl-bitblit p 100: "+ (double)((_f_dur_total_sum-_f_dur_self_sum)/100)/1000.0+" s"); System.out.println("total p 100: "+ (double)(_f_dur_total_sum/100)/1000.0+" s"); _f_dur_self_sum=0; _f_dur_tst_sum=0; _f_dur_total_sum=0; _f_dur_times=0; } } } listeners.sendPostDisplayEvent(this); } /** * * This is the rendering-method called by repaint * *

* The default implementation of display() sends display events to * all {@link gl4java.drawable.GLEventListener}s associated with this * GLOffScreenDrawable, and automatically calls {@link * gl4java.GLContext#gljMakeCurrent} and {@link * gl4java.GLContext#gljFree} as necessary. * *

} * If you use the subclassing model (as opposed to the * GLEventListener model), your subclass will redefine this to * perform its OpenGL drawing. * *

* BE SURE, if you want to call 'display' by yourself * (e.g. in the run method for animation) * YOU HAVE TO CALL sDisplay ! * * 'sDisplay' manages a semaphore to avoid reentrance of * the display function !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * @return void * * @see gl4java.awt.GLOffScreenDrawable#sDisplay * @see gl4java.awt.GLOffScreenDrawable#paint * @see gl4java.drawable.GLEventListener#display */ public void display() { listeners.sendDisplayEvent(this); } /** * * This is your pre-init method. * preInit is called just BEFORE the GL-Context is created. * You should override preInit, to initialize your visual-stuff, * like the protected vars: doubleBuffer and stereoView * * @return void * * @see gl4java.awt.GLOffScreenDrawable#paint * @see gl4java.awt.GLOffScreenDrawable#stereoView * @see gl4java.awt.GLOffScreenDrawable#rgba * @see gl4java.awt.GLOffScreenDrawable#stencilBits * @see gl4java.awt.GLOffScreenDrawable#accumSize */ public void preInit() { } /** * * init is called right after the GL-Context is initialized. * The default implementation calls init() on all of this * component's GLEventListeners. * *

* If using the subclassing model, you can override this to * perform one-time OpenGL initializations such as setting up * lights and display lists. * * @return void * * @see gl4java.awt.GLOffScreenDrawable#paint * @see gl4java.drawable.GLEventListener#init */ public void init() { listeners.sendInitEvent(this); } /** * This method is used to clean up any OpenGL stuff (delete textures * or whatever) prior to actually deleting the OpenGL context. * You should override this with your own version, if you need to do * any cleanup work at this phase. * This functions is called within cvsDispose * * @return void * * @see gl4java.awt.GLOffScreenDrawable#cvsDispose * @see gl4java.drawable.GLEventListener#cleanup */ public void doCleanup() { listeners.sendCleanupEvent(this); } /** * This function returns, if everything is init: the GLContext, * the and the users init function * This value is set in the paint method! * * @return boolean * * @see gl4java.awt.GLOffScreenDrawable#paint * @see gl4java.awt.GLOffScreenDrawable#init */ public boolean cvsIsInit() { return cvsInitialized; } protected long _f_dur_self = 0; protected long _f_dur_self_sum = 0; protected long _f_dur_tst_sum = 0; protected long _f_dur_total = 0; protected long _f_dur_total_sum = 0; protected int _f_dur_times = 0; /** * * This ´reshape´ method will be invoked after the first paint command * after GLCanvas.componentResize is called AND only if ´gljMakeCurrent´ was * successful (so a call of gljMakeCurrent is redundant). * ´reshape´ is not an overloading of java.awt.Component.reshape, * ´reshape´ is more like ´glut´-reshape. * *

* GLCanvas.reshape already has a simple default implementation, * which calls ´gljResize´ and ´glViewport´. It also sends the * reshape() event to all GLEventListeners. If using the * GLEventListener model, it may not be necessary to do anything * in your event listener's reshape() method; if using the * subclassing model, it may not be necessary to override this. * *

* The needed call to ´gljResize´ is done by the invoker paint ! * * @param width the new width * @param height the new height * @return void * * @see gl4java.awt.GLOffScreenDrawable#paint * @see gl4java.awt.GLOffScreenDrawable#sDisplay * @see gl4java.drawable.GLEventListener#reshape */ public void reshape( int width, int height ) { if(GLContext.gljClassDebug) System.out.println("GLOffScreenDrawable::reshape size("+getSize()+")"); gl.glViewport(0,0, width, height); listeners.sendReshapeEvent(this, width, height); } /** * You should call this before releasing/dispose this Window ! * Also you can overwrite this class, * to dispose your own elements, e.g. a Frame etc. - * but be shure that you call * cvsDispose implementation call this one ! * * This function calls gljDestroy of GLContext ! * * @see gl4java.GLContext#gljDestroy * @see gl4java.awt.GLOffScreenDrawable#doCleanup * @see gl4java.drawable.GLEventListener#cleanup */ public void cvsDispose() { cvsInitialized = false; if (glj != null) { if (glj.gljIsInit()) { /* Sometimes the Microsoft VM calls the Applet.stop() method but doesn't have permissions to do J/Direct calls, so this whole block of code will throw a security exception. If this happens, however, windowClosing() will still call us again later and we will have another opportunity to shut down the context, so it all works out fine. */ try { glj.gljFree(); doCleanup(); //locks and free's GLContext glj.setEnabled(false); glj.gljDestroy(); needCvsDispose = false; } catch (Exception ex) { needCvsDispose = true; } } } // Setting glj to null will simply cause paint() to re-initialize. // We don't want that to happen, so we will leave glj non-null. } /** * does nothing than: * * @see gl4java.awt.GLCanvas#cvsDispose */ protected void finalize() throws Throwable { if(GLContext.gljClassDebug) System.out.println("GLCanvas finalize .."); cvsDispose(); super.finalize(); } /** * get methods */ public final int cvsGetWidth() { return getSize().width; } public final int cvsGetHeight() { return getSize().height; } //---------------------------------------------------------------------- // Implementation of GLDrawable // /** * the components listener's should be implemented also ! * since JDK 1.1 */ public void addComponentListener(ComponentListener l) { } public void removeComponentListener(ComponentListener l) { } public void addFocusListener(FocusListener l) { } public void addKeyListener(KeyListener l) { } public void removeFocusListener(FocusListener l) { } public void addMouseListener(MouseListener l) { } public void removeMouseListener(MouseListener l) { } public void addMouseMotionListener(MouseMotionListener l) { } public void removeMouseMotionListener(MouseMotionListener l) { } public void addGLEventListener(GLEventListener listener) { listeners.add(listener); } public void removeGLEventListener(GLEventListener listener) { listeners.remove(listener); } public GLFunc getGL() { return gl; } public GLUFunc getGLU() { return glu; } }