Add Bitmap#eraseColor(@ColorLong) + helpers
Bug: 120904891
Test: I162451ebf807f3a8a44679e5c10406468c922500
- Add Bitmap#eraseColor(@ColorLong). This allows erasing in ColorSpaces
besides SRGB. New API is hidden pending API-council approval. It is
@TestApi so it can be used by the new tests.
- Rewrite Bitmap#erase(@ColorInt)'s internals. The ColorInt should be
treated as an SRGB color. The old code (deep in SkPixmap::erase)
treated the color as being in the SkColorSpace of the SkBitmap.
- Update getNativeColorSpace to return immediately when it throws.
Existing callers should never throw anyway, since they do their own
checks (and throws) in Java before reaching this method. But relying
on this method to properly return simplifies the new callers.
Change-Id: I1b736934ce1b8294c827bb61c2a363207569da4f
diff --git a/api/test-current.txt b/api/test-current.txt
index 957f957..928aa08 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -494,6 +494,10 @@
package android.graphics {
+ public final class Bitmap implements android.os.Parcelable {
+ method public void eraseColor(long);
+ }
+
public final class ImageDecoder implements java.lang.AutoCloseable {
method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index eb7338a..f188a0f 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -568,11 +568,34 @@
return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE;
}
+static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color,
+ const sk_sp<SkColorSpace>& colorSpace) {
+ SkPaint p;
+ p.setColor4f(color, colorSpace.get());
+ p.setBlendMode(SkBlendMode::kSrc);
+ SkCanvas canvas(bitmap);
+ canvas.drawPaint(p);
+}
+
static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
LocalScopedBitmap bitmap(bitmapHandle);
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
- skBitmap.eraseColor(color);
+ bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
+}
+
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace,
+ jfloat r, jfloat g, jfloat b, jfloat a) {
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+ if (GraphicsJNI::hasException(env)) {
+ return;
+ }
+
+ LocalScopedBitmap bitmap(bitmapHandle);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ SkColor4f color = SkColor4f{r, g, b, a};
+ bitmapErase(skBitmap, color, cs);
}
static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -1183,6 +1206,7 @@
{ "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
(void*)Bitmap_compress },
{ "nativeErase", "(JI)V", (void*)Bitmap_erase },
+ { "nativeErase", "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong },
{ "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
{ "nativeConfig", "(J)I", (void*)Bitmap_config },
{ "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 67d0c8a..548a249 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -456,12 +456,14 @@
if (colorSpace == nullptr) return nullptr;
if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
doThrowIAE(env, "The color space must be an RGB color space");
+ return nullptr;
}
jobject transferParams = env->CallObjectMethod(colorSpace,
gColorSpaceRGB_getTransferParametersMethodID);
if (transferParams == nullptr) {
doThrowIAE(env, "The color space must use an ICC parametric transfer function");
+ return nullptr;
}
jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 790b37e..30f0bfa 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -18,9 +18,11 @@
import android.annotation.CheckResult;
import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
@@ -1780,6 +1782,30 @@
}
/**
+ * Fills the bitmap's pixels with the specified {@link Color}.
+ *
+ * @throws IllegalStateException if the bitmap is not mutable.
+ * @throws IllegalArgumentException if the color space encoded in the long
+ * is invalid or unknown.
+ *
+ * @hide pending API approval
+ */
+ @TestApi
+ public void eraseColor(@ColorLong long c) {
+ checkRecycled("Can't erase a recycled bitmap");
+ if (!isMutable()) {
+ throw new IllegalStateException("cannot erase immutable bitmaps");
+ }
+
+ ColorSpace cs = Color.colorSpace(c);
+ float r = Color.red(c);
+ float g = Color.green(c);
+ float b = Color.blue(c);
+ float a = Color.alpha(c);
+ nativeErase(mNativePtr, cs, r, g, b, a);
+ }
+
+ /**
* Returns the {@link Color} at the specified location. Throws an exception
* if x or y are out of bounds (negative or >= to the width or height
* respectively). The returned color is a non-premultiplied ARGB value in
@@ -2123,6 +2149,8 @@
int quality, OutputStream stream,
byte[] tempStorage);
private static native void nativeErase(long nativeBitmap, int color);
+ private static native void nativeErase(long nativeBitmap, ColorSpace cs,
+ float r, float g, float b, float a);
private static native int nativeRowBytes(long nativeBitmap);
private static native int nativeConfig(long nativeBitmap);