gecko/mobile/android/base/gfx/SurfaceTextureLayer.java

275 lines
9.5 KiB
Java

/* -*- 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 <jwillcox@mozilla.com>
*
* 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;
}
}