blob: 5ff85d664c09f6832b34466befe227dc45cad996 [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;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070020import android.content.res.Resources;
Michael Jurka5f1c5092010-09-03 14:15:02 -070021import android.graphics.Bitmap;
22import android.graphics.BlurMaskFilter;
23import android.graphics.Canvas;
Sunny Goyal508da152014-08-14 10:53:27 -070024import android.graphics.Color;
Michael Jurka5f1c5092010-09-03 14:15:02 -070025import android.graphics.Paint;
Michael Jurka5f1c5092010-09-03 14:15:02 -070026import android.graphics.PorterDuff;
27import android.graphics.PorterDuffXfermode;
Sunny Goyal508da152014-08-14 10:53:27 -070028import android.graphics.Rect;
Sunny Goyal4fe5a372015-05-14 19:55:10 -070029import android.graphics.drawable.Drawable;
30import android.util.SparseArray;
Michael Jurka5f1c5092010-09-03 14:15:02 -070031
Sunny Goyal4fe5a372015-05-14 19:55:10 -070032/**
33 * Utility class to generate shadow and outline effect, which are used for click feedback
34 * and drag-n-drop respectively.
35 */
Michael Jurka5f1c5092010-09-03 14:15:02 -070036public class HolographicOutlineHelper {
Sunny Goyal508da152014-08-14 10:53:27 -070037
Sunny Goyal4fe5a372015-05-14 19:55:10 -070038 private static HolographicOutlineHelper sInstance;
Sunny Goyal508da152014-08-14 10:53:27 -070039
40 private final Canvas mCanvas = new Canvas();
41 private final Paint mDrawPaint = new Paint();
Joe Onorato4be866d2010-10-10 11:26:02 -070042 private final Paint mBlurPaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070043 private final Paint mErasePaint = new Paint();
Michael Jurka5f1c5092010-09-03 14:15:02 -070044
Sunny Goyal508da152014-08-14 10:53:27 -070045 private final BlurMaskFilter mMediumOuterBlurMaskFilter;
46 private final BlurMaskFilter mThinOuterBlurMaskFilter;
47 private final BlurMaskFilter mMediumInnerBlurMaskFilter;
Joe Onorato4be866d2010-10-10 11:26:02 -070048
Sunny Goyal4fe5a372015-05-14 19:55:10 -070049 private final BlurMaskFilter mShadowBlurMaskFilter;
Adam Cohen5bb50bd2010-12-03 11:39:55 -080050
Sunny Goyal4fe5a372015-05-14 19:55:10 -070051 // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps
52 private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4);
Daniel Sandlere572fe42013-06-12 22:46:02 -040053
Daniel Sandlere4f98912013-06-25 15:13:26 -040054 private HolographicOutlineHelper(Context context) {
Sunny Goyal4fe5a372015-05-14 19:55:10 -070055 Resources res = context.getResources();
Patrick Dubroy8e58e912010-10-14 13:21:48 -070056
Sunny Goyal4fe5a372015-05-14 19:55:10 -070057 float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline);
58 mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER);
59 mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL);
Daniel Sandlere572fe42013-06-12 22:46:02 -040060
Sunny Goyal4fe5a372015-05-14 19:55:10 -070061 mThinOuterBlurMaskFilter = new BlurMaskFilter(
62 res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER);
63
64 mShadowBlurMaskFilter = new BlurMaskFilter(
65 res.getDimension(R.dimen.blur_size_click_shadow), BlurMaskFilter.Blur.NORMAL);
Sunny Goyal508da152014-08-14 10:53:27 -070066
67 mDrawPaint.setFilterBitmap(true);
68 mDrawPaint.setAntiAlias(true);
Joe Onorato4be866d2010-10-10 11:26:02 -070069 mBlurPaint.setFilterBitmap(true);
70 mBlurPaint.setAntiAlias(true);
Michael Jurka5f1c5092010-09-03 14:15:02 -070071 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
72 mErasePaint.setFilterBitmap(true);
73 mErasePaint.setAntiAlias(true);
74 }
75
Daniel Sandlere4f98912013-06-25 15:13:26 -040076 public static HolographicOutlineHelper obtain(Context context) {
Sunny Goyal4fe5a372015-05-14 19:55:10 -070077 if (sInstance == null) {
78 sInstance = new HolographicOutlineHelper(context);
Daniel Sandlere4f98912013-06-25 15:13:26 -040079 }
Sunny Goyal4fe5a372015-05-14 19:55:10 -070080 return sInstance;
Daniel Sandlere572fe42013-06-12 22:46:02 -040081 }
82
Michael Jurka5f1c5092010-09-03 14:15:02 -070083 /**
Winson Chung64a3cd42010-09-17 16:47:33 -070084 * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
85 * bitmap.
Michael Jurka5f1c5092010-09-03 14:15:02 -070086 */
Winson Chung64a3cd42010-09-17 16:47:33 -070087 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Sunny Goyal508da152014-08-14 10:53:27 -070088 int outlineColor) {
89 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
Peter Ng8db70002011-10-25 15:40:08 -070090 }
91 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
Sunny Goyal508da152014-08-14 10:53:27 -070092 int outlineColor, boolean clipAlpha) {
Adam Cohen5bb50bd2010-12-03 11:39:55 -080093
94 // We start by removing most of the alpha channel so as to ignore shadows, and
95 // other types of partial transparency when defining the shape of the object
Michael Jurka8c3339b2012-06-14 16:18:21 -070096 if (clipAlpha) {
97 int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
98 srcDst.getPixels(srcBuffer,
99 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
100 for (int i = 0; i < srcBuffer.length; i++) {
101 final int alpha = srcBuffer[i] >>> 24;
102 if (alpha < 188) {
103 srcBuffer[i] = 0;
104 }
105 }
106 srcDst.setPixels(srcBuffer,
107 0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
Peter Ng8db70002011-10-25 15:40:08 -0700108 }
Michael Jurka8c3339b2012-06-14 16:18:21 -0700109 Bitmap glowShape = srcDst.extractAlpha();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800110
Winson Chung64a3cd42010-09-17 16:47:33 -0700111 // calculate the outer blur first
Sunny Goyal508da152014-08-14 10:53:27 -0700112 mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700113 int[] outerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800114 Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800115
Sunny Goyal508da152014-08-14 10:53:27 -0700116 mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800117 int[] brightOutlineOffset = new int[2];
118 Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700119
120 // calculate the inner blur
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800121 srcDstCanvas.setBitmap(glowShape);
Winson Chung64a3cd42010-09-17 16:47:33 -0700122 srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
Sunny Goyal508da152014-08-14 10:53:27 -0700123 mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
Winson Chung64a3cd42010-09-17 16:47:33 -0700124 int[] thickInnerBlurOffset = new int[2];
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800125 Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
Winson Chung64a3cd42010-09-17 16:47:33 -0700126
127 // mask out the inner blur
128 srcDstCanvas.setBitmap(thickInnerBlur);
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800129 srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
Winson Chung64a3cd42010-09-17 16:47:33 -0700130 -thickInnerBlurOffset[1], mErasePaint);
131 srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
132 mErasePaint);
133 srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
134 mErasePaint);
135
136 // draw the inner and outer blur
137 srcDstCanvas.setBitmap(srcDst);
Michael Jurka2a9e7062011-01-14 15:48:04 -0800138 srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
Sunny Goyal508da152014-08-14 10:53:27 -0700139 mDrawPaint.setColor(color);
Winson Chung64a3cd42010-09-17 16:47:33 -0700140 srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700141 mDrawPaint);
Winson Chung64a3cd42010-09-17 16:47:33 -0700142 srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700143 mDrawPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700144
Winson Chung64a3cd42010-09-17 16:47:33 -0700145 // draw the bright outline
Sunny Goyal508da152014-08-14 10:53:27 -0700146 mDrawPaint.setColor(outlineColor);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800147 srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
Sunny Goyal508da152014-08-14 10:53:27 -0700148 mDrawPaint);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700149
Winson Chung64a3cd42010-09-17 16:47:33 -0700150 // cleanup
Adam Cohenaaf473c2011-08-03 12:02:47 -0700151 srcDstCanvas.setBitmap(null);
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800152 brightOutline.recycle();
Winson Chung64a3cd42010-09-17 16:47:33 -0700153 thickOuterBlur.recycle();
154 thickInnerBlur.recycle();
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800155 glowShape.recycle();
Michael Jurka5f1c5092010-09-03 14:15:02 -0700156 }
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800157
Sunny Goyal508da152014-08-14 10:53:27 -0700158 Bitmap createMediumDropShadow(BubbleTextView view) {
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700159 Drawable icon = view.getIcon();
Sunny Goyal5537faa2015-05-19 17:35:07 -0700160 if (icon == null) {
161 return null;
162 }
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700163 Rect rect = icon.getBounds();
Michael Jurka38b4f7c2010-12-14 16:46:39 -0800164
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700165 int bitmapWidth = (int) (rect.width() * view.getScaleX());
166 int bitmapHeight = (int) (rect.height() * view.getScaleY());
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800167
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700168 int key = (bitmapWidth << 16) | bitmapHeight;
169 Bitmap cache = mBitmapCache.get(key);
170 if (cache == null) {
171 cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
172 mCanvas.setBitmap(cache);
173 mBitmapCache.put(key, cache);
174 } else {
175 mCanvas.setBitmap(cache);
176 mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
177 }
Peter Ng8db70002011-10-25 15:40:08 -0700178
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700179 mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
180 mCanvas.scale(view.getScaleX(), view.getScaleY());
181 mCanvas.translate(-rect.left, -rect.top);
182 icon.draw(mCanvas);
Sunny Goyal508da152014-08-14 10:53:27 -0700183 mCanvas.restore();
Sunny Goyal508da152014-08-14 10:53:27 -0700184 mCanvas.setBitmap(null);
Sunny Goyal508da152014-08-14 10:53:27 -0700185
Sunny Goyal4fe5a372015-05-14 19:55:10 -0700186 mBlurPaint.setMaskFilter(mShadowBlurMaskFilter);
187 return cache.extractAlpha(mBlurPaint, null);
Sunny Goyal508da152014-08-14 10:53:27 -0700188 }
Winson Chung7da10252010-10-28 16:07:04 -0700189}