Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f1b06b5 authored by Leon Scroggins III's avatar Leon Scroggins III
Browse files

Reland "Use ImageDecoder for NinePatchDrawable and BitmapDrawable"

This reverts commit ac9c8f7c.

Bug: 73083791
Bug: 73364985

Don't scale NinePatchDrawable if bitmap has no density (including fix
for 73364985).
Fixed bugs around density and input streams.

Update PointerIcon to account for the fact that BitmapDrawable no longer
scales its Bitmap up at decode time. PointerIcon now handles the
scaling. This is necessary because PointerIcon never draws its Bitmap.
Instead, native code uses the Bitmap's internal SkBitmap without
accounting for density.

Test: Ran CTS:
- CtsUiRenderingTestCases
- CtsGraphicsTestCases
  - I2d3976061d164ab4d58209db1320917f272a1958
- CtsViewTestCases
- ThemeHostTests

Change-Id: I3e0c11195622d65f084ce79dad887504630177ca
parent ee3a2ba9
Loading
Loading
Loading
Loading
+35 −2
Original line number Original line Diff line number Diff line
@@ -23,6 +23,10 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable {
        return true;
        return true;
    }
    }


    /**
     *  Get the Bitmap from the Drawable.
     *
     *  If the Bitmap needed to be scaled up to account for density, BitmapDrawable
     *  handles this at draw time. But this class doesn't actually draw the Bitmap;
     *  it is just a holder for native code to access its SkBitmap. So this needs to
     *  get a version that is scaled to account for density.
     */
    private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
        Bitmap bitmap = bitmapDrawable.getBitmap();
        final int scaledWidth  = bitmapDrawable.getIntrinsicWidth();
        final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
        if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
            return bitmap;
        }

        Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);

        Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
        Canvas canvas = new Canvas(scaled);
        Paint paint = new Paint();
        paint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, src, dst, paint);
        return scaled;
    }

    private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
    private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
        final XmlResourceParser parser = resources.getXml(resourceId);
        final XmlResourceParser parser = resources.getXml(resourceId);
        final int bitmapRes;
        final int bitmapRes;
@@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable {
                                + "is different. All frames should have the exact same size and "
                                + "is different. All frames should have the exact same size and "
                                + "share the same hotspot.");
                                + "share the same hotspot.");
                    }
                    }
                    mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
                    BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
                    mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
                }
                }
            }
            }
        }
        }
@@ -461,7 +493,8 @@ public final class PointerIcon implements Parcelable {
                    + "refer to a bitmap drawable.");
                    + "refer to a bitmap drawable.");
        }
        }


        final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
        validateHotSpot(bitmap, hotSpotX, hotSpotY);
        validateHotSpot(bitmap, hotSpotX, hotSpotY);
        // Set the properties now that we have successfully loaded the icon.
        // Set the properties now that we have successfully loaded the icon.
        mBitmap = bitmap;
        mBitmap = bitmap;
