blob: b1e0e68a4c43e09ad17b67f0f684be3504f2fa1b [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;
Sunny Goyal508da152014-08-14 10:53:27 -070023import android.graphics.Color;
Michael Jurka5f1c5092010-09-03 14:15:02 -070024import android.graphics.Paint;
Michael Jurka5f1c5092010-09-03 14:15:02 -070025import android.graphics.PorterDuff;
26import android.graphics.PorterDuffXfermode;
Sunny Goyal508da152014-08-14 10:53:27 -070027import android.graphics.Rect;
28import android.graphics.Region.Op;
Michael Jurka5f1c5092010-09-03 14:15:02 -070029
30public class HolographicOutlineHelper {
Sunny Goyal508da152014-08-14 10:53:27 -070031
32 private static final Rect sTempRect = new Rect();
33
34 private final Canvas mCanvas = new Canvas();
35 private final Paint mDrawPaint = new Paint();
Joe Onorato4be866d2010-10-10 11:26:02 -070036 private final Paint mBlurPaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070037 private final Paint mErasePaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070038
Sunny Goyal508da152014-08-14 10:53:27 -070039 private final BlurMaskFilter mMediumOuterBlurMaskFilter;
40 private final BlurMaskFilter mThinOuterBlurMaskFilter;
41 private final BlurMaskFilter mMediumInnerBlurMaskFilter;
Joe Onorato4be866d2010-10-10 11:26:02 -070042
Sunny Goyal508da152014-08-14 10:53:27 -070043 private final BlurMaskFilter mShaowBlurMaskFilter;
44 private final int mShadowOffset;
Adam Cohen5bb50bd2010-12-03 11:39:55 -080045
Sunny Goyal508da152014-08-14 10:53:27 -070046 /**
47 * Padding used when creating shadow bitmap;
48 */
49 final int shadowBitmapPadding;
Patrick Dubroy8e58e912010-10-14 13:21:48 -070050
Daniel Sandlere572fe42013-06-12 22:46:02 -040051 static HolographicOutlineHelper INSTANCE;
52
Daniel Sandlere4f98912013-06-25 15:13:26 -040053 private HolographicOutlineHelper(Context context) {
54 final float scale = LauncherAppState.getInstance().getScreenDensity();
Patrick Dubroy8e58e912010-10-14 13:21:48 -070055
Daniel Sandlere4f98912013-06-25 15:13:26 -040056 mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
57 mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
Daniel Sandlere4f98912013-06-25 15:13:26 -040058 mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
Daniel Sandlere572fe42013-06-12 22:46:02 -040059
Sunny Goyal508da152014-08-14 10:53:27 -070060 mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
61 mShadowOffset = (int) (scale * 2.0f);
62 shadowBitmapPadding = (int) (scale * 4.0f);
63
64 mDrawPaint.setFilterBitmap(true);
65 mDrawPaint.setAntiAlias(true);
Joe Onorato4be866d2010-10-10 11:26:02 -070066 mBlurPaint.setFilterBitmap(true);
67 mBlurPaint.setAntiAlias(true);
Michael Jurka5f1c5092010-09-03 14:15:02 -070068 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
69 mErasePaint.setFilterBitmap(true);
70 mErasePaint.setAntiAlias(true);
71 }
72
Daniel Sandlere4f98912013-06-25 15:13:26 -040073 public static HolographicOutlineHelper obtain(Context context) {
74 if (INSTANCE == null) {
75 INSTANCE = new HolographicOutlineHelper(context);
76 }
Daniel Sandlere572fe42013-06-12 22:46:02 -040077 return INSTANCE;
78 }
79
Michael Jurka5f1c5092010-09-03 14:15:02 -070080 /**
Winson Chung64a3cd42010-09-17 16:47:33 -070081 * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
82 * bitmap.
Michael Jurka5f1c5092010-09-03 14:15:02 -070083 */
Winson Chung64a3cd42010-09-17 16:47:33 -070084 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Sunny Goyal508da152014-08-14 10:53:27 -070085 int outlineColor) {
86 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
Peter Ng8db70002011-10-25 15:40:08 -070087 }
88 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Sunny Goyal508da152014-08-14 10:53:27 -070089 int outlineColor, boolean clipAlpha) {
Adam Cohen5bb50bd2010-12-03 11:39:55 -080090
91 // We start by removing most of the alpha channel so as to ignore shadows, and
92 // other types of partial transparency when defining the shape of the object
Michael Jurka8c3339b2012-06-14 16:18:21 -070093 if (clipAlpha) {
94 int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
95 srcDst.getPixels(srcBuffer,
96 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
97 for (int i = 0; i < srcBuffer.length; i++) {
98 final int alpha = srcBuffer[i] >>> 24;
99 if (alpha < 188) {
100 srcBuffer[i] = 0;
101 }
102 }
103 srcDst.setPixels(srcBuffer,
104 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
Peter Ng8db70002011-10-25 15:40:08 -0700105 }
Michael Jurka8c3339b2012-06-14 16:18:21 -0700106 Bitmap glowShape = srcDst.extractAlpha();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800107
Winson Chung64a3cd42010-09-17 16:47:33 -0700108 // calculate the outer blur first
Sunny Goyal508da152014-08-14 10:53:27 -0700109 mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700110 int[] outerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800111 Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800112
Sunny Goyal508da152014-08-14 10:53:27 -0700113 mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800114 int[] brightOutlineOffset = new int[2];
115 Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700116
117 // calculate the inner blur
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800118 srcDstCanvas.setBitmap(glowShape);
Winson Chung64a3cd42010-09-17 16:47:33 -0700119 srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
Sunny Goyal508da152014-08-14 10:53:27 -0700120 mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700121 int[] thickInnerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800122 Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700123
124 // mask out the inner blur
125 srcDstCanvas.setBitmap(thickInnerBlur);
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800126 srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
Winson Chung64a3cd42010-09-17 16:47:33 -0700127 -thickInnerBlurOffset[1], mErasePaint);
128 srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
129 mErasePaint);
130 srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
131 mErasePaint);
132
133 // draw the inner and outer blur
134 srcDstCanvas.setBitmap(srcDst);
Michael Jurka2a9e7062011-01-14 15:48:04 -0800135 srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
Sunny Goyal508da152014-08-14 10:53:27 -0700136 mDrawPaint.setColor(color);
Winson Chung64a3cd42010-09-17 16:47:33 -0700137 srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700138 mDrawPaint);
Winson Chung64a3cd42010-09-17 16:47:33 -0700139 srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700140 mDrawPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700141
Winson Chung64a3cd42010-09-17 16:47:33 -0700142 // draw the bright outline
Sunny Goyal508da152014-08-14 10:53:27 -0700143 mDrawPaint.setColor(outlineColor);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800144 srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700145 mDrawPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700146
Winson Chung64a3cd42010-09-17 16:47:33 -0700147 // cleanup
Adam Cohenaaf473c2011-08-03 12:02:47 -0700148 srcDstCanvas.setBitmap(null);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800149 brightOutline.recycle();
Winson Chung64a3cd42010-09-17 16:47:33 -0700150 thickOuterBlur.recycle();
151 thickInnerBlur.recycle();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800152 glowShape.recycle();
Michael Jurka5f1c5092010-09-03 14:15:02 -0700153 }
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800154
Sunny Goyal508da152014-08-14 10:53:27 -0700155 Bitmap createMediumDropShadow(BubbleTextView view) {
156 final Bitmap result = Bitmap.createBitmap(
157 view.getWidth() + shadowBitmapPadding + shadowBitmapPadding,
158 view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset,
159 Bitmap.Config.ARGB_8888);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800160
Sunny Goyal508da152014-08-14 10:53:27 -0700161 mCanvas.setBitmap(result);
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800162
Sunny Goyal508da152014-08-14 10:53:27 -0700163 final Rect clipRect = sTempRect;
164 view.getDrawingRect(sTempRect);
165 // adjust the clip rect so that we don't include the text label
166 clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V
167 + view.getLayout().getLineTop(0);
Peter Ng8db70002011-10-25 15:40:08 -0700168
Sunny Goyal508da152014-08-14 10:53:27 -0700169 // Draw the View into the bitmap.
170 // The translate of scrollX and scrollY is necessary when drawing TextViews, because
171 // they set scrollX and scrollY to large values to achieve centered text
172 mCanvas.save();
173 mCanvas.scale(view.getScaleX(), view.getScaleY(),
174 view.getWidth() / 2 + shadowBitmapPadding,
175 view.getHeight() / 2 + shadowBitmapPadding);
176 mCanvas.translate(-view.getScrollX() + shadowBitmapPadding,
177 -view.getScrollY() + shadowBitmapPadding);
178 mCanvas.clipRect(clipRect, Op.REPLACE);
179 view.draw(mCanvas);
180 mCanvas.restore();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800181
Sunny Goyal508da152014-08-14 10:53:27 -0700182 int[] blurOffst = new int[2];
183 mBlurPaint.setMaskFilter(mShaowBlurMaskFilter);
184 Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst);
185
186 mCanvas.save();
187 mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
188 mCanvas.translate(blurOffst[0], blurOffst[1]);
189
190 mDrawPaint.setColor(Color.BLACK);
191 mDrawPaint.setAlpha(30);
192 mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint);
193
194 mDrawPaint.setAlpha(60);
195 mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint);
196 mCanvas.restore();
197
198 mCanvas.setBitmap(null);
199 blurBitmap.recycle();
200
201 return result;
202 }
Winson Chung7da10252010-10-28 16:07:04 -0700203}