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><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