From 26a2b97dbe48ee45e9ae70110714048f2f360f97 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Mon, 17 Apr 2017 09:39:51 -0700 Subject: [PATCH] Enable wide color gamut rendering When wide color gamut rendering is requested, hwui will now use an rgba16f scRGB-nl surface for rendering. This change also fixes the way screenshots are handled in the platform to behave properly with wide gamut rendering. This change does not affect hardware layers. They also need to use rgba16f scRGB-nl; this will be addressed in another CL. Bug: 29940137 Test: CtsUiRenderingTestCases, CtsGraphicsTestCases Change-Id: I68fd96c451652136c566ec48fb0e97c2a7a257c5 --- cmds/screencap/screencap.cpp | 39 ++++++- core/java/android/view/ThreadedRenderer.java | 15 ++- core/java/android/view/ViewRootImpl.java | 5 + core/java/android/view/WindowManager.java | 9 ++ core/jni/android_view_SurfaceControl.cpp | 11 +- core/jni/android_view_ThreadedRenderer.cpp | 7 ++ .../hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 6 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 3 +- .../hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 4 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 3 +- libs/hwui/renderthread/CanvasContext.cpp | 7 +- libs/hwui/renderthread/CanvasContext.h | 2 + libs/hwui/renderthread/EglManager.cpp | 104 +++++++++++++++--- libs/hwui/renderthread/EglManager.h | 5 +- libs/hwui/renderthread/IRenderPipeline.h | 8 +- libs/hwui/renderthread/OpenGLPipeline.cpp | 5 +- libs/hwui/renderthread/OpenGLPipeline.h | 2 +- libs/hwui/renderthread/RenderProxy.cpp | 12 ++ libs/hwui/renderthread/RenderProxy.h | 3 +- .../src/com/android/shell/Screenshooter.java | 2 +- .../systemui/screenshot/GlobalScreenshot.java | 3 +- .../display/DisplayTransformManager.java | 56 +++++++++- 22 files changed, 274 insertions(+), 37 deletions(-) diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 5fedc9e74087f..607e6e0186e7e 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -33,17 +33,24 @@ #include #include +#include + // TODO: Fix Skia. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include #include +#include #pragma GCC diagnostic pop using namespace android; static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain; +#define COLORSPACE_UNKNOWN 0 +#define COLORSPACE_SRGB 1 +#define COLORSPACE_DISPLAY_P3 2 + static void usage(const char* pname) { fprintf(stderr, @@ -67,6 +74,31 @@ static SkColorType flinger2skia(PixelFormat f) } } +static sk_sp dataSpaceToColorSpace(android_dataspace d) +{ + switch (d) { + case HAL_DATASPACE_V0_SRGB: + return SkColorSpace::MakeSRGB(); + case HAL_DATASPACE_DISPLAY_P3: + return SkColorSpace::MakeRGB( + SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); + default: + return nullptr; + } +} + +static uint32_t dataSpaceToInt(android_dataspace d) +{ + switch (d) { + case HAL_DATASPACE_V0_SRGB: + return COLORSPACE_SRGB; + case HAL_DATASPACE_DISPLAY_P3: + return COLORSPACE_DISPLAY_P3; + default: + return COLORSPACE_UNKNOWN; + } +} + static status_t notifyMediaScanner(const char* fileName) { String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://"); String8 fileUrl("\""); @@ -139,6 +171,7 @@ int main(int argc, char** argv) void const* base = NULL; uint32_t w, s, h, f; + android_dataspace d; size_t size = 0; // Maps orientations from DisplayInfo to ISurfaceComposer @@ -177,13 +210,15 @@ int main(int argc, char** argv) h = screenshot.getHeight(); s = screenshot.getStride(); f = screenshot.getFormat(); + d = screenshot.getDataSpace(); size = screenshot.getSize(); } if (base != NULL) { if (png) { const SkImageInfo info = - SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType); + SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, + dataSpaceToColorSpace(d)); SkPixmap pixmap(info, base, s * bytesPerPixel(f)); struct FDWStream final : public SkWStream { size_t fBytesWritten = 0; @@ -200,9 +235,11 @@ int main(int argc, char** argv) notifyMediaScanner(fn); } } else { + uint32_t c = dataSpaceToInt(d); write(fd, &w, 4); write(fd, &h, 4); write(fd, &f, 4); + write(fd, &c, 4); size_t Bpp = bytesPerPixel(f); for (size_t y=0 ; y colorSpace; + if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) { + colorSpace = SkColorSpace::MakeRGB( + SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); + } else { + colorSpace = SkColorSpace::MakeSRGB(); + } + SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(), screenshot->getHeight(), colorType, alphaType, - GraphicsJNI::defaultColorSpace()); + colorSpace); const size_t rowBytes = screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 4c530d7d16812..3125753309ca2 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -674,6 +674,12 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, proxy->setOpaque(opaque); } +static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean wideGamut) { + RenderProxy* proxy = reinterpret_cast(proxyPtr); + proxy->setWideGamut(wideGamut); +} + static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, @@ -974,6 +980,7 @@ static const JNINativeMethod gMethods[] = { { "nSetup", "(JFII)V", (void*) android_view_ThreadedRenderer_setup }, { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter }, { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, + { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut }, { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index ae1313101f3c0..46cee67ff89a6 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -155,7 +155,8 @@ void SkiaOpenGLPipeline::onStop() { } } -bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { +bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, + ColorMode colorMode) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); @@ -163,7 +164,8 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) } if (surface) { - mEglSurface = mEglManager.createSurface(surface); + const bool wideColorGamut = colorMode == ColorMode::WideColorGamut; + mEglSurface = mEglManager.createSurface(surface, wideColorGamut); } if (mEglSurface != EGL_NO_SURFACE) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 36685ddb17a78..caf9467598779 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -40,7 +40,8 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override; + bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, + renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index d28e605a051f9..e8e8ccd96654f 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -131,13 +131,15 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() { } -bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { +bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, + ColorMode colorMode) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; } if (surface) { + // TODO: handle color mode mVkSurface = mVkManager.createSurface(surface); } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index aab1d7a547c00..12935a604ffb8 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -41,7 +41,8 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override; + bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, + renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 02a9ffa3039cc..0058e4de166e9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -186,7 +186,8 @@ void CanvasContext::setSurface(Surface* surface) { mNativeSurface = surface; - bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior); + ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb; + bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior, colorMode); mFrameNumber = -1; @@ -241,6 +242,10 @@ void CanvasContext::setOpaque(bool opaque) { mOpaque = opaque; } +void CanvasContext::setWideGamut(bool wideGamut) { + mWideColorGamut = wideGamut; +} + bool CanvasContext::makeCurrent() { if (mStopped) return false; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 33eda96a2d77c..76623f9ae954f 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -128,6 +128,7 @@ public: uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightCenter(const Vector3& lightCenter); void setOpaque(bool opaque); + void setWideGamut(bool wideGamut); bool makeCurrent(); void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target); @@ -240,6 +241,7 @@ private: nsecs_t mLastDropVsync = 0; bool mOpaque; + bool mWideColorGamut = false; BakedOpRenderer::LightInfo mLightInfo; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 44af5fd8d0dde..53d42a2b9c43f 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -76,12 +76,16 @@ const char* EglManager::eglErrorString() { static struct { bool bufferAge = false; bool setDamage = false; + bool noConfigContext = false; + bool pixelFormatFloat = false; + bool glColorSpace = false; } EglExtensions; EglManager::EglManager(RenderThread& thread) : mRenderThread(thread) , mEglDisplay(EGL_NO_DISPLAY) , mEglConfig(nullptr) + , mEglConfigWideGamut(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) , mCurrentSurface(EGL_NO_SURFACE) { @@ -116,7 +120,7 @@ void EglManager::initialize() { } } - loadConfig(); + loadConfigs(); createContext(); createPBufferSurface(); makeCurrent(mPBufferSurface); @@ -143,6 +147,7 @@ void EglManager::initialize() { void EglManager::initExtensions() { auto extensions = StringUtils::split( eglQueryString(mEglDisplay, EGL_EXTENSIONS)); + // For our purposes we don't care if EGL_BUFFER_AGE is a result of // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered // under EGL_KHR_partial_update and we don't need the expanded scope @@ -152,13 +157,17 @@ void EglManager::initExtensions() { EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update"); LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"), "Missing required extension EGL_KHR_swap_buffers_with_damage"); + + EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace"); + EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context"); + EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float"); } bool EglManager::hasEglContext() { return mEglDisplay != EGL_NO_DISPLAY; } -void EglManager::loadConfig() { +void EglManager::loadConfigs() { ALOGD("Swap behavior %d", static_cast(mSwapBehavior)); EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; @@ -175,19 +184,44 @@ void EglManager::loadConfig() { EGL_NONE }; - EGLint num_configs = 1; - if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) - || num_configs != 1) { + EGLint numConfigs = 1; + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs) + || numConfigs != 1) { if (mSwapBehavior == SwapBehavior::Preserved) { // Try again without dirty regions enabled ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); mSwapBehavior = SwapBehavior::Discard; - loadConfig(); + loadConfigs(); + return; // the call to loadConfigs() we just made picks the wide gamut config } else { // Failed to get a valid config LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString()); } } + + if (EglExtensions.pixelFormatFloat) { + // If we reached this point, we have a valid swap behavior + EGLint attribs16F[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT, + EGL_RED_SIZE, 16, + EGL_GREEN_SIZE, 16, + EGL_BLUE_SIZE, 16, + EGL_ALPHA_SIZE, 16, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, Stencil::getStencilSize(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, + EGL_NONE + }; + + numConfigs = 1; + if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs) + || numConfigs != 1) { + LOG_ALWAYS_FATAL( + "Device claims wide gamut support, cannot find matching config, error = %s", + eglErrorString()); + } + } } void EglManager::createContext() { @@ -195,7 +229,9 @@ void EglManager::createContext() { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; - mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); + mEglContext = eglCreateContext(mEglDisplay, + EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig, + EGL_NO_CONTEXT, attribs); LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, "Failed to create context, error = %s", eglErrorString()); } @@ -210,18 +246,60 @@ void EglManager::createPBufferSurface() { } } -EGLSurface EglManager::createSurface(EGLNativeWindowType window) { +EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) { initialize(); + wideColorGamut = wideColorGamut && EglExtensions.glColorSpace + && EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext; + + // 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 + // color gamut rendering is off, the app simply renders in the display's native + // color gamut. + // + // When wide gamut rendering is off: + // - Blending is done by default in gamma space, which requires using a + // linear EGL color space (the GPU uses the color values as is) + // - If linear blending is on, we must use the sRGB EGL color space (the + // GPU will perform sRGB to linear and linear to SRGB conversions before + // and after blending) + // + // When wide gamut rendering is on we cannot rely on the GPU performing + // linear blending for us. We use two different color spaces to tag the + // surface appropriately for SurfaceFlinger: + // - Gamma blending (default) requires the use of the scRGB-nl color space + // - Linear blending requires the use of the scRGB color space + + // Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension + // We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value. + // According to section 3.4.1 of the EGL specification, the attributes + // list is considered empty if the first entry is EGL_NONE EGLint attribs[] = { -#ifdef ANDROID_ENABLE_LINEAR_BLENDING - EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, - EGL_COLORSPACE, EGL_COLORSPACE_sRGB, -#endif + EGL_NONE, EGL_NONE, EGL_NONE }; - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs); + if (EglExtensions.glColorSpace) { + attribs[0] = EGL_GL_COLORSPACE_KHR; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + if (wideColorGamut) { + attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + } +#else + if (wideColorGamut) { + // TODO: this should be using scRGB-nl, not scRGB, we need an extension for this + // TODO: in the meantime SurfaceFlinger just assumes that scRGB is scRGB-nl + attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + } +#endif + } + + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, + wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", (void*) window, eglErrorString()); diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 025192511cd98..2982c23552c9a 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -39,7 +39,7 @@ public: bool hasEglContext(); - EGLSurface createSurface(EGLNativeWindowType window); + EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut); void destroySurface(EGLSurface surface); void destroy(); @@ -68,7 +68,7 @@ private: void initExtensions(); void createPBufferSurface(); - void loadConfig(); + void loadConfigs(); void createContext(); EGLint queryBufferAge(EGLSurface surface); @@ -76,6 +76,7 @@ private: EGLDisplay mEglDisplay; EGLConfig mEglConfig; + EGLConfig mEglConfigWideGamut; EGLContext mEglContext; EGLSurface mPBufferSurface; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 45f6718a68fb2..46ac0d23637d5 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -44,6 +44,12 @@ enum class MakeCurrentResult { Succeeded }; +enum class ColorMode { + Srgb, + WideColorGamut, + // Hdr +}; + class Frame; class IRenderPipeline { @@ -61,7 +67,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0; + virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp index e1ae58532a020..9631fd6510162 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.cpp +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -146,7 +146,7 @@ void OpenGLPipeline::onStop() { } } -bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { +bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); @@ -154,7 +154,8 @@ bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { } if (surface) { - mEglSurface = mEglManager.createSurface(surface); + const bool wideColorGamut = colorMode == ColorMode::WideColorGamut; + mEglSurface = mEglManager.createSurface(surface, wideColorGamut); } if (mEglSurface != EGL_NO_SURFACE) { diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h index 6df8be477e9c1..c3cf80369bb05 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.h +++ b/libs/hwui/renderthread/OpenGLPipeline.h @@ -44,7 +44,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; - bool setSurface(Surface* window, SwapBehavior swapBehavior) override; + bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index eed5238104038..83b3e3a3ab1e8 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -227,6 +227,18 @@ void RenderProxy::setOpaque(bool opaque) { post(task); } +CREATE_BRIDGE2(setWideGamut, CanvasContext* context, bool wideGamut) { + args->context->setWideGamut(args->wideGamut); + return nullptr; +} + +void RenderProxy::setWideGamut(bool wideGamut) { + SETUP_TASK(setWideGamut); + args->context = mContext; + args->wideGamut = wideGamut; + post(task); +} + int64_t* RenderProxy::frameInfo() { return mDrawFrameTask.frameInfo(); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index b21772cd88de5..e1e2808e734fa 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -69,7 +69,7 @@ namespace DumpFlags { */ class ANDROID_API RenderProxy { public: - ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory); + ANDROID_API RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory); ANDROID_API virtual ~RenderProxy(); // Won't take effect until next EGLSurface creation @@ -85,6 +85,7 @@ public: uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setLightCenter(const Vector3& lightCenter); ANDROID_API void setOpaque(bool opaque); + ANDROID_API void setWideGamut(bool wideGamut); ANDROID_API int64_t* frameInfo(); ANDROID_API int syncAndDrawFrame(); ANDROID_API void destroy(); diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java index 92c5fcc3139f9..8e27edf93d9ce 100644 --- a/packages/Shell/src/com/android/shell/Screenshooter.java +++ b/packages/Shell/src/com/android/shell/Screenshooter.java @@ -100,7 +100,7 @@ final class Screenshooter { // Rotate the screenshot to the current orientation if (rotation != ROTATION_FREEZE_0) { Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight, - Bitmap.Config.ARGB_8888); + Bitmap.Config.ARGB_8888, screenShot.hasAlpha(), screenShot.getColorSpace()); Canvas canvas = new Canvas(unrotatedScreenShot); canvas.translate(unrotatedScreenShot.getWidth() / 2, unrotatedScreenShot.getHeight() / 2); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 2d47c7bcaa2f1..a7b845e2e62c1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -578,7 +578,8 @@ class GlobalScreenshot { if (requiresRotation) { // Rotate the screenshot to the current orientation Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, - mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); + mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888, + mScreenBitmap.hasAlpha(), mScreenBitmap.getColorSpace()); Canvas c = new Canvas(ss); c.translate(ss.getWidth() / 2, ss.getHeight() / 2); c.rotate(degrees); diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java index 6902b1a5cd046..ffee99f21c3ed 100644 --- a/services/core/java/com/android/server/display/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/DisplayTransformManager.java @@ -21,6 +21,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.MathUtils; import android.util.Slog; import android.util.SparseArray; @@ -48,6 +49,10 @@ public class DisplayTransformManager { */ public static final int LEVEL_COLOR_MATRIX_INVERT_COLOR = 300; + private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015; + private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014; + private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022; + /** * Map of level -> color transformation matrix. */ @@ -66,6 +71,10 @@ public class DisplayTransformManager { @GuardedBy("mDaltonizerModeLock") private int mDaltonizerMode = -1; + private final Object mSaturationLock = new Object(); + @GuardedBy("mSaturationLock") + private float mSaturation = 1.0f; + /* package */ DisplayTransformManager() { } @@ -155,6 +164,30 @@ public class DisplayTransformManager { } } + /** + * Returns the current saturation. + */ + public float getSaturation() { + synchronized (mSaturationLock) { + return mSaturation; + } + } + + /** + * Sets the saturation level of the display. The default value is 1.0. + * + * @param saturation A value between 0 (0% saturation, grayscale) and 2 (100% extra saturation) + */ + public void setSaturation(float saturation) { + synchronized (mSaturationLock) { + saturation = MathUtils.constrain(saturation, 0.0f, 2.0f); + if (mSaturation != saturation) { + mSaturation = saturation; + applySaturation(saturation); + } + } + } + /** * Propagates the provided color transformation matrix to the SurfaceFlinger. */ @@ -172,7 +205,7 @@ public class DisplayTransformManager { data.writeInt(0); } try { - flinger.transact(1015, data, null, 0); + flinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0); } catch (RemoteException ex) { Slog.e(TAG, "Failed to set color transform", ex); } finally { @@ -191,7 +224,7 @@ public class DisplayTransformManager { data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeInt(mode); try { - flinger.transact(1014, data, null, 0); + flinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0); } catch (RemoteException ex) { Slog.e(TAG, "Failed to set Daltonizer mode", ex); } finally { @@ -199,4 +232,23 @@ public class DisplayTransformManager { } } } + + /** + * Propagates the provided saturation to the SurfaceFlinger. + */ + private static void applySaturation(float saturation) { + final IBinder flinger = ServiceManager.getService("SurfaceFlinger"); + if (flinger != null) { + final Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeFloat(saturation); + try { + flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to set saturation", ex); + } finally { + data.recycle(); + } + } + } }