Wire-up colorMode="hdr"

Fow now it uses a fixed white point of 150nits
TBD if this is disabled or adjusted

Test: Demo app
Change-Id: Iac13597b3d7633fdef3feaf7ec1da0c27c87904c
diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h
new file mode 100644
index 0000000..6d387f9
--- /dev/null
+++ b/libs/hwui/ColorMode.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::uirenderer {
+
+// Must match the constants in ActivityInfo.java
+enum class ColorMode {
+    // SRGB means HWUI will produce buffer in SRGB color space.
+    Default = 0,
+    // WideColorGamut selects the most optimal colorspace & format for the device's display
+    // Most commonly DisplayP3 + RGBA_8888 currently.
+    WideColorGamut = 1,
+    // HDR Rec2020 + F16
+    Hdr = 2,
+    // HDR Rec2020 + 1010102
+    Hdr10 = 3,
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 42743db..7d6875f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -143,11 +143,10 @@
 }
 
 static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
-        jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) {
+        jboolean translucent, jlong rootRenderNodePtr) {
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
     ContextFactoryImpl factory(rootRenderNode);
     RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
-    proxy->setWideGamut(isWideGamut);
     return (jlong) proxy;
 }
 
@@ -218,10 +217,10 @@
     proxy->setOpaque(opaque);
 }
 
-static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jboolean wideGamut) {
+static void android_view_ThreadedRenderer_setColorMode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint colorMode) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setWideGamut(wideGamut);
+    proxy->setColorMode(static_cast<ColorMode>(colorMode));
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
@@ -659,7 +658,7 @@
          (void*)android_view_ThreadedRenderer_setProcessStatsBuffer},
         {"nGetRenderThreadTid", "(J)I", (void*)android_view_ThreadedRenderer_getRenderThreadTid},
         {"nCreateRootRenderNode", "()J", (void*)android_view_ThreadedRenderer_createRootRenderNode},
-        {"nCreateProxy", "(ZZJ)J", (void*)android_view_ThreadedRenderer_createProxy},
+        {"nCreateProxy", "(ZJ)J", (void*)android_view_ThreadedRenderer_createProxy},
         {"nDeleteProxy", "(J)V", (void*)android_view_ThreadedRenderer_deleteProxy},
         {"nLoadSystemProperties", "(J)Z",
          (void*)android_view_ThreadedRenderer_loadSystemProperties},
