CircularBitmapDrawable.java revision 8dd5cac690e42383d6bfc20af04eb781abd14f54
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 47 public CircularBitmapDrawable(Resources res, 48 BitmapCache cache, boolean limitDensity) { 49 this(res, cache, limitDensity, null); 50 } 51 52 public CircularBitmapDrawable(Resources res, 53 BitmapCache cache, boolean limitDensity, ExtendedOptions opts) { 54 super(res, cache, limitDensity, opts); 55 56 mBitmapPaint.setAntiAlias(true); 57 mBitmapPaint.setFilterBitmap(true); 58 mBitmapPaint.setDither(true); 59 60 mBorderPaint.setColor(Color.TRANSPARENT); 61 mBorderPaint.setStyle(Style.STROKE); 62 mBorderPaint.setStrokeWidth(mBorderWidth); 63 mBorderPaint.setAntiAlias(true); 64 } 65 66 /** 67 * Set the border stroke width of this drawable. 68 */ 69 public void setBorderWidth(final float borderWidth) { 70 final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth; 71 mBorderPaint.setStrokeWidth(borderWidth); 72 mBorderWidth = borderWidth; 73 74 if (changed) { 75 invalidateSelf(); 76 } 77 } 78 79 /** 80 * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable. 81 */ 82 public void setBorderColor(final int color) { 83 final boolean changed = mBorderPaint.getColor() != color; 84 mBorderPaint.setColor(color); 85 86 if (changed) { 87 invalidateSelf(); 88 } 89 } 90 91 @Override 92 protected void onDrawBitmap(final Canvas canvas, final Rect src, 93 final Rect dst) { 94 onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f); 95 } 96 97 @Override 98 protected void onDrawPlaceholderOrProgress(final Canvas canvas, 99 final TileDrawable drawable) { 100 BitmapDrawable placeholder = (BitmapDrawable) drawable.getInnerDrawable(); 101 Bitmap bitmap = placeholder.getBitmap(); 102 float alpha = placeholder.getPaint().getAlpha() / 255f; 103 sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); 104 onDrawCircularBitmap(bitmap, canvas, sRect, getBounds(), alpha); 105 } 106 107 /** 108 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 109 * BitmapShader. 110 */ 111 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 112 final Rect src, final Rect dst) { 113 onDrawCircularBitmap(bitmap, canvas, src, dst, 1f); 114 } 115 116 /** 117 * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular 118 * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by. 119 */ 120 protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, 121 final Rect src, final Rect dst, final float alpha) { 122 // Draw bitmap through shader first. 123 BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, 124 TileMode.CLAMP); 125 sMatrix.reset(); 126 127 // Fit bitmap to bounds. 128 float scale = Math.max((float) dst.width() / src.width(), 129 (float) dst.height() / src.height()); 130 sMatrix.postScale(scale, scale); 131 132 // Translate bitmap to dst bounds. 133 sMatrix.postTranslate(dst.left, dst.top); 134 135 shader.setLocalMatrix(sMatrix); 136 mBitmapPaint.setShader(shader); 137 int oldAlpha = mBitmapPaint.getAlpha(); 138 mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); 139 canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, 140 mBitmapPaint); 141 mBitmapPaint.setAlpha(oldAlpha); 142 143 // Then draw the border. 144 canvas.drawCircle(dst.centerX(), dst.centerY(), 145 dst.width() / 2f - mBorderWidth / 2, mBorderPaint); 146 } 147 148 @Override 149 public void setAlpha(int alpha) { 150 super.setAlpha(alpha); 151 152 final int old = mBitmapPaint.getAlpha(); 153 mBitmapPaint.setAlpha(alpha); 154 if (alpha != old) { 155 invalidateSelf(); 156 } 157 } 158 159 @Override 160 public void setColorFilter(ColorFilter cf) { 161 super.setColorFilter(cf); 162 mPaint.setColorFilter(cf); 163 } 164} 165