2012-01-31 06:40:58 -08:00
|
|
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
2012-05-21 04:12:37 -07:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2012-01-31 06:40:58 -08:00
|
|
|
|
|
|
|
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;
|
2012-03-12 10:03:54 -07:00
|
|
|
import android.opengl.GLES20;
|
2012-01-31 06:40:58 -08:00
|
|
|
import android.util.Log;
|
|
|
|
import android.view.Surface;
|
|
|
|
import java.nio.Buffer;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.ByteOrder;
|
|
|
|
import java.nio.FloatBuffer;
|
|
|
|
|
|
|
|
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;
|
2012-03-12 10:03:54 -07:00
|
|
|
private float[] mTextureTransform = new float[16];
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-04-27 13:04:47 -07:00
|
|
|
private Rect mPageRect;
|
|
|
|
|
2012-01-31 06:40:58 -08:00
|
|
|
private boolean mInverted;
|
|
|
|
private boolean mNewInverted;
|
|
|
|
private boolean mBlend;
|
|
|
|
private boolean mNewBlend;
|
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
private static int mProgram;
|
|
|
|
private static int mPositionHandle;
|
|
|
|
private static int mTextureHandle;
|
|
|
|
private static int mSampleHandle;
|
|
|
|
private static int mProjectionMatrixHandle;
|
|
|
|
private static int mTextureMatrixHandle;
|
|
|
|
|
|
|
|
private static final float[] PROJECTION_MATRIX = {
|
|
|
|
2.0f, 0.0f, 0.0f, 0.0f,
|
|
|
|
0.0f, 2.0f, 0.0f, 0.0f,
|
|
|
|
0.0f, 0.0f, 2.0f, 0.0f,
|
|
|
|
-1.0f, -1.0f, 0.0f, 1.0f
|
|
|
|
};
|
|
|
|
|
|
|
|
private static final String VERTEX_SHADER =
|
|
|
|
"uniform mat4 projectionMatrix;\n" +
|
|
|
|
"uniform mat4 textureMatrix;\n" +
|
|
|
|
"attribute vec4 vPosition;\n" +
|
|
|
|
"attribute vec4 aTexCoord;\n" +
|
|
|
|
"varying vec2 vTexCoord;\n" +
|
|
|
|
"void main() {\n" +
|
|
|
|
" gl_Position = projectionMatrix * vPosition;\n" +
|
|
|
|
" vTexCoord = (textureMatrix * vec4(aTexCoord.x, aTexCoord.y, 0.0, 1.0)).xy;\n" +
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
private static String FRAGMENT_SHADER_OES =
|
|
|
|
"#extension GL_OES_EGL_image_external : require\n" +
|
|
|
|
"precision mediump float;\n" +
|
|
|
|
"varying vec2 vTexCoord; \n" +
|
|
|
|
"uniform samplerExternalOES sTexture; \n" +
|
|
|
|
"void main() {\n" +
|
|
|
|
" gl_FragColor = texture2D(sTexture, vTexCoord); \n" +
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
|
|
|
|
private static final float TEXTURE_MAP[] = {
|
|
|
|
0.0f, 1.0f, // top left
|
|
|
|
0.0f, 0.0f, // bottom left
|
|
|
|
1.0f, 1.0f, // top right
|
|
|
|
1.0f, 0.0f, // bottom right
|
|
|
|
};
|
|
|
|
|
|
|
|
private static final float TEXTURE_MAP_INVERTED[] = {
|
|
|
|
0.0f, 0.0f, // bottom left
|
|
|
|
0.0f, 1.0f, // top left
|
|
|
|
1.0f, 0.0f, // bottom right
|
|
|
|
1.0f, 1.0f, // top right
|
|
|
|
};
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
private SurfaceTextureLayer(int textureId) {
|
2012-01-31 06:40:58 -08:00
|
|
|
mTextureId = textureId;
|
|
|
|
mHaveFrame = true;
|
|
|
|
mInverted = false;
|
|
|
|
|
2012-04-27 13:04:47 -07:00
|
|
|
// We have our own special shaders necessary for rendering the SurfaceTexture
|
|
|
|
this.mUsesDefaultProgram = false;
|
|
|
|
|
2012-01-31 06:40:58 -08:00
|
|
|
mSurfaceTexture = new SurfaceTexture(mTextureId);
|
|
|
|
mSurfaceTexture.setOnFrameAvailableListener(this);
|
|
|
|
|
2012-01-31 22:29:09 -08:00
|
|
|
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;
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
mHaveFrame = true;
|
|
|
|
GeckoApp.mAppContext.requestRender();
|
|
|
|
}
|
|
|
|
|
2012-04-27 13:04:47 -07:00
|
|
|
public void update(Rect rect, boolean inverted, boolean blend) {
|
|
|
|
beginTransaction();
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-04-27 13:04:47 -07:00
|
|
|
setPosition(rect);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
|
|
|
mNewInverted = inverted;
|
|
|
|
mNewBlend = blend;
|
|
|
|
|
|
|
|
endTransaction();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void finalize() throws Throwable {
|
2012-05-10 06:46:53 -07:00
|
|
|
try {
|
|
|
|
if (mTextureId > 0)
|
|
|
|
TextureReaper.get().add(mTextureId);
|
|
|
|
} finally {
|
|
|
|
super.finalize();
|
2012-01-31 22:29:09 -08:00
|
|
|
}
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-04-05 08:32:16 -07:00
|
|
|
protected void performUpdates(RenderContext context) {
|
2012-02-08 21:13:08 -08:00
|
|
|
super.performUpdates(context);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
|
|
|
mInverted = mNewInverted;
|
|
|
|
mBlend = mNewBlend;
|
2012-03-12 10:03:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean ensureProgram() {
|
|
|
|
if (mProgram != 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
|
|
|
|
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_OES);
|
|
|
|
|
|
|
|
mProgram = GLES20.glCreateProgram();
|
|
|
|
GLES20.glAttachShader(mProgram, vertexShader);
|
|
|
|
GLES20.glAttachShader(mProgram, fragmentShader);
|
|
|
|
GLES20.glLinkProgram(mProgram);
|
|
|
|
|
|
|
|
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
|
|
|
|
mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
|
|
|
|
mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
|
|
|
|
mProjectionMatrixHandle = GLES20.glGetUniformLocation(mProgram, "projectionMatrix");
|
|
|
|
mTextureMatrixHandle = GLES20.glGetUniformLocation(mProgram, "textureMatrix");
|
|
|
|
|
|
|
|
return mProgram != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int loadShader(int type, String shaderCode) {
|
|
|
|
int shader = GLES20.glCreateShader(type);
|
|
|
|
GLES20.glShaderSource(shader, shaderCode);
|
|
|
|
GLES20.glCompileShader(shader);
|
|
|
|
return shader;
|
|
|
|
}
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
private static void activateProgram() {
|
|
|
|
GLES20.glUseProgram(mProgram);
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
public static void deactivateProgram() {
|
|
|
|
GLES20.glDisableVertexAttribArray(mTextureHandle);
|
|
|
|
GLES20.glDisableVertexAttribArray(mPositionHandle);
|
|
|
|
GLES20.glUseProgram(0);
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void draw(RenderContext context) {
|
2012-03-12 10:03:54 -07:00
|
|
|
if (!ensureProgram() || !mHaveFrame)
|
|
|
|
return;
|
2012-03-12 10:03:54 -07:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
RectF rect = getBounds(context);
|
|
|
|
RectF viewport = context.viewport;
|
|
|
|
rect.offset(-viewport.left, -viewport.top);
|
2012-03-12 10:03:54 -07:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
float viewWidth = viewport.width();
|
|
|
|
float viewHeight = viewport.height();
|
2012-03-12 10:03:54 -07:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
float top = viewHeight - rect.top;
|
|
|
|
float bot = viewHeight - rect.bottom;
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
float[] textureCoords = mInverted ? TEXTURE_MAP_INVERTED : TEXTURE_MAP;
|
|
|
|
|
|
|
|
float[] coords = {
|
|
|
|
// x, y, z, texture_x, texture_y
|
|
|
|
rect.left/viewWidth, bot/viewHeight, 0,
|
|
|
|
textureCoords[0], textureCoords[1],
|
|
|
|
|
|
|
|
rect.left/viewWidth, (bot+rect.height())/viewHeight, 0,
|
|
|
|
textureCoords[2], textureCoords[3],
|
|
|
|
|
|
|
|
(rect.left+rect.width())/viewWidth, bot/viewHeight, 0,
|
|
|
|
textureCoords[4], textureCoords[5],
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
(rect.left+rect.width())/viewWidth, (bot+rect.height())/viewHeight, 0,
|
|
|
|
textureCoords[6], textureCoords[7]
|
|
|
|
};
|
|
|
|
|
|
|
|
FloatBuffer coordBuffer = context.coordBuffer;
|
|
|
|
coordBuffer.position(0);
|
|
|
|
coordBuffer.put(coords);
|
|
|
|
|
|
|
|
activateProgram();
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
// Set the transformation matrix
|
|
|
|
GLES20.glUniformMatrix4fv(mProjectionMatrixHandle, 1, false, PROJECTION_MATRIX, 0);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
// Enable the arrays from which we get the vertex and texture coordinates
|
|
|
|
GLES20.glEnableVertexAttribArray(mPositionHandle);
|
|
|
|
GLES20.glEnableVertexAttribArray(mTextureHandle);
|
2012-03-12 10:03:54 -07:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
|
|
|
GLES20.glUniform1i(mSampleHandle, 0);
|
2012-04-27 13:04:47 -07:00
|
|
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
2012-03-12 10:03:54 -07:00
|
|
|
GLES20.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
|
|
|
|
|
|
|
|
mSurfaceTexture.updateTexImage();
|
|
|
|
mSurfaceTexture.getTransformMatrix(mTextureTransform);
|
|
|
|
|
|
|
|
GLES20.glUniformMatrix4fv(mTextureMatrixHandle, 1, false, mTextureTransform, 0);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-04-27 18:08:30 -07:00
|
|
|
// Unbind any the current array buffer so we can use client side buffers
|
|
|
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
|
|
|
coordBuffer.position(0);
|
|
|
|
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
|
|
|
coordBuffer);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
|
|
|
|
coordBuffer.position(3);
|
2012-04-27 13:04:47 -07:00
|
|
|
GLES20.glVertexAttribPointer(mTextureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
2012-03-12 10:03:54 -07:00
|
|
|
coordBuffer);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
|
|
|
if (mBlend) {
|
2012-03-12 10:03:54 -07:00
|
|
|
GLES20.glEnable(GLES20.GL_BLEND);
|
|
|
|
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
2012-03-12 10:03:54 -07:00
|
|
|
|
|
|
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
2012-01-31 06:40:58 -08:00
|
|
|
|
2012-03-12 10:03:54 -07:00
|
|
|
if (mBlend)
|
|
|
|
GLES20.glDisable(GLES20.GL_BLEND);
|
|
|
|
|
|
|
|
deactivateProgram();
|
2012-01-31 06:40:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public SurfaceTexture getSurfaceTexture() {
|
|
|
|
return mSurfaceTexture;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Surface getSurface() {
|
|
|
|
return mSurface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|