From df01966f999ddcc69b3e479c9efbd733ad86bc84 Mon Sep 17 00:00:00 2001 From: Mark Wei Date: Wed, 25 Jun 2014 15:40:38 -0700 Subject: Import latest changes to the bitmap library. Main development is now going on in the Bigtop fork. This import includes the following changes: cl/68071490 Cache BitmapShader instance in CircularBitmapDrawable. This simple implementation is eliminating almost all of the BitmapShader construction during startup. cl/69907083 Add compatibility mode to StyledCornersBitmapDrawable for b/15023700 in 4.4.3 The rounded corners and flaps are now drawn on the canvas, instead of clipped with a path. This part is in the bitmap library. The drawn corners must match the color of the background color of the container. The bt_megalist_selected_item_background color has been pre-mixed with @android:color/white so it's in a ColorDrawable instead of a LayerDrawable, and so its color can be used to draw the fake corners. This part is in the Bigtop codebase. Change-Id: I21a22d8550fbe1dd3de7410cd82969ff947c27ea --- .../bitmap/drawable/CircularBitmapDrawable.java | 15 ++- .../drawable/StyledCornersBitmapDrawable.java | 143 ++++++++++++++++++++- 2 files changed, 146 insertions(+), 12 deletions(-) diff --git a/src/com/android/bitmap/drawable/CircularBitmapDrawable.java b/src/com/android/bitmap/drawable/CircularBitmapDrawable.java index 8536f58..a4af368 100644 --- a/src/com/android/bitmap/drawable/CircularBitmapDrawable.java +++ b/src/com/android/bitmap/drawable/CircularBitmapDrawable.java @@ -43,6 +43,7 @@ public class CircularBitmapDrawable extends ExtendedBitmapDrawable { private final Paint mBorderPaint = new Paint(); private float mBorderWidth; + private Bitmap mShaderBitmap; public CircularBitmapDrawable(Resources res, BitmapCache cache, boolean limitDensity) { @@ -120,20 +121,22 @@ public class CircularBitmapDrawable extends ExtendedBitmapDrawable { protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, final Rect src, final Rect dst, final float alpha) { // Draw bitmap through shader first. - BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, - TileMode.CLAMP); - sMatrix.reset(); + BitmapShader shader = (BitmapShader) mBitmapPaint.getShader(); + if (shader == null || mShaderBitmap != bitmap) { + shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); + mShaderBitmap = bitmap; + mBitmapPaint.setShader(shader); + } + sMatrix.reset(); // Fit bitmap to bounds. float scale = Math.max((float) dst.width() / src.width(), (float) dst.height() / src.height()); sMatrix.postScale(scale, scale); - // Translate bitmap to dst bounds. sMatrix.postTranslate(dst.left, dst.top); - shader.setLocalMatrix(sMatrix); - mBitmapPaint.setShader(shader); + int oldAlpha = mBitmapPaint.getAlpha(); mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, diff --git a/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java b/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java index d5d04b3..e23642d 100644 --- a/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java +++ b/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java @@ -53,7 +53,9 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { private final Paint mFlapPaint = new Paint(); private final Paint mBorderPaint = new Paint(); + private final Paint mCompatibilityModeBackgroundPaint = new Paint(); private final Path mClipPath = new Path(); + private final Path mCompatibilityModePath = new Path(); private final float mCornerRoundRadius; private final float mCornerFlapSide; @@ -63,6 +65,7 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { private int mBottomLeftCornerStyle = CORNER_STYLE_SHARP; private int mScrimColor; private float mBorderWidth; + private boolean mIsCompatibilityMode; /** * Create a new StyledCornersBitmapDrawable. @@ -77,12 +80,17 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { mFlapPaint.setColor(Color.TRANSPARENT); mFlapPaint.setStyle(Style.FILL); + mFlapPaint.setAntiAlias(true); mBorderPaint.setColor(Color.TRANSPARENT); mBorderPaint.setStyle(Style.STROKE); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderPaint.setAntiAlias(true); + mCompatibilityModeBackgroundPaint.setColor(Color.TRANSPARENT); + mCompatibilityModeBackgroundPaint.setStyle(Style.FILL); + mCompatibilityModeBackgroundPaint.setAntiAlias(true); + mScrimColor = Color.TRANSPARENT; } @@ -128,6 +136,13 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { } } + /** + * Get the flap color for all corners that have style {@link #CORNER_STYLE_SHARP}. + */ + public int getFlapColor() { + return mFlapPaint.getColor(); + } + /** * Set the flap color for all corners that have style {@link #CORNER_STYLE_SHARP}. * @@ -163,6 +178,34 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { } } + /** + * Sets whether we should work around an issue introduced in Android 4.4.3, + * where a WebView can corrupt the stencil buffer of the canvas when the canvas is clipped + * using a non-rectangular Path. + */ + public void setCompatibilityMode(boolean isCompatibilityMode) { + boolean changed = mIsCompatibilityMode != isCompatibilityMode; + mIsCompatibilityMode = isCompatibilityMode; + + if (changed) { + invalidateSelf(); + } + } + + /** + * Sets the color of the container that this drawable is in. The given color will be used in + * {@link #setCompatibilityMode compatibility mode} to draw fake corners to emulate clipped + * corners. + */ + public void setCompatibilityModeBackgroundColor(int color) { + boolean changed = mCompatibilityModeBackgroundPaint.getColor() != color; + mCompatibilityModeBackgroundPaint.setColor(color); + + if (changed) { + invalidateSelf(); + } + } + @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); @@ -182,8 +225,10 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { } // Clip to path. - canvas.save(); - canvas.clipPath(mClipPath); + if (!mIsCompatibilityMode) { + canvas.save(); + canvas.clipPath(mClipPath); + } // Draw parent within path. super.draw(canvas); @@ -191,7 +236,7 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { // Draw scrim on top of parent. canvas.drawColor(mScrimColor); - // Draw flap. + // Draw flaps. float left = bounds.left + mBorderWidth / 2; float top = bounds.top + mBorderWidth / 2; float right = bounds.right - mBorderWidth / 2; @@ -221,14 +266,100 @@ public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { mCornerRoundRadius, mFlapPaint); } - canvas.restore(); + if (!mIsCompatibilityMode) { + canvas.restore(); + } + + if (mIsCompatibilityMode) { + drawFakeCornersForCompatibilityMode(canvas); + } // Draw border around path. canvas.drawPath(mClipPath, mBorderPaint); } - protected Path getClipPath() { - return mClipPath; + protected void drawFakeCornersForCompatibilityMode(final Canvas canvas) { + final Rect bounds = getBounds(); + + float left = bounds.left; + float top = bounds.top; + float right = bounds.right; + float bottom = bounds.bottom; + + // Draw fake round corners. + RectF fakeCornerRectF = sRectF; + fakeCornerRectF.set(0, 0, mCornerRoundRadius * 2, mCornerRoundRadius * 2); + if (mTopLeftCornerStyle == CORNER_STYLE_ROUND) { + fakeCornerRectF.offsetTo(left, top); + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(left, top); + mCompatibilityModePath.lineTo(left + mCornerRoundRadius, top); + mCompatibilityModePath.arcTo(fakeCornerRectF, START_TOP, -QUARTER_CIRCLE); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mTopRightCornerStyle == CORNER_STYLE_ROUND) { + fakeCornerRectF.offsetTo(right - fakeCornerRectF.width(), top); + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(right, top); + mCompatibilityModePath.lineTo(right, top + mCornerRoundRadius); + mCompatibilityModePath.arcTo(fakeCornerRectF, START_RIGHT, -QUARTER_CIRCLE); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mBottomRightCornerStyle == CORNER_STYLE_ROUND) { + fakeCornerRectF + .offsetTo(right - fakeCornerRectF.width(), bottom - fakeCornerRectF.height()); + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(right, bottom); + mCompatibilityModePath.lineTo(right - mCornerRoundRadius, bottom); + mCompatibilityModePath.arcTo(fakeCornerRectF, START_BOTTOM, -QUARTER_CIRCLE); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mBottomLeftCornerStyle == CORNER_STYLE_ROUND) { + fakeCornerRectF.offsetTo(left, bottom - fakeCornerRectF.height()); + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(left, bottom); + mCompatibilityModePath.lineTo(left, bottom - mCornerRoundRadius); + mCompatibilityModePath.arcTo(fakeCornerRectF, START_LEFT, -QUARTER_CIRCLE); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + + // Draw fake flap corners. + if (mTopLeftCornerStyle == CORNER_STYLE_FLAP) { + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(left, top); + mCompatibilityModePath.lineTo(left + mCornerFlapSide, top); + mCompatibilityModePath.lineTo(left, top + mCornerFlapSide); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mTopRightCornerStyle == CORNER_STYLE_FLAP) { + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(right, top); + mCompatibilityModePath.lineTo(right, top + mCornerFlapSide); + mCompatibilityModePath.lineTo(right - mCornerFlapSide, top); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mBottomRightCornerStyle == CORNER_STYLE_FLAP) { + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(right, bottom); + mCompatibilityModePath.lineTo(right - mCornerFlapSide, bottom); + mCompatibilityModePath.lineTo(right, bottom - mCornerFlapSide); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } + if (mBottomLeftCornerStyle == CORNER_STYLE_FLAP) { + mCompatibilityModePath.rewind(); + mCompatibilityModePath.moveTo(left, bottom); + mCompatibilityModePath.lineTo(left, bottom - mCornerFlapSide); + mCompatibilityModePath.lineTo(left + mCornerFlapSide, bottom); + mCompatibilityModePath.close(); + canvas.drawPath(mCompatibilityModePath, mCompatibilityModeBackgroundPaint); + } } private void recalculatePath() { -- cgit v1.2.3