blob: d7b960abac813da417cd2436b01256ee2bfe3346 [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
Daniel Sandlere4f98912013-06-25 15:13:26 -040019import android.content.Context;
Michael Jurka5f1c5092010-09-03 14:15:02 -070020import android.graphics.Bitmap;
21import android.graphics.BlurMaskFilter;
22import android.graphics.Canvas;
Michael Jurka5f1c5092010-09-03 14:15:02 -070023import android.graphics.Paint;
Michael Jurka5f1c5092010-09-03 14:15:02 -070024import android.graphics.PorterDuff;
25import android.graphics.PorterDuffXfermode;
26
27public class HolographicOutlineHelper {
Michael Jurka5f1c5092010-09-03 14:15:02 -070028 private final Paint mHolographicPaint = new Paint();
Joe Onorato4be866d2010-10-10 11:26:02 -070029 private final Paint mBlurPaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070030 private final Paint mErasePaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070031
Daniel Sandlere4f98912013-06-25 15:13:26 -040032 public int mMaxOuterBlurRadius;
33 public int mMinOuterBlurRadius;
Joe Onorato4be866d2010-10-10 11:26:02 -070034
Daniel Sandlere4f98912013-06-25 15:13:26 -040035 private BlurMaskFilter mExtraThickOuterBlurMaskFilter;
36 private BlurMaskFilter mThickOuterBlurMaskFilter;
37 private BlurMaskFilter mMediumOuterBlurMaskFilter;
38 private BlurMaskFilter mThinOuterBlurMaskFilter;
39 private BlurMaskFilter mThickInnerBlurMaskFilter;
40 private BlurMaskFilter mExtraThickInnerBlurMaskFilter;
41 private BlurMaskFilter mMediumInnerBlurMaskFilter;
Adam Cohen5bb50bd2010-12-03 11:39:55 -080042
43 private static final int THICK = 0;
44 private static final int MEDIUM = 1;
Michael Jurka38b4f7c2010-12-14 16:46:39 -080045 private static final int EXTRA_THICK = 2;
Patrick Dubroy8e58e912010-10-14 13:21:48 -070046
Daniel Sandlere572fe42013-06-12 22:46:02 -040047 static HolographicOutlineHelper INSTANCE;
48
Daniel Sandlere4f98912013-06-25 15:13:26 -040049 private HolographicOutlineHelper(Context context) {
50 final float scale = LauncherAppState.getInstance().getScreenDensity();
Patrick Dubroy8e58e912010-10-14 13:21:48 -070051
Daniel Sandlere4f98912013-06-25 15:13:26 -040052 mMinOuterBlurRadius = (int) (scale * 1.0f);
53 mMaxOuterBlurRadius = (int) (scale * 12.0f);
Patrick Dubroy8e58e912010-10-14 13:21:48 -070054
Daniel Sandlere4f98912013-06-25 15:13:26 -040055 mExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
56 mThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
57 mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
58 mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
59 mExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
60 mThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
61 mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
Daniel Sandlere572fe42013-06-12 22:46:02 -040062
Michael Jurka5f1c5092010-09-03 14:15:02 -070063 mHolographicPaint.setFilterBitmap(true);
64 mHolographicPaint.setAntiAlias(true);
Joe Onorato4be866d2010-10-10 11:26:02 -070065 mBlurPaint.setFilterBitmap(true);
66 mBlurPaint.setAntiAlias(true);
Michael Jurka5f1c5092010-09-03 14:15:02 -070067 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
68 mErasePaint.setFilterBitmap(true);
69 mErasePaint.setAntiAlias(true);
70 }
71
Daniel Sandlere4f98912013-06-25 15:13:26 -040072 public static HolographicOutlineHelper obtain(Context context) {
73 if (INSTANCE == null) {
74 INSTANCE = new HolographicOutlineHelper(context);
75 }
Daniel Sandlere572fe42013-06-12 22:46:02 -040076 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:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400133 outerBlurMaskFilter = mExtraThickOuterBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800134 break;
135 case THICK:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400136 outerBlurMaskFilter = mThickOuterBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800137 break;
138 case MEDIUM:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400139 outerBlurMaskFilter = mMediumOuterBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800140 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) {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400148 mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800149 } else {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400150 mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800151 }
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:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400162 innerBlurMaskFilter = mExtraThickInnerBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800163 break;
164 case THICK:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400165 innerBlurMaskFilter = mThickInnerBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800166 break;
167 case MEDIUM:
Daniel Sandlere4f98912013-06-25 15:13:26 -0400168 innerBlurMaskFilter = mMediumInnerBlurMaskFilter;
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800169 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}