Add new RGBA_F16 bitmap config

This configuration uses 64 bits per pixel. Heach component is stored as a
half precision float value (16 bits). Half floats can be decoded/encoded
using android.util.Half.

RGBA_F16 bitmaps are used to decode wide-gamut images stored in 16 bit
formats (PNG 16 bit for instance). aapt is currently not aware of PNG
16 bits so such files must be placed in raw/ resource directories.

This first pass provides only partial drawing support with hardware
acceleration. RGBA_F16 bitmaps are stored in linear space and need
to be encoded to gamma space with the appropriate OETF to be rendered
properly on Android's current surfaces. They are however suitable for
linear blending. Full rendering support will be provided in a future
CL (BitmapShaders might be a bit tricky to handle properly during
shader generation).

Bug: 32984164
Test: bit CtsGraphicsTestCases:android.graphics.cts.BitmapRGBAF16Test

Change-Id: I328e6b567441a1b9d152a3e7be944a2cf63193bd
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 673cf86..b656bb0 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -5,7 +5,11 @@
 #include "SkPixelRef.h"
 #include "SkImageEncoder.h"
 #include "SkImageInfo.h"
+#include "SkColor.h"
 #include "SkColorPriv.h"
+#include "SkHalf.h"
+#include "SkPM4f.h"
+#include "SkPM4fPriv.h"
 #include "GraphicsJNI.h"
 #include "SkDither.h"
 #include "SkUnPreMultiply.h"
@@ -232,6 +236,28 @@
 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
                               int x, int y);
 
+static void FromColor_F16(void* dst, const SkColor src[], int width,
+                          int, int) {
+    uint64_t* d = (uint64_t*)dst;
+
+    for (int i = 0; i < width; i++) {
+        *d++ = SkColor4f::FromColor(*src++).premul().toF16();
+    }
+}
+
+static void FromColor_F16_Raw(void* dst, const SkColor src[], int width,
+                          int, int) {
+    uint64_t* d = (uint64_t*)dst;
+
+    for (int i = 0; i < width; i++) {
+        const float* color = SkColor4f::FromColor(*src++).vec();
+        uint16_t* scratch = reinterpret_cast<uint16_t*>(d++);
+        for (int i = 0; i < 4; ++i) {
+            scratch[i] = SkFloatToHalf(color[i]);
+        }
+    }
+}
+
 static void FromColor_D32(void* dst, const SkColor src[], int width,
                           int, int) {
     SkPMColor* d = (SkPMColor*)dst;
@@ -321,6 +347,8 @@
             return FromColor_D565;
         case kAlpha_8_SkColorType:
             return FromColor_DA8;
+        case kRGBA_F16_SkColorType:
+            return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_F16 : FromColor_F16_Raw;
         default:
             break;
     }
@@ -351,8 +379,7 @@
 
     dstBitmap.notifyPixelsChanged();
 
-    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
-                                 JNI_ABORT);
+    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT);
     return true;
 }
 
@@ -361,6 +388,24 @@
 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
                             SkColorTable*);
 
+static void ToColor_F16_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    uint64_t* s = (uint64_t*)src;
+    do {
+        *dst++ = SkPM4f::FromF16((const uint16_t*) s++).unpremul().toSkColor();
+    } while (--width != 0);
+}
+
+static void ToColor_F16_Raw(SkColor dst[], const void* src, int width,
+                            SkColorTable*) {
+    SkASSERT(width > 0);
+    uint64_t* s = (uint64_t*)src;
+    do {
+        *dst++ = Sk4f_toS32(swizzle_rb(SkHalfToFloat_finite_ftz(*s++)));
+    } while (--width != 0);
+}
+
 static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
                               SkColorTable*) {
     SkASSERT(width > 0);
@@ -520,6 +565,17 @@
             }
         case kAlpha_8_SkColorType:
             return ToColor_SA8;
+        case kRGBA_F16_SkColorType:
+            switch (src.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return ToColor_F16_Raw;
+                case kPremul_SkAlphaType:
+                    return ToColor_F16_Alpha;
+                case kUnpremul_SkAlphaType:
+                    return ToColor_F16_Raw;
+                default:
+                    return NULL;
+            }
         default:
             break;
     }
@@ -554,7 +610,7 @@
 
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
-            GraphicsJNI::defaultColorSpace()));
+            GraphicsJNI::colorSpaceForType(colorType)));
 
     sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);
     if (!nativeBitmap) {
@@ -562,8 +618,7 @@
     }
 
     if (jColors != NULL) {
-        GraphicsJNI::SetPixels(env, jColors, offset, stride,
-                0, 0, width, height, bitmap);
+        GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap);
     }
 
     return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));
@@ -790,6 +845,7 @@
     const int         density = p->readInt32();
 
     if (kN32_SkColorType != colorType &&
+            kRGBA_F16_SkColorType != colorType &&
             kRGB_565_SkColorType != colorType &&
             kARGB_4444_SkColorType != colorType &&
             kIndex_8_SkColorType != colorType &&
@@ -800,8 +856,15 @@
 
     std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
 
-    if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType,
-            isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) {
+    sk_sp<SkColorSpace> colorSpace;
+    if (kRGBA_F16_SkColorType == colorType) {
+        colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named);
+    } else {
+        colorSpace = isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr;
+    }
+
+    if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace),
+            rowBytes)) {
         return NULL;
     }