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}