CircularBitmapDrawable.java revision c5644927c0e7e121049b063046296ee8a59a4b37
1ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei/*
2ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * Copyright (C) 2014 The Android Open Source Project
3ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei *
4ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * Licensed under the Apache License, Version 2.0 (the "License");
5ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * you may not use this file except in compliance with the License.
6ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * You may obtain a copy of the License at
7ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei *
8ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei *      http://www.apache.org/licenses/LICENSE-2.0
9ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei *
10ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * Unless required by applicable law or agreed to in writing, software
11ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * distributed under the License is distributed on an "AS IS" BASIS,
12ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * See the License for the specific language governing permissions and
14ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * limitations under the License.
15ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei */
16ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
17ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weipackage com.android.bitmap.drawable;
18ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
19ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.content.res.Resources;
20ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Bitmap;
21ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.BitmapShader;
22ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Canvas;
23ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Color;
24ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.ColorFilter;
25ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Matrix;
26ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Paint;
27ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Paint.Style;
28ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Rect;
29ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport android.graphics.Shader.TileMode;
3009f46006437e7de33afdb51192bf0bdc08e97040Mark Weiimport android.graphics.drawable.BitmapDrawable;
31ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
32ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Weiimport com.android.bitmap.BitmapCache;
33ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
34ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei/**
35ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * Custom BasicBitmapDrawable implementation for circular images.
36ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei *
37ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei * This draws all bitmaps as a circle with an optional border stroke.
38ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei */
3909f46006437e7de33afdb51192bf0bdc08e97040Mark Weipublic class CircularBitmapDrawable extends ExtendedBitmapDrawable {
40ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    private static Matrix sMatrix = new Matrix();
41ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
42ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    private final Paint mBitmapPaint = new Paint();
43ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    private final Paint mBorderPaint = new Paint();
44ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
45ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    private float mBorderWidth;
46df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei    private Bitmap mShaderBitmap;
47ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
48ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public CircularBitmapDrawable(Resources res,
49ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            BitmapCache cache, boolean limitDensity) {
5009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        this(res, cache, limitDensity, null);
5109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    }
5209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei
5309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    public CircularBitmapDrawable(Resources res,
5409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            BitmapCache cache, boolean limitDensity, ExtendedOptions opts) {
5509f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        super(res, cache, limitDensity, opts);
56ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
57ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setAntiAlias(true);
58ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setFilterBitmap(true);
59ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setDither(true);
60ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
61ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setColor(Color.TRANSPARENT);
62ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStyle(Style.STROKE);
63ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStrokeWidth(mBorderWidth);
64ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setAntiAlias(true);
65ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
66ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
67ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
68ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Set the border stroke width of this drawable.
69ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
70ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setBorderWidth(final float borderWidth) {
71ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth;
72ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStrokeWidth(borderWidth);
73ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderWidth = borderWidth;
74ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
75ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (changed) {
76ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
77ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
78ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
79ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
80ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
81ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable.
82ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
83ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setBorderColor(final int color) {
84ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final boolean changed = mBorderPaint.getColor() != color;
85ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setColor(color);
86ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
87ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (changed) {
88ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
89ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
90ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
91ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
92ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
93ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    protected void onDrawBitmap(final Canvas canvas, final Rect src,
94ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            final Rect dst) {
9509f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f);
9609f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    }
9709f46006437e7de33afdb51192bf0bdc08e97040Mark Wei
9809f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    @Override
9909f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    protected void onDrawPlaceholderOrProgress(final Canvas canvas,
10009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final TileDrawable drawable) {
101c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        Rect bounds = getBounds();
102c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        if (drawable.getInnerDrawable() instanceof BitmapDrawable) {
103c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            BitmapDrawable placeholder =
104c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                (BitmapDrawable) drawable.getInnerDrawable();
105c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            Bitmap bitmap = placeholder.getBitmap();
106c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            float alpha = placeholder.getPaint().getAlpha() / 255f;
107c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
108c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            onDrawCircularBitmap(bitmap, canvas, sRect, bounds, alpha);
109c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        } else {
110c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei          super.onDrawPlaceholderOrProgress(canvas, drawable);
111c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        }
112c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei
113c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        // Then draw the border.
114c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        canvas.drawCircle(bounds.centerX(), bounds.centerY(),
115c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                bounds.width() / 2f - mBorderWidth / 2, mBorderPaint);
116ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
117ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
118ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
119ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
120ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * BitmapShader.
121ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
122ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
1238dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei            final Rect src, final Rect dst) {
1248dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei        onDrawCircularBitmap(bitmap, canvas, src, dst, 1f);
1258dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    }
1268dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei
1278dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    /**
1288dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
1298dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by.
1308dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     */
1318dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
13209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final Rect src, final Rect dst, final float alpha) {
133ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Draw bitmap through shader first.
134df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        BitmapShader shader = (BitmapShader) mBitmapPaint.getShader();
135df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        if (shader == null || mShaderBitmap != bitmap) {
136df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei          shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
137df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei          mShaderBitmap = bitmap;
138df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        }
139ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
140df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        sMatrix.reset();
141ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Fit bitmap to bounds.
142ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        float scale = Math.max((float) dst.width() / src.width(),
143ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                (float) dst.height() / src.height());
144ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postScale(scale, scale);
145ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Translate bitmap to dst bounds.
146ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postTranslate(dst.left, dst.top);
147ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        shader.setLocalMatrix(sMatrix);
1485c102db5a1a16a2adcc7728693a6e65a981f91aeLeon Scroggins III        mBitmapPaint.setShader(shader);
149df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei
15009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        int oldAlpha = mBitmapPaint.getAlpha();
15109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha((int) (oldAlpha * alpha));
152ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2,
153ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                mBitmapPaint);
15409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha(oldAlpha);
155ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
156ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Then draw the border.
157ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(),
158ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                dst.width() / 2f - mBorderWidth / 2, mBorderPaint);
159ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
160ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
161ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
162ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setAlpha(int alpha) {
163ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setAlpha(alpha);
164ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
165ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final int old = mBitmapPaint.getAlpha();
166ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setAlpha(alpha);
167ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (alpha != old) {
168ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
169ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
170ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
171ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
172ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
173ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setColorFilter(ColorFilter cf) {
174ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setColorFilter(cf);
175ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mPaint.setColorFilter(cf);
176ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
177ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei}
178