CircularBitmapDrawable.java revision 5c102db5a1a16a2adcc7728693a6e65a981f91ae
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) {
10109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        BitmapDrawable placeholder = (BitmapDrawable) drawable.getInnerDrawable();
10209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        Bitmap bitmap = placeholder.getBitmap();
10309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        float alpha = placeholder.getPaint().getAlpha() / 255f;
10409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
10509f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        onDrawCircularBitmap(bitmap, canvas, sRect, getBounds(), alpha);
106ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
107ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
108ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
109ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
110ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * BitmapShader.
111ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
112ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
1138dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei            final Rect src, final Rect dst) {
1148dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei        onDrawCircularBitmap(bitmap, canvas, src, dst, 1f);
1158dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    }
1168dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei
1178dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    /**
1188dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
1198dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by.
1208dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     */
1218dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
12209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final Rect src, final Rect dst, final float alpha) {
123ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Draw bitmap through shader first.
124df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        BitmapShader shader = (BitmapShader) mBitmapPaint.getShader();
125df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        if (shader == null || mShaderBitmap != bitmap) {
126df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei          shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
127df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei          mShaderBitmap = bitmap;
128df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        }
129ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
130df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei        sMatrix.reset();
131ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Fit bitmap to bounds.
132ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        float scale = Math.max((float) dst.width() / src.width(),
133ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                (float) dst.height() / src.height());
134ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postScale(scale, scale);
135ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Translate bitmap to dst bounds.
136ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postTranslate(dst.left, dst.top);
137ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        shader.setLocalMatrix(sMatrix);
1385c102db5a1a16a2adcc7728693a6e65a981f91aeLeon Scroggins III        mBitmapPaint.setShader(shader);
139df01966f999ddcc69b3e479c9efbd733ad86bc84Mark Wei
14009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        int oldAlpha = mBitmapPaint.getAlpha();
14109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha((int) (oldAlpha * alpha));
142ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2,
143ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                mBitmapPaint);
14409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha(oldAlpha);
145ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
146ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Then draw the border.
147ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(),
148ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                dst.width() / 2f - mBorderWidth / 2, mBorderPaint);
149ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
150ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
151ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
152ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setAlpha(int alpha) {
153ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setAlpha(alpha);
154ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
155ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final int old = mBitmapPaint.getAlpha();
156ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setAlpha(alpha);
157ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (alpha != old) {
158ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
159ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
160ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
161ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
162ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
163ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setColorFilter(ColorFilter cf) {
164ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setColorFilter(cf);
165ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mPaint.setColorFilter(cf);
166ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
167ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei}
168