CircularBitmapDrawable.java revision 09f46006437e7de33afdb51192bf0bdc08e97040
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, final float alpha) { 113 // Draw bitmap through shader first. 114 BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, 115 TileMode.CLAMP); 116 sMatrix.reset(); 117 118 // Fit bitmap to bounds. 119 float scale = Math.max((float) dst.width() / src.width(), 120 (float) dst.height() / src.height()); 121 sMatrix.postScale(scale, scale); 122 123 // Translate bitmap to dst bounds. 124 sMatrix.postTranslate(dst.left, dst.top); 125 126 shader.setLocalMatrix(sMatrix); 127 mBitmapPaint.setShader(shader); 128 int oldAlpha = mBitmapPaint.getAlpha(); 129 mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); 130 canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, 131 mBitmapPaint); 132 mBitmapPaint.setAlpha(oldAlpha); 133 134 // Then draw the border. 135 canvas.drawCircle(dst.centerX(), dst.centerY(), 136 dst.width() / 2f - mBorderWidth / 2, mBorderPaint); 137 } 138 139 @Override 140 public void setAlpha(int alpha) { 141 super.setAlpha(alpha); 142 143 final int old = mBitmapPaint.getAlpha(); 144 mBitmapPaint.setAlpha(alpha); 145 if (alpha != old) { 146 invalidateSelf(); 147 } 148 } 149 150 @Override 151 public void setColorFilter(ColorFilter cf) { 152 super.setColorFilter(cf); 153 mPaint.setColorFilter(cf); 154 } 155} 156