HolographicOutlineHelper.java revision 4be866d3a1665aa2098cb5d38d535b1ad1aab6d6
15f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka/*
25f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * Copyright (C) 2008 The Android Open Source Project
35f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka *
45f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
55f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * you may not use this file except in compliance with the License.
65f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * You may obtain a copy of the License at
75f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka *
85f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
95f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka *
105f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * Unless required by applicable law or agreed to in writing, software
115f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
125f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * See the License for the specific language governing permissions and
145f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka * limitations under the License.
155f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka */
165f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
175f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkapackage com.android.launcher2;
185f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
195f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.Bitmap;
205f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.BlurMaskFilter;
215f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.Canvas;
224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.MaskFilter;
235f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.Paint;
245f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.PorterDuff;
255f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkaimport android.graphics.PorterDuffXfermode;
264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.Rect;
274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onoratoimport android.graphics.TableMaskFilter;
285f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurkapublic class HolographicOutlineHelper {
305f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private final Paint mHolographicPaint = new Paint();
314be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private final Paint mBlurPaint = new Paint();
325f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    private final Paint mErasePaint = new Paint();
335f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
344be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sLargeGlowBlurMaskFilter = new BlurMaskFilter(
354be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            10.0f, BlurMaskFilter.Blur.OUTER);
364be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sThickOuterBlurMaskFilter = new BlurMaskFilter(
374be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            6.0f, BlurMaskFilter.Blur.OUTER);
384be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sMediumOuterBlurMaskFilter = new BlurMaskFilter(
394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            2.0f, BlurMaskFilter.Blur.OUTER);
404be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sThinOuterBlurMaskFilter = new BlurMaskFilter(
414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            1.0f, BlurMaskFilter.Blur.OUTER);
424be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
434be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sThickInnerBlurMaskFilter = new BlurMaskFilter(
444be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            4.0f, BlurMaskFilter.Blur.NORMAL);
454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final BlurMaskFilter sThinInnerBlurMaskFilter = new BlurMaskFilter(
464be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            1.0f, BlurMaskFilter.Blur.INNER);
474be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
484be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final MaskFilter sFineClipTable = TableMaskFilter.CreateClipTable(0, 20);
494be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private static final MaskFilter sCoarseClipTable = TableMaskFilter.CreateClipTable(0, 200);
504be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
514be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    private int[] mTempOffset = new int[2];
525f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
5364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung    HolographicOutlineHelper() {
545f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mHolographicPaint.setFilterBitmap(true);
555f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mHolographicPaint.setAntiAlias(true);
564be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setFilterBitmap(true);
574be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setAntiAlias(true);
585f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
595f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mErasePaint.setFilterBitmap(true);
605f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        mErasePaint.setAntiAlias(true);
615f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
625f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
635f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    /**
645f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
655f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * pages.
665f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     */
675f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float highlightAlphaInterpolator(float r) {
6864a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        float maxAlpha = 0.8f;
69e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung        return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
705f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
715f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
725f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    /**
735f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     * Returns the interpolated view alpha for the effect we want when scrolling pages.
745f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     */
755f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    public float viewAlphaInterpolator(float r) {
76e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung        final float pivot = 0.95f;
775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        if (r < pivot) {
78e8878e3c5ac2b426be931018493ce82bd9c90378Winson Chung            return (float) Math.pow(r / pivot, 1.5f);
795f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        } else {
805f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka            return 1.0f;
815f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka        }
825f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
835f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
844be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    void applyGlow(Bitmap bitmap, Canvas canvas, int color) {
854be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setMaskFilter(sThickOuterBlurMaskFilter);
864be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap glow = bitmap.extractAlpha(mBlurPaint, mTempOffset);
874be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
884be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Use the clip table to make the glow heavier closer to the outline
894be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mHolographicPaint.setMaskFilter(sCoarseClipTable);
904be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mHolographicPaint.setAlpha(150);
914be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mHolographicPaint.setColor(color);
924be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        canvas.drawBitmap(glow, mTempOffset[0], mTempOffset[1], mHolographicPaint);
934be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        glow.recycle();
944be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
954be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
964be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    /**
974be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     * Draws a solid outline around a bitmap, erasing the original pixels.
984be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     *
994be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     * @param bitmap The bitmap to modify
1004be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     * @param canvas A canvas on the bitmap
1014be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     * @param color The color to draw the outline and glow in
1024be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     * @param removeOrig If true, punch out the original pixels to just leave the outline
1034be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato     */
1044be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    void applyExpensiveOuterOutline(Bitmap bitmap, Canvas canvas, int color, boolean removeOrig) {
1054be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap originalImage = null;
1064be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (removeOrig) {
1074be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            originalImage = bitmap.extractAlpha();
1084be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
1094be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1104be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Compute an outer blur on the original bitmap
1114be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter);
1124be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap outline = bitmap.extractAlpha(mBlurPaint, mTempOffset);
1134be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1144be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // Paint the blurred bitmap back into the canvas. Using the clip table causes any alpha
1154be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // pixels above a certain threshold to be rounded up to be fully opaque. This gives the
1164be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        // effect of a thick outline, with a slight blur on the edge
1174be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mHolographicPaint.setColor(color);
1184be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mHolographicPaint.setMaskFilter(sFineClipTable);
1194be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        canvas.drawBitmap(outline, mTempOffset[0], mTempOffset[1], mHolographicPaint);
1204be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        outline.recycle();
1214be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1224be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        if (removeOrig) {
1234be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            // Finally, punch out the original pixels, leaving just the outline
1244be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            canvas.drawBitmap(originalImage, 0, 0, mErasePaint);
1254be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato            originalImage.recycle();
1264be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        }
1274be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato    }
1284be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato
1295f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    /**
13064a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung     * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
13164a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung     * bitmap.
1325f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka     */
13364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung    void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
13464a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung            int outlineColor) {
13564a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // calculate the outer blur first
1364be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setMaskFilter(sThickOuterBlurMaskFilter);
13764a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        int[] outerBlurOffset = new int[2];
1384be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset);
1394be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter);
14064a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        int[] thinOuterBlurOffset = new int[2];
1414be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap thinOuterBlur = srcDst.extractAlpha(mBlurPaint, thinOuterBlurOffset);
1425f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
14364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // calculate the inner blur
14464a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
1454be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        mBlurPaint.setMaskFilter(sThickInnerBlurMaskFilter);
14664a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        int[] thickInnerBlurOffset = new int[2];
1474be866d3a1665aa2098cb5d38d535b1ad1aab6d6Joe Onorato        Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset);
1485f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
14964a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // mask out the inner blur
15064a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.setBitmap(thickInnerBlur);
15164a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
15264a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                -thickInnerBlurOffset[1], mErasePaint);
15364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
15464a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                mErasePaint);
15564a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
15664a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                mErasePaint);
1575f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka
15864a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // draw the inner and outer blur
15964a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.setBitmap(srcDst);
16064a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
16164a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        mHolographicPaint.setColor(color);
16264a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
16364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                mHolographicPaint);
16464a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
16564a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                mHolographicPaint);
16664a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung
16764a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // draw the bright outline
16864a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        mHolographicPaint.setColor(outlineColor);
16964a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        srcDstCanvas.drawBitmap(thinOuterBlur, thinOuterBlurOffset[0], thinOuterBlurOffset[1],
17064a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung                mHolographicPaint);
17164a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung
17264a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        // cleanup
17364a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        thinOuterBlur.recycle();
17464a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        thickOuterBlur.recycle();
17564a3cd4f204dd5f3676249d50aa0881b2e279b1fWinson Chung        thickInnerBlur.recycle();
1765f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka    }
1775f1c509d5ad1954a7e38e77db4d5f27c7345fd39Michael Jurka}