|
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/. */ |
|
5 |
|
6 package org.mozilla.gecko.gfx; |
|
7 |
|
8 import android.graphics.Rect; |
|
9 import android.opengl.GLES20; |
|
10 import android.util.Log; |
|
11 |
|
12 import java.nio.ByteBuffer; |
|
13 |
|
14 /** |
|
15 * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL |
|
16 * ES. |
|
17 */ |
|
18 public abstract class TileLayer extends Layer { |
|
19 private static final String LOGTAG = "GeckoTileLayer"; |
|
20 |
|
21 private final Rect mDirtyRect; |
|
22 private IntSize mSize; |
|
23 private int[] mTextureIDs; |
|
24 |
|
25 protected final CairoImage mImage; |
|
26 |
|
27 public enum PaintMode { NORMAL, REPEAT, STRETCH }; |
|
28 private PaintMode mPaintMode; |
|
29 |
|
30 public TileLayer(CairoImage image, PaintMode paintMode) { |
|
31 super(image.getSize()); |
|
32 |
|
33 mPaintMode = paintMode; |
|
34 mImage = image; |
|
35 mSize = new IntSize(0, 0); |
|
36 mDirtyRect = new Rect(); |
|
37 } |
|
38 |
|
39 protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; } |
|
40 protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; } |
|
41 protected int getTextureID() { return mTextureIDs[0]; } |
|
42 protected boolean initialized() { return mImage != null && mTextureIDs != null; } |
|
43 |
|
44 @Override |
|
45 protected void finalize() throws Throwable { |
|
46 try { |
|
47 if (mTextureIDs != null) |
|
48 TextureReaper.get().add(mTextureIDs); |
|
49 } finally { |
|
50 super.finalize(); |
|
51 } |
|
52 } |
|
53 |
|
54 public void destroy() { |
|
55 try { |
|
56 if (mImage != null) { |
|
57 mImage.destroy(); |
|
58 } |
|
59 } catch (Exception ex) { |
|
60 Log.e(LOGTAG, "error clearing buffers: ", ex); |
|
61 } |
|
62 } |
|
63 |
|
64 public void setPaintMode(PaintMode mode) { |
|
65 mPaintMode = mode; |
|
66 } |
|
67 |
|
68 /** |
|
69 * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a |
|
70 * transaction. |
|
71 */ |
|
72 |
|
73 public void invalidate() { |
|
74 if (!inTransaction()) |
|
75 throw new RuntimeException("invalidate() is only valid inside a transaction"); |
|
76 IntSize bufferSize = mImage.getSize(); |
|
77 mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height); |
|
78 } |
|
79 |
|
80 private void validateTexture() { |
|
81 /* Calculate the ideal texture size. This must be a power of two if |
|
82 * the texture is repeated or OpenGL ES 2.0 isn't supported, as |
|
83 * OpenGL ES 2.0 is required for NPOT texture support (without |
|
84 * extensions), but doesn't support repeating NPOT textures. |
|
85 * |
|
86 * XXX Currently, we don't pick a GLES 2.0 context, so always round. |
|
87 */ |
|
88 IntSize textureSize = mImage.getSize().nextPowerOfTwo(); |
|
89 |
|
90 if (!textureSize.equals(mSize)) { |
|
91 mSize = textureSize; |
|
92 |
|
93 // Delete the old texture |
|
94 if (mTextureIDs != null) { |
|
95 TextureReaper.get().add(mTextureIDs); |
|
96 mTextureIDs = null; |
|
97 |
|
98 // Free the texture immediately, so we don't incur a |
|
99 // temporarily increased memory usage. |
|
100 TextureReaper.get().reap(); |
|
101 } |
|
102 } |
|
103 } |
|
104 |
|
105 @Override |
|
106 protected void performUpdates(RenderContext context) { |
|
107 super.performUpdates(context); |
|
108 |
|
109 // Reallocate the texture if the size has changed |
|
110 validateTexture(); |
|
111 |
|
112 // Don't do any work if the image has an invalid size. |
|
113 if (!mImage.getSize().isPositive()) |
|
114 return; |
|
115 |
|
116 // If we haven't allocated a texture, assume the whole region is dirty |
|
117 if (mTextureIDs == null) { |
|
118 uploadFullTexture(); |
|
119 } else { |
|
120 uploadDirtyRect(mDirtyRect); |
|
121 } |
|
122 |
|
123 mDirtyRect.setEmpty(); |
|
124 } |
|
125 |
|
126 private void uploadFullTexture() { |
|
127 IntSize bufferSize = mImage.getSize(); |
|
128 uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height)); |
|
129 } |
|
130 |
|
131 private void uploadDirtyRect(Rect dirtyRect) { |
|
132 // If we have nothing to upload, just return for now |
|
133 if (dirtyRect.isEmpty()) |
|
134 return; |
|
135 |
|
136 // It's possible that the buffer will be null, check for that and return |
|
137 ByteBuffer imageBuffer = mImage.getBuffer(); |
|
138 if (imageBuffer == null) |
|
139 return; |
|
140 |
|
141 if (mTextureIDs == null) { |
|
142 mTextureIDs = new int[1]; |
|
143 GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); |
|
144 } |
|
145 |
|
146 int cairoFormat = mImage.getFormat(); |
|
147 CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); |
|
148 |
|
149 bindAndSetGLParameters(); |
|
150 |
|
151 // XXX TexSubImage2D is too broken to rely on on Adreno, and very slow |
|
152 // on other chipsets, so we always upload the entire buffer. |
|
153 IntSize bufferSize = mImage.getSize(); |
|
154 if (mSize.equals(bufferSize)) { |
|
155 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, |
|
156 mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); |
|
157 } else { |
|
158 // Our texture has been expanded to the next power of two. |
|
159 // XXX We probably never want to take this path, so throw an exception. |
|
160 throw new RuntimeException("Buffer/image size mismatch in TileLayer!"); |
|
161 } |
|
162 } |
|
163 |
|
164 private void bindAndSetGLParameters() { |
|
165 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
|
166 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); |
|
167 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, |
|
168 GLES20.GL_LINEAR); |
|
169 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, |
|
170 GLES20.GL_LINEAR); |
|
171 |
|
172 int repeatMode = repeats() ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE; |
|
173 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode); |
|
174 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode); |
|
175 } |
|
176 } |
|
177 |