diff options
-rw-r--r-- | libs/hwui/DeviceInfo.cpp | 16 | ||||
-rw-r--r-- | libs/hwui/DeviceInfo.h | 9 | ||||
-rw-r--r-- | libs/hwui/LayerUpdateQueue.h | 1 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 28 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 5 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 31 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.h | 13 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 14 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/EglManager.cpp | 45 | ||||
-rw-r--r-- | libs/hwui/renderthread/EglManager.h | 11 | ||||
-rw-r--r-- | libs/hwui/renderthread/IRenderPipeline.h | 25 | ||||
-rw-r--r-- | libs/hwui/renderthread/VulkanManager.h | 4 | ||||
-rw-r--r-- | libs/hwui/tests/unit/RenderNodeDrawableTests.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaPipelineTests.cpp | 30 | ||||
-rw-r--r-- | libs/hwui/utils/Color.cpp | 39 | ||||
-rw-r--r-- | libs/hwui/utils/Color.h | 5 |
19 files changed, 212 insertions, 82 deletions
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index a43f58cd1e56..b5b87d516ff7 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -66,12 +66,16 @@ void DeviceInfo::initialize(int maxTextureSize) { sDeviceInfo = new DeviceInfo(); sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo(); sDeviceInfo->mMaxTextureSize = maxTextureSize; + sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace, + &sDeviceInfo->mTargetPixelFormat); }); } void DeviceInfo::load() { mDisplayInfo = queryDisplayInfo(); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace, + &sDeviceInfo->mTargetPixelFormat); } DisplayInfo DeviceInfo::queryDisplayInfo() { @@ -86,5 +90,17 @@ DisplayInfo DeviceInfo::queryDisplayInfo() { return displayInfo; } +void DeviceInfo::queryCompositionPreference(ui::Dataspace* dataSpace, + ui::PixelFormat* pixelFormat) { + if (Properties::isolatedProcess) { + *dataSpace = ui::Dataspace::V0_SRGB; + *pixelFormat = ui::PixelFormat::RGBA_8888; + } + + status_t status = + SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat); + LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 297b2664414b..416af179eb21 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -17,6 +17,7 @@ #define DEVICEINFO_H #include <ui/DisplayInfo.h> +#include <ui/GraphicTypes.h> #include "Extensions.h" #include "utils/Macros.h" @@ -39,6 +40,8 @@ public: static void initialize(int maxTextureSize); int maxTextureSize() const { return mMaxTextureSize; } + ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; } + ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; } const DisplayInfo& displayInfo() const { return mDisplayInfo; } const Extensions& extensions() const { return mExtensions; } @@ -50,6 +53,9 @@ public: static DisplayInfo queryDisplayInfo(); private: + static void queryCompositionPreference(ui::Dataspace* dataSpace, + ui::PixelFormat* pixelFormat); + DeviceInfo() {} ~DeviceInfo() {} @@ -58,6 +64,9 @@ private: int mMaxTextureSize; DisplayInfo mDisplayInfo; Extensions mExtensions; + // TODO(lpy) Replace below with android_ prefix types. + ui::Dataspace mTargetDataSpace; + ui::PixelFormat mTargetPixelFormat; }; } /* namespace uirenderer */ diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h index b14b80cc598a..6857999500f0 100644 --- a/libs/hwui/LayerUpdateQueue.h +++ b/libs/hwui/LayerUpdateQueue.h @@ -19,6 +19,7 @@ #include <utils/StrongPointer.h> #include "Rect.h" +#include "RenderNode.h" #include "utils/Macros.h" #include <unordered_map> diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index d58b59e83380..26f01cecf802 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -62,21 +62,23 @@ Frame SkiaOpenGLPipeline::getFrame() { bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { mEglManager.damageFrame(frame, dirty); - SkColorType colorType; + SkColorType colorType = getSurfaceColorType(); // setup surface for fbo0 GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; - if (wideColorGamut) { + if (colorType == kRGBA_F16_SkColorType) { fboInfo.fFormat = GL_RGBA16F; - colorType = kRGBA_F16_SkColorType; - } else { + } else if (colorType == kN32_SkColorType) { + // 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; - colorType = kN32_SkColorType; + } else { + LOG_ALWAYS_FATAL("Unsupported color type."); } GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo); @@ -89,8 +91,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con nullptr, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, - surface); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); layerUpdateQueue->clear(); // Draw visual debugging features @@ -147,8 +148,7 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, if (surface) { mRenderThread.requireGlContext(); - const bool wideColorGamut = colorMode == ColorMode::WideColorGamut; - mEglSurface = mEglManager.createSurface(surface, wideColorGamut); + mEglSurface = mEglManager.createSurface(surface, colorMode); } if (mEglSurface != EGL_NO_SURFACE) { @@ -168,6 +168,14 @@ bool SkiaOpenGLPipeline::isContextReady() { return CC_LIKELY(mEglManager.hasEglContext()); } +SkColorType SkiaOpenGLPipeline::getSurfaceColorType() const { + return mEglManager.getSurfaceColorType(); +} + +sk_sp<SkColorSpace> SkiaOpenGLPipeline::getSurfaceColorSpace() { + return mEglManager.getSurfaceColorSpace(); +} + void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; if (thread.eglManager().hasEglContext()) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 808685ad4460..fbdf3139dabd 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -34,8 +34,7 @@ public: renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, @@ -46,6 +45,8 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; + SkColorType getSurfaceColorType() const override; + sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 988981d65775..7f8abb8afa97 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -83,16 +83,15 @@ void SkiaPipeline::onPrepareTree() { void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, - bool wideColorGamut, const LightInfo& lightInfo) { + const LightInfo& lightInfo) { updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); renderVectorDrawableCache(); - renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut); + renderLayersImpl(*layerUpdateQueue, opaque); layerUpdateQueue->clear(); } -void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, - bool wideColorGamut) { +void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) { sk_sp<GrContext> cachedContext; // Render all layers that need to be updated, in order. @@ -161,7 +160,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, } bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) { + ErrorHandler* errorHandler) { // compute the size of the surface (i.e. texture) to be allocated for this layer const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE; const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE; @@ -169,12 +168,8 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator SkSurface* layer = node->getLayerSurface(); if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { SkImageInfo info; - if (wideColorGamut) { - info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType, - kPremul_SkAlphaType); - } else { - info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight); - } + info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(), + kPremul_SkAlphaType); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), @@ -321,19 +316,18 @@ void SkiaPipeline::endCapture(SkSurface* surface) { void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - bool wideColorGamut, const Rect& contentDrawBounds, - sk_sp<SkSurface> surface) { + const Rect& contentDrawBounds, sk_sp<SkSurface> surface) { renderVectorDrawableCache(); // draw all layers up front - renderLayersImpl(layers, opaque, wideColorGamut); + renderLayersImpl(layers, opaque); // initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. std::unique_ptr<SkPictureRecorder> recorder; SkCanvas* canvas = tryCapture(surface.get()); - renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); + renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas); endCapture(surface.get()); @@ -354,13 +348,12 @@ static Rect nodeBounds(RenderNode& node) { void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - bool wideColorGamut, const Rect& contentDrawBounds, - SkCanvas* canvas) { + const Rect& contentDrawBounds, SkCanvas* canvas) { SkAutoCanvasRestore saver(canvas, true); canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 - if (!opaque || wideColorGamut) { + if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) { canvas->clear(SK_ColorTRANSPARENT); } @@ -493,7 +486,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // each time a pixel would have been drawn. // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero // initialized. - renderFrameImpl(layers, clip, nodes, true, false, contentDrawBounds, &overdrawCanvas); + renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); // Draw overdraw colors to the canvas. The color filter will convert counts to colors. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 8c9c80381b84..b78dea1a5c87 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -42,15 +42,14 @@ public: void unpinImages() override; void onPrepareTree() override; - void renderLayers(const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo) override; + void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + bool opaque, const LightInfo& lightInfo) override; bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) override; + ErrorHandler* errorHandler) override; void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, sk_sp<SkSurface> surface); std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; } @@ -59,7 +58,7 @@ public: static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); - void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut); + void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); static float getLightRadius() { if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { @@ -112,7 +111,7 @@ protected: private: void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas); /** diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 611a34c069d4..5cbe33debbce 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -63,8 +63,7 @@ Frame SkiaVulkanPipeline::getFrame() { bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface(); @@ -72,8 +71,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, - backBuffer); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer); layerUpdateQueue->clear(); // Draw visual debugging features @@ -139,6 +137,14 @@ bool SkiaVulkanPipeline::isContextReady() { return CC_LIKELY(mVkManager.hasVkContext()); } +SkColorType SkiaVulkanPipeline::getSurfaceColorType() const { + return mVkManager.getSurfaceColorType(); +} + +sk_sp<SkColorSpace> SkiaVulkanPipeline::getSurfaceColorSpace() { + return mVkManager.getSurfaceColorSpace(); +} + void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { // TODO: we currently don't support OpenGL WebView's DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 900b054e35bd..6e723a8373e1 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -32,8 +32,7 @@ public: renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, @@ -44,6 +43,8 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; + SkColorType getSurfaceColorType() const override; + sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 727cef3035f5..ea6a851419c0 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -151,7 +151,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { mNativeSurface = std::move(surface); - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb; + // TODO(b/111436479) Introduce a way for app to specify DisplayColorGamut mode. + ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Legacy; bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); mFrameNumber = -1; @@ -416,7 +417,7 @@ void CanvasContext::draw() { SkRect windowDirty = computeDirtyRect(frame, &dirty); bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, - mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, + mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; @@ -555,8 +556,7 @@ void CanvasContext::buildLayer(RenderNode* node) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); - mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mWideColorGamut, - mLightInfo); + mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 02ee72f05f04..8448788eb9a6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -75,8 +75,7 @@ public: */ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator, ErrorHandler* errorHandler) { - return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut, - errorHandler); + return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler); } /** diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 5f8d7ad3373a..8e44d633f505 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -20,6 +20,7 @@ #include <log/log.h> #include <private/gui/SyncFeatures.h> #include <utils/Trace.h> +#include "utils/Color.h" #include "utils/StringUtils.h" #include "DeviceInfo.h" @@ -75,6 +76,7 @@ static struct { bool pixelFormatFloat = false; bool glColorSpace = false; bool scRGB = false; + bool displayP3 = false; bool contextPriority = false; bool surfacelessContext = false; } EglExtensions; @@ -125,6 +127,17 @@ void EglManager::initialize() { createPBufferSurface(); makeCurrent(mPBufferSurface, nullptr, /* force */ true); DeviceInfo::initialize(); + + mSurfaceColorGamut = DataSpaceToColorGamut( + static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); + + LOG_ALWAYS_FATAL_IF(mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut && + !EglExtensions.displayP3, "EGL doesn't support Display P3."); + + mSurfaceColorType = PixelFormatToColorType( + static_cast<android_pixel_format>(DeviceInfo::get()->getTargetPixelFormat())); + mSurfaceColorSpace = DataSpaceToColorSpace( + static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); } void EglManager::initExtensions() { @@ -148,6 +161,7 @@ void EglManager::initExtensions() { #else EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb"); #endif + EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3"); EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority"); EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); } @@ -160,6 +174,10 @@ void EglManager::loadConfigs() { ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + + // Note: The default pixel format is RGBA_8888, when other formats are + // available, we should check the target pixel format and configure the + // attributes list properly. EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, @@ -255,11 +273,12 @@ void EglManager::createPBufferSurface() { } } -EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) { +EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) { LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized"); - wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB && - EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext; + bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace && + EglExtensions.scRGB && 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 @@ -269,9 +288,9 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG // 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) + // - If linear blending is on, we must use the non-linear 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 @@ -279,7 +298,7 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG // - 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 + // Not all Android targets support the EGL_GL_COLORSPACE_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 @@ -291,13 +310,21 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; } else { - attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { + attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + } } #else if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT; } else { - attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { + attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + } } #endif } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 507673adf26e..e97228cd0a39 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -18,11 +18,13 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <SkImageInfo.h> #include <SkRect.h> #include <cutils/compiler.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> +#include "IRenderPipeline.h" namespace android { namespace uirenderer { @@ -45,7 +47,7 @@ public: bool hasEglContext(); - EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut); + EGLSurface createSurface(EGLNativeWindowType window, ColorMode colorMode); void destroySurface(EGLSurface surface); void destroy(); @@ -76,6 +78,9 @@ public: // Depending on installed extensions, the result is either Android native fence or EGL fence. status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence); + SkColorType getSurfaceColorType() const { return mSurfaceColorType; } + sk_sp<SkColorSpace> getSurfaceColorSpace() { return mSurfaceColorSpace; } + private: void initExtensions(); @@ -89,8 +94,10 @@ private: EGLConfig mEglConfigWideGamut; EGLContext mEglContext; EGLSurface mPBufferSurface; - EGLSurface mCurrentSurface; + SkColorSpace::Gamut mSurfaceColorGamut; + SkColorType mSurfaceColorType; + sk_sp<SkColorSpace> mSurfaceColorSpace; enum class SwapBehavior { Discard, diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index b7b7853e6ed7..0297c9c141ff 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -16,11 +16,12 @@ #pragma once +#include "DamageAccumulator.h" #include "FrameInfoVisualizer.h" #include "LayerUpdateQueue.h" +#include "Lighting.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" -#include "thread/TaskManager.h" #include <SkRect.h> #include <utils/RefBase.h> @@ -35,13 +36,25 @@ namespace uirenderer { class DeferredLayerUpdater; class ErrorHandler; +class TaskManager; namespace renderthread { enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; enum class ColorMode { - Srgb, + // Legacy means HWUI will produce buffer with whatever platform prefers + // HWUI to produce, however, HWUI doesn't accurately convert color from + // source color space to destination color space, instead HWUI will take + // the pixel value directly and interpret it destination color space. + Legacy, + // DisplayColorGamut means HWUI will produce buffer with whatever platform + // prefers HWUI to produce and accurately convert color from source color + // space to destination color space. + DisplayColorGamut, + // 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 }; @@ -55,7 +68,7 @@ public: virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, @@ -67,15 +80,17 @@ public: virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; virtual void renderLayers(const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, + LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) = 0; + ErrorHandler* errorHandler) = 0; virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; virtual void unpinImages() = 0; virtual void onPrepareTree() = 0; + virtual SkColorType getSurfaceColorType() const = 0; + virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0; virtual ~IRenderPipeline() {} }; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index ebc11a50685e..7a539ae42605 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -118,6 +118,10 @@ public: // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. status_t createReleaseFence(sp<Fence>& nativeFence); + // TODO(b/115636873): Handle composition preference. + SkColorType getSurfaceColorType() const { return SkColorType::kN32_SkColorType; } + sk_sp<SkColorSpace> getSurfaceColorSpace() { return SkColorSpace::MakeSRGB(); } + private: friend class RenderThread; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 48bc8e4df155..2926ef3a2d95 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -537,7 +537,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { layerUpdateQueue.enqueueLayerWithDamage(child.get(), android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - pipeline->renderLayersImpl(layerUpdateQueue, true, false); + pipeline->renderLayersImpl(layerUpdateQueue, true); EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true); diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index a9124d90f9a5..5e5d13485942 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -50,7 +50,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { auto surface = SkSurface::MakeRasterN32Premul(1, 1); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -83,7 +83,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -105,11 +105,11 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); @@ -129,7 +129,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE); @@ -171,7 +171,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { lightGeometry.center = {0.0f, 0.0f, 0.0f}; LightInfo lightInfo; auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo); + pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo); ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE); @@ -202,37 +202,37 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // Single draw, should be white. - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); // 1 Overdraw, should be blue blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff); // 2 Overdraw, should be green blended onto white renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0); // 3 Overdraw, should be pink blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0); // 4 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); // 5 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); } @@ -318,7 +318,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { SkRect dirty = SkRect::MakeWH(800, 600); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface); EXPECT_EQ(4, surface->canvas()->mDrawCounter); } @@ -348,7 +348,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } @@ -378,7 +378,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index a3e785957526..d35fe4f01aa0 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -57,6 +57,45 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) { return false; } +SkColorType PixelFormatToColorType(android_pixel_format pixelFormat) { + switch (pixelFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + return SkColorType::kN32_SkColorType; + case HAL_PIXEL_FORMAT_RGBA_FP16: + return SkColorType::kRGBA_F16_SkColorType; + case HAL_PIXEL_FORMAT_RGBA_1010102: + return SkColorType::kRGBA_1010102_SkColorType; + default: + ALOGW("Unsupported pixel format: %d, return kN32 by default", pixelFormat); + return SkColorType::kN32_SkColorType; + } +} + +SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) { + switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + return SkColorSpace::kSRGB_Gamut; + case HAL_DATASPACE_STANDARD_BT2020: + return SkColorSpace::kRec2020_Gamut; + case HAL_DATASPACE_STANDARD_DCI_P3: + return SkColorSpace::kDCIP3_D65_Gamut; + case HAL_DATASPACE_STANDARD_ADOBE_RGB: + return SkColorSpace::kAdobeRGB_Gamut; + case HAL_DATASPACE_STANDARD_UNSPECIFIED: + case HAL_DATASPACE_STANDARD_BT601_625: + case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT601_525: + case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: + case HAL_DATASPACE_STANDARD_BT470M: + case HAL_DATASPACE_STANDARD_FILM: + default: + ALOGW("Unsupported Gamut: %d, return SRGB gamut by default", dataSpace); + return SkColorSpace::kSRGB_Gamut; + } +} + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { SkColorSpace::Gamut gamut; diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 3c13a548fe76..ff0e755934f2 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -21,6 +21,7 @@ #include <SkColor.h> #include <SkColorSpace.h> +#include <SkImageInfo.h> namespace android { namespace uirenderer { @@ -113,6 +114,10 @@ static constexpr float EOCF(float srgb) { // returns true for sRGB, gamma 2.2 and Display P3 for instance bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); +SkColorType PixelFormatToColorType(android_pixel_format pixelFormat); + +SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace); + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); struct Lab { |