package com.aviary.android.feather.opengl; import java.lang.ref.WeakReference; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.opengl.GLSurfaceView; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import com.aviary.android.feather.library.log.LoggerFactory; import com.aviary.android.feather.library.log.LoggerFactory.Logger; import com.aviary.android.feather.library.log.LoggerFactory.LoggerType; import com.aviary.android.feather.library.threading.Future; import com.aviary.android.feather.library.threading.FutureListener; import com.aviary.android.feather.library.threading.ThreadMediaPool; import com.aviary.android.feather.library.threading.ThreadMediaPool.Job; import com.aviary.android.feather.library.threading.ThreadMediaPool.JobContext; import com.aviary.android.feather.library.threading.ThreadMediaPool.Worker; public class AviaryGLSurfaceView extends GLSurfaceView { private static final Logger logger = LoggerFactory.getLogger( "gl-surface", LoggerType.ConsoleLoggerType ); private static final Handler mUIHandler = new Handler(); public AviaryGLSurfaceView( Context context ) { super( context ); mPtr = init( context, null ); } public AviaryGLSurfaceView( Context context, AttributeSet attrs ) { super( context, attrs ); mPtr = init( context, attrs ); } private long init( Context context, AttributeSet attrs ) { this.setEGLContextClientVersion( 2 ); this.setEGLConfigChooser( 8, 8, 8, 8, 0, 0 ); this.setRenderer( new AviaryGLRenderer() ); this.setRenderMode( RENDERMODE_WHEN_DIRTY ); return nativeCreate(); } @Override protected void onDetachedFromWindow() { logger.debug( "onDetachedfromWindow" ); nativeDispose(); super.onDetachedFromWindow(); } private void initializeOpenGL() { InitializeOpenGLJob job = new InitializeOpenGLJob(); FutureListener<Void> listener = new FutureListener<Void>() { @Override public void onFutureDone( Future<Void> arg0 ) { fireOnSurfaceCreated(); } }; submit( job, listener ); } private void setRenderbufferSize( final boolean changed, final int width, final int height ) { SetRenderbufferSizeJob job = new SetRenderbufferSizeJob(); FutureListener<Void> listener = new FutureListener<Void>() { @Override public void onFutureDone( Future<Void> arg0 ) { fireOnSurfaceChanged( changed, width, height ); } }; submit( job, listener, width, height ); } public Future<Boolean> executeEffect( String effectName, Bitmap input, boolean use_gpu, FutureListener<Boolean> listener ) { RenderJob job = new RenderJob( input ); return submit( job, listener, effectName ); } public Future<Boolean> writeBitmap( Bitmap output, FutureListener<Boolean> listener ) { WriteBitmapJob job = new WriteBitmapJob( output ); return submit( job, listener ); } /** * Returns the native pointer to the MoaGLSurfaceView * * @return */ private long nativeCreate() { logger.log( "nativeCreate" ); synchronized ( mNativeLock ) { return n_create(); } } /** * Free the native instance */ private void nativeDispose() { logger.log( "nativeDispose" ); synchronized ( mNativeLock ) { n_dispose( mPtr ); } } /** * Initialize the GL data in the native instance * * @return */ private boolean nativeInitialize() { logger.log( "nativeInitialize" ); synchronized ( mNativeLock ) { return n_initialize( mPtr ); } } /** * Change the render buffer size in the native instance * * @param width * @param height * @return */ private boolean nativeSetRenderbufferSize( int width, int height ) { logger.log( "nativeSetRenderBufferSize" ); synchronized ( mNativeLock ) { return n_setRenderbufferSize( mPtr, width, height ); } } private boolean nativeRender( Bitmap input, String effect ) { logger.log( "nativeRender" ); synchronized ( mNativeLock ) { return n_render( mPtr, input, effect ); } } private boolean nativeWriteBitmap( Bitmap output ) { logger.log( "nativeWriteBitmap" ); synchronized ( mNativeLock ) { return n_writeCurrentBitmap( mPtr, output ); } } public <I,O> Future<O> submit( final Job<I,O> job, FutureListener<O> listener, final I...params ) { Worker<I,O> w = new Worker<I,O>( getContext(), job, listener, params ){ @Override public void run() { O result = null; if ( setMode( ThreadMediaPool.MODE_CPU ) ) { try { result = job.run( this, params ); } catch ( Throwable ex ) { Log.e( Worker.TAG, "Exception in running a job", ex ); } } synchronized ( this ) { setMode( ThreadMediaPool.MODE_NONE ); setResult( result ); setIsDone(); notifyAll(); } fireOnDoneEvent(); } }; queueEvent( w ); return w; } private final Object mNativeLock = new Object(); private final long mPtr; private static native long n_create(); private static native boolean n_dispose( long ptr ); private static native boolean n_initialize( long ptr ); private static native boolean n_setRenderbufferSize( long ptr, int width, int heights ); private static native boolean n_render( long ptr, Bitmap input, String effect ); private static native boolean n_writeCurrentBitmap( long ptr, Bitmap output ); class RenderJob implements Job<String,Boolean> { WeakReference<Bitmap> mBitmap; public RenderJob( Bitmap bitmap ) { mBitmap = new WeakReference<Bitmap>( bitmap ); } @Override public Boolean run( JobContext jc, String... params ) { logger.log( "RenderJob::run" ); if ( null != mBitmap && null != mBitmap.get() ) { return nativeRender( mBitmap.get(), params[0] ); } return false; } } class WriteBitmapJob implements Job<Void, Boolean> { WeakReference<Bitmap> mBitmap; public WriteBitmapJob( Bitmap bitmap ) { mBitmap = new WeakReference<Bitmap>( bitmap ); } @Override public Boolean run( JobContext context, Void... params ) { if( null != mBitmap && null != mBitmap.get() ) { return nativeWriteBitmap( mBitmap.get() ); } return false; } } class InitializeOpenGLJob implements Job<Void, Void> { @Override public Void run( JobContext jc, Void... input ) { logger.log( "InitializeOpenGlJob::run" ); nativeInitialize(); logger.log( "end::nativeInitialize" ); return null; } } class SetRenderbufferSizeJob implements Job<Integer, Void> { public SetRenderbufferSizeJob() { } @Override public Void run( JobContext jc, Integer... params ) { logger.log( "SetRenderbufferSizeJob::run" ); nativeSetRenderbufferSize( params[0], params[1] ); return null; } } private class AviaryGLRenderer implements GLSurfaceView.Renderer { private int mWidth, mHeight; @Override public void onDrawFrame( GL10 gl ) { logger.log( "onDrawFrame" ); } @Override public void onSurfaceChanged( GL10 gl, int width, int height ) { logger.log( "onSurfaceChanged. " + width + "x" + height ); final boolean changed = mWidth != width || mHeight != height; mWidth = width; mHeight = height; setRenderbufferSize( changed, width, height ); } @Override public void onSurfaceCreated( GL10 gl, EGLConfig config ) { Log.d( "GL", "GL_RENDERER = " + gl.glGetString( GL10.GL_RENDERER ) ); Log.d( "GL", "GL_VENDOR = " + gl.glGetString( GL10.GL_VENDOR ) ); Log.d( "GL", "GL_VERSION = " + gl.glGetString( GL10.GL_VERSION ) ); Log.i( "GL", "GL_EXTENSIONS = " + gl.glGetString( GL10.GL_EXTENSIONS ) ); logger.log( "onSurfaceCreated" ); initializeOpenGL(); } } private GLRendererListener mGlRendererListener; public void setOnGlRendererListener( GLRendererListener listener ) { mGlRendererListener = listener; } private void fireOnSurfaceCreated() { if ( mGlRendererListener != null ) { mGlRendererListener.OnSurfaceCreated(); } } private void fireOnSurfaceChanged( final boolean changed, final int width, final int height ) { if ( mGlRendererListener != null ) { getHandler().post( new Runnable() { @Override public void run() { mGlRendererListener.OnSurfaceChanged( changed, width, height ); } } ); } } public static interface GLRendererListener { public void OnSurfaceCreated(); public void OnSurfaceChanged( boolean changed, int width, int height ); } }