diff options
| author | 2018-02-02 19:20:48 +0000 | |
|---|---|---|
| committer | 2018-02-02 19:20:48 +0000 | |
| commit | 5ecc69effa24ea8df6dfb0c7e07ad8b77728e42f (patch) | |
| tree | 05a16ed54b252b84933de400215732bcbe3bc8c8 | |
| parent | c48b5eff34477da85762835bc521065ae914c31b (diff) | |
| parent | 5aec67ca1baaa5ce7af5062cec0437a72969d2f9 (diff) | |
Merge "Use ImageDecoder for NinePatchDrawable and BitmapDrawable"
5 files changed, 172 insertions, 42 deletions
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 3fd469630db0..8cb46b704c18 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -23,6 +23,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; 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.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable { 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) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; @@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable { + "is different. All frames should have the exact same size and " + "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."); } - final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable); validateHotSpot(bitmap, hotSpotX, hotSpotY); // Set the properties now that we have successfully loaded the icon. mBitmap = bitmap; diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index bbf214559459..7405664c2a59 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -438,6 +438,7 @@ public final class ImageDecoder implements AutoCloseable { private boolean mPreferRamOverQuality = false; private boolean mAsAlphaMask = false; private Rect mCropRect; + private Rect mOutPaddingRect; private Source mSource; private PostProcessor mPostProcessor; @@ -765,6 +766,18 @@ public final class ImageDecoder implements AutoCloseable { } /** + * 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. * * <p>By default, a {@link Bitmap} created will be immutable, but that can @@ -875,7 +888,6 @@ public final class ImageDecoder implements AutoCloseable { postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, mPreferRamOverQuality, mAsAlphaMask); - } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -948,7 +960,10 @@ public final class ImageDecoder implements AutoCloseable { if (np != null && NinePatch.isNinePatchChunk(np)) { Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); - Rect padding = new Rect(); + Rect padding = decoder.mOutPaddingRect; + if (padding == null) { + padding = new Rect(); + } nGetPadding(decoder.mNativePtr, padding); return new NinePatchDrawable(res, bm, np, padding, opticalInsets, null); @@ -991,6 +1006,15 @@ public final class ImageDecoder implements AutoCloseable { final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); 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; } } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 7ad062a6f6f9..44b783bb6e63 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated 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. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + 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) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + 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) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -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; 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) { // 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. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f17cd768c386..36a4d26d62bb 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1175,6 +1178,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated 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 @@ -1207,6 +1214,33 @@ public abstract class Drawable { 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 resources in XML, see @@ -1306,11 +1340,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 17900204fa22..a56e8d1b25ed 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -24,9 +24,9 @@ import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable { restoreAlpha = -1; } - final boolean needsDensityScaling = canvas.getDensity() == 0; + final boolean needsDensityScaling = canvas.getDensity() == 0 + && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity(); if (needsDensityScaling) { 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); 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 opticalInsets = new Rect(); Bitmap bitmap = null; @@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable { final TypedValue value = new TypedValue(); 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(); } catch (IOException e) { |