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/api/current.txt b/api/current.txt
index cec2b9f..89573ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11769,6 +11769,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -11833,6 +11835,7 @@
     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 RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12192,6 +12195,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -12727,7 +12731,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -61421,31 +61427,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/system-current.txt b/api/system-current.txt
index b0236ea..7d8da95 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12262,6 +12262,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -12326,6 +12328,7 @@
     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 RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12685,6 +12688,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -13220,7 +13224,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -64923,31 +64929,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/test-current.txt b/api/test-current.txt
index c798b5e..251a353 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -11800,6 +11800,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -11864,6 +11866,7 @@
     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 RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12223,6 +12226,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -12758,7 +12762,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -61710,31 +61716,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
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;
     }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 18ed0ed..69c7054 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -283,8 +283,8 @@
 
     // Create the codec.
     NinePatchPeeker peeker;
-    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
-            &peeker));
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(
+            streamDeleter.release(), &peeker));
     if (!codec.get()) {
         return nullObjectReturn("SkAndroidCodec::NewFromStream returned null");
     }
@@ -399,7 +399,8 @@
 
     // We always decode to sRGB, but only mark the bitmap with a color space if linear
     // blending is enabled.
-    SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(GraphicsJNI::defaultColorSpace());
+    SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(
+            GraphicsJNI::colorSpaceForType(decodeColorType));
     if (decodeColorType == kGray_8_SkColorType) {
         // The legacy implementation of BitmapFactory used kAlpha8 for
         // grayscale images (before kGray8 existed).  While the codec
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index d8984d3..6f97c60 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -297,13 +297,16 @@
     kRGB_565_LegacyBitmapConfig     = 3,
     kARGB_4444_LegacyBitmapConfig   = 4,
     kARGB_8888_LegacyBitmapConfig   = 5,
-    kHardware_LegacyBitmapConfig    = 6,
+    kRGBA_16F_LegacyBitmapConfig    = 6,
+    kHardware_LegacyBitmapConfig    = 7,
 
     kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig
 };
 
 jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
     switch (colorType) {
+        case kRGBA_F16_SkColorType:
+            return kRGBA_16F_LegacyBitmapConfig;
         case kN32_SkColorType:
             return kARGB_8888_LegacyBitmapConfig;
         case kARGB_4444_SkColorType:
@@ -329,6 +332,7 @@
         kRGB_565_SkColorType,
         kARGB_4444_SkColorType,
         kN32_SkColorType,
+        kRGBA_F16_SkColorType,
         kN32_SkColorType
     };
 
@@ -458,6 +462,19 @@
 #endif
 }
 
+sk_sp<SkColorSpace> GraphicsJNI::linearColorSpace() {
+    return SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named);
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) {
+    switch (type) {
+        case kRGBA_F16_SkColorType:
+            return linearColorSpace();
+        default:
+            return defaultColorSpace();
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 03dc1fb..508c9ff 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -95,6 +95,8 @@
             const SkBitmap& dstBitmap);
 
     static sk_sp<SkColorSpace> defaultColorSpace();
+    static sk_sp<SkColorSpace> linearColorSpace();
+    static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
 };
 
 class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index b6b5245..bb69e27 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,6 +142,10 @@
             return kN32_SkColorType;
         case PIXEL_FORMAT_RGBX_8888:
             return kN32_SkColorType;
+        case PIXEL_FORMAT_RGBA_FP16:
+            return kRGBA_F16_SkColorType;
+        case PIXEL_FORMAT_RGBX_FP16:
+            return kRGBA_F16_SkColorType;
         case PIXEL_FORMAT_RGB_565:
             return kRGB_565_SkColorType;
         default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 92693b7..a5b7671 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -168,6 +168,8 @@
     switch(format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBX_FP16:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
@@ -283,6 +285,8 @@
     switch (format) {
     case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
     case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGBX_FP16:    return kRGBA_F16_SkColorType;
+    case PIXEL_FORMAT_RGBA_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
     default:                        return kUnknown_SkColorType;
     }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5b88181..62c3d04 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,6 +164,16 @@
             alphaType = kPremul_SkAlphaType;
             break;
         }
