1 /* 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 17 package com.android.launcher2; 18 19 import android.graphics.Bitmap; 20 import android.graphics.BlurMaskFilter; 21 import android.graphics.Canvas; 22 import android.graphics.MaskFilter; 23 import android.graphics.Paint; 24 import android.graphics.PorterDuff; 25 import android.graphics.PorterDuffXfermode; 26 import android.graphics.TableMaskFilter; 27 28 public class HolographicOutlineHelper { 29 private final Paint mHolographicPaint = new Paint(); 30 private final Paint mBlurPaint = new Paint(); 31 private final Paint mErasePaint = new Paint(); 32 private final Paint mAlphaClipPaint = new Paint(); 33 34 public static final int MAX_OUTER_BLUR_RADIUS; 35 public static final int MIN_OUTER_BLUR_RADIUS; 36 37 private static final BlurMaskFilter sExtraThickOuterBlurMaskFilter; 38 private static final BlurMaskFilter sThickOuterBlurMaskFilter; 39 private static final BlurMaskFilter sMediumOuterBlurMaskFilter; 40 private static final BlurMaskFilter sThinOuterBlurMaskFilter; 41 private static final BlurMaskFilter sThickInnerBlurMaskFilter; 42 private static final BlurMaskFilter sExtraThickInnerBlurMaskFilter; 43 private static final BlurMaskFilter sMediumInnerBlurMaskFilter; 44 45 private static final int THICK = 0; 46 private static final int MEDIUM = 1; 47 private static final int EXTRA_THICK = 2; 48 49 static { 50 final float scale = LauncherApplication.getScreenDensity(); 51 52 MIN_OUTER_BLUR_RADIUS = (int) (scale * 1.0f); 53 MAX_OUTER_BLUR_RADIUS = (int) (scale * 12.0f); 54 55 sExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER); 56 sThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER); 57 sMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER); 58 sThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER); 59 sExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL); 60 sThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL); 61 sMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL); 62 } 63 64 private int[] mTempOffset = new int[2]; 65 66 HolographicOutlineHelper() { 67 mHolographicPaint.setFilterBitmap(true); 68 mHolographicPaint.setAntiAlias(true); 69 mBlurPaint.setFilterBitmap(true); 70 mBlurPaint.setAntiAlias(true); 71 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); 72 mErasePaint.setFilterBitmap(true); 73 mErasePaint.setAntiAlias(true); 74 MaskFilter alphaClipTable = TableMaskFilter.CreateClipTable(180, 255); 75 mAlphaClipPaint.setMaskFilter(alphaClipTable); 76 } 77 78 /** 79 * Returns the interpolated holographic highlight alpha for the effect we want when scrolling 80 * pages. 81 */ 82 public static float highlightAlphaInterpolator(float r) { 83 float maxAlpha = 0.6f; 84 return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f); 85 } 86 87 /** 88 * Returns the interpolated view alpha for the effect we want when scrolling pages. 89 */ 90 public static float viewAlphaInterpolator(float r) { 91 final float pivot = 0.95f; 92 if (r < pivot) { 93 return (float) Math.pow(r / pivot, 1.5f); 94 } else { 95 return 1.0f; 96 } 97 } 98 99 /** 100 * Applies a more expensive and accurate outline to whatever is currently drawn in a specified 101 * bitmap. 102 */ 103 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 104 int outlineColor, int thickness) { 105 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, mAlphaClipPaint, 106 thickness); 107 } 108 void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 109 int outlineColor, Paint alphaClipPaint, int thickness) { 110 111 // We start by removing most of the alpha channel so as to ignore shadows, and 112 // other types of partial transparency when defining the shape of the object 113 if (alphaClipPaint == null) { 114 alphaClipPaint = mAlphaClipPaint; 115 } 116 Bitmap glowShape = srcDst.extractAlpha(alphaClipPaint, mTempOffset); 117 118 // calculate the outer blur first 119 BlurMaskFilter outerBlurMaskFilter; 120 switch (thickness) { 121 case EXTRA_THICK: 122 outerBlurMaskFilter = sExtraThickOuterBlurMaskFilter; 123 break; 124 case THICK: 125 outerBlurMaskFilter = sThickOuterBlurMaskFilter; 126 break; 127 case MEDIUM: 128 outerBlurMaskFilter = sMediumOuterBlurMaskFilter; 129 break; 130 default: 131 throw new RuntimeException("Invalid blur thickness"); 132 } 133 mBlurPaint.setMaskFilter(outerBlurMaskFilter); 134 int[] outerBlurOffset = new int[2]; 135 Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset); 136 if (thickness == EXTRA_THICK) { 137 mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter); 138 } else { 139 mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter); 140 } 141 142 int[] brightOutlineOffset = new int[2]; 143 Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset); 144 145 // calculate the inner blur 146 srcDstCanvas.setBitmap(glowShape); 147 srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT); 148 BlurMaskFilter innerBlurMaskFilter; 149 switch (thickness) { 150 case EXTRA_THICK: 151 innerBlurMaskFilter = sExtraThickInnerBlurMaskFilter; 152 break; 153 case THICK: 154 innerBlurMaskFilter = sThickInnerBlurMaskFilter; 155 break; 156 case MEDIUM: 157 innerBlurMaskFilter = sMediumInnerBlurMaskFilter; 158 break; 159 default: 160 throw new RuntimeException("Invalid blur thickness"); 161 } 162 mBlurPaint.setMaskFilter(innerBlurMaskFilter); 163 int[] thickInnerBlurOffset = new int[2]; 164 Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset); 165 166 // mask out the inner blur 167 srcDstCanvas.setBitmap(thickInnerBlur); 168 srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0], 169 -thickInnerBlurOffset[1], mErasePaint); 170 srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), 171 mErasePaint); 172 srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], 173 mErasePaint); 174 175 // draw the inner and outer blur 176 srcDstCanvas.setBitmap(srcDst); 177 srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR); 178 mHolographicPaint.setColor(color); 179 srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1], 180 mHolographicPaint); 181 srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], 182 mHolographicPaint); 183 184 // draw the bright outline 185 mHolographicPaint.setColor(outlineColor); 186 srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], 187 mHolographicPaint); 188 189 // cleanup 190 srcDstCanvas.setBitmap(null); 191 brightOutline.recycle(); 192 thickOuterBlur.recycle(); 193 thickInnerBlur.recycle(); 194 glowShape.recycle(); 195 } 196 197 void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 198 int outlineColor) { 199 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK); 200 } 201 202 void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 203 int outlineColor) { 204 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK); 205 } 206 207 void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 208 int outlineColor, Paint alphaClipPaint) { 209 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, alphaClipPaint, 210 MEDIUM); 211 } 212 213 void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color, 214 int outlineColor) { 215 applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM); 216 } 217 218 } 219