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
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 @@
     private final Paint mBorderPaint = new Paint();
 
     private float mBorderWidth;
+    private Bitmap mShaderBitmap;
 
     public CircularBitmapDrawable(Resources res,
             BitmapCache cache, boolean limitDensity) {
@@ -120,20 +121,22 @@
     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 @@
 
     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 @@
     private int mBottomLeftCornerStyle = CORNER_STYLE_SHARP;
     private int mScrimColor;
     private float mBorderWidth;
+    private boolean mIsCompatibilityMode;
 
     /**
      * Create a new StyledCornersBitmapDrawable.
@@ -77,12 +80,17 @@
 
         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;
     }
 
@@ -129,6 +137,13 @@
     }
 
     /**
+     * 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}.
      *
      * Use {@link android.graphics.Color#TRANSPARENT} to disable flap colors.
@@ -163,6 +178,34 @@
         }
     }
 
+    /**
+     * 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 @@
         }
 
         // 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 @@
         // 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 @@
                     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() {