CircularBitmapDrawable.java revision 8dd5cac690e42383d6bfc20af04eb781abd14f54
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;
46ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
47ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public CircularBitmapDrawable(Resources res,
48ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            BitmapCache cache, boolean limitDensity) {
4909f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        this(res, cache, limitDensity, null);
5009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    }
5109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei
5209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    public CircularBitmapDrawable(Resources res,
5309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            BitmapCache cache, boolean limitDensity, ExtendedOptions opts) {
5409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        super(res, cache, limitDensity, opts);
55ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
56ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setAntiAlias(true);
57ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setFilterBitmap(true);
58ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setDither(true);
59ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
60ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setColor(Color.TRANSPARENT);
61ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStyle(Style.STROKE);
62ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStrokeWidth(mBorderWidth);
63ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setAntiAlias(true);
64ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
65ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
66ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
67ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Set the border stroke width of this drawable.
68ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
69ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setBorderWidth(final float borderWidth) {
70ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth;
71ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setStrokeWidth(borderWidth);
72ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderWidth = borderWidth;
73ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
74ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (changed) {
75ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
76ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
77ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
78ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
79ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
80ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable.
81ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
82ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setBorderColor(final int color) {
83ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final boolean changed = mBorderPaint.getColor() != color;
84ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBorderPaint.setColor(color);
85ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
86ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (changed) {
87ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
88ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
89ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
90ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
91ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
92ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    protected void onDrawBitmap(final Canvas canvas, final Rect src,
93ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            final Rect dst) {
9409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        onDrawCircularBitmap(getBitmap().bmp, canvas, src, dst, 1f);
9509f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    }
9609f46006437e7de33afdb51192bf0bdc08e97040Mark Wei
9709f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    @Override
9809f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    protected void onDrawPlaceholderOrProgress(final Canvas canvas,
9909f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final TileDrawable drawable) {
10009f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        BitmapDrawable placeholder = (BitmapDrawable) drawable.getInnerDrawable();
10109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        Bitmap bitmap = placeholder.getBitmap();
10209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        float alpha = placeholder.getPaint().getAlpha() / 255f;
10309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        sRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
10409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        onDrawCircularBitmap(bitmap, canvas, sRect, getBounds(), alpha);
105ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
106ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
107ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    /**
108ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
109ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     * BitmapShader.
110ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei     */
111ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
1128dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei            final Rect src, final Rect dst) {
1138dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei        onDrawCircularBitmap(bitmap, canvas, src, dst, 1f);
1148dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    }
1158dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei
1168dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    /**
1178dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * Call this method with a given bitmap to draw it onto the given canvas, masked by a circular
1188dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     * BitmapShader. The alpha parameter is the value from 0f to 1f to attenuate the alpha by.
1198dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei     */
1208dd5cac690e42383d6bfc20af04eb781abd14f54Mark Wei    protected void onDrawCircularBitmap(final Bitmap bitmap, final Canvas canvas,
12109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final Rect src, final Rect dst, final float alpha) {
122ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Draw bitmap through shader first.
123ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP,
124ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                TileMode.CLAMP);
125ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.reset();
126ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
127ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Fit bitmap to bounds.
128ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        float scale = Math.max((float) dst.width() / src.width(),
129ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                (float) dst.height() / src.height());
130ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postScale(scale, scale);
131ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
132ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Translate bitmap to dst bounds.
133ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        sMatrix.postTranslate(dst.left, dst.top);
134ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
135ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        shader.setLocalMatrix(sMatrix);
136ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setShader(shader);
13709f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        int oldAlpha = mBitmapPaint.getAlpha();
13809f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha((int) (oldAlpha * alpha));
139ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(), dst.width() / 2,
140ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                mBitmapPaint);
14109f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        mBitmapPaint.setAlpha(oldAlpha);
142ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
143ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        // Then draw the border.
144ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        canvas.drawCircle(dst.centerX(), dst.centerY(),
145ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei                dst.width() / 2f - mBorderWidth / 2, mBorderPaint);
146ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
147ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
148ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
149ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setAlpha(int alpha) {
150ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setAlpha(alpha);
151ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
152ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        final int old = mBitmapPaint.getAlpha();
153ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mBitmapPaint.setAlpha(alpha);
154ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        if (alpha != old) {
155ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei            invalidateSelf();
156ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        }
157ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
158ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei
159ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    @Override
160ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    public void setColorFilter(ColorFilter cf) {
161ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        super.setColorFilter(cf);
162ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei        mPaint.setColorFilter(cf);
163ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei    }
164ad6ca3f895022ded1a11f3eedc50d70ea90cd4daMark Wei}
165