LibreOffice Module android (master) 1
LayerRenderer.java
Go to the documentation of this file.
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
6package org.mozilla.gecko.gfx;
7
8import android.graphics.Color;
9import android.graphics.Point;
10import android.graphics.Rect;
11import android.graphics.RectF;
12import android.opengl.GLES20;
13import android.opengl.GLSurfaceView;
14import android.os.SystemClock;
15import android.util.Log;
16
19
20import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
22import java.nio.FloatBuffer;
23import java.nio.IntBuffer;
24import java.util.concurrent.CopyOnWriteArrayList;
25
26import javax.microedition.khronos.egl.EGLConfig;
27import javax.microedition.khronos.opengles.GL10;
28
32public class LayerRenderer implements GLSurfaceView.Renderer {
33 private static final String LOGTAG = "GeckoLayerRenderer";
34
35 /*
36 * The amount of time a frame is allowed to take to render before we declare it a dropped
37 * frame.
38 */
39 private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */
40
41 private final LayerView mView;
46 private final FadeRunnable mFadeRunnable;
47 private ByteBuffer mCoordByteBuffer;
48 private FloatBuffer mCoordBuffer;
50 private int mMaxTextureSize;
51 private int mBackgroundColor;
52
53 private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
54
55 /* Used by robocop for testing purposes */
56 private IntBuffer mPixelBuffer;
57
58 // Used by GLES 2.0
59 private int mProgram;
60 private int mPositionHandle;
61 private int mTextureHandle;
62 private int mSampleHandle;
63 private int mTMatrixHandle;
64
65 // column-major matrix applied to each vertex to shift the viewport from
66 // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by
67 // a factor of 2 to fill up the screen
68 public static final float[] DEFAULT_TEXTURE_MATRIX = {
69 2.0f, 0.0f, 0.0f, 0.0f,
70 0.0f, 2.0f, 0.0f, 0.0f,
71 0.0f, 0.0f, 2.0f, 0.0f,
72 -1.0f, -1.0f, 0.0f, 1.0f
73 };
74
75 private static final int COORD_BUFFER_SIZE = 20;
76
77 // The shaders run on the GPU directly, the vertex shader is only applying the
78 // matrix transform detailed above
79
80 // Note we flip the y-coordinate in the vertex shader from a
81 // coordinate system with (0,0) in the top left to one with (0,0) in
82 // the bottom left.
83
84 public static final String DEFAULT_VERTEX_SHADER =
85 "uniform mat4 uTMatrix;\n" +
86 "attribute vec4 vPosition;\n" +
87 "attribute vec2 aTexCoord;\n" +
88 "varying vec2 vTexCoord;\n" +
89 "void main() {\n" +
90 " gl_Position = uTMatrix * vPosition;\n" +
91 " vTexCoord.x = aTexCoord.x;\n" +
92 " vTexCoord.y = 1.0 - aTexCoord.y;\n" +
93 "}\n";
94
95 // We use highp because the screenshot textures
96 // we use are large and we stretch them a lot
97 // so we need all the precision we can get.
98 // Unfortunately, highp is not required by ES 2.0
99 // so on GPU's like Mali we end up getting mediump
100 public static final String DEFAULT_FRAGMENT_SHADER =
101 "precision highp float;\n" +
102 "varying vec2 vTexCoord;\n" +
103 "uniform sampler2D sTexture;\n" +
104 "void main() {\n" +
105 " gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
106 "}\n";
107
109 mView = view;
110
111 CairoImage backgroundImage = new BufferedCairoImage(view.getBackgroundPattern());
112 mBackgroundLayer = new SingleTileLayer(true, backgroundImage);
113
114 CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern());
115 mShadowLayer = new NinePatchTileLayer(shadowImage);
116
119 mFadeRunnable = new FadeRunnable();
120
121 // Initialize the FloatBuffer that will be used to store all vertices and texture
122 // coordinates in draw() commands.
123 mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
124 mCoordByteBuffer.order(ByteOrder.nativeOrder());
125 mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
126 }
127
128 @Override
129 protected void finalize() throws Throwable {
130 try {
131 DirectBufferAllocator.free(mCoordByteBuffer);
132 mCoordByteBuffer = null;
133 mCoordBuffer = null;
134 } finally {
135 super.finalize();
136 }
137 }
138
139 public void destroy() {
140 DirectBufferAllocator.free(mCoordByteBuffer);
141 mCoordByteBuffer = null;
142 mCoordBuffer = null;
147 }
148
149 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
152 }
153
154 public void createDefaultProgram() {
155 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER);
156 int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER);
157
158 mProgram = GLES20.glCreateProgram();
159 GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
160 GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
161 GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
162
163 // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members.
164 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
165 mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
166 mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
167 mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
168
169 int maxTextureSizeResult[] = new int[1];
170 GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
171 mMaxTextureSize = maxTextureSizeResult[0];
172 }
173
174 // Activates the shader program.
176 // Add the program to the OpenGL environment
177 GLES20.glUseProgram(mProgram);
178
179 // Set the transformation matrix
180 GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0);
181
182 // Enable the arrays from which we get the vertex and texture coordinates
183 GLES20.glEnableVertexAttribArray(mPositionHandle);
184 GLES20.glEnableVertexAttribArray(mTextureHandle);
185
186 GLES20.glUniform1i(mSampleHandle, 0);
187
188 // TODO: Move these calls into a separate deactivate() call that is called after the
189 // underlay and overlay are rendered.
190 }
191
192 // Deactivates the shader program. This must be done to avoid crashes after returning to the
193 // Gecko C++ compositor from Java.
195 GLES20.glDisableVertexAttribArray(mTextureHandle);
196 GLES20.glDisableVertexAttribArray(mPositionHandle);
197 GLES20.glUseProgram(0);
198 }
199
200 public int getMaxTextureSize() {
201 return mMaxTextureSize;
202 }
203
204 public void addLayer(Layer layer) {
205 synchronized (mExtraLayers) {
206 if (mExtraLayers.contains(layer)) {
207 mExtraLayers.remove(layer);
208 }
209
210 mExtraLayers.add(layer);
211 }
212 }
213
214 public void removeLayer(Layer layer) {
215 synchronized (mExtraLayers) {
216 mExtraLayers.remove(layer);
217 }
218 }
219
223 public void onDrawFrame(GL10 gl) {
225 synchronized (mView.getLayerClient()) {
226 frame.beginDrawing();
227 frame.drawBackground();
228 frame.drawRootLayer();
229 frame.drawForeground();
230 frame.endDrawing();
231 }
232 }
233
235 RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight());
236 RectF pageRect = new RectF(metrics.getPageRect());
237 return createContext(viewport, pageRect, 1.0f);
238 }
239
241 Rect viewport = RectUtils.round(metrics.getViewport());
242 RectF pageRect = metrics.getPageRect();
243 float zoomFactor = metrics.zoomFactor;
244 return createContext(new RectF(viewport), pageRect, zoomFactor);
245 }
246
247 private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor) {
248 return new RenderContext(viewport, pageRect, zoomFactor, mPositionHandle, mTextureHandle,
250 }
251
252 public void onSurfaceChanged(GL10 gl, final int width, final int height) {
253 GLES20.glViewport(0, 0, width, height);
254 }
255
256 /*
257 * create a vertex shader type (GLES20.GL_VERTEX_SHADER)
258 * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
259 */
260 public static int loadShader(int type, String shaderCode) {
261 int shader = GLES20.glCreateShader(type);
262 GLES20.glShaderSource(shader, shaderCode);
263 GLES20.glCompileShader(shader);
264 return shader;
265 }
266
268 return new Frame(metrics);
269 }
270
271 class FadeRunnable implements Runnable {
272 private boolean mStarted;
273 private long mRunAt;
274
275 void scheduleStartFade(long delay) {
276 mRunAt = SystemClock.elapsedRealtime() + delay;
277 if (!mStarted) {
278 mView.postDelayed(this, delay);
279 mStarted = true;
280 }
281 }
282
283 void scheduleNextFadeFrame() {
284 if (mStarted) {
285 Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade");
286 }
287 mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps
288 }
289
290 boolean timeToFade() {
291 return !mStarted;
292 }
293
294 public void run() {
295 long timeDelta = mRunAt - SystemClock.elapsedRealtime();
296 if (timeDelta > 0) {
297 // the run-at time was pushed back, so reschedule
298 mView.postDelayed(this, timeDelta);
299 } else {
300 // reached the run-at time, execute
301 mStarted = false;
303 }
304 }
305 }
306
307 public class Frame {
308 // The timestamp recording the start of this frame.
309 private long mFrameStartTime;
310 // A fixed snapshot of the viewport metrics that this frame is using to render content.
312 // A rendering context for page-positioned layers, and one for screen-positioned layers.
314 // Whether a layer was updated.
315 private boolean mUpdated;
316 private final Rect mPageRect;
317
319 mFrameMetrics = metrics;
323 }
324
325 private void setScissorRect() {
327 GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
328 GLES20.glScissor(scissorRect.left, scissorRect.top,
329 scissorRect.width(), scissorRect.height());
330 }
331
332 private Rect transformToScissorRect(Rect rect) {
333 IntSize screenSize = new IntSize(mFrameMetrics.getSize());
334
335 int left = Math.max(0, rect.left);
336 int top = Math.max(0, rect.top);
337 int right = Math.min(screenSize.width, rect.right);
338 int bottom = Math.min(screenSize.height, rect.bottom);
339
340 return new Rect(left, screenSize.height - bottom, right,
341 (screenSize.height - bottom) + (bottom - top));
342 }
343
344 private Rect getPageRect() {
347 pageRect.offset(-origin.x, -origin.y);
348 return pageRect;
349 }
350
352 public void beginDrawing() {
353 mFrameStartTime = SystemClock.uptimeMillis();
354
357
358 mUpdated = true;
359
360 Layer rootLayer = mView.getLayerClient().getRoot();
361 Layer lowResLayer = mView.getLayerClient().getLowResLayer();
362
364 // the viewport or page changed, so show the scrollbars again
365 // as per UX decision
368 mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
369 } else if (mFadeRunnable.timeToFade()) {
370 boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
371 if (stillFading) {
372 mFadeRunnable.scheduleNextFadeFrame();
373 }
374 }
376
377 /* Update layers. */
378 if (rootLayer != null) mUpdated &= rootLayer.update(mPageContext); // called on compositor thread
379 if (lowResLayer != null) mUpdated &= lowResLayer.update(mPageContext); // called on compositor thread
380 mUpdated &= mBackgroundLayer.update(mScreenContext); // called on compositor thread
381 mUpdated &= mShadowLayer.update(mPageContext); // called on compositor thread
382 mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread
383 mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
384
385 for (Layer layer : mExtraLayers)
386 mUpdated &= layer.update(mPageContext); // called on compositor thread
387 }
388
395 private Rect getMaskForLayer(Layer layer) {
396 if (layer == null) {
397 return null;
398 }
399
400 RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f);
401 Rect mask = RectUtils.roundIn(bounds);
402
403 // If the mask is within two pixels of any page edge, stretch it over
404 // that edge. This is to avoid drawing thin slivers when masking
405 // layers.
406 if (mask.top <= 2) {
407 mask.top = -1;
408 }
409 if (mask.left <= 2) {
410 mask.left = -1;
411 }
412
413 // Because we're drawing relative to the page-rect, we only need to
414 // take into account its width and height (and not its origin)
415 int pageRight = mPageRect.width();
416 int pageBottom = mPageRect.height();
417
418 if (mask.right >= pageRight - 2) {
419 mask.right = pageRight + 1;
420 }
421 if (mask.bottom >= pageBottom - 2) {
422 mask.bottom = pageBottom + 1;
423 }
424
425 return mask;
426 }
427
429 public void drawBackground() {
430 GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
431
432 /* Update background color. */
433 mBackgroundColor = Color.WHITE;
434
435 /* Clear to the page background colour. The bits set here need to
436 * match up with those used in gfx/layers/opengl/LayerManagerOGL.cpp.
437 */
438 GLES20.glClearColor(((mBackgroundColor>>16)&0xFF) / 255.0f,
439 ((mBackgroundColor>>8)&0xFF) / 255.0f,
440 (mBackgroundColor&0xFF) / 255.0f,
441 0.0f);
442 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT |
443 GLES20.GL_DEPTH_BUFFER_BIT);
444
445 /* Draw the background. */
448
449 /* Draw the drop shadow, if we need to. */
450 RectF untransformedPageRect = new RectF(0.0f, 0.0f, mPageRect.width(),
451 mPageRect.height());
452 if (!untransformedPageRect.contains(mFrameMetrics.getViewport()))
454
455 /* Scissor around the page-rect, in case the page has shrunk
456 * since the screenshot layer was last updated.
457 */
458 setScissorRect(); // Calls glEnable(GL_SCISSOR_TEST))
459 }
460
461 // Draws the layer the client added to us.
462 void drawRootLayer() {
463 Layer lowResLayer = mView.getLayerClient().getLowResLayer();
464 if (lowResLayer == null) {
465 return;
466 }
467 lowResLayer.draw(mPageContext);
468
469 Layer rootLayer = mView.getLayerClient().getRoot();
470 if (rootLayer == null) {
471 return;
472 }
473
474 rootLayer.draw(mPageContext);
475 }
476
478 public void drawForeground() {
479 /* Draw any extra layers that were added (likely plugins) */
480 if (mExtraLayers.size() > 0) {
481 for (Layer layer : mExtraLayers) {
482 if (!layer.usesDefaultProgram())
484
485 layer.draw(mPageContext);
486
487 if (!layer.usesDefaultProgram())
489 }
490 }
491
492 /* Draw the vertical scrollbar. */
493 if (mPageRect.height() > mFrameMetrics.getHeight())
495
496 /* Draw the horizontal scrollbar. */
497 if (mPageRect.width() > mFrameMetrics.getWidth())
499 }
500
502 public void endDrawing() {
503 // If a layer update requires further work, schedule another redraw
504 if (!mUpdated)
506
507 /* Used by robocop for testing purposes */
508 IntBuffer pixelBuffer = mPixelBuffer;
509 if (mUpdated && pixelBuffer != null) {
510 synchronized (pixelBuffer) {
511 pixelBuffer.position(0);
512 GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
513 (int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
514 GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
515 pixelBuffer.notify();
516 }
517 }
518 }
519 }
520}
A Cairo image that simply saves a buffer of pixel data.
ImmutableViewportMetrics getViewportMetrics()
Implementation of PanZoomTarget.
ImmutableViewportMetrics are used to store the viewport metrics in way that we can access a version o...
Rect getMaskForLayer(Layer layer)
Retrieves the bounds for the layer, rounded in such a way that it can be used as a mask for something...
void endDrawing()
This function is invoked via JNI; be careful when modifying signature.
void drawBackground()
This function is invoked via JNI; be careful when modifying signature.
void beginDrawing()
This function is invoked via JNI; be careful when modifying signature.
Frame(ImmutableViewportMetrics metrics)
void drawForeground()
This function is invoked via JNI; be careful when modifying signature.
The layer renderer implements the rendering logic for a layer view.
static final float[] DEFAULT_TEXTURE_MATRIX
RenderContext createScreenContext(ImmutableViewportMetrics metrics)
final ScrollbarLayer mHorizScrollLayer
final SingleTileLayer mBackgroundLayer
CopyOnWriteArrayList< Layer > mExtraLayers
final ScrollbarLayer mVertScrollLayer
static int loadShader(int type, String shaderCode)
void onDrawFrame(GL10 gl)
Called whenever a new frame is about to be drawn.
void onSurfaceCreated(GL10 gl, EGLConfig config)
static final String DEFAULT_VERTEX_SHADER
RenderContext createPageContext(ImmutableViewportMetrics metrics)
final NinePatchTileLayer mShadowLayer
static final String DEFAULT_FRAGMENT_SHADER
RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor)
void onSurfaceChanged(GL10 gl, final int width, final int height)
Frame createFrame(ImmutableViewportMetrics metrics)
A view rendered by the layer compositor.
Definition: LayerView.java:41
GeckoLayerClient getLayerClient()
Definition: LayerView.java:155
boolean fuzzyEquals(RenderContext other)
Definition: Layer.java:208
abstract void draw(RenderContext context)
Subclasses override this function to draw the layer.
final boolean update(RenderContext context)
Updates the layer.
Definition: Layer.java:45
RectF getBounds(RenderContext context)
Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect.
Definition: Layer.java:67
Encapsulates the logic needed to draw a nine-patch bitmap using OpenGL ES.
void draw(RenderContext context)
Subclasses override this function to draw the layer.
static Point round(PointF point)
Definition: PointUtils.java:27
static Rect round(RectF rect)
Returns the nearest integer rect of the given rect.
Definition: RectUtils.java:61
static RectF contract(RectF rect, float lessWidth, float lessHeight)
Definition: RectUtils.java:27
static Rect roundIn(RectF rect)
Definition: RectUtils.java:72
boolean unfade()
Restore the opacity of the scrollbar to fully opaque.
static ScrollbarLayer create(LayerRenderer renderer, boolean vertical)
void draw(RenderContext context)
Subclasses override this function to draw the layer.
boolean fade()
Decrease the opacity of the scrollbar by one frame's worth.
Encapsulates the logic needed to draw a single textured tile.
void setMask(Rect aMaskRect)
Set an area to mask out when rendering.
void draw(RenderContext context)
Subclasses override this function to draw the layer.
Manages a list of dead tiles, so we don't leak resources.
OString right
OString top
OString bottom
def run(arg=None, arg2=-1)
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
ResultType type
sal_uInt64 left