+26 −2
Original line number Original line Diff line number Diff line
@@ -444,6 +444,7 @@ public final class ImageDecoder implements AutoCloseable {
    private boolean mPreferRamOverQuality = false;
    private boolean mPreferRamOverQuality = false;
    private boolean mAsAlphaMask = false;
    private boolean mAsAlphaMask = false;
    private Rect    mCropRect;
    private Rect    mCropRect;
    private Rect    mOutPaddingRect;
    private Source  mSource;
    private Source  mSource;


    private PostProcessor          mPostProcessor;
    private PostProcessor          mPostProcessor;
@@ -781,6 +782,18 @@ public final class ImageDecoder implements AutoCloseable {
        mCropRect = subset;
        mCropRect = subset;
    }
    }


    /**
     *  Set a Rect for retrieving nine patch padding.
     *
     *  If the image is a nine patch, this Rect will be set to the padding
     *  rectangle during decode. Otherwise it will not be modified.
     *
     *  @hide
     */
    public void setOutPaddingRect(@NonNull Rect outPadding) {
        mOutPaddingRect = outPadding;
    }

    /**
    /**
     *  Specify whether the {@link Bitmap} should be mutable.
     *  Specify whether the {@link Bitmap} should be mutable.
     *
     *
@@ -892,7 +905,6 @@ public final class ImageDecoder implements AutoCloseable {
                postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
                postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
                mMutable, mAllocator, mRequireUnpremultiplied,
                mMutable, mAllocator, mRequireUnpremultiplied,
                mPreferRamOverQuality, mAsAlphaMask);
                mPreferRamOverQuality, mAsAlphaMask);

    }
    }


    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -965,7 +977,10 @@ public final class ImageDecoder implements AutoCloseable {
            if (np != null && NinePatch.isNinePatchChunk(np)) {
            if (np != null && NinePatch.isNinePatchChunk(np)) {
                Rect opticalInsets = new Rect();
                Rect opticalInsets = new Rect();
                bm.getOpticalInsets(opticalInsets);
                bm.getOpticalInsets(opticalInsets);
                Rect padding = new Rect();
                Rect padding = decoder.mOutPaddingRect;
                if (padding == null) {
                    padding = new Rect();
                }
                nGetPadding(decoder.mNativePtr, padding);
                nGetPadding(decoder.mNativePtr, padding);
                return new NinePatchDrawable(res, bm, np, padding,
                return new NinePatchDrawable(res, bm, np, padding,
                        opticalInsets, null);
                        opticalInsets, null);
@@ -1008,6 +1023,15 @@ public final class ImageDecoder implements AutoCloseable {
            final int srcDensity = computeDensity(src, decoder);
            final int srcDensity = computeDensity(src, decoder);
            Bitmap bm = decoder.decodeBitmap();
            Bitmap bm = decoder.decodeBitmap();
            bm.setDensity(srcDensity);
            bm.setDensity(srcDensity);

            Rect padding = decoder.mOutPaddingRect;
            if (padding != null) {
                byte[] np = bm.getNinePatchChunk();
                if (np != null && NinePatch.isNinePatchChunk(np)) {
                    nGetPadding(decoder.mNativePtr, padding);
                }
            }

            return bm;
            return bm;
        }
        }
    }
    }
+59 −26
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Outline;
@@ -49,6 +50,7 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserException;


import java.io.FileInputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;


@@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @Deprecated
    @Deprecated
    public BitmapDrawable() {
    public BitmapDrawable() {
        mBitmapState = new BitmapState((Bitmap) null);
        init(new BitmapState((Bitmap) null), null);
    }
    }


    /**
    /**
@@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable {
    @SuppressWarnings("unused")
    @SuppressWarnings("unused")
    @Deprecated
    @Deprecated
    public BitmapDrawable(Resources res) {
    public BitmapDrawable(Resources res) {
        mBitmapState = new BitmapState((Bitmap) null);
        init(new BitmapState((Bitmap) null), res);
        mBitmapState.mTargetDensity = mTargetDensity;
    }
    }


    /**
    /**
@@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @Deprecated
    @Deprecated
    public BitmapDrawable(Bitmap bitmap) {
    public BitmapDrawable(Bitmap bitmap) {
        this(new BitmapState(bitmap), null);
        init(new BitmapState(bitmap), null);
    }
    }


    /**
    /**
@@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable {
     * the display metrics of the resources.
     * the display metrics of the resources.
     */
     */
    public BitmapDrawable(Resources res, Bitmap bitmap) {
    public BitmapDrawable(Resources res, Bitmap bitmap) {
        this(new BitmapState(bitmap), res);
        init(new BitmapState(bitmap), res);
        mBitmapState.mTargetDensity = mTargetDensity;
    }
    }


    /**
    /**
@@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @Deprecated
    @Deprecated
    public BitmapDrawable(String filepath) {
    public BitmapDrawable(String filepath) {
        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
        this(null, filepath);
        if (mBitmapState.mBitmap == null) {
            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
        }
    }
    }


    /**
    /**
@@ -165,12 +162,23 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
    public BitmapDrawable(Resources res, String filepath) {
    public BitmapDrawable(Resources res, String filepath) {
        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
        Bitmap bitmap = null;
        mBitmapState.mTargetDensity = mTargetDensity;
        try (FileInputStream stream = new FileInputStream(filepath)) {
            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
                    (decoder, info, src) -> {
                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
            });
        } catch (Exception e) {
            /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
                If the exception happened on decode, mBitmapState.mBitmap will be null.
            */
        } finally {
            init(new BitmapState(bitmap), res);
            if (mBitmapState.mBitmap == null) {
            if (mBitmapState.mBitmap == null) {
                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
            }
            }
        }
        }
    }


    /**
    /**
     * Create a drawable by decoding a bitmap from the given input stream.
     * Create a drawable by decoding a bitmap from the given input stream.
@@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @Deprecated
    @Deprecated
    public BitmapDrawable(java.io.InputStream is) {
    public BitmapDrawable(java.io.InputStream is) {
        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
        this(null, is);
        if (mBitmapState.mBitmap == null) {
            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
        }
    }
    }


    /**
    /**
@@ -190,12 +195,23 @@ public class BitmapDrawable extends Drawable {
     */
     */
    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
    @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
    public BitmapDrawable(Resources res, java.io.InputStream is) {
    public BitmapDrawable(Resources res, java.io.InputStream is) {
        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
        Bitmap bitmap = null;
        mBitmapState.mTargetDensity = mTargetDensity;
        try {
            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
                    (decoder, info, src) -> {
                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
            });
        } catch (Exception e) {
            /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
                If the exception happened on decode, mBitmapState.mBitmap will be null.
            */
        } finally {
            init(new BitmapState(bitmap), res);
            if (mBitmapState.mBitmap == null) {
            if (mBitmapState.mBitmap == null) {
                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
            }
            }
        }
        }
    }


    /**
    /**
     * Returns the paint used to render this drawable.
     * Returns the paint used to render this drawable.
@@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable {
                }
                }
            }
            }


            int density = Bitmap.DENSITY_NONE;
            if (value.density == TypedValue.DENSITY_DEFAULT) {
                density = DisplayMetrics.DENSITY_DEFAULT;
            } else if (value.density != TypedValue.DENSITY_NONE) {
                density = value.density;
            }

            Bitmap bitmap = null;
            Bitmap bitmap = null;
            try (InputStream is = r.openRawResource(srcResId, value)) {
            try (InputStream is = r.openRawResource(srcResId, value)) {
                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
                });
            } catch (Exception e) {
            } catch (Exception e) {
                // Do nothing and pick up the error below.
                // Do nothing and pick up the error below.
            }
            }
@@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable {
        }
        }
    }
    }


    private BitmapDrawable(BitmapState state, Resources res) {
        init(state, res);
    }

    /**
    /**
     * The one constructor to rule them all. This is called by all public
     * The one helper to rule them all. This is called by all public & private
     * constructors to set the state and initialize local properties.
     * constructors to set the state and initialize local properties.
     */
     */
    private BitmapDrawable(BitmapState state, Resources res) {
    private void init(BitmapState state, Resources res) {
        mBitmapState = state;
        mBitmapState = state;

        updateLocalState(res);
        updateLocalState(res);

        if (mBitmapState != null && res != null) {
            mBitmapState.mTargetDensity = mTargetDensity;
        }
    }
    }


    /**
    /**
+38 −6
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.Outline;
@@ -50,11 +51,13 @@ import android.graphics.Xfermode;
import android.os.Trace;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.StateSet;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.TypedValue;
import android.util.Xml;
import android.util.Xml;
import android.view.View;
import android.view.View;


import java.io.FileInputStream;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
@@ -1179,6 +1182,10 @@ public abstract class Drawable {
            return null;
            return null;
        }
        }


        if (opts == null) {
            return getBitmapDrawable(res, value, is);
        }

        /*  ugh. The decodeStream contract is that we have already allocated
        /*  ugh. The decodeStream contract is that we have already allocated
            the pad rect, but if the bitmap does not had a ninepatch chunk,
            the pad rect, but if the bitmap does not had a ninepatch chunk,
            then the pad will be ignored. If we could change this to lazily
            then the pad will be ignored. If we could change this to lazily
@@ -1194,7 +1201,6 @@ public abstract class Drawable {
        // an application in compatibility mode, without scaling those down
        // an application in compatibility mode, without scaling those down
        // to the compatibility density only to have them scaled back up when
        // to the compatibility density only to have them scaled back up when
        // drawn to the screen.
        // drawn to the screen.
        if (opts == null) opts = new BitmapFactory.Options();
        opts.inScreenDensity = Drawable.resolveDensity(res, 0);
        opts.inScreenDensity = Drawable.resolveDensity(res, 0);
        Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
        Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
        if (bm != null) {
        if (bm != null) {
@@ -1211,6 +1217,33 @@ public abstract class Drawable {
        return null;
        return null;
    }
    }


    private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
        try {
            ImageDecoder.Source source = null;
            if (value != null) {
                int density = Bitmap.DENSITY_NONE;
                if (value.density == TypedValue.DENSITY_DEFAULT) {
                    density = DisplayMetrics.DENSITY_DEFAULT;
                } else if (value.density != TypedValue.DENSITY_NONE) {
                    density = value.density;
                }
                source = ImageDecoder.createSource(res, is, density);
            } else {
                source = ImageDecoder.createSource(res, is);
            }

            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
            });
        } catch (IOException e) {
            /*  do nothing.
                If the exception happened on decode, the drawable will be null.
            */
            Log.e("Drawable", "Unable to decode stream: " + e);
        }
        return null;
    }

    /**
    /**
     * Create a drawable from an XML document. For more information on how to
     * Create a drawable from an XML document. For more information on how to
     * create resources in XML, see
     * create resources in XML, see
@@ -1310,11 +1343,10 @@ public abstract class Drawable {
        }
        }


        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
        try {
        try (FileInputStream stream = new FileInputStream(pathName)) {
            Bitmap bm = BitmapFactory.decodeFile(pathName);
            return getBitmapDrawable(null, null, stream);
            if (bm != null) {
        } catch(IOException e) {
                return drawableFromBitmap(null, bm, null, null, null, pathName);
            // Do nothing; we will just return null if the FileInputStream had an error
            }
        } finally {
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
        }
+16 −8
Original line number Original line Diff line number Diff line
@@ -24,9 +24,9 @@ import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.Insets;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.Outline;
@@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable {
            restoreAlpha = -1;
            restoreAlpha = -1;
        }
        }


        final boolean needsDensityScaling = canvas.getDensity() == 0;
        final boolean needsDensityScaling = canvas.getDensity() == 0
                && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
        if (needsDensityScaling) {
        if (needsDensityScaling) {
            restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
            restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();


@@ -421,10 +422,6 @@ public class NinePatchDrawable extends Drawable {


        final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
        final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
        if (srcResId != 0) {
        if (srcResId != 0) {
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inDither = !state.mDither;
            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;

            final Rect padding = new Rect();
            final Rect padding = new Rect();
            final Rect opticalInsets = new Rect();
            final Rect opticalInsets = new Rect();
            Bitmap bitmap = null;
            Bitmap bitmap = null;
@@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable {
                final TypedValue value = new TypedValue();
                final TypedValue value = new TypedValue();
                final InputStream is = r.openRawResource(srcResId, value);
                final InputStream is = r.openRawResource(srcResId, value);


                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
                int density = Bitmap.DENSITY_NONE;
                if (value.density == TypedValue.DENSITY_DEFAULT) {
                    density = DisplayMetrics.DENSITY_DEFAULT;
                } else if (value.density != TypedValue.DENSITY_NONE) {
                    density = value.density;
                }
                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
                    decoder.setOutPaddingRect(padding);
                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
                });


                is.close();
                is.close();
            } catch (IOException e) {
            } catch (IOException e) {
@@ -660,8 +667,9 @@ public class NinePatchDrawable extends Drawable {
            return;
            return;
        }
        }


        final int sourceDensity = ninePatch.getDensity();
        final int targetDensity = mTargetDensity;
        final int targetDensity = mTargetDensity;
        final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ?
            targetDensity : ninePatch.getDensity();


        final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets;
        final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets;
        if (sourceOpticalInsets != Insets.NONE) {
        if (sourceOpticalInsets != Insets.NONE) {