From 401c6844d9b2040288ded461b81105e6bdf4f0b4 Mon Sep 17 00:00:00 2001 From: Kelsey Gilbert Date: Mon, 6 May 2024 23:36:51 +0000 Subject: [PATCH] Bug 1885447 - Tonemap/color-manage webgl sdr/wcg output into Display profile as pre-compositor post-process. r=gfx-reviewers,webidl,smaug,ahale Differential Revision: https://phabricator.services.mozilla.com/D207651 --- dom/canvas/ClientWebGLContext.cpp | 17 +- dom/canvas/ClientWebGLContext.h | 14 + dom/canvas/HostWebGLContext.h | 3 + dom/canvas/WebGLContext.cpp | 299 ++++++++++------ dom/canvas/WebGLContext.h | 25 ++ dom/canvas/WebGLContextTextures.cpp | 2 +- dom/canvas/WebGLIpdl.h | 28 ++ dom/canvas/WebGLMethodDispatcher.h | 1 + dom/canvas/WebGLQueueParamTraits.h | 41 +-- dom/canvas/WebGLTypes.h | 4 +- .../reftest/colors/_generated_reftest.list | 309 +++++++++------- .../test/reftest/colors/color_canvas.html | 1 + .../colors/generate_color_canvas_reftests.py | 44 ++- dom/webidl/WebGLRenderingContext.webidl | 5 + gfx/gl/AutoMappable.h | 159 +++------ gfx/gl/Colorspaces.h | 126 +++++-- gfx/gl/GLBlitHelper.cpp | 335 ++++++++++++++---- gfx/gl/GLBlitHelper.h | 33 +- gfx/gl/GLBlitHelperD3D.cpp | 15 +- gfx/gl/GLContext.h | 51 ++- gfx/gl/SharedSurface.h | 5 + gfx/gl/SharedSurfaceIO.h | 2 + gfx/gl/gtest/TestColorspaces.cpp | 12 +- gfx/thebes/gfxPlatform.cpp | 2 +- gfx/thebes/gfxPlatform.h | 2 +- gfx/webrender_bindings/DCLayerTree.cpp | 14 +- gfx/webrender_bindings/DCLayerTree.h | 8 +- 27 files changed, 1033 insertions(+), 524 deletions(-) diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index 236ea916d0cc..5c92ba003d9b 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -736,6 +736,21 @@ void ClientWebGLContext::GetCanvas( } } +void ClientWebGLContext::SetDrawingBufferColorSpace( + const dom::PredefinedColorSpace val) { + mDrawingBufferColorSpace = val; + + // Just in case, update in Options too. + // Why not treat our WebGLContextOptions as the source of truth? Well, + // mNotLost is lost on context-loss, so we'd lose any setting we had here if + // that happens. + if (mNotLost) { + mNotLost->info.options.colorSpace = mDrawingBufferColorSpace; + } + + Run(mDrawingBufferColorSpace); +} + void ClientWebGLContext::GetContextAttributes( dom::Nullable& retval) { retval.SetNull(); @@ -1058,9 +1073,7 @@ ClientWebGLContext::SetContextOptions(JSContext* cx, if (attributes.mAntialias.WasPassed()) { newOpts.antialias = attributes.mAntialias.Value(); } - newOpts.ignoreColorSpace = true; if (attributes.mColorSpace.WasPassed()) { - newOpts.ignoreColorSpace = false; newOpts.colorSpace = attributes.mColorSpace.Value(); } diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h index e736235361ee..e870d5860a75 100644 --- a/dom/canvas/ClientWebGLContext.h +++ b/dom/canvas/ClientWebGLContext.h @@ -1052,6 +1052,20 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, const FuncScope funcScope(*this, "drawingBufferHeight"); return AutoAssertCast(DrawingBufferSize().y); } + + // - + + private: + dom::PredefinedColorSpace mDrawingBufferColorSpace = + dom::PredefinedColorSpace::Srgb; + + public: + auto DrawingBufferColorSpace() const { return mDrawingBufferColorSpace; } + + void SetDrawingBufferColorSpace(dom::PredefinedColorSpace); + + // - + void GetContextAttributes(dom::Nullable& retval); private: diff --git a/dom/canvas/HostWebGLContext.h b/dom/canvas/HostWebGLContext.h index 23b836f9db94..7d8fe29cd8a2 100644 --- a/dom/canvas/HostWebGLContext.h +++ b/dom/canvas/HostWebGLContext.h @@ -201,6 +201,9 @@ class HostWebGLContext final : public SupportsWeakPtr { // - + void SetDrawingBufferColorSpace(const dom::PredefinedColorSpace val) const { + mContext->SetDrawingBufferColorSpace(val); + } void Resize(const uvec2& size) { return mContext->Resize(size); } uvec2 DrawingBufferSize() { return mContext->DrawingBufferSize(); } diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 4bd189c46c02..667ef4303731 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -894,63 +894,62 @@ void WebGLContext::BlitBackbufferToCurDriverFB( if (mScissorTestEnabled) { gl->fDisable(LOCAL_GL_SCISSOR_TEST); } - - [&]() { - // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not - // used since it might not have completeness info, while the MozFramebuffer - // can still supply the needed information. - MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb)); - const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get(); - GLuint fbo = 0; - gfx::IntSize size; - if (srcAsWebglFb) { - fbo = srcAsWebglFb->mGLName; - const auto* info = srcAsWebglFb->GetCompletenessInfo(); - MOZ_ASSERT(info); - size = gfx::IntSize(info->width, info->height); - } else { - fbo = mozFb->mFB; - size = mozFb->mSize; + const auto cleanup = MakeScopeExit([&]() { + if (mScissorTestEnabled) { + gl->fEnable(LOCAL_GL_SCISSOR_TEST); } + }); - // If no format conversion is necessary, then attempt to directly blit - // between framebuffers. Otherwise, if we need to convert to RGBA from - // the source format, then we will need to use the texture blit path - // below. - if (!srcIsBGRA) { - if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); - gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width, - size.height, LOCAL_GL_COLOR_BUFFER_BIT, - LOCAL_GL_NEAREST); - return; - } - if (mDefaultFB->mSamples && - gl->IsExtensionSupported( - gl::GLContext::APPLE_framebuffer_multisample)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); - gl->fResolveMultisampleFramebufferAPPLE(); - return; - } - } - - GLuint colorTex = 0; - if (srcAsWebglFb) { - const auto& attach = srcAsWebglFb->ColorAttachment0(); - MOZ_ASSERT(attach.Texture()); - colorTex = attach.Texture()->mGLName; - } else { - colorTex = mozFb->ColorTex(); - } - - // DrawBlit handles ColorMask itself. - gl->BlitHelper()->DrawBlitTextureToFramebuffer( - colorTex, size, size, LOCAL_GL_TEXTURE_2D, srcIsBGRA); - }(); - - if (mScissorTestEnabled) { - gl->fEnable(LOCAL_GL_SCISSOR_TEST); + // If a MozFramebuffer is supplied, ensure that a WebGLFramebuffer is not + // used since it might not have completeness info, while the MozFramebuffer + // can still supply the needed information. + MOZ_ASSERT(!(srcAsMozFb && srcAsWebglFb)); + const auto* mozFb = srcAsMozFb ? srcAsMozFb : mDefaultFB.get(); + GLuint fbo = 0; + gfx::IntSize size; + if (srcAsWebglFb) { + fbo = srcAsWebglFb->mGLName; + const auto* info = srcAsWebglFb->GetCompletenessInfo(); + MOZ_ASSERT(info); + size = gfx::IntSize(info->width, info->height); + } else { + fbo = mozFb->mFB; + size = mozFb->mSize; } + + // If no format conversion is necessary, then attempt to directly blit + // between framebuffers. Otherwise, if we need to convert to RGBA from + // the source format, then we will need to use the texture blit path + // below. + if (!srcIsBGRA) { + if (gl->IsSupported(gl::GLFeature::framebuffer_blit)) { + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); + gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width, + size.height, LOCAL_GL_COLOR_BUFFER_BIT, + LOCAL_GL_NEAREST); + return; + } + if (mDefaultFB->mSamples && + gl->IsExtensionSupported( + gl::GLContext::APPLE_framebuffer_multisample)) { + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fbo); + gl->fResolveMultisampleFramebufferAPPLE(); + return; + } + } + + GLuint colorTex = 0; + if (srcAsWebglFb) { + const auto& attach = srcAsWebglFb->ColorAttachment0(); + MOZ_ASSERT(attach.Texture()); + colorTex = attach.Texture()->mGLName; + } else { + colorTex = mozFb->ColorTex(); + } + + // DrawBlit handles ColorMask itself. + gl->BlitHelper()->DrawBlitTextureToFramebuffer( + colorTex, size, size, LOCAL_GL_TEXTURE_2D, srcIsBGRA); } // - @@ -960,19 +959,34 @@ constexpr auto MakeArray(Args... args) -> std::array { return {{static_cast(args)...}}; } -inline gfx::ColorSpace2 ToColorSpace2(const WebGLContextOptions& options) { - auto ret = gfx::ColorSpace2::UNKNOWN; - if (true) { - ret = gfx::ColorSpace2::SRGB; +inline gfx::ColorSpace2 ToColorSpace2ForOutput( + const std::optional chosenCspace) { + const auto cmsMode = GfxColorManagementMode(); + switch (cmsMode) { + case CMSMode::Off: + return gfx::ColorSpace2::Display; + case CMSMode::TaggedOnly: + if (!chosenCspace) { + return gfx::ColorSpace2::Display; + } + break; + case CMSMode::All: + if (!chosenCspace) { + return gfx::ColorSpace2::SRGB; + } + break; } - if (!options.ignoreColorSpace) { - ret = gfx::ToColorSpace2(options.colorSpace); - } - return ret; + return gfx::ToColorSpace2(*chosenCspace); } // - +template +GLuint GLNameOrZero(const T& t) { + if (t) return t->mGLName; + return 0; +} + // For an overview of how WebGL compositing works, see: // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing bool WebGLContext::PresentInto(gl::SwapChain& swapChain) { @@ -980,46 +994,100 @@ bool WebGLContext::PresentInto(gl::SwapChain& swapChain) { if (!ValidateAndInitFB(nullptr)) return false; - { - const auto colorSpace = ToColorSpace2(mOptions); - auto presenter = swapChain.Acquire(mDefaultFB->mSize, colorSpace); + const auto size = mDefaultFB->mSize; + + const auto error = [&]() -> std::optional { + const auto canvasCspace = ToColorSpace2ForOutput(mOptions.colorSpace); + auto presenter = swapChain.Acquire(size, canvasCspace); if (!presenter) { - GenerateWarning("Swap chain surface creation failed."); - LoseContext(); - return false; + return "Swap chain surface creation failed."; } - + const auto outputCspace = presenter->BackBuffer()->mDesc.colorSpace; const auto destFb = presenter->Fb(); - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb); - BlitBackbufferToCurDriverFB(); + // - - if (!mOptions.preserveDrawingBuffer) { - if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) { - gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB); - constexpr auto attachments = MakeArray( - LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); - gl->fInvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, - attachments.size(), attachments.data()); - } - mDefaultFB_IsInvalid = true; + bool colorManage = (canvasCspace != gfx::ColorSpace2::Display); + if (canvasCspace == outputCspace) { + colorManage = false; + } + if (!gl->IsSupported(gl::GLFeature::texture_3D)) { + NS_WARNING("Missing GLFeature::texture_3D => colorManage = false."); + colorManage = false; } -#ifdef DEBUG - if (!mOptions.alpha) { - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, destFb); - gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); - if (IsWebGL2()) { - gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0); - gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, 0); - gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0); + auto colorLut = std::shared_ptr{}; + if (colorManage) { + MOZ_ASSERT(canvasCspace != gfx::ColorSpace2::Display); + colorLut = gl->BlitHelper()->GetColorLutTex(gl::GLBlitHelper::ColorLutKey{ + .src = canvasCspace, .dst = outputCspace}); + if (!colorLut) { + NS_WARNING("GetColorLutTex() -> nullptr => colorManage = false."); + colorManage = false; } - uint32_t pixel = 0xffbadbad; - gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, - &pixel); - MOZ_ASSERT((pixel & 0xff000000) == 0xff000000); } -#endif + + if (!colorManage) { + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb); + BlitBackbufferToCurDriverFB(); + return {}; + } + + // - + + const auto canvasFb = GetDefaultFBForRead({.endOfFrame = true}); + if (!canvasFb) { + return "[WebGLContext::PresentInto] BindDefaultFBForRead failed."; + } + + const auto& blitter = gl->BlitHelper()->GetDrawBlitProg({ + .fragHeader = gl::kFragHeader_Tex2D, + .fragParts = {gl::kFragSample_OnePlane, gl::kFragConvert_ColorLut3d}, + }); + + constexpr uint8_t texUnit_src = 0; + constexpr uint8_t texUnit_lut = 1; + gl->BindSamplerTexture(texUnit_src, SamplerLinear(), LOCAL_GL_TEXTURE_2D, + canvasFb->ColorTex()); + gl->BindSamplerTexture(texUnit_lut, SamplerLinear(), LOCAL_GL_TEXTURE_3D, + colorLut->name); + const auto texCleanup = MakeScopeExit([&]() { + gl->BindSamplerTexture( + texUnit_src, GLNameOrZero(mBoundSamplers[texUnit_src]), + LOCAL_GL_TEXTURE_2D, GLNameOrZero(mBound2DTextures[texUnit_src])); + gl->BindSamplerTexture( + texUnit_lut, GLNameOrZero(mBoundSamplers[texUnit_lut]), + LOCAL_GL_TEXTURE_3D, GLNameOrZero(mBound3DTextures[texUnit_lut])); + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); + }); + + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFb); + + gl->fUseProgram(blitter.mProg); + const auto cleanupProg = MakeScopeExit( + [&]() { gl->fUseProgram(GLNameOrZero(mCurrentProgram)); }); + + gl->fUniform1i(blitter.mLoc_uColorLut, texUnit_lut); + + blitter.Draw({ + .texMatrix0 = gl::Mat3::I(), + .yFlip = false, + .destSize = size, + .destRect = {}, + }); + + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, canvasFb->mFB); + return {}; + }(); + if (error) { + GenerateWarning("%s", error->c_str()); + LoseContext(); + return false; + } + + if (!mOptions.preserveDrawingBuffer) { + gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER); + mDefaultFB_IsInvalid = true; } return true; @@ -1029,7 +1097,7 @@ bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain, const gl::MozFramebuffer& fb) { OnEndOfFrame(); - const auto colorSpace = ToColorSpace2(mOptions); + const auto colorSpace = ToColorSpace2ForOutput(mOptions.colorSpace); auto presenter = swapChain.Acquire(fb.mSize, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); @@ -1153,7 +1221,7 @@ bool WebGLContext::CopyToSwapChain( { // ColorSpace will need to be part of SwapChainOptions for DTWebgl. - const auto colorSpace = ToColorSpace2(mOptions); + const auto colorSpace = ToColorSpace2ForOutput(mOptions.colorSpace); auto presenter = srcFb->mSwapChain.Acquire(size, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); @@ -1697,12 +1765,12 @@ bool WebGLContext::BindCurFBForColorRead( return true; } -bool WebGLContext::BindDefaultFBForRead() { - if (!ValidateAndInitFB(nullptr)) return false; +const gl::MozFramebuffer* WebGLContext::GetDefaultFBForRead( + const GetDefaultFBForReadDesc& desc) { + if (!ValidateAndInitFB(nullptr)) return nullptr; if (!mDefaultFB->mSamples) { - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB); - return true; + return mDefaultFB.get(); } if (!mResolvedDefaultFB) { @@ -1710,14 +1778,24 @@ bool WebGLContext::BindDefaultFBForRead() { gl::MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false); if (!mResolvedDefaultFB) { gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB."; - return false; + return nullptr; } } - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB); + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mResolvedDefaultFB->mFB); BlitBackbufferToCurDriverFB(); - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB); + if (desc.endOfFrame && !mOptions.preserveDrawingBuffer) { + gl->InvalidateFramebuffer(LOCAL_GL_READ_FRAMEBUFFER); + } + + return mResolvedDefaultFB.get(); +} + +bool WebGLContext::BindDefaultFBForRead() { + const auto fb = GetDefaultFBForRead(); + if (!fb) return false; + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fb->mFB); return true; } @@ -2661,4 +2739,21 @@ webgl::ExplicitPixelPackingState::ForUseWith( return {{state, metrics}}; } +GLuint WebGLContext::SamplerLinear() const { + if (!mSamplerLinear) { + mSamplerLinear = std::make_unique(*gl); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MAG_FILTER, + LOCAL_GL_LINEAR); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_LINEAR); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + gl->fSamplerParameteri(mSamplerLinear->name, LOCAL_GL_TEXTURE_WRAP_R, + LOCAL_GL_CLAMP_TO_EDGE); + } + return mSamplerLinear->name; +} + } // namespace mozilla diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index d144584f4fd9..69cff4e7dd7a 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -10,6 +10,7 @@ #include #include +#include "Colorspaces.h" #include "GLContextTypes.h" #include "GLDefs.h" #include "GLScreenBuffer.h" @@ -96,6 +97,7 @@ namespace gl { class GLScreenBuffer; class MozFramebuffer; class SharedSurface; +class Sampler; class Texture; } // namespace gl @@ -536,6 +538,12 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr { Maybe GetFrontBuffer(WebGLFramebuffer*, const bool webvr); + std::optional mDisplayProfile; + + void SetDrawingBufferColorSpace(const dom::PredefinedColorSpace val) { + mOptions.colorSpace = val; + } + void ClearVRSwapChain(); void RunContextLossTimer(); @@ -1156,6 +1164,10 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr { nsTArray> mBound2DArrayTextures; nsTArray> mBoundSamplers; + mutable std::unique_ptr mSamplerLinear; + + GLuint SamplerLinear() const; + void ResolveTexturesForDraw() const; RefPtr mCurrentProgram; @@ -1265,6 +1277,10 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr { mutable bool mDefaultFB_IsInvalid = false; mutable UniquePtr mResolvedDefaultFB; + mutable std::unordered_map, + std::shared_ptr> + mLutTexByColorMapping; + gl::SwapChain mSwapChain; gl::SwapChain mWebVRSwapChain; @@ -1303,6 +1319,15 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr { WebGLFramebuffer* const srcAsWebglFb = nullptr, const gl::MozFramebuffer* const srcAsMozFb = nullptr, bool srcIsBGRA = false) const; + + struct GetDefaultFBForReadDesc { + bool endOfFrame = false; + }; + const gl::MozFramebuffer* GetDefaultFBForRead(const GetDefaultFBForReadDesc&); + const gl::MozFramebuffer* GetDefaultFBForRead() { + return GetDefaultFBForRead({}); + } + bool BindDefaultFBForRead(); // -- diff --git a/dom/canvas/WebGLContextTextures.cpp b/dom/canvas/WebGLContextTextures.cpp index e65533ec9486..2bc56f4b0521 100644 --- a/dom/canvas/WebGLContextTextures.cpp +++ b/dom/canvas/WebGLContextTextures.cpp @@ -96,7 +96,7 @@ void WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) { return; } - const TexTarget texTarget(rawTarget); + const auto texTarget = TexTarget(rawTarget); if (newTex) { if (!newTex->BindTexture(texTarget)) return; } else { diff --git a/dom/canvas/WebGLIpdl.h b/dom/canvas/WebGLIpdl.h index 45a5c5ad648d..4c81393b704c 100644 --- a/dom/canvas/WebGLIpdl.h +++ b/dom/canvas/WebGLIpdl.h @@ -18,6 +18,8 @@ #include "TupleUtils.h" #include "WebGLTypes.h" +#include + namespace mozilla { namespace webgl { @@ -652,6 +654,32 @@ struct ParamTraits> final { } }; +// - + +template +struct ParamTraits> final { + using T = std::optional; + + static void Write(MessageWriter* const writer, const T& in) { + WriteParam(writer, bool{in}); + if (in) { + WriteParam(writer, *in); + } + } + + static bool Read(MessageReader* const reader, T* const out) { + bool isSome; + if (!ReadParam(reader, &isSome)) return false; + + if (!isSome) { + out->reset(); + return true; + } + out->emplace(); + return ReadParam(reader, &**out); + } +}; + } // namespace IPC #endif diff --git a/dom/canvas/WebGLMethodDispatcher.h b/dom/canvas/WebGLMethodDispatcher.h index 4bf67deb0854..e55b19d0b40b 100644 --- a/dom/canvas/WebGLMethodDispatcher.h +++ b/dom/canvas/WebGLMethodDispatcher.h @@ -93,6 +93,7 @@ DEFINE_ASYNC(HostWebGLContext::ProvokingVertex) DEFINE_ASYNC(HostWebGLContext::Present) DEFINE_ASYNC(HostWebGLContext::SampleCoverage) DEFINE_ASYNC(HostWebGLContext::Scissor) +DEFINE_ASYNC(HostWebGLContext::SetDrawingBufferColorSpace) DEFINE_ASYNC(HostWebGLContext::ShaderSource) DEFINE_ASYNC(HostWebGLContext::StencilFuncSeparate) DEFINE_ASYNC(HostWebGLContext::StencilMaskSeparate) diff --git a/dom/canvas/WebGLQueueParamTraits.h b/dom/canvas/WebGLQueueParamTraits.h index 3c74f0875041..136316433f9a 100644 --- a/dom/canvas/WebGLQueueParamTraits.h +++ b/dom/canvas/WebGLQueueParamTraits.h @@ -61,25 +61,6 @@ struct QueueParamTraits> : QueueParamTraits_TiedFields> {}; // --------------------------------------------------------------------- // Enums! -inline constexpr bool IsEnumCase(const dom::WebGLPowerPreference raw) { - switch (raw) { - case dom::WebGLPowerPreference::Default: - case dom::WebGLPowerPreference::Low_power: - case dom::WebGLPowerPreference::High_performance: - return true; - } - return false; -} - -inline constexpr bool IsEnumCase(const dom::PredefinedColorSpace raw) { - switch (raw) { - case dom::PredefinedColorSpace::Srgb: - case dom::PredefinedColorSpace::Display_p3: - return true; - } - return false; -} - inline constexpr bool IsEnumCase(const webgl::AttribBaseType raw) { switch (raw) { case webgl::AttribBaseType::Boolean: @@ -91,16 +72,14 @@ inline constexpr bool IsEnumCase(const webgl::AttribBaseType raw) { return false; } -static_assert(IsEnumCase(dom::WebGLPowerPreference(2))); -static_assert(!IsEnumCase(dom::WebGLPowerPreference(3))); -static_assert(!IsEnumCase(dom::WebGLPowerPreference(5))); +static_assert(IsEnumCase(webgl::AttribBaseType(3))); +static_assert(!IsEnumCase(webgl::AttribBaseType(4))); +static_assert(!IsEnumCase(webgl::AttribBaseType(5))); #define USE_IS_ENUM_CASE(T) \ template <> \ struct QueueParamTraits : QueueParamTraits_IsEnumCase {}; -USE_IS_ENUM_CASE(dom::WebGLPowerPreference) -USE_IS_ENUM_CASE(dom::PredefinedColorSpace) USE_IS_ENUM_CASE(webgl::AttribBaseType) USE_IS_ENUM_CASE(webgl::ProvokingVertex) @@ -283,6 +262,20 @@ struct QueueParamTraits : public ContiguousEnumSerializerInclusive< gfxAlphaType, gfxAlphaType::Opaque, gfxAlphaType::NonPremult> {}; +// - + +template +using WebIDLEnumQueueSerializer = + ContiguousEnumSerializerInclusive::min, + ContiguousEnumValues::max>; + +template <> +struct QueueParamTraits + : public WebIDLEnumQueueSerializer {}; +template <> +struct QueueParamTraits + : public WebIDLEnumQueueSerializer {}; + } // namespace webgl } // namespace mozilla diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index f5f78e98cb9e..2f1d9331a750 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -362,8 +362,7 @@ struct WebGLContextOptions final { dom::WebGLPowerPreference powerPreference = dom::WebGLPowerPreference::Default; - bool ignoreColorSpace = true; - dom::PredefinedColorSpace colorSpace = dom::PredefinedColorSpace::Srgb; + std::optional colorSpace; bool shouldResistFingerprinting = true; bool enableDebugRendererInfo = false; @@ -383,7 +382,6 @@ struct WebGLContextOptions final { powerPreference, colorSpace, - ignoreColorSpace, shouldResistFingerprinting, enableDebugRendererInfo); diff --git a/dom/canvas/test/reftest/colors/_generated_reftest.list b/dom/canvas/test/reftest/colors/_generated_reftest.list index 29761e3df8df..df5fa9884737 100644 --- a/dom/canvas/test/reftest/colors/_generated_reftest.list +++ b/dom/canvas/test/reftest/colors/_generated_reftest.list @@ -17,160 +17,217 @@ defaults pref(webgl.colorspaces.prototype,true) ### Generated, do not edit. ### # - -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) -skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) +skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) ### Generated, do not edit. ### -skip-if(cocoaWidget) fails-if(!Android) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) -skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) +skip-if(cocoaWidget) fails-if(!Android) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) +skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) ### Generated, do not edit. ### -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) +skip-if(!cocoaWidget) fuzzy(0-1,0-10000) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) ### Generated, do not edit. ### -skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) -skip-if(cocoaWidget) fails-if(!Android) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(cocoaWidget) fuzzy(0-1,0-10000) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) +skip-if(cocoaWidget) fuzzy(0-1,0-10000) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(cocoaWidget) fails-if(!Android) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) ### Generated, do not edit. ### -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) -skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) +skip-if(cocoaWidget) fails == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) ### Generated, do not edit. ### -skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) -skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(!cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) +skip-if(cocoaWidget) == color_canvas.html?e_context=webgl&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) +skip-if(cocoaWidget||winWidget) fails == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(cocoaWidget||winWidget) fails-if(!Android) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) +skip-if(cocoaWidget||winWidget) fails == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) +skip-if(!cocoaWidget&&!winWidget) fuzzy(0-1,0-10000) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) +skip-if(cocoaWidget||winWidget) fuzzy(0-1,0-10000) fails == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) +skip-if(cocoaWidget||winWidget) fuzzy(0-1,0-10000) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(cocoaWidget||winWidget) fails-if(!Android) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) +skip-if(cocoaWidget||winWidget) fails == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) +skip-if(!cocoaWidget&&!winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) +skip-if(cocoaWidget||winWidget) == color_canvas.html?e_context=webgl2&e_webgl_format=RGBA8&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=srgb&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) ### Generated, do not edit. ### - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) - == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"true"}&e_cspace=display-p3&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=srgb&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.200,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.200,0.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.502,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.502,0.000) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(srgb,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(srgb,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(srgb,1.000,1.000,1.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.200,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.200,0.200) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.200,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.200,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.200,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.200,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.200) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.200) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.502,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.502,0.502) + ### Generated, do not edit. ### + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.502,0.000,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.502,0.000,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.502,0.000) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.502,0.000) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,0.000,0.000,0.502) color_canvas.html?e_context=css&e_color=color(display-p3,0.000,0.000,0.502) + == color_canvas.html?e_context=2d&e_options={"willReadFrequently":"false"}&e_cspace=display-p3&e_color=color(display-p3,1.000,1.000,1.000) color_canvas.html?e_context=css&e_color=color(display-p3,1.000,1.000,1.000) diff --git a/dom/canvas/test/reftest/colors/color_canvas.html b/dom/canvas/test/reftest/colors/color_canvas.html index 7abbc8625528..53314f2714bc 100644 --- a/dom/canvas/test/reftest/colors/color_canvas.html +++ b/dom/canvas/test/reftest/colors/color_canvas.html @@ -29,6 +29,7 @@ +
Options: diff --git a/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py b/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py index 8c1e5f378844..ad04d9a524e4 100644 --- a/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py +++ b/dom/canvas/test/reftest/colors/generate_color_canvas_reftests.py @@ -120,9 +120,8 @@ WEBGL_FORMATS = keyed_alternatives( #'RGBA16F', ], ) -WEBGL = cross_combine( - [{"e_context": "webgl"}], WEBGL_FORMATS, CANVAS_CSPACES, WEBGL_COLORS -) +WEBGL_VERSIONS = keyed_alternatives("e_context", ["webgl", "webgl2"]) +WEBGL = cross_combine(WEBGL_VERSIONS, WEBGL_FORMATS, CANVAS_CSPACES, WEBGL_COLORS) # - @@ -212,7 +211,7 @@ def correct_color_from_test_config(test_config: Config) -> CssColor: canvas_cspace = "srgb" correct_color = parse_css_color(test_config["e_color"]) - if test_config["e_context"] == "webgl": + if test_config["e_context"].startswith("webgl"): # Webgl ignores the color's cspace, because webgl has no concept of # source colorspace for clears/draws to the backbuffer. # This (correct) behavior is as if the color's cspace were overwritten by the @@ -231,12 +230,12 @@ def correct_color_from_test_config(test_config: Config) -> CssColor: def reftests_from_config(test_config: Config) -> Iterable[ColorReftest]: correct_color = correct_color_from_test_config(test_config) - if test_config["e_context"] == "2d": + if test_config["e_context"] in ["2d"]: # Canvas2d generally has the same behavior as css, so expect all passing. yield ColorReftest([], test_config, correct_color) return - assert test_config["e_context"] == "webgl", test_config["e_context"] + assert test_config["e_context"].startswith("webgl"), test_config["e_context"] # - @@ -249,6 +248,11 @@ def reftests_from_config(test_config: Config) -> Iterable[ColorReftest]: # If we fix an error, we'll see one unexpected-pass and one unexpected-fail. # If we get a new wrong answer, we'll see one unexpected-fail. + if correct_color.is_same_color( + parse_css_color("color(display-p3 0.502 0.000 0.000)") + ): + notes += ["fuzzy(0-1,0-10000)"] + if not expected_color.is_same_color(correct_color): yield ColorReftest(notes + ["fails"], test_config, correct_color) yield ColorReftest(notes, test_config, expected_color) @@ -261,12 +265,26 @@ def reftests_from_config(test_config: Config) -> Iterable[ColorReftest]: # right now. This is the same as "srgb". expected_color_srgb = CssColor("srgb", correct_color.rgb) - # Mac - yield from reftests_from_expected_color(["skip-if(!cocoaWidget)"], correct_color) - # Win, Lin, Android - yield from reftests_from_expected_color( - ["skip-if(cocoaWidget) "], expected_color_srgb - ) + if test_config["e_context"] == "webgl2": + # Win, Mac + yield from reftests_from_expected_color( + ["skip-if(!cocoaWidget&&!winWidget)"], correct_color + ) + # Lin, Android + yield from reftests_from_expected_color( + ["skip-if(cocoaWidget||winWidget) "], expected_color_srgb + ) + elif test_config["e_context"] == "webgl": + # Mac + yield from reftests_from_expected_color( + ["skip-if(!cocoaWidget)"], correct_color + ) + # Win, Lin, Android + yield from reftests_from_expected_color( + ["skip-if(cocoaWidget) "], expected_color_srgb + ) + else: + assert False, test_config["e_context"] # - @@ -279,7 +297,7 @@ def amended_notes_from_reftest(reftest: ColorReftest) -> list[str]: is_green_only = ref_rgb_vals == (0, ref_rgb_vals[1], 0) if ( "fails" in reftest.notes - and reftest.test_config["e_context"] == "webgl" + and reftest.test_config["e_context"].startswith("webgl") and reftest.test_config["e_cspace"] == "display-p3" and is_green_only ): diff --git a/dom/webidl/WebGLRenderingContext.webidl b/dom/webidl/WebGLRenderingContext.webidl index 00585a06bf66..15cf4ee7a868 100644 --- a/dom/webidl/WebGLRenderingContext.webidl +++ b/dom/webidl/WebGLRenderingContext.webidl @@ -546,6 +546,11 @@ interface mixin WebGLRenderingContextBase { readonly attribute GLsizei drawingBufferWidth; readonly attribute GLsizei drawingBufferHeight; + /* Upon context creation, drawingBufferColorSpace and unpackColorSpace both + default to the value "srgb". */ + attribute PredefinedColorSpace drawingBufferColorSpace; + //attribute PredefinedColorSpace unpackColorSpace; + [WebGLHandlesContextLoss] WebGLContextAttributes? getContextAttributes(); [WebGLHandlesContextLoss] boolean isContextLost(); diff --git a/gfx/gl/AutoMappable.h b/gfx/gl/AutoMappable.h index f93b2ccb5738..700e45d680b6 100644 --- a/gfx/gl/AutoMappable.h +++ b/gfx/gl/AutoMappable.h @@ -8,38 +8,13 @@ // Here be dragons. #include +#include -namespace mozilla::gfx { +namespace mozilla { template -size_t Hash(const T&); - -template -struct StaticStdHasher { - static auto HashImpl(const T& v) { return std::hash()(v); } -}; - -template -struct StaticHasher { - static auto HashImpl(const T& v) { return v.hash(); } -}; -template -struct StaticHasher> { - static size_t HashImpl(const std::optional& v) { - if (!v) return 0; - return Hash(*v); - } -}; -template <> -struct StaticHasher : public StaticStdHasher {}; -template <> -struct StaticHasher : public StaticStdHasher {}; -template <> -struct StaticHasher : public StaticStdHasher {}; - -template -size_t Hash(const T& v) { - return StaticHasher::HashImpl(v); +size_t StdHash(const T& t) { + return std::hash()(t); } //- @@ -52,97 +27,59 @@ inline size_t HashCombine(size_t seed, const size_t hash) { } // - -// See -// https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17 +namespace detail { template -size_t HashTupleN(const std::tuple& tup, - const std::index_sequence&) { +size_t StdHashTupleN(const std::tuple& tup, + const std::index_sequence&) { size_t seed = 0; - for (const auto& hash : {Hash(std::get(tup))...}) { + for (const auto& hash : {StdHash(std::get(tup))...}) { seed = HashCombine(seed, hash); } return seed; } +} // namespace detail + +// - + +template +struct StdHashMembers { + size_t operator()(const T& t) const { + const auto members = t.Members(); + return StdHash(members); + } +}; + +// - + +#define MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(OP, T) \ + bool operator OP(const T& rhs) const { return Members() OP rhs.Members(); } + +#define MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(==, T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(!=, T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(<, T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(<=, T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(>, T) \ + MOZ_MIXIN_DERIVE_CMP_OP_BY_MEMBERS(>=, T) + +template +struct DeriveCmpOpMembers { + private: + auto Members() const { return reinterpret_cast(this)->Members(); } + + public: + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(T) +}; + +} // namespace mozilla template -size_t HashTuple(const std::tuple& tup) { - return HashTupleN(tup, std::make_index_sequence()); -} - -// - - -template -auto MembersEq(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersLt(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersHash(const T& a) { - const auto atup = a.Members(); - return HashTuple(atup); -} - -template -struct MembersHasher final { - auto operator()(const T& v) const { return v.hash(); } +struct std::hash> { + size_t operator()(const std::tuple& t) const { + return mozilla::detail::StdHashTupleN( + t, std::make_index_sequence()); + } }; -/** E.g.: -struct Foo { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } - INLINE_AUTO_MAPPABLE(Foo) -}; -std::unordered_set easy; -**/ -#define INLINE_DERIVE_MEMBERS_EQ(T) \ - friend bool operator==(const T& a, const T& b) { \ - return mozilla::gfx::MembersEq(a, b); \ - } \ - friend bool operator!=(const T& a, const T& b) { return !operator==(a, b); } -#define INLINE_AUTO_MAPPABLE(T) \ - friend bool operator<(const T& a, const T& b) { \ - return mozilla::gfx::MembersLt(a, b); \ - } \ - INLINE_DERIVE_MEMBERS_EQ(T) \ - size_t hash() const { \ - return mozilla::gfx::MembersHash(*reinterpret_cast(this)); \ - } \ - using Hasher = mozilla::gfx::MembersHasher; - -// - - -/** E.g.: -``` -struct Foo : public AutoMappable { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } -}; -std::unordered_set easy; -``` -`easy.insert({{}, 2, true});` -The initial {} is needed for aggregate initialization of AutoMappable. -Use INLINE_AUTO_MAPPABLE if this is too annoying. -**/ -template -struct AutoMappable { - INLINE_AUTO_MAPPABLE(T) -}; - -} // namespace mozilla::gfx - #endif // MOZILLA_AUTO_MAPPABLE_H diff --git a/gfx/gl/Colorspaces.h b/gfx/gl/Colorspaces.h index 8f36854d2d4f..062897d2dd0e 100644 --- a/gfx/gl/Colorspaces.h +++ b/gfx/gl/Colorspaces.h @@ -42,19 +42,27 @@ typedef struct _qcms_profile qcms_profile; namespace mozilla::color { +// - + struct YuvLumaCoeffs final { float r = 0.2126; float g = 0.7152; float b = 0.0722; auto Members() const { return std::tie(r, g, b); } - INLINE_AUTO_MAPPABLE(YuvLumaCoeffs) + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(YuvLumaCoeffs) static constexpr auto Rec709() { return YuvLumaCoeffs(); } static constexpr auto Rec2020() { - return YuvLumaCoeffs{0.2627, 0.6780, 0.0593}; + return YuvLumaCoeffs{ + .r = 0.2627, + .g = 0.6780, + .b = 0.0593, + }; } + + static constexpr auto Gbr() { return YuvLumaCoeffs{.r = 0, .g = 1, .b = 0}; } }; struct PiecewiseGammaDesc final { @@ -68,26 +76,26 @@ struct PiecewiseGammaDesc final { float k = 12.92; auto Members() const { return std::tie(a, b, g, k); } - INLINE_AUTO_MAPPABLE(PiecewiseGammaDesc) + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(PiecewiseGammaDesc) static constexpr auto Srgb() { return PiecewiseGammaDesc(); } static constexpr auto DisplayP3() { return Srgb(); } static constexpr auto Rec709() { return PiecewiseGammaDesc{ - 1.099, - 0.018, - 1.0 / 0.45, // ~2.222 - 4.5, + .a = 1.099, + .b = 0.018, + .g = 1.0 / 0.45, // ~2.222 + .k = 4.5, }; } // FYI: static constexpr auto Rec2020_10bit() { return Rec709(); } static constexpr auto Rec2020_12bit() { return PiecewiseGammaDesc{ - 1.0993, - 0.0181, - 1.0 / 0.45, // ~2.222 - 4.5, + .a = 1.0993, + .b = 0.0181, + .g = 1.0 / 0.45, // ~2.222 + .k = 4.5, }; } }; @@ -99,21 +107,21 @@ struct YcbcrDesc final { float uPlusHalf = 240 / 255.0; auto Members() const { return std::tie(y0, y1, u0, uPlusHalf); } - INLINE_AUTO_MAPPABLE(YcbcrDesc) + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(YcbcrDesc) static constexpr auto Narrow8() { // AKA limited/studio/tv return YcbcrDesc(); } static constexpr auto Full8() { // AKA pc return YcbcrDesc{ - 0 / 255.0, - 255 / 255.0, - 128 / 255.0, - 254 / 255.0, + .y0 = 0 / 255.0, + .y1 = 255 / 255.0, + .u0 = 128 / 255.0, + .uPlusHalf = 254 / 255.0, }; } static constexpr auto Float() { // Best for a LUT - return YcbcrDesc{0.0, 1.0, 0.5, 1.0}; + return YcbcrDesc{.y0 = 0.0, .y1 = 1.0, .u0 = 0.5, .uPlusHalf = 1.0}; } }; @@ -129,7 +137,7 @@ struct Chromaticities final { static constexpr float wy = 0.3290; auto Members() const { return std::tie(rx, ry, gx, gy, bx, by); } - INLINE_AUTO_MAPPABLE(Chromaticities) + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(Chromaticities) // - @@ -145,23 +153,32 @@ struct Chromaticities final { } static constexpr auto Rec601_525_Ntsc() { return Chromaticities{ - 0.630, 0.340, // r - 0.310, 0.595, // g - 0.155, 0.070, // b + .rx = 0.630, + .ry = 0.340, // r + .gx = 0.310, + .gy = 0.595, // g + .bx = 0.155, + .by = 0.070, // b }; } static constexpr auto Rec2020() { return Chromaticities{ - 0.708, 0.292, // r - 0.170, 0.797, // g - 0.131, 0.046, // b + .rx = 0.708, + .ry = 0.292, // r + .gx = 0.170, + .gy = 0.797, // g + .bx = 0.131, + .by = 0.046, // b }; } static constexpr auto DisplayP3() { return Chromaticities{ - 0.680, 0.320, // r - 0.265, 0.690, // g - 0.150, 0.060, // b + .rx = 0.680, + .ry = 0.320, // r + .gx = 0.265, + .gy = 0.690, // g + .bx = 0.150, + .by = 0.060, // b }; } }; @@ -173,7 +190,7 @@ struct YuvDesc final { YcbcrDesc ycbcr; auto Members() const { return std::tie(yCoeffs, ycbcr); } - INLINE_AUTO_MAPPABLE(YuvDesc); + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(YuvDesc) }; struct ColorspaceDesc final { @@ -182,11 +199,30 @@ struct ColorspaceDesc final { std::optional yuv; auto Members() const { return std::tie(chrom, tf, yuv); } - INLINE_AUTO_MAPPABLE(ColorspaceDesc); + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(ColorspaceDesc) }; // - +} // namespace mozilla::color + +#define _(X) \ + template <> \ + struct std::hash : mozilla::StdHashMembers {}; + +_(mozilla::color::YuvLumaCoeffs) +_(mozilla::color::PiecewiseGammaDesc) +_(mozilla::color::YcbcrDesc) +_(mozilla::color::Chromaticities) +_(mozilla::color::YuvDesc) +_(mozilla::color::ColorspaceDesc) + +#undef _ + +namespace mozilla::color { + +// - + template struct avec final { using T = TT; @@ -335,6 +371,11 @@ auto max(const avec& a, const avec& b) { return ret; } +template +auto clamp(const avec& v, const avec& lo, const avec& hi) { + return max(lo, min(v, hi)); +} + template auto floor(const avec& a) { auto ret = avec{}; @@ -404,6 +445,11 @@ struct mat final { } } + constexpr bool operator==(const mat& rhs) const { + return this->rows == rhs.rows; + } + constexpr bool operator!=(const mat& rhs) const { return !(*this == rhs); } + const auto& at(const int x, const int y) const { return rows.at(y)[x]; } auto& at(const int x, const int y) { return rows.at(y)[x]; } @@ -722,17 +768,19 @@ inline float SampleOutByIn(const C& outByIn, const float in) { return outByIn.at(0); } MOZ_ASSERT(outByIn.size() >= 2); - const auto begin = outByIn.begin(); - const auto in0i = size_t(floorf(in * (outByIn.size() - 1))); - const auto out0_itr = begin + std::min(in0i, outByIn.size() - 2); + // Estimate based on nearest (first) derivative: + // Find the nearest point to `in` in `outByIn`. + const auto inId = in * (outByIn.size() - 1); + const auto inId0F = std::clamp(floorf(inId), 0.f, float(outByIn.size() - 2)); + const auto inId0 = size_t(inId0F); + const auto out0 = outByIn.at(inId0 + 0); + const auto out1 = outByIn.at(inId0 + 1); + const auto d_inId0 = float(1); + const auto d_out0 = out1 - out0; + const auto d_inId = inId - inId0; - const auto in0 = float(out0_itr - begin) / (outByIn.size() - 1); - const auto out0 = *out0_itr; - const auto d_in = float(1) / (outByIn.size() - 1); - const auto d_out = *(out0_itr + 1) - *out0_itr; - - const auto out = out0 + (d_out / d_in) * (in - in0); + const auto out = out0 + (d_out0 / d_inId0) * d_inId; // printf("SampleOutByIn(%f)->%f\n", in, out); return out; } @@ -963,7 +1011,7 @@ struct ColorProfileConversionDesc { }; static ColorProfileConversionDesc From(const FromDesc&); - vec3 Apply(const vec3 src) const { + vec3 DstFromSrc(const vec3 src) const { const auto srcRgb = vec3(srcRgbFromSrcYuv * vec4(src, 1)); const auto srcLinear = vec3{{ SampleOutByIn(srcLinearFromSrcTf.r, srcRgb.x()), diff --git a/gfx/gl/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp index c5c8995dab7e..fdecbfda9927 100644 --- a/gfx/gl/GLBlitHelper.cpp +++ b/gfx/gl/GLBlitHelper.cpp @@ -19,6 +19,7 @@ #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/UniquePtr.h" +#include "mozilla/gfx/BuildConstants.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/ImageDataSerializer.h" @@ -189,16 +190,16 @@ const char* const kFragConvert_ColorMatrix = R"( return (uColorMatrix * vec4(src, 1)).rgb; } )"; -const char* const kFragConvert_ColorLut = R"( +const char* const kFragConvert_ColorLut3d = R"( uniform PRECISION sampler3D uColorLut; vec3 metaConvert(vec3 src) { // Half-texel filtering hazard! // E.g. For texture size of 2, - // E.g. 0.5/2=0.25 is still sampling 100% of texel 0, 0% of texel 1. - // For the LUT, we need 0.5/2=0.25 to filter 25/75 texel 0 and 1. - // That is, we need to adjust our sampling point such that it's 0.25 of the - // way from texel 0's center to texel 1's center. + // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1. + // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1. + // That is, we need to adjust our sampling point such that it starts in the + // center of texel 0, and ends in the center of texel N-1. // We need, for N=2: // v=0.0|N=2 => v'=0.5/2 // v=1.0|N=2 => v'=1.5/2 @@ -211,6 +212,44 @@ const char* const kFragConvert_ColorLut = R"( return texture(uColorLut, src).rgb; } )"; +// Delete if unused after 2024-10-01: +const char* const kFragConvert_ColorLut2d = R"( + uniform PRECISION sampler2D uColorLut; + uniform mediump vec3 uColorLut3dSize; + + vec3 metaConvert(vec3 src) { + // Half-texel filtering hazard! + // E.g. For texture size of 2, + // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1. + // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1. + // That is, we need to adjust our sampling point such that it starts in the + // center of texel 0, and ends in the center of texel N-1. + // We need, for N=2: + // v=0.0|N=2 => v'=0.5/2 + // v=1.0|N=2 => v'=1.5/2 + // For N=3: + // v=0.0|N=3 => v'=0.5/3 + // v=1.0|N=3 => v'=2.5/3 + // => v' = ( 0.5 + v * (3 - 1) )/3 + src = clamp(src, vec3(0,0,0), vec3(1,1,1)); + vec3 lut3dSize = uColorLut3dSize; + vec2 lut2dSize = vec2(lut3dSize.x, lut3dSize.y * lut3dSize.z); + vec3 texelSrc3d = 0.5 + src * (lut3dSize - 1.0); + + vec3 texelSrc3d_zFloor = texelSrc3d; + texelSrc3d_zFloor.z = floor(texelSrc3d_zFloor.z); + vec3 texelSrc3d_zNext = texelSrc3d_zFloor + vec3(0,0,1); + texelSrc3d_zNext.z = min(texelSrc3d_zNext.z, lut3dSize.z - 1.0); + + vec2 texelSrc2d_zFloor = texelSrc3d_zFloor.xy + vec2(0, texelSrc3d_zFloor.z * lut3dSize.y); + vec2 texelSrc2d_zNext = texelSrc3d_zNext.xy + vec2(0, texelSrc3d_zNext.z * lut3dSize.y); + + vec4 dst_zFloor = texture(uColorLut, texelSrc2d_zFloor / lut2dSize); + vec4 dst_zNext = texture(uColorLut, texelSrc2d_zNext / lut2dSize); + + return mix(dst_zFloor, dst_zNext, texelSrc3d.z - texelSrc3d_zFloor.z); + } +)"; // - @@ -277,13 +316,13 @@ Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize, // -- ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, - const std::vector& texUnits, + const size_t texUnits, const GLenum texTarget) : mGL(*gl), mTexUnits(texUnits), mTexTarget(texTarget), mOldTexUnit(mGL.GetIntAs(LOCAL_GL_ACTIVE_TEXTURE)) { - MOZ_RELEASE_ASSERT(texUnits.size() >= 1); + MOZ_RELEASE_ASSERT(texUnits >= 1); GLenum texBinding; switch (mTexTarget) { @@ -304,12 +343,11 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, MOZ_CRASH(); } - for (const auto i : IntegerRange(mTexUnits.size())) { - const auto& unit = mTexUnits[i]; - mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + unit); + for (const auto i : IntegerRange(mTexUnits)) { + mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); if (mGL.IsSupported(GLFeature::sampler_objects)) { mOldTexSampler[i] = mGL.GetIntAs(LOCAL_GL_SAMPLER_BINDING); - mGL.fBindSampler(unit, 0); + mGL.fBindSampler(i, 0); } mOldTex[i] = mGL.GetIntAs(texBinding); } @@ -319,11 +357,10 @@ ScopedSaveMultiTex::~ScopedSaveMultiTex() { // Unbind in reverse order, in case we have repeats. // Order matters because we unbound samplers during ctor, so now we have to // make sure we rebind them in the right order. - for (const auto i : Reversed(IntegerRange(mTexUnits.size()))) { - const auto& unit = mTexUnits[i]; - mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + unit); + for (const auto i : Reversed(IntegerRange(mTexUnits))) { + mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); if (mGL.IsSupported(GLFeature::sampler_objects)) { - mGL.fBindSampler(unit, mOldTexSampler[i]); + mGL.fBindSampler(i, mOldTexSampler[i]); } mGL.fBindTexture(mTexTarget, mOldTex[i]); } @@ -520,11 +557,6 @@ void DrawBlitProg::Draw(const BaseArgs& args, gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m); gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m); - if (args.texUnitForColorLut) { - gl->fUniform1i(mLoc_uColorLut, - AssertedCast(*args.texUnitForColorLut)); - } - MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1)); if (argsYUV) { gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m); @@ -684,10 +716,6 @@ GLBlitHelper::GLBlitHelper(GLContext* const gl) } GLBlitHelper::~GLBlitHelper() { - for (const auto& pair : mDrawBlitProgs) { - const auto& ptr = pair.second; - delete ptr; - } mDrawBlitProgs.clear(); if (!mGL->MakeCurrent()) return; @@ -702,18 +730,16 @@ GLBlitHelper::~GLBlitHelper() { // -- -const DrawBlitProg* GLBlitHelper::GetDrawBlitProg( +const DrawBlitProg& GLBlitHelper::GetDrawBlitProg( const DrawBlitProg::Key& key) const { - const auto& res = mDrawBlitProgs.insert({key, nullptr}); - auto& pair = *(res.first); - const auto& didInsert = res.second; - if (didInsert) { - pair.second = CreateDrawBlitProg(pair.first); + auto& ret = mDrawBlitProgs[key]; + if (!ret) { + ret = CreateDrawBlitProg(key); } - return pair.second; + return *ret; } -const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg( +std::unique_ptr GLBlitHelper::CreateDrawBlitProg( const DrawBlitProg::Key& key) const { const auto precisionPref = StaticPrefs::gfx_blithelper_precision(); const char* precision; @@ -790,7 +816,7 @@ const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg( mGL->fUniform1i(loc, i); } - return new DrawBlitProg(this, prog); + return std::make_unique(this, prog); } GLuint progLogLen = 0; @@ -997,7 +1023,7 @@ bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture, {kFragHeader_TexExt, {kFragSample_OnePlane, kFragConvert_None}}); const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, destSize, Nothing()}; - prog->Draw(baseArgs, nullptr); + prog.Draw(baseArgs, nullptr); if (surfaceTexture->IsSingleBuffer()) { surfaceTexture->ReleaseTexImage(); @@ -1087,7 +1113,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, // -- - const ScopedSaveMultiTex saveTex(mGL, {0, 1, 2}, LOCAL_GL_TEXTURE_2D); + const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D); const ResetUnpackState reset(mGL); const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height); const gfx::IntSize uvTexSize(yuvData.mCbCrStride, @@ -1139,7 +1165,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, yFlip, destSize, Nothing()}; const DrawBlitProg::YUVArgs yuvArgs = { SubRectMat3(clipRect, uvTexSize, divisors), Some(yuvData.mYUVColorSpace)}; - prog->Draw(baseArgs, &yuvArgs); + prog.Draw(baseArgs, &yuvArgs); return true; } @@ -1197,11 +1223,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE; - std::vector texUnits; - for (uint8_t i = 0; i < planes; i++) { - texUnits.push_back(i); - } - const ScopedSaveMultiTex saveTex(mGL, texUnits, texTarget); + const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); const ScopedTexture tex0(mGL); const ScopedTexture tex1(mGL); const ScopedTexture tex2(mGL); @@ -1282,7 +1304,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, kFragHeader_Tex2DRect, {fragSample, kFragConvert_ColorMatrix}, }); - prog->Draw(baseArgs, pYuvArgs); + prog.Draw(baseArgs, pYuvArgs); return true; } #endif @@ -1315,14 +1337,14 @@ void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex, {kFragSample_OnePlane, fragConvert}, }); - const ScopedSaveMultiTex saveTex(mGL, {0}, srcTarget); + const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget); mGL->fActiveTexture(LOCAL_GL_TEXTURE0); mGL->fBindTexture(srcTarget, srcTex); const bool yFlip = false; const DrawBlitProg::BaseArgs baseArgs = {texMatrix0, yFlip, destSize, Nothing()}; - prog->Draw(baseArgs); + prog.Draw(baseArgs); } // ----------------------------------------------------------------------------- @@ -1519,11 +1541,7 @@ bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntSize& destSize, const GLenum texTarget = LOCAL_GL_TEXTURE_2D; - std::vector texUnits; - for (uint8_t i = 0; i < planes; i++) { - texUnits.push_back(i); - } - const ScopedSaveMultiTex saveTex(mGL, texUnits, texTarget); + const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); const auto pixelFormat = surface->GetSurfaceType(); const char* fragSample; @@ -1560,7 +1578,7 @@ bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntSize& destSize, const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, {fragSample, fragConvert}}); - prog->Draw(baseArgs, pYuvArgs); + prog.Draw(baseArgs, pYuvArgs); return true; } @@ -1603,23 +1621,205 @@ static uint32_t toRgb10A2(const color::vec4& val) { return ret; } -std::shared_ptr GLBlitHelper::GetColorLutTex( - const ColorLutKey& key) const { - auto& weak = mColorLutTexMap[key]; - auto strong = weak.lock(); - if (!strong) { - auto& gl = *mGL; - strong = std::make_shared(gl); - weak = strong; +// - - const auto ct = color::ColorspaceTransform::Create(key.src, key.dst); +color::ColorspaceDesc ToColorspaceDesc(const gfx::YUVRangedColorSpace cs) { + switch (cs) { + case gfx::YUVRangedColorSpace::BT601_Narrow: + return { + .chrom = color::Chromaticities::Rec601_525_Ntsc(), + .tf = color::PiecewiseGammaDesc::Rec709(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec709(), + .ycbcr = color::YcbcrDesc::Narrow8(), + }, + }; + case gfx::YUVRangedColorSpace::BT601_Full: + return { + .chrom = color::Chromaticities::Rec601_525_Ntsc(), + .tf = color::PiecewiseGammaDesc::Rec709(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec709(), + .ycbcr = color::YcbcrDesc::Full8(), + }, + }; + case gfx::YUVRangedColorSpace::BT709_Narrow: + return { + .chrom = color::Chromaticities::Rec709(), + .tf = color::PiecewiseGammaDesc::Rec709(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec709(), + .ycbcr = color::YcbcrDesc::Narrow8(), + }, + }; + case gfx::YUVRangedColorSpace::BT709_Full: + return { + .chrom = color::Chromaticities::Rec709(), + .tf = color::PiecewiseGammaDesc::Rec709(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec709(), + .ycbcr = color::YcbcrDesc::Full8(), + }, + }; + case gfx::YUVRangedColorSpace::BT2020_Narrow: + return { + .chrom = color::Chromaticities::Rec2020(), + .tf = color::PiecewiseGammaDesc::Rec2020_12bit(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec709(), + .ycbcr = color::YcbcrDesc::Narrow8(), + }, + }; + case gfx::YUVRangedColorSpace::BT2020_Full: + return { + .chrom = color::Chromaticities::Rec2020(), + .tf = color::PiecewiseGammaDesc::Rec2020_12bit(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Rec2020(), + .ycbcr = color::YcbcrDesc::Full8(), + }, + }; + case gfx::YUVRangedColorSpace::GbrIdentity: + return { + .chrom = color::Chromaticities::Rec709(), + .tf = color::PiecewiseGammaDesc::Rec709(), + .yuv = + color::YuvDesc{ + .yCoeffs = color::YuvLumaCoeffs::Gbr(), + .ycbcr = color::YcbcrDesc::Full8(), + }, + }; + } + MOZ_CRASH("Bad YUVRangedColorSpace."); +} + +static std::optional ToColorProfileDesc( + gfx::ColorSpace2); + +} // namespace gl +namespace gfx { + +color::ColorProfileDesc QueryOutputColorProfile(); + +} // namespace gfx +namespace gl { + +// - + +static std::optional ToColorProfileDesc( + const gfx::ColorSpace2 cspace) { + color::ColorspaceDesc cspaceDesc; + switch (cspace) { + case gfx::ColorSpace2::Display: + if (kIsWindows) { +#ifdef XP_WIN + return gfx::QueryOutputColorProfile(); +#endif + } + return {}; + + case gfx::ColorSpace2::SRGB: + cspaceDesc = {.chrom = color::Chromaticities::Srgb(), + .tf = color::PiecewiseGammaDesc::Srgb()}; + break; + case gfx::ColorSpace2::DISPLAY_P3: + cspaceDesc = {.chrom = color::Chromaticities::DisplayP3(), + .tf = color::PiecewiseGammaDesc::DisplayP3()}; + break; + case gfx::ColorSpace2::BT601_525: // aka smpte170m NTSC + cspaceDesc = {.chrom = color::Chromaticities::Rec601_525_Ntsc(), + .tf = color::PiecewiseGammaDesc::Rec709()}; + break; + case gfx::ColorSpace2::BT709: // Same gamut as SRGB, but different gamma. + cspaceDesc = {.chrom = color::Chromaticities::Rec709(), + .tf = color::PiecewiseGammaDesc::Rec709()}; + break; + case gfx::ColorSpace2::BT2020: + cspaceDesc = {.chrom = color::Chromaticities::Rec2020(), + .tf = color::PiecewiseGammaDesc::Rec2020_12bit()}; + break; + } + const auto profileDesc = color::ColorProfileDesc::From(cspaceDesc); + return profileDesc; +} + +// - + +// For std::visit +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; + +// - + +template +inline auto MaybeFind(C& container, const K& key) + -> decltype(&(container.find(key)->second)) { + const auto itr = container.find(key); + if (itr == container.end()) return nullptr; + return &(itr->second); +} + +// - + +std::shared_ptr GLBlitHelper::GetColorLutTex( + const ColorLutKey& request) const { + if (const auto found = MaybeFind(mColorLutTexMap, request)) { + return *found; // Might be *Some(nullptr) -> nullptr! + } + + return mColorLutTexMap[request] = [&]() -> std::shared_ptr { + auto& gl = *mGL; + const auto tex = std::make_shared(gl); + + // - + + const std::optional srcProfile = + std::visit(overloaded{ + [&](const gfx::ColorSpace2& cs) + -> std::optional { + MOZ_ASSERT(cs != request.dst); + const auto cpd = ToColorProfileDesc(cs); + return cpd; + }, + [&](const gfx::YUVRangedColorSpace& cs) + -> std::optional { + const auto csd = ToColorspaceDesc(cs); + const auto cpd = color::ColorProfileDesc::From(csd); + return cpd; + }, + }, + request.src); + MOZ_ASSERT(srcProfile); + + const auto dstProfile = ToColorProfileDesc(request.dst); + if (kIsWindows) { + MOZ_ASSERT(dstProfile); + } + if (!srcProfile || !dstProfile) return nullptr; + const auto conversion = color::ColorProfileConversionDesc::From({ + .src = *srcProfile, + .dst = *dstProfile, + }); // - const auto minLutSize = color::ivec3{2}; const auto maxLutSize = color::ivec3{256}; auto lutSize = minLutSize; - if (ct.srcSpace.yuv) { + const bool isYcbcr = + (conversion.srcRgbFromSrcYuv != color::mat4::Identity()); + if (isYcbcr) { lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y())); lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb())); lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr())); @@ -1628,15 +1828,20 @@ std::shared_ptr GLBlitHelper::GetColorLutTex( lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g())); lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b())); } - lutSize = max(minLutSize, min(lutSize, maxLutSize)); // Clamp + lutSize = clamp(lutSize, minLutSize, maxLutSize); - const auto lut = ct.ToLut3(lutSize); + const auto lut = [&]() { + auto lut = color::Lut3::Create(lutSize); + lut.SetMap( + [&](const color::vec3& src) { return conversion.DstFromSrc(src); }); + return lut; + }(); const auto& size = lut.size; // - constexpr GLenum target = LOCAL_GL_TEXTURE_3D; - const auto bind = gl::ScopedBindTexture(&gl, strong->name, target); + const auto bind = gl::ScopedBindTexture(&gl, tex->name, target); gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); @@ -1673,8 +1878,8 @@ std::shared_ptr GLBlitHelper::GetColorLutTex( LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, uploadData.data()); } - } - return strong; + return tex; + }(); } } // namespace gl diff --git a/gfx/gl/GLBlitHelper.h b/gfx/gl/GLBlitHelper.h index 83915090979a..3f9e066c0e37 100644 --- a/gfx/gl/GLBlitHelper.h +++ b/gfx/gl/GLBlitHelper.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "Colorspaces.h" #include "GLConsts.h" #include "GLContextTypes.h" @@ -126,6 +127,8 @@ Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize, class DrawBlitProg final { const GLBlitHelper& mParent; + + public: const GLuint mProg; const GLint mLoc_uDestMatrix; const GLint mLoc_uTexMatrix0; @@ -154,7 +157,6 @@ class DrawBlitProg final { gfx::IntSize destSize; // Always needed for (at least) setting the viewport. Maybe destRect; - Maybe texUnitForColorLut; }; struct YUVArgs final { Mat3 texMatrix1; @@ -166,15 +168,14 @@ class DrawBlitProg final { class ScopedSaveMultiTex final { GLContext& mGL; - const std::vector mTexUnits; + const size_t mTexUnits; const GLenum mTexTarget; const GLuint mOldTexUnit; GLuint mOldTexSampler[3]; GLuint mOldTex[3]; public: - ScopedSaveMultiTex(GLContext* gl, const std::vector& texUnits, - GLenum texTarget); + ScopedSaveMultiTex(GLContext* gl, size_t texUnits, GLenum texTarget); ~ScopedSaveMultiTex(); }; @@ -185,7 +186,8 @@ class GLBlitHelper final { friend class GLContext; GLContext* const mGL; - mutable std::map mDrawBlitProgs; + mutable std::map> + mDrawBlitProgs; GLuint mQuadVAO = 0; GLuint mQuadVBO = 0; @@ -197,16 +199,19 @@ class GLBlitHelper final { gfx::IntSize mYuvUploads_UVSize = {0, 0}; public: - struct ColorLutKey { - color::ColorspaceDesc src; - color::ColorspaceDesc dst; + struct ColorLutKey : DeriveCmpOpMembers { + std::variant src; + gfx::ColorSpace2 dst; auto Members() const { return std::tie(src, dst); } - INLINE_AUTO_MAPPABLE(ColorLutKey) + + MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(ColorLutKey) + + struct Hasher : mozilla::StdHashMembers {}; }; private: - mutable std::unordered_map, + mutable std::unordered_map, ColorLutKey::Hasher> mColorLutTexMap; @@ -219,10 +224,11 @@ class GLBlitHelper final { ID3D11Device* GetD3D11() const; #endif - const DrawBlitProg* GetDrawBlitProg(const DrawBlitProg::Key& key) const; + const DrawBlitProg& GetDrawBlitProg(const DrawBlitProg::Key& key) const; private: - const DrawBlitProg* CreateDrawBlitProg(const DrawBlitProg::Key& key) const; + std::unique_ptr CreateDrawBlitProg( + const DrawBlitProg::Key& key) const; public: bool BlitPlanarYCbCr(const layers::PlanarYCbCrData&, @@ -326,7 +332,8 @@ extern const char* const kFragSample_ThreePlane; extern const char* const kFragConvert_None; extern const char* const kFragConvert_BGR; extern const char* const kFragConvert_ColorMatrix; -extern const char* const kFragConvert_ColorLut; +extern const char* const kFragConvert_ColorLut3d; +extern const char* const kFragConvert_ColorLut2d; extern const char* const kFragMixin_AlphaMultColors; extern const char* const kFragMixin_AlphaClampColors; diff --git a/gfx/gl/GLBlitHelperD3D.cpp b/gfx/gl/GLBlitHelperD3D.cpp index ae6362e278e8..1c101d8980af 100644 --- a/gfx/gl/GLBlitHelperD3D.cpp +++ b/gfx/gl/GLBlitHelperD3D.cpp @@ -95,16 +95,7 @@ class BindAnglePlanes final { const EGLAttrib* const* postAttribsList = nullptr) : mParent(*parent), mNumPlanes(numPlanes), - mMultiTex( - mParent.mGL, - [&]() { - std::vector ret; - for (int i = 0; i < numPlanes; i++) { - ret.push_back(i); - } - return ret; - }(), - LOCAL_GL_TEXTURE_EXTERNAL), + mMultiTex(mParent.mGL, mNumPlanes, LOCAL_GL_TEXTURE_EXTERNAL), mTempTexs{0}, mStreams{0}, mSuccess(true) { @@ -336,7 +327,7 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc, const auto& prog = GetDrawBlitProg( {kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}}); - prog->Draw(baseArgs, &yuvArgs); + prog.Draw(baseArgs, &yuvArgs); return true; } @@ -391,7 +382,7 @@ bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3], const auto& prog = GetDrawBlitProg( {kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}}); - prog->Draw(baseArgs, &yuvArgs); + prog.Draw(baseArgs, &yuvArgs); return true; } diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index eea26024ae40..4ee256b0530a 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -45,6 +45,12 @@ #include "mozilla/GenericRefCounted.h" #include "mozilla/WeakPtr.h" +template +constexpr inline std::array make_array( + ElemT&& arg1, More&&... more) { + return {std::forward(arg1), std::forward(more)...}; +} + #ifdef MOZ_WIDGET_ANDROID # include "mozilla/ProfilerLabels.h" #endif @@ -797,8 +803,15 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr { AFTER_GL_CALL; } + void InvalidateFramebuffer(GLenum target) { + constexpr auto ATTACHMENTS = make_array(GLenum{LOCAL_GL_COLOR_ATTACHMENT0}, + LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); + fInvalidateFramebuffer(target, ATTACHMENTS.size(), ATTACHMENTS.data()); + } + void fInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments) { + if (!mSymbols.fInvalidateFramebuffer) return; BeforeGLDrawCall(); BEFORE_GL_CALL; ASSERT_SYMBOL_PRESENT(fInvalidateFramebuffer); @@ -810,6 +823,7 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr { void fInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) { + if (!mSymbols.fInvalidateSubFramebuffer) return; BeforeGLDrawCall(); BEFORE_GL_CALL; ASSERT_SYMBOL_PRESENT(fInvalidateSubFramebuffer); @@ -825,6 +839,13 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr { AFTER_GL_CALL; } + void BindSamplerTexture(GLuint texUnitId, GLuint samplerHandle, + GLenum texTarget, GLuint texHandle) { + fBindSampler(texUnitId, samplerHandle); + fActiveTexture(LOCAL_GL_TEXTURE0 + texUnitId); + fBindTexture(texTarget, texHandle); + } + void fBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { BEFORE_GL_CALL; mSymbols.fBlendColor(red, green, blue, alpha); @@ -2028,7 +2049,11 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr { public: bool mElideDuplicateBindFramebuffers = false; - void fBindFramebuffer(const GLenum target, const GLuint fb) const { + // If e.g. GL_DRAW_FRAMEBUFFER isn't supported, will bind GL_FRAMEBUFFER. + void fBindFramebuffer(GLenum target, const GLuint fb) const { + if (!IsSupported(gl::GLFeature::framebuffer_blit)) { + target = LOCAL_GL_FRAMEBUFFER; + } if (mElideDuplicateBindFramebuffers) { MOZ_ASSERT(mCachedDrawFb == GetIntAs(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING)); @@ -3861,6 +3886,30 @@ class Texture final { } }; +// - + +class Sampler final { + public: + const WeakPtr weakGl; + const GLuint name; + + private: + static GLuint Create(GLContext& gl) { + GLuint ret = 0; + gl.fGenSamplers(1, &ret); + return ret; + } + + public: + explicit Sampler(GLContext& gl) : weakGl(&gl), name(Create(gl)) {} + + ~Sampler() { + const RefPtr gl = weakGl.get(); + if (!gl || !gl->MakeCurrent()) return; + gl->fDeleteSamplers(1, &name); + } +}; + /** * Helper function that creates a 2D texture aSize.width x aSize.height with * storage type specified by aFormats. Returns GL texture object id. diff --git a/gfx/gl/SharedSurface.h b/gfx/gl/SharedSurface.h index 4168de69a2bc..12aace9e9e28 100644 --- a/gfx/gl/SharedSurface.h +++ b/gfx/gl/SharedSurface.h @@ -182,8 +182,13 @@ class SurfaceFactory { const SharedSurfaceDesc&) = 0; public: + virtual bool SupportsCspaces() const { return false; } + UniquePtr CreateShared(const gfx::IntSize& size, gfx::ColorSpace2 cs) { + if (!SupportsCspaces()) { + cs = gfx::ColorSpace2::Display; + } return CreateSharedImpl({mDesc, size, cs}); } }; diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h index 938173b9823e..94804e3d0cec 100644 --- a/gfx/gl/SharedSurfaceIO.h +++ b/gfx/gl/SharedSurfaceIO.h @@ -47,6 +47,8 @@ class SurfaceFactory_IOSurface : public SurfaceFactory { explicit SurfaceFactory_IOSurface(GLContext& gl); + bool SupportsCspaces() const override { return true; } + virtual UniquePtr CreateSharedImpl( const SharedSurfaceDesc& desc) override { if (desc.size.width > mMaxDims.width || diff --git a/gfx/gl/gtest/TestColorspaces.cpp b/gfx/gl/gtest/TestColorspaces.cpp index c437e204af02..31a907115fa1 100644 --- a/gfx/gl/gtest/TestColorspaces.cpp +++ b/gfx/gl/gtest/TestColorspaces.cpp @@ -650,7 +650,7 @@ TEST(Colorspaces, ColorProfileConversionDesc_SrgbFromRec709) .dst = srgb, }); auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; + auto dst = conv.DstFromSrc(src / 255) * 255; const auto tfa = PiecewiseGammaDesc::Srgb(); const auto tfb = PiecewiseGammaDesc::Srgb(); @@ -667,7 +667,7 @@ TEST(Colorspaces, ColorProfileConversionDesc_SrgbFromRec709) .dst = rec709, }); auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; + auto dst = conv.DstFromSrc(src / 255) * 255; const auto tfa = PiecewiseGammaDesc::Rec709(); const auto tfb = PiecewiseGammaDesc::Rec709(); @@ -684,7 +684,7 @@ TEST(Colorspaces, ColorProfileConversionDesc_SrgbFromRec709) .dst = srgb, }); auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; + auto dst = conv.DstFromSrc(src / 255) * 255; const auto tfa = PiecewiseGammaDesc::Rec709(); const auto tfb = PiecewiseGammaDesc::Srgb(); @@ -696,3 +696,9 @@ TEST(Colorspaces, ColorProfileConversionDesc_SrgbFromRec709) EXPECT_LT(Stats::Diff(dst.data, vec3(expected).data), (Stats::Error{0.12})); } } + +TEST(Colorspaces, SampleOutByIn_NegativeInputs) +{ + const auto tf = MakeGamma(1.0 / 2.2, 256); + EXPECT_LT(SampleOutByIn(tf, -0.1f), 0.0f); +} diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index ccc58213efd4..d6e0f0ec4932 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2083,7 +2083,7 @@ Maybe>& gfxPlatform::GetCMSOutputProfileData() { CMSMode GfxColorManagementMode() { const auto mode = StaticPrefs::gfx_color_management_mode(); - if (mode >= 0 && mode < UnderlyingValue(CMSMode::AllCount)) { + if (mode >= 0 && mode <= UnderlyingValue(CMSMode::_ENUM_MAX)) { return CMSMode(mode); } return CMSMode::Off; diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 452e7208b424..37d6ec918713 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -82,7 +82,7 @@ enum class CMSMode : int32_t { Off = 0, // No color management All = 1, // Color manage everything TaggedOnly = 2, // Color manage tagged Images Only - AllCount = 3 + _ENUM_MAX = TaggedOnly }; enum eGfxLog { diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index 65dc9aca9aeb..c867740ba5d9 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -751,9 +751,9 @@ DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage( auto cprofileOut = mDCLayerTree->OutputColorProfile(); bool pretendSrgb = true; if (pretendSrgb) { - cprofileOut = color::ColorProfileDesc::From({ - color::Chromaticities::Srgb(), - color::PiecewiseGammaDesc::Srgb(), + cprofileOut = color::ColorProfileDesc::From(color::ColorspaceDesc{ + .chrom = color::Chromaticities::Srgb(), + .tf = color::PiecewiseGammaDesc::Srgb(), }); } const auto conversion = color::ColorProfileConversionDesc::From({ @@ -2097,7 +2097,10 @@ void DCLayerTree::DestroyEGLSurface() { // - -color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() { +} // namespace wr +namespace gfx { + +color::ColorProfileDesc QueryOutputColorProfile() { // GPU process can't simply init gfxPlatform, (and we don't need most of it) // but we do need gfxPlatform::GetCMSOutputProfile(). // So we steal what we need through the window: @@ -2151,6 +2154,9 @@ color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() { return ret; } +} // namespace gfx +namespace wr { + inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) { return D2D1_MATRIX_5X4_F{{{ m.rows[0][0], diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index d5ade5781e1e..8289eaf50d37 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -44,6 +44,10 @@ struct IDCompositionVirtualSurface; namespace mozilla { +namespace gfx { +color::ColorProfileDesc QueryOutputColorProfile(); +} + namespace gl { class GLContext; } @@ -248,8 +252,6 @@ class DCLayerTree { bool mPendingCommit; - static color::ColorProfileDesc QueryOutputColorProfile(); - mutable Maybe mOutputColorProfile; DCompOverlayTypes mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY; @@ -258,7 +260,7 @@ class DCLayerTree { public: const color::ColorProfileDesc& OutputColorProfile() const { if (!mOutputColorProfile) { - mOutputColorProfile = Some(QueryOutputColorProfile()); + mOutputColorProfile = Some(gfx::QueryOutputColorProfile()); } return *mOutputColorProfile; }