/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.canvas; import static com.metsci.glimpse.util.logging.LoggerUtils.*; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.util.logging.Logger; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLContext; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLOffscreenAutoDrawable; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; import com.jogamp.opengl.FBObject.Colorbuffer; import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureIO; import com.metsci.glimpse.context.GlimpseBounds; import com.metsci.glimpse.context.GlimpseTargetStack; import com.metsci.glimpse.gl.util.GLUtils; import com.metsci.glimpse.layout.GlimpseLayout; import com.metsci.glimpse.support.texture.ExternalTextureProjected2D; import com.metsci.glimpse.support.texture.TextureProjected2D; // example JOGL FBO Usage: https://github.com/sgothel/jogl/blob/master/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOffThreadSharedContextMix2DemosES2NEWT.java // https://github.com/sgothel/jogl/blob/master/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOAutoDrawableFactoryNEWT.java public class FBOGlimpseCanvas extends AbstractGlimpseCanvas { private static final Logger logger = Logger.getLogger( FBOGlimpseCanvas.class.getName( ) ); public static final int DEFAULT_TEXTURE_UNIT = 0; protected GLProfile glProfile; protected GLOffscreenAutoDrawable.FBO drawable; protected boolean isDestroyed; public FBOGlimpseCanvas( GLProfile glProfile, int width, int height ) { init( glProfile, null, width, height, true ); } public FBOGlimpseCanvas( GLContext glContext, int width, int height ) { init( glContext.getGLDrawable( ).getGLProfile( ), glContext, width, height, true ); } public FBOGlimpseCanvas( GLContext glContext, int width, int height, boolean isBackgroundOpaque ) { init( glContext.getGLDrawable( ).getGLProfile( ), glContext, width, height, isBackgroundOpaque ); } /** * @deprecated Use {@link #FBOGlimpseCanvas(GLContext,int,int)} instead. The context implicitly provides a GLProfile. */ @Deprecated public FBOGlimpseCanvas( String glProfileName, GLContext glContext, int width, int height ) { this( GLProfile.get( glProfileName ), glContext, width, height ); } /** * @deprecated Use {@link #FBOGlimpseCanvas(GLContext,int,int)} instead. The context implicitly provides a GLProfile. */ @Deprecated public FBOGlimpseCanvas( GLProfile glProfile, GLContext glContext, int width, int height ) { init( glProfile, glContext, width, height, true ); } private void init( GLProfile glProfile, GLContext glContext, int width, int height, boolean isbackgroundOpaque ) { this.glProfile = glProfile; GLCapabilities caps = new GLCapabilities( glProfile ); caps.setBackgroundOpaque( isbackgroundOpaque ); caps.setDoubleBuffered( false ); this.drawable = ( GLOffscreenAutoDrawable.FBO ) GLUtils.newOffscreenDrawable( caps, glProfile, glContext ); this.drawable.addGLEventListener( createGLEventListener( ) ); this.drawable.setSurfaceSize( width, height ); this.drawable.setTextureUnit( DEFAULT_TEXTURE_UNIT ); } public void resize( int width, int height ) { this.drawable.setSurfaceSize( width, height ); } public BufferedImage toBufferedImage( ) { GLContext glContext = this.drawable.getContext( ); glContext.makeCurrent( ); try { this.paint( ); AWTGLReadBufferUtil util = new AWTGLReadBufferUtil( this.glProfile, true ); return util.readPixelsToBufferedImage( glContext.getGL( ), true ); } finally { glContext.release( ); } } //XXX this link probably no longer relevant to 2.2.0 // see: http://forum.jogamp.org/querying-textures-bound-to-default-draw-read-framebuffers-td4026564.html public int getTextureName( ) { return drawable.getColorbuffer( GL.GL_FRONT ).getName( ); } public TextureProjected2D getProjectedTexture( ) { Colorbuffer b = drawable.getColorbuffer( GL.GL_FRONT ); return new ExternalTextureProjected2D( b.getName( ), b.getWidth( ), b.getHeight( ), false ); } public Texture getTexture( ) { return TextureIO.newTexture( drawable.getTextureUnit( ) ); } protected GLEventListener createGLEventListener( ) { return new GLEventListener( ) { @Override public void init( GLAutoDrawable drawable ) { try { GL gl = drawable.getGL( ); gl.setSwapInterval( 0 ); } catch ( Exception e ) { // without this, repaint rate is tied to screen refresh rate on some systems // this doesn't work on some machines (Mac OSX in particular) // but it's not a big deal if it fails logWarning( logger, "Trouble in init.", e ); } } @Override public void display( GLAutoDrawable drawable ) { for ( GlimpseLayout layout : layoutManager.getLayoutList( ) ) { layout.paintTo( getGlimpseContext( ) ); } } @Override public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height ) { for ( GlimpseLayout layout : layoutManager.getLayoutList( ) ) { layout.layoutTo( getGlimpseContext( ) ); } } @Override public void dispose( GLAutoDrawable drawable ) { for ( GLRunnable runnable : disposeListeners ) { runnable.run( drawable ); } } }; } @Override public GLProfile getGLProfile( ) { return this.glProfile; } @Override public GLOffscreenAutoDrawable.FBO getGLDrawable( ) { return drawable; } @Override public GLContext getGLContext( ) { return drawable.getContext( ); } @Override public void paint( ) { drawable.display( ); } @Override public GlimpseBounds getTargetBounds( ) { Colorbuffer b = drawable.getColorbuffer( GL.GL_FRONT ); return new GlimpseBounds( new Dimension( b.getWidth( ), b.getHeight( ) ) ); } @Override public GlimpseBounds getTargetBounds( GlimpseTargetStack stack ) { return getTargetBounds( ); } @Override public void destroy( ) { if ( !isDestroyed ) { this.drawable.destroy( ); this.isDestroyed = true; } } @Override public boolean isDestroyed( ) { return this.isDestroyed; } @Override public boolean isVisible( ) { return true; } @Override public int[] getSurfaceScale( ) { return new int[] { 1, 1 }; } }