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
;
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
;
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" +
55 " gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" +
56 " gl_FragColor.a *= uOpacity;\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
= {
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
= {
76 0 , 1.0f
- CAP_RADIUS
/TEX_HEIGHT
,
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
= {
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
= {
94 0 , 1.0f
- BAR_SIZE
/TEX_HEIGHT
,
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
= {
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
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
,
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
)) {
190 beginTransaction(); // called on compositor thread
191 mOpacity
= Math
.max(mOpacity
- FADE_AMOUNT
, 0.0f
);
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
)) {
205 beginTransaction(); // called on compositor thread
213 public void draw(RenderContext context
) {
217 // Create the shader program, if necessary
222 // Enable the shader program
223 mRenderer
.deactivateDefaultProgram();
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,
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,
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);
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,
308 // Texture coordinates are texture_x, texture_y starting at position 3 into the
310 coordBuffer
.position(3);
311 GLES20
.glVertexAttribPointer(textureHandle
, 2, GLES20
.GL_FLOAT
, false, 20,
314 GLES20
.glDrawArrays(GLES20
.GL_TRIANGLE_STRIP
, 0, 4);
316 // Reset the position in the buffer for the next set of vertex and texture
318 coordBuffer
.position(0);
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,
343 // Texture coordinates are texture_x, texture_y starting at position 3 into the
345 coordBuffer
.position(3);
346 GLES20
.glVertexAttribPointer(textureHandle
, 2, GLES20
.GL_FLOAT
, false, 20,
349 GLES20
.glDrawArrays(GLES20
.GL_TRIANGLE_STRIP
, 0, 4);
351 // Reset the position in the buffer for the next set of vertex and texture
353 coordBuffer
.position(0);
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,
375 // Texture coordinates are texture_x, texture_y starting at position 3 into the
377 coordBuffer
.position(3);
378 GLES20
.glVertexAttribPointer(textureHandle
, 2, GLES20
.GL_FLOAT
, false, 20,
381 GLES20
.glDrawArrays(GLES20
.GL_TRIANGLE_STRIP
, 0, 4);
383 // Reset the position in the buffer for the next set of vertex and texture
385 coordBuffer
.position(0);
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,
410 // Texture coordinates are texture_x, texture_y starting at position 3 into the
412 coordBuffer
.position(3);
413 GLES20
.glVertexAttribPointer(textureHandle
, 2, GLES20
.GL_FLOAT
, false, 20,
416 GLES20
.glDrawArrays(GLES20
.GL_TRIANGLE_STRIP
, 0, 4);
419 // Enable the default shader program again
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: */