+        case PIXEL_FORMAT_RGBX_FP16: {
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kOpaque_SkAlphaType;
+            break;
+        }
+        case PIXEL_FORMAT_RGBA_FP16: {
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kPremul_SkAlphaType;
+            break;
+        }
         case PIXEL_FORMAT_RGB_565: {
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 3c8db7f..d3c0202 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -83,6 +83,14 @@
             colorType = kN32_SkColorType;
             alphaType = kOpaque_SkAlphaType;
             break;
+        case WINDOW_FORMAT_RGBA_FP16:
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kPremul_SkAlphaType;
+            break;
+        case WINDOW_FORMAT_RGBX_FP16:
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kOpaque_SkAlphaType;
+            break;
         case WINDOW_FORMAT_RGB_565:
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
diff --git a/docs/html/reference/images/graphics/colorspace_ucs.png b/docs/html/reference/images/graphics/colorspace_ucs.png
new file mode 100644
index 0000000..3e0f0c6
--- /dev/null
+++ b/docs/html/reference/images/graphics/colorspace_ucs.png
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd75fe9..a041a28 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -140,7 +140,7 @@
      * Native bitmap has been reconfigured, so set premult and cached
      * width/height values
      */
-    // called from JNI
+    @SuppressWarnings("unused") // called from JNI
     void reinit(int width, int height, boolean requestPremultiplied) {
         mWidth = width;
         mHeight = height;
@@ -465,6 +465,15 @@
          */
         ARGB_8888   (5),
 
+        /**
+         * Each pixels is stored on 8 bytes. Each channel (RGB and alpha
+         * for translucency) is stored as a
+         * {@link android.util.Half half-precision floating point value}.
+         *
+         * This configuration is particularly suited for wide-gamut and
+         * HDR content.
+         */
+        RGBA_F16    (6),
 
         /**
          * Special configuration, when bitmap is stored only in graphic memory.
@@ -473,12 +482,12 @@
          * It is optimal for cases, when the only operation with the bitmap is to draw it on a
          * screen.
          */
-        HARDWARE    (6);
+        HARDWARE    (7);
 
         final int nativeInt;
 
         private static Config sConfigs[] = {
-            null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, HARDWARE
+            null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
         };
 
         Config(int ni) {
@@ -760,6 +769,9 @@
                 case ALPHA_8:
                     newConfig = Config.ALPHA_8;
                     break;
+                case RGBA_F16:
+                    newConfig = Config.RGBA_F16;
+                    break;
                 //noinspection deprecation
                 case ARGB_4444:
                 case ARGB_8888:
@@ -781,8 +793,13 @@
             neww = Math.round(deviceR.width());
             newh = Math.round(deviceR.height());
 
-            bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig,
-                    transformed || source.hasAlpha());
+            Config transformedConfig = newConfig;
+            if (transformed) {
+                if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) {
+                    transformedConfig = Config.ARGB_8888;
+                }
+            }
+            bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
 
             canvas.translate(-deviceR.left, -deviceR.top);
             canvas.concat(m);
@@ -845,14 +862,14 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
-     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
+     *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
      * @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) {
+    public static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
         return createBitmap(null, width, height, config, hasAlpha);
     }
 
@@ -865,14 +882,14 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
-     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
+     *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
      * @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,
+    public 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");
@@ -885,7 +902,7 @@
             bm.mDensity = display.densityDpi;
         }
         bm.setHasAlpha(hasAlpha);
