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
This commit is contained in:
Kelsey Gilbert 2024-05-06 23:36:51 +00:00
Родитель df99f6fd0f
Коммит 401c6844d9
27 изменённых файлов: 1033 добавлений и 524 удалений

Просмотреть файл

@ -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<RPROC(SetDrawingBufferColorSpace)>(mDrawingBufferColorSpace);
}
void ClientWebGLContext::GetContextAttributes(
dom::Nullable<dom::WebGLContextAttributes>& 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();
}

Просмотреть файл

@ -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<dom::WebGLContextAttributes>& retval);
private:

Просмотреть файл

@ -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(); }

Просмотреть файл

@ -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<T, sizeof...(Args)> {
return {{static_cast<T>(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<dom::PredefinedColorSpace> 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 <class T>
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<std::string> {
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<GLenum>(
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<gl::Texture>{};
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::Sampler>(*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

Просмотреть файл

@ -10,6 +10,7 @@
#include <memory>
#include <stdarg.h>
#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<layers::SurfaceDescriptor> GetFrontBuffer(WebGLFramebuffer*,
const bool webvr);
std::optional<color::ColorProfileDesc> 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<RefPtr<WebGLTexture>> mBound2DArrayTextures;
nsTArray<RefPtr<WebGLSampler>> mBoundSamplers;
mutable std::unique_ptr<gl::Sampler> mSamplerLinear;
GLuint SamplerLinear() const;
void ResolveTexturesForDraw() const;
RefPtr<WebGLProgram> mCurrentProgram;
@ -1265,6 +1277,10 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
mutable bool mDefaultFB_IsInvalid = false;
mutable UniquePtr<gl::MozFramebuffer> mResolvedDefaultFB;
mutable std::unordered_map<std::tuple<gfx::ColorSpace2, gfx::ColorSpace2>,
std::shared_ptr<gl::Texture>>
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();
// --

Просмотреть файл

@ -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 {

Просмотреть файл

@ -18,6 +18,8 @@
#include "TupleUtils.h"
#include "WebGLTypes.h"
#include <memory>
namespace mozilla {
namespace webgl {
@ -652,6 +654,32 @@ struct ParamTraits<mozilla::avec3<U>> final {
}
};
// -
template <class U>
struct ParamTraits<std::optional<U>> final {
using T = std::optional<U>;
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

Просмотреть файл

@ -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)

Просмотреть файл

@ -61,25 +61,6 @@ struct QueueParamTraits<avec3<T>> : QueueParamTraits_TiedFields<avec3<T>> {};
// ---------------------------------------------------------------------
// 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<T> : QueueParamTraits_IsEnumCase<T> {};
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<gfxAlphaType>
: public ContiguousEnumSerializerInclusive<
gfxAlphaType, gfxAlphaType::Opaque, gfxAlphaType::NonPremult> {};
// -
template <class Enum>
using WebIDLEnumQueueSerializer =
ContiguousEnumSerializerInclusive<Enum, ContiguousEnumValues<Enum>::min,
ContiguousEnumValues<Enum>::max>;
template <>
struct QueueParamTraits<dom::WebGLPowerPreference>
: public WebIDLEnumQueueSerializer<dom::WebGLPowerPreference> {};
template <>
struct QueueParamTraits<dom::PredefinedColorSpace>
: public WebIDLEnumQueueSerializer<dom::PredefinedColorSpace> {};
} // namespace webgl
} // namespace mozilla

Просмотреть файл

@ -362,8 +362,7 @@ struct WebGLContextOptions final {
dom::WebGLPowerPreference powerPreference =
dom::WebGLPowerPreference::Default;
bool ignoreColorSpace = true;
dom::PredefinedColorSpace colorSpace = dom::PredefinedColorSpace::Srgb;
std::optional<dom::PredefinedColorSpace> colorSpace;
bool shouldResistFingerprinting = true;
bool enableDebugRendererInfo = false;
@ -383,7 +382,6 @@ struct WebGLContextOptions final {
powerPreference,
colorSpace,
ignoreColorSpace,
shouldResistFingerprinting,
enableDebugRendererInfo);

Просмотреть файл

@ -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)

Просмотреть файл

@ -29,6 +29,7 @@
<option value=css selected>css</option>
<option value=2d>2d</option>
<option value=webgl>webgl</option>
<option value=webgl2>webgl2</option>
</select>
<br>
Options: <input id=e_options type=text value={}>

Просмотреть файл

@ -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
):

Просмотреть файл

@ -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();

Просмотреть файл

@ -8,38 +8,13 @@
// Here be dragons.
#include <functional>
#include <tuple>
namespace mozilla::gfx {
namespace mozilla {
template <class T>
size_t Hash(const T&);
template <class T>
struct StaticStdHasher {
static auto HashImpl(const T& v) { return std::hash<T>()(v); }
};
template <class T>
struct StaticHasher {
static auto HashImpl(const T& v) { return v.hash(); }
};
template <class T>
struct StaticHasher<std::optional<T>> {
static size_t HashImpl(const std::optional<T>& v) {
if (!v) return 0;
return Hash(*v);
}
};
template <>
struct StaticHasher<int> : public StaticStdHasher<int> {};
template <>
struct StaticHasher<bool> : public StaticStdHasher<bool> {};
template <>
struct StaticHasher<float> : public StaticStdHasher<float> {};
template <class T>
size_t Hash(const T& v) {
return StaticHasher<T>::HashImpl(v);
size_t StdHash(const T& t) {
return std::hash<T>()(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 <class... Args, size_t... Ids>
size_t HashTupleN(const std::tuple<Args...>& tup,
const std::index_sequence<Ids...>&) {
size_t StdHashTupleN(const std::tuple<Args...>& tup,
const std::index_sequence<Ids...>&) {
size_t seed = 0;
for (const auto& hash : {Hash(std::get<Ids>(tup))...}) {
for (const auto& hash : {StdHash(std::get<Ids>(tup))...}) {
seed = HashCombine(seed, hash);
}
return seed;
}
} // namespace detail
// -
template <class T>
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 <class T>
struct DeriveCmpOpMembers {
private:
auto Members() const { return reinterpret_cast<const T*>(this)->Members(); }
public:
MOZ_MIXIN_DERIVE_CMP_OPS_BY_MEMBERS(T)
};
} // namespace mozilla
template <class... Args>
size_t HashTuple(const std::tuple<Args...>& tup) {
return HashTupleN(tup, std::make_index_sequence<sizeof...(Args)>());
}
// -
template <class T>
auto MembersEq(const T& a, const T& b) {
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
}
template <class T>
auto MembersLt(const T& a, const T& b) {
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
}
template <class T>
auto MembersHash(const T& a) {
const auto atup = a.Members();
return HashTuple(atup);
}
template <class T>
struct MembersHasher final {
auto operator()(const T& v) const { return v.hash(); }
struct std::hash<std::tuple<Args...>> {
size_t operator()(const std::tuple<Args...>& t) const {
return mozilla::detail::StdHashTupleN(
t, std::make_index_sequence<sizeof...(Args)>());
}
};
/** E.g.:
struct Foo {
int i;
bool b;
auto Members() const { return std::tie(i, b); }
INLINE_AUTO_MAPPABLE(Foo)
};
std::unordered_set<T, T::Hasher> 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<const T*>(this)); \
} \
using Hasher = mozilla::gfx::MembersHasher<T>;
// -
/** E.g.:
```
struct Foo : public AutoMappable<Foo> {
int i;
bool b;
auto Members() const { return std::tie(i, b); }
};
std::unordered_set<T, T::Hasher> easy;
```
`easy.insert({{}, 2, true});`
The initial {} is needed for aggregate initialization of AutoMappable<Foo>.
Use INLINE_AUTO_MAPPABLE if this is too annoying.
**/
template <class T>
struct AutoMappable {
INLINE_AUTO_MAPPABLE(T)
};
} // namespace mozilla::gfx
#endif // MOZILLA_AUTO_MAPPABLE_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<YuvDesc> 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<X> : mozilla::StdHashMembers<X> {};
_(mozilla::color::YuvLumaCoeffs)
_(mozilla::color::PiecewiseGammaDesc)
_(mozilla::color::YcbcrDesc)
_(mozilla::color::Chromaticities)
_(mozilla::color::YuvDesc)
_(mozilla::color::ColorspaceDesc)
#undef _
namespace mozilla::color {
// -
template <class TT, int NN>
struct avec final {
using T = TT;
@ -335,6 +371,11 @@ auto max(const avec<T, N>& a, const avec<T, N>& b) {
return ret;
}
template <class T, int N>
auto clamp(const avec<T, N>& v, const avec<T, N>& lo, const avec<T, N>& hi) {
return max(lo, min(v, hi));
}
template <class T, int N>
auto floor(const avec<T, N>& a) {
auto ret = avec<T, N>{};
@ -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()),

Просмотреть файл

@ -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<uint8_t>& texUnits,
const size_t texUnits,
const GLenum texTarget)
: mGL(*gl),
mTexUnits(texUnits),
mTexTarget(texTarget),
mOldTexUnit(mGL.GetIntAs<GLenum>(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<GLuint>(LOCAL_GL_SAMPLER_BINDING);
mGL.fBindSampler(unit, 0);
mGL.fBindSampler(i, 0);
}
mOldTex[i] = mGL.GetIntAs<GLuint>(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<GLint>(*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<const DrawBlitProg> 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<DrawBlitProg>(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<uint8_t> 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<uint8_t> 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<gl::Texture> GLBlitHelper::GetColorLutTex(
const ColorLutKey& key) const {
auto& weak = mColorLutTexMap[key];
auto strong = weak.lock();
if (!strong) {
auto& gl = *mGL;
strong = std::make_shared<gl::Texture>(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<color::ColorProfileDesc> ToColorProfileDesc(
gfx::ColorSpace2);
} // namespace gl
namespace gfx {
color::ColorProfileDesc QueryOutputColorProfile();
} // namespace gfx
namespace gl {
// -
static std::optional<color::ColorProfileDesc> 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 <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
// -
template <typename C, typename K>
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<gl::Texture> 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<gl::Texture> {
auto& gl = *mGL;
const auto tex = std::make_shared<gl::Texture>(gl);
// -
const std::optional<color::ColorProfileDesc> srcProfile =
std::visit(overloaded{
[&](const gfx::ColorSpace2& cs)
-> std::optional<color::ColorProfileDesc> {
MOZ_ASSERT(cs != request.dst);
const auto cpd = ToColorProfileDesc(cs);
return cpd;
},
[&](const gfx::YUVRangedColorSpace& cs)
-> std::optional<color::ColorProfileDesc> {
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<gl::Texture> 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<gl::Texture> GLBlitHelper::GetColorLutTex(
LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV,
uploadData.data());
}
}
return strong;
return tex;
}();
}
} // namespace gl

Просмотреть файл

@ -12,6 +12,7 @@
#include <map>
#include <memory>
#include <unordered_map>
#include <variant>
#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<gfx::IntRect> destRect;
Maybe<uint32_t> texUnitForColorLut;
};
struct YUVArgs final {
Mat3 texMatrix1;
@ -166,15 +168,14 @@ class DrawBlitProg final {
class ScopedSaveMultiTex final {
GLContext& mGL;
const std::vector<uint8_t> mTexUnits;
const size_t mTexUnits;
const GLenum mTexTarget;
const GLuint mOldTexUnit;
GLuint mOldTexSampler[3];
GLuint mOldTex[3];
public:
ScopedSaveMultiTex(GLContext* gl, const std::vector<uint8_t>& 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<DrawBlitProg::Key, const DrawBlitProg*> mDrawBlitProgs;
mutable std::map<DrawBlitProg::Key, std::unique_ptr<const DrawBlitProg>>
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<ColorLutKey> {
std::variant<gfx::ColorSpace2, gfx::YUVRangedColorSpace> 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<ColorLutKey> {};
};
private:
mutable std::unordered_map<ColorLutKey, std::weak_ptr<gl::Texture>,
mutable std::unordered_map<ColorLutKey, std::shared_ptr<gl::Texture>,
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<const DrawBlitProg> 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;

Просмотреть файл

@ -95,16 +95,7 @@ class BindAnglePlanes final {
const EGLAttrib* const* postAttribsList = nullptr)
: mParent(*parent),
mNumPlanes(numPlanes),
mMultiTex(
mParent.mGL,
[&]() {
std::vector<uint8_t> 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;
}

Просмотреть файл

@ -45,6 +45,12 @@
#include "mozilla/GenericRefCounted.h"
#include "mozilla/WeakPtr.h"
template <class ElemT, class... More>
constexpr inline std::array<ElemT, 1 + sizeof...(More)> make_array(
ElemT&& arg1, More&&... more) {
return {std::forward<ElemT>(arg1), std::forward<ElemT>(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<GLuint>(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING));
@ -3861,6 +3886,30 @@ class Texture final {
}
};
// -
class Sampler final {
public:
const WeakPtr<GLContext> 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<GLContext> 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.

Просмотреть файл

@ -182,8 +182,13 @@ class SurfaceFactory {
const SharedSurfaceDesc&) = 0;
public:
virtual bool SupportsCspaces() const { return false; }
UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size,
gfx::ColorSpace2 cs) {
if (!SupportsCspaces()) {
cs = gfx::ColorSpace2::Display;
}
return CreateSharedImpl({mDesc, size, cs});
}
};

Просмотреть файл

@ -47,6 +47,8 @@ class SurfaceFactory_IOSurface : public SurfaceFactory {
explicit SurfaceFactory_IOSurface(GLContext& gl);
bool SupportsCspaces() const override { return true; }
virtual UniquePtr<SharedSurface> CreateSharedImpl(
const SharedSurfaceDesc& desc) override {
if (desc.size.width > mMaxDims.width ||

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -2083,7 +2083,7 @@ Maybe<nsTArray<uint8_t>>& 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;

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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],

Просмотреть файл

@ -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<color::ColorProfileDesc> 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;
}