NinePatchDrawable.java revision 41551849e698129d4bd8861a1de9cb58f1300ec5
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics.drawable;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.res.ColorStateList;
22import android.content.res.Resources;
23import android.content.res.Resources.Theme;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
27import android.graphics.Canvas;
28import android.graphics.ColorFilter;
29import android.graphics.Insets;
30import android.graphics.NinePatch;
31import android.graphics.Outline;
32import android.graphics.Paint;
33import android.graphics.PixelFormat;
34import android.graphics.PorterDuff;
35import android.graphics.PorterDuff.Mode;
36import android.graphics.PorterDuffColorFilter;
37import android.graphics.Rect;
38import android.graphics.Region;
39import android.util.AttributeSet;
40import android.util.DisplayMetrics;
41import android.util.LayoutDirection;
42import android.util.TypedValue;
43
44import com.android.internal.R;
45
46import org.xmlpull.v1.XmlPullParser;
47import org.xmlpull.v1.XmlPullParserException;
48
49import java.io.IOException;
50import java.io.InputStream;
51import java.util.Collection;
52
53/**
54 *
55 * A resizeable bitmap, with stretchable areas that you define. This type of image
56 * is defined in a .png file with a special format.
57 *
58 * <div class="special reference">
59 * <h3>Developer Guides</h3>
60 * <p>For more information about how to use a NinePatchDrawable, read the
61 * <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">
62 * Canvas and Drawables</a> developer guide. For information about creating a NinePatch image
63 * file using the draw9patch tool, see the
64 * <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-patch</a> tool guide.</p></div>
65 */
66public class NinePatchDrawable extends Drawable {
67    // dithering helps a lot, and is pretty cheap, so default is true
68    private static final boolean DEFAULT_DITHER = false;
69    private NinePatchState mNinePatchState;
70    private NinePatch mNinePatch;
71    private PorterDuffColorFilter mTintFilter;
72    private Rect mPadding;
73    private Insets mOpticalInsets = Insets.NONE;
74    private Paint mPaint;
75    private boolean mMutated;
76
77    private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
78
79    // These are scaled to match the target density.
80    private int mBitmapWidth = -1;
81    private int mBitmapHeight = -1;
82
83    NinePatchDrawable() {
84        mNinePatchState = new NinePatchState();
85    }
86
87    /**
88     * Create drawable from raw nine-patch data, not dealing with density.
89     * @deprecated Use {@link #NinePatchDrawable(Resources, Bitmap, byte[], Rect, String)}
90     * to ensure that the drawable has correctly set its target density.
91     */
92    @Deprecated
93    public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
94        this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null);
95    }
96
97    /**
98     * Create drawable from raw nine-patch data, setting initial target density
99     * based on the display metrics of the resources.
100     */
101    public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
102            Rect padding, String srcName) {
103        this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res);
104        mNinePatchState.mTargetDensity = mTargetDensity;
105    }
106
107    /**
108     * Create drawable from raw nine-patch data, setting initial target density
109     * based on the display metrics of the resources.
110     *
111     * @hide
112     */
113    public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk,
114            Rect padding, Rect opticalInsets, String srcName) {
115        this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, opticalInsets),
116                res);
117        mNinePatchState.mTargetDensity = mTargetDensity;
118    }
119
120    /**
121     * Create drawable from existing nine-patch, not dealing with density.
122     * @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)}
123     * to ensure that the drawable has correctly set its target density.
124     */
125    @Deprecated
126    public NinePatchDrawable(NinePatch patch) {
127        this(new NinePatchState(patch, new Rect()), null);
128    }
129
130    /**
131     * Create drawable from existing nine-patch, setting initial target density
132     * based on the display metrics of the resources.
133     */
134    public NinePatchDrawable(Resources res, NinePatch patch) {
135        this(new NinePatchState(patch, new Rect()), res);
136        mNinePatchState.mTargetDensity = mTargetDensity;
137    }
138
139    /**
140     * Set the density scale at which this drawable will be rendered. This
141     * method assumes the drawable will be rendered at the same density as the
142     * specified canvas.
143     *
144     * @param canvas The Canvas from which the density scale must be obtained.
145     *
146     * @see android.graphics.Bitmap#setDensity(int)
147     * @see android.graphics.Bitmap#getDensity()
148     */
149    public void setTargetDensity(Canvas canvas) {
150        setTargetDensity(canvas.getDensity());
151    }
152
153    /**
154     * Set the density scale at which this drawable will be rendered.
155     *
156     * @param metrics The DisplayMetrics indicating the density scale for this drawable.
157     *
158     * @see android.graphics.Bitmap#setDensity(int)
159     * @see android.graphics.Bitmap#getDensity()
160     */
161    public void setTargetDensity(DisplayMetrics metrics) {
162        setTargetDensity(metrics.densityDpi);
163    }
164
165    /**
166     * Set the density at which this drawable will be rendered.
167     *
168     * @param density The density scale for this drawable.
169     *
170     * @see android.graphics.Bitmap#setDensity(int)
171     * @see android.graphics.Bitmap#getDensity()
172     */
173    public void setTargetDensity(int density) {
174        if (density != mTargetDensity) {
175            mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
176            if (mNinePatch != null) {
177                computeBitmapSize();
178            }
179            invalidateSelf();
180        }
181    }
182
183    private static Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
184        int left = Bitmap.scaleFromDensity(insets.left, sdensity, tdensity);
185        int top = Bitmap.scaleFromDensity(insets.top, sdensity, tdensity);
186        int right = Bitmap.scaleFromDensity(insets.right, sdensity, tdensity);
187        int bottom = Bitmap.scaleFromDensity(insets.bottom, sdensity, tdensity);
188        return Insets.of(left, top, right, bottom);
189    }
190
191    private void computeBitmapSize() {
192        final int sdensity = mNinePatch.getDensity();
193        final int tdensity = mTargetDensity;
194        if (sdensity == tdensity) {
195            mBitmapWidth = mNinePatch.getWidth();
196            mBitmapHeight = mNinePatch.getHeight();
197            mOpticalInsets = mNinePatchState.mOpticalInsets;
198        } else {
199            mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), sdensity, tdensity);
200            mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), sdensity, tdensity);
201            if (mNinePatchState.mPadding != null && mPadding != null) {
202                Rect dest = mPadding;
203                Rect src = mNinePatchState.mPadding;
204                if (dest == src) {
205                    mPadding = dest = new Rect(src);
206                }
207                dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
208                dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
209                dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
210                dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
211            }
212            mOpticalInsets = scaleFromDensity(mNinePatchState.mOpticalInsets, sdensity, tdensity);
213        }
214    }
215
216    /**
217     * Sets the nine patch used by this drawable.
218     *
219     * @param ninePatch the nine patch for this drawable
220     */
221    public void setNinePatch(NinePatch ninePatch) {
222        if (mNinePatch != ninePatch) {
223            mNinePatch = ninePatch;
224            if (ninePatch != null) {
225                computeBitmapSize();
226            } else {
227                mBitmapWidth = mBitmapHeight = -1;
228                mOpticalInsets = Insets.NONE;
229            }
230            invalidateSelf();
231        }
232    }
233
234    /**
235     * @return the nine patch used by this drawable
236     */
237    public NinePatch getNinePatch() {
238        return mNinePatch;
239    }
240
241    @Override
242    public void draw(Canvas canvas) {
243        final Rect bounds = getBounds();
244
245        final boolean clearColorFilter;
246        if (mTintFilter != null && getPaint().getColorFilter() == null) {
247            mPaint.setColorFilter(mTintFilter);
248            clearColorFilter = true;
249        } else {
250            clearColorFilter = false;
251        }
252
253        final boolean needsMirroring = needsMirroring();
254        if (needsMirroring) {
255            // Mirror the 9patch
256            canvas.translate(bounds.right - bounds.left, 0);
257            canvas.scale(-1.0f, 1.0f);
258        }
259
260        final int restoreAlpha;
261        if (mNinePatchState.mBaseAlpha != 1.0f) {
262            restoreAlpha = mPaint.getAlpha();
263            mPaint.setAlpha((int) (restoreAlpha * mNinePatchState.mBaseAlpha + 0.5f));
264        } else {
265            restoreAlpha = -1;
266        }
267
268        mNinePatch.draw(canvas, bounds, mPaint);
269
270        if (clearColorFilter) {
271            mPaint.setColorFilter(null);
272        }
273
274        if (restoreAlpha >= 0) {
275            mPaint.setAlpha(restoreAlpha);
276        }
277    }
278
279    @Override
280    public int getChangingConfigurations() {
281        return super.getChangingConfigurations() | mNinePatchState.getChangingConfigurations();
282    }
283
284    @Override
285    public boolean getPadding(Rect padding) {
286        final Rect scaledPadding = mPadding;
287        if (scaledPadding != null) {
288            if (needsMirroring()) {
289                padding.set(scaledPadding.right, scaledPadding.top,
290                        scaledPadding.left, scaledPadding.bottom);
291            } else {
292                padding.set(scaledPadding);
293            }
294            return (padding.left | padding.top | padding.right | padding.bottom) != 0;
295        }
296        return false;
297    }
298
299    @Override
300    public void getOutline(@NonNull Outline outline) {
301        final Rect bounds = getBounds();
302        if (bounds.isEmpty()) return;
303
304        if (mNinePatchState != null) {
305            NinePatch.InsetStruct insets = mNinePatchState.mNinePatch.getBitmap().getNinePatchInsets();
306            if (insets != null) {
307                final Rect outlineInsets = insets.outlineRect;
308                outline.setRoundRect(bounds.left + outlineInsets.left,
309                        bounds.top + outlineInsets.top,
310                        bounds.right - outlineInsets.right,
311                        bounds.bottom - outlineInsets.bottom,
312                        insets.outlineRadius);
313                outline.setAlpha(insets.outlineAlpha * (getAlpha() / 255.0f));
314                return;
315            }
316        }
317        super.getOutline(outline);
318    }
319
320    /**
321     * @hide
322     */
323    @Override
324    public Insets getOpticalInsets() {
325        if (needsMirroring()) {
326            return Insets.of(mOpticalInsets.right, mOpticalInsets.top,
327                    mOpticalInsets.left, mOpticalInsets.bottom);
328        } else {
329            return mOpticalInsets;
330        }
331    }
332
333    @Override
334    public void setAlpha(int alpha) {
335        if (mPaint == null && alpha == 0xFF) {
336            // Fast common case -- leave at normal alpha.
337            return;
338        }
339        getPaint().setAlpha(alpha);
340        invalidateSelf();
341    }
342
343    @Override
344    public int getAlpha() {
345        if (mPaint == null) {
346            // Fast common case -- normal alpha.
347            return 0xFF;
348        }
349        return getPaint().getAlpha();
350    }
351
352    @Override
353    public void setColorFilter(ColorFilter colorFilter) {
354        if (mPaint == null && colorFilter == null) {
355            // Fast common case -- leave at no color filter.
356            return;
357        }
358        getPaint().setColorFilter(colorFilter);
359        invalidateSelf();
360    }
361
362    @Override
363    public void setTintList(ColorStateList tint) {
364        mNinePatchState.mTint = tint;
365        mTintFilter = updateTintFilter(mTintFilter, tint, mNinePatchState.mTintMode);
366        invalidateSelf();
367    }
368
369    @Override
370    public void setTintMode(PorterDuff.Mode tintMode) {
371        mNinePatchState.mTintMode = tintMode;
372        mTintFilter = updateTintFilter(mTintFilter, mNinePatchState.mTint, tintMode);
373        invalidateSelf();
374    }
375
376    @Override
377    public void setDither(boolean dither) {
378        //noinspection PointlessBooleanExpression
379        if (mPaint == null && dither == DEFAULT_DITHER) {
380            // Fast common case -- leave at default dither.
381            return;
382        }
383
384        getPaint().setDither(dither);
385        invalidateSelf();
386    }
387
388    @Override
389    public void setAutoMirrored(boolean mirrored) {
390        mNinePatchState.mAutoMirrored = mirrored;
391    }
392
393    private boolean needsMirroring() {
394        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
395    }
396
397    @Override
398    public boolean isAutoMirrored() {
399        return mNinePatchState.mAutoMirrored;
400    }
401
402    @Override
403    public void setFilterBitmap(boolean filter) {
404        getPaint().setFilterBitmap(filter);
405        invalidateSelf();
406    }
407
408    @Override
409    public boolean isFilterBitmap() {
410        if (mPaint == null) {
411            return false;
412        }
413        return getPaint().isFilterBitmap();
414    }
415
416    @Override
417    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
418            throws XmlPullParserException, IOException {
419        super.inflate(r, parser, attrs, theme);
420
421        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable);
422        updateStateFromTypedArray(a);
423        a.recycle();
424
425        updateLocalState(r);
426    }
427
428    /**
429     * Updates the constant state from the values in the typed array.
430     */
431    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
432        final Resources r = a.getResources();
433        final NinePatchState state = mNinePatchState;
434
435        // Account for any configuration changes.
436        state.mChangingConfigurations |= a.getChangingConfigurations();
437
438        // Extract the theme attributes, if any.
439        state.mThemeAttrs = a.extractThemeAttrs();
440
441        state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither);
442
443        final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
444        if (srcResId != 0) {
445            final BitmapFactory.Options options = new BitmapFactory.Options();
446            options.inDither = !state.mDither;
447            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
448
449            final Rect padding = new Rect();
450            final Rect opticalInsets = new Rect();
451            Bitmap bitmap = null;
452
453            try {
454                final TypedValue value = new TypedValue();
455                final InputStream is = r.openRawResource(srcResId, value);
456
457                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
458
459                is.close();
460            } catch (IOException e) {
461                // Ignore
462            }
463
464            if (bitmap == null) {
465                throw new XmlPullParserException(a.getPositionDescription() +
466                        ": <nine-patch> requires a valid src attribute");
467            } else if (bitmap.getNinePatchChunk() == null) {
468                throw new XmlPullParserException(a.getPositionDescription() +
469                        ": <nine-patch> requires a valid 9-patch source image");
470            }
471
472            bitmap.getOpticalInsets(opticalInsets);
473
474            state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk());
475            state.mPadding = padding;
476            state.mOpticalInsets = Insets.of(opticalInsets);
477        }
478
479        state.mAutoMirrored = a.getBoolean(
480                R.styleable.NinePatchDrawable_autoMirrored, state.mAutoMirrored);
481        state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, state.mBaseAlpha);
482
483        final int tintMode = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1);
484        if (tintMode != -1) {
485            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
486        }
487
488        final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint);
489        if (tint != null) {
490            state.mTint = tint;
491        }
492
493        final int densityDpi = r.getDisplayMetrics().densityDpi;
494        state.mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
495    }
496
497    @Override
498    public void applyTheme(Theme t) {
499        super.applyTheme(t);
500
501        final NinePatchState state = mNinePatchState;
502        if (state == null) {
503            return;
504        }
505
506        if (state.mThemeAttrs != null) {
507            final TypedArray a = t.resolveAttributes(
508                    state.mThemeAttrs, R.styleable.NinePatchDrawable);
509            try {
510                updateStateFromTypedArray(a);
511            } catch (XmlPullParserException e) {
512                throw new RuntimeException(e);
513            } finally {
514                a.recycle();
515            }
516        }
517
518        if (state.mTint != null && state.mTint.canApplyTheme()) {
519            state.mTint = state.mTint.obtainForTheme(t);
520        }
521
522        updateLocalState(t.getResources());
523    }
524
525    @Override
526    public boolean canApplyTheme() {
527        return mNinePatchState != null && mNinePatchState.canApplyTheme();
528    }
529
530    public Paint getPaint() {
531        if (mPaint == null) {
532            mPaint = new Paint();
533            mPaint.setDither(DEFAULT_DITHER);
534        }
535        return mPaint;
536    }
537
538    /**
539     * Retrieves the width of the source .png file (before resizing).
540     */
541    @Override
542    public int getIntrinsicWidth() {
543        return mBitmapWidth;
544    }
545
546    /**
547     * Retrieves the height of the source .png file (before resizing).
548     */
549    @Override
550    public int getIntrinsicHeight() {
551        return mBitmapHeight;
552    }
553
554    @Override
555    public int getMinimumWidth() {
556        return mBitmapWidth;
557    }
558
559    @Override
560    public int getMinimumHeight() {
561        return mBitmapHeight;
562    }
563
564    /**
565     * Returns a {@link android.graphics.PixelFormat graphics.PixelFormat}
566     * value of OPAQUE or TRANSLUCENT.
567     */
568    @Override
569    public int getOpacity() {
570        return mNinePatch.hasAlpha() || (mPaint != null && mPaint.getAlpha() < 255) ?
571                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
572    }
573
574    @Override
575    public Region getTransparentRegion() {
576        return mNinePatch.getTransparentRegion(getBounds());
577    }
578
579    @Override
580    public ConstantState getConstantState() {
581        mNinePatchState.mChangingConfigurations = getChangingConfigurations();
582        return mNinePatchState;
583    }
584
585    @Override
586    public Drawable mutate() {
587        if (!mMutated && super.mutate() == this) {
588            mNinePatchState = new NinePatchState(mNinePatchState);
589            mNinePatch = mNinePatchState.mNinePatch;
590            mMutated = true;
591        }
592        return this;
593    }
594
595    /**
596     * @hide
597     */
598    public void clearMutated() {
599        super.clearMutated();
600        mMutated = false;
601    }
602
603    @Override
604    protected boolean onStateChange(int[] stateSet) {
605        final NinePatchState state = mNinePatchState;
606        if (state.mTint != null && state.mTintMode != null) {
607            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
608            return true;
609        }
610
611        return false;
612    }
613
614    @Override
615    public boolean isStateful() {
616        final NinePatchState s = mNinePatchState;
617        return super.isStateful() || (s.mTint != null && s.mTint.isStateful());
618    }
619
620    final static class NinePatchState extends ConstantState {
621        // Values loaded during inflation.
622        int[] mThemeAttrs = null;
623        NinePatch mNinePatch = null;
624        ColorStateList mTint = null;
625        Mode mTintMode = DEFAULT_TINT_MODE;
626        Rect mPadding = null;
627        Insets mOpticalInsets = Insets.NONE;
628        float mBaseAlpha = 1.0f;
629        boolean mDither = DEFAULT_DITHER;
630        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
631        boolean mAutoMirrored = false;
632
633        int mChangingConfigurations;
634
635        NinePatchState() {
636            // Empty constructor.
637        }
638
639        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding) {
640            this(ninePatch, padding, null, DEFAULT_DITHER, false);
641        }
642
643        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
644                @Nullable Rect opticalInsets) {
645            this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
646        }
647
648        NinePatchState(@NonNull NinePatch ninePatch, @Nullable Rect padding,
649                @Nullable Rect opticalInsets, boolean dither, boolean autoMirror) {
650            mNinePatch = ninePatch;
651            mPadding = padding;
652            mOpticalInsets = Insets.of(opticalInsets);
653            mDither = dither;
654            mAutoMirrored = autoMirror;
655        }
656
657        // Copy constructor
658
659        NinePatchState(@NonNull NinePatchState state) {
660            // We don't deep-copy any fields because they are all immutable.
661            mNinePatch = state.mNinePatch;
662            mTint = state.mTint;
663            mTintMode = state.mTintMode;
664            mThemeAttrs = state.mThemeAttrs;
665            mPadding = state.mPadding;
666            mOpticalInsets = state.mOpticalInsets;
667            mBaseAlpha = state.mBaseAlpha;
668            mDither = state.mDither;
669            mChangingConfigurations = state.mChangingConfigurations;
670            mTargetDensity = state.mTargetDensity;
671            mAutoMirrored = state.mAutoMirrored;
672        }
673
674        @Override
675        public boolean canApplyTheme() {
676            return mThemeAttrs != null
677                    || (mTint != null && mTint.canApplyTheme());
678        }
679
680        @Override
681        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
682            final Bitmap bitmap = mNinePatch.getBitmap();
683            if (isAtlasable(bitmap) && atlasList.add(bitmap)) {
684                return bitmap.getWidth() * bitmap.getHeight();
685            }
686            return 0;
687        }
688
689        @Override
690        public Drawable newDrawable() {
691            return new NinePatchDrawable(this, null);
692        }
693
694        @Override
695        public Drawable newDrawable(Resources res) {
696            return new NinePatchDrawable(this, res);
697        }
698
699        @Override
700        public int getChangingConfigurations() {
701            return mChangingConfigurations
702                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
703        }
704    }
705
706    /**
707     * The one constructor to rule them all. This is called by all public
708     * constructors to set the state and initialize local properties.
709     */
710    private NinePatchDrawable(NinePatchState state, Resources res) {
711        mNinePatchState = state;
712
713        updateLocalState(res);
714
715        // Push density applied by setNinePatchState into state.
716        mNinePatchState.mTargetDensity = mTargetDensity;
717    }
718
719    /**
720     * Initializes local dynamic properties from state.
721     */
722    private void updateLocalState(Resources res) {
723        final NinePatchState state = mNinePatchState;
724
725        if (res != null) {
726            final int densityDpi = res.getDisplayMetrics().densityDpi;
727            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
728        } else {
729            mTargetDensity = state.mTargetDensity;
730        }
731
732
733        // If we can, avoid calling any methods that initialize Paint.
734        if (state.mDither != DEFAULT_DITHER) {
735            setDither(state.mDither);
736        }
737
738        // Make a local copy of the padding.
739        if (state.mPadding != null) {
740            mPadding = new Rect(state.mPadding);
741        }
742
743        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
744        setNinePatch(state.mNinePatch);
745    }
746}
747