-        if (config == Config.ARGB_8888 && !hasAlpha) {
+        if ((config == Config.ARGB_8888 || config == Config.RGBA_F16) && !hasAlpha) {
             nativeErase(bm.mNativePtr, 0xff000000);
         }
         // No need to initialize the bitmap to zeroes with other configs;
@@ -1526,7 +1543,7 @@
 
     /**
      * <p>Replace pixels in the bitmap with the colors in the array. Each element
-     * in the array is a packed int prepresenting a non-premultiplied ARGB
+     * in the array is a packed int representing a non-premultiplied ARGB
      * {@link Color}.</p>
      *
      * @param pixels   The colors to write to the bitmap
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 7dc5de3..d968516 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1671,6 +1671,28 @@
     }
 
     /**
+     * Converts values from CIE xyY to CIE L*u*v*. Y is assumed to be 1 so the
+     * input xyY array only contains the x and y components. After this method
+     * returns, the xyY array contains the converted u and v components.
+     *
+     * @param xyY The xyY value to convert to XYZ, cannot be null,
+     *            length must be a multiple of 2
+     */
+    private static void xyYToUv(@NonNull @Size(multiple = 2) float[] xyY) {
+        for (int i = 0; i < xyY.length; i += 2) {
+            float x = xyY[i];
+            float y = xyY[i + 1];
+
+            float d = -2.0f * x + 12.0f * y + 3;
+            float u = (4.0f * x) / d;
+            float v = (9.0f * y) / d;
+
+            xyY[i] = u;
+            xyY[i + 1] = v;
+        }
+    }
+
+    /**
      * <p>Computes the chromatic adaptation transform from the specified
      * source white point to the specified destination white point.</p>
      *
@@ -3162,10 +3184,10 @@
     /**
      * <p>A color space renderer can be used to visualize and compare the gamut and
      * white point of one or more color spaces. The output is an sRGB {@link Bitmap}
-     * showing a CIE 1931 xyY chromaticity diagram.</p>
+     * showing a CIE 1931 xyY or a CIE 1976 UCS chromaticity diagram.</p>
      *
      * <p>The following code snippet shows how to compare the {@link Named#SRGB}
-     * and {@link Named#DCI_P3} color spaces:</p>
+     * and {@link Named#DCI_P3} color spaces in a CIE 1931 diagram:</p>
      *
      * <pre class="prettyprint">
      * Bitmap bitmap = ColorSpace.createRenderer()
@@ -3188,12 +3210,18 @@
      */
     public static class Renderer {
         private static final int NATIVE_SIZE = 1440;
+        private static final float UCS_SCALE = 9.0f / 6.0f;
+
+        // Number of subdivision of the inside of the spectral locus
+        private static final int CHROMATICITY_RESOLUTION = 32;
+        private static final double ONE_THIRD = 1.0 / 3.0;
 
         @IntRange(from = 128, to = Integer.MAX_VALUE)
         private int mSize = 1024;
 
         private boolean mShowWhitePoint = true;
         private boolean mClip = false;
+        private boolean mUcs = false;
 
         private final List<Pair<ColorSpace, Integer>> mColorSpaces = new ArrayList<>(2);
         private final List<Point> mPoints = new ArrayList<>(0);
@@ -3241,6 +3269,35 @@
         }
 
         /**
+         * <p>Defines whether the chromaticity diagram should use the uniform
+         * chromaticity scale (CIE 1976 UCS). When the uniform chromaticity scale
+         * is used, the distance between two points on the diagram is approximately
+         * proportional to the perceived color difference.</p>
+         *
+         * <p>The following code snippet shows how to enable the uniform chromaticity
+         * scale. The image below shows the result:</p>
+         * <pre class="prettyprint">
+         * Bitmap bitmap = ColorSpace.createRenderer()
+         *     .uniformChromaticityScale(true)
+         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+         *     .render();
+         * </pre>
+         * <p>
+         *     <img src="{@docRoot}reference/android/images/graphics/colorspace_ucs.png" />
+         *     <figcaption style="text-align: center;">CIE 1976 UCS diagram</figcaption>
+         * </p>
+         *
+         * @param ucs True to render the chromaticity diagram as the CIE 1976 UCS diagram
+         * @return This instance of {@link Renderer}
+         */
+        @NonNull
+        public Renderer uniformChromaticityScale(boolean ucs) {
+            mUcs = ucs;
+            return this;
+        }
+
+        /**
          * Sets the dimensions (width and height) in pixels of the output bitmap.
          * The size must be at least 128px and defaults to 1024px.
          *
@@ -3302,7 +3359,7 @@
          * </pre>
          * <p>
          *     <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison2.png" />
-         *     <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
+         *     <figcaption style="text-align: center;">sRGB, DCI-P3, ACES and scRGB</figcaption>
          * </p>
          *
          * @param colorSpace The color space whose gamut to render on the diagram
@@ -3385,6 +3442,7 @@
 
             setTransform(canvas, width, height, primaries);
             drawBox(canvas, width, height, paint, path);
+            setUcsTransform(canvas, height);
             drawLocus(canvas, width, height, paint, path, primaries);
             drawGamuts(canvas, width, height, paint, path, primaries, whitePoint);
             drawPoints(canvas, width, height, paint);
@@ -3406,7 +3464,11 @@
 
             paint.setStyle(Paint.Style.FILL);
 
+            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
+
             float[] v = new float[3];
+            float[] xy = new float[2];
+
             for (final Point point : mPoints) {
                 v[0] = point.mRgb[0];
                 v[1] = point.mRgb[1];
@@ -3415,10 +3477,13 @@
 
                 paint.setColor(point.mColor);
 
-                // XYZ to xyY, assuming Y=1.0
+                // XYZ to xyY, assuming Y=1.0, then to L*u*v* if needed
                 float sum = v[0] + v[1] + v[2];
-                canvas.drawCircle(width * v[0] / sum, height - height * v[1] / sum,
-                        4.0f, paint);
+                xy[0] = v[0] / sum;
+                xy[1] = v[1] / sum;
+                if (mUcs) xyYToUv(xy);
+
+                canvas.drawCircle(width * xy[0], height - height * xy[1], radius, paint);
             }
         }
 
@@ -3440,6 +3505,8 @@
                 @NonNull Paint paint, @NonNull Path path,
                 @NonNull @Size(6) float[] primaries, @NonNull @Size(2) float[] whitePoint) {
 
+            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
+
             for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
                 ColorSpace colorSpace = item.first;
                 int color = item.second;
@@ -3447,7 +3514,7 @@
                 if (colorSpace.getModel() != Model.RGB) continue;
 
                 Rgb rgb = (Rgb) colorSpace;
-                getPrimaries(rgb, primaries);
+                getPrimaries(rgb, primaries, mUcs);
 
                 path.rewind();
                 path.moveTo(width * primaries[0], height - height * primaries[1]);
@@ -3462,11 +3529,12 @@
                 // Draw the white point
                 if (mShowWhitePoint) {
                     rgb.getWhitePoint(whitePoint);
+                    if (mUcs) xyYToUv(whitePoint);
 
                     paint.setStyle(Paint.Style.FILL);
                     paint.setColor(color);
-                    canvas.drawCircle(width * whitePoint[0], height - height * whitePoint[1],
-                            4.0f, paint);
+                    canvas.drawCircle(
+                            width * whitePoint[0], height - height * whitePoint[1], radius, paint);
                 }
             }
         }
@@ -3477,10 +3545,12 @@
          *
          * @param rgb The color space whose primaries to extract
          * @param primaries A pre-allocated array of 6 floats that will hold the result
+         * @param asUcs True if the primaries should be returned in Luv, false for xyY
          */
         @NonNull
         @Size(6)
-        private static float[] getPrimaries(@NonNull Rgb rgb, @NonNull @Size(6) float[] primaries) {
+        private static float[] getPrimaries(@NonNull Rgb rgb,
+                @NonNull @Size(6) float[] primaries, boolean asUcs) {
             // TODO: We should find a better way to handle these cases
             if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) ||
                     rgb.equals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB))) {
@@ -3490,9 +3560,11 @@
                 primaries[3] = 1.24f;
                 primaries[4] = -0.23f;
                 primaries[5] = -0.57f;
-                return primaries;
+            } else {
+                rgb.getPrimaries(primaries);
             }
