CircularBitmapDrawable.java revision c5644927c0e7e121049b063046296ee8a59a4b37
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 Rect bounds = getBounds(); 102 if (drawable.getInnerDrawable() instanceof BitmapDrawable) { 103 BitmapDrawable placeholder = 104 (BitmapDrawable) drawable.getInnerDrawable(); 105 Bitmap bitmap = placeholder.getBitmap(); 106 float alpha = placeholder.getPaint().getAlpha() / 255f; 107 sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); 108 onDrawCircularBitmap(bitmap, canvas, sRect, bounds, alpha); 109 } else { 110 super.onDrawPlaceholderOrProgress(canvas, drawable); 111 } 112 113 // Then draw the border. 114 canvas.drawCircle(bounds.centerX(), bounds.centerY(), 115 bounds.width() / 2f - mBorderWidth / 2, mBorderPaint); 116 } 117 118 /** 119 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 120 * BitmapShader. 121 */ 122 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 123 final Rect src, final Rect dst) { 124 onDrawCircularBitmap(bitmap, canvas, src, dst, 1f); 125 } 126 127 /** 128 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 129 * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by. 130 */ 131 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 132 final Rect src, final Rect dst, final float alpha) { 133 // Draw bitmap through shader first. 134 BitmapShader shader = (BitmapShader) mBitmapPaint.getShader(); 135 if (shader == null || mShaderBitmap != bitmap) { 136 shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); 137 mShaderBitmap = bitmap; 138 } 139 140 sMatrix.reset(); 141 // Fit bitmap to bounds. 142 float scale = Math.max((float) dst.width() / src.width(), 143 (float) dst.height() / src.height()); 144 sMatrix.postScale(scale, scale); 145 // Translate bitmap to dst bounds. 146 sMatrix.postTranslate(dst.left, dst.top); 147 shader.setLocalMatrix(sMatrix); 148 mBitmapPaint.setShader(shader); 149 150 int oldAlpha = mBitmapPaint.getAlpha(); 151 mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); 152 canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, 153 mBitmapPaint); 154 mBitmapPaint.setAlpha(oldAlpha); 155 156 // Then draw the border. 157 canvas.drawCircle(dst.centerX(), dst.centerY(), 158 dst.width() / 2f - mBorderWidth / 2, mBorderPaint); 159 } 160 161 @Override 162 public void setAlpha(int alpha) { 163 super.setAlpha(alpha); 164 165 final int old = mBitmapPaint.getAlpha(); 166 mBitmapPaint.setAlpha(alpha); 167 if (alpha != old) { 168 invalidateSelf(); 169 } 170 } 171 172 @Override 173 public void setColorFilter(ColorFilter cf) { 174 super.setColorFilter(cf); 175 mPaint.setColorFilter(cf); 176 } 177} 178