summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/source/src/java/org/mozilla/gecko/gfx/SubTile.java')
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/SubTile.java254
1 files changed, 254 insertions, 0 deletions
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java b/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
new file mode 100644
index 000000000..bdad37195
--- /dev/null
+++ b/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
@@ -0,0 +1,254 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko.gfx;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import org.libreoffice.TileIdentifier;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+
+public class SubTile extends Layer {
+ private static String LOGTAG = SubTile.class.getSimpleName();
+ public final TileIdentifier id;
+
+ private final RectF mBounds;
+ private final RectF mTextureBounds;
+ private final RectF mViewport;
+ private final Rect mIntBounds;
+ private final Rect mSubRect;
+ private final RectF mSubRectF;
+ private final Region mMaskedBounds;
+ private final Rect mCropRect;
+ private final RectF mObjRectF;
+ private final float[] mCoords;
+
+ public boolean markedForRemoval = false;
+
+ private CairoImage mImage;
+ private IntSize mSize;
+ private int[] mTextureIDs;
+ private boolean mDirtyTile;
+
+ public SubTile(TileIdentifier id) {
+ super();
+ this.id = id;
+
+ mBounds = new RectF();
+ mTextureBounds = new RectF();
+ mViewport = new RectF();
+ mIntBounds = new Rect();
+ mSubRect = new Rect();
+ mSubRectF = new RectF();
+ mMaskedBounds = new Region();
+ mCropRect = new Rect();
+ mObjRectF = new RectF();
+ mCoords = new float[20];
+
+ mImage = null;
+ mTextureIDs = null;
+ mSize = new IntSize(0, 0);
+ mDirtyTile = false;
+ }
+
+ public void setImage(CairoImage image) {
+ if (image.getSize().isPositive()) {
+ this.mImage = image;
+ }
+ }
+
+ public void refreshTileMetrics() {
+ setPosition(id.getCSSRect());
+ }
+
+ public void markForRemoval() {
+ markedForRemoval = true;
+ }
+
+ protected int getTextureID() {
+ return mTextureIDs[0];
+ }
+
+ protected boolean initialized() {
+ return mTextureIDs != null;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ destroyImage();
+ cleanTexture();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void cleanTexture() {
+ if (mTextureIDs != null) {
+ TextureReaper.get().add(mTextureIDs);
+ mTextureIDs = null;
+ TextureReaper.get().reap();
+ }
+ }
+
+ public void destroy() {
+ try {
+ destroyImage();
+ cleanTexture();
+ } catch (Exception ex) {
+ Log.e(LOGTAG, "Error clearing buffers: ", ex);
+ }
+ }
+
+ public void destroyImage() {
+ if (mImage != null) {
+ mImage.destroy();
+ mImage = null;
+ }
+ }
+
+ /**
+ * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a
+ * transaction.
+ */
+ public void invalidate() {
+ if (!inTransaction()) {
+ throw new RuntimeException("invalidate() is only valid inside a transaction");
+ }
+ if (mImage == null) {
+ return;
+ }
+ mDirtyTile = true;
+ }
+
+ /**
+ * Remove the texture if the image is of different size than the current uploaded texture.
+ */
+ private void validateTexture() {
+ IntSize textureSize = mImage.getSize().nextPowerOfTwo();
+
+ if (!textureSize.equals(mSize)) {
+ mSize = textureSize;
+ cleanTexture();
+ }
+ }
+
+ @Override
+ protected void performUpdates(RenderContext context) {
+ super.performUpdates(context);
+ if (mImage == null && !mDirtyTile) {
+ return;
+ }
+ validateTexture();
+ uploadNewTexture();
+ mDirtyTile = false;
+ }
+
+ private void uploadNewTexture() {
+ ByteBuffer imageBuffer = mImage.getBuffer();
+ if (imageBuffer == null) {
+ return;
+ }
+
+ if (mTextureIDs == null) {
+ mTextureIDs = new int[1];
+ GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
+ }
+
+ int cairoFormat = mImage.getFormat();
+ CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
+
+ bindAndSetGLParameters();
+
+ IntSize bufferSize = mImage.getSize();
+
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat,
+ mSize.width, mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
+
+ destroyImage();
+ }
+
+ private void bindAndSetGLParameters() {
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
+
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ }
+
+ @Override
+ public void draw(RenderContext context) {
+ // mTextureIDs may be null here during startup if Layer.java's draw method
+ // failed to acquire the transaction lock and call performUpdates.
+ if (!initialized())
+ return;
+
+ mViewport.set(context.viewport);
+
+ mBounds.set(getBounds(context));
+ mTextureBounds.set(mBounds);
+
+ mBounds.roundOut(mIntBounds);
+ mMaskedBounds.set(mIntBounds);
+
+ // XXX Possible optimisation here, form this array so we can draw it in
+ // a single call.
+ RegionIterator iterator = new RegionIterator(mMaskedBounds);
+ while (iterator.next(mSubRect)) {
+ // Compensate for rounding errors at the edge of the tile caused by
+ // the roundOut above
+ mSubRectF.set(Math.max(mBounds.left, (float) mSubRect.left),
+ Math.max(mBounds.top, (float) mSubRect.top),
+ Math.min(mBounds.right, (float) mSubRect.right),
+ Math.min(mBounds.bottom, (float) mSubRect.bottom));
+
+ // This is the left/top/right/bottom of the rect, relative to the
+ // bottom-left of the layer, to use for texture coordinates.
+ mCropRect.set(Math.round(mSubRectF.left - mBounds.left),
+ Math.round(mBounds.bottom - mSubRectF.top),
+ Math.round(mSubRectF.right - mBounds.left),
+ Math.round(mBounds.bottom - mSubRectF.bottom));
+
+ mObjRectF.set(mSubRectF.left - mViewport.left,
+ mViewport.bottom - mSubRectF.bottom,
+ mSubRectF.right - mViewport.left,
+ mViewport.bottom - mSubRectF.top);
+
+ fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), mCropRect, mTextureBounds.width(), mTextureBounds.height());
+
+ FloatBuffer coordBuffer = context.coordBuffer;
+ int positionHandle = context.positionHandle;
+ int textureHandle = context.textureHandle;
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
+
+ // Make sure we are at position zero in the buffer
+ coordBuffer.position(0);
+ coordBuffer.put(mCoords);
+
+ // Unbind any the current array buffer so we can use client side buffers
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
+
+ // Vertex coordinates are x,y,z starting at position 0 into the buffer.
+ coordBuffer.position(0);
+ GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
+
+ // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
+ coordBuffer.position(3);
+ GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ }
+ }
+}