1ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee/*
2ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * Copyright (C) 2014 The Android Open Source Project
3ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee *
4ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
5ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * you may not use this file except in compliance with the License.
6ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * You may obtain a copy of the License at
7ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee *
8ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
9ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee *
10ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * Unless required by applicable law or agreed to in writing, software
11ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
12ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * See the License for the specific language governing permissions and
14ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * limitations under the License.
15ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee */
16ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leepackage android.support.v4.graphics.drawable;
17ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
18ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.content.res.Resources;
19ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.Bitmap;
20ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.BitmapShader;
21ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.Canvas;
22ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.ColorFilter;
233a1a3b98f843ab5c72644da9addb9473d895a826Chris Craikimport android.graphics.Matrix;
24ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.Paint;
25ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.PixelFormat;
26ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.Rect;
27ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.RectF;
28ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.Shader;
29ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.graphics.drawable.Drawable;
30ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.util.DisplayMetrics;
31ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Leeimport android.view.Gravity;
32ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
33ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee/**
34ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * A Drawable that wraps a bitmap and can be drawn with rounded corners. You can create a
35ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * RoundedBitmapDrawable from a file path, an input stream, or from a
36ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * {@link android.graphics.Bitmap} object.
37ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * <p>
38ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * Also see the {@link android.graphics.Bitmap} class, which handles the management and
39ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * transformation of raw bitmap graphics, and should be used when drawing to a
40ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * {@link android.graphics.Canvas}.
41ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee * </p>
42ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee */
439038afb70d70650d87659bb252181ca8645670b8Chris Craikpublic abstract class RoundedBitmapDrawable extends Drawable {
44ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private static final int DEFAULT_PAINT_FLAGS =
453a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
463a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik    final Bitmap mBitmap;
47ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
48ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private int mGravity = Gravity.FILL;
493a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik    private final Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
503a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik    private final BitmapShader mBitmapShader;
513a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik    private final Matrix mShaderMatrix = new Matrix();
52ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private float mCornerRadius;
53ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
549038afb70d70650d87659bb252181ca8645670b8Chris Craik    final Rect mDstRect = new Rect();   // Gravity.apply() sets this
553a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik    private final RectF mDstRectF = new RectF();
56ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
57ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private boolean mApplyGravity = true;
589265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    private boolean mIsCircular;
59ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
609265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    // These are scaled to match the target density.
61ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private int mBitmapWidth;
62ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private int mBitmapHeight;
63ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
64ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
65ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Returns the paint used to render this drawable.
66ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
67ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public final Paint getPaint() {
68ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mPaint;
69ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
70ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
71ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
72ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Returns the bitmap used by this drawable to render. May be null.
73ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
74ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public final Bitmap getBitmap() {
75ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mBitmap;
76ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
77ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
78ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    private void computeBitmapSize() {
79ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity);
80ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity);
81ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
82ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
83ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
84ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Set the density scale at which this drawable will be rendered. This
85ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * method assumes the drawable will be rendered at the same density as the
86ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * specified canvas.
87ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
88ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param canvas The Canvas from which the density scale must be obtained.
89ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
90ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#setDensity(int)
91ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#getDensity()
92ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
93ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setTargetDensity(Canvas canvas) {
94ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        setTargetDensity(canvas.getDensity());
95ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
96ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
97ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
98ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Set the density scale at which this drawable will be rendered.
99ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
100ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param metrics The DisplayMetrics indicating the density scale for this drawable.
101ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
102ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#setDensity(int)
103ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#getDensity()
104ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
105ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setTargetDensity(DisplayMetrics metrics) {
106ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        setTargetDensity(metrics.densityDpi);
107ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
108ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
109ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
110ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Set the density at which this drawable will be rendered.
111ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
112ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param density The density scale for this drawable.
113ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
114ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#setDensity(int)
115ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.graphics.Bitmap#getDensity()
116ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
117ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setTargetDensity(int density) {
118ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (mTargetDensity != density) {
119ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
120ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            if (mBitmap != null) {
121ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee                computeBitmapSize();
122ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            }
123ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            invalidateSelf();
124ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
125ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
126ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
127ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
128ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Get the gravity used to position/stretch the bitmap within its bounds.
129ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
130ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @return the gravity applied to the bitmap
131ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
132ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.view.Gravity
133ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
134ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public int getGravity() {
135ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mGravity;
136ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
137ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
138ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
139ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Set the gravity used to position/stretch the bitmap within its bounds.
140ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
141ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param gravity the gravity
142ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
143ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see android.view.Gravity
144ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
145ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setGravity(int gravity) {
146ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (mGravity != gravity) {
147ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mGravity = gravity;
148ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mApplyGravity = true;
149ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            invalidateSelf();
150ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
151ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
152ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
153ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
154ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Enables or disables the mipmap hint for this drawable's bitmap.
155ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * See {@link Bitmap#setHasMipMap(boolean)} for more information.
156ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
157ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * If the bitmap is null, or the current API version does not support setting a mipmap hint,
158ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * calling this method has no effect.
159ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
160ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param mipMap True if the bitmap should use mipmaps, false otherwise.
161ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
162ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see #hasMipMap()
163ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
164ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setMipMap(boolean mipMap) {
1659038afb70d70650d87659bb252181ca8645670b8Chris Craik        throw new UnsupportedOperationException(); // must be overridden in subclasses
166ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
167ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
168ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
169ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Indicates whether the mipmap hint is enabled on this drawable's bitmap.
170ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
171ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @return True if the mipmap hint is set, false otherwise. If the bitmap
172ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *         is null, this method always returns false.
173ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
174ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see #setMipMap(boolean)
175ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
176ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public boolean hasMipMap() {
1779038afb70d70650d87659bb252181ca8645670b8Chris Craik        throw new UnsupportedOperationException(); // must be overridden in subclasses
178ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
179ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
180ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
181ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Enables or disables anti-aliasing for this drawable. Anti-aliasing affects
182ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * the edges of the bitmap only so it applies only when the drawable is rotated.
183ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
184ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @param aa True if the bitmap should be anti-aliased, false otherwise.
185ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
186ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see #hasAntiAlias()
187ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
188ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setAntiAlias(boolean aa) {
189ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mPaint.setAntiAlias(aa);
190ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        invalidateSelf();
191ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
192ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
193ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
194ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Indicates whether anti-aliasing is enabled for this drawable.
195ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
196ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @return True if anti-aliasing is enabled, false otherwise.
197ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     *
198ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @see #setAntiAlias(boolean)
199ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
200ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public boolean hasAntiAlias() {
201ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mPaint.isAntiAlias();
202ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
203ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
204ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
205ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setFilterBitmap(boolean filter) {
206ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mPaint.setFilterBitmap(filter);
207ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        invalidateSelf();
208ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
209ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
210ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
211ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setDither(boolean dither) {
212ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mPaint.setDither(dither);
213ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        invalidateSelf();
214ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
215ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
2169038afb70d70650d87659bb252181ca8645670b8Chris Craik    void gravityCompatApply(int gravity, int bitmapWidth, int bitmapHeight,
2179038afb70d70650d87659bb252181ca8645670b8Chris Craik            Rect bounds, Rect outRect) {
2189038afb70d70650d87659bb252181ca8645670b8Chris Craik        throw new UnsupportedOperationException();
2199038afb70d70650d87659bb252181ca8645670b8Chris Craik    }
2209038afb70d70650d87659bb252181ca8645670b8Chris Craik
2219038afb70d70650d87659bb252181ca8645670b8Chris Craik    void updateDstRect() {
2229038afb70d70650d87659bb252181ca8645670b8Chris Craik        if (mApplyGravity) {
2239265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            if (mIsCircular) {
2249265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss                final int minDimen = Math.min(mBitmapWidth, mBitmapHeight);
2253a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                gravityCompatApply(mGravity, minDimen, minDimen, getBounds(), mDstRect);
2263a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik
2273a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                // inset the drawing rectangle to the largest contained square,
2283a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                // so that a circle will be drawn
2293a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                final int minDrawDimen = Math.min(mDstRect.width(), mDstRect.height());
2303a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                final int insetX = Math.max(0, (mDstRect.width() - minDrawDimen) / 2);
2313a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                final int insetY = Math.max(0, (mDstRect.height() - minDrawDimen) / 2);
2323a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mDstRect.inset(insetX, insetY);
2333a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mCornerRadius = 0.5f * minDrawDimen;
2349265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            } else {
2359265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss                gravityCompatApply(mGravity, mBitmapWidth, mBitmapHeight, getBounds(), mDstRect);
2369265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            }
2379038afb70d70650d87659bb252181ca8645670b8Chris Craik            mDstRectF.set(mDstRect);
2383a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik
2393a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            if (mBitmapShader != null) {
2403a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                // setup shader matrix
2413a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mShaderMatrix.setTranslate(mDstRectF.left,mDstRectF.top);
2423a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mShaderMatrix.preScale(
2433a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                        mDstRectF.width() / mBitmap.getWidth(),
2443a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                        mDstRectF.height() / mBitmap.getHeight());
2453a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mBitmapShader.setLocalMatrix(mShaderMatrix);
2463a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik                mPaint.setShader(mBitmapShader);
2473a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            }
2483a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik
2499038afb70d70650d87659bb252181ca8645670b8Chris Craik            mApplyGravity = false;
2509038afb70d70650d87659bb252181ca8645670b8Chris Craik        }
2519038afb70d70650d87659bb252181ca8645670b8Chris Craik    }
2529038afb70d70650d87659bb252181ca8645670b8Chris Craik
253ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
254ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void draw(Canvas canvas) {
255ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        final Bitmap bitmap = mBitmap;
256ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (bitmap == null) {
257ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            return;
258ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
259ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
2609038afb70d70650d87659bb252181ca8645670b8Chris Craik        updateDstRect();
2613a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        if (mPaint.getShader() == null) {
2623a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            canvas.drawBitmap(bitmap, null, mDstRect, mPaint);
263ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        } else {
2643a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, mPaint);
265ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
266ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
267ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
268ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
269ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setAlpha(int alpha) {
270ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        final int oldAlpha = mPaint.getAlpha();
271ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (alpha != oldAlpha) {
272ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mPaint.setAlpha(alpha);
273ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            invalidateSelf();
274ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
275ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
276ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
277ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public int getAlpha() {
278ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mPaint.getAlpha();
279ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
280ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
281ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
282ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setColorFilter(ColorFilter cf) {
283ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mPaint.setColorFilter(cf);
284ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        invalidateSelf();
285ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
286ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
287ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public ColorFilter getColorFilter() {
288ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mPaint.getColorFilter();
289ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
290ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
291ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
2929265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss     * Sets the image shape to circular.
2939265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss     * <p>This overwrites any calls made to {@link #setCornerRadius(float)} so far.</p>
2949265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss     */
2959265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    public void setCircular(boolean circular) {
2969265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        mIsCircular = circular;
2979265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        mApplyGravity = true;
2989265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        if (circular) {
2999265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            updateCircularCornerRadius();
3003a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            mPaint.setShader(mBitmapShader);
3019265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            invalidateSelf();
3029265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        } else {
3039265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            setCornerRadius(0);
3049265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        }
3059265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    }
3069265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss
3079265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    private void updateCircularCornerRadius() {
3089265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        final int minCircularSize = Math.min(mBitmapHeight, mBitmapWidth);
3099265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        mCornerRadius = minCircularSize / 2;
3109265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    }
3119265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss
3129265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    /**
3139265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss     * @return <code>true</code> if the image is circular, else <code>false</code>.
3149265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss     */
3159265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    public boolean isCircular() {
3169265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        return mIsCircular;
3179265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    }
3189265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss
3199265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    /**
320ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * Sets the corner radius to be applied when drawing the bitmap.
321ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
322ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public void setCornerRadius(float cornerRadius) {
3233a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        if (mCornerRadius == cornerRadius) return;
3243a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik
3253a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        mIsCircular = false;
326ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (isGreaterThanZero(cornerRadius)) {
327ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mPaint.setShader(mBitmapShader);
328ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        } else {
329ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mPaint.setShader(null);
330ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
3313a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik
3323a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        mCornerRadius = cornerRadius;
3333a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        invalidateSelf();
3349265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    }
3359265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss
3369265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    @Override
3379265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss    protected void onBoundsChange(Rect bounds) {
3389265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        super.onBoundsChange(bounds);
3399265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        if (mIsCircular) {
3409265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss            updateCircularCornerRadius();
3419265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        }
3423a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        mApplyGravity = true;
343ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
344ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
345ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    /**
346ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     * @return The corner radius applied when drawing the bitmap.
347ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee     */
348ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public float getCornerRadius() {
349ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mCornerRadius;
350ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
351ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
352ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
353ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public int getIntrinsicWidth() {
354ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mBitmapWidth;
355ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
356ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
357ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
358ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public int getIntrinsicHeight() {
359ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return mBitmapHeight;
360ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
361ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
362ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    @Override
363ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    public int getOpacity() {
3649265673544525c1668786032195d5e6a1f370eb1Benjamin Weiss        if (mGravity != Gravity.FILL || mIsCircular) {
365ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            return PixelFormat.TRANSLUCENT;
366ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
367ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        Bitmap bm = mBitmap;
368ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        return (bm == null
369ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee                || bm.hasAlpha()
370ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee                || mPaint.getAlpha() < 255
371ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee                || isGreaterThanZero(mCornerRadius))
372ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee                ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
373ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
374ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
3759038afb70d70650d87659bb252181ca8645670b8Chris Craik    RoundedBitmapDrawable(Resources res, Bitmap bitmap) {
376ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (res != null) {
377ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mTargetDensity = res.getDisplayMetrics().densityDpi;
378ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
379ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
380ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        mBitmap = bitmap;
381ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        if (mBitmap != null) {
382ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            computeBitmapSize();
3833a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
384ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        } else {
385ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee            mBitmapWidth = mBitmapHeight = -1;
3863a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik            mBitmapShader = null;
387ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee        }
388ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
389ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee
3909038afb70d70650d87659bb252181ca8645670b8Chris Craik    private static boolean isGreaterThanZero(float toCompare) {
3913a1a3b98f843ab5c72644da9addb9473d895a826Chris Craik        return toCompare > 0.05f;
392ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee    }
393ddb24f29a236175d3cda4c11bda98a6212ecf9e5Yorke Lee}
394