Home | History | Annotate | Download | only in launcher3
      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.launcher3;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.BlurMaskFilter;
     22 import android.graphics.Canvas;
     23 import android.graphics.Color;
     24 import android.graphics.Paint;
     25 import android.graphics.PorterDuff;
     26 import android.graphics.PorterDuffXfermode;
     27 import android.graphics.Rect;
     28 import android.graphics.Region.Op;
     29 
     30 public class HolographicOutlineHelper {
     31 
     32     private static final Rect sTempRect = new Rect();
     33 
     34     private final Canvas mCanvas = new Canvas();
     35     private final Paint mDrawPaint = new Paint();
     36     private final Paint mBlurPaint = new Paint();
     37     private final Paint mErasePaint = new Paint();
     38 
     39     private final BlurMaskFilter mMediumOuterBlurMaskFilter;
     40     private final BlurMaskFilter mThinOuterBlurMaskFilter;
     41     private final BlurMaskFilter mMediumInnerBlurMaskFilter;
     42 
     43     private final BlurMaskFilter mShaowBlurMaskFilter;
     44     private final int mShadowOffset;
     45 
     46     /**
     47      * Padding used when creating shadow bitmap;
     48      */
     49     final int shadowBitmapPadding;
     50 
     51     static HolographicOutlineHelper INSTANCE;
     52 
     53     private HolographicOutlineHelper(Context context) {
     54         final float scale = LauncherAppState.getInstance().getScreenDensity();
     55 
     56         mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
     57         mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
     58         mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
     59 
     60         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);
     66         mBlurPaint.setFilterBitmap(true);
     67         mBlurPaint.setAntiAlias(true);
     68         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
     69         mErasePaint.setFilterBitmap(true);
     70         mErasePaint.setAntiAlias(true);
     71     }
     72 
     73     public static HolographicOutlineHelper obtain(Context context) {
     74         if (INSTANCE == null) {
     75             INSTANCE = new HolographicOutlineHelper(context);
     76         }
     77         return INSTANCE;
     78     }
     79 
     80     /**
     81      * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
     82      * bitmap.
     83      */
     84     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
     85             int outlineColor) {
     86         applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
     87     }
     88     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
     89             int outlineColor, boolean clipAlpha) {
     90 
     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
     93         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());
    105         }
    106         Bitmap glowShape = srcDst.extractAlpha();
    107 
    108         // calculate the outer blur first
    109         mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
    110         int[] outerBlurOffset = new int[2];
    111         Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
    112 
    113         mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
    114         int[] brightOutlineOffset = new int[2];
    115         Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
    116 
    117         // calculate the inner blur
    118         srcDstCanvas.setBitmap(glowShape);
    119         srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
    120         mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
    121         int[] thickInnerBlurOffset = new int[2];
    122         Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
    123 
    124         // mask out the inner blur
    125         srcDstCanvas.setBitmap(thickInnerBlur);
    126         srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
    127                 -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);
    135         srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
    136         mDrawPaint.setColor(color);
    137         srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
    138                 mDrawPaint);
    139         srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
    140                 mDrawPaint);
    141 
    142         // draw the bright outline
    143         mDrawPaint.setColor(outlineColor);
    144         srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
    145                 mDrawPaint);
    146 
    147         // cleanup
    148         srcDstCanvas.setBitmap(null);
    149         brightOutline.recycle();
    150         thickOuterBlur.recycle();
    151         thickInnerBlur.recycle();
    152         glowShape.recycle();
    153     }
    154 
    155     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);
    160 
    161         mCanvas.setBitmap(result);
    162 
    163         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);
    168 
    169         // 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();
    181 
    182         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     }
    203 }
    204