/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Android code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2009-2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * James Willcox * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoApp; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.opengl.GLES11; import android.opengl.GLES11Ext; import android.opengl.Matrix; import android.util.Log; import android.view.Surface; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11Ext; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import android.hardware.Camera; public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { private static final String LOGTAG = "SurfaceTextureLayer"; private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) private final SurfaceTexture mSurfaceTexture; private final Surface mSurface; private int mTextureId; private boolean mHaveFrame; private IntSize mSize; private IntSize mNewSize; private boolean mInverted; private boolean mNewInverted; private boolean mBlend; private boolean mNewBlend; private FloatBuffer textureBuffer; private FloatBuffer textureBufferInverted; public SurfaceTextureLayer(int textureId) { mTextureId = textureId; mHaveFrame = true; mInverted = false; mSurfaceTexture = new SurfaceTexture(mTextureId); mSurfaceTexture.setOnFrameAvailableListener(this); Surface tmp = null; try { tmp = Surface.class.getConstructor(SurfaceTexture.class).newInstance(mSurfaceTexture); } catch (Exception ie) { Log.e(LOGTAG, "error constructing the surface", ie); } mSurface = tmp; float textureMap[] = { 0.0f, 1.0f, // top left 0.0f, 0.0f, // bottom left 1.0f, 1.0f, // top right 1.0f, 0.0f, // bottom right }; textureBuffer = createBuffer(textureMap); float textureMapInverted[] = { 0.0f, 0.0f, // bottom left 0.0f, 1.0f, // top left 1.0f, 0.0f, // bottom right 1.0f, 1.0f, // top right }; textureBufferInverted = createBuffer(textureMapInverted); } public static SurfaceTextureLayer create() { int textureId = TextureGenerator.get().take(); if (textureId == 0) return null; return new SurfaceTextureLayer(textureId); } // For SurfaceTexture.OnFrameAvailableListener public void onFrameAvailable(SurfaceTexture texture) { // FIXME: for some reason this doesn't get called mHaveFrame = true; GeckoApp.mAppContext.requestRender(); } private FloatBuffer createBuffer(float[] input) { // a float has 4 bytes so we allocate for each coordinate 4 bytes ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); floatBuffer.put(input); floatBuffer.position(0); return floatBuffer; } public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) { beginTransaction(null); setOrigin(origin); setResolution(resolution); mNewSize = size; mNewInverted = inverted; mNewBlend = blend; endTransaction(); } @Override public IntSize getSize() { return mSize; } @Override protected void finalize() throws Throwable { if (mSurfaceTexture != null) { try { SurfaceTexture.class.getDeclaredMethod("release").invoke(mSurfaceTexture); } catch (NoSuchMethodException nsme) { Log.e(LOGTAG, "error finding release method on mSurfaceTexture", nsme); } catch (IllegalAccessException iae) { Log.e(LOGTAG, "error invoking release method on mSurfaceTexture", iae); } catch (Exception e) { Log.e(LOGTAG, "some other exception while invoking release method on mSurfaceTexture", e); } } if (mTextureId > 0) TextureReaper.get().add(mTextureId); } @Override protected boolean performUpdates(GL10 gl, RenderContext context) { super.performUpdates(gl, context); if (mNewSize != null) { mSize = mNewSize; mNewSize = null; } mInverted = mNewInverted; mBlend = mNewBlend; gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); mSurfaceTexture.updateTexImage(); gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); // FIXME: we should return true and rely on onFrameAvailable, but // that isn't working for some reason return false; } private float mapToGLCoords(float input, float viewport, boolean flip) { if (flip) input = viewport - input; return ((input / viewport) * 2.0f) - 1.0f; } @Override public void draw(RenderContext context) { // Enable GL_TEXTURE_EXTERNAL_OES and bind our texture GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); // Enable vertex and texture coordinate buffers GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY); GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // Load whatever texture transform the SurfaceMatrix needs float[] matrix = new float[16]; mSurfaceTexture.getTransformMatrix(matrix); GLES11.glMatrixMode(GLES11.GL_TEXTURE); GLES11.glLoadMatrixf(matrix, 0); // Figure out vertices to put the texture in the right spot on the screen IntSize size = getSize(); RectF bounds = getBounds(context, new FloatSize(size)); RectF viewport = context.viewport; bounds.offset(-viewport.left, -viewport.top); float vertices[] = new float[8]; // Bottom left vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false); vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true); // Top left vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false); vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true); // Bottom right vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false); vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true); // Top right vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false); vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true); // Set texture and vertex buffers GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices)); GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer); if (mBlend) { GLES11.glEnable(GL10.GL_BLEND); GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); } // Draw the vertices as triangle strip GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2); // Clean up GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY); GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); GLES11.glLoadIdentity(); if (mBlend) { GLES11.glDisable(GL10.GL_BLEND); } } public SurfaceTexture getSurfaceTexture() { return mSurfaceTexture; } public Surface getSurface() { return mSurface; } }