Reland "Implement AndroidBitmap_getDataSpace"

Originally reviewed in Ie05a45da32b2fd670abdae35626cd6548cfb102c
(and reverted in I0b06312f6583f766512cc771a35d3d735debcce1)

Bug: 135133301
Test: I7a5fcb726fba0c832bbb86a424d7534a7cfa35b6

This supplements AndroidBitmap_getInfo, allowing NDK clients to know how
to interpret the colors in an android.graphics.Bitmap.

Only build android_bitmap.cpp on Android so that it can rely on
libnativewindow (which is Android-only) for data_space.h

Change-Id: I4b23c68c7e62ed733e95af6f76c47fecbc2c5747
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6e6746f..a2f514a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -344,7 +344,6 @@
     cppflags: ["-Wno-conversion-null"],
 
     srcs: [
-        "android/graphics/apex/android_bitmap.cpp",
         "android/graphics/apex/android_matrix.cpp",
         "android/graphics/apex/android_paint.cpp",
         "android/graphics/apex/android_region.cpp",
@@ -430,6 +429,7 @@
         android: {
             srcs: [ // sources that depend on android only libraries
                 "android/graphics/apex/android_canvas.cpp",
+                "android/graphics/apex/android_bitmap.cpp",
                 "android/graphics/apex/renderthread.cpp",
                 "android/graphics/apex/jni_runtime.cpp",
 
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
index 90cc986..6a3c01e 100644
--- a/core/jni/android/graphics/apex/android_bitmap.cpp
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -122,6 +122,98 @@
     return getInfo(bitmap->info(), bitmap->rowBytes());
 }
 
+static bool nearlyEqual(float a, float b) {
+    // By trial and error, this is close enough to match for the ADataSpaces we
+    // compare for.
+    return ::fabs(a - b) < .002f;
+}
+
+static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
+    return nearlyEqual(x.g, y.g)
+        && nearlyEqual(x.a, y.a)
+        && nearlyEqual(x.b, y.b)
+        && nearlyEqual(x.c, y.c)
+        && nearlyEqual(x.d, y.d)
+        && nearlyEqual(x.e, y.e)
+        && nearlyEqual(x.f, y.f);
+}
+
+static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) {
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false;
+        }
+    }
+    return true;
+}
+
+static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+
+// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut
+// matches the white point used by ColorSpace.Named.DCIP3.
+static constexpr skcms_Matrix3x3 kDCIP3 = {{
+        {0.486143, 0.323835, 0.154234},
+        {0.226676, 0.710327, 0.0629966},
+        {0.000800549, 0.0432385, 0.78275},
+}};
+
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    const SkImageInfo& info = bitmap->info();
+    SkColorSpace* colorSpace = info.colorSpace();
+    if (!colorSpace) {
+        return ADATASPACE_UNKNOWN;
+    }
+
+    if (colorSpace->isSRGB()) {
+        if (info.colorType() == kRGBA_F16_SkColorType) {
+            return ADATASPACE_SCRGB;
+        }
+        return ADATASPACE_SRGB;
+    }
+
+    skcms_TransferFunction fn;
+    LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn));
+
+    skcms_Matrix3x3 gamut;
+    LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut));
+
+    if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) {
+        if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) {
+            // Skia doesn't differentiate amongst the RANGES. In Java, we associate
+            // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs.
+            // Make the same association here.
+            if (info.colorType() == kRGBA_F16_SkColorType) {
+                return ADATASPACE_SCRGB_LINEAR;
+            }
+            return ADATASPACE_SRGB_LINEAR;
+        }
+
+        if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) {
+            return ADATASPACE_BT709;
+        }
+    }
+
+    if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) {
+        return ADATASPACE_DISPLAY_P3;
+    }
+
+    if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) {
+        return ADATASPACE_ADOBE_RGB;
+    }
+
+    if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) &&
+        nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
+        return ADATASPACE_BT2020;
+    }
+
+    if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) {
+        return ADATASPACE_DCI_P3;
+    }
+
+    return ADATASPACE_UNKNOWN;
+}
+
 AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
     uint32_t rowBytes = 0;
     SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes);
diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
index f231eed..32b8a45 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -17,6 +17,7 @@
 #define ANDROID_GRAPHICS_BITMAP_H
 
 #include <android/bitmap.h>
+#include <android/data_space.h>
 #include <jni.h>
 #include <sys/cdefs.h>
 
@@ -49,6 +50,7 @@
 void ABitmap_releaseRef(ABitmap* bitmap);
 
 AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap);
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmap);
 
 void* ABitmap_getPixels(ABitmap* bitmap);
 void ABitmap_notifyPixelsChanged(ABitmap* bitmap);
@@ -106,6 +108,7 @@
         ABitmap* get() const { return mBitmap; }
 
         AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); }
+        ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); }
         void* getPixels() const { return ABitmap_getPixels(mBitmap); }
         void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); }
 
@@ -119,4 +122,4 @@
 }; // namespace android
 #endif // __cplusplus
 
-#endif // ANDROID_GRAPHICS_BITMAP_H
\ No newline at end of file
+#endif // ANDROID_GRAPHICS_BITMAP_H
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 1aebeaf..26c7f8d 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -16,6 +16,7 @@
 
 #include <android/bitmap.h>
 #include <android/graphics/bitmap.h>
+#include <android/data_space.h>
 
 int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
                           AndroidBitmapInfo* info) {
@@ -29,6 +30,15 @@
     return ANDROID_BITMAP_RESULT_SUCCESS;
 }
 
+int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) {
+    if (NULL == env || NULL == jbitmap) {
+        return ADATASPACE_UNKNOWN; // Or return a real error?
+    }
+
+    android::graphics::Bitmap bitmap(env, jbitmap);
+    return bitmap.getDataSpace();
+}
+
 int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
     if (NULL == env || NULL == jbitmap) {
         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index a601d8a..6adb955 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -1,6 +1,7 @@
 LIBJNIGRAPHICS {
   global:
     AndroidBitmap_getInfo;
+    AndroidBitmap_getDataSpace;
     AndroidBitmap_lockPixels;
     AndroidBitmap_unlockPixels;
   local: