BitmapDrawable.java revision e0f95f39c5a669a48ee3ebb8dc45bf2d7ee940f1
1c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk/*
2c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * Copyright (C) 2006 The Android Open Source Project
3c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk *
4c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * Licensed under the Apache License, Version 2.0 (the "License");
5c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * you may not use this file except in compliance with the License.
6c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * You may obtain a copy of the License at
7c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk *
8c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk *      http://www.apache.org/licenses/LICENSE-2.0
9c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk *
10c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * Unless required by applicable law or agreed to in writing, software
11c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * distributed under the License is distributed on an "AS IS" BASIS,
12c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * See the License for the specific language governing permissions and
14c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * limitations under the License.
15c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk */
16c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
17c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkpackage android.graphics.drawable;
18c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
19c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.annotation.NonNull;
20c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.content.res.ColorStateList;
21c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.content.res.Resources;
22c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.content.res.Resources.Theme;
23c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.content.res.TypedArray;
24c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Bitmap;
25c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.BitmapFactory;
26c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.BitmapShader;
27c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Canvas;
28c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.ColorFilter;
29c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Insets;
30c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Matrix;
31c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Outline;
32c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Paint;
33c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.PixelFormat;
34c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.PorterDuff;
35c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.PorterDuff.Mode;
36c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.PorterDuffColorFilter;
37c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Rect;
38c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Shader;
39c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.graphics.Xfermode;
40c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.util.AttributeSet;
41c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.util.DisplayMetrics;
42c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.util.LayoutDirection;
43c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport android.view.Gravity;
44c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
45c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport com.android.internal.R;
46c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
47c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport org.xmlpull.v1.XmlPullParser;
48c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport org.xmlpull.v1.XmlPullParserException;
49c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
50c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport java.io.IOException;
51c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burkimport java.util.Collection;
52c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
53c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk/**
54c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
55c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * BitmapDrawable from a file path, an input stream, through XML inflation, or from
56c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * a {@link android.graphics.Bitmap} object.
57c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * <p>It can be defined in an XML file with the <code>&lt;bitmap></code> element.  For more
58c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * information, see the guide to <a
59c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
60c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * <p>
61c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * Also see the {@link android.graphics.Bitmap} class, which handles the management and
62c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * transformation of raw bitmap graphics, and should be used when drawing to a
6371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk * {@link android.graphics.Canvas}.
64c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * </p>
6587c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk *
66c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_src
67c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_antialias
68c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_filter
69c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_dither
70c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_gravity
71c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_mipMap
72c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk * @attr ref android.R.styleable#BitmapDrawable_tileMode
73c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk */
7471f35bb687476694882a617ba4a810a0bb56fe23Phil Burkpublic class BitmapDrawable extends Drawable {
75c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private static final int DEFAULT_PAINT_FLAGS =
76c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
77c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
78c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    // Constants for {@link android.R.styleable#BitmapDrawable_tileMode}.
7937a466ae847cf08fc346e629fe4cf964ad67063fGlenn Kasten    private static final int TILE_MODE_UNDEFINED = -2;
80c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private static final int TILE_MODE_DISABLED = -1;
81c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private static final int TILE_MODE_CLAMP = 0;
8271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    private static final int TILE_MODE_REPEAT = 1;
8371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    private static final int TILE_MODE_MIRROR = 2;
84c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
85c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private final Rect mDstRect = new Rect();   // #updateDstRectAndInsetsIfDirty() sets this
86c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
87c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private BitmapState mBitmapState;
8837a466ae847cf08fc346e629fe4cf964ad67063fGlenn Kasten    private PorterDuffColorFilter mTintFilter;
89c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
90c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
9171f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
9271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    private boolean mDstRectAndInsetsDirty = true;
93c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private boolean mMutated;
94c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
95c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     // These are scaled to match the target density.
96c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private int mBitmapWidth;
97c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private int mBitmapHeight;
98c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
9971f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    /** Optical insets due to gravity. */
10071f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    private Insets mOpticalInsets = Insets.NONE;
10171f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
10271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    // Mirroring matrix for using with Shaders
10371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    private Matrix mMirrorMatrix;
10471f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
10571f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    /**
10671f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * Create an empty drawable, not dealing with density.
107c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
108c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * instead to specify a bitmap to draw with and ensure the correct density is set.
109c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
110c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    @Deprecated
111c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public BitmapDrawable() {
112c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        mBitmapState = new BitmapState((Bitmap) null);
113c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
114c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
115c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
116c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Create an empty drawable, setting initial target density based on
117c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * the display metrics of the resources.
118c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     *
119c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
120c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * instead to specify a bitmap to draw with.
121c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
122c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    @SuppressWarnings("unused")
123c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    @Deprecated
124c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public BitmapDrawable(Resources res) {
125c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        mBitmapState = new BitmapState((Bitmap) null);
126c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        mBitmapState.mTargetDensity = mTargetDensity;
127c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
128c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
129c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
130c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Create drawable from a bitmap, not dealing with density.
131c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @deprecated Use {@link #BitmapDrawable(Resources, Bitmap)} to ensure
13271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * that the drawable has correctly set its target density.
13371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     */
13471f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    @Deprecated
13571f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    public BitmapDrawable(Bitmap bitmap) {
13687c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk        this(new BitmapState(bitmap), null);
13771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    }
138c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
139c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
140c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Create drawable from a bitmap, setting initial target density based on
141c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * the display metrics of the resources.
142c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
143c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public BitmapDrawable(Resources res, Bitmap bitmap) {
144c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        this(new BitmapState(bitmap), res);
145c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        mBitmapState.mTargetDensity = mTargetDensity;
146c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
147c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
148c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
149c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Create a drawable by opening a given file path and decoding the bitmap.
150c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @deprecated Use {@link #BitmapDrawable(Resources, String)} to ensure
151c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * that the drawable has correctly set its target density.
152c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
15371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    @Deprecated
15471f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    public BitmapDrawable(String filepath) {
15571f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
15671f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        if (mBitmapState.mBitmap == null) {
15771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
15871f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        }
15971f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    }
16071f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
16171f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    /**
16271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * Create a drawable by opening a given file path and decoding the bitmap.
16371f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     */
16471f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    @SuppressWarnings("unused")
16571f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    public BitmapDrawable(Resources res, String filepath) {
16671f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
16771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        mBitmapState.mTargetDensity = mTargetDensity;
16871f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        if (mBitmapState.mBitmap == null) {
16971f35bb687476694882a617ba4a810a0bb56fe23Phil Burk            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
17071f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        }
171c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
17271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
173c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
174c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Create a drawable by decoding a bitmap from the given input stream.
175c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @deprecated Use {@link #BitmapDrawable(Resources, java.io.InputStream)} to ensure
176c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * that the drawable has correctly set its target density.
17771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     */
178c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    @Deprecated
179c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public BitmapDrawable(java.io.InputStream is) {
180c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
181c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        if (mBitmapState.mBitmap == null) {
182c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
183c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        }
184c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
185c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
186c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    /**
18771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * Create a drawable by decoding a bitmap from the given input stream.
18871f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     */
189c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    @SuppressWarnings("unused")
190c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public BitmapDrawable(Resources res, java.io.InputStream is) {
191c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
192c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        mBitmapState.mTargetDensity = mTargetDensity;
193c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        if (mBitmapState.mBitmap == null) {
19471f35bb687476694882a617ba4a810a0bb56fe23Phil Burk            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
19571f35bb687476694882a617ba4a810a0bb56fe23Phil Burk        }
19671f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    }
19771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
19871f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    /**
19971f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * Returns the paint used to render this drawable.
20071f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     */
20171f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    public final Paint getPaint() {
202942bdc0aebc88dc8b12c0e7742ec0003bbb8b80fPhil Burk        return mBitmapState.mPaint;
203942bdc0aebc88dc8b12c0e7742ec0003bbb8b80fPhil Burk    }
204942bdc0aebc88dc8b12c0e7742ec0003bbb8b80fPhil Burk
205942bdc0aebc88dc8b12c0e7742ec0003bbb8b80fPhil Burk    /**
206c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * Returns the bitmap used by this drawable to render. May be null.
207c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
208c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public final Bitmap getBitmap() {
209c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        return mBitmapState.mBitmap;
210c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
211c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
212c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private void computeBitmapSize() {
213c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        final Bitmap bitmap = mBitmapState.mBitmap;
214c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        if (bitmap != null) {
215c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            mBitmapWidth = bitmap.getScaledWidth(mTargetDensity);
216c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            mBitmapHeight = bitmap.getScaledHeight(mTargetDensity);
217c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        } else {
218c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            mBitmapWidth = mBitmapHeight = -1;
219c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        }
220c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
221c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk
222c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    private void setBitmap(Bitmap bitmap) {
223c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        if (mBitmapState.mBitmap != bitmap) {
224c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            mBitmapState.mBitmap = bitmap;
225c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            computeBitmapSize();
226c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk            invalidateSelf();
227c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk        }
228c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    }
22971f35bb687476694882a617ba4a810a0bb56fe23Phil Burk
23071f35bb687476694882a617ba4a810a0bb56fe23Phil Burk    /**
23171f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * Set the density scale at which this drawable will be rendered. This
23271f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * method assumes the drawable will be rendered at the same density as the
233c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * specified canvas.
234c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     *
235c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     * @param canvas The Canvas from which the density scale must be obtained.
23671f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     *
23771f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * @see android.graphics.Bitmap#setDensity(int)
23871f35bb687476694882a617ba4a810a0bb56fe23Phil Burk     * @see android.graphics.Bitmap#getDensity()
239c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk     */
240c0c70e3c7dd10bc2c0caffcab1f3f5fb406b35fbPhil Burk    public void setTargetDensity(Canvas canvas) {
241        setTargetDensity(canvas.getDensity());
242    }
243
244    /**
245     * Set the density scale at which this drawable will be rendered.
246     *
247     * @param metrics The DisplayMetrics indicating the density scale for this drawable.
248     *
249     * @see android.graphics.Bitmap#setDensity(int)
250     * @see android.graphics.Bitmap#getDensity()
251     */
252    public void setTargetDensity(DisplayMetrics metrics) {
253        setTargetDensity(metrics.densityDpi);
254    }
255
256    /**
257     * Set the density at which this drawable will be rendered.
258     *
259     * @param density The density scale for this drawable.
260     *
261     * @see android.graphics.Bitmap#setDensity(int)
262     * @see android.graphics.Bitmap#getDensity()
263     */
264    public void setTargetDensity(int density) {
265        if (mTargetDensity != density) {
266            mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
267            if (mBitmapState.mBitmap != null) {
268                computeBitmapSize();
269            }
270            invalidateSelf();
271        }
272    }
273
274    /** Get the gravity used to position/stretch the bitmap within its bounds.
275     * See android.view.Gravity
276     * @return the gravity applied to the bitmap
277     */
278    public int getGravity() {
279        return mBitmapState.mGravity;
280    }
281
282    /** Set the gravity used to position/stretch the bitmap within its bounds.
283        See android.view.Gravity
284     * @param gravity the gravity
285     */
286    public void setGravity(int gravity) {
287        if (mBitmapState.mGravity != gravity) {
288            mBitmapState.mGravity = gravity;
289            mDstRectAndInsetsDirty = true;
290            invalidateSelf();
291        }
292    }
293
294    /**
295     * Enables or disables the mipmap hint for this drawable's bitmap.
296     * See {@link Bitmap#setHasMipMap(boolean)} for more information.
297     *
298     * If the bitmap is null calling this method has no effect.
299     *
300     * @param mipMap True if the bitmap should use mipmaps, false otherwise.
301     *
302     * @see #hasMipMap()
303     */
304    public void setMipMap(boolean mipMap) {
305        if (mBitmapState.mBitmap != null) {
306            mBitmapState.mBitmap.setHasMipMap(mipMap);
307            invalidateSelf();
308        }
309    }
310
311    /**
312     * Indicates whether the mipmap hint is enabled on this drawable's bitmap.
313     *
314     * @return True if the mipmap hint is set, false otherwise. If the bitmap
315     *         is null, this method always returns false.
316     *
317     * @see #setMipMap(boolean)
318     * @attr ref android.R.styleable#BitmapDrawable_mipMap
319     */
320    public boolean hasMipMap() {
321        return mBitmapState.mBitmap != null && mBitmapState.mBitmap.hasMipMap();
322    }
323
324    /**
325     * Enables or disables anti-aliasing for this drawable. Anti-aliasing affects
326     * the edges of the bitmap only so it applies only when the drawable is rotated.
327     *
328     * @param aa True if the bitmap should be anti-aliased, false otherwise.
329     *
330     * @see #hasAntiAlias()
331     */
332    public void setAntiAlias(boolean aa) {
333        mBitmapState.mPaint.setAntiAlias(aa);
334        invalidateSelf();
335    }
336
337    /**
338     * Indicates whether anti-aliasing is enabled for this drawable.
339     *
340     * @return True if anti-aliasing is enabled, false otherwise.
341     *
342     * @see #setAntiAlias(boolean)
343     */
344    public boolean hasAntiAlias() {
345        return mBitmapState.mPaint.isAntiAlias();
346    }
347
348    @Override
349    public void setFilterBitmap(boolean filter) {
350        mBitmapState.mPaint.setFilterBitmap(filter);
351        invalidateSelf();
352    }
353
354    @Override
355    public void setDither(boolean dither) {
356        mBitmapState.mPaint.setDither(dither);
357        invalidateSelf();
358    }
359
360    @Override
361    public boolean getDither() {
362        return mBitmapState.mPaint.isDither();
363    }
364
365    /**
366     * Indicates the repeat behavior of this drawable on the X axis.
367     *
368     * @return {@link android.graphics.Shader.TileMode#CLAMP} if the bitmap does not repeat,
369     *         {@link android.graphics.Shader.TileMode#REPEAT} or
370     *         {@link android.graphics.Shader.TileMode#MIRROR} otherwise.
371     */
372    public Shader.TileMode getTileModeX() {
373        return mBitmapState.mTileModeX;
374    }
375
376    /**
377     * Indicates the repeat behavior of this drawable on the Y axis.
378     *
379     * @return {@link android.graphics.Shader.TileMode#CLAMP} if the bitmap does not repeat,
380     *         {@link android.graphics.Shader.TileMode#REPEAT} or
381     *         {@link android.graphics.Shader.TileMode#MIRROR} otherwise.
382     */
383    public Shader.TileMode getTileModeY() {
384        return mBitmapState.mTileModeY;
385    }
386
387    /**
388     * Sets the repeat behavior of this drawable on the X axis. By default, the drawable
389     * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or
390     * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled)
391     * if the bitmap is smaller than this drawable.
392     *
393     * @param mode The repeat mode for this drawable.
394     *
395     * @see #setTileModeY(android.graphics.Shader.TileMode)
396     * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
397     * @attr ref android.R.styleable#BitmapDrawable_tileModeX
398     */
399    public void setTileModeX(Shader.TileMode mode) {
400        setTileModeXY(mode, mBitmapState.mTileModeY);
401    }
402
403    /**
404     * Sets the repeat behavior of this drawable on the Y axis. By default, the drawable
405     * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or
406     * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled)
407     * if the bitmap is smaller than this drawable.
408     *
409     * @param mode The repeat mode for this drawable.
410     *
411     * @see #setTileModeX(android.graphics.Shader.TileMode)
412     * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
413     * @attr ref android.R.styleable#BitmapDrawable_tileModeY
414     */
415    public final void setTileModeY(Shader.TileMode mode) {
416        setTileModeXY(mBitmapState.mTileModeX, mode);
417    }
418
419    /**
420     * Sets the repeat behavior of this drawable on both axis. By default, the drawable
421     * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or
422     * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled)
423     * if the bitmap is smaller than this drawable.
424     *
425     * @param xmode The X repeat mode for this drawable.
426     * @param ymode The Y repeat mode for this drawable.
427     *
428     * @see #setTileModeX(android.graphics.Shader.TileMode)
429     * @see #setTileModeY(android.graphics.Shader.TileMode)
430     */
431    public void setTileModeXY(Shader.TileMode xmode, Shader.TileMode ymode) {
432        final BitmapState state = mBitmapState;
433        if (state.mTileModeX != xmode || state.mTileModeY != ymode) {
434            state.mTileModeX = xmode;
435            state.mTileModeY = ymode;
436            state.mRebuildShader = true;
437            mDstRectAndInsetsDirty = true;
438            invalidateSelf();
439        }
440    }
441
442    @Override
443    public void setAutoMirrored(boolean mirrored) {
444        if (mBitmapState.mAutoMirrored != mirrored) {
445            mBitmapState.mAutoMirrored = mirrored;
446            invalidateSelf();
447        }
448    }
449
450    @Override
451    public final boolean isAutoMirrored() {
452        return mBitmapState.mAutoMirrored;
453    }
454
455    @Override
456    public int getChangingConfigurations() {
457        return super.getChangingConfigurations() | mBitmapState.getChangingConfigurations();
458    }
459
460    private boolean needMirroring() {
461        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
462    }
463
464    private void updateMirrorMatrix(float dx) {
465        if (mMirrorMatrix == null) {
466            mMirrorMatrix = new Matrix();
467        }
468        mMirrorMatrix.setTranslate(dx, 0);
469        mMirrorMatrix.preScale(-1.0f, 1.0f);
470    }
471
472    @Override
473    protected void onBoundsChange(Rect bounds) {
474        mDstRectAndInsetsDirty = true;
475
476        final Shader shader = mBitmapState.mPaint.getShader();
477        if (shader != null) {
478            if (needMirroring()) {
479                updateMirrorMatrix(bounds.right - bounds.left);
480                shader.setLocalMatrix(mMirrorMatrix);
481                mBitmapState.mPaint.setShader(shader);
482            } else {
483                if (mMirrorMatrix != null) {
484                    mMirrorMatrix = null;
485                    shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
486                    mBitmapState.mPaint.setShader(shader);
487                }
488            }
489        }
490    }
491
492    @Override
493    public void draw(Canvas canvas) {
494        final Bitmap bitmap = mBitmapState.mBitmap;
495        if (bitmap == null) {
496            return;
497        }
498
499        final BitmapState state = mBitmapState;
500        final Paint paint = state.mPaint;
501        if (state.mRebuildShader) {
502            final Shader.TileMode tmx = state.mTileModeX;
503            final Shader.TileMode tmy = state.mTileModeY;
504            if (tmx == null && tmy == null) {
505                paint.setShader(null);
506            } else {
507                paint.setShader(new BitmapShader(bitmap,
508                        tmx == null ? Shader.TileMode.CLAMP : tmx,
509                        tmy == null ? Shader.TileMode.CLAMP : tmy));
510            }
511
512            state.mRebuildShader = false;
513        }
514
515        final int restoreAlpha;
516        if (state.mBaseAlpha != 1.0f) {
517            final Paint p = getPaint();
518            restoreAlpha = p.getAlpha();
519            p.setAlpha((int) (restoreAlpha * state.mBaseAlpha + 0.5f));
520        } else {
521            restoreAlpha = -1;
522        }
523
524        final boolean clearColorFilter;
525        if (mTintFilter != null && paint.getColorFilter() == null) {
526            paint.setColorFilter(mTintFilter);
527            clearColorFilter = true;
528        } else {
529            clearColorFilter = false;
530        }
531
532        updateDstRectAndInsetsIfDirty();
533        final Shader shader = paint.getShader();
534        final boolean needMirroring = needMirroring();
535        if (shader == null) {
536            if (needMirroring) {
537                canvas.save();
538                // Mirror the bitmap
539                canvas.translate(mDstRect.right - mDstRect.left, 0);
540                canvas.scale(-1.0f, 1.0f);
541            }
542
543            canvas.drawBitmap(bitmap, null, mDstRect, paint);
544
545            if (needMirroring) {
546                canvas.restore();
547            }
548        } else {
549            if (needMirroring) {
550                // Mirror the bitmap
551                updateMirrorMatrix(mDstRect.right - mDstRect.left);
552                shader.setLocalMatrix(mMirrorMatrix);
553                paint.setShader(shader);
554            } else {
555                if (mMirrorMatrix != null) {
556                    mMirrorMatrix = null;
557                    shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
558                    paint.setShader(shader);
559                }
560            }
561
562            canvas.drawRect(mDstRect, paint);
563        }
564
565        if (clearColorFilter) {
566            paint.setColorFilter(null);
567        }
568
569        if (restoreAlpha >= 0) {
570            paint.setAlpha(restoreAlpha);
571        }
572    }
573
574    private void updateDstRectAndInsetsIfDirty() {
575        if (mDstRectAndInsetsDirty) {
576            if (mBitmapState.mTileModeX == null && mBitmapState.mTileModeY == null) {
577                final Rect bounds = getBounds();
578                final int layoutDirection = getLayoutDirection();
579                Gravity.apply(mBitmapState.mGravity, mBitmapWidth, mBitmapHeight,
580                        bounds, mDstRect, layoutDirection);
581
582                final int left = mDstRect.left - bounds.left;
583                final int top = mDstRect.top - bounds.top;
584                final int right = bounds.right - mDstRect.right;
585                final int bottom = bounds.bottom - mDstRect.bottom;
586                mOpticalInsets = Insets.of(left, top, right, bottom);
587            } else {
588                copyBounds(mDstRect);
589                mOpticalInsets = Insets.NONE;
590            }
591        }
592        mDstRectAndInsetsDirty = false;
593    }
594
595    /**
596     * @hide
597     */
598    @Override
599    public Insets getOpticalInsets() {
600        updateDstRectAndInsetsIfDirty();
601        return mOpticalInsets;
602    }
603
604    @Override
605    public void getOutline(@NonNull Outline outline) {
606        updateDstRectAndInsetsIfDirty();
607        outline.setRect(mDstRect);
608
609        // Only opaque Bitmaps can report a non-0 alpha,
610        // since only they are guaranteed to fill their bounds
611        boolean opaqueOverShape = mBitmapState.mBitmap != null
612                && !mBitmapState.mBitmap.hasAlpha();
613        outline.setAlpha(opaqueOverShape ? getAlpha() / 255.0f : 0.0f);
614    }
615
616    @Override
617    public void setAlpha(int alpha) {
618        final int oldAlpha = mBitmapState.mPaint.getAlpha();
619        if (alpha != oldAlpha) {
620            mBitmapState.mPaint.setAlpha(alpha);
621            invalidateSelf();
622        }
623    }
624
625    @Override
626    public int getAlpha() {
627        return mBitmapState.mPaint.getAlpha();
628    }
629
630    @Override
631    public void setColorFilter(ColorFilter colorFilter) {
632        mBitmapState.mPaint.setColorFilter(colorFilter);
633        invalidateSelf();
634    }
635
636    @Override
637    public ColorFilter getColorFilter() {
638        return mBitmapState.mPaint.getColorFilter();
639    }
640
641    @Override
642    public void setTintList(ColorStateList tint) {
643        mBitmapState.mTint = tint;
644        mTintFilter = updateTintFilter(mTintFilter, tint, mBitmapState.mTintMode);
645        invalidateSelf();
646    }
647
648    @Override
649    public void setTintMode(PorterDuff.Mode tintMode) {
650        mBitmapState.mTintMode = tintMode;
651        mTintFilter = updateTintFilter(mTintFilter, mBitmapState.mTint, tintMode);
652        invalidateSelf();
653    }
654
655    /**
656     * @hide only needed by a hack within ProgressBar
657     */
658    public ColorStateList getTint() {
659        return mBitmapState.mTint;
660    }
661
662    /**
663     * @hide only needed by a hack within ProgressBar
664     */
665    public Mode getTintMode() {
666        return mBitmapState.mTintMode;
667    }
668
669    /**
670     * @hide Candidate for future API inclusion
671     */
672    @Override
673    public void setXfermode(Xfermode xfermode) {
674        mBitmapState.mPaint.setXfermode(xfermode);
675        invalidateSelf();
676    }
677
678    /**
679     * A mutable BitmapDrawable still shares its Bitmap with any other Drawable
680     * that comes from the same resource.
681     *
682     * @return This drawable.
683     */
684    @Override
685    public Drawable mutate() {
686        if (!mMutated && super.mutate() == this) {
687            mBitmapState = new BitmapState(mBitmapState);
688            mMutated = true;
689        }
690        return this;
691    }
692
693    /**
694     * @hide
695     */
696    public void clearMutated() {
697        super.clearMutated();
698        mMutated = false;
699    }
700
701    @Override
702    protected boolean onStateChange(int[] stateSet) {
703        final BitmapState state = mBitmapState;
704        if (state.mTint != null && state.mTintMode != null) {
705            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
706            return true;
707        }
708        return false;
709    }
710
711    @Override
712    public boolean isStateful() {
713        return (mBitmapState.mTint != null && mBitmapState.mTint.isStateful())
714                || super.isStateful();
715    }
716
717    @Override
718    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
719            throws XmlPullParserException, IOException {
720        super.inflate(r, parser, attrs, theme);
721
722        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
723        updateStateFromTypedArray(a);
724        verifyRequiredAttributes(a);
725        a.recycle();
726
727        // Update local properties.
728        updateLocalState(r);
729    }
730
731    /**
732     * Ensures all required attributes are set.
733     *
734     * @throws XmlPullParserException if any required attributes are missing
735     */
736    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
737        // If we're not waiting on a theme, verify required attributes.
738        final BitmapState state = mBitmapState;
739        if (state.mBitmap == null && (state.mThemeAttrs == null
740                || state.mThemeAttrs[R.styleable.BitmapDrawable_src] == 0)) {
741            throw new XmlPullParserException(a.getPositionDescription() +
742                    ": <bitmap> requires a valid 'src' attribute");
743        }
744    }
745
746    /**
747     * Updates the constant state from the values in the typed array.
748     */
749    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
750        final Resources r = a.getResources();
751        final BitmapState state = mBitmapState;
752
753        // Account for any configuration changes.
754        state.mChangingConfigurations |= a.getChangingConfigurations();
755
756        // Extract the theme attributes, if any.
757        state.mThemeAttrs = a.extractThemeAttrs();
758
759        final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
760        if (srcResId != 0) {
761            final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
762            if (bitmap == null) {
763                throw new XmlPullParserException(a.getPositionDescription() +
764                        ": <bitmap> requires a valid 'src' attribute");
765            }
766
767            state.mBitmap = bitmap;
768        }
769
770        state.mTargetDensity = r.getDisplayMetrics().densityDpi;
771
772        final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false;
773        setMipMap(a.getBoolean(R.styleable.BitmapDrawable_mipMap, defMipMap));
774
775        state.mAutoMirrored = a.getBoolean(
776                R.styleable.BitmapDrawable_autoMirrored, state.mAutoMirrored);
777        state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, state.mBaseAlpha);
778
779        final int tintMode = a.getInt(R.styleable.BitmapDrawable_tintMode, -1);
780        if (tintMode != -1) {
781            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
782        }
783
784        final ColorStateList tint = a.getColorStateList(R.styleable.BitmapDrawable_tint);
785        if (tint != null) {
786            state.mTint = tint;
787        }
788
789        final Paint paint = mBitmapState.mPaint;
790        paint.setAntiAlias(a.getBoolean(
791                R.styleable.BitmapDrawable_antialias, paint.isAntiAlias()));
792        paint.setFilterBitmap(a.getBoolean(
793                R.styleable.BitmapDrawable_filter, paint.isFilterBitmap()));
794        paint.setDither(a.getBoolean(R.styleable.BitmapDrawable_dither, paint.isDither()));
795
796        setGravity(a.getInt(R.styleable.BitmapDrawable_gravity, state.mGravity));
797
798        final int tileMode = a.getInt(R.styleable.BitmapDrawable_tileMode, TILE_MODE_UNDEFINED);
799        if (tileMode != TILE_MODE_UNDEFINED) {
800            final Shader.TileMode mode = parseTileMode(tileMode);
801            setTileModeXY(mode, mode);
802        }
803
804        final int tileModeX = a.getInt(R.styleable.BitmapDrawable_tileModeX, TILE_MODE_UNDEFINED);
805        if (tileModeX != TILE_MODE_UNDEFINED) {
806            setTileModeX(parseTileMode(tileModeX));
807        }
808
809        final int tileModeY = a.getInt(R.styleable.BitmapDrawable_tileModeY, TILE_MODE_UNDEFINED);
810        if (tileModeY != TILE_MODE_UNDEFINED) {
811            setTileModeY(parseTileMode(tileModeY));
812        }
813    }
814
815    @Override
816    public void applyTheme(Theme t) {
817        super.applyTheme(t);
818
819        final BitmapState state = mBitmapState;
820        if (state == null) {
821            return;
822        }
823
824        if (state.mThemeAttrs != null) {
825            final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable);
826            try {
827                updateStateFromTypedArray(a);
828            } catch (XmlPullParserException e) {
829                throw new RuntimeException(e);
830            } finally {
831                a.recycle();
832            }
833        }
834
835        // Apply theme to contained color state list.
836        if (state.mTint != null && state.mTint.canApplyTheme()) {
837            state.mTint = state.mTint.obtainForTheme(t);
838        }
839
840        // Update local properties.
841        updateLocalState(t.getResources());
842    }
843
844    private static Shader.TileMode parseTileMode(int tileMode) {
845        switch (tileMode) {
846            case TILE_MODE_CLAMP:
847                return Shader.TileMode.CLAMP;
848            case TILE_MODE_REPEAT:
849                return Shader.TileMode.REPEAT;
850            case TILE_MODE_MIRROR:
851                return Shader.TileMode.MIRROR;
852            default:
853                return null;
854        }
855    }
856
857    @Override
858    public boolean canApplyTheme() {
859        return mBitmapState != null && mBitmapState.canApplyTheme();
860    }
861
862    @Override
863    public int getIntrinsicWidth() {
864        return mBitmapWidth;
865    }
866
867    @Override
868    public int getIntrinsicHeight() {
869        return mBitmapHeight;
870    }
871
872    @Override
873    public int getOpacity() {
874        if (mBitmapState.mGravity != Gravity.FILL) {
875            return PixelFormat.TRANSLUCENT;
876        }
877
878        final Bitmap bitmap = mBitmapState.mBitmap;
879        return (bitmap == null || bitmap.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255) ?
880                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
881    }
882
883    @Override
884    public final ConstantState getConstantState() {
885        mBitmapState.mChangingConfigurations |= getChangingConfigurations();
886        return mBitmapState;
887    }
888
889    final static class BitmapState extends ConstantState {
890        final Paint mPaint;
891
892        // Values loaded during inflation.
893        int[] mThemeAttrs = null;
894        Bitmap mBitmap = null;
895        ColorStateList mTint = null;
896        Mode mTintMode = DEFAULT_TINT_MODE;
897        int mGravity = Gravity.FILL;
898        float mBaseAlpha = 1.0f;
899        Shader.TileMode mTileModeX = null;
900        Shader.TileMode mTileModeY = null;
901        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
902        boolean mAutoMirrored = false;
903
904        int mChangingConfigurations;
905        boolean mRebuildShader;
906
907        BitmapState(Bitmap bitmap) {
908            mBitmap = bitmap;
909            mPaint = new Paint(DEFAULT_PAINT_FLAGS);
910        }
911
912        BitmapState(BitmapState bitmapState) {
913            mBitmap = bitmapState.mBitmap;
914            mTint = bitmapState.mTint;
915            mTintMode = bitmapState.mTintMode;
916            mThemeAttrs = bitmapState.mThemeAttrs;
917            mChangingConfigurations = bitmapState.mChangingConfigurations;
918            mGravity = bitmapState.mGravity;
919            mTileModeX = bitmapState.mTileModeX;
920            mTileModeY = bitmapState.mTileModeY;
921            mTargetDensity = bitmapState.mTargetDensity;
922            mBaseAlpha = bitmapState.mBaseAlpha;
923            mPaint = new Paint(bitmapState.mPaint);
924            mRebuildShader = bitmapState.mRebuildShader;
925            mAutoMirrored = bitmapState.mAutoMirrored;
926        }
927
928        @Override
929        public boolean canApplyTheme() {
930            return mThemeAttrs != null || mTint != null && mTint.canApplyTheme();
931        }
932
933        @Override
934        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
935            if (isAtlasable(mBitmap) && atlasList.add(mBitmap)) {
936                return mBitmap.getWidth() * mBitmap.getHeight();
937            }
938            return 0;
939        }
940
941        @Override
942        public Drawable newDrawable() {
943            return new BitmapDrawable(this, null);
944        }
945
946        @Override
947        public Drawable newDrawable(Resources res) {
948            return new BitmapDrawable(this, res);
949        }
950
951        @Override
952        public int getChangingConfigurations() {
953            return mChangingConfigurations
954                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
955        }
956    }
957
958    /**
959     * The one constructor to rule them all. This is called by all public
960     * constructors to set the state and initialize local properties.
961     */
962    private BitmapDrawable(BitmapState state, Resources res) {
963        mBitmapState = state;
964
965        updateLocalState(res);
966    }
967
968    /**
969     * Initializes local dynamic properties from state. This should be called
970     * after significant state changes, e.g. from the One True Constructor and
971     * after inflating or applying a theme.
972     */
973    private void updateLocalState(Resources res) {
974        if (res != null) {
975            mTargetDensity = res.getDisplayMetrics().densityDpi;
976        } else {
977            mTargetDensity = mBitmapState.mTargetDensity;
978        }
979
980        mTintFilter = updateTintFilter(mTintFilter, mBitmapState.mTint, mBitmapState.mTintMode);
981        computeBitmapSize();
982    }
983}
984