Changing the holographic outline algorithm to match designs.

Change-Id: Ibb9668514c7c3ce56473cf041051245b9c19c793
diff --git a/res/layout-xlarge/all_apps_paged_view_application.xml b/res/layout-xlarge/all_apps_paged_view_application.xml
index 55779e5..bee13d6 100644
--- a/res/layout-xlarge/all_apps_paged_view_application.xml
+++ b/res/layout-xlarge/all_apps_paged_view_application.xml
@@ -14,7 +14,15 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.PagedViewIcon xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher2.PagedViewIcon
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+    launcher:blurColor="#FF6B8CF0"
+    launcher:outlineColor="#FF8CD2FF"
+    launcher:checkedBlurColor="#FFBBE83C"
+    launcher:checkedOutlineColor="#FF8CD2FF"
+
     android:id="@+id/name"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8aa6efb..3ef26fb 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -55,6 +55,19 @@
         <attr name="yAxisEndPadding" format="dimension"  />
     </declare-styleable>
 
+    <!-- PagedViewIcon specific attributes. These attributes are used to customize
+         a PagedViewIcon view in XML files. -->
+    <declare-styleable name="PagedViewIcon">
+        <!-- The blur color of the holographic outline -->
+        <attr name="blurColor" format="color" />
+        <!-- The outline color of the holographic outline -->
+        <attr name="outlineColor" format="color" />
+        <!-- The checked blur color of the holographic outline -->
+        <attr name="checkedBlurColor" format="color" />
+        <!-- The checked outline color of the holographic outline -->
+        <attr name="checkedOutlineColor" format="color" />
+    </declare-styleable>
+
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
diff --git a/src/com/android/launcher2/HolographicOutlineHelper.java b/src/com/android/launcher2/HolographicOutlineHelper.java
index d6c5484..597a725 100644
--- a/src/com/android/launcher2/HolographicOutlineHelper.java
+++ b/src/com/android/launcher2/HolographicOutlineHelper.java
@@ -19,47 +19,39 @@
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
-import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 
 public class HolographicOutlineHelper {
-    private float mDensity;
     private final Paint mHolographicPaint = new Paint();
-    private final Paint mBlurPaint = new Paint();
+    private final Paint mExpensiveBlurPaint = new Paint();
     private final Paint mErasePaint = new Paint();
-    private static final Matrix mIdentity = new Matrix();;
-    private static final float BLUR_FACTOR = 3.5f;
 
-    public static final float DEFAULT_STROKE_WIDTH = 6.0f;
-    public static final int HOLOGRAPHIC_BLUE = 0xFF6699FF;
-    public static final int HOLOGRAPHIC_GREEN = 0xFF51E633;
+    private static final BlurMaskFilter mThickOuterBlurMaskFilter = new BlurMaskFilter(6.0f,
+            BlurMaskFilter.Blur.OUTER);
+    private static final BlurMaskFilter mThinOuterBlurMaskFilter = new BlurMaskFilter(1.0f,
+            BlurMaskFilter.Blur.OUTER);
+    private static final BlurMaskFilter mThickInnerBlurMaskFilter = new BlurMaskFilter(4.0f,
+            BlurMaskFilter.Blur.NORMAL);
 
-    HolographicOutlineHelper(float density) {
-        mDensity = density;
-        mHolographicPaint.setColor(HOLOGRAPHIC_BLUE);
+    public static float DEFAULT_STROKE_WIDTH = 6.0f;
+
+    HolographicOutlineHelper() {
         mHolographicPaint.setFilterBitmap(true);
         mHolographicPaint.setAntiAlias(true);
-        mBlurPaint.setMaskFilter(new BlurMaskFilter(BLUR_FACTOR * density,
-                BlurMaskFilter.Blur.OUTER));
-        mBlurPaint.setFilterBitmap(true);
+        mExpensiveBlurPaint.setFilterBitmap(true);
         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
         mErasePaint.setFilterBitmap(true);
         mErasePaint.setAntiAlias(true);
     }
 
-    private float cubic(float r) {
-        return (float) (Math.pow(r - 1, 3) + 1);
-    }
-
     /**
      * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
      * pages.
      */
     public float highlightAlphaInterpolator(float r) {
-        float maxAlpha = 0.6f;
+        float maxAlpha = 0.8f;
         return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
     }
 
@@ -76,39 +68,51 @@
     }
 
     /**
-     * Sets the color of the holographic paint to be used when applying the outline/blur.
+     * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
+     * bitmap.
      */
-    void setColor(int color) {
+    void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
+            int outlineColor) {
+        // calculate the outer blur first
+        mExpensiveBlurPaint.setMaskFilter(mThickOuterBlurMaskFilter);
+        int[] outerBlurOffset = new int[2];
+        Bitmap thickOuterBlur = srcDst.extractAlpha(mExpensiveBlurPaint, outerBlurOffset);
+        mExpensiveBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
+        int[] thinOuterBlurOffset = new int[2];
+        Bitmap thinOuterBlur = srcDst.extractAlpha(mExpensiveBlurPaint, thinOuterBlurOffset);
+
+        // calculate the inner blur
+        srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
+        mExpensiveBlurPaint.setMaskFilter(mThickInnerBlurMaskFilter);
+        int[] thickInnerBlurOffset = new int[2];
+        Bitmap thickInnerBlur = srcDst.extractAlpha(mExpensiveBlurPaint, thickInnerBlurOffset);
+
+        // mask out the inner blur
+        srcDstCanvas.setBitmap(thickInnerBlur);
+        srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
+                -thickInnerBlurOffset[1], mErasePaint);
+        srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
+                mErasePaint);
+        srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
+                mErasePaint);
+
+        // draw the inner and outer blur
+        srcDstCanvas.setBitmap(srcDst);
+        srcDstCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
         mHolographicPaint.setColor(color);
