Improving the look of holographic outlines

- The outlines are used in AllApps, Workspace and Customize

Change-Id: I1c3aba81df163c98a839498a3d421a8f03c51f06
diff --git a/src/com/android/launcher2/HolographicOutlineHelper.java b/src/com/android/launcher2/HolographicOutlineHelper.java
index 658490a..bf4c05a 100644
--- a/src/com/android/launcher2/HolographicOutlineHelper.java
+++ b/src/com/android/launcher2/HolographicOutlineHelper.java
@@ -29,6 +29,7 @@
     private final Paint mHolographicPaint = new Paint();
     private final Paint mBlurPaint = new Paint();
     private final Paint mErasePaint = new Paint();
+    private final Paint mAlphaClipPaint = new Paint();
 
     public static final int OUTER_BLUR_RADIUS;
 
@@ -36,6 +37,10 @@
     private static final BlurMaskFilter sMediumOuterBlurMaskFilter;
     private static final BlurMaskFilter sThinOuterBlurMaskFilter;
     private static final BlurMaskFilter sThickInnerBlurMaskFilter;
+    private static final BlurMaskFilter sMediumInnerBlurMaskFilter;
+
+    private static final int THICK = 0;
+    private static final int MEDIUM = 1;
 
     static {
         final float scale = LauncherApplication.getScreenDensity();
@@ -47,6 +52,7 @@
         sMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
         sThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
         sThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
+        sMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
     }
 
     private static final MaskFilter sFineClipTable = TableMaskFilter.CreateClipTable(0, 20);
@@ -62,6 +68,8 @@
         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
         mErasePaint.setFilterBitmap(true);
         mErasePaint.setAntiAlias(true);
+        MaskFilter alphaClipTable = TableMaskFilter.CreateClipTable(180, 255);
+        mAlphaClipPaint.setMaskFilter(alphaClipTable);
     }
 
     /**
@@ -98,66 +106,42 @@
         mHolographicPaint.setMaskFilter(sCoarseClipTable);
         mHolographicPaint.setAlpha(150);
         mHolographicPaint.setColor(color);
+
         canvas.drawBitmap(glow, mTempOffset[0], mTempOffset[1], mHolographicPaint);
         glow.recycle();
     }
 
     /**
-     * Draws a solid outline around a bitmap, erasing the original pixels.
-     *
-     * @param bitmap The bitmap to modify
-     * @param canvas A canvas on the bitmap
-     * @param color The color to draw the outline and glow in
-     * @param removeOrig If true, punch out the original pixels to just leave the outline
-     */
-    void applyExpensiveOuterOutline(Bitmap bitmap, Canvas canvas, int color, boolean removeOrig) {
-        Bitmap originalImage = null;
-        if (removeOrig) {
-            originalImage = bitmap.extractAlpha();
-        }
-
-        // Compute an outer blur on the original bitmap
-        mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter);
-        Bitmap outline = bitmap.extractAlpha(mBlurPaint, mTempOffset);
-
-        // Paint the blurred bitmap back into the canvas. Using the clip table causes any alpha
-        // pixels above a certain threshold to be rounded up to be fully opaque. This gives the
-        // effect of a thick outline, with a slight blur on the edge
-        mHolographicPaint.setColor(color);
-        mHolographicPaint.setMaskFilter(sFineClipTable);
-        canvas.drawBitmap(outline, mTempOffset[0], mTempOffset[1], mHolographicPaint);
-        outline.recycle();
-
-        if (removeOrig) {
-            // Finally, punch out the original pixels, leaving just the outline
-            canvas.drawBitmap(originalImage, 0, 0, mErasePaint);
-            originalImage.recycle();
-        }
-    }
-
-    /**
      * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
      * bitmap.
      */
     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor) {
+            int outlineColor, int thickness) {
+
+        // We start by removing most of the alpha channel so as to ignore shadows, and
+        // other types of partial transparency when defining the shape of the object
+        Bitmap glowShape = srcDst.extractAlpha(mAlphaClipPaint, mTempOffset);
+
         // calculate the outer blur first
-        mBlurPaint.setMaskFilter(sThickOuterBlurMaskFilter);
+        mBlurPaint.setMaskFilter(thickness == THICK ? sThickOuterBlurMaskFilter :
+                                                      sMediumOuterBlurMaskFilter);
         int[] outerBlurOffset = new int[2];
-        Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset);
+        Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
         mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter);
         int[] thinOuterBlurOffset = new int[2];
-        Bitmap thinOuterBlur = srcDst.extractAlpha(mBlurPaint, thinOuterBlurOffset);
+        Bitmap thinOuterBlur = glowShape.extractAlpha(mBlurPaint, thinOuterBlurOffset);
 
         // calculate the inner blur
+        srcDstCanvas.setBitmap(glowShape);
         srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
-        mBlurPaint.setMaskFilter(sThickInnerBlurMaskFilter);
+        mBlurPaint.setMaskFilter(thickness == THICK ? sThickInnerBlurMaskFilter :
+                                                      sMediumInnerBlurMaskFilter);
         int[] thickInnerBlurOffset = new int[2];
-        Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset);
+        Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
 
         // mask out the inner blur
         srcDstCanvas.setBitmap(thickInnerBlur);
-        srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
+        srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
                 -thickInnerBlurOffset[1], mErasePaint);
         srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
                 mErasePaint);
@@ -182,5 +166,17 @@
         thinOuterBlur.recycle();
         thickOuterBlur.recycle();
         thickInnerBlur.recycle();
+        glowShape.recycle();
     }
+
+    void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
+            int outlineColor) {
+        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
+    }
+
+    void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
+            int outlineColor) {
+        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
+    }
+
 }
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
index 0e72598..300bab5 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -81,7 +81,7 @@
             Canvas holographicOutlineCanvas = new Canvas(holographicOutline);
             holographicOutlineCanvas.drawBitmap(icon.mIcon, 0, 0, mPaint);
 
-            sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(holographicOutline,
+            sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(holographicOutline,
                     holographicOutlineCanvas, icon.mHoloBlurColor, icon.mHoloOutlineColor);
 
             mHandler.post(new Runnable() {
@@ -235,7 +235,7 @@
                 mPaint.setAlpha(255);
                 checkedOutlineCanvas.drawBitmap(mIcon, 0, 0, mPaint);
 
-                sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mCheckedOutline,
+                sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(mCheckedOutline,
                         checkedOutlineCanvas, mCheckedBlurColor, mCheckedOutlineColor);
             } else {
                 invalidateCheckedImage();
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 40e507b..4d4ccc2 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -80,7 +80,7 @@
             widget.mPreview.setAlpha(prevAlpha);
             widget.mHolographicOutlineCanvas.restore();
 
-            sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(outline,
+            sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(outline,
                     widget.mHolographicOutlineCanvas, widget.mHoloBlurColor,
                     widget.mHoloOutlineColor);
 
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 7027d83..42dbc9a 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1324,8 +1324,7 @@
 
         canvas.setBitmap(b);
         drawDragView(v, canvas, padding);
-        mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true);
-
+        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
         return b;
     }
 
@@ -1347,8 +1346,7 @@
         canvas.setBitmap(b);
         canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset),
                 rectRadius, rectRadius, mExternalDragOutlinePaint);
-        mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true);
-
+        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
         return b;
     }