1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher2;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.BlurMaskFilter;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.PorterDuff;
24 import android.graphics.PorterDuffXfermode;
25 
26 public class HolographicOutlineHelper {
27     private final Paint mHolographicPaint = new Paint();
28     private final Paint mBlurPaint = new Paint();
29     private final Paint mErasePaint = new Paint();
30 
31     public static final int MAX_OUTER_BLUR_RADIUS;
32     public static final int MIN_OUTER_BLUR_RADIUS;
33 
34     private static final BlurMaskFilter sExtraThickOuterBlurMaskFilter;
35     private static final BlurMaskFilter sThickOuterBlurMaskFilter;
36     private static final BlurMaskFilter sMediumOuterBlurMaskFilter;
37     private static final BlurMaskFilter sThinOuterBlurMaskFilter;
38     private static final BlurMaskFilter sThickInnerBlurMaskFilter;
39     private static final BlurMaskFilter sExtraThickInnerBlurMaskFilter;
40     private static final BlurMaskFilter sMediumInnerBlurMaskFilter;
41 
42     private static final int THICK = 0;
43     private static final int MEDIUM = 1;
44     private static final int EXTRA_THICK = 2;
45 
46     static {
47         final float scale = LauncherApplication.getScreenDensity();
48 
49         MIN_OUTER_BLUR_RADIUS = (int) (scale * 1.0f);
50         MAX_OUTER_BLUR_RADIUS = (int) (scale * 12.0f);
51 
52         sExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
53         sThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
54         sMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
55         sThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
56         sExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
57         sThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
58         sMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
59     }
60 
HolographicOutlineHelper()61     HolographicOutlineHelper() {
62         mHolographicPaint.setFilterBitmap(true);
63         mHolographicPaint.setAntiAlias(true);
64         mBlurPaint.setFilterBitmap(true);
65         mBlurPaint.setAntiAlias(true);
66         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
67         mErasePaint.setFilterBitmap(true);
68         mErasePaint.setAntiAlias(true);
69     }
70 
71     /**
72      * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
73      * pages.
74      */
highlightAlphaInterpolator(float r)75     public static float highlightAlphaInterpolator(float r) {
76         float maxAlpha = 0.6f;
77         return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
78     }
79 
80     /**
81      * Returns the interpolated view alpha for the effect we want when scrolling pages.
82      */
viewAlphaInterpolator(float r)83     public static float viewAlphaInterpolator(float r) {
84         final float pivot = 0.95f;
85         if (r < pivot) {
86             return (float) Math.pow(r / pivot, 1.5f);
87         } else {
88             return 1.0f;
89         }
90     }
91 
92     /**
93      * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
94      * bitmap.
95      */
applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor, int thickness)96     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
97             int outlineColor, int thickness) {
98         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true,
99                 thickness);
100     }
applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor, boolean clipAlpha, int thickness)101     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
102             int outlineColor, boolean clipAlpha, int thickness) {
103 
104         // We start by removing most of the alpha channel so as to ignore shadows, and
105         // other types of partial transparency when defining the shape of the object
106         if (clipAlpha) {
107             int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
108             srcDst.getPixels(srcBuffer,
109                     0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
110             for (int i = 0; i < srcBuffer.length; i++) {
111                 final int alpha = srcBuffer[i] >>> 24;
112                 if (alpha < 188) {
113                     srcBuffer[i] = 0;
114                 }
115             }
116             srcDst.setPixels(srcBuffer,
117                     0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
118         }
119         Bitmap glowShape = srcDst.extractAlpha();
120 
121         // calculate the outer blur first
122         BlurMaskFilter outerBlurMaskFilter;
123         switch (thickness) {
124             case EXTRA_THICK:
125                 outerBlurMaskFilter = sExtraThickOuterBlurMaskFilter;
126                 break;
127             case THICK:
128                 outerBlurMaskFilter = sThickOuterBlurMaskFilter;
129                 break;
130             case MEDIUM:
131                 outerBlurMaskFilter = sMediumOuterBlurMaskFilter;
132                 break;
133             default:
134                 throw new RuntimeException("Invalid blur thickness");
135         }
136         mBlurPaint.setMaskFilter(outerBlurMaskFilter);
137         int[] outerBlurOffset = new int[2];
138         Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
139         if (thickness == EXTRA_THICK) {
140             mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter);
141         } else {
142             mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter);
143         }
144 
145         int[] brightOutlineOffset = new int[2];
146         Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
147 
148         // calculate the inner blur
149         srcDstCanvas.setBitmap(glowShape);
150         srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
151         BlurMaskFilter innerBlurMaskFilter;
152         switch (thickness) {
153             case EXTRA_THICK:
154                 innerBlurMaskFilter = sExtraThickInnerBlurMaskFilter;
155                 break;
156             case THICK:
157                 innerBlurMaskFilter = sThickInnerBlurMaskFilter;
158                 break;
159             case MEDIUM:
160                 innerBlurMaskFilter = sMediumInnerBlurMaskFilter;
161                 break;
162             default:
163                 throw new RuntimeException("Invalid blur thickness");
164         }
165         mBlurPaint.setMaskFilter(innerBlurMaskFilter);
166         int[] thickInnerBlurOffset = new int[2];
167         Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
168 
169         // mask out the inner blur
170         srcDstCanvas.setBitmap(thickInnerBlur);
171         srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
172                 -thickInnerBlurOffset[1], mErasePaint);
173         srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
174                 mErasePaint);
175         srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
176                 mErasePaint);
177 
178         // draw the inner and outer blur
179         srcDstCanvas.setBitmap(srcDst);
180         srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
181         mHolographicPaint.setColor(color);
182         srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
183                 mHolographicPaint);
184         srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
185                 mHolographicPaint);
186 
187         // draw the bright outline
188         mHolographicPaint.setColor(outlineColor);
189         srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
190                 mHolographicPaint);
191 
192         // cleanup
193         srcDstCanvas.setBitmap(null);
194         brightOutline.recycle();
195         thickOuterBlur.recycle();
196         thickInnerBlur.recycle();
197         glowShape.recycle();
198     }
199 
applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor)200     void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
201             int outlineColor) {
202         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK);
203     }
204 
applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor)205     void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
206             int outlineColor) {
207         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
208     }
209 
applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor, boolean clipAlpha)210     void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
211             int outlineColor, boolean clipAlpha) {
212         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha,
213                 MEDIUM);
214     }
215 
applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, int outlineColor)216     void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
217             int outlineColor) {
218         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
219     }
220 
221 }
222