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 17 package com.android.bitmap.drawable; 18 19 import android.content.res.Resources; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapShader; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.ColorFilter; 25 import android.graphics.Matrix; 26 import android.graphics.Paint; 27 import android.graphics.Paint.Style; 28 import android.graphics.Rect; 29 import android.graphics.Shader.TileMode; 30 import android.graphics.drawable.BitmapDrawable; 31 32 import 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 */ 39 public class CircularBitmapDrawable extends ExtendedBitmapDrawable { 40 private final Paint mBitmapPaint = new Paint(); 41 private final Paint mBorderPaint = new Paint(); 42 private final Rect mRect = new Rect(); 43 private final Matrix mMatrix = new Matrix(); 44 45 private float mBorderWidth; 46 private Bitmap mShaderBitmap; 47 CircularBitmapDrawable(Resources res, BitmapCache cache, boolean limitDensity)48 public CircularBitmapDrawable(Resources res, 49 BitmapCache cache, boolean limitDensity) { 50 this(res, cache, limitDensity, null); 51 } 52 CircularBitmapDrawable(Resources res, BitmapCache cache, boolean limitDensity, ExtendedOptions opts)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 */ setBorderWidth(final float borderWidth)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 */ setBorderColor(final int color)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 onDrawBitmap(final Canvas canvas, final Rect src, final Rect dst)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 onDrawPlaceholderOrProgress(final Canvas canvas, final TileDrawable drawable)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 mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); 108 onDrawCircularBitmap(bitmap, canvas, mRect, 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 */ onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, final Rect src, final Rect dst)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 */ onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, final Rect src, final Rect dst, final float alpha)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 mMatrix.reset(); 141 // Fit bitmap to bounds. 142 float scale = Math.max((float) dst.width() / src.width(), 143 (float) dst.height() / src.height()); 144 mMatrix.postScale(scale, scale); 145 // Translate bitmap to dst bounds. 146 mMatrix.postTranslate(dst.left, dst.top); 147 shader.setLocalMatrix(mMatrix); 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 setAlpha(int alpha)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 setColorFilter(ColorFilter cf)173 public void setColorFilter(ColorFilter cf) { 174 super.setColorFilter(cf); 175 mPaint.setColorFilter(cf); 176 } 177 } 178