Loading graphics/java/android/graphics/ImageDecoder.java +133 −7 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ import android.graphics.drawable.NinePatchDrawable; import android.net.Uri; import android.system.ErrnoException; import android.system.Os; import android.util.DisplayMetrics; import android.util.TypedValue; import libcore.io.IoUtils; import dalvik.system.CloseGuard; Loading Loading @@ -63,6 +65,19 @@ public final class ImageDecoder implements AutoCloseable { /* @hide */ Resources getResources() { return null; } /* @hide */ int getDensity() { return Bitmap.DENSITY_NONE; } /* @hide */ int computeDstDensity() { Resources res = getResources(); if (res == null) { return Bitmap.getDefaultDensity(); } return res.getDisplayMetrics().densityDpi; } /* @hide */ abstract ImageDecoder createImageDecoder() throws IOException; }; Loading Loading @@ -170,26 +185,73 @@ public final class ImageDecoder implements AutoCloseable { return decoder; } private static class InputStreamSource extends Source { InputStreamSource(Resources res, InputStream is, int inputDensity) { if (is == null) { throw new IllegalArgumentException("The InputStream cannot be null"); } mResources = res; mInputStream = is; mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; } final Resources mResources; InputStream mInputStream; final int mInputDensity; @Override public Resources getResources() { return mResources; } @Override public int getDensity() { return mInputDensity; } @Override public ImageDecoder createImageDecoder() throws IOException { synchronized (this) { if (mInputStream == null) { throw new IOException("Cannot reuse InputStreamSource"); } InputStream is = mInputStream; mInputStream = null; return createFromStream(is); } } } private static class ResourceSource extends Source { ResourceSource(Resources res, int resId) { mResources = res; mResId = resId; mResDensity = Bitmap.DENSITY_NONE; } final Resources mResources; final int mResId; int mResDensity; @Override public Resources getResources() { return mResources; } @Override public int getDensity() { return mResDensity; } @Override public ImageDecoder createImageDecoder() throws IOException { // This is just used in order to access the underlying Asset and // keep it alive. FIXME: Can we skip creating this object? InputStream is = null; ImageDecoder decoder = null; TypedValue value = new TypedValue(); try { is = mResources.openRawResource(mResId); is = mResources.openRawResource(mResId, value); if (value.density == TypedValue.DENSITY_DEFAULT) { mResDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (value.density != TypedValue.DENSITY_NONE) { mResDensity = value.density; } if (!(is instanceof AssetManager.AssetInputStream)) { // This should never happen. throw new RuntimeException("Resource is not an asset?"); Loading Loading @@ -420,6 +482,22 @@ public final class ImageDecoder implements AutoCloseable { return new ByteBufferSource(buffer); } /** * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) * @hide */ public static Source createSource(Resources res, InputStream is) { return new InputStreamSource(res, is, Bitmap.getDefaultDensity()); } /** * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) * @hide */ public static Source createSource(Resources res, InputStream is, int density) { return new InputStreamSource(res, is, density); } /** * Return the width and height of a given sample size. * Loading Loading @@ -476,6 +554,10 @@ public final class ImageDecoder implements AutoCloseable { this.resize(dimensions.x, dimensions.y); } private boolean requestedResize() { return mWidth != mDesiredWidth || mHeight != mDesiredHeight; } // These need to stay in sync with ImageDecoder.cpp's Allocator enum. /** * Use the default allocation for the pixel memory. Loading Loading @@ -730,6 +812,9 @@ public final class ImageDecoder implements AutoCloseable { "Drawable!"); } // this call potentially manipulates the decoder so it must be performed prior to // decoding the bitmap and after decode set the density on the resulting bitmap final int srcDensity = computeDensity(src, decoder); if (decoder.mAnimated) { // AnimatedImageDrawable calls postProcessAndRelease only if // mPostProcess exists. Loading @@ -737,7 +822,8 @@ public final class ImageDecoder implements AutoCloseable { null : decoder; Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, postProcessPtr, decoder.mDesiredWidth, decoder.mDesiredHeight, decoder.mCropRect, decoder.mDesiredHeight, srcDensity, src.computeDstDensity(), decoder.mCropRect, decoder.mInputStream, decoder.mAssetFd); // d has taken ownership of these objects. decoder.mInputStream = null; Loading @@ -746,13 +832,15 @@ public final class ImageDecoder implements AutoCloseable { } Bitmap bm = decoder.decodeBitmap(); Resources res = src.getResources(); if (res == null) { bm.setDensity(Bitmap.DENSITY_NONE); } bm.setDensity(srcDensity); Resources res = src.getResources(); byte[] np = bm.getNinePatchChunk(); if (np != null && NinePatch.isNinePatchChunk(np)) { if (res != null) { bm.setDensity(res.getDisplayMetrics().densityDpi); } Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); Rect padding = new Rect(); Loading Loading @@ -799,10 +887,48 @@ public final class ImageDecoder implements AutoCloseable { } } return decoder.decodeBitmap(); // this call potentially manipulates the decoder so it must be performed prior to // decoding the bitmap final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); bm.setDensity(srcDensity); return bm; } } // This method may modify the decoder so it must be called prior to performing the decode private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) { // if the caller changed the size then we treat the density as unknown if (decoder.requestedResize()) { return Bitmap.DENSITY_NONE; } // Special stuff for compatibility mode: if the target density is not // the same as the display density, but the resource -is- the same as // the display density, then don't scale it down to the target density. // This allows us to load the system's density-correct resources into // an application in compatibility mode, without scaling those down // to the compatibility density only to have them scaled back up when // drawn to the screen. Resources res = src.getResources(); final int srcDensity = src.getDensity(); if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) { return srcDensity; } // downscale the bitmap if the asset has a higher density than the default final int dstDensity = src.computeDstDensity(); if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) { float scale = (float) dstDensity / srcDensity; int scaledWidth = (int) (decoder.mWidth * scale + 0.5f); int scaledHeight = (int) (decoder.mHeight * scale + 0.5f); decoder.resize(scaledWidth, scaledHeight); return dstDensity; } return srcDensity; } private String getMimeType() { return nGetMimeType(mNativePtr); } Loading graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +16 −5 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.ImageDecoder; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.SystemClock; import android.util.DisplayMetrics; import libcore.io.IoUtils; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -59,22 +61,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { * decoder is only non-null if it has a PostProcess */ public AnimatedImageDrawable(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect, @Nullable ImageDecoder decoder, int width, int height, int srcDensity, int dstDensity, Rect cropRect, InputStream inputStream, AssetFileDescriptor afd) throws IOException { mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); mInputStream = inputStream; mAssetFd = afd; width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity); height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity); if (cropRect == null) { mIntrinsicWidth = width; mIntrinsicHeight = height; } else { cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity)); mIntrinsicWidth = cropRect.width(); mIntrinsicHeight = cropRect.height(); } long nativeSize = nNativeByteSize(mNativePtr); mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); mInputStream = inputStream; mAssetFd = afd; // FIXME: Use the right size for the native allocation. long nativeSize = 200; NativeAllocationRegistry registry = new NativeAllocationRegistry( AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); registry.registerNativeAllocation(this, mNativePtr); Loading graphics/java/android/graphics/drawable/BitmapDrawable.java +59 −26 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { mBitmapState = new BitmapState((Bitmap) null); init(new BitmapState((Bitmap) null), null); } /** Loading @@ -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); } /** Loading @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { this(new BitmapState(bitmap), null); init(new BitmapState(bitmap), null); } /** Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -165,12 +162,23 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); mBitmapState.mTargetDensity = mTargetDensity; Bitmap bitmap = null; try (FileInputStream stream = new FileInputStream(filepath)) { bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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); } } } /** * Create a drawable by decoding a bitmap from the given input stream. Loading @@ -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); } /** Loading @@ -190,12 +195,23 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { this(new BitmapState(BitmapFactory.decodeStream(is)), null); mBitmapState.mTargetDensity = mTargetDensity; Bitmap bitmap = null; try { bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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); } } } /** * Returns the paint used to render this drawable. Loading Loading @@ -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, (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } catch (Exception e) { // Do nothing and pick up the error below. } Loading Loading @@ -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; } } /** Loading graphics/java/android/graphics/drawable/Drawable.java +38 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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, (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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 Loading Loading @@ -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); } Loading Loading
graphics/java/android/graphics/ImageDecoder.java +133 −7 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ import android.graphics.drawable.NinePatchDrawable; import android.net.Uri; import android.system.ErrnoException; import android.system.Os; import android.util.DisplayMetrics; import android.util.TypedValue; import libcore.io.IoUtils; import dalvik.system.CloseGuard; Loading Loading @@ -63,6 +65,19 @@ public final class ImageDecoder implements AutoCloseable { /* @hide */ Resources getResources() { return null; } /* @hide */ int getDensity() { return Bitmap.DENSITY_NONE; } /* @hide */ int computeDstDensity() { Resources res = getResources(); if (res == null) { return Bitmap.getDefaultDensity(); } return res.getDisplayMetrics().densityDpi; } /* @hide */ abstract ImageDecoder createImageDecoder() throws IOException; }; Loading Loading @@ -170,26 +185,73 @@ public final class ImageDecoder implements AutoCloseable { return decoder; } private static class InputStreamSource extends Source { InputStreamSource(Resources res, InputStream is, int inputDensity) { if (is == null) { throw new IllegalArgumentException("The InputStream cannot be null"); } mResources = res; mInputStream = is; mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; } final Resources mResources; InputStream mInputStream; final int mInputDensity; @Override public Resources getResources() { return mResources; } @Override public int getDensity() { return mInputDensity; } @Override public ImageDecoder createImageDecoder() throws IOException { synchronized (this) { if (mInputStream == null) { throw new IOException("Cannot reuse InputStreamSource"); } InputStream is = mInputStream; mInputStream = null; return createFromStream(is); } } } private static class ResourceSource extends Source { ResourceSource(Resources res, int resId) { mResources = res; mResId = resId; mResDensity = Bitmap.DENSITY_NONE; } final Resources mResources; final int mResId; int mResDensity; @Override public Resources getResources() { return mResources; } @Override public int getDensity() { return mResDensity; } @Override public ImageDecoder createImageDecoder() throws IOException { // This is just used in order to access the underlying Asset and // keep it alive. FIXME: Can we skip creating this object? InputStream is = null; ImageDecoder decoder = null; TypedValue value = new TypedValue(); try { is = mResources.openRawResource(mResId); is = mResources.openRawResource(mResId, value); if (value.density == TypedValue.DENSITY_DEFAULT) { mResDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (value.density != TypedValue.DENSITY_NONE) { mResDensity = value.density; } if (!(is instanceof AssetManager.AssetInputStream)) { // This should never happen. throw new RuntimeException("Resource is not an asset?"); Loading Loading @@ -420,6 +482,22 @@ public final class ImageDecoder implements AutoCloseable { return new ByteBufferSource(buffer); } /** * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) * @hide */ public static Source createSource(Resources res, InputStream is) { return new InputStreamSource(res, is, Bitmap.getDefaultDensity()); } /** * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) * @hide */ public static Source createSource(Resources res, InputStream is, int density) { return new InputStreamSource(res, is, density); } /** * Return the width and height of a given sample size. * Loading Loading @@ -476,6 +554,10 @@ public final class ImageDecoder implements AutoCloseable { this.resize(dimensions.x, dimensions.y); } private boolean requestedResize() { return mWidth != mDesiredWidth || mHeight != mDesiredHeight; } // These need to stay in sync with ImageDecoder.cpp's Allocator enum. /** * Use the default allocation for the pixel memory. Loading Loading @@ -730,6 +812,9 @@ public final class ImageDecoder implements AutoCloseable { "Drawable!"); } // this call potentially manipulates the decoder so it must be performed prior to // decoding the bitmap and after decode set the density on the resulting bitmap final int srcDensity = computeDensity(src, decoder); if (decoder.mAnimated) { // AnimatedImageDrawable calls postProcessAndRelease only if // mPostProcess exists. Loading @@ -737,7 +822,8 @@ public final class ImageDecoder implements AutoCloseable { null : decoder; Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, postProcessPtr, decoder.mDesiredWidth, decoder.mDesiredHeight, decoder.mCropRect, decoder.mDesiredHeight, srcDensity, src.computeDstDensity(), decoder.mCropRect, decoder.mInputStream, decoder.mAssetFd); // d has taken ownership of these objects. decoder.mInputStream = null; Loading @@ -746,13 +832,15 @@ public final class ImageDecoder implements AutoCloseable { } Bitmap bm = decoder.decodeBitmap(); Resources res = src.getResources(); if (res == null) { bm.setDensity(Bitmap.DENSITY_NONE); } bm.setDensity(srcDensity); Resources res = src.getResources(); byte[] np = bm.getNinePatchChunk(); if (np != null && NinePatch.isNinePatchChunk(np)) { if (res != null) { bm.setDensity(res.getDisplayMetrics().densityDpi); } Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); Rect padding = new Rect(); Loading Loading @@ -799,10 +887,48 @@ public final class ImageDecoder implements AutoCloseable { } } return decoder.decodeBitmap(); // this call potentially manipulates the decoder so it must be performed prior to // decoding the bitmap final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); bm.setDensity(srcDensity); return bm; } } // This method may modify the decoder so it must be called prior to performing the decode private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) { // if the caller changed the size then we treat the density as unknown if (decoder.requestedResize()) { return Bitmap.DENSITY_NONE; } // Special stuff for compatibility mode: if the target density is not // the same as the display density, but the resource -is- the same as // the display density, then don't scale it down to the target density. // This allows us to load the system's density-correct resources into // an application in compatibility mode, without scaling those down // to the compatibility density only to have them scaled back up when // drawn to the screen. Resources res = src.getResources(); final int srcDensity = src.getDensity(); if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) { return srcDensity; } // downscale the bitmap if the asset has a higher density than the default final int dstDensity = src.computeDstDensity(); if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) { float scale = (float) dstDensity / srcDensity; int scaledWidth = (int) (decoder.mWidth * scale + 0.5f); int scaledHeight = (int) (decoder.mHeight * scale + 0.5f); decoder.resize(scaledWidth, scaledHeight); return dstDensity; } return srcDensity; } private String getMimeType() { return nGetMimeType(mNativePtr); } Loading
graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +16 −5 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.ImageDecoder; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.SystemClock; import android.util.DisplayMetrics; import libcore.io.IoUtils; import libcore.util.NativeAllocationRegistry; Loading Loading @@ -59,22 +61,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { * decoder is only non-null if it has a PostProcess */ public AnimatedImageDrawable(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect, @Nullable ImageDecoder decoder, int width, int height, int srcDensity, int dstDensity, Rect cropRect, InputStream inputStream, AssetFileDescriptor afd) throws IOException { mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); mInputStream = inputStream; mAssetFd = afd; width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity); height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity); if (cropRect == null) { mIntrinsicWidth = width; mIntrinsicHeight = height; } else { cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity), Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity)); mIntrinsicWidth = cropRect.width(); mIntrinsicHeight = cropRect.height(); } long nativeSize = nNativeByteSize(mNativePtr); mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); mInputStream = inputStream; mAssetFd = afd; // FIXME: Use the right size for the native allocation. long nativeSize = 200; NativeAllocationRegistry registry = new NativeAllocationRegistry( AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); registry.registerNativeAllocation(this, mNativePtr); Loading
graphics/java/android/graphics/drawable/BitmapDrawable.java +59 −26 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { mBitmapState = new BitmapState((Bitmap) null); init(new BitmapState((Bitmap) null), null); } /** Loading @@ -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); } /** Loading @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { this(new BitmapState(bitmap), null); init(new BitmapState(bitmap), null); } /** Loading @@ -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); } /** Loading @@ -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); } /** Loading @@ -165,12 +162,23 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); mBitmapState.mTargetDensity = mTargetDensity; Bitmap bitmap = null; try (FileInputStream stream = new FileInputStream(filepath)) { bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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); } } } /** * Create a drawable by decoding a bitmap from the given input stream. Loading @@ -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); } /** Loading @@ -190,12 +195,23 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { this(new BitmapState(BitmapFactory.decodeStream(is)), null); mBitmapState.mTargetDensity = mTargetDensity; Bitmap bitmap = null; try { bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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); } } } /** * Returns the paint used to render this drawable. Loading Loading @@ -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, (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } catch (Exception e) { // Do nothing and pick up the error below. } Loading Loading @@ -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; } } /** Loading
graphics/java/android/graphics/drawable/Drawable.java +38 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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, (info, decoder) -> { decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); }); } 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 Loading Loading @@ -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); } Loading