-            return rgb.getPrimaries(primaries);
+            if (asUcs) xyYToUv(primaries);
+            return primaries;
         }
 
         /**
@@ -3513,7 +3585,13 @@
             int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6;
             float[] vertices = new float[vertexCount * 2];
             int[] colors = new int[vertices.length];
-            computeChromaticityMesh(NATIVE_SIZE, NATIVE_SIZE, vertices, colors);
+            computeChromaticityMesh(vertices, colors);
+
+            if (mUcs) xyYToUv(vertices);
+            for (int i = 0; i < vertices.length; i += 2) {
+                vertices[i] *= width;
+                vertices[i + 1] = height - vertices[i + 1] * height;
+            }
 
             // Draw the spectral locus
             if (mClip && mColorSpaces.size() > 0) {
@@ -3522,7 +3600,8 @@
                     if (colorSpace.getModel() != Model.RGB) continue;
 
                     Rgb rgb = (Rgb) colorSpace;
-                    getPrimaries(rgb, primaries);
+                    getPrimaries(rgb, primaries, mUcs);
+
                     break;
                 }
 
@@ -3559,6 +3638,7 @@
             }
             path.close();
 
+            paint.setStrokeWidth(4.0f / (mUcs ? UCS_SCALE : 1.0f));
             paint.setStyle(Paint.Style.STROKE);
             paint.setColor(0xff000000);
             canvas.drawPath(path, paint);
@@ -3576,25 +3656,38 @@
          */
         private void drawBox(@NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
                 @NonNull Path path) {
+
+            int lineCount = 10;
+            float scale = 1.0f;
+            if (mUcs) {
+                lineCount = 7;
+                scale = UCS_SCALE;
+            }
+
             // Draw the unit grid
             paint.setStyle(Paint.Style.STROKE);
             paint.setStrokeWidth(2.0f);
             paint.setColor(0xffc0c0c0);
-            for (int i = 1; i <= 9; i++) {
-                canvas.drawLine(0.0f, height - (height * i / 10.0f),
-                        0.9f * width, height - (height * i / 10.0f), paint);
-                canvas.drawLine(width * i / 10.0f, height,
-                        width * i / 10.0f, 0.1f * height, paint);
+
+            for (int i = 1; i < lineCount - 1; i++) {
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
+
+                canvas.drawLine(0.0f, y, 0.9f * width, y, paint);
+                canvas.drawLine(x, height, x, 0.1f * height, paint);
             }
 
             // Draw tick marks
             paint.setStrokeWidth(4.0f);
             paint.setColor(0xff000000);
-            for (int i = 1; i <= 9; i++) {
-                canvas.drawLine(0.0f, height - (height * i / 10.0f),
-                        width / 100.0f, height - (height * i / 10.0f), paint);
-                canvas.drawLine(width * i / 10.0f, height,
-                        width * i / 10.0f, height - (height / 100.0f), paint);
+            for (int i = 1; i < lineCount - 1; i++) {
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
+
+                canvas.drawLine(0.0f, y, width / 100.0f, y, paint);
+                canvas.drawLine(x, height, x, height - (height / 100.0f), paint);
             }
 
             // Draw the axis labels
@@ -3603,14 +3696,15 @@
             paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
 
             Rect bounds = new Rect();
-            for (int i = 1; i < 9; i++) {
+            for (int i = 1; i < lineCount - 1; i++) {
                 String text = "0." + i;
                 paint.getTextBounds(text, 0, text.length(), bounds);
 
-                float y = height - (height * i / 10.0f);
-                canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
 
-                float x = width * i / 10.0f;
+                canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
                 canvas.drawText(text, x - bounds.width() / 2.0f,
                         height + bounds.height() + 16, paint);
             }
@@ -3643,7 +3737,7 @@
                 if (colorSpace.getModel() != Model.RGB) continue;
 
                 Rgb rgb = (Rgb) colorSpace;
-                getPrimaries(rgb, primaries);
+                getPrimaries(rgb, primaries, mUcs);
 
                 primariesBounds.left = Math.min(primariesBounds.left, primaries[4]);
                 primariesBounds.top = Math.min(primariesBounds.top, primaries[5]);
@@ -3651,26 +3745,42 @@
                 primariesBounds.bottom = Math.max(primariesBounds.bottom, primaries[3]);
             }
 
+            float max = mUcs ? 0.6f : 0.9f;
+
             primariesBounds.left = Math.min(0.0f, primariesBounds.left);
             primariesBounds.top = Math.min(0.0f, primariesBounds.top);
-            primariesBounds.right = Math.max(0.9f, primariesBounds.right);
-            primariesBounds.bottom = Math.max(0.9f, primariesBounds.bottom);
+            primariesBounds.right = Math.max(max, primariesBounds.right);
+            primariesBounds.bottom = Math.max(max, primariesBounds.bottom);
 
-            float scaleX = 0.9f / primariesBounds.width();
-            float scaleY = 0.9f / primariesBounds.height();
+            float scaleX = max / primariesBounds.width();
+            float scaleY = max / primariesBounds.height();
             float scale = Math.min(scaleX, scaleY);
 
             canvas.scale(mSize / (float) NATIVE_SIZE, mSize / (float) NATIVE_SIZE);
             canvas.scale(scale, scale);
             canvas.translate(
-                    (primariesBounds.width() - 0.9f) * width / 2.0f,
-                    (primariesBounds.height() - 0.9f) * height / 2.0f);
+                    (primariesBounds.width() - max) * width / 2.0f,
+                    (primariesBounds.height() - max) * height / 2.0f);
 
             // The spectrum extends ~0.85 vertically and ~0.65 horizontally
             // We shift the canvas a little bit to get nicer margins
             canvas.translate(0.05f * width, -0.05f * height);
         }
 
+        /**
+         * Computes and applies the Canvas transforms required to render the CIE
+         * 197 UCS chromaticity diagram.
+         *
+         * @param canvas The canvas to transform
+         * @param height Height in pixel of the final image
+         */
+        private void setUcsTransform(@NonNull Canvas canvas, int height) {
+            if (mUcs) {
+                canvas.translate(0.0f, (height - height * UCS_SCALE));
+                canvas.scale(UCS_SCALE, UCS_SCALE);
+            }
+        }
+
         // X coordinates of the spectral locus in CIE 1931
         private static final float[] SPECTRUM_LOCUS_X = {
                 0.175596f, 0.172787f, 0.170806f, 0.170085f, 0.160343f,
@@ -3716,21 +3826,15 @@
                 0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f
         };
 
-        // Number of subdivision of the inside of the spectral locus
-        private static final int CHROMATICITY_RESOLUTION = 32;
-        private static final double ONE_THIRD = 1.0 / 3.0;
-
         /**
          * Computes a 2D mesh representation of the CIE 1931 chromaticity
          * diagram.
          *
-         * @param width Width in pixels of the mesh
-         * @param height Height in pixels of the mesh
          * @param vertices Array of floats that will hold the mesh vertices
          * @param colors Array of floats that will hold the mesh colors
          */
-        private static void computeChromaticityMesh(int width, int height,
-                @NonNull float[] vertices, @NonNull int[] colors) {
+        private static void computeChromaticityMesh(@NonNull float[] vertices,
+                @NonNull int[] colors) {
 
             ColorSpace colorSpace = get(Named.SRGB);
 
@@ -3796,18 +3900,18 @@
                     colorIndex += 6;
 
                     // Flip the mesh upside down to match Canvas' coordinates system
-                    vertices[vertexIndex++] = v1x * width;
-                    vertices[vertexIndex++] = height - v1y * height;
-                    vertices[vertexIndex++] = v2x * width;
-                    vertices[vertexIndex++] = height - v2y * height;
-                    vertices[vertexIndex++] = v3x * width;
-                    vertices[vertexIndex++] = height - v3y * height;
-                    vertices[vertexIndex++] = v1x * width;
-                    vertices[vertexIndex++] = height - v1y * height;
-                    vertices[vertexIndex++] = v3x * width;
-                    vertices[vertexIndex++] = height - v3y * height;
-                    vertices[vertexIndex++] = v4x * width;
-                    vertices[vertexIndex++] = height - v4y * height;
+                    vertices[vertexIndex++] = v1x;
+                    vertices[vertexIndex++] = v1y;
+                    vertices[vertexIndex++] = v2x;
+                    vertices[vertexIndex++] = v2y;
+                    vertices[vertexIndex++] = v3x;
+                    vertices[vertexIndex++] = v3y;
+                    vertices[vertexIndex++] = v1x;
+                    vertices[vertexIndex++] = v1y;
+                    vertices[vertexIndex++] = v3x;
+                    vertices[vertexIndex++] = v3y;
+                    vertices[vertexIndex++] = v4x;
+                    vertices[vertexIndex++] = v4y;
                 }
             }
         }
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 98082ca..0fa52f8 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -22,13 +22,17 @@
 import java.lang.annotation.RetentionPolicy;
 
 public class PixelFormat {
-
     /** @hide */
     @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Opacity {}
 
-    /* these constants need to match those in hardware/hardware.h */
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+    public @interface Format { };
+
+    // NOTE: these constants must match the values from graphics/common/x.x/types.hal
 
     public static final int UNKNOWN     = 0;
 
@@ -62,7 +66,6 @@
     @Deprecated
     public static final int RGB_332     = 0xB;
 
-
     /**
      * @deprecated use {@link android.graphics.ImageFormat#NV16
      * ImageFormat.NV16} instead.
@@ -84,6 +87,9 @@
     @Deprecated
     public static final int YCbCr_422_I = 0x14;
 
+    public static final int RGBA_F16    = 0x16;
+    public static final int RGBX_F16    = 0x17;
+
     /**
      * @deprecated use {@link android.graphics.ImageFormat#JPEG
      * ImageFormat.JPEG} instead.
@@ -91,7 +97,10 @@
     @Deprecated
     public static final int JPEG        = 0x100;
 
-    public static void getPixelFormatInfo(int format, PixelFormat info) {
+    public int bytesPerPixel;
+    public int bitsPerPixel;
+
+    public static void getPixelFormatInfo(@Format int format, PixelFormat info) {
         switch (format) {
             case RGBA_8888:
             case RGBX_8888:
@@ -124,18 +133,24 @@
                 info.bitsPerPixel = 12;
                 info.bytesPerPixel = 1;
                 break;
+            case RGBA_F16:
+            case RGBX_F16:
+                info.bitsPerPixel = 64;
+                info.bytesPerPixel = 8;
+                break;
             default:
                 throw new IllegalArgumentException("unknown pixel format " + format);
         }
     }
 
-    public static boolean formatHasAlpha(int format) {
+    public static boolean formatHasAlpha(@Format int format) {
         switch (format) {
             case PixelFormat.A_8:
             case PixelFormat.LA_88:
             case PixelFormat.RGBA_4444:
             case PixelFormat.RGBA_5551:
             case PixelFormat.RGBA_8888:
+            case PixelFormat.RGBA_F16:
             case PixelFormat.TRANSLUCENT:
             case PixelFormat.TRANSPARENT:
                 return true;
@@ -143,9 +158,6 @@
         return false;
     }
 
-    public int  bytesPerPixel;
-    public int  bitsPerPixel;
-
     /**
      * Determine whether or not this is a public-visible and non-deprecated {@code format}.
      *
@@ -159,12 +171,14 @@
      *
      * @hide
      */
-    public static boolean isPublicFormat(int format) {
+    public static boolean isPublicFormat(@Format int format) {
         switch (format) {
             case RGBA_8888:
             case RGBX_8888:
             case RGB_888:
             case RGB_565:
+            case RGBA_F16:
+            case RGBX_F16:
                 return true;
         }
 
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 4c8abc5..938b6ef 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -131,6 +131,13 @@
                 destWidth, destHeight, caches.maxTextureSize);
         return CopyResult::DestinationInvalid;
     }
+
+    // TODO: Add support for RGBA_F16 destinations
+    if (bitmap->colorType() == kRGBA_F16_SkColorType) {
+        ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+        return CopyResult::DestinationInvalid;
+    }
+
     GLuint fbo = renderState.createFramebuffer();
     if (!fbo) {
         ALOGW("Could not obtain an FBO");
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5b5b74e..705395e 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -225,6 +225,12 @@
         *outInternalFormat = GL_LUMINANCE;
         *outType = GL_UNSIGNED_BYTE;
         break;
+    case kRGBA_F16_SkColorType:
+        // This format is always linear
+        *outFormat = GL_RGBA;
+        *outInternalFormat = GL_RGBA16F;
+        *outType = GL_HALF_FLOAT;
+        break;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
         break;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 2177af1..a9058b1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -108,6 +108,8 @@
         return PIXEL_FORMAT_RGBA_8888;
     case GL_RGB:
         return PIXEL_FORMAT_RGB_565;
+    case GL_RGBA16F:
+        return PIXEL_FORMAT_RGBA_FP16;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
         return PIXEL_FORMAT_UNKNOWN;
@@ -306,7 +308,8 @@
 
 sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
     PixelFormat format = graphicBuffer->getPixelFormat();
-    if (!graphicBuffer.get() || format != PIXEL_FORMAT_RGBA_8888) {
+    if (!graphicBuffer.get() ||
+            (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
         return nullptr;
     }
     SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),