From 7cf061700ce1b733a630ef9155573c3c327d3ce6 Mon Sep 17 00:00:00 2001 From: Kelsey Gilbert Date: Wed, 29 Jun 2022 21:48:53 +0000 Subject: [PATCH] Bug 1771374 - Fix lint warnings. r=gfx-reviewers,aosmond Differential Revision: https://phabricator.services.mozilla.com/D150620 --- gfx/gl/AutoMappable.h | 298 +++-- gfx/gl/Colorspaces.cpp | 471 ++++---- gfx/gl/Colorspaces.h | 1351 +++++++++++------------ gfx/gl/GLBlitHelper.cpp | 39 +- gfx/gl/GLBlitHelperD3D.cpp | 7 +- gfx/gl/gtest/TestColorspaces.cpp | 267 +++-- gfx/layers/D3D9SurfaceImage.cpp | 8 +- gfx/layers/d3d11/HelpersD3D11.h | 2 +- gfx/layers/d3d11/TextureD3D11.cpp | 6 +- gfx/webrender_bindings/DCLayerTree.cpp | 38 +- gfx/webrender_bindings/RenderD3D11TextureHost.h | 4 +- 11 files changed, 1228 insertions(+), 1263 deletions(-) rewrite gfx/gl/AutoMappable.h (61%) rewrite gfx/gl/Colorspaces.cpp (84%) rewrite gfx/gl/Colorspaces.h (70%) diff --git a/gfx/gl/AutoMappable.h b/gfx/gl/AutoMappable.h dissimilarity index 61% index ea8e7f516714..f93b2ccb5738 100644 --- a/gfx/gl/AutoMappable.h +++ b/gfx/gl/AutoMappable.h @@ -1,150 +1,148 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MOZILLA_AUTO_MAPPABLE_H -#define MOZILLA_AUTO_MAPPABLE_H - -// Here be dragons. - -#include - -namespace mozilla::gfx { - -template -size_t Hash(const T&); - -template -struct StaticStdHasher { - static auto HashImpl(const T& v) { - return std::hash()(v); - } -}; - -template -struct StaticHasher { - static auto HashImpl(const T& v) { - return v.hash(); - } -}; -template -struct StaticHasher> { - static size_t HashImpl(const std::optional& v) { - if (!v) return 0; - return Hash(*v); - } -}; -template<> struct StaticHasher : public StaticStdHasher {}; -template<> struct StaticHasher : public StaticStdHasher {}; -template<> struct StaticHasher : public StaticStdHasher {}; - -template -size_t Hash(const T& v) { - return StaticHasher::HashImpl(v); -} - -//- -// From Boost: https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine - -inline size_t HashCombine(size_t seed, const size_t hash) { - seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; -} - -// - -// See https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17 - -template -size_t HashTupleN(const std::tuple& tup, const std::index_sequence&) { - size_t seed = 0; - for (const auto& hash : {Hash(std::get(tup))...}) { - seed = HashCombine(seed, hash); - } - return seed; -} - -template -size_t HashTuple(const std::tuple& tup) { - return HashTupleN(tup, std::make_index_sequence()); -} - -// - - -template -auto MembersEq(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersLt(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersHash(const T& a) { - const auto atup = a.Members(); - return HashTuple(atup); -} - -template -struct MembersHasher final { - auto operator()(const T& v) const { - return v.hash(); - } -}; - -/** E.g.: -struct Foo { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } - INLINE_AUTO_MAPPABLE(Foo) -}; -std::unordered_set easy; -**/ -#define INLINE_DERIVE_MEMBERS_EQ(T) \ - friend bool operator==(const T& a, const T& b) { \ - return mozilla::gfx::MembersEq(a, b); \ - } \ - friend bool operator!=(const T& a, const T& b) { \ - return !operator==(a, b); \ - } -#define INLINE_AUTO_MAPPABLE(T) \ - friend bool operator<(const T& a, const T& b) { \ - return mozilla::gfx::MembersLt(a, b); \ - } \ - INLINE_DERIVE_MEMBERS_EQ(T) \ - size_t hash() const { \ - return mozilla::gfx::MembersHash(*reinterpret_cast(this)); \ - } \ - using Hasher = mozilla::gfx::MembersHasher; - -// - - -/** E.g.: -``` -struct Foo : public AutoMappable { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } -}; -std::unordered_set easy; -``` -`easy.insert({{}, 2, true});` -The initial {} is needed for aggregate initialization of AutoMappable. -Use INLINE_AUTO_MAPPABLE if this is too annoying. -**/ -template -struct AutoMappable { - INLINE_AUTO_MAPPABLE(T) -}; - -} // namespace mozilla::gfx - -#endif // MOZILLA_AUTO_MAPPABLE_H +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_AUTO_MAPPABLE_H +#define MOZILLA_AUTO_MAPPABLE_H + +// Here be dragons. + +#include + +namespace mozilla::gfx { + +template +size_t Hash(const T&); + +template +struct StaticStdHasher { + static auto HashImpl(const T& v) { return std::hash()(v); } +}; + +template +struct StaticHasher { + static auto HashImpl(const T& v) { return v.hash(); } +}; +template +struct StaticHasher> { + static size_t HashImpl(const std::optional& v) { + if (!v) return 0; + return Hash(*v); + } +}; +template <> +struct StaticHasher : public StaticStdHasher {}; +template <> +struct StaticHasher : public StaticStdHasher {}; +template <> +struct StaticHasher : public StaticStdHasher {}; + +template +size_t Hash(const T& v) { + return StaticHasher::HashImpl(v); +} + +//- +// From Boost: +// https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine + +inline size_t HashCombine(size_t seed, const size_t hash) { + seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; +} + +// - +// See +// https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17 + +template +size_t HashTupleN(const std::tuple& tup, + const std::index_sequence&) { + size_t seed = 0; + for (const auto& hash : {Hash(std::get(tup))...}) { + seed = HashCombine(seed, hash); + } + return seed; +} + +template +size_t HashTuple(const std::tuple& tup) { + return HashTupleN(tup, std::make_index_sequence()); +} + +// - + +template +auto MembersEq(const T& a, const T& b) { + const auto atup = a.Members(); + const auto btup = b.Members(); + return atup == btup; +} + +template +auto MembersLt(const T& a, const T& b) { + const auto atup = a.Members(); + const auto btup = b.Members(); + return atup == btup; +} + +template +auto MembersHash(const T& a) { + const auto atup = a.Members(); + return HashTuple(atup); +} + +template +struct MembersHasher final { + auto operator()(const T& v) const { return v.hash(); } +}; + +/** E.g.: +struct Foo { + int i; + bool b; + + auto Members() const { return std::tie(i, b); } + INLINE_AUTO_MAPPABLE(Foo) +}; +std::unordered_set easy; +**/ +#define INLINE_DERIVE_MEMBERS_EQ(T) \ + friend bool operator==(const T& a, const T& b) { \ + return mozilla::gfx::MembersEq(a, b); \ + } \ + friend bool operator!=(const T& a, const T& b) { return !operator==(a, b); } +#define INLINE_AUTO_MAPPABLE(T) \ + friend bool operator<(const T& a, const T& b) { \ + return mozilla::gfx::MembersLt(a, b); \ + } \ + INLINE_DERIVE_MEMBERS_EQ(T) \ + size_t hash() const { \ + return mozilla::gfx::MembersHash(*reinterpret_cast(this)); \ + } \ + using Hasher = mozilla::gfx::MembersHasher; + +// - + +/** E.g.: +``` +struct Foo : public AutoMappable { + int i; + bool b; + + auto Members() const { return std::tie(i, b); } +}; +std::unordered_set easy; +``` +`easy.insert({{}, 2, true});` +The initial {} is needed for aggregate initialization of AutoMappable. +Use INLINE_AUTO_MAPPABLE if this is too annoying. +**/ +template +struct AutoMappable { + INLINE_AUTO_MAPPABLE(T) +}; + +} // namespace mozilla::gfx + +#endif // MOZILLA_AUTO_MAPPABLE_H diff --git a/gfx/gl/Colorspaces.cpp b/gfx/gl/Colorspaces.cpp dissimilarity index 84% index ad95ed872ef1..8ef15a58d77d 100644 --- a/gfx/gl/Colorspaces.cpp +++ b/gfx/gl/Colorspaces.cpp @@ -1,242 +1,229 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// We are going to be doing so, so many transforms, so descriptive labels are -// critical. - -#include "Colorspaces.h" - -namespace mozilla::color { - -// tf = { k * linear | linear < b -// { a * pow(linear, 1/g) - (1-a) | linear >= b -float TfFromLinear(const PiecewiseGammaDesc& desc, const float linear) { - if (linear < desc.b) { - return linear * desc.k; - } - float ret = linear; - ret = powf(ret, 1.0f / desc.g); - ret *= desc.a; - ret -= (desc.a - 1); - return ret; -} - -float LinearFromTf(const PiecewiseGammaDesc& desc, const float tf) { - const auto linear_if_low = tf / desc.k; - if (linear_if_low < desc.b) { - return linear_if_low; - } - float ret = tf; - ret += (desc.a - 1); - ret /= desc.a; - ret = powf(ret, 1.0f * desc.g); - return ret; -} - -// - - -mat3 YuvFromRgb(const YuvLumaCoeffs& yc) { - // Y is always [0,1] - // U and V are signed, and could be either [-1,+1] or [-0.5,+0.5]. - // Specs generally use [-0.5,+0.5], so we use that too. - // E.g. - // y = 0.2126*r + 0.7152*g + 0.0722*b - // u = (b - y) / (u_range = u_max - u_min) // u_min = -u_max - // = (b - y) / (u(0,0,1) - u(1,1,0)) - // = (b - y) / (2 * u(0,0,1)) - // = (b - y) / (2 * u.b)) - // = (b - y) / (2 * (1 - 0.0722)) - // = (-0.2126*r + -0.7152*g + (1-0.0722)*b) / 1.8556 - // v = (r - y) / 1.5748; - // = ((1-0.2126)*r + -0.7152*g + -0.0722*b) / 1.5748 - const auto y = vec3({yc.r, yc.g, yc.b}); - const auto u = vec3({0,0,1}) - y; - const auto v = vec3({1,0,0}) - y; - - // From rows: - return mat3({y, - u / (2 * u.z()), - v / (2 * v.x())}); -} - -mat4 YuvFromYcbcr(const YcbcrDesc& d) { - // E.g. - // y = (yy - 16) / (235 - 16); // 16->0, 235->1 - // u = (cb - 128) / (240 - 16); // 16->-0.5, 128->0, 240->+0.5 - // v = (cr - 128) / (240 - 16); - - const auto yRange = d.y1 - d.y0; - const auto uHalfRange = d.uPlusHalf - d.u0; - const auto uRange = 2 * uHalfRange; - - const auto ycbcrFromYuv = mat4{{ - vec4{{yRange, 0, 0, d.y0}}, - {{0, uRange, 0, d.u0}}, - {{0, 0, uRange, d.u0}}, - {{0,0,0,1}} - }}; - const auto yuvFromYcbcr = inverse(ycbcrFromYuv); - return yuvFromYcbcr; -} - -mat3 XyzFromLinearRgb(const Chromaticities& c) { - // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html - - // Given red (xr, yr), green (xg, yg), blue (xb, yb), - // and whitepoint (XW, YW, ZW) - - // [ X ] [ R ] - // [ Y ] = M x [ G ] - // [ Z ] [ B ] - - // [ Sr*Xr Sg*Xg Sb*Xb ] - // M = [ Sr*Yr Sg*Yg Sb*Yb ] - // [ Sr*Zr Sg*Zg Sb*Zb ] - - // Xr = xr / yr - // Yr = 1 - // Zr = (1 - xr - yr) / yr - - // Xg = xg / yg - // Yg = 1 - // Zg = (1 - xg - yg) / yg - - // Xb = xb / yb - // Yb = 1 - // Zb = (1 - xb - yb) / yb - - // [ Sr ] [ Xr Xg Xb ]^-1 [ XW ] - // [ Sg ] = [ Yr Yg Yb ] x [ YW ] - // [ Sb ] [ Zr Zg Zb ] [ ZW ] - - const auto xrgb = vec3({c.rx, c.gx, c.bx}); - const auto yrgb = vec3({c.ry, c.gy, c.by}); - - const auto Xrgb = xrgb / yrgb; - const auto Yrgb = vec3(1); - const auto Zrgb = (vec3(1) - xrgb - yrgb) / yrgb; - - const auto XYZrgb = mat3({ - Xrgb, - Yrgb, - Zrgb}); - const auto XYZrgb_inv = inverse(XYZrgb); - const auto XYZwhitepoint = vec3({c.wx, c.wy, 1 - c.wx - c.wy}); - const auto Srgb = XYZrgb_inv * XYZwhitepoint; - - const auto M = mat3({ - Srgb * Xrgb, - Srgb * Yrgb, - Srgb * Zrgb}); - return M; -} - -// - -ColorspaceTransform -ColorspaceTransform::Create(const ColorspaceDesc& src, - const ColorspaceDesc& dst) { - auto ct = ColorspaceTransform{src, dst}; - ct.srcTf = src.tf; - ct.dstTf = dst.tf; - - const auto RgbTfFrom = [&](const ColorspaceDesc& cs) { - auto rgbFrom = mat4::Identity(); - if (cs.yuv) { - const auto yuvFromYcbcr = YuvFromYcbcr(cs.yuv->ycbcr); - const auto yuvFromRgb = YuvFromRgb(cs.yuv->yCoeffs); - const auto rgbFromYuv = inverse(yuvFromRgb); - const auto rgbFromYuv4 = mat4(rgbFromYuv); - - const auto rgbFromYcbcr = rgbFromYuv4 * yuvFromYcbcr; - rgbFrom = rgbFromYcbcr; - } - return rgbFrom; - }; - - ct.srcRgbTfFromSrc = RgbTfFrom(src); - const auto dstRgbTfFromDst = RgbTfFrom(dst); - ct.dstFromDstRgbTf = inverse(dstRgbTfFromDst); - - // - - - ct.dstRgbLinFromSrcRgbLin = mat3::Identity(); - if (!(src.chrom == dst.chrom)) { - const auto xyzFromSrcRgbLin = XyzFromLinearRgb(src.chrom); - const auto xyzFromDstRgbLin = XyzFromLinearRgb(dst.chrom); - const auto dstRgbLinFromXyz = inverse(xyzFromDstRgbLin); - ct.dstRgbLinFromSrcRgbLin = dstRgbLinFromXyz * xyzFromSrcRgbLin; - } - - return ct; -} - -vec3 ColorspaceTransform::DstFromSrc(const vec3 src) const { - const auto srcRgbTf = srcRgbTfFromSrc * vec4(src, 1); - auto srcRgbLin = srcRgbTf; - if (srcTf) { - srcRgbLin.x( LinearFromTf(*srcTf, srcRgbTf.x()) ); - srcRgbLin.y( LinearFromTf(*srcTf, srcRgbTf.y()) ); - srcRgbLin.z( LinearFromTf(*srcTf, srcRgbTf.z()) ); - } - - const auto dstRgbLin = dstRgbLinFromSrcRgbLin * vec3(srcRgbLin); - auto dstRgbTf = dstRgbLin; - if (dstTf) { - dstRgbTf.x( TfFromLinear(*dstTf, dstRgbLin.x()) ); - dstRgbTf.y( TfFromLinear(*dstTf, dstRgbLin.y()) ); - dstRgbTf.z( TfFromLinear(*dstTf, dstRgbLin.z()) ); - } - - const auto dst4 = dstFromDstRgbTf * vec4(dstRgbTf, 1); - return vec3(dst4); -} - -// - - -std::optional ColorspaceTransform::ToMat4() const { - mat4 fromSrc = srcRgbTfFromSrc; - if (srcTf) return {}; - fromSrc = mat4(dstRgbLinFromSrcRgbLin) * fromSrc; - if (dstTf) return {}; - fromSrc = dstFromDstRgbTf * fromSrc; - return fromSrc; -} - -Lut3 ColorspaceTransform::ToLut3(const ivec3 size) const { - auto lut = Lut3::Create(size); - lut.SetMap([&](const vec3& srcVal) { - return DstFromSrc(srcVal); - }); - return lut; -} - -vec3 Lut3::Sample(const vec3 in01) const { - const auto coord = vec3(size-1) * in01; - const auto p0 = floor(coord); - const auto dp = coord - p0; - const auto ip0 = ivec3(p0); - - // Trilinear - const auto f000 = Fetch(ip0 + ivec3({0,0,0})); - const auto f100 = Fetch(ip0 + ivec3({1,0,0})); - const auto f010 = Fetch(ip0 + ivec3({0,1,0})); - const auto f110 = Fetch(ip0 + ivec3({1,1,0})); - const auto f001 = Fetch(ip0 + ivec3({0,0,1})); - const auto f101 = Fetch(ip0 + ivec3({1,0,1})); - const auto f011 = Fetch(ip0 + ivec3({0,1,1})); - const auto f111 = Fetch(ip0 + ivec3({1,1,1})); - - const auto fx00 = mix(f000, f100, dp.x()); - const auto fx10 = mix(f010, f110, dp.x()); - const auto fx01 = mix(f001, f101, dp.x()); - const auto fx11 = mix(f011, f111, dp.x()); - - const auto fxy0 = mix(fx00, fx10, dp.y()); - const auto fxy1 = mix(fx01, fx11, dp.y()); - - const auto fxyz = mix(fxy0, fxy1, dp.z()); - return fxyz; -} - -} // namespace mozilla::color +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// We are going to be doing so, so many transforms, so descriptive labels are +// critical. + +#include "Colorspaces.h" + +namespace mozilla::color { + +// tf = { k * linear | linear < b +// { a * pow(linear, 1/g) - (1-a) | linear >= b +float TfFromLinear(const PiecewiseGammaDesc& desc, const float linear) { + if (linear < desc.b) { + return linear * desc.k; + } + float ret = linear; + ret = powf(ret, 1.0f / desc.g); + ret *= desc.a; + ret -= (desc.a - 1); + return ret; +} + +float LinearFromTf(const PiecewiseGammaDesc& desc, const float tf) { + const auto linear_if_low = tf / desc.k; + if (linear_if_low < desc.b) { + return linear_if_low; + } + float ret = tf; + ret += (desc.a - 1); + ret /= desc.a; + ret = powf(ret, 1.0f * desc.g); + return ret; +} + +// - + +mat3 YuvFromRgb(const YuvLumaCoeffs& yc) { + // Y is always [0,1] + // U and V are signed, and could be either [-1,+1] or [-0.5,+0.5]. + // Specs generally use [-0.5,+0.5], so we use that too. + // E.g. + // y = 0.2126*r + 0.7152*g + 0.0722*b + // u = (b - y) / (u_range = u_max - u_min) // u_min = -u_max + // = (b - y) / (u(0,0,1) - u(1,1,0)) + // = (b - y) / (2 * u(0,0,1)) + // = (b - y) / (2 * u.b)) + // = (b - y) / (2 * (1 - 0.0722)) + // = (-0.2126*r + -0.7152*g + (1-0.0722)*b) / 1.8556 + // v = (r - y) / 1.5748; + // = ((1-0.2126)*r + -0.7152*g + -0.0722*b) / 1.5748 + const auto y = vec3({yc.r, yc.g, yc.b}); + const auto u = vec3({0, 0, 1}) - y; + const auto v = vec3({1, 0, 0}) - y; + + // From rows: + return mat3({y, u / (2 * u.z()), v / (2 * v.x())}); +} + +mat4 YuvFromYcbcr(const YcbcrDesc& d) { + // E.g. + // y = (yy - 16) / (235 - 16); // 16->0, 235->1 + // u = (cb - 128) / (240 - 16); // 16->-0.5, 128->0, 240->+0.5 + // v = (cr - 128) / (240 - 16); + + const auto yRange = d.y1 - d.y0; + const auto uHalfRange = d.uPlusHalf - d.u0; + const auto uRange = 2 * uHalfRange; + + const auto ycbcrFromYuv = mat4{{vec4{{yRange, 0, 0, d.y0}}, + {{0, uRange, 0, d.u0}}, + {{0, 0, uRange, d.u0}}, + {{0, 0, 0, 1}}}}; + const auto yuvFromYcbcr = inverse(ycbcrFromYuv); + return yuvFromYcbcr; +} + +mat3 XyzFromLinearRgb(const Chromaticities& c) { + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + + // Given red (xr, yr), green (xg, yg), blue (xb, yb), + // and whitepoint (XW, YW, ZW) + + // [ X ] [ R ] + // [ Y ] = M x [ G ] + // [ Z ] [ B ] + + // [ Sr*Xr Sg*Xg Sb*Xb ] + // M = [ Sr*Yr Sg*Yg Sb*Yb ] + // [ Sr*Zr Sg*Zg Sb*Zb ] + + // Xr = xr / yr + // Yr = 1 + // Zr = (1 - xr - yr) / yr + + // Xg = xg / yg + // Yg = 1 + // Zg = (1 - xg - yg) / yg + + // Xb = xb / yb + // Yb = 1 + // Zb = (1 - xb - yb) / yb + + // [ Sr ] [ Xr Xg Xb ]^-1 [ XW ] + // [ Sg ] = [ Yr Yg Yb ] x [ YW ] + // [ Sb ] [ Zr Zg Zb ] [ ZW ] + + const auto xrgb = vec3({c.rx, c.gx, c.bx}); + const auto yrgb = vec3({c.ry, c.gy, c.by}); + + const auto Xrgb = xrgb / yrgb; + const auto Yrgb = vec3(1); + const auto Zrgb = (vec3(1) - xrgb - yrgb) / yrgb; + + const auto XYZrgb = mat3({Xrgb, Yrgb, Zrgb}); + const auto XYZrgb_inv = inverse(XYZrgb); + const auto XYZwhitepoint = vec3({c.wx, c.wy, 1 - c.wx - c.wy}); + const auto Srgb = XYZrgb_inv * XYZwhitepoint; + + const auto M = mat3({Srgb * Xrgb, Srgb * Yrgb, Srgb * Zrgb}); + return M; +} + +// - +ColorspaceTransform ColorspaceTransform::Create(const ColorspaceDesc& src, + const ColorspaceDesc& dst) { + auto ct = ColorspaceTransform{src, dst}; + ct.srcTf = src.tf; + ct.dstTf = dst.tf; + + const auto RgbTfFrom = [&](const ColorspaceDesc& cs) { + auto rgbFrom = mat4::Identity(); + if (cs.yuv) { + const auto yuvFromYcbcr = YuvFromYcbcr(cs.yuv->ycbcr); + const auto yuvFromRgb = YuvFromRgb(cs.yuv->yCoeffs); + const auto rgbFromYuv = inverse(yuvFromRgb); + const auto rgbFromYuv4 = mat4(rgbFromYuv); + + const auto rgbFromYcbcr = rgbFromYuv4 * yuvFromYcbcr; + rgbFrom = rgbFromYcbcr; + } + return rgbFrom; + }; + + ct.srcRgbTfFromSrc = RgbTfFrom(src); + const auto dstRgbTfFromDst = RgbTfFrom(dst); + ct.dstFromDstRgbTf = inverse(dstRgbTfFromDst); + + // - + + ct.dstRgbLinFromSrcRgbLin = mat3::Identity(); + if (!(src.chrom == dst.chrom)) { + const auto xyzFromSrcRgbLin = XyzFromLinearRgb(src.chrom); + const auto xyzFromDstRgbLin = XyzFromLinearRgb(dst.chrom); + const auto dstRgbLinFromXyz = inverse(xyzFromDstRgbLin); + ct.dstRgbLinFromSrcRgbLin = dstRgbLinFromXyz * xyzFromSrcRgbLin; + } + + return ct; +} + +vec3 ColorspaceTransform::DstFromSrc(const vec3 src) const { + const auto srcRgbTf = srcRgbTfFromSrc * vec4(src, 1); + auto srcRgbLin = srcRgbTf; + if (srcTf) { + srcRgbLin.x(LinearFromTf(*srcTf, srcRgbTf.x())); + srcRgbLin.y(LinearFromTf(*srcTf, srcRgbTf.y())); + srcRgbLin.z(LinearFromTf(*srcTf, srcRgbTf.z())); + } + + const auto dstRgbLin = dstRgbLinFromSrcRgbLin * vec3(srcRgbLin); + auto dstRgbTf = dstRgbLin; + if (dstTf) { + dstRgbTf.x(TfFromLinear(*dstTf, dstRgbLin.x())); + dstRgbTf.y(TfFromLinear(*dstTf, dstRgbLin.y())); + dstRgbTf.z(TfFromLinear(*dstTf, dstRgbLin.z())); + } + + const auto dst4 = dstFromDstRgbTf * vec4(dstRgbTf, 1); + return vec3(dst4); +} + +// - + +std::optional ColorspaceTransform::ToMat4() const { + mat4 fromSrc = srcRgbTfFromSrc; + if (srcTf) return {}; + fromSrc = mat4(dstRgbLinFromSrcRgbLin) * fromSrc; + if (dstTf) return {}; + fromSrc = dstFromDstRgbTf * fromSrc; + return fromSrc; +} + +Lut3 ColorspaceTransform::ToLut3(const ivec3 size) const { + auto lut = Lut3::Create(size); + lut.SetMap([&](const vec3& srcVal) { return DstFromSrc(srcVal); }); + return lut; +} + +vec3 Lut3::Sample(const vec3 in01) const { + const auto coord = vec3(size - 1) * in01; + const auto p0 = floor(coord); + const auto dp = coord - p0; + const auto ip0 = ivec3(p0); + + // Trilinear + const auto f000 = Fetch(ip0 + ivec3({0, 0, 0})); + const auto f100 = Fetch(ip0 + ivec3({1, 0, 0})); + const auto f010 = Fetch(ip0 + ivec3({0, 1, 0})); + const auto f110 = Fetch(ip0 + ivec3({1, 1, 0})); + const auto f001 = Fetch(ip0 + ivec3({0, 0, 1})); + const auto f101 = Fetch(ip0 + ivec3({1, 0, 1})); + const auto f011 = Fetch(ip0 + ivec3({0, 1, 1})); + const auto f111 = Fetch(ip0 + ivec3({1, 1, 1})); + + const auto fx00 = mix(f000, f100, dp.x()); + const auto fx10 = mix(f010, f110, dp.x()); + const auto fx01 = mix(f001, f101, dp.x()); + const auto fx11 = mix(f011, f111, dp.x()); + + const auto fxy0 = mix(fx00, fx10, dp.y()); + const auto fxy1 = mix(fx01, fx11, dp.y()); + + const auto fxyz = mix(fxy0, fxy1, dp.z()); + return fxyz; +} + +} // namespace mozilla::color diff --git a/gfx/gl/Colorspaces.h b/gfx/gl/Colorspaces.h dissimilarity index 70% index a62084f206a3..a08f27f85a43 100644 --- a/gfx/gl/Colorspaces.h +++ b/gfx/gl/Colorspaces.h @@ -1,694 +1,657 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MOZILLA_GFX_GL_COLORSPACES_H_ -#define MOZILLA_GFX_GL_COLORSPACES_H_ - -// Reference: https://hackmd.io/0wkiLmP7RWOFjcD13M870A - -// We are going to be doing so, so many transforms, so descriptive labels are -// critical. - -// Colorspace background info: https://hackmd.io/0wkiLmP7RWOFjcD13M870A - -#include -#include -#include -#include - -#include "AutoMappable.h" -#include "mozilla/Attributes.h" - -#ifdef DEBUG - #define ASSERT(EXPR) do { \ - if (!(EXPR)) { \ - __builtin_trap(); \ - } \ - } while (false) -#else - #define ASSERT(EXPR) (void)(EXPR) -#endif - -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) - - static constexpr auto Rec709() { - return YuvLumaCoeffs(); - } - - static constexpr auto Rec2020() { - return YuvLumaCoeffs{0.2627, 0.6780, 0.0593}; - } -}; - -struct PiecewiseGammaDesc final { - // tf = { k * linear | linear < b - // { a * pow(linear, 1/g) - (1-a) | linear >= b - - // Default to Srgb - float a = 1.055; - float b = 0.04045 / 12.92; - float g = 2.4; - float k = 12.92; - - auto Members() const { - return std::tie(a, b, g, k); - } - INLINE_AUTO_MAPPABLE(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, - }; - } - 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, - }; - } -}; - -struct YcbcrDesc final { - float y0 = 16 / 255.0; - float y1 = 235 / 255.0; - float u0 = 128 / 255.0; - float uPlusHalf = 240 / 255.0; - - auto Members() const { - return std::tie(y0, y1, u0, uPlusHalf); - } - INLINE_AUTO_MAPPABLE(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, - }; - } - static constexpr auto Float() { // Best for a LUT - return YcbcrDesc{0.0, 1.0, 0.5, 1.0}; - } -}; - -struct Chromaticities final { - float rx = 0.640; - float ry = 0.330; - float gx = 0.300; - float gy = 0.600; - float bx = 0.150; - float by = 0.060; - // D65: - static constexpr float wx = 0.3127; - static constexpr float wy = 0.3290; - - auto Members() const { - return std::tie(rx, ry, gx, gy, bx, by); - } - INLINE_AUTO_MAPPABLE(Chromaticities) - - // - - - static constexpr auto Rec709() { // AKA limited/studio/tv - return Chromaticities(); - } - static constexpr auto Srgb() { return Rec709(); } - - static constexpr auto Rec601_625_Pal() { - auto ret = Rec709(); - ret.gx = 0.290; - return ret; - } - static constexpr auto Rec601_525_Ntsc() { - return Chromaticities{ - 0.630, 0.340, // r - 0.310, 0.595, // g - 0.155, 0.070, // b - }; - } - static constexpr auto Rec2020() { - return Chromaticities{ - 0.708, 0.292, // r - 0.170, 0.797, // g - 0.131, 0.046, // b - }; - } - static constexpr auto DisplayP3() { - return Chromaticities{ - 0.680, 0.320, // r - 0.265, 0.690, // g - 0.150, 0.060, // b - }; - } -}; - -// - - -struct YuvDesc final { - YuvLumaCoeffs yCoeffs; - YcbcrDesc ycbcr; - - auto Members() const { - return std::tie(yCoeffs, ycbcr); - } - INLINE_AUTO_MAPPABLE(YuvDesc); -}; - -struct ColorspaceDesc final { - Chromaticities chrom; - std::optional tf; - std::optional yuv; - - auto Members() const { - return std::tie(chrom, tf, yuv); - } - INLINE_AUTO_MAPPABLE(ColorspaceDesc); -}; - -// - - -template -struct avec final { - using T = TT; - static constexpr auto N = NN; - - std::array data = {}; - - // - - - constexpr avec() = default; - constexpr avec(const avec&) = default; - - constexpr avec(const avec& v, T a) { - for (int i = 0; i < N-1; i++) { - data[i] = v[i]; - } - data[N-1] = a; - } - constexpr avec(const avec& v, T a, T b) { - for (int i = 0; i < N-2; i++) { - data[i] = v[i]; - } - data[N-2] = a; - data[N-1] = b; - } - - MOZ_IMPLICIT constexpr avec(const std::array& data) { - this->data = data; - } - - explicit constexpr avec(const T v) { - for (int i = 0; i < N; i++) { - data[i] = v; - } - } - - template - explicit constexpr avec(const avec& v) { - const auto n = std::min(N, N2); - for (int i = 0; i < n; i++) { - data[i] = static_cast(v[i]); - } - } - - // - - - const auto& operator[](const size_t n) const { return data[n]; } - auto& operator[](const size_t n) { return data[n]; } - - template - constexpr auto get() const { - return (i < N) ? data[i] : 0; - } - constexpr auto x() const { return get<0>(); } - constexpr auto y() const { return get<1>(); } - constexpr auto z() const { return get<2>(); } - constexpr auto w() const { return get<3>(); } - - constexpr auto xyz() const { - return vec3({x(), y(), z()}); - } - - template - void set(const T v) { - if (i < N) { - data[i] = v; - } - } - void x(const T v) { set<0>(v); } - void y(const T v) { set<1>(v); } - void z(const T v) { set<2>(v); } - void w(const T v) { set<3>(v); } - - // - - - #define _(OP) \ - friend avec operator OP(const avec a, const avec b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a[i] OP b[i]; \ - } \ - return c; \ - } \ - friend avec operator OP(const avec a, const T b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a[i] OP b; \ - } \ - return c; \ - } \ - friend avec operator OP(const T a, const avec b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a OP b[i]; \ - } \ - return c; \ - } - _(+) - _(-) - _(*) - _(/) - #undef _ - - friend bool operator==(const avec a, const avec b) { - bool eq = true; - for (int i = 0; i < N; i++) { - eq &= (a[i] == b[i]); - } - return eq; - } -}; -using vec3 = avec; -using vec4 = avec; -using ivec3 = avec; -using ivec4 = avec; - -template -T dot(const avec& a, const avec& b) { - const auto c = a * b; - T ret = 0; - for (int i = 0; i < N; i++) { - ret += c[i]; - } - return ret; -} - -template -V mix(const V& zero, const V& one, const float val) { - return zero * (1 - val) + one * val; -} - -template -auto min(const avec& a, const avec& b) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = std::min(a[i], b[i]); - } - return ret; -} - -template -auto max(const avec& a, const avec& b) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = std::max(a[i], b[i]); - } - return ret; -} - -template -auto floor(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = floorf(a[i]); - } - return ret; -} - -template -auto round(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = roundf(a[i]); - } - return ret; -} - -template -inline auto t_abs(const T& a) { - return abs(a); -} -template<> -inline auto t_abs(const float& a) { - return fabs(a); -} - -template -auto abs(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = t_abs(a[i]); - } - return ret; -} - -// - - -template -struct mat final { - static constexpr int y_rows = Y_Rows; - static constexpr int x_cols = X_Cols; - - static constexpr auto Identity() { - auto ret = mat{}; - for (int x = 0; x < x_cols; x++) { - for (int y = 0; y < y_rows; y++) { - ret.at(x,y) = (x == y ? 1 : 0); - } - } - return ret; - } - - std::array, Y_Rows> rows = {}; // row-major - - // - - - constexpr mat() {} - - explicit constexpr mat(const std::array, Y_Rows>& rows) { - this->rows = rows; - } - - template - explicit constexpr mat(const mat& m) { - *this = Identity(); - for (int x = 0; x < std::min(X_Cols, X_Cols2); x++) { - for (int y = 0; y < std::min(Y_Rows, Y_Rows2); y++) { - at(x,y) = m.at(x, y); - } - } - } - - 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]; } - - friend auto operator*(const mat& a, - const avec& b_colvec) { - avec c_colvec; - for (int i = 0; i < y_rows; i++) { - c_colvec[i] = dot(a.rows.at(i), b_colvec); - } - return c_colvec; - } - - friend auto operator*(const mat& a, const float b) { - mat c; - for (int x = 0; x < x_cols; x++) { - for (int y = 0; y < y_rows; y++) { - c.at(x,y) = a.at(x,y) * b; - } - } - return c; - } - friend auto operator/(const mat& a, const float b) { - return a * (1 / b); - } - - template - friend auto operator*(const mat& a, - const mat& b) { - const auto bt = transpose(b); - const auto& b_cols = bt.rows; - - mat c; - for (int x = 0; x < BCols; x++) { - for (int y = 0; y < Y_Rows; y++) { - c.at(x,y) = dot(a.rows.at(y), b_cols.at(x)); - } - } - return c; - } -}; -using mat3 = mat<3,3>; -using mat4 = mat<4,4>; - -inline float determinant(const mat<1,1>& m) { - return m.at(0,0); -} -template -float determinant(const T& m) { - static_assert(T::x_cols == T::y_rows); - - float ret = 0; - for (int i = 0; i < T::x_cols; i++) { - const auto cofact = cofactor(m, i, 0); - ret += m.at(i, 0) * cofact; - } - return ret; -} - -// - - -template -float cofactor(const T& m, const int x_col, const int y_row) { - ASSERT(0 <= x_col && x_col < T::x_cols); - ASSERT(0 <= y_row && y_row < T::y_rows); - - auto cofactor = minor_val(m, x_col, y_row); - if ((x_col+y_row) % 2 == 1) { - cofactor *= -1; - } - return cofactor; -} - -// - - -// Unfortunately, can't call this `minor(...)` because there is -// `#define minor(dev) gnu_dev_minor (dev)` -// in /usr/include/x86_64-linux-gnu/sys/sysmacros.h:62 -template -float minor_val(const T& a, const int skip_x, const int skip_y) { - ASSERT(0 <= skip_x && skip_x < T::x_cols); - ASSERT(0 <= skip_y && skip_y < T::y_rows); - - // A minor matrix is a matrix without its x_col and y_row. - mat b; - - int x_skips = 0; - for (int ax = 0; ax < T::x_cols; ax++) { - if (ax == skip_x) { - x_skips = 1; - continue; - } - - int y_skips = 0; - for (int ay = 0; ay < T::y_rows; ay++) { - if (ay == skip_y) { - y_skips = 1; - continue; - } - - b.at(ax - x_skips, ay - y_skips) = a.at(ax, ay); - } - } - - const auto minor = determinant(b); - return minor; -} - -// - - -/// The matrix of cofactors. -template -auto comatrix(const T& a) { - auto b = T{}; - for (int x = 0; x < T::x_cols; x++) { - for (int y = 0; y < T::y_rows; y++) { - b.at(x, y) = cofactor(a, x, y); - } - } - return b; -} - -// - - -template -auto transpose(const T& a) { - auto b = mat{}; - for (int x = 0; x < T::x_cols; x++) { - for (int y = 0; y < T::y_rows; y++) { - b.at(y, x) = a.at(x, y); - } - } - return b; -} - -// - - -template -inline T inverse(const T& a) { - const auto det = determinant(a); - const auto comat = comatrix(a); - const auto adjugate = transpose(comat); - const auto inv = adjugate / det; - return inv; -} - -// - - -template -void ForEachIntWithin(const ivec3 size, const F& f) { - ivec3 p; - for (p.z(0); p.z() < size.z(); p.z(p.z()+1)) { - for (p.y(0); p.y() < size.y(); p.y(p.y()+1)) { - for (p.x(0); p.x() < size.x(); p.x(p.x()+1)) { - f(p); - } - } - } -} -template -void ForEachSampleWithin(const ivec3 size, const F& f) { - const auto div = vec3(size - 1); - ForEachIntWithin(size, [&](const ivec3& isrc) { - const auto fsrc = vec3(isrc) / div; - f(fsrc); - }); -} - -// - - -struct Lut3 final { - ivec3 size; - std::vector data; - - // - - - static Lut3 Create(const ivec3 size) { - Lut3 lut; - lut.size = size; - lut.data.resize(size.x() * size.y() * size.z()); - return lut; - } - - // - - - /// p: [0, N-1] (clamps) - size_t Index(ivec3 p) const { - const auto scales = ivec3({ - 1, - size.x(), - size.x() * size.y() - }); - p = max(ivec3(0), min(p, size - 1)); // clamp - return dot(p, scales); - } - - // - - - template - void SetMap(const F& dstFromSrc01) { - ForEachIntWithin(size, [&](const ivec3 p) { - const auto i = Index(p); - const auto src01 = vec3(p) / vec3(size - 1); - const auto dstVal = dstFromSrc01(src01); - data.at(i) = dstVal; - }); - } - - // - - - /// p: [0, N-1] (clamps) - vec3 Fetch(ivec3 p) const { - const auto i = Index(p); - return data.at(i); - } - - /// in01: [0.0, 1.0] (clamps) - vec3 Sample(vec3 in01) const; -}; - -// - - -/** -Naively, it would be ideal to map directly from ycbcr to rgb, -but headroom and footroom are problematic: For e.g. narrow-range-8-bit, -our naive LUT would start at absolute y=0/255. However, values only start -at y=16/255, and depending on where your first LUT sample is, you might get -very poor approximations for y=16/255. -Further, even for full-range-8-bit, y=-0.5 is encoded as 1/255. U and v -aren't *as* important as y, but we should try be accurate for the min and -max values. Additionally, it would be embarassing to get whites/greys wrong, -so preserving u=0.0 should also be a goal. -Finally, when using non-linear transfer functions, the linear approximation of a -point between two samples will be fairly inaccurate. -We preserve min and max by choosing our input range such that min and max are -the endpoints of their LUT axis. -We preserve accuracy (at and around) mid by choosing odd sizes for dimentions. - -But also, the LUT is surprisingly robust, so check if the simple version works -before adding complexity! -**/ - -struct ColorspaceTransform final { - ColorspaceDesc srcSpace; - ColorspaceDesc dstSpace; - mat4 srcRgbTfFromSrc; - std::optional srcTf; - mat3 dstRgbLinFromSrcRgbLin; - std::optional dstTf; - mat4 dstFromDstRgbTf; - - static ColorspaceTransform Create(const ColorspaceDesc& src, - const ColorspaceDesc& dst); - - // - - - vec3 DstFromSrc(vec3 src) const; - - std::optional ToMat4() const; - - Lut3 ToLut3(const ivec3 size) const; - Lut3 ToLut3() const { - auto defaultSize = ivec3({31,31,15}); // Order of importance: G, R, B - if (srcSpace.yuv) { - defaultSize = ivec3({31,15,31}); // Y, Cb, Cr - } - return ToLut3(defaultSize); - } -}; - -} // namespace mozilla::color - -#undef ASSERT - -#endif // MOZILLA_GFX_GL_COLORSPACES_H_ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_GL_COLORSPACES_H_ +#define MOZILLA_GFX_GL_COLORSPACES_H_ + +// Reference: https://hackmd.io/0wkiLmP7RWOFjcD13M870A + +// We are going to be doing so, so many transforms, so descriptive labels are +// critical. + +// Colorspace background info: https://hackmd.io/0wkiLmP7RWOFjcD13M870A + +#include +#include +#include +#include +#include + +#include "AutoMappable.h" +#include "mozilla/Attributes.h" + +#ifdef DEBUG +# define ASSERT(EXPR) \ + do { \ + if (!(EXPR)) { \ + __builtin_trap(); \ + } \ + } while (false) +#else +# define ASSERT(EXPR) (void)(EXPR) +#endif + +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) + + static constexpr auto Rec709() { return YuvLumaCoeffs(); } + + static constexpr auto Rec2020() { + return YuvLumaCoeffs{0.2627, 0.6780, 0.0593}; + } +}; + +struct PiecewiseGammaDesc final { + // tf = { k * linear | linear < b + // { a * pow(linear, 1/g) - (1-a) | linear >= b + + // Default to Srgb + float a = 1.055; + float b = 0.04045 / 12.92; + float g = 2.4; + float k = 12.92; + + auto Members() const { return std::tie(a, b, g, k); } + INLINE_AUTO_MAPPABLE(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, + }; + } + 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, + }; + } +}; + +struct YcbcrDesc final { + float y0 = 16 / 255.0; + float y1 = 235 / 255.0; + float u0 = 128 / 255.0; + float uPlusHalf = 240 / 255.0; + + auto Members() const { return std::tie(y0, y1, u0, uPlusHalf); } + INLINE_AUTO_MAPPABLE(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, + }; + } + static constexpr auto Float() { // Best for a LUT + return YcbcrDesc{0.0, 1.0, 0.5, 1.0}; + } +}; + +struct Chromaticities final { + float rx = 0.640; + float ry = 0.330; + float gx = 0.300; + float gy = 0.600; + float bx = 0.150; + float by = 0.060; + // D65: + static constexpr float wx = 0.3127; + static constexpr float wy = 0.3290; + + auto Members() const { return std::tie(rx, ry, gx, gy, bx, by); } + INLINE_AUTO_MAPPABLE(Chromaticities) + + // - + + static constexpr auto Rec709() { // AKA limited/studio/tv + return Chromaticities(); + } + static constexpr auto Srgb() { return Rec709(); } + + static constexpr auto Rec601_625_Pal() { + auto ret = Rec709(); + ret.gx = 0.290; + return ret; + } + static constexpr auto Rec601_525_Ntsc() { + return Chromaticities{ + 0.630, 0.340, // r + 0.310, 0.595, // g + 0.155, 0.070, // b + }; + } + static constexpr auto Rec2020() { + return Chromaticities{ + 0.708, 0.292, // r + 0.170, 0.797, // g + 0.131, 0.046, // b + }; + } + static constexpr auto DisplayP3() { + return Chromaticities{ + 0.680, 0.320, // r + 0.265, 0.690, // g + 0.150, 0.060, // b + }; + } +}; + +// - + +struct YuvDesc final { + YuvLumaCoeffs yCoeffs; + YcbcrDesc ycbcr; + + auto Members() const { return std::tie(yCoeffs, ycbcr); } + INLINE_AUTO_MAPPABLE(YuvDesc); +}; + +struct ColorspaceDesc final { + Chromaticities chrom; + std::optional tf; + std::optional yuv; + + auto Members() const { return std::tie(chrom, tf, yuv); } + INLINE_AUTO_MAPPABLE(ColorspaceDesc); +}; + +// - + +template +struct avec final { + using T = TT; + static constexpr auto N = NN; + + std::array data = {}; + + // - + + constexpr avec() = default; + constexpr avec(const avec&) = default; + + constexpr avec(const avec& v, T a) { + for (int i = 0; i < N - 1; i++) { + data[i] = v[i]; + } + data[N - 1] = a; + } + constexpr avec(const avec& v, T a, T b) { + for (int i = 0; i < N - 2; i++) { + data[i] = v[i]; + } + data[N - 2] = a; + data[N - 1] = b; + } + + MOZ_IMPLICIT constexpr avec(const std::array& data) { + this->data = data; + } + + explicit constexpr avec(const T v) { + for (int i = 0; i < N; i++) { + data[i] = v; + } + } + + template + explicit constexpr avec(const avec& v) { + const auto n = std::min(N, N2); + for (int i = 0; i < n; i++) { + data[i] = static_cast(v[i]); + } + } + + // - + + const auto& operator[](const size_t n) const { return data[n]; } + auto& operator[](const size_t n) { return data[n]; } + + template + constexpr auto get() const { + return (i < N) ? data[i] : 0; + } + constexpr auto x() const { return get<0>(); } + constexpr auto y() const { return get<1>(); } + constexpr auto z() const { return get<2>(); } + constexpr auto w() const { return get<3>(); } + + constexpr auto xyz() const { return vec3({x(), y(), z()}); } + + template + void set(const T v) { + if (i < N) { + data[i] = v; + } + } + void x(const T v) { set<0>(v); } + void y(const T v) { set<1>(v); } + void z(const T v) { set<2>(v); } + void w(const T v) { set<3>(v); } + + // - + +#define _(OP) \ + friend avec operator OP(const avec a, const avec b) { \ + avec c; \ + for (int i = 0; i < N; i++) { \ + c[i] = a[i] OP b[i]; \ + } \ + return c; \ + } \ + friend avec operator OP(const avec a, const T b) { \ + avec c; \ + for (int i = 0; i < N; i++) { \ + c[i] = a[i] OP b; \ + } \ + return c; \ + } \ + friend avec operator OP(const T a, const avec b) { \ + avec c; \ + for (int i = 0; i < N; i++) { \ + c[i] = a OP b[i]; \ + } \ + return c; \ + } + _(+) + _(-) + _(*) + _(/) +#undef _ + + friend bool operator==(const avec a, const avec b) { + bool eq = true; + for (int i = 0; i < N; i++) { + eq &= (a[i] == b[i]); + } + return eq; + } +}; +using vec3 = avec; +using vec4 = avec; +using ivec3 = avec; +using ivec4 = avec; + +template +T dot(const avec& a, const avec& b) { + const auto c = a * b; + T ret = 0; + for (int i = 0; i < N; i++) { + ret += c[i]; + } + return ret; +} + +template +V mix(const V& zero, const V& one, const float val) { + return zero * (1 - val) + one * val; +} + +template +auto min(const avec& a, const avec& b) { + auto ret = avec{}; + for (int i = 0; i < ret.N; i++) { + ret[i] = std::min(a[i], b[i]); + } + return ret; +} + +template +auto max(const avec& a, const avec& b) { + auto ret = avec{}; + for (int i = 0; i < ret.N; i++) { + ret[i] = std::max(a[i], b[i]); + } + return ret; +} + +template +auto floor(const avec& a) { + auto ret = avec{}; + for (int i = 0; i < ret.N; i++) { + ret[i] = floorf(a[i]); + } + return ret; +} + +template +auto round(const avec& a) { + auto ret = avec{}; + for (int i = 0; i < ret.N; i++) { + ret[i] = roundf(a[i]); + } + return ret; +} + +template +auto abs(const avec& a) { + auto ret = avec{}; + for (int i = 0; i < ret.N; i++) { + ret[i] = std::abs(a[i]); + } + return ret; +} + +// - + +template +struct mat final { + static constexpr int y_rows = Y_Rows; + static constexpr int x_cols = X_Cols; + + static constexpr auto Identity() { + auto ret = mat{}; + for (int x = 0; x < x_cols; x++) { + for (int y = 0; y < y_rows; y++) { + ret.at(x, y) = (x == y ? 1 : 0); + } + } + return ret; + } + + std::array, Y_Rows> rows = {}; // row-major + + // - + + constexpr mat() = default; + + explicit constexpr mat(const std::array, Y_Rows>& rows) { + this->rows = rows; + } + + template + explicit constexpr mat(const mat& m) { + *this = Identity(); + for (int x = 0; x < std::min(X_Cols, X_Cols2); x++) { + for (int y = 0; y < std::min(Y_Rows, Y_Rows2); y++) { + at(x, y) = m.at(x, y); + } + } + } + + 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]; } + + friend auto operator*(const mat& a, const avec& b_colvec) { + avec c_colvec; + for (int i = 0; i < y_rows; i++) { + c_colvec[i] = dot(a.rows.at(i), b_colvec); + } + return c_colvec; + } + + friend auto operator*(const mat& a, const float b) { + mat c; + for (int x = 0; x < x_cols; x++) { + for (int y = 0; y < y_rows; y++) { + c.at(x, y) = a.at(x, y) * b; + } + } + return c; + } + friend auto operator/(const mat& a, const float b) { return a * (1 / b); } + + template + friend auto operator*(const mat& a, const mat& b) { + const auto bt = transpose(b); + const auto& b_cols = bt.rows; + + mat c; + for (int x = 0; x < BCols; x++) { + for (int y = 0; y < Y_Rows; y++) { + c.at(x, y) = dot(a.rows.at(y), b_cols.at(x)); + } + } + return c; + } +}; +using mat3 = mat<3, 3>; +using mat4 = mat<4, 4>; + +inline float determinant(const mat<1, 1>& m) { return m.at(0, 0); } +template +float determinant(const T& m) { + static_assert(T::x_cols == T::y_rows); + + float ret = 0; + for (int i = 0; i < T::x_cols; i++) { + const auto cofact = cofactor(m, i, 0); + ret += m.at(i, 0) * cofact; + } + return ret; +} + +// - + +template +float cofactor(const T& m, const int x_col, const int y_row) { + ASSERT(0 <= x_col && x_col < T::x_cols); + ASSERT(0 <= y_row && y_row < T::y_rows); + + auto cofactor = minor_val(m, x_col, y_row); + if ((x_col + y_row) % 2 == 1) { + cofactor *= -1; + } + return cofactor; +} + +// - + +// Unfortunately, can't call this `minor(...)` because there is +// `#define minor(dev) gnu_dev_minor (dev)` +// in /usr/include/x86_64-linux-gnu/sys/sysmacros.h:62 +template +float minor_val(const T& a, const int skip_x, const int skip_y) { + ASSERT(0 <= skip_x && skip_x < T::x_cols); + ASSERT(0 <= skip_y && skip_y < T::y_rows); + + // A minor matrix is a matrix without its x_col and y_row. + mat b; + + int x_skips = 0; + for (int ax = 0; ax < T::x_cols; ax++) { + if (ax == skip_x) { + x_skips = 1; + continue; + } + + int y_skips = 0; + for (int ay = 0; ay < T::y_rows; ay++) { + if (ay == skip_y) { + y_skips = 1; + continue; + } + + b.at(ax - x_skips, ay - y_skips) = a.at(ax, ay); + } + } + + const auto minor = determinant(b); + return minor; +} + +// - + +/// The matrix of cofactors. +template +auto comatrix(const T& a) { + auto b = T{}; + for (int x = 0; x < T::x_cols; x++) { + for (int y = 0; y < T::y_rows; y++) { + b.at(x, y) = cofactor(a, x, y); + } + } + return b; +} + +// - + +template +auto transpose(const T& a) { + auto b = mat{}; + for (int x = 0; x < T::x_cols; x++) { + for (int y = 0; y < T::y_rows; y++) { + b.at(y, x) = a.at(x, y); + } + } + return b; +} + +// - + +template +inline T inverse(const T& a) { + const auto det = determinant(a); + const auto comat = comatrix(a); + const auto adjugate = transpose(comat); + const auto inv = adjugate / det; + return inv; +} + +// - + +template +void ForEachIntWithin(const ivec3 size, const F& f) { + ivec3 p; + for (p.z(0); p.z() < size.z(); p.z(p.z() + 1)) { + for (p.y(0); p.y() < size.y(); p.y(p.y() + 1)) { + for (p.x(0); p.x() < size.x(); p.x(p.x() + 1)) { + f(p); + } + } + } +} +template +void ForEachSampleWithin(const ivec3 size, const F& f) { + const auto div = vec3(size - 1); + ForEachIntWithin(size, [&](const ivec3& isrc) { + const auto fsrc = vec3(isrc) / div; + f(fsrc); + }); +} + +// - + +struct Lut3 final { + ivec3 size; + std::vector data; + + // - + + static Lut3 Create(const ivec3 size) { + Lut3 lut; + lut.size = size; + lut.data.resize(size.x() * size.y() * size.z()); + return lut; + } + + // - + + /// p: [0, N-1] (clamps) + size_t Index(ivec3 p) const { + const auto scales = ivec3({1, size.x(), size.x() * size.y()}); + p = max(ivec3(0), min(p, size - 1)); // clamp + return dot(p, scales); + } + + // - + + template + void SetMap(const F& dstFromSrc01) { + ForEachIntWithin(size, [&](const ivec3 p) { + const auto i = Index(p); + const auto src01 = vec3(p) / vec3(size - 1); + const auto dstVal = dstFromSrc01(src01); + data.at(i) = dstVal; + }); + } + + // - + + /// p: [0, N-1] (clamps) + vec3 Fetch(ivec3 p) const { + const auto i = Index(p); + return data.at(i); + } + + /// in01: [0.0, 1.0] (clamps) + vec3 Sample(vec3 in01) const; +}; + +// - + +/** +Naively, it would be ideal to map directly from ycbcr to rgb, +but headroom and footroom are problematic: For e.g. narrow-range-8-bit, +our naive LUT would start at absolute y=0/255. However, values only start +at y=16/255, and depending on where your first LUT sample is, you might get +very poor approximations for y=16/255. +Further, even for full-range-8-bit, y=-0.5 is encoded as 1/255. U and v +aren't *as* important as y, but we should try be accurate for the min and +max values. Additionally, it would be embarassing to get whites/greys wrong, +so preserving u=0.0 should also be a goal. +Finally, when using non-linear transfer functions, the linear approximation of a +point between two samples will be fairly inaccurate. +We preserve min and max by choosing our input range such that min and max are +the endpoints of their LUT axis. +We preserve accuracy (at and around) mid by choosing odd sizes for dimentions. + +But also, the LUT is surprisingly robust, so check if the simple version works +before adding complexity! +**/ + +struct ColorspaceTransform final { + ColorspaceDesc srcSpace; + ColorspaceDesc dstSpace; + mat4 srcRgbTfFromSrc; + std::optional srcTf; + mat3 dstRgbLinFromSrcRgbLin; + std::optional dstTf; + mat4 dstFromDstRgbTf; + + static ColorspaceTransform Create(const ColorspaceDesc& src, + const ColorspaceDesc& dst); + + // - + + vec3 DstFromSrc(vec3 src) const; + + std::optional ToMat4() const; + + Lut3 ToLut3(const ivec3 size) const; + Lut3 ToLut3() const { + auto defaultSize = ivec3({31, 31, 15}); // Order of importance: G, R, B + if (srcSpace.yuv) { + defaultSize = ivec3({31, 15, 31}); // Y, Cb, Cr + } + return ToLut3(defaultSize); + } +}; + +} // namespace mozilla::color + +#undef ASSERT + +#endif // MOZILLA_GFX_GL_COLORSPACES_H_ diff --git a/gfx/gl/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp index df398a722b99..9091109bf8a4 100644 --- a/gfx/gl/GLBlitHelper.cpp +++ b/gfx/gl/GLBlitHelper.cpp @@ -14,6 +14,7 @@ #include "ScopedGLHelpers.h" #include "gfxUtils.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/UniquePtr.h" @@ -273,7 +274,7 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, gfxCriticalError() << "Unhandled texTarget: " << texTarget; } - for (int i = 0; i < int(mTexUnits.size()); i++) { + for (const auto i : IntegerRange(mTexUnits.size())) { const auto& unit = mTexUnits[i]; mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + unit); if (mGL.IsSupported(GLFeature::sampler_objects)) { @@ -285,7 +286,10 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, } ScopedSaveMultiTex::~ScopedSaveMultiTex() { - for (int i = mTexUnits.size() - 1; i >= 0; i--) { // reverse + // 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); if (mGL.IsSupported(GLFeature::sampler_objects)) { @@ -487,7 +491,8 @@ void DrawBlitProg::Draw(const BaseArgs& args, gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m); if (args.texUnitForColorLut) { - gl->fUniform1i(mLoc_uColorLut, *args.texUnitForColorLut); + gl->fUniform1i(mLoc_uColorLut, + AssertedCast(*args.texUnitForColorLut)); } MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1)); @@ -600,9 +605,11 @@ GLBlitHelper::GLBlitHelper(GLContext* const gl) const auto glslVersion = mGL->ShadingLanguageVersion(); if (mGL->IsGLES()) { - // If you run into problems on old android devices, it might be because some devices have OES_EGL_image_external but not OES_EGL_image_external_essl3. - // We could just use 100 in that particular case, but then we lose out on e.g. sampler3D. - // Let's just try 300 for now, and if we get regressions we'll add an essl100 fallback. + // If you run into problems on old android devices, it might be because some + // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3. + // We could just use 100 in that particular case, but then we lose out on + // e.g. sampler3D. Let's just try 300 for now, and if we get regressions + // we'll add an essl100 fallback. if (glslVersion >= 300) { mDrawBlitProg_VersionLine = nsCString("#version 300 es\n"); } else { @@ -717,7 +724,8 @@ const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg( } parts.push_back(kFragBody); } - mGL->fShaderSource(fs, parts.size(), parts.data(), nullptr); + mGL->fShaderSource(fs, AssertedCast(parts.size()), parts.data(), + nullptr); mGL->fCompileShader(fs); const auto prog = mGL->fCreateProgram(); @@ -1226,8 +1234,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, const auto& prog = GetDrawBlitProg({ kFragHeader_Tex2DRect, - {fragSample, - kFragConvert_ColorMatrix}, + {fragSample, kFragConvert_ColorMatrix}, }); prog->Draw(baseArgs, pYuvArgs); return true; @@ -1528,15 +1535,15 @@ std::shared_ptr GLBlitHelper::GetColorLutTex( const auto maxLutSize = color::ivec3{256}; auto lutSize = minLutSize; if (ct.srcSpace.yuv) { - lutSize.x(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y()); - lutSize.y(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb()); - lutSize.z(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr()); + 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())); } else { - lutSize.x(StaticPrefs::gfx_blithelper_lut_size_rgb_r()); - lutSize.y(StaticPrefs::gfx_blithelper_lut_size_rgb_g()); - lutSize.z(StaticPrefs::gfx_blithelper_lut_size_rgb_b()); + lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r())); + 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 = max(minLutSize, min(lutSize, maxLutSize)); // Clamp const auto lut = ct.ToLut3(lutSize); const auto& size = lut.size; diff --git a/gfx/gl/GLBlitHelperD3D.cpp b/gfx/gl/GLBlitHelperD3D.cpp index 4de11809f223..7651e16490f9 100644 --- a/gfx/gl/GLBlitHelperD3D.cpp +++ b/gfx/gl/GLBlitHelperD3D.cpp @@ -317,7 +317,8 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc, const DrawBlitProg::YUVArgs yuvArgs = { SubRectMat3(clipRect, uvSize, divisors), Some(yuvColorSpace)}; - const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}}); + const auto& prog = GetDrawBlitProg( + {kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}}); prog->Draw(baseArgs, &yuvArgs); return true; } @@ -367,8 +368,8 @@ bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3], const DrawBlitProg::YUVArgs yuvArgs = { SubRectMat3(clipRect, uvSize, divisors), Some(colorSpace)}; - const auto& prog = - GetDrawBlitProg({kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}}); + const auto& prog = GetDrawBlitProg( + {kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}}); prog->Draw(baseArgs, &yuvArgs); return true; } diff --git a/gfx/gl/gtest/TestColorspaces.cpp b/gfx/gl/gtest/TestColorspaces.cpp index 91735361d8f0..4ece3a5d41b5 100644 --- a/gfx/gl/gtest/TestColorspaces.cpp +++ b/gfx/gl/gtest/TestColorspaces.cpp @@ -33,13 +33,13 @@ TEST(Colorspaces, YcbcrDesc_Narrow8) const auto m = YuvFromYcbcr(YcbcrDesc::Narrow8()); const auto Yuv8 = [&](const ivec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); + const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); + const auto yuv = m * ycbcr; + return ivec3(round(yuv * 255)); }; - EXPECT_EQ(Yuv8({{ 16, 128, 128}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{ 17, 128, 128}}), (ivec3{{1, 0, 0}})); + EXPECT_EQ(Yuv8({{16, 128, 128}}), (ivec3{{0, 0, 0}})); + EXPECT_EQ(Yuv8({{17, 128, 128}}), (ivec3{{1, 0, 0}})); // y = 0.5 => (16 + 235) / 2 = 125.5 EXPECT_EQ(Yuv8({{125, 128, 128}}), (ivec3{{127, 0, 0}})); EXPECT_EQ(Yuv8({{126, 128, 128}}), (ivec3{{128, 0, 0}})); @@ -47,7 +47,7 @@ TEST(Colorspaces, YcbcrDesc_Narrow8) EXPECT_EQ(Yuv8({{235, 128, 128}}), (ivec3{{255, 0, 0}})); // Check that we get the naive out-of-bounds behavior we'd expect: - EXPECT_EQ(Yuv8({{ 15, 128, 128}}), (ivec3{{-1,0,0}})); + EXPECT_EQ(Yuv8({{15, 128, 128}}), (ivec3{{-1, 0, 0}})); EXPECT_EQ(Yuv8({{236, 128, 128}}), (ivec3{{256, 0, 0}})); } @@ -56,13 +56,13 @@ TEST(Colorspaces, YcbcrDesc_Full8) const auto m = YuvFromYcbcr(YcbcrDesc::Full8()); const auto Yuv8 = [&](const ivec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); + const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); + const auto yuv = m * ycbcr; + return ivec3(round(yuv * 255)); }; - EXPECT_EQ(Yuv8({{ 0, 128, 128}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{ 1, 128, 128}}), (ivec3{{1, 0, 0}})); + EXPECT_EQ(Yuv8({{0, 128, 128}}), (ivec3{{0, 0, 0}})); + EXPECT_EQ(Yuv8({{1, 128, 128}}), (ivec3{{1, 0, 0}})); EXPECT_EQ(Yuv8({{127, 128, 128}}), (ivec3{{127, 0, 0}})); EXPECT_EQ(Yuv8({{128, 128, 128}}), (ivec3{{128, 0, 0}})); EXPECT_EQ(Yuv8({{254, 128, 128}}), (ivec3{{254, 0, 0}})); @@ -74,84 +74,85 @@ TEST(Colorspaces, YcbcrDesc_Float) const auto m = YuvFromYcbcr(YcbcrDesc::Float()); const auto Yuv8 = [&](const vec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); + const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); + const auto yuv = m * ycbcr; + return ivec3(round(yuv * 255)); }; - EXPECT_EQ(Yuv8({{ 0, 0.5*255, 0.5*255}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{ 1, 0.5*255, 0.5*255}}), (ivec3{{1, 0, 0}})); - EXPECT_EQ(Yuv8({{127, 0.5*255, 0.5*255}}), (ivec3{{127, 0, 0}})); - EXPECT_EQ(Yuv8({{128, 0.5*255, 0.5*255}}), (ivec3{{128, 0, 0}})); - EXPECT_EQ(Yuv8({{254, 0.5*255, 0.5*255}}), (ivec3{{254, 0, 0}})); - EXPECT_EQ(Yuv8({{255, 0.5*255, 0.5*255}}), (ivec3{{255, 0, 0}})); + EXPECT_EQ(Yuv8({{0, 0.5 * 255, 0.5 * 255}}), (ivec3{{0, 0, 0}})); + EXPECT_EQ(Yuv8({{1, 0.5 * 255, 0.5 * 255}}), (ivec3{{1, 0, 0}})); + EXPECT_EQ(Yuv8({{127, 0.5 * 255, 0.5 * 255}}), (ivec3{{127, 0, 0}})); + EXPECT_EQ(Yuv8({{128, 0.5 * 255, 0.5 * 255}}), (ivec3{{128, 0, 0}})); + EXPECT_EQ(Yuv8({{254, 0.5 * 255, 0.5 * 255}}), (ivec3{{254, 0, 0}})); + EXPECT_EQ(Yuv8({{255, 0.5 * 255, 0.5 * 255}}), (ivec3{{255, 0, 0}})); } TEST(Colorspaces, ColorspaceTransform_Rec709Narrow) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {}, }; const auto ct = ColorspaceTransform::Create(src, dst); - EXPECT_EQ(Calc8From8(ct, {{ 16, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Calc8From8(ct, {{ 17, 128, 128}}), (ivec3{1})); + EXPECT_EQ(Calc8From8(ct, {{16, 128, 128}}), (ivec3{0})); + EXPECT_EQ(Calc8From8(ct, {{17, 128, 128}}), (ivec3{1})); EXPECT_EQ(Calc8From8(ct, {{126, 128, 128}}), (ivec3{128})); EXPECT_EQ(Calc8From8(ct, {{234, 128, 128}}), (ivec3{254})); EXPECT_EQ(Calc8From8(ct, {{235, 128, 128}}), (ivec3{255})); // Check that we get the naive out-of-bounds behavior we'd expect: - EXPECT_EQ(Calc8From8(ct, {{ 15, 128, 128}}), (ivec3{-1})); + EXPECT_EQ(Calc8From8(ct, {{15, 128, 128}}), (ivec3{-1})); EXPECT_EQ(Calc8From8(ct, {{236, 128, 128}}), (ivec3{256})); } TEST(Colorspaces, LutSample_Rec709Float) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Float()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Float()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {}, }; const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - EXPECT_EQ(Sample8From8(lut, {{ 0, 0.5*255, 0.5*255}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{ 1, 0.5*255, 0.5*255}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{127, 0.5*255, 0.5*255}}), (ivec3{127})); - EXPECT_EQ(Sample8From8(lut, {{128, 0.5*255, 0.5*255}}), (ivec3{128})); - EXPECT_EQ(Sample8From8(lut, {{254, 0.5*255, 0.5*255}}), (ivec3{254})); - EXPECT_EQ(Sample8From8(lut, {{255, 0.5*255, 0.5*255}}), (ivec3{255})); + EXPECT_EQ(Sample8From8(lut, {{0, 0.5 * 255, 0.5 * 255}}), (ivec3{0})); + EXPECT_EQ(Sample8From8(lut, {{1, 0.5 * 255, 0.5 * 255}}), (ivec3{1})); + EXPECT_EQ(Sample8From8(lut, {{127, 0.5 * 255, 0.5 * 255}}), (ivec3{127})); + EXPECT_EQ(Sample8From8(lut, {{128, 0.5 * 255, 0.5 * 255}}), (ivec3{128})); + EXPECT_EQ(Sample8From8(lut, {{254, 0.5 * 255, 0.5 * 255}}), (ivec3{254})); + EXPECT_EQ(Sample8From8(lut, {{255, 0.5 * 255, 0.5 * 255}}), (ivec3{255})); } TEST(Colorspaces, LutSample_Rec709Narrow) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {}, }; const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - EXPECT_EQ(Sample8From8(lut, {{ 16, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{ 17, 128, 128}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{(235+16)/2, 128, 128}}), (ivec3{127})); - EXPECT_EQ(Sample8From8(lut, {{(235+16)/2 + 1, 128, 128}}), (ivec3{128})); + EXPECT_EQ(Sample8From8(lut, {{16, 128, 128}}), (ivec3{0})); + EXPECT_EQ(Sample8From8(lut, {{17, 128, 128}}), (ivec3{1})); + EXPECT_EQ(Sample8From8(lut, {{int((235 + 16) / 2), 128, 128}}), (ivec3{127})); + EXPECT_EQ(Sample8From8(lut, {{int((235 + 16) / 2) + 1, 128, 128}}), + (ivec3{128})); EXPECT_EQ(Sample8From8(lut, {{234, 128, 128}}), (ivec3{254})); EXPECT_EQ(Sample8From8(lut, {{235, 128, 128}}), (ivec3{255})); } @@ -159,20 +160,20 @@ TEST(Colorspaces, LutSample_Rec709Narrow) TEST(Colorspaces, LutSample_Rec709Full) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {}, }; const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - EXPECT_EQ(Sample8From8(lut, {{ 0, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{ 1, 128, 128}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{ 16, 128, 128}}), (ivec3{16})); + EXPECT_EQ(Sample8From8(lut, {{0, 128, 128}}), (ivec3{0})); + EXPECT_EQ(Sample8From8(lut, {{1, 128, 128}}), (ivec3{1})); + EXPECT_EQ(Sample8From8(lut, {{16, 128, 128}}), (ivec3{16})); EXPECT_EQ(Sample8From8(lut, {{128, 128, 128}}), (ivec3{128})); EXPECT_EQ(Sample8From8(lut, {{235, 128, 128}}), (ivec3{235})); EXPECT_EQ(Sample8From8(lut, {{254, 128, 128}}), (ivec3{254})); @@ -183,59 +184,59 @@ TEST(Colorspaces, PiecewiseGammaDesc_Srgb) { const auto tf = PiecewiseGammaDesc::Srgb(); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x00 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x01 / 255.0) * 255 )), 0x0d); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x37 / 255.0) * 255 )), 0x80); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x80 / 255.0) * 255 )), 0xbc); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xfd / 255.0) * 255 )), 0xfe); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xfe / 255.0) * 255 )), 0xff); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xff / 255.0) * 255 )), 0xff); - - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x00 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x01 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x06 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x07 / 255.0) * 255 )), 0x01); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x0d / 255.0) * 255 )), 0x01); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x80 / 255.0) * 255 )), 0x37); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xbc / 255.0) * 255 )), 0x80); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xfe / 255.0) * 255 )), 0xfd); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xff / 255.0) * 255 )), 0xff); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x00 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x01 / 255.0) * 255)), 0x0d); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x37 / 255.0) * 255)), 0x80); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x80 / 255.0) * 255)), 0xbc); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfd / 255.0) * 255)), 0xfe); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfe / 255.0) * 255)), 0xff); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xff / 255.0) * 255)), 0xff); + + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x00 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x01 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x06 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x07 / 255.0) * 255)), 0x01); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x0d / 255.0) * 255)), 0x01); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x80 / 255.0) * 255)), 0x37); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xbc / 255.0) * 255)), 0x80); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xfe / 255.0) * 255)), 0xfd); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xff / 255.0) * 255)), 0xff); } TEST(Colorspaces, PiecewiseGammaDesc_Rec709) { const auto tf = PiecewiseGammaDesc::Rec709(); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x00 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x01 / 255.0) * 255 )), 0x05); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x43 / 255.0) * 255 )), 0x80); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0x80 / 255.0) * 255 )), 0xb4); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xfd / 255.0) * 255 )), 0xfe); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xfe / 255.0) * 255 )), 0xff); - EXPECT_EQ(int(roundf( TfFromLinear(tf, 0xff / 255.0) * 255 )), 0xff); - - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x00 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x01 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x02 / 255.0) * 255 )), 0x00); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x03 / 255.0) * 255 )), 0x01); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x05 / 255.0) * 255 )), 0x01); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0x80 / 255.0) * 255 )), 0x43); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xb4 / 255.0) * 255 )), 0x80); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xfe / 255.0) * 255 )), 0xfd); - EXPECT_EQ(int(roundf( LinearFromTf(tf, 0xff / 255.0) * 255 )), 0xff); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x00 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x01 / 255.0) * 255)), 0x05); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x43 / 255.0) * 255)), 0x80); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x80 / 255.0) * 255)), 0xb4); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfd / 255.0) * 255)), 0xfe); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfe / 255.0) * 255)), 0xff); + EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xff / 255.0) * 255)), 0xff); + + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x00 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x01 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x02 / 255.0) * 255)), 0x00); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x03 / 255.0) * 255)), 0x01); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x05 / 255.0) * 255)), 0x01); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x80 / 255.0) * 255)), 0x43); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xb4 / 255.0) * 255)), 0x80); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xfe / 255.0) * 255)), 0xfd); + EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xff / 255.0) * 255)), 0xff); } TEST(Colorspaces, ColorspaceTransform_PiecewiseGammaDesc) { const auto src = ColorspaceDesc{ - Chromaticities::Srgb(), - {}, - {}, + Chromaticities::Srgb(), + {}, + {}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, + Chromaticities::Srgb(), + PiecewiseGammaDesc::Srgb(), + {}, }; const auto toGamma = ColorspaceTransform::Create(src, dst); const auto toLinear = ColorspaceTransform::Create(dst, src); @@ -261,14 +262,14 @@ TEST(Colorspaces, ColorspaceTransform_PiecewiseGammaDesc) TEST(Colorspaces, SrgbFromRec709) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, + Chromaticities::Srgb(), + PiecewiseGammaDesc::Srgb(), + {}, }; const auto ct = ColorspaceTransform::Create(src, dst); @@ -283,21 +284,22 @@ TEST(Colorspaces, SrgbFromRec709) TEST(Colorspaces, SrgbFromDisplayP3) { const auto p3C = ColorspaceDesc{ - Chromaticities::DisplayP3(), - PiecewiseGammaDesc::DisplayP3(), + Chromaticities::DisplayP3(), + PiecewiseGammaDesc::DisplayP3(), }; const auto srgbC = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), + Chromaticities::Srgb(), + PiecewiseGammaDesc::Srgb(), }; const auto srgbLinearC = ColorspaceDesc{ - Chromaticities::Srgb(), - {}, + Chromaticities::Srgb(), + {}, }; const auto srgbFromP3 = ColorspaceTransform::Create(p3C, srgbC); const auto srgbLinearFromP3 = ColorspaceTransform::Create(p3C, srgbLinearC); - // E.g. https://colorjs.io/apps/convert/?color=color(display-p3%200.4%200.8%200.4)&precision=4 + // E.g. + // https://colorjs.io/apps/convert/?color=color(display-p3%200.4%200.8%200.4)&precision=4 auto srgb = srgbFromP3.DstFromSrc(vec3{{0.4, 0.8, 0.4}}); EXPECT_NEAR(srgb.x(), 0.179, 0.001); EXPECT_NEAR(srgb.y(), 0.812, 0.001); @@ -313,10 +315,10 @@ TEST(Colorspaces, SrgbFromDisplayP3) struct Stats { double mean = 0; double variance = 0; - double min = 1.0/0; - double max = -1.0/0; + double min = 1.0 / 0; + double max = -1.0 / 0; - template + template static Stats For(const T& iterable) { auto ret = Stats{}; for (const auto& cur : iterable) { @@ -332,17 +334,14 @@ struct Stats { return ret; } - constexpr double standardDeviation() const { - return sqrt(variance); - } + constexpr double standardDeviation() const { return sqrt(variance); } }; static Stats StatsForLutError(const ColorspaceTransform& ct, - const ivec3 srcQuants, - const ivec3 dstQuants) { + const ivec3 srcQuants, const ivec3 dstQuants) { const auto lut = ct.ToLut3(); - const auto dstScale = vec3(dstQuants-1); + const auto dstScale = vec3(dstQuants - 1); std::vector quantErrors; quantErrors.reserve(srcQuants.x() * srcQuants.y() * srcQuants.z()); @@ -366,14 +365,14 @@ static Stats StatsForLutError(const ColorspaceTransform& ct, TEST(Colorspaces, LutError_Rec709Full_Rec709Rgb) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {}, }; const auto ct = ColorspaceTransform::Create(src, dst); const auto stats = StatsForLutError(ct, ivec3{64}, ivec3{256}); @@ -386,14 +385,14 @@ TEST(Colorspaces, LutError_Rec709Full_Rec709Rgb) TEST(Colorspaces, LutError_Rec709Full_Srgb) { const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, + Chromaticities::Rec709(), + PiecewiseGammaDesc::Rec709(), + {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, }; const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, + Chromaticities::Srgb(), + PiecewiseGammaDesc::Srgb(), + {}, }; const auto ct = ColorspaceTransform::Create(src, dst); const auto stats = StatsForLutError(ct, ivec3{64}, ivec3{256}); diff --git a/gfx/layers/D3D9SurfaceImage.cpp b/gfx/layers/D3D9SurfaceImage.cpp index 52b71954d471..2c8037a03fa7 100644 --- a/gfx/layers/D3D9SurfaceImage.cpp +++ b/gfx/layers/D3D9SurfaceImage.cpp @@ -80,10 +80,10 @@ already_AddRefed DXGID3D9TextureData::GetD3D9Surface() } bool DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { - SurfaceDescriptorD3D10 desc( - (WindowsHandle)(mHandle), /* gpuProcessTextureId */ Nothing(), - /* arrayIndex */ 0, mFormat, GetSize(), gfx::ColorSpace2::SRGB, - gfx::ColorRange::FULL); + SurfaceDescriptorD3D10 desc((WindowsHandle)(mHandle), + /* gpuProcessTextureId */ Nothing(), + /* arrayIndex */ 0, mFormat, GetSize(), + gfx::ColorSpace2::SRGB, gfx::ColorRange::FULL); // In reality, with D3D9 we will only ever deal with RGBA textures. bool isYUV = mFormat == gfx::SurfaceFormat::NV12 || mFormat == gfx::SurfaceFormat::P010 || diff --git a/gfx/layers/d3d11/HelpersD3D11.h b/gfx/layers/d3d11/HelpersD3D11.h index 5289c40ac18d..ef40acd0a012 100644 --- a/gfx/layers/d3d11/HelpersD3D11.h +++ b/gfx/layers/d3d11/HelpersD3D11.h @@ -52,7 +52,7 @@ static inline bool WaitForFrameGPUQuery(ID3D11Device* aDevice, } inline void ClearResource(ID3D11Device* const device, ID3D11Resource* const res, - const std::array& vals) { + const std::array& vals) { RefPtr rtv; (void)device->CreateRenderTargetView(res, nullptr, getter_AddRefs(rtv)); diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 03a16ad04353..f7a2aae9165a 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -963,9 +963,9 @@ void DXGITextureHostD3D11::UnlockInternal() { void DXGITextureHostD3D11::CreateRenderTexture( const wr::ExternalImageId& aExternalImageId) { - RefPtr texture = new wr::RenderDXGITextureHost( - mHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mColorSpace, - mColorRange, mSize); + RefPtr texture = + new wr::RenderDXGITextureHost(mHandle, mGpuProcessTextureId, mArrayIndex, + mFormat, mColorSpace, mColorRange, mSize); wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, texture.forget()); } diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index 79b64dc7dbd7..162b74fd6872 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -652,7 +652,7 @@ GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { } bool DCLayerTree::EnsureVideoProcessorAtLeast(const gfx::IntSize& aInputSize, - const gfx::IntSize& aOutputSize) { + const gfx::IntSize& aOutputSize) { HRESULT hr; if (!mVideoDevice || !mVideoContext) { @@ -893,7 +893,8 @@ bool IsYuv(const gfx::SurfaceFormat aFormat) { MOZ_ASSERT_UNREACHABLE(); } -void DCSurfaceSwapChain::AttachExternalImage(wr::ExternalImageId aExternalImage) { +void DCSurfaceSwapChain::AttachExternalImage( + wr::ExternalImageId aExternalImage) { RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aExternalImage); MOZ_RELEASE_ASSERT(texture); @@ -1084,7 +1085,8 @@ static CspaceTransformPlan ChooseCspaceTransformPlan( // Let's do DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 + // DXGI_FORMAT_R10G10B10A2_UNORM - plan = { // Rec2020 g2.2 rgb10 + plan = { + // Rec2020 g2.2 rgb10 plan.srcSpace, {color::Chromaticities::Rec2020(), {color::PiecewiseGammaDesc::Srgb()}}, @@ -1092,10 +1094,11 @@ static CspaceTransformPlan ChooseCspaceTransformPlan( // gamma! DXGI_FORMAT_R10G10B10A2_UNORM, }; - plan = { // Actually, that doesn't work, so use scRGB g1.0 rgb16f + plan = { + // Actually, that doesn't work, so use scRGB g1.0 rgb16f plan.srcSpace, {color::Chromaticities::Rec709(), {}}, - DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // scRGB + DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // scRGB DXGI_FORMAT_R16G16B16A16_FLOAT, }; break; @@ -1122,14 +1125,21 @@ Maybe DCSurfaceSwapChain::EnsurePresented( // When video is rendered to axis aligned integer rectangle, video scaling // should be done by stretching in the VideoProcessor. - // Sotaro has observed a reduction in gpu queue tasks by doing scaling ourselves (via VideoProcessor) instead of having DComp handle it. + // Sotaro has observed a reduction in gpu queue tasks by doing scaling + // ourselves (via VideoProcessor) instead of having DComp handle it. if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() && aTransform.PreservesAxisAlignedRectangles()) { - const auto absScales = aTransform.ScaleFactors(); // E.g. [2, 0, 0, -2] => [2, 2] - const auto presentationTransformUnscaled = presentationTransform.Copy().PreScale(1 / absScales.xScale, 1 / absScales.yScale); - const auto dstSizeScaled = gfx::IntSize::Round(gfx::Size(dstSize) * aTransform.ScaleFactors()); - const auto presentSizeOld = presentationTransform.TransformSize(gfx::Size(dstSize)); - const auto presentSizeNew = presentationTransformUnscaled.TransformSize(gfx::Size(dstSizeScaled)); + const auto absScales = + aTransform.ScaleFactors(); // E.g. [2, 0, 0, -2] => [2, 2] + const auto presentationTransformUnscaled = + presentationTransform.Copy().PreScale(1 / absScales.xScale, + 1 / absScales.yScale); + const auto dstSizeScaled = + gfx::IntSize::Round(gfx::Size(dstSize) * aTransform.ScaleFactors()); + const auto presentSizeOld = + presentationTransform.TransformSize(gfx::Size(dstSize)); + const auto presentSizeNew = + presentationTransformUnscaled.TransformSize(gfx::Size(dstSizeScaled)); if (gfx::FuzzyEqual(presentSizeNew.width, presentSizeOld.width, 0.1f) && gfx::FuzzyEqual(presentSizeNew.height, presentSizeOld.height, 0.1f)) { dstSize = dstSizeScaled; @@ -1139,8 +1149,8 @@ Maybe DCSurfaceSwapChain::EnsurePresented( // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0 // subsampled formats like NV12 must have an even width and height. - // And we should be able to pad width and height because we clip the Visual to the unpadded rect. - // Just do this unconditionally. + // And we should be able to pad width and height because we clip the Visual to + // the unpadded rect. Just do this unconditionally. if (dstSize.width % 2 == 1) { dstSize.width += 1; } @@ -1511,7 +1521,7 @@ bool DCSurfaceSwapChain::CallBlitHelper() const { if (plan.srcSpace != plan.dstSpace) { if (!mDest->lut) { - mDest->lut = blitHelper->GetColorLutTex({ plan.srcSpace, plan.dstSpace }); + mDest->lut = blitHelper->GetColorLutTex({plan.srcSpace, plan.dstSpace}); } MOZ_ASSERT(mDest->lut); gl->fActiveTexture(LOCAL_GL_TEXTURE0 + LUT_TEX_UNIT); diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.h b/gfx/webrender_bindings/RenderD3D11TextureHost.h index 4a930b05a3a6..220899ec0585 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.h +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.h @@ -22,8 +22,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { RenderDXGITextureHost(WindowsHandle aHandle, Maybe& aGpuProcessTextureId, uint32_t aArrayIndex, gfx::SurfaceFormat aFormat, - gfx::ColorSpace2, - gfx::ColorRange aColorRange, gfx::IntSize aSize); + gfx::ColorSpace2, gfx::ColorRange aColorRange, + gfx::IntSize aSize); wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL, wr::ImageRendering aRendering) override; -- 2.11.4.GIT