Avoid potential negative array index access to cached text.
[LibreOffice.git] / android / source / src / java / org / mozilla / gecko / gfx / SubTile.java
blobbdad37195d903a69b79d4a1bede6042e0158d484
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko.gfx;
8 import android.graphics.Rect;
9 import android.graphics.RectF;
10 import android.graphics.Region;
11 import android.graphics.RegionIterator;
12 import android.opengl.GLES20;
13 import android.util.Log;
15 import org.libreoffice.TileIdentifier;
17 import java.nio.ByteBuffer;
18 import java.nio.FloatBuffer;
20 public class SubTile extends Layer {
21 private static String LOGTAG = SubTile.class.getSimpleName();
22 public final TileIdentifier id;
24 private final RectF mBounds;
25 private final RectF mTextureBounds;
26 private final RectF mViewport;
27 private final Rect mIntBounds;
28 private final Rect mSubRect;
29 private final RectF mSubRectF;
30 private final Region mMaskedBounds;
31 private final Rect mCropRect;
32 private final RectF mObjRectF;
33 private final float[] mCoords;
35 public boolean markedForRemoval = false;
37 private CairoImage mImage;
38 private IntSize mSize;
39 private int[] mTextureIDs;
40 private boolean mDirtyTile;
42 public SubTile(TileIdentifier id) {
43 super();
44 this.id = id;
46 mBounds = new RectF();
47 mTextureBounds = new RectF();
48 mViewport = new RectF();
49 mIntBounds = new Rect();
50 mSubRect = new Rect();
51 mSubRectF = new RectF();
52 mMaskedBounds = new Region();
53 mCropRect = new Rect();
54 mObjRectF = new RectF();
55 mCoords = new float[20];
57 mImage = null;
58 mTextureIDs = null;
59 mSize = new IntSize(0, 0);
60 mDirtyTile = false;
63 public void setImage(CairoImage image) {
64 if (image.getSize().isPositive()) {
65 this.mImage = image;
69 public void refreshTileMetrics() {
70 setPosition(id.getCSSRect());
73 public void markForRemoval() {
74 markedForRemoval = true;
77 protected int getTextureID() {
78 return mTextureIDs[0];
81 protected boolean initialized() {
82 return mTextureIDs != null;
85 @Override
86 protected void finalize() throws Throwable {
87 try {
88 destroyImage();
89 cleanTexture();
90 } finally {
91 super.finalize();
95 private void cleanTexture() {
96 if (mTextureIDs != null) {
97 TextureReaper.get().add(mTextureIDs);
98 mTextureIDs = null;
99 TextureReaper.get().reap();
103 public void destroy() {
104 try {
105 destroyImage();
106 cleanTexture();
107 } catch (Exception ex) {
108 Log.e(LOGTAG, "Error clearing buffers: ", ex);
112 public void destroyImage() {
113 if (mImage != null) {
114 mImage.destroy();
115 mImage = null;
120 * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a
121 * transaction.
123 public void invalidate() {
124 if (!inTransaction()) {
125 throw new RuntimeException("invalidate() is only valid inside a transaction");
127 if (mImage == null) {
128 return;
130 mDirtyTile = true;
134 * Remove the texture if the image is of different size than the current uploaded texture.
136 private void validateTexture() {
137 IntSize textureSize = mImage.getSize().nextPowerOfTwo();
139 if (!textureSize.equals(mSize)) {
140 mSize = textureSize;
141 cleanTexture();
145 @Override
146 protected void performUpdates(RenderContext context) {
147 super.performUpdates(context);
148 if (mImage == null && !mDirtyTile) {
149 return;
151 validateTexture();
152 uploadNewTexture();
153 mDirtyTile = false;
156 private void uploadNewTexture() {
157 ByteBuffer imageBuffer = mImage.getBuffer();
158 if (imageBuffer == null) {
159 return;
162 if (mTextureIDs == null) {
163 mTextureIDs = new int[1];
164 GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
167 int cairoFormat = mImage.getFormat();
168 CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
170 bindAndSetGLParameters();
172 IntSize bufferSize = mImage.getSize();
174 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat,
175 mSize.width, mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
177 destroyImage();
180 private void bindAndSetGLParameters() {
181 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
182 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
184 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
185 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
186 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
187 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
190 @Override
191 public void draw(RenderContext context) {
192 // mTextureIDs may be null here during startup if Layer.java's draw method
193 // failed to acquire the transaction lock and call performUpdates.
194 if (!initialized())
195 return;
197 mViewport.set(context.viewport);
199 mBounds.set(getBounds(context));
200 mTextureBounds.set(mBounds);
202 mBounds.roundOut(mIntBounds);
203 mMaskedBounds.set(mIntBounds);
205 // XXX Possible optimisation here, form this array so we can draw it in
206 // a single call.
207 RegionIterator iterator = new RegionIterator(mMaskedBounds);
208 while (iterator.next(mSubRect)) {
209 // Compensate for rounding errors at the edge of the tile caused by
210 // the roundOut above
211 mSubRectF.set(Math.max(mBounds.left, (float) mSubRect.left),
212 Math.max(mBounds.top, (float) mSubRect.top),
213 Math.min(mBounds.right, (float) mSubRect.right),
214 Math.min(mBounds.bottom, (float) mSubRect.bottom));
216 // This is the left/top/right/bottom of the rect, relative to the
217 // bottom-left of the layer, to use for texture coordinates.
218 mCropRect.set(Math.round(mSubRectF.left - mBounds.left),
219 Math.round(mBounds.bottom - mSubRectF.top),
220 Math.round(mSubRectF.right - mBounds.left),
221 Math.round(mBounds.bottom - mSubRectF.bottom));
223 mObjRectF.set(mSubRectF.left - mViewport.left,
224 mViewport.bottom - mSubRectF.bottom,
225 mSubRectF.right - mViewport.left,
226 mViewport.bottom - mSubRectF.top);
228 fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), mCropRect, mTextureBounds.width(), mTextureBounds.height());
230 FloatBuffer coordBuffer = context.coordBuffer;
231 int positionHandle = context.positionHandle;
232 int textureHandle = context.textureHandle;
234 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
235 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
237 // Make sure we are at position zero in the buffer
238 coordBuffer.position(0);
239 coordBuffer.put(mCoords);
241 // Unbind any the current array buffer so we can use client side buffers
242 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
244 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
245 coordBuffer.position(0);
246 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
248 // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
249 coordBuffer.position(3);
250 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
251 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);