Prohibit Config.HARDWARE in factory methods, that create mutable bitmaps
Test: android.cts.graphics.BitmapTest#testCreateMutableBitmapWithHardwareConfig
bug:30999911
Change-Id: I7cd4e2625563b6659613ccd180a57c56dcf7c2b1
diff --git a/api/current.txt b/api/current.txt
index 0209b5e..3af2c8f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11779,6 +11779,7 @@
enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+ enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
enum_constant public static final android.graphics.Bitmap.Config RGB_565;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 1d14343..8034c3b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12265,6 +12265,7 @@
enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+ enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
enum_constant public static final android.graphics.Bitmap.Config RGB_565;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ef7fa5..f10fdb0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -11810,6 +11810,7 @@
enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+ enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
enum_constant public static final android.graphics.Bitmap.Config RGB_565;
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index e86acc4..762a3f3 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -276,6 +276,11 @@
}
}
+ if (isMutable && isHardware) {
+ doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable");
+ return nullObjectReturn("Cannot create mutable hardware bitmap");
+ }
+
// Create the codec.
NinePatchPeeker peeker;
std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 756087c..cd75fe9 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -467,7 +467,11 @@
/**
- * @hide
+ * Special configuration, when bitmap is stored only in graphic memory.
+ * Bitmaps in this configuration are always immutable.
+ *
+ * It is optimal for cases, when the only operation with the bitmap is to draw it on a
+ * screen.
*/
HARDWARE (6);
@@ -810,7 +814,8 @@
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
public static Bitmap createBitmap(int width, int height, Config config) {
return createBitmap(width, height, config, true);
@@ -825,7 +830,8 @@
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
public static Bitmap createBitmap(DisplayMetrics display, int width,
int height, Config config) {
@@ -843,7 +849,8 @@
* bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
return createBitmap(null, width, height, config, hasAlpha);
@@ -862,13 +869,17 @@
* bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
*
- * @throws IllegalArgumentException if the width or height are <= 0
+ * @throws IllegalArgumentException if the width or height are <= 0, or if
+ * Config is Config.HARDWARE, because hardware bitmaps are always immutable
*/
private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
Config config, boolean hasAlpha) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
+ if (config == Config.HARDWARE) {
+ throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
+ }
Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
if (display != null) {
bm.mDensity = display.densityDpi;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 447a4c4..a5517f0 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -16,6 +16,8 @@
package android.graphics;
+import static android.graphics.BitmapFactory.Options.validate;
+
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -103,6 +105,9 @@
* If set, decode methods will always return a mutable Bitmap instead of
* an immutable one. This can be used for instance to programmatically apply
* effects to a Bitmap loaded through BitmapFactory.
+ * <p>Can not be set simultaneously with inPreferredConfig =
+ * {@link android.graphics.Bitmap.Config#HARDWARE},
+ * because hardware bitmaps are always immutable.
*/
@SuppressWarnings({"UnusedDeclaration"}) // used in native code
public boolean inMutable;
@@ -381,6 +386,12 @@
public void requestCancelDecode() {
mCancel = true;
}
+
+ static void validate(Options opts) {
+ if (opts != null && opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+ throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+ }
+ }
}
/**
@@ -393,8 +404,12 @@
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeFile(String pathName, Options opts) {
+ validate(opts);
Bitmap bm = null;
InputStream stream = null;
try {
@@ -431,10 +446,13 @@
/**
* Decode a new Bitmap from an InputStream. This InputStream was obtained from
* resources, which we pass to be able to scale the bitmap accordingly.
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
-
+ validate(opts);
if (opts == null) {
opts = new Options();
}
@@ -466,8 +484,12 @@
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeResource(Resources res, int id, Options opts) {
+ validate(opts);
Bitmap bm = null;
InputStream is = null;
@@ -520,11 +542,15 @@
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
if ((offset | length) < 0 || data.length < offset + length) {
throw new ArrayIndexOutOfBoundsException();
}
+ validate(opts);
Bitmap bm;
@@ -598,6 +624,9 @@
* @return The decoded bitmap, or null if the image data could not be
* decoded, or, if opts is non-null, if opts requested only the
* size be returned (in opts.outWidth and opts.outHeight)
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*
* <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
* if {@link InputStream#markSupported is.markSupported()} returns true,
@@ -610,6 +639,7 @@
if (is == null) {
return null;
}
+ validate(opts);
Bitmap bm = null;
@@ -673,8 +703,12 @@
* @param opts null-ok; Options that control downsampling and whether the
* image should be completely decoded, or just its size returned.
* @return the decoded bitmap, or null
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+ validate(opts);
Bitmap bm;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index e689b08..04abca1 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -178,8 +178,12 @@
* inPurgeable is not supported.
* @return The decoded bitmap, or null if the image data could not be
* decoded.
+ * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+ * is {@link android.graphics.Bitmap.Config#HARDWARE}
+ * and {@link BitmapFactory.Options#inMutable} is set.
*/
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ BitmapFactory.Options.validate(options);
synchronized (mNativeLock) {
checkRecycled("decodeRegion called on recycled region decoder");
if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()