CircularBitmapDrawable.java revision 5c102db5a1a16a2adcc7728693a6e65a981f91ae
1/* 2 * Copyright (C) 2014 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 17package com.android.bitmap.drawable; 18 19import android.content.res.Resources; 20import android.graphics.Bitmap; 21import android.graphics.BitmapShader; 22import android.graphics.Canvas; 23import android.graphics.Color; 24import android.graphics.ColorFilter; 25import android.graphics.Matrix; 26import android.graphics.Paint; 27import android.graphics.Paint.Style; 28import android.graphics.Rect; 29import android.graphics.Shader.TileMode; 30import android.graphics.drawable.BitmapDrawable; 31 32import com.android.bitmap.BitmapCache; 33 34/** 35 * Custom BasicBitmapDrawable implementation for circular images. 36 * 37 * This draws all bitmaps as a circle with an optional border stroke. 38 */ 39public class CircularBitmapDrawable extends ExtendedBitmapDrawable { 40 private static Matrix sMatrix = new Matrix(); 41 42 private final Paint mBitmapPaint = new Paint(); 43 private final Paint mBorderPaint = new Paint(); 44 45 private float mBorderWidth; 46 private Bitmap mShaderBitmap; 47 48 public CircularBitmapDrawable(Resources res, 49 BitmapCache cache, boolean limitDensity) { 50 this(res, cache, limitDensity, null); 51 } 52 53 public CircularBitmapDrawable(Resources res, 54 BitmapCache cache, boolean limitDensity, ExtendedOptions opts) { 55 super(res, cache, limitDensity, opts); 56 57 mBitmapPaint.setAntiAlias(true); 58 mBitmapPaint.setFilterBitmap(true); 59 mBitmapPaint.setDither(true); 60 61 mBorderPaint.setColor(Color.TRANSPARENT); 62 mBorderPaint.setStyle(Style.STROKE); 63 mBorderPaint.setStrokeWidth(mBorderWidth); 64 mBorderPaint.setAntiAlias(true); 65 } 66 67 /** 68 * Set the border stroke width of this drawable. 69 */ 70 public void setBorderWidth(final float borderWidth) { 71 final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth; 72 mBorderPaint.setStrokeWidth(borderWidth); 73 mBorderWidth = borderWidth; 74 75 if (changed) { 76 invalidateSelf(); 77 } 78 } 79 80 /** 81 * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable. 82 */ 83 public void setBorderColor(final int color) { 84 final boolean changed = mBorderPaint.getColor() != color; 85 mBorderPaint.setColor(color); 86 87 if (changed) { 88 invalidateSelf(); 89 } 90 } 91 92 @Override 93 protected void onDrawBitmap(final Canvas canvas, final Rect src, 94 final Rect dst) { 95 onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f); 96 } 97 98 @Override 99 protected void onDrawPlaceholderOrProgress(final Canvas canvas, 100 final TileDrawable drawable) { 101 BitmapDrawable placeholder = (BitmapDrawable) drawable.getInnerDrawable(); 102 Bitmap bitmap = placeholder.getBitmap(); 103 float alpha = placeholder.getPaint().getAlpha() / 255f; 104 sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); 105 onDrawCircularBitmap(bitmap, canvas, sRect, getBounds(), alpha); 106 } 107 108 /** 109 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 110 * BitmapShader. 111 */ 112 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 113 final Rect src, final Rect dst) { 114 onDrawCircularBitmap(bitmap, canvas, src, dst, 1f); 115 } 116 117 /** 118 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 119 * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by. 120 */ 121 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 122 final Rect src, final Rect dst, final float alpha) { 123 // Draw bitmap through shader first. 124 BitmapShader shader = (BitmapShader) mBitmapPaint.getShader(); 125 if (shader == null || mShaderBitmap != bitmap) { 126 shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); 127 mShaderBitmap = bitmap; 128 } 129 130 sMatrix.reset(); 131 // Fit bitmap to bounds. 132 float scale = Math.max((float) dst.width() / src.width(), 133 (float) dst.height() / src.height()); 134 sMatrix.postScale(scale, scale); 135 // Translate bitmap to dst bounds. 136 sMatrix.postTranslate(dst.left, dst.top); 137 shader.setLocalMatrix(sMatrix); 138 mBitmapPaint.setShader(shader); 139 140 int oldAlpha = mBitmapPaint.getAlpha(); 141 mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); 142 canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, 143 mBitmapPaint); 144 mBitmapPaint.setAlpha(oldAlpha); 145 146 // Then draw the border. 147 canvas.drawCircle(dst.centerX(), dst.centerY(), 148 dst.width() / 2f - mBorderWidth / 2, mBorderPaint); 149 } 150 151 @Override 152 public void setAlpha(int alpha) { 153 super.setAlpha(alpha); 154 155 final int old = mBitmapPaint.getAlpha(); 156 mBitmapPaint.setAlpha(alpha); 157 if (alpha != old) { 158 invalidateSelf(); 159 } 160 } 161 162 @Override 163 public void setColorFilter(ColorFilter cf) { 164 super.setColorFilter(cf); 165 mPaint.setColorFilter(cf); 166 } 167} 168