From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- .../src/java/org/mozilla/gecko/gfx/SubTile.java | 254 +++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 android/source/src/java/org/mozilla/gecko/gfx/SubTile.java (limited to 'android/source/src/java/org/mozilla/gecko/gfx/SubTile.java') 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); + } + } +} -- cgit v1.2.3