1 /* 2 * Copyright (C) 2016 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.launcher3.graphics; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Bitmap.Config; 21 import android.graphics.BlurMaskFilter; 22 import android.graphics.BlurMaskFilter.Blur; 23 import android.graphics.Canvas; 24 import android.graphics.Paint; 25 import android.graphics.RectF; 26 27 import com.android.launcher3.LauncherAppState; 28 import com.android.launcher3.util.Preconditions; 29 30 /** 31 * Utility class to add shadows to bitmaps. 32 */ 33 public class ShadowGenerator { 34 35 // Percent of actual icon size 36 private static final float HALF_DISTANCE = 0.5f; 37 private static final float BLUR_FACTOR = 0.5f/48; 38 39 // Percent of actual icon size 40 private static final float KEY_SHADOW_DISTANCE = 1f/48; 41 private static final int KEY_SHADOW_ALPHA = 61; 42 43 private static final int AMBIENT_SHADOW_ALPHA = 30; 44 45 private static final Object LOCK = new Object(); 46 // Singleton object guarded by {@link #LOCK} 47 private static ShadowGenerator sShadowGenerator; 48 49 private final int mIconSize; 50 51 private final Canvas mCanvas; 52 private final Paint mBlurPaint; 53 private final Paint mDrawPaint; 54 55 private ShadowGenerator() { 56 mIconSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; 57 mCanvas = new Canvas(); 58 mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 59 mBlurPaint.setMaskFilter(new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL)); 60 mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 61 } 62 63 public synchronized Bitmap recreateIcon(Bitmap icon) { 64 int[] offset = new int[2]; 65 Bitmap shadow = icon.extractAlpha(mBlurPaint, offset); 66 Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Config.ARGB_8888); 67 mCanvas.setBitmap(result); 68 69 // Draw ambient shadow 70 mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA); 71 mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint); 72 73 // Draw key shadow 74 mDrawPaint.setAlpha(KEY_SHADOW_ALPHA); 75 mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint); 76 77 // Draw the icon 78 mDrawPaint.setAlpha(255); 79 mCanvas.drawBitmap(icon, 0, 0, mDrawPaint); 80 81 mCanvas.setBitmap(null); 82 return result; 83 } 84 85 public static ShadowGenerator getInstance() { 86 Preconditions.assertNonUiThread(); 87 synchronized (LOCK) { 88 if (sShadowGenerator == null) { 89 sShadowGenerator = new ShadowGenerator(); 90 } 91 } 92 return sShadowGenerator; 93 } 94 95 /** 96 * Returns the minimum amount by which an icon with {@param bounds} should be scaled 97 * so that the shadows do not get clipped. 98 */ 99 public static float getScaleForBounds(RectF bounds) { 100 float scale = 1; 101 102 // For top, left & right, we need same space. 103 float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top); 104 if (minSide < BLUR_FACTOR) { 105 scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide); 106 } 107 108 float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE; 109 if (bounds.bottom < bottomSpace) { 110 scale = Math.min(scale, (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom)); 111 } 112 return scale; 113 } 114 } 115