-    }
+        srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
+                mHolographicPaint);
+        srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
+                mHolographicPaint);
 
-    /**
-     * Applies an outline to whatever is currently drawn in the specified bitmap.
-     */
-    void applyOutline(Bitmap srcDst, Canvas srcDstCanvas, float strokeWidth, PointF offset) {
-        strokeWidth *= mDensity;
-        Bitmap mask = srcDst.extractAlpha();
-        Matrix m = new Matrix();
-        final int width = srcDst.getWidth();
-        final int height = srcDst.getHeight();
-        float xScale = strokeWidth * 2.0f / width;
-        float yScale = strokeWidth * 2.0f / height;
-        m.preScale(1 + xScale, 1 + yScale, (width / 2.0f) + offset.x,
-                (height / 2.0f) + offset.y);
+        // draw the bright outline
+        mHolographicPaint.setColor(outlineColor);
+        srcDstCanvas.drawBitmap(thinOuterBlur, thinOuterBlurOffset[0], thinOuterBlurOffset[1],
+                mHolographicPaint);
 
-        srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
-        srcDstCanvas.drawBitmap(mask, m, mHolographicPaint);
-        srcDstCanvas.drawBitmap(mask, mIdentity, mErasePaint);
-        mask.recycle();
-    }
-
-    /**
-     * Applies an blur to whatever is currently drawn in the specified bitmap.
-     */
-    void applyBlur(Bitmap srcDst, Canvas srcDstCanvas) {
-        int[] xy = new int[2];
-        Bitmap mask = srcDst.extractAlpha(mBlurPaint, xy);
-        srcDstCanvas.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
-        mask.recycle();
+        // cleanup
+        thinOuterBlur.recycle();
+        thickOuterBlur.recycle();
+        thickInnerBlur.recycle();
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
index 4fa83df..0714a93 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -16,12 +16,10 @@
 
 package com.android.launcher2;
 
-import com.android.launcher2.PagedView.PagedViewIconCache;
-
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -30,10 +28,12 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.widget.Checkable;
 import android.widget.TextView;
 
+import com.android.launcher.R;
+import com.android.launcher2.PagedView.PagedViewIconCache;
+
 
 
 /**
@@ -60,6 +60,13 @@
 
     private boolean mIsChecked;
 
+    // Highlight colours
+    private int mHoloBlurColor;
+    private int mHoloOutlineColor;
+    private int mCheckedBlurColor;
+    private int mCheckedOutlineColor;
+
+
     public PagedViewIcon(Context context) {
         this(context, null);
     }
@@ -70,12 +77,15 @@
 
     public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewIcon, defStyle, 0);
+        mHoloBlurColor = a.getColor(R.styleable.PagedViewIcon_blurColor, 0);
+        mHoloOutlineColor = a.getColor(R.styleable.PagedViewIcon_outlineColor, 0);
+        mCheckedBlurColor = a.getColor(R.styleable.PagedViewIcon_checkedBlurColor, 0);
+        mCheckedOutlineColor = a.getColor(R.styleable.PagedViewIcon_checkedOutlineColor, 0);
+        a.recycle();
 
         if (sHolographicOutlineHelper == null) {
-            final Resources resources = context.getResources();
-            final DisplayMetrics metrics = resources.getDisplayMetrics();
-            final float density = metrics.density;
-            sHolographicOutlineHelper = new HolographicOutlineHelper(density);
+            sHolographicOutlineHelper = new HolographicOutlineHelper();
         }
         mDrawableClipRect = new Rect();
 
@@ -128,9 +138,6 @@
         super.onLayout(changed, left, top, right, bottom);
 
         if (mHolographicOutline == null) {
-            final PointF offset = new PointF(0,
-                    -(getCompoundPaddingBottom() + getCompoundPaddingTop())/2.0f);
-
             // update the clipping rect to be used in the holographic pass below
             getDrawingRect(mDrawableClipRect);
             mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop();
@@ -143,10 +150,8 @@
             mHolographicOutlineCanvas = new Canvas(mHolographicOutline);
             mHolographicOutlineCanvas.concat(getMatrix());
             draw(mHolographicOutlineCanvas);
-            sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_BLUE);
-            sHolographicOutlineHelper.applyOutline(mHolographicOutline, mHolographicOutlineCanvas,
-                    HolographicOutlineHelper.DEFAULT_STROKE_WIDTH, offset);
-            sHolographicOutlineHelper.applyBlur(mHolographicOutline, mHolographicOutlineCanvas);
+            sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mHolographicOutline,
+                    mHolographicOutlineCanvas, mHoloBlurColor, mHoloOutlineColor);
             mIsHolographicUpdatePass = false;
             mIconCache.addOutline(mIconCacheKey, mHolographicOutline);
             mHolographicOutlineCanvas = null;
@@ -196,8 +201,9 @@
             mIsChecked = checked;
 
             if (mIsChecked) {
-                final PointF offset = new PointF(0,
-                        -(getCompoundPaddingBottom() + getCompoundPaddingTop())/2.0f);
+                // update the clipping rect to be used in the holographic pass below
+                getDrawingRect(mDrawableClipRect);
+                mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop();
 
                 // set a flag to indicate that we are going to draw the view at full alpha with the text
                 // clipped for the generation of the holographic icon
@@ -207,9 +213,8 @@
                 mHolographicOutlineCanvas = new Canvas(mCheckedOutline);
                 mHolographicOutlineCanvas.concat(getMatrix());
                 draw(mHolographicOutlineCanvas);
-                sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_GREEN);
-                sHolographicOutlineHelper.applyOutline(mCheckedOutline, mHolographicOutlineCanvas,
-                        HolographicOutlineHelper.DEFAULT_STROKE_WIDTH + 1.0f, offset);
+                sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mCheckedOutline,
+                        mHolographicOutlineCanvas, mCheckedBlurColor, mCheckedOutlineColor);
                 mIsHolographicUpdatePass = false;
                 mHolographicOutlineCanvas = null;
             } else {