@@ -671,7 +670,7 @@
         {"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha},
         {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry},
         {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque},
-        {"nSetWideGamut", "(JZ)V", (void*)android_view_ThreadedRenderer_setWideGamut},
+        {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
         {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
         {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
         {"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 24a6228..389fe7e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -87,6 +87,8 @@
         // Note: The default preference of pixel format is RGBA_8888, when other
         // pixel format is available, we should branch out and do more check.
         fboInfo.fFormat = GL_RGBA8;
+    } else if (colorType == kRGBA_1010102_SkColorType) {
+        fboInfo.fFormat = GL_RGB10_A2;
     } else {
         LOG_ALWAYS_FATAL("Unsupported color type.");
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 89a1c71..6dd3698 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -35,6 +35,7 @@
 #include "VectorDrawable.h"
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
+#include "utils/Color.h"
 #include "utils/String8.h"
 #include "utils/TraceUtils.h"
 
@@ -587,14 +588,23 @@
 
 void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
     mColorMode = colorMode;
-    if (colorMode == ColorMode::SRGB) {
-        mSurfaceColorType = SkColorType::kN32_SkColorType;
-        mSurfaceColorSpace = SkColorSpace::MakeSRGB();
-    } else if (colorMode == ColorMode::WideColorGamut) {
-        mSurfaceColorType = DeviceInfo::get()->getWideColorType();
-        mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
-    } else {
-        LOG_ALWAYS_FATAL("Unreachable: unsupported color mode.");
+    switch (colorMode) {
+        case ColorMode::Default:
+            mSurfaceColorType = SkColorType::kN32_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+            break;
+        case ColorMode::WideColorGamut:
+            mSurfaceColorType = DeviceInfo::get()->getWideColorType();
+            mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
+            break;
+        case ColorMode::Hdr:
+            mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
+            break;
+        case ColorMode::Hdr10:
+            mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
+            break;
     }
 }
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 8341164..100bfb6 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -50,7 +50,7 @@
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
-    void setSurfaceColorProperties(renderthread::ColorMode colorMode) override;
+    void setSurfaceColorProperties(ColorMode colorMode) override;
     SkColorType getSurfaceColorType() const override { return mSurfaceColorType; }
     sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
 
@@ -76,7 +76,7 @@
 
     renderthread::RenderThread& mRenderThread;
 
-    renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB;
+    ColorMode mColorMode = ColorMode::Default;
     SkColorType mSurfaceColorType;
     sk_sp<SkColorSpace> mSurfaceColorSpace;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a362bd2..13d544c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -174,7 +174,10 @@
     } else {
         mNativeSurface = nullptr;
     }
+    setupPipelineSurface();
+}
 
+void CanvasContext::setupPipelineSurface() {
     bool hasSurface = mRenderPipeline->setSurface(
             mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
 
@@ -184,7 +187,7 @@
 
     mFrameNumber = -1;
 
-    if (window != nullptr && hasSurface) {
+    if (mNativeSurface != nullptr && hasSurface) {
         mHaveNewSurface = true;
         mSwapHistory.clear();
         // Enable frame stats after the surface has been bound to the appropriate graphics API.
@@ -239,9 +242,9 @@
     mOpaque = opaque;
 }
 
-void CanvasContext::setWideGamut(bool wideGamut) {
-    ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
-    mRenderPipeline->setSurfaceColorProperties(colorMode);
+void CanvasContext::setColorMode(ColorMode mode) {
+    mRenderPipeline->setSurfaceColorProperties(mode);
+    setupPipelineSurface();
 }
 
 bool CanvasContext::makeCurrent() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0306eec..cba710f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -30,6 +30,7 @@
 #include "renderthread/RenderTask.h"
 #include "renderthread/RenderThread.h"
 #include "utils/RingBuffer.h"
+#include "ColorMode.h"
 
 #include <SkBitmap.h>
 #include <SkRect.h>
@@ -119,7 +120,7 @@
     void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightGeometry(const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
-    void setWideGamut(bool wideGamut);
+    void setColorMode(ColorMode mode);
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
     void draw();
@@ -211,6 +212,7 @@
     bool isSwapChainStuffed();
     bool surfaceRequiresRedraw();
     void setPresentTime();
+    void setupPipelineSurface();
 
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c701353..2a8aa8c 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -76,6 +76,7 @@
     bool glColorSpace = false;
     bool scRGB = false;
     bool displayP3 = false;
+    bool hdr = false;
     bool contextPriority = false;
     bool surfacelessContext = false;
     bool nativeFenceSync = false;
@@ -86,7 +87,8 @@
 EglManager::EglManager()
         : mEglDisplay(EGL_NO_DISPLAY)
         , mEglConfig(nullptr)
-        , mEglConfigWideGamut(nullptr)
+        , mEglConfigF16(nullptr)
+        , mEglConfig1010102(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
         , mCurrentSurface(EGL_NO_SURFACE)
@@ -143,8 +145,7 @@
     } else {
         LOG_ALWAYS_FATAL("Unsupported wide color space.");
     }
-    mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension &&
-                                mEglConfigWideGamut != EGL_NO_CONFIG_KHR;
+    mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension;
 }
 
 EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
@@ -177,6 +178,35 @@
     return config;
 }
 
+EGLConfig EglManager::load1010102Config(EGLDisplay display, SwapBehavior swapBehavior) {
+    EGLint eglSwapBehavior =
+            (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    // If we reached this point, we have a valid swap behavior
+    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+                        EGL_OPENGL_ES2_BIT,
+                        EGL_RED_SIZE,
+                        10,
+                        EGL_GREEN_SIZE,
+                        10,
+                        EGL_BLUE_SIZE,
+                        10,
+                        EGL_ALPHA_SIZE,
+                        2,
+                        EGL_DEPTH_SIZE,
+                        0,
+                        EGL_STENCIL_SIZE,
+                        STENCIL_BUFFER_SIZE,
+                        EGL_SURFACE_TYPE,
+                        EGL_WINDOW_BIT | eglSwapBehavior,
+                        EGL_NONE};
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EGLint numConfigs = 1;
+    if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || numConfigs != 1) {
+        return EGL_NO_CONFIG_KHR;
+    }
+    return config;
+}
+
 EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior) {
     EGLint eglSwapBehavior =
             (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
@@ -230,6 +260,7 @@
     EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
     EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
     EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
+    EglExtensions.hdr = extensions.has("EGL_EXT_gl_colorspace_bt2020_pq");
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
     EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
     EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
@@ -260,18 +291,20 @@
             LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
         }
     }
-    SkColorType wideColorType = DeviceInfo::get()->getWideColorType();
 
     // When we reach this point, we have a valid swap behavior
-    if (wideColorType == SkColorType::kRGBA_F16_SkColorType && EglExtensions.pixelFormatFloat) {
-        mEglConfigWideGamut = loadFP16Config(mEglDisplay, mSwapBehavior);
-        if (mEglConfigWideGamut == EGL_NO_CONFIG_KHR) {
+    if (EglExtensions.pixelFormatFloat) {
+        mEglConfigF16 = loadFP16Config(mEglDisplay, mSwapBehavior);
+        if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
             ALOGE("Device claims wide gamut support, cannot find matching config, error = %s",
                   eglErrorString());
             EglExtensions.pixelFormatFloat = false;
         }
-    } else if (wideColorType == SkColorType::kN32_SkColorType) {
-        mEglConfigWideGamut = load8BitsConfig(mEglDisplay, mSwapBehavior);
+    }
+    mEglConfig1010102 = load1010102Config(mEglDisplay, mSwapBehavior);
+    if (mEglConfig1010102 == EGL_NO_CONFIG_KHR) {
+        ALOGW("Failed to initialize 101010-2 format, error = %s",
+              eglErrorString());
     }
 }
 
@@ -311,8 +344,9 @@
                                                      sk_sp<SkColorSpace> colorSpace) {
     LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
-    bool wideColorGamut = colorMode == ColorMode::WideColorGamut && mHasWideColorGamutSupport &&
-                          EglExtensions.noConfigContext;
+    if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) {
+        colorMode = ColorMode::Default;
+    }
 
     // The color space we want to use depends on whether linear blending is turned
     // on and whether the app has requested wide color gamut rendering. When wide
@@ -338,26 +372,47 @@
     // list is considered empty if the first entry is EGL_NONE
     EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
 
+    EGLConfig config = mEglConfig;
+    if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+        if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+            colorMode = ColorMode::Default;
+        } else {
+            config = mEglConfigF16;
+        }
+    }
     if (EglExtensions.glColorSpace) {
         attribs[0] = EGL_GL_COLORSPACE_KHR;
-        if (wideColorGamut) {
-            skcms_Matrix3x3 colorGamut;
-            LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
-                                "Could not get gamut matrix from color space");
-            if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
-                attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
-            } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
-                attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
-            } else {
-                LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+        switch (colorMode) {
+            case ColorMode::Default:
+                attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+                break;
+            case ColorMode::WideColorGamut: {
+                skcms_Matrix3x3 colorGamut;
+                LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+                                    "Could not get gamut matrix from color space");
+                if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+                } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+                } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                } else {
+                    LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+                }
+                break;
             }
