mobile/android/base/gfx/ScrollbarLayer.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.gfx;
michael@0 7
michael@0 8 import org.mozilla.gecko.util.FloatUtils;
michael@0 9
michael@0 10 import android.graphics.Bitmap;
michael@0 11 import android.graphics.Rect;
michael@0 12 import android.graphics.RectF;
michael@0 13 import android.opengl.GLES20;
michael@0 14
michael@0 15 import java.nio.FloatBuffer;
michael@0 16
michael@0 17 public class ScrollbarLayer extends TileLayer {
michael@0 18 public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
michael@0 19 private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame
michael@0 20
michael@0 21 private final boolean mVertical;
michael@0 22 private float mOpacity;
michael@0 23
michael@0 24 // To avoid excessive GC, declare some objects here that would otherwise
michael@0 25 // be created and destroyed frequently during draw().
michael@0 26 private final RectF mBarRectF;
michael@0 27 private final Rect mBarRect;
michael@0 28 private final float[] mCoords;
michael@0 29 private final RectF mCapRectF;
michael@0 30
michael@0 31 private LayerRenderer mRenderer;
michael@0 32 private int mProgram;
michael@0 33 private int mPositionHandle;
michael@0 34 private int mTextureHandle;
michael@0 35 private int mSampleHandle;
michael@0 36 private int mTMatrixHandle;
michael@0 37 private int mOpacityHandle;
michael@0 38
michael@0 39 // Fragment shader used to draw the scroll-bar with opacity
michael@0 40 private static final String FRAGMENT_SHADER =
michael@0 41 "precision mediump float;\n" +
michael@0 42 "varying vec2 vTexCoord;\n" +
michael@0 43 "uniform sampler2D sTexture;\n" +
michael@0 44 "uniform float uOpacity;\n" +
michael@0 45 "void main() {\n" +
michael@0 46 " gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
michael@0 47 " gl_FragColor.a *= uOpacity;\n" +
michael@0 48 "}\n";
michael@0 49
michael@0 50 // Dimensions of the texture bitmap (will always be power-of-two)
michael@0 51 private final int mTexWidth;
michael@0 52 private final int mTexHeight;
michael@0 53 // Some useful dimensions of the actual content in the bitmap
michael@0 54 private final int mBarWidth;
michael@0 55 private final int mCapLength;
michael@0 56
michael@0 57 private final Rect mStartCapTexCoords; // top/left endcap coordinates
michael@0 58 private final Rect mBodyTexCoords; // 1-pixel slice of the texture to be stretched
michael@0 59 private final Rect mEndCapTexCoords; // bottom/right endcap coordinates
michael@0 60
michael@0 61 ScrollbarLayer(LayerRenderer renderer, Bitmap scrollbarImage, IntSize imageSize, boolean vertical) {
michael@0 62 super(new BufferedCairoImage(scrollbarImage), TileLayer.PaintMode.NORMAL);
michael@0 63 mRenderer = renderer;
michael@0 64 mVertical = vertical;
michael@0 65
michael@0 66 mBarRectF = new RectF();
michael@0 67 mBarRect = new Rect();
michael@0 68 mCoords = new float[20];
michael@0 69 mCapRectF = new RectF();
michael@0 70
michael@0 71 mTexHeight = scrollbarImage.getHeight();
michael@0 72 mTexWidth = scrollbarImage.getWidth();
michael@0 73
michael@0 74 if (mVertical) {
michael@0 75 mBarWidth = imageSize.width;
michael@0 76 mCapLength = imageSize.height / 2;
michael@0 77 mStartCapTexCoords = new Rect(0, mTexHeight - mCapLength, imageSize.width, mTexHeight);
michael@0 78 mBodyTexCoords = new Rect(0, mTexHeight - (mCapLength + 1), imageSize.width, mTexHeight - mCapLength);
michael@0 79 mEndCapTexCoords = new Rect(0, mTexHeight - imageSize.height, imageSize.width, mTexHeight - (mCapLength + 1));
michael@0 80 } else {
michael@0 81 mBarWidth = imageSize.height;
michael@0 82 mCapLength = imageSize.width / 2;
michael@0 83 mStartCapTexCoords = new Rect(0, mTexHeight - imageSize.height, mCapLength, mTexHeight);
michael@0 84 mBodyTexCoords = new Rect(mCapLength, mTexHeight - imageSize.height, mCapLength + 1, mTexHeight);
michael@0 85 mEndCapTexCoords = new Rect(mCapLength + 1, mTexHeight - imageSize.height, imageSize.width, mTexHeight);
michael@0 86 }
michael@0 87 }
michael@0 88
michael@0 89 private void createProgram() {
michael@0 90 int vertexShader = LayerRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
michael@0 91 LayerRenderer.DEFAULT_VERTEX_SHADER);
michael@0 92 int fragmentShader = LayerRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
michael@0 93 FRAGMENT_SHADER);
michael@0 94
michael@0 95 mProgram = GLES20.glCreateProgram();
michael@0 96 GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
michael@0 97 GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
michael@0 98 GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
michael@0 99
michael@0 100 // Get handles to the shaders' vPosition, aTexCoord, sTexture, and uTMatrix members.
michael@0 101 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
michael@0 102 mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
michael@0 103 mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
michael@0 104 mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
michael@0 105 mOpacityHandle = GLES20.glGetUniformLocation(mProgram, "uOpacity");
michael@0 106 }
michael@0 107
michael@0 108 private void activateProgram() {
michael@0 109 // Add the program to the OpenGL environment
michael@0 110 GLES20.glUseProgram(mProgram);
michael@0 111
michael@0 112 // Set the transformation matrix
michael@0 113 GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false,
michael@0 114 LayerRenderer.DEFAULT_TEXTURE_MATRIX, 0);
michael@0 115
michael@0 116 // Enable the arrays from which we get the vertex and texture coordinates
michael@0 117 GLES20.glEnableVertexAttribArray(mPositionHandle);
michael@0 118 GLES20.glEnableVertexAttribArray(mTextureHandle);
michael@0 119
michael@0 120 GLES20.glUniform1i(mSampleHandle, 0);
michael@0 121 GLES20.glUniform1f(mOpacityHandle, mOpacity);
michael@0 122 }
michael@0 123
michael@0 124 private void deactivateProgram() {
michael@0 125 GLES20.glDisableVertexAttribArray(mTextureHandle);
michael@0 126 GLES20.glDisableVertexAttribArray(mPositionHandle);
michael@0 127 GLES20.glUseProgram(0);
michael@0 128 }
michael@0 129
michael@0 130 /**
michael@0 131 * Decrease the opacity of the scrollbar by one frame's worth.
michael@0 132 * Return true if the opacity was decreased, or false if the scrollbars
michael@0 133 * are already fully faded out.
michael@0 134 */
michael@0 135 public boolean fade() {
michael@0 136 if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) {
michael@0 137 return false;
michael@0 138 }
michael@0 139 beginTransaction(); // called on compositor thread
michael@0 140 mOpacity = Math.max(mOpacity - FADE_AMOUNT, 0.0f);
michael@0 141 endTransaction();
michael@0 142 return true;
michael@0 143 }
michael@0 144
michael@0 145 /**
michael@0 146 * Restore the opacity of the scrollbar to fully opaque.
michael@0 147 * Return true if the opacity was changed, or false if the scrollbars
michael@0 148 * are already fully opaque.
michael@0 149 */
michael@0 150 public boolean unfade() {
michael@0 151 if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) {
michael@0 152 return false;
michael@0 153 }
michael@0 154 beginTransaction(); // called on compositor thread
michael@0 155 mOpacity = 1.0f;
michael@0 156 endTransaction();
michael@0 157 return true;
michael@0 158 }
michael@0 159
michael@0 160 @Override
michael@0 161 public void draw(RenderContext context) {
michael@0 162 if (!initialized())
michael@0 163 return;
michael@0 164
michael@0 165 // Create the shader program, if necessary
michael@0 166 if (mProgram == 0) {
michael@0 167 createProgram();
michael@0 168 }
michael@0 169
michael@0 170 // Enable the shader program
michael@0 171 mRenderer.deactivateDefaultProgram();
michael@0 172 activateProgram();
michael@0 173
michael@0 174 GLES20.glEnable(GLES20.GL_BLEND);
michael@0 175 GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
michael@0 176
michael@0 177 if (mVertical) {
michael@0 178 getVerticalRect(context, mBarRectF);
michael@0 179 } else {
michael@0 180 getHorizontalRect(context, mBarRectF);
michael@0 181 }
michael@0 182 RectUtils.round(mBarRectF, mBarRect);
michael@0 183
michael@0 184 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
michael@0 185 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
michael@0 186
michael@0 187 float viewWidth = context.viewport.width();
michael@0 188 float viewHeight = context.viewport.height();
michael@0 189
michael@0 190 mBarRectF.set(mBarRect.left, viewHeight - mBarRect.top, mBarRect.right, viewHeight - mBarRect.bottom);
michael@0 191 mBarRectF.offset(context.offset.x, -context.offset.y);
michael@0 192
michael@0 193 // We take a 1-pixel slice from the center of the image and scale it to become the bar
michael@0 194 fillRectCoordBuffer(mCoords, mBarRectF, viewWidth, viewHeight, mBodyTexCoords, mTexWidth, mTexHeight);
michael@0 195
michael@0 196 // Get the buffer and handles from the context
michael@0 197 FloatBuffer coordBuffer = context.coordBuffer;
michael@0 198 int positionHandle = mPositionHandle;
michael@0 199 int textureHandle = mTextureHandle;
michael@0 200
michael@0 201 // Make sure we are at position zero in the buffer in case other draw methods did not
michael@0 202 // clean up after themselves
michael@0 203 coordBuffer.position(0);
michael@0 204 coordBuffer.put(mCoords);
michael@0 205
michael@0 206 // Unbind any the current array buffer so we can use client side buffers
michael@0 207 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
michael@0 208
michael@0 209 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
michael@0 210 coordBuffer.position(0);
michael@0 211 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 212
michael@0 213 // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
michael@0 214 coordBuffer.position(3);
michael@0 215 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 216
michael@0 217 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
michael@0 218
michael@0 219 // Reset the position in the buffer for the next set of vertex and texture coordinates.
michael@0 220 coordBuffer.position(0);
michael@0 221 if (mVertical) {
michael@0 222 // top endcap
michael@0 223 mCapRectF.set(mBarRectF.left, mBarRectF.top + mCapLength, mBarRectF.right, mBarRectF.top);
michael@0 224 } else {
michael@0 225 // left endcap
michael@0 226 mCapRectF.set(mBarRectF.left - mCapLength, mBarRectF.bottom + mBarWidth, mBarRectF.left, mBarRectF.bottom);
michael@0 227 }
michael@0 228
michael@0 229 fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mStartCapTexCoords, mTexWidth, mTexHeight);
michael@0 230 coordBuffer.put(mCoords);
michael@0 231
michael@0 232 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
michael@0 233 coordBuffer.position(0);
michael@0 234 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 235
michael@0 236 // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
michael@0 237 coordBuffer.position(3);
michael@0 238 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 239
michael@0 240 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
michael@0 241
michael@0 242 // Reset the position in the buffer for the next set of vertex and texture coordinates.
michael@0 243 coordBuffer.position(0);
michael@0 244 if (mVertical) {
michael@0 245 // bottom endcap
michael@0 246 mCapRectF.set(mBarRectF.left, mBarRectF.bottom, mBarRectF.right, mBarRectF.bottom - mCapLength);
michael@0 247 } else {
michael@0 248 // right endcap
michael@0 249 mCapRectF.set(mBarRectF.right, mBarRectF.bottom + mBarWidth, mBarRectF.right + mCapLength, mBarRectF.bottom);
michael@0 250 }
michael@0 251 fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mEndCapTexCoords, mTexWidth, mTexHeight);
michael@0 252 coordBuffer.put(mCoords);
michael@0 253
michael@0 254 // Vertex coordinates are x,y,z starting at position 0 into the buffer.
michael@0 255 coordBuffer.position(0);
michael@0 256 GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 257
michael@0 258 // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
michael@0 259 coordBuffer.position(3);
michael@0 260 GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
michael@0 261
michael@0 262 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
michael@0 263
michael@0 264 // Reset the position in the buffer for the next set of vertex and texture coordinates.
michael@0 265 coordBuffer.position(0);
michael@0 266
michael@0 267 // Enable the default shader program again
michael@0 268 deactivateProgram();
michael@0 269 mRenderer.activateDefaultProgram();
michael@0 270 }
michael@0 271
michael@0 272 private void getVerticalRect(RenderContext context, RectF dest) {
michael@0 273 RectF viewport = context.viewport;
michael@0 274 RectF pageRect = context.pageRect;
michael@0 275 float viewportHeight = viewport.height() - context.offset.y;
michael@0 276 float barStart = ((viewport.top - context.offset.y - pageRect.top) * (viewportHeight / pageRect.height())) + mCapLength;
michael@0 277 float barEnd = ((viewport.bottom - context.offset.y - pageRect.top) * (viewportHeight / pageRect.height())) - mCapLength;
michael@0 278 if (barStart > barEnd) {
michael@0 279 float middle = (barStart + barEnd) / 2.0f;
michael@0 280 barStart = barEnd = middle;
michael@0 281 }
michael@0 282 dest.set(viewport.width() - mBarWidth, barStart, viewport.width(), barEnd);
michael@0 283 }
michael@0 284
michael@0 285 private void getHorizontalRect(RenderContext context, RectF dest) {
michael@0 286 RectF viewport = context.viewport;
michael@0 287 RectF pageRect = context.pageRect;
michael@0 288 float viewportWidth = viewport.width() - context.offset.x;
michael@0 289 float barStart = ((viewport.left - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) + mCapLength;
michael@0 290 float barEnd = ((viewport.right - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) - mCapLength;
michael@0 291 if (barStart > barEnd) {
michael@0 292 float middle = (barStart + barEnd) / 2.0f;
michael@0 293 barStart = barEnd = middle;
michael@0 294 }
michael@0 295 dest.set(barStart, viewport.height() - mBarWidth, barEnd, viewport.height());
michael@0 296 }
michael@0 297 }

mercurial