blob: 47c40b657b2d181847b570b221ab69bf9de285a9 [file] [log] [blame]
Michael Jurka5f1c5092010-09-03 14:15:02 -07001/*
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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
Michael Jurka5f1c5092010-09-03 14:15:02 -070018
19import android.graphics.Bitmap;
20import android.graphics.BlurMaskFilter;
21import android.graphics.Canvas;
Michael Jurka5f1c5092010-09-03 14:15:02 -070022import android.graphics.Paint;
Michael Jurka5f1c5092010-09-03 14:15:02 -070023import android.graphics.PorterDuff;
24import android.graphics.PorterDuffXfermode;
25
26public class HolographicOutlineHelper {
Michael Jurka5f1c5092010-09-03 14:15:02 -070027 private final Paint mHolographicPaint = new Paint();
Joe Onorato4be866d2010-10-10 11:26:02 -070028 private final Paint mBlurPaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070029 private final Paint mErasePaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070030
Michael Jurka38b4f7c2010-12-14 16:46:39 -080031 public static final int MAX_OUTER_BLUR_RADIUS;
Winson Chung1908d072011-02-24 18:09:44 -080032 public static final int MIN_OUTER_BLUR_RADIUS;
Joe Onorato4be866d2010-10-10 11:26:02 -070033
Michael Jurka38b4f7c2010-12-14 16:46:39 -080034 private static final BlurMaskFilter sExtraThickOuterBlurMaskFilter;
Patrick Dubroy8e58e912010-10-14 13:21:48 -070035 private static final BlurMaskFilter sThickOuterBlurMaskFilter;
36 private static final BlurMaskFilter sMediumOuterBlurMaskFilter;
37 private static final BlurMaskFilter sThinOuterBlurMaskFilter;
38 private static final BlurMaskFilter sThickInnerBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -080039 private static final BlurMaskFilter sExtraThickInnerBlurMaskFilter;
Adam Cohen5bb50bd2010-12-03 11:39:55 -080040 private static final BlurMaskFilter sMediumInnerBlurMaskFilter;
41
42 private static final int THICK = 0;
43 private static final int MEDIUM = 1;
Michael Jurka38b4f7c2010-12-14 16:46:39 -080044 private static final int EXTRA_THICK = 2;
Patrick Dubroy8e58e912010-10-14 13:21:48 -070045
Daniel Sandlere572fe42013-06-12 22:46:02 -040046 static HolographicOutlineHelper INSTANCE;
47
Patrick Dubroy8e58e912010-10-14 13:21:48 -070048 static {
Daniel Sandlercc8befa2013-06-11 14:45:48 -040049 final float scale = LauncherAppState.getScreenDensity();
Patrick Dubroy8e58e912010-10-14 13:21:48 -070050
Winson Chung1908d072011-02-24 18:09:44 -080051 MIN_OUTER_BLUR_RADIUS = (int) (scale * 1.0f);
Michael Jurka38b4f7c2010-12-14 16:46:39 -080052 MAX_OUTER_BLUR_RADIUS = (int) (scale * 12.0f);
Patrick Dubroy8e58e912010-10-14 13:21:48 -070053
Michael Jurka38b4f7c2010-12-14 16:46:39 -080054 sExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
55 sThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
Patrick Dubroy8e58e912010-10-14 13:21:48 -070056 sMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
57 sThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
Michael Jurka38b4f7c2010-12-14 16:46:39 -080058 sExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
Patrick Dubroy8e58e912010-10-14 13:21:48 -070059 sThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
Adam Cohen5bb50bd2010-12-03 11:39:55 -080060 sMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
Daniel Sandlere572fe42013-06-12 22:46:02 -040061
62 INSTANCE = new HolographicOutlineHelper();
Patrick Dubroy8e58e912010-10-14 13:21:48 -070063 }
Joe Onorato4be866d2010-10-10 11:26:02 -070064
Daniel Sandlere572fe42013-06-12 22:46:02 -040065 private HolographicOutlineHelper() {
Michael Jurka5f1c5092010-09-03 14:15:02 -070066 mHolographicPaint.setFilterBitmap(true);
67 mHolographicPaint.setAntiAlias(true);
Joe Onorato4be866d2010-10-10 11:26:02 -070068 mBlurPaint.setFilterBitmap(true);
69 mBlurPaint.setAntiAlias(true);
Michael Jurka5f1c5092010-09-03 14:15:02 -070070 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
71 mErasePaint.setFilterBitmap(true);
72 mErasePaint.setAntiAlias(true);
73 }
74
Daniel Sandlere572fe42013-06-12 22:46:02 -040075 public static HolographicOutlineHelper obtain() {
76 return INSTANCE;
77 }
78
Michael Jurka5f1c5092010-09-03 14:15:02 -070079 /**
80 * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
81 * pages.
82 */
Winson Chungb44b5242011-06-13 11:32:14 -070083 public static float highlightAlphaInterpolator(float r) {
84 float maxAlpha = 0.6f;
Winson Chunge8878e32010-09-15 20:37:09 -070085 return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
Michael Jurka5f1c5092010-09-03 14:15:02 -070086 }
87
88 /**
89 * Returns the interpolated view alpha for the effect we want when scrolling pages.
90 */
Winson Chungb44b5242011-06-13 11:32:14 -070091 public static float viewAlphaInterpolator(float r) {
Winson Chunge8878e32010-09-15 20:37:09 -070092 final float pivot = 0.95f;
Michael Jurka5f1c5092010-09-03 14:15:02 -070093 if (r < pivot) {
Winson Chunge8878e32010-09-15 20:37:09 -070094 return (float) Math.pow(r / pivot, 1.5f);
Michael Jurka5f1c5092010-09-03 14:15:02 -070095 } else {
96 return 1.0f;
97 }
98 }
99
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700100 /**
Winson Chung64a3cd42010-09-17 16:47:33 -0700101 * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
102 * bitmap.
Michael Jurka5f1c5092010-09-03 14:15:02 -0700103 */
Winson Chung64a3cd42010-09-17 16:47:33 -0700104 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800105 int outlineColor, int thickness) {
Michael Jurka8c3339b2012-06-14 16:18:21 -0700106 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true,
Peter Ng8db70002011-10-25 15:40:08 -0700107 thickness);
108 }
109 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Michael Jurka8c3339b2012-06-14 16:18:21 -0700110 int outlineColor, boolean clipAlpha, int thickness) {
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800111
112 // We start by removing most of the alpha channel so as to ignore shadows, and
113 // other types of partial transparency when defining the shape of the object
Michael Jurka8c3339b2012-06-14 16:18:21 -0700114 if (clipAlpha) {
115 int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
116 srcDst.getPixels(srcBuffer,
117 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
118 for (int i = 0; i < srcBuffer.length; i++) {
119 final int alpha = srcBuffer[i] >>> 24;
120 if (alpha < 188) {
121 srcBuffer[i] = 0;
122 }
123 }
124 srcDst.setPixels(srcBuffer,
125 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
Peter Ng8db70002011-10-25 15:40:08 -0700126 }
Michael Jurka8c3339b2012-06-14 16:18:21 -0700127 Bitmap glowShape = srcDst.extractAlpha();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800128
Winson Chung64a3cd42010-09-17 16:47:33 -0700129 // calculate the outer blur first
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800130 BlurMaskFilter outerBlurMaskFilter;
131 switch (thickness) {
132 case EXTRA_THICK:
133 outerBlurMaskFilter = sExtraThickOuterBlurMaskFilter;
134 break;
135 case THICK:
136 outerBlurMaskFilter = sThickOuterBlurMaskFilter;
137 break;
138 case MEDIUM:
139 outerBlurMaskFilter = sMediumOuterBlurMaskFilter;
140 break;
141 default:
142 throw new RuntimeException("Invalid blur thickness");
143 }
144 mBlurPaint.setMaskFilter(outerBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700145 int[] outerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800146 Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800147 if (thickness == EXTRA_THICK) {
148 mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter);
149 } else {
150 mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter);
151 }
152
153 int[] brightOutlineOffset = new int[2];
154 Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700155
156 // calculate the inner blur
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800157 srcDstCanvas.setBitmap(glowShape);
Winson Chung64a3cd42010-09-17 16:47:33 -0700158 srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800159 BlurMaskFilter innerBlurMaskFilter;
160 switch (thickness) {
161 case EXTRA_THICK:
162 innerBlurMaskFilter = sExtraThickInnerBlurMaskFilter;
163 break;
164 case THICK:
165 innerBlurMaskFilter = sThickInnerBlurMaskFilter;
166 break;
167 case MEDIUM:
168 innerBlurMaskFilter = sMediumInnerBlurMaskFilter;
169 break;
170 default:
171 throw new RuntimeException("Invalid blur thickness");
172 }
173 mBlurPaint.setMaskFilter(innerBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700174 int[] thickInnerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800175 Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700176
177 // mask out the inner blur
178 srcDstCanvas.setBitmap(thickInnerBlur);
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800179 srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
Winson Chung64a3cd42010-09-17 16:47:33 -0700180 -thickInnerBlurOffset[1], mErasePaint);
181 srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
182 mErasePaint);
183 srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
184 mErasePaint);
185
186 // draw the inner and outer blur
187 srcDstCanvas.setBitmap(srcDst);
Michael Jurka2a9e7062011-01-14 15:48:04 -0800188 srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700189 mHolographicPaint.setColor(color);
Winson Chung64a3cd42010-09-17 16:47:33 -0700190 srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
191 mHolographicPaint);
192 srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
193 mHolographicPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700194
Winson Chung64a3cd42010-09-17 16:47:33 -0700195 // draw the bright outline
196 mHolographicPaint.setColor(outlineColor);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800197 srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
Winson Chung64a3cd42010-09-17 16:47:33 -0700198 mHolographicPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700199
Winson Chung64a3cd42010-09-17 16:47:33 -0700200 // cleanup
Adam Cohenaaf473c2011-08-03 12:02:47 -0700201 srcDstCanvas.setBitmap(null);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800202 brightOutline.recycle();
Winson Chung64a3cd42010-09-17 16:47:33 -0700203 thickOuterBlur.recycle();
204 thickInnerBlur.recycle();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800205 glowShape.recycle();
Michael Jurka5f1c5092010-09-03 14:15:02 -0700206 }
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800207
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800208 void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
209 int outlineColor) {
210 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK);
211 }
212
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800213 void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
214 int outlineColor) {
215 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
216 }
217
218 void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Michael Jurka8c3339b2012-06-14 16:18:21 -0700219 int outlineColor, boolean clipAlpha) {
220 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha,
Peter Ng8db70002011-10-25 15:40:08 -0700221 MEDIUM);
222 }
223
224 void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800225 int outlineColor) {
226 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
227 }
228
Winson Chung7da10252010-10-28 16:07:04 -0700229}