Avoid potential negative array index access to cached text.
[LibreOffice.git] / android / source / src / java / org / mozilla / gecko / gfx / ScrollbarLayer.java
blob7ef8ff02061071ba56f9e9cafa33b657fd715593
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.Bitmap;
9 import android.graphics.Canvas;
10 import android.graphics.Color;
11 import android.graphics.Paint;
12 import android.graphics.PorterDuff;
13 import android.graphics.Rect;
14 import android.graphics.RectF;
15 import android.opengl.GLES20;
17 import org.libreoffice.kit.DirectBufferAllocator;
18 import org.mozilla.gecko.util.FloatUtils;
20 import java.nio.ByteBuffer;
21 import java.nio.FloatBuffer;
23 /**
24 * Draws a small rect. This is scaled to become a scrollbar.
26 public class ScrollbarLayer extends TileLayer {
27 private static String LOGTAG = LayerView.class.getName();
28 public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
29 private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame
31 private static final int PADDING = 1; // gap between scrollbar and edge of viewport
32 private static final int BAR_SIZE = 6;
33 private static final int CAP_RADIUS = (BAR_SIZE / 2);
35 private final boolean mVertical;
36 private final Bitmap mBitmap;
37 private final Canvas mCanvas;
38 private float mOpacity;
40 private LayerRenderer mRenderer;
41 private int mProgram;
42 private int mPositionHandle;
43 private int mTextureHandle;
44 private int mSampleHandle;
45 private int mTMatrixHandle;
46 private int mOpacityHandle;
48 // Fragment shader used to draw the scroll-bar with opacity
49 private static final String FRAGMENT_SHADER =
50 "precision mediump float;\n" +
51 "varying vec2 vTexCoord;\n" +
52 "uniform sampler2D sTexture;\n" +
53 "uniform float uOpacity;\n" +
54 "void main() {\n" +
55 " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" +
56 " gl_FragColor.a *= uOpacity;\n" +
57 "}\n";
59 // Dimensions of the texture image
60 private static final float TEX_HEIGHT = 8.0f;
61 private static final float TEX_WIDTH = 8.0f;
63 // Texture coordinates for the scrollbar's body
64 // We take a 1x1 pixel from the center of the image and scale it to become the bar
65 private static final float[] BODY_TEX_COORDS = {
66 // x, y
67 CAP_RADIUS/TEX_WIDTH, CAP_RADIUS/TEX_HEIGHT,
68 CAP_RADIUS/TEX_WIDTH, (CAP_RADIUS+1)/TEX_HEIGHT,
69 (CAP_RADIUS+1)/TEX_WIDTH, CAP_RADIUS/TEX_HEIGHT,
70 (CAP_RADIUS+1)/TEX_WIDTH, (CAP_RADIUS+1)/TEX_HEIGHT
73 // Texture coordinates for the top cap of the scrollbar
74 private static final float[] TOP_CAP_TEX_COORDS = {
75 // x, y
76 0 , 1.0f - CAP_RADIUS/TEX_HEIGHT,
77 0 , 1.0f,
78 BAR_SIZE/TEX_WIDTH, 1.0f - CAP_RADIUS/TEX_HEIGHT,
79 BAR_SIZE/TEX_WIDTH, 1.0f
82 // Texture coordinates for the bottom cap of the scrollbar
83 private static final float[] BOT_CAP_TEX_COORDS = {
84 // x, y
85 0 , 1.0f - BAR_SIZE/TEX_HEIGHT,
86 0 , 1.0f - CAP_RADIUS/TEX_HEIGHT,
87 BAR_SIZE/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
88 BAR_SIZE/TEX_WIDTH, 1.0f - CAP_RADIUS/TEX_HEIGHT
91 // Texture coordinates for the left cap of the scrollbar
92 private static final float[] LEFT_CAP_TEX_COORDS = {
93 // x, y
94 0 , 1.0f - BAR_SIZE/TEX_HEIGHT,
95 0 , 1.0f,
96 CAP_RADIUS/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
97 CAP_RADIUS/TEX_WIDTH, 1.0f
100 // Texture coordinates for the right cap of the scrollbar
101 private static final float[] RIGHT_CAP_TEX_COORDS = {
102 // x, y
103 CAP_RADIUS/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
104 CAP_RADIUS/TEX_WIDTH, 1.0f,
105 BAR_SIZE/TEX_WIDTH , 1.0f - BAR_SIZE/TEX_HEIGHT,
106 BAR_SIZE/TEX_WIDTH , 1.0f
109 private ScrollbarLayer(LayerRenderer renderer, CairoImage image, boolean vertical, ByteBuffer buffer) {
110 super(image, TileLayer.PaintMode.NORMAL);
111 mVertical = vertical;
112 mRenderer = renderer;
114 IntSize size = image.getSize();
115 mBitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888);
116 mCanvas = new Canvas(mBitmap);
118 // Paint a spot to use as the scroll indicator
119 Paint foregroundPaint = new Paint();
120 foregroundPaint.setAntiAlias(true);
121 foregroundPaint.setStyle(Paint.Style.FILL);
122 foregroundPaint.setColor(Color.argb(127, 0, 0, 0));
124 mCanvas.drawColor(Color.argb(0, 0, 0, 0), PorterDuff.Mode.CLEAR);
125 mCanvas.drawCircle(CAP_RADIUS, CAP_RADIUS, CAP_RADIUS, foregroundPaint);
127 mBitmap.copyPixelsToBuffer(buffer.asIntBuffer());
130 public static ScrollbarLayer create(LayerRenderer renderer, boolean vertical) {
131 // just create an empty image for now, it will get drawn
132 // on demand anyway
133 int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE);
134 ByteBuffer buffer = DirectBufferAllocator.allocate(imageSize * imageSize * 4);
135 CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize,
136 CairoImage.FORMAT_ARGB32);
137 return new ScrollbarLayer(renderer, image, vertical, buffer);
140 private void createProgram() {
141 int vertexShader = LayerRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
142 LayerRenderer.DEFAULT_VERTEX_SHADER);
143 int fragmentShader = LayerRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
144 FRAGMENT_SHADER);
146 mProgram = GLES20.glCreateProgram();
147 GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
148 GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
149 GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
151 // Get handles to the shaders' vPosition, aTexCoord, sTexture, and uTMatrix members.
152 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
153 mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
154 mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
155 mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
156 mOpacityHandle = GLES20.glGetUniformLocation(mProgram, "uOpacity");
159 private void activateProgram() {
160 // Add the program to the OpenGL environment
161 GLES20.glUseProgram(mProgram);
163 // Set the transformation matrix
164 GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false,
165 LayerRenderer.DEFAULT_TEXTURE_MATRIX, 0);
167 // Enable the arrays from which we get the vertex and texture coordinates
168 GLES20.glEnableVertexAttribArray(mPositionHandle);
169 GLES20.glEnableVertexAttribArray(mTextureHandle);
171 GLES20.glUniform1i(mSampleHandle, 0);
172 GLES20.glUniform1f(mOpacityHandle, mOpacity);
175 private void deactivateProgram() {
176 GLES20.glDisableVertexAttribArray(mTextureHandle);
177 GLES20.glDisableVertexAttribArray(mPositionHandle);
178 GLES20.glUseProgram(0);
182 * Decrease the opacity of the scrollbar by one frame's worth.
183 * Return true if the opacity was decreased, or false if the scrollbars
184 * are already fully faded out.
186 public boolean fade() {
187 if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) {
188 return false;
190 beginTransaction(); // called on compositor thread
191 mOpacity = Math.max(mOpacity - FADE_AMOUNT, 0.0f);
192 endTransaction();
193 return true;
197 * Restore the opacity of the scrollbar to fully opaque.
198 * Return true if the opacity was changed, or false if the scrollbars
199 * are already fully opaque.
201 public boolean unfade() {
202 if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) {
203 return false;
205 beginTransaction(); // called on compositor thread
206 mOpacity = 1.0f;
207 endTransaction();
209 return true;
212 @Override
213 public void draw(RenderContext context) {
214 if (!initialized())
215 return;
217 // Create the shader program, if necessary
218 if (mProgram == 0) {
219 createProgram();
222 // Enable the shader program
223 mRenderer.deactivateDefaultProgram();
224 activateProgram();
226 GLES20.glEnable(GLES20.GL_BLEND);
227 GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
229 Rect rect = RectUtils.round(mVertical
230 ? getVerticalRect(context)
231 : getHorizontalRect(context));
232 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
234 float viewWidth = context.viewport.width();
235 float viewHeight = context.viewport.height();
237 float top = viewHeight - rect.top;
238 float bot = viewHeight - rect.bottom;
240 // Coordinates for the scrollbar's body combined with the texture coordinates
241 float[] bodyCoords = {
242 // x, y, z, texture_x, texture_y
243 rect.left/viewWidth, bot/viewHeight, 0,
244 BODY_TEX_COORDS[0], BODY_TEX_COORDS[1],
246 rect.left/viewWidth, (bot+rect.height())/viewHeight, 0,
247 BODY_TEX_COORDS[2], BODY_TEX_COORDS[3],
249 (rect.left+rect.width())/viewWidth, bot/viewHeight, 0,
250 BODY_TEX_COORDS[4], BODY_TEX_COORDS[5],
252 (rect.left+rect.width())/viewWidth, (bot+rect.height())/viewHeight, 0,
253 BODY_TEX_COORDS[6], BODY_TEX_COORDS[7]
256 // Get the buffer and handles from the context
257 FloatBuffer coordBuffer = context.coordBuffer;
258 int positionHandle = mPositionHandle;
259 int textureHandle = mTextureHandle;
261 // Make sure we are at position zero in the buffer in case other draw methods did not
262 // clean up after themselves
263 coordBuffer.position(0);
264 coordBuffer.put(bodyCoords);
266 // Unbind any the current array buffer so we can use client side buffers
267 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
269 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
270 coordBuffer.position(0);
271 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
272 coordBuffer);
274 // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
275 coordBuffer.position(3);
276 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
277 coordBuffer);
279 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
281 // Reset the position in the buffer for the next set of vertex and texture coordinates.
282 coordBuffer.position(0);
284 if (mVertical) {
285 // top endcap
286 float[] topCap = {
287 // x, y, z, texture_x, texture_y
288 rect.left/viewWidth, top/viewHeight, 0,
289 TOP_CAP_TEX_COORDS[0], TOP_CAP_TEX_COORDS[1],
291 rect.left/viewWidth, (top+CAP_RADIUS)/viewHeight, 0,
292 TOP_CAP_TEX_COORDS[2], TOP_CAP_TEX_COORDS[3],
294 (rect.left+BAR_SIZE)/viewWidth, top/viewHeight, 0,
295 TOP_CAP_TEX_COORDS[4], TOP_CAP_TEX_COORDS[5],
297 (rect.left+BAR_SIZE)/viewWidth, (top+CAP_RADIUS)/viewHeight, 0,
298 TOP_CAP_TEX_COORDS[6], TOP_CAP_TEX_COORDS[7]
301 coordBuffer.put(topCap);
303 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
304 coordBuffer.position(0);
305 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
306 coordBuffer);
308 // Texture coordinates are texture_x, texture_y starting at position 3 into the
309 // buffer.
310 coordBuffer.position(3);
311 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
312 coordBuffer);
314 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
316 // Reset the position in the buffer for the next set of vertex and texture
317 // coordinates.
318 coordBuffer.position(0);
320 // bottom endcap
321 float[] botCap = {
322 // x, y, z, texture_x, texture_y
323 rect.left/viewWidth, (bot-CAP_RADIUS)/viewHeight, 0,
324 BOT_CAP_TEX_COORDS[0], BOT_CAP_TEX_COORDS[1],
326 rect.left/viewWidth, (bot)/viewHeight, 0,
327 BOT_CAP_TEX_COORDS[2], BOT_CAP_TEX_COORDS[3],
329 (rect.left+BAR_SIZE)/viewWidth, (bot-CAP_RADIUS)/viewHeight, 0,
330 BOT_CAP_TEX_COORDS[4], BOT_CAP_TEX_COORDS[5],
332 (rect.left+BAR_SIZE)/viewWidth, (bot)/viewHeight, 0,
333 BOT_CAP_TEX_COORDS[6], BOT_CAP_TEX_COORDS[7]
336 coordBuffer.put(botCap);
338 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
339 coordBuffer.position(0);
340 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
341 coordBuffer);
343 // Texture coordinates are texture_x, texture_y starting at position 3 into the
344 // buffer.
345 coordBuffer.position(3);
346 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
347 coordBuffer);
349 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
351 // Reset the position in the buffer for the next set of vertex and texture
352 // coordinates.
353 coordBuffer.position(0);
354 } else {
355 // left endcap
356 float[] leftCap = {
357 // x, y, z, texture_x, texture_y
358 (rect.left-CAP_RADIUS)/viewWidth, bot/viewHeight, 0,
359 LEFT_CAP_TEX_COORDS[0], LEFT_CAP_TEX_COORDS[1],
360 (rect.left-CAP_RADIUS)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
361 LEFT_CAP_TEX_COORDS[2], LEFT_CAP_TEX_COORDS[3],
362 (rect.left)/viewWidth, bot/viewHeight, 0, LEFT_CAP_TEX_COORDS[4],
363 LEFT_CAP_TEX_COORDS[5],
364 (rect.left)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
365 LEFT_CAP_TEX_COORDS[6], LEFT_CAP_TEX_COORDS[7]
368 coordBuffer.put(leftCap);
370 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
371 coordBuffer.position(0);
372 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
373 coordBuffer);
375 // Texture coordinates are texture_x, texture_y starting at position 3 into the
376 // buffer.
377 coordBuffer.position(3);
378 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
379 coordBuffer);
381 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
383 // Reset the position in the buffer for the next set of vertex and texture
384 // coordinates.
385 coordBuffer.position(0);
387 // right endcap
388 float[] rightCap = {
389 // x, y, z, texture_x, texture_y
390 rect.right/viewWidth, (bot)/viewHeight, 0,
391 RIGHT_CAP_TEX_COORDS[0], RIGHT_CAP_TEX_COORDS[1],
393 rect.right/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
394 RIGHT_CAP_TEX_COORDS[2], RIGHT_CAP_TEX_COORDS[3],
396 (rect.right+CAP_RADIUS)/viewWidth, (bot)/viewHeight, 0,
397 RIGHT_CAP_TEX_COORDS[4], RIGHT_CAP_TEX_COORDS[5],
399 (rect.right+CAP_RADIUS)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
400 RIGHT_CAP_TEX_COORDS[6], RIGHT_CAP_TEX_COORDS[7]
403 coordBuffer.put(rightCap);
405 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
406 coordBuffer.position(0);
407 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
408 coordBuffer);
410 // Texture coordinates are texture_x, texture_y starting at position 3 into the
411 // buffer.
412 coordBuffer.position(3);
413 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
414 coordBuffer);
416 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
419 // Enable the default shader program again
420 deactivateProgram();
421 mRenderer.activateDefaultProgram();
424 private RectF getVerticalRect(RenderContext context) {
425 RectF viewport = context.viewport;
426 RectF pageRect = context.pageRect;
427 float barStart = ((viewport.top - pageRect.top) * (viewport.height() / pageRect.height())) + CAP_RADIUS;
428 float barEnd = ((viewport.bottom - pageRect.top) * (viewport.height() / pageRect.height())) - CAP_RADIUS;
429 if (barStart > barEnd) {
430 float middle = (barStart + barEnd) / 2.0f;
431 barStart = barEnd = middle;
433 float right = viewport.width() - PADDING;
434 return new RectF(right - BAR_SIZE, barStart, right, barEnd);
437 private RectF getHorizontalRect(RenderContext context) {
438 RectF viewport = context.viewport;
439 RectF pageRect = context.pageRect;
440 float barStart = ((viewport.left - pageRect.left) * (viewport.width() / pageRect.width())) + CAP_RADIUS;
441 float barEnd = ((viewport.right - pageRect.left) * (viewport.width() / pageRect.width())) - CAP_RADIUS;
442 if (barStart > barEnd) {
443 float middle = (barStart + barEnd) / 2.0f;
444 barStart = barEnd = middle;
446 float bottom = viewport.height() - PADDING;
447 return new RectF(barStart, bottom - BAR_SIZE, barEnd, bottom);
451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */