mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
275 lines
9.5 KiB
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;
|
|
}
|
|
}
|
|
|