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 9d83ac225d20b..2657140cd634a 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -681,6 +681,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, @@ -982,6 +988,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 9c80ab304b800..e2a4a2a8dec26 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(); + } + } + } }