/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bitmap.drawable; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import com.android.bitmap.BitmapCache; /** * Custom BasicBitmapDrawable implementation for circular images. * * This draws all bitmaps as a circle with an optional border stroke. */ public class CircularBitmapDrawable extends ExtendedBitmapDrawable { private final Paint mBitmapPaint = new Paint(); private final Paint mBorderPaint = new Paint(); private final Rect mRect = new Rect(); private final Matrix mMatrix = new Matrix(); private float mBorderWidth; private Bitmap mShaderBitmap; public CircularBitmapDrawable(Resources res, BitmapCache cache, boolean limitDensity) { this(res, cache, limitDensity, null); } public CircularBitmapDrawable(Resources res, BitmapCache cache, boolean limitDensity, ExtendedOptions opts) { super(res, cache, limitDensity, opts); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setFilterBitmap(true); mBitmapPaint.setDither(true); mBorderPaint.setColor(Color.TRANSPARENT); mBorderPaint.setStyle(Style.STROKE); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderPaint.setAntiAlias(true); } /** * Set the border stroke width of this drawable. */ public void setBorderWidth(final float borderWidth) { final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth; mBorderPaint.setStrokeWidth(borderWidth); mBorderWidth = borderWidth; if (changed) { invalidateSelf(); } } /** * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable. */ public void setBorderColor(final int color) { final boolean changed = mBorderPaint.getColor() != color; mBorderPaint.setColor(color); if (changed) { invalidateSelf(); } } @Override protected void onDrawBitmap(final Canvas canvas, final Rect src, final Rect dst) { onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f); } @Override protected void onDrawPlaceholderOrProgress(final Canvas canvas, final TileDrawable drawable) { Rect bounds = getBounds(); if (drawable.getInnerDrawable() instanceof BitmapDrawable) { BitmapDrawable placeholder = (BitmapDrawable) drawable.getInnerDrawable(); Bitmap bitmap = placeholder.getBitmap(); float alpha = placeholder.getPaint().getAlpha() / 255f; mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); onDrawCircularBitmap(bitmap, canvas, mRect, bounds, alpha); } else { super.onDrawPlaceholderOrProgress(canvas, drawable); } // Then draw the border. canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() / 2f - mBorderWidth / 2, mBorderPaint); } /** * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular * BitmapShader. */ protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, final Rect src, final Rect dst) { onDrawCircularBitmap(bitmap, canvas, src, dst, 1f); } /** * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by. */ protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas, final Rect src, final Rect dst, final float alpha) { // Draw bitmap through shader first. BitmapShader shader = (BitmapShader) mBitmapPaint.getShader(); if (shader == null || mShaderBitmap != bitmap) { shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); mShaderBitmap = bitmap; } mMatrix.reset(); // Fit bitmap to bounds. float scale = Math.max((float) dst.width() / src.width(), (float) dst.height() / src.height()); mMatrix.postScale(scale, scale); // Translate bitmap to dst bounds. mMatrix.postTranslate(dst.left, dst.top); shader.setLocalMatrix(mMatrix); mBitmapPaint.setShader(shader); int oldAlpha = mBitmapPaint.getAlpha(); mBitmapPaint.setAlpha((int) (oldAlpha * alpha)); canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2, mBitmapPaint); mBitmapPaint.setAlpha(oldAlpha); // Then draw the border. canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2f - mBorderWidth / 2, mBorderPaint); } @Override public void setAlpha(int alpha) { super.setAlpha(alpha); final int old = mBitmapPaint.getAlpha(); mBitmapPaint.setAlpha(alpha); if (alpha != old) { invalidateSelf(); } } @Override public void setColorFilter(ColorFilter cf) { super.setColorFilter(cf); mPaint.setColorFilter(cf); } }