-        } else {
-            attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+            case ColorMode::Hdr:
+                config = mEglConfigF16;
+                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                break;
+            case ColorMode::Hdr10:
+                config = mEglConfig1010102;
+                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                break;
         }
     }
 
-    EGLSurface surface = eglCreateWindowSurface(
-            mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
+    EGLSurface surface = eglCreateWindowSurface(mEglDisplay, config, window, attribs);
     if (surface == EGL_NO_SURFACE) {
         return Error<EGLint>{eglGetError()};
     }
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index f67fb31..69f3ed0 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -88,6 +88,7 @@
 
     static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
     static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
+    static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior);
 
     void initExtensions();
     void createPBufferSurface();
@@ -97,7 +98,8 @@
 
     EGLDisplay mEglDisplay;
     EGLConfig mEglConfig;
-    EGLConfig mEglConfigWideGamut;
+    EGLConfig mEglConfigF16;
+    EGLConfig mEglConfig1010102;
     EGLContext mEglContext;
     EGLSurface mPBufferSurface;
     EGLSurface mCurrentSurface;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index c3c2286..a04738d 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -22,6 +22,7 @@
 #include "Lighting.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
+#include "ColorMode.h"
 
 #include <SkRect.h>
 #include <utils/RefBase.h>
@@ -42,16 +43,6 @@
 
 enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded };
 
-enum class ColorMode {
-    // SRGB means HWUI will produce buffer in SRGB color space.
-    SRGB,
-    // WideColorGamut means HWUI would support rendering scRGB non-linear into
-    // a signed buffer with enough range to support the wide color gamut of the
-    // display.
-    WideColorGamut,
-    // Hdr
-};
-
 class Frame;
 
 class IRenderPipeline {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b764f74b..aad0cca 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -109,8 +109,8 @@
     mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); });
 }
 
-void RenderProxy::setWideGamut(bool wideGamut) {
-    mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); });
+void RenderProxy::setColorMode(ColorMode mode) {
+    mRenderThread.queue().post([=]() { mContext->setColorMode(mode); });
 }
 
 int64_t* RenderProxy::frameInfo() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 16eabad..33dabc9 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -24,6 +24,7 @@
 
 #include "../FrameMetricsObserver.h"
 #include "../IContextFactory.h"
+#include "ColorMode.h"
 #include "DrawFrameTask.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
@@ -77,7 +78,7 @@
     void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightGeometry(const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
-    void setWideGamut(bool wideGamut);
+    void setColorMode(ColorMode mode);
     int64_t* frameInfo();
     int syncAndDrawFrame();
     void destroy();
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index ca1bf69..eff34a8 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -344,5 +344,27 @@
             static_cast<uint8_t>(rgb.b * 255));
 }
 
+// Note that SkColorSpace doesn't have the notion of an unspecified SDR white
+// level.
+static constexpr float kDefaultSDRWhiteLevel = 150.f;
+
+skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
+    if (sdr_white_level <= 0.f) {
+        sdr_white_level = kDefaultSDRWhiteLevel;
+    }
+    // The generic PQ transfer function produces normalized luminance values i.e.
+    // the range 0-1 represents 0-10000 nits for the reference display, but we
+    // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
+    const double w = 10000. / sdr_white_level;
+    // Distribute scaling factor W by scaling A and B with X ^ (1/F):
+    // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
+    // See https://crbug.com/1058580#c32 for discussion.
+    skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
+    const double ws = pow(w, 1. / fn.f);
+    fn.a = ws * fn.a;
+    fn.b = ws * fn.b;
+    return fn;
+}
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 71ed683..1654072 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -126,6 +126,7 @@
 
 Lab sRGBToLab(SkColor color);
 SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level = 0.f);
 
 } /* namespace uirenderer */
 } /* namespace android */