Bug 771746 - Draw scrollbars using a white outline so it shows up on dark backgrounds. r=Cwiiis

This commit is contained in:
Kartikaya Gupta 2012-12-29 15:24:01 -05:00
parent e65170c39d
commit 7affc8fd4f
5 changed files with 75 additions and 62 deletions

View File

@ -572,6 +572,7 @@ RES_DRAWABLE_BASE = \
res/drawable/handle_end.png \
res/drawable/handle_middle.png \
res/drawable/handle_start.png \
res/drawable/scrollbar.png \
$(addprefix res/drawable-mdpi/,$(notdir $(SYNC_RES_DRAWABLE_MDPI))) \
$(NULL)

View File

@ -13,7 +13,10 @@ import org.mozilla.gecko.mozglue.DirectBufferAllocator;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@ -134,8 +137,11 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern());
mShadowLayer = new NinePatchTileLayer(shadowImage);
mHorizScrollLayer = ScrollbarLayer.create(this, false);
mVertScrollLayer = ScrollbarLayer.create(this, true);
Bitmap scrollbarImage = view.getScrollbarImage();
IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
mFadeRunnable = new FadeRunnable();
mFrameTimings = new int[60];
@ -150,6 +156,24 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
Tabs.registerOnTabsChangedListener(this);
}
private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) {
IntSize potSize = size.nextPowerOfTwo();
if (size.equals(potSize)) {
return image;
}
// make the bitmap size a power-of-two in both dimensions if it's not already.
Bitmap potImage = Bitmap.createBitmap(potSize.width, potSize.height, image.getConfig());
new Canvas(potImage).drawBitmap(image, new Matrix(), null);
return potImage;
}
private Bitmap diagonalFlip(Bitmap image) {
Matrix rotation = new Matrix();
rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x)
Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true);
return rotated;
}
public void destroy() {
DirectBufferAllocator.free(mCoordByteBuffer);
mCoordByteBuffer = null;

View File

@ -319,6 +319,10 @@ public class LayerView extends FrameLayout {
return getDrawable(R.drawable.shadow);
}
Bitmap getScrollbarImage() {
return getDrawable(R.drawable.scrollbar);
}
private void onSizeChanged(int width, int height) {
mGLController.surfaceChanged(width, height);

View File

@ -20,17 +20,10 @@ import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
/**
* Draws a small rect. This is scaled to become a scrollbar.
*/
public class ScrollbarLayer extends TileLayer {
public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame
private static final int PADDING = 1; // gap between scrollbar and edge of viewport
private static final int BAR_SIZE = 6;
private static final int CAP_RADIUS = (BAR_SIZE / 2);
private final boolean mVertical;
private float mOpacity;
@ -60,50 +53,43 @@ public class ScrollbarLayer extends TileLayer {
" gl_FragColor.a *= uOpacity;\n" +
"}\n";
// Dimensions of the texture image
private static final int TEX_HEIGHT = 8;
private static final int TEX_WIDTH = 8;
// Dimensions of the texture bitmap (will always be power-of-two)
private final int mTexWidth;
private final int mTexHeight;
// Some useful dimensions of the actual content in the bitmap
private final int mBarWidth;
private final int mCapLength;
private static final Rect BODY_TEX_COORDS = new Rect(CAP_RADIUS, CAP_RADIUS, CAP_RADIUS + 1, CAP_RADIUS + 1);
private static final Rect LEFT_CAP_TEX_COORDS = new Rect(0, TEX_HEIGHT - BAR_SIZE, CAP_RADIUS, TEX_HEIGHT);
private static final Rect TOP_CAP_TEX_COORDS = new Rect(0, TEX_HEIGHT - CAP_RADIUS, BAR_SIZE, TEX_HEIGHT);
private static final Rect RIGHT_CAP_TEX_COORDS = new Rect(CAP_RADIUS, TEX_HEIGHT - BAR_SIZE, BAR_SIZE, TEX_HEIGHT);
private static final Rect BOT_CAP_TEX_COORDS = new Rect(0, TEX_HEIGHT - BAR_SIZE, BAR_SIZE, TEX_HEIGHT - CAP_RADIUS);
private final Rect mStartCapTexCoords; // top/left endcap coordinates
private final Rect mBodyTexCoords; // 1-pixel slice of the texture to be stretched
private final Rect mEndCapTexCoords; // bottom/right endcap coordinates
private ScrollbarLayer(LayerRenderer renderer, CairoImage image, boolean vertical, ByteBuffer buffer) {
super(image, TileLayer.PaintMode.NORMAL);
mVertical = vertical;
ScrollbarLayer(LayerRenderer renderer, Bitmap scrollbarImage, IntSize imageSize, boolean vertical) {
super(new BufferedCairoImage(scrollbarImage), TileLayer.PaintMode.NORMAL);
mRenderer = renderer;
IntSize size = image.getSize();
Bitmap bitmap = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// Paint a spot to use as the scroll indicator
Paint foregroundPaint = new Paint();
foregroundPaint.setAntiAlias(true);
foregroundPaint.setStyle(Paint.Style.FILL);
foregroundPaint.setColor(Color.argb(127, 0, 0, 0));
canvas.drawColor(Color.argb(0, 0, 0, 0), PorterDuff.Mode.CLEAR);
canvas.drawCircle(CAP_RADIUS, CAP_RADIUS, CAP_RADIUS, foregroundPaint);
bitmap.copyPixelsToBuffer(buffer.asIntBuffer());
mVertical = vertical;
mBarRectF = new RectF();
mBarRect = new Rect();
mCoords = new float[20];
mCapRectF = new RectF();
}
public static ScrollbarLayer create(LayerRenderer renderer, boolean vertical) {
// just create an empty image for now, it will get drawn
// on demand anyway
int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE);
ByteBuffer buffer = DirectBufferAllocator.allocate(imageSize * imageSize * 4);
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize,
CairoImage.FORMAT_ARGB32);
return new ScrollbarLayer(renderer, image, vertical, buffer);
mTexHeight = scrollbarImage.getHeight();
mTexWidth = scrollbarImage.getWidth();
if (mVertical) {
mBarWidth = imageSize.width;
mCapLength = imageSize.height / 2;
mStartCapTexCoords = new Rect(0, mTexHeight - mCapLength, imageSize.width, mTexHeight);
mBodyTexCoords = new Rect(0, mTexHeight - (mCapLength + 1), imageSize.width, mTexHeight - mCapLength);
mEndCapTexCoords = new Rect(0, mTexHeight - imageSize.height, imageSize.width, mTexHeight - (mCapLength + 1));
} else {
mBarWidth = imageSize.height;
mCapLength = imageSize.width / 2;
mStartCapTexCoords = new Rect(0, mTexHeight - imageSize.height, mCapLength, mTexHeight);
mBodyTexCoords = new Rect(mCapLength, mTexHeight - imageSize.height, mCapLength + 1, mTexHeight);
mEndCapTexCoords = new Rect(mCapLength + 1, mTexHeight - imageSize.height, imageSize.width, mTexHeight);
}
}
private void createProgram() {
@ -209,8 +195,8 @@ public class ScrollbarLayer extends TileLayer {
mBarRectF.set(mBarRect.left, viewHeight - mBarRect.top, mBarRect.right, viewHeight - mBarRect.bottom);
// We take a 1x1 pixel from the center of the image and scale it to become the bar
fillRectCoordBuffer(mCoords, mBarRectF, viewWidth, viewHeight, BODY_TEX_COORDS, TEX_WIDTH, TEX_HEIGHT);
// We take a 1-pixel slice from the center of the image and scale it to become the bar
fillRectCoordBuffer(mCoords, mBarRectF, viewWidth, viewHeight, mBodyTexCoords, mTexWidth, mTexHeight);
// Get the buffer and handles from the context
FloatBuffer coordBuffer = context.coordBuffer;
@ -239,12 +225,13 @@ public class ScrollbarLayer extends TileLayer {
coordBuffer.position(0);
if (mVertical) {
// top endcap
mCapRectF.set(mBarRectF.left, mBarRectF.top + CAP_RADIUS, mBarRectF.left + BAR_SIZE, mBarRectF.top);
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, TOP_CAP_TEX_COORDS, TEX_WIDTH, TEX_HEIGHT);
mCapRectF.set(mBarRectF.left, mBarRectF.top + mCapLength, mBarRectF.right, mBarRectF.top);
} else {
mCapRectF.set(mBarRectF.left - CAP_RADIUS, mBarRectF.bottom + BAR_SIZE, mBarRectF.left, mBarRectF.bottom);
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, LEFT_CAP_TEX_COORDS, TEX_WIDTH, TEX_HEIGHT);
// left endcap
mCapRectF.set(mBarRectF.left - mCapLength, mBarRectF.bottom + mBarWidth, mBarRectF.left, mBarRectF.bottom);
}
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mStartCapTexCoords, mTexWidth, mTexHeight);
coordBuffer.put(mCoords);
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
@ -261,13 +248,12 @@ public class ScrollbarLayer extends TileLayer {
coordBuffer.position(0);
if (mVertical) {
// bottom endcap
mCapRectF.set(mBarRectF.left, mBarRectF.bottom, mBarRectF.left + BAR_SIZE, mBarRectF.bottom - CAP_RADIUS);
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, BOT_CAP_TEX_COORDS, TEX_WIDTH, TEX_HEIGHT);
mCapRectF.set(mBarRectF.left, mBarRectF.bottom, mBarRectF.right, mBarRectF.bottom - mCapLength);
} else {
// right endcap
mCapRectF.set(mBarRectF.right, mBarRectF.bottom + BAR_SIZE, mBarRectF.right + CAP_RADIUS, mBarRectF.bottom);
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, RIGHT_CAP_TEX_COORDS, TEX_WIDTH, TEX_HEIGHT);
mCapRectF.set(mBarRectF.right, mBarRectF.bottom + mBarWidth, mBarRectF.right + mCapLength, mBarRectF.bottom);
}
fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mEndCapTexCoords, mTexWidth, mTexHeight);
coordBuffer.put(mCoords);
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
@ -291,26 +277,24 @@ public class ScrollbarLayer extends TileLayer {
private void getVerticalRect(RenderContext context, RectF dest) {
RectF viewport = context.viewport;
RectF pageRect = context.pageRect;
float barStart = ((viewport.top - pageRect.top) * (viewport.height() / pageRect.height())) + CAP_RADIUS;
float barEnd = ((viewport.bottom - pageRect.top) * (viewport.height() / pageRect.height())) - CAP_RADIUS;
float barStart = ((viewport.top - pageRect.top) * (viewport.height() / pageRect.height())) + mCapLength;
float barEnd = ((viewport.bottom - pageRect.top) * (viewport.height() / pageRect.height())) - mCapLength;
if (barStart > barEnd) {
float middle = (barStart + barEnd) / 2.0f;
barStart = barEnd = middle;
}
float right = viewport.width() - PADDING;
dest.set(right - BAR_SIZE, barStart, right, barEnd);
dest.set(viewport.width() - mBarWidth, barStart, viewport.width(), barEnd);
}
private void getHorizontalRect(RenderContext context, RectF dest) {
RectF viewport = context.viewport;
RectF pageRect = context.pageRect;
float barStart = ((viewport.left - pageRect.left) * (viewport.width() / pageRect.width())) + CAP_RADIUS;
float barEnd = ((viewport.right - pageRect.left) * (viewport.width() / pageRect.width())) - CAP_RADIUS;
float barStart = ((viewport.left - pageRect.left) * (viewport.width() / pageRect.width())) + mCapLength;
float barEnd = ((viewport.right - pageRect.left) * (viewport.width() / pageRect.width())) - mCapLength;
if (barStart > barEnd) {
float middle = (barStart + barEnd) / 2.0f;
barStart = barEnd = middle;
}
float bottom = viewport.height() - PADDING;
dest.set(barStart, bottom - BAR_SIZE, barEnd, bottom);
dest.set(barStart, viewport.height() - mBarWidth, barEnd, viewport.height());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B