1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/gfx/TileLayer.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,177 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.gfx; 1.10 + 1.11 +import android.graphics.Rect; 1.12 +import android.opengl.GLES20; 1.13 +import android.util.Log; 1.14 + 1.15 +import java.nio.ByteBuffer; 1.16 + 1.17 +/** 1.18 + * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL 1.19 + * ES. 1.20 + */ 1.21 +public abstract class TileLayer extends Layer { 1.22 + private static final String LOGTAG = "GeckoTileLayer"; 1.23 + 1.24 + private final Rect mDirtyRect; 1.25 + private IntSize mSize; 1.26 + private int[] mTextureIDs; 1.27 + 1.28 + protected final CairoImage mImage; 1.29 + 1.30 + public enum PaintMode { NORMAL, REPEAT, STRETCH }; 1.31 + private PaintMode mPaintMode; 1.32 + 1.33 + public TileLayer(CairoImage image, PaintMode paintMode) { 1.34 + super(image.getSize()); 1.35 + 1.36 + mPaintMode = paintMode; 1.37 + mImage = image; 1.38 + mSize = new IntSize(0, 0); 1.39 + mDirtyRect = new Rect(); 1.40 + } 1.41 + 1.42 + protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; } 1.43 + protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; } 1.44 + protected int getTextureID() { return mTextureIDs[0]; } 1.45 + protected boolean initialized() { return mImage != null && mTextureIDs != null; } 1.46 + 1.47 + @Override 1.48 + protected void finalize() throws Throwable { 1.49 + try { 1.50 + if (mTextureIDs != null) 1.51 + TextureReaper.get().add(mTextureIDs); 1.52 + } finally { 1.53 + super.finalize(); 1.54 + } 1.55 + } 1.56 + 1.57 + public void destroy() { 1.58 + try { 1.59 + if (mImage != null) { 1.60 + mImage.destroy(); 1.61 + } 1.62 + } catch (Exception ex) { 1.63 + Log.e(LOGTAG, "error clearing buffers: ", ex); 1.64 + } 1.65 + } 1.66 + 1.67 + public void setPaintMode(PaintMode mode) { 1.68 + mPaintMode = mode; 1.69 + } 1.70 + 1.71 + /** 1.72 + * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a 1.73 + * transaction. 1.74 + */ 1.75 + 1.76 + public void invalidate() { 1.77 + if (!inTransaction()) 1.78 + throw new RuntimeException("invalidate() is only valid inside a transaction"); 1.79 + IntSize bufferSize = mImage.getSize(); 1.80 + mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height); 1.81 + } 1.82 + 1.83 + private void validateTexture() { 1.84 + /* Calculate the ideal texture size. This must be a power of two if 1.85 + * the texture is repeated or OpenGL ES 2.0 isn't supported, as 1.86 + * OpenGL ES 2.0 is required for NPOT texture support (without 1.87 + * extensions), but doesn't support repeating NPOT textures. 1.88 + * 1.89 + * XXX Currently, we don't pick a GLES 2.0 context, so always round. 1.90 + */ 1.91 + IntSize textureSize = mImage.getSize().nextPowerOfTwo(); 1.92 + 1.93 + if (!textureSize.equals(mSize)) { 1.94 + mSize = textureSize; 1.95 + 1.96 + // Delete the old texture 1.97 + if (mTextureIDs != null) { 1.98 + TextureReaper.get().add(mTextureIDs); 1.99 + mTextureIDs = null; 1.100 + 1.101 + // Free the texture immediately, so we don't incur a 1.102 + // temporarily increased memory usage. 1.103 + TextureReaper.get().reap(); 1.104 + } 1.105 + } 1.106 + } 1.107 + 1.108 + @Override 1.109 + protected void performUpdates(RenderContext context) { 1.110 + super.performUpdates(context); 1.111 + 1.112 + // Reallocate the texture if the size has changed 1.113 + validateTexture(); 1.114 + 1.115 + // Don't do any work if the image has an invalid size. 1.116 + if (!mImage.getSize().isPositive()) 1.117 + return; 1.118 + 1.119 + // If we haven't allocated a texture, assume the whole region is dirty 1.120 + if (mTextureIDs == null) { 1.121 + uploadFullTexture(); 1.122 + } else { 1.123 + uploadDirtyRect(mDirtyRect); 1.124 + } 1.125 + 1.126 + mDirtyRect.setEmpty(); 1.127 + } 1.128 + 1.129 + private void uploadFullTexture() { 1.130 + IntSize bufferSize = mImage.getSize(); 1.131 + uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height)); 1.132 + } 1.133 + 1.134 + private void uploadDirtyRect(Rect dirtyRect) { 1.135 + // If we have nothing to upload, just return for now 1.136 + if (dirtyRect.isEmpty()) 1.137 + return; 1.138 + 1.139 + // It's possible that the buffer will be null, check for that and return 1.140 + ByteBuffer imageBuffer = mImage.getBuffer(); 1.141 + if (imageBuffer == null) 1.142 + return; 1.143 + 1.144 + if (mTextureIDs == null) { 1.145 + mTextureIDs = new int[1]; 1.146 + GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); 1.147 + } 1.148 + 1.149 + int cairoFormat = mImage.getFormat(); 1.150 + CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); 1.151 + 1.152 + bindAndSetGLParameters(); 1.153 + 1.154 + // XXX TexSubImage2D is too broken to rely on on Adreno, and very slow 1.155 + // on other chipsets, so we always upload the entire buffer. 1.156 + IntSize bufferSize = mImage.getSize(); 1.157 + if (mSize.equals(bufferSize)) { 1.158 + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, 1.159 + mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); 1.160 + } else { 1.161 + // Our texture has been expanded to the next power of two. 1.162 + // XXX We probably never want to take this path, so throw an exception. 1.163 + throw new RuntimeException("Buffer/image size mismatch in TileLayer!"); 1.164 + } 1.165 + } 1.166 + 1.167 + private void bindAndSetGLParameters() { 1.168 + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 1.169 + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); 1.170 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 1.171 + GLES20.GL_LINEAR); 1.172 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 1.173 + GLES20.GL_LINEAR); 1.174 + 1.175 + int repeatMode = repeats() ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE; 1.176 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode); 1.177 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode); 1.178 + } 1.179 +} 1.180 +