Bug 1771374 - Fix build and lint warnings. r=gfx-reviewers,aosmond

Differential Revision: https://phabricator.services.mozilla.com/D150620
This commit is contained in:
Kelsey Gilbert 2022-08-01 19:49:27 +00:00
parent 5ebff89891
commit 0bd05e8c5c
11 changed files with 799 additions and 830 deletions

View File

@ -11,90 +11,90 @@
namespace mozilla::gfx {
template<class T>
template <class T>
size_t Hash(const T&);
template<class T>
template <class T>
struct StaticStdHasher {
static auto HashImpl(const T& v) {
return std::hash<T>()(v);
}
static auto HashImpl(const T& v) { return std::hash<T>()(v); }
};
template<class T>
template <class T>
struct StaticHasher {
static auto HashImpl(const T& v) {
return v.hash();
}
static auto HashImpl(const T& v) { return v.hash(); }
};
template<class T>
template <class T>
struct StaticHasher<std::optional<T>> {
static size_t HashImpl(const std::optional<T>& v) {
if (!v) return 0;
return Hash(*v);
}
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 <>
struct StaticHasher<int> : public StaticStdHasher<int> {};
template <>
struct StaticHasher<bool> : public StaticStdHasher<bool> {};
template <>
struct StaticHasher<float> : public StaticStdHasher<float> {};
template<class T>
template <class T>
size_t Hash(const T& v) {
return StaticHasher<T>::HashImpl(v);
return StaticHasher<T>::HashImpl(v);
}
//-
// From Boost: https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine
// 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;
seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
// -
// See https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17
// See
// https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17
template<class... Args, size_t... Ids>
size_t HashTupleN(const std::tuple<Args...>& tup, const std::index_sequence<Ids...>&) {
size_t seed = 0;
for (const auto& hash : {Hash(std::get<Ids>(tup))...}) {
seed = HashCombine(seed, hash);
}
return seed;
template <class... Args, size_t... Ids>
size_t HashTupleN(const std::tuple<Args...>& tup,
const std::index_sequence<Ids...>&) {
size_t seed = 0;
for (const auto& hash : {Hash(std::get<Ids>(tup))...}) {
seed = HashCombine(seed, hash);
}
return seed;
}
template<class... Args>
template <class... Args>
size_t HashTuple(const std::tuple<Args...>& tup) {
return HashTupleN(tup, std::make_index_sequence<sizeof...(Args)>());
return HashTupleN(tup, std::make_index_sequence<sizeof...(Args)>());
}
// -
template<class T>
template <class T>
auto MembersEq(const T& a, const T& b) {
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
}
template<class T>
template <class T>
auto MembersLt(const T& a, const T& b) {
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
const auto atup = a.Members();
const auto btup = b.Members();
return atup == btup;
}
template<class T>
template <class T>
auto MembersHash(const T& a) {
const auto atup = a.Members();
return HashTuple(atup);
const auto atup = a.Members();
return HashTuple(atup);
}
template<class T>
template <class T>
struct MembersHasher final {
auto operator()(const T& v) const {
return v.hash();
}
auto operator()(const T& v) const { return v.hash(); }
};
/** E.g.:
@ -107,22 +107,20 @@ struct 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>;
#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>;
// -
@ -140,9 +138,9 @@ std::unordered_set<T, T::Hasher> easy;
The initial {} is needed for aggregate initialization of AutoMappable<Foo>.
Use INLINE_AUTO_MAPPABLE if this is too annoying.
**/
template<class T>
template <class T>
struct AutoMappable {
INLINE_AUTO_MAPPABLE(T)
INLINE_AUTO_MAPPABLE(T)
};
} // namespace mozilla::gfx

View File

@ -12,231 +12,218 @@ 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;
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;
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;
// 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())});
// 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);
// 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 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;
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
// 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)
// Given red (xr, yr), green (xg, yg), blue (xb, yb),
// and whitepoint (XW, YW, ZW)
// [ X ] [ R ]
// [ Y ] = M x [ G ]
// [ Z ] [ B ]
// [ 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 ]
// [ 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
// Xr = xr / yr
// Yr = 1
// Zr = (1 - xr - yr) / yr
// Xg = xg / yg
// Yg = 1
// Zg = (1 - xg - yg) / yg
// Xg = xg / yg
// Yg = 1
// Zg = (1 - xg - yg) / yg
// Xb = xb / yb
// Yb = 1
// Zb = (1 - xb - yb) / yb
// 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 ]
// [ 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 = 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 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 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;
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;
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 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;
};
const auto rgbFromYcbcr = rgbFromYuv4 * yuvFromYcbcr;
rgbFrom = rgbFromYcbcr;
}
return rgbFrom;
};
ct.srcRgbTfFromSrc = RgbTfFrom(src);
const auto dstRgbTfFromDst = RgbTfFrom(dst);
ct.dstFromDstRgbTf = inverse(dstRgbTfFromDst);
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;
}
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;
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 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 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);
const auto dst4 = dstFromDstRgbTf * vec4(dstRgbTf, 1);
return vec3(dst4);
}
// -
std::optional<mat4> ColorspaceTransform::ToMat4() const {
mat4 fromSrc = srcRgbTfFromSrc;
if (srcTf) return {};
fromSrc = mat4(dstRgbLinFromSrcRgbLin) * fromSrc;
if (dstTf) return {};
fromSrc = dstFromDstRgbTf * fromSrc;
return fromSrc;
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;
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);
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}));
// 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 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 fxy0 = mix(fx00, fx10, dp.y());
const auto fxy1 = mix(fx01, fx11, dp.y());
const auto fxyz = mix(fxy0, fxy1, dp.z());
return fxyz;
const auto fxyz = mix(fxy0, fxy1, dp.z());
return fxyz;
}
} // namespace mozilla::color

View File

@ -15,456 +15,424 @@
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdlib>
#include <optional>
#include <vector>
#include "AutoMappable.h"
#include "mozilla/Attributes.h"
#ifdef DEBUG
#define ASSERT(EXPR) do { \
if (!(EXPR)) { \
# define ASSERT(EXPR) \
do { \
if (!(EXPR)) { \
__builtin_trap(); \
} \
} \
} while (false)
#else
#define ASSERT(EXPR) (void)(EXPR)
# define ASSERT(EXPR) (void)(EXPR)
#endif
namespace mozilla::color {
struct YuvLumaCoeffs final {
float r = 0.2126;
float g = 0.7152;
float b = 0.0722;
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)
auto Members() const { return std::tie(r, g, b); }
INLINE_AUTO_MAPPABLE(YuvLumaCoeffs)
static constexpr auto Rec709() {
return YuvLumaCoeffs();
}
static constexpr auto Rec709() { return YuvLumaCoeffs(); }
static constexpr auto Rec2020() {
return YuvLumaCoeffs{0.2627, 0.6780, 0.0593};
}
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
// 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;
// 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)
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 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 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,
};
}
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;
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)
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};
}
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;
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)
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 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
};
}
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;
YuvLumaCoeffs yCoeffs;
YcbcrDesc ycbcr;
auto Members() const {
return std::tie(yCoeffs, ycbcr);
}
INLINE_AUTO_MAPPABLE(YuvDesc);
auto Members() const { return std::tie(yCoeffs, ycbcr); }
INLINE_AUTO_MAPPABLE(YuvDesc);
};
struct ColorspaceDesc final {
Chromaticities chrom;
std::optional<PiecewiseGammaDesc> tf;
std::optional<YuvDesc> yuv;
Chromaticities chrom;
std::optional<PiecewiseGammaDesc> tf;
std::optional<YuvDesc> yuv;
auto Members() const {
return std::tie(chrom, tf, yuv);
}
INLINE_AUTO_MAPPABLE(ColorspaceDesc);
auto Members() const { return std::tie(chrom, tf, yuv); }
INLINE_AUTO_MAPPABLE(ColorspaceDesc);
};
// -
template<class TT, int NN>
template <class TT, int NN>
struct avec final {
using T = TT;
static constexpr auto N = NN;
using T = TT;
static constexpr auto N = NN;
std::array<T,N> data = {};
std::array<T, N> data = {};
// -
// -
constexpr avec() = default;
constexpr avec(const avec&) = default;
constexpr avec() = default;
constexpr avec(const avec&) = default;
constexpr avec(const avec<T, N-1>& v, T a) {
for (int i = 0; i < N-1; i++) {
data[i] = v[i];
}
data[N-1] = a;
}
constexpr avec(const avec<T, N-2>& 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;
}
constexpr avec(const avec<T, N - 1>& v, T a) {
for (int i = 0; i < N - 1; i++) {
data[i] = v[i];
}
data[N - 1] = a;
}
constexpr avec(const avec<T, N - 2>& 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<T,N>& data) {
this->data = data;
}
MOZ_IMPLICIT constexpr avec(const std::array<T, N>& data) {
this->data = data;
}
explicit constexpr avec(const T v) {
for (int i = 0; i < N; i++) {
data[i] = v;
}
}
explicit constexpr avec(const T v) {
for (int i = 0; i < N; i++) {
data[i] = v;
}
}
template<class T2, int N2>
explicit constexpr avec(const avec<T2, N2>& v) {
const auto n = std::min(N, N2);
for (int i = 0; i < n; i++) {
data[i] = static_cast<T>(v[i]);
}
}
template <class T2, int N2>
explicit constexpr avec(const avec<T2, N2>& v) {
const auto n = std::min(N, N2);
for (int i = 0; i < n; i++) {
data[i] = static_cast<T>(v[i]);
}
}
// -
// -
const auto& operator[](const size_t n) const { return data[n]; }
auto& operator[](const size_t n) { return data[n]; }
const auto& operator[](const size_t n) const { return data[n]; }
auto& operator[](const size_t n) { return data[n]; }
template<int i>
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>(); }
template <int i>
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()});
}
constexpr auto xyz() const { return vec3({x(), y(), z()}); }
template<int i>
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); }
template <int i>
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 _
#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;
}
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<float, 3>;
using vec4 = avec<float, 4>;
using ivec3 = avec<int32_t, 3>;
using ivec4 = avec<int32_t, 4>;
template<class T, int N>
template <class T, int N>
T dot(const avec<T, N>& a, const avec<T, N>& b) {
const auto c = a * b;
T ret = 0;
for (int i = 0; i < N; i++) {
ret += c[i];
}
return ret;
const auto c = a * b;
T ret = 0;
for (int i = 0; i < N; i++) {
ret += c[i];
}
return ret;
}
template<class V>
template <class V>
V mix(const V& zero, const V& one, const float val) {
return zero * (1 - val) + one * val;
return zero * (1 - val) + one * val;
}
template<class T, int N>
template <class T, int N>
auto min(const avec<T, N>& a, const avec<T, N>& b) {
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = std::min(a[i], b[i]);
}
return ret;
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = std::min(a[i], b[i]);
}
return ret;
}
template<class T, int N>
template <class T, int N>
auto max(const avec<T, N>& a, const avec<T, N>& b) {
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = std::max(a[i], b[i]);
}
return ret;
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = std::max(a[i], b[i]);
}
return ret;
}
template<class T, int N>
template <class T, int N>
auto floor(const avec<T, N>& a) {
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = floorf(a[i]);
}
return ret;
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = floorf(a[i]);
}
return ret;
}
template<class T, int N>
template <class T, int N>
auto round(const avec<T, N>& a) {
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = roundf(a[i]);
}
return ret;
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = roundf(a[i]);
}
return ret;
}
template<class T>
inline auto t_abs(const T& a) {
return abs(a);
}
template<>
inline auto t_abs<float>(const float& a) {
return fabs(a);
}
template<class T, int N>
template <class T, int N>
auto abs(const avec<T, N>& a) {
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = t_abs(a[i]);
}
return ret;
auto ret = avec<T, N>{};
for (int i = 0; i < ret.N; i++) {
ret[i] = std::abs(a[i]);
}
return ret;
}
// -
template<int Y_Rows, int X_Cols>
template <int Y_Rows, int X_Cols>
struct mat final {
static constexpr int y_rows = Y_Rows;
static constexpr int x_cols = X_Cols;
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);
}
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;
}
}
return ret;
}
std::array<avec<float, X_Cols>, Y_Rows> rows = {}; // row-major
std::array<avec<float, X_Cols>, Y_Rows> rows = {}; // row-major
// -
// -
constexpr mat() {}
constexpr mat() = default;
explicit constexpr mat(const std::array<avec<float, X_Cols>, Y_Rows>& rows) {
this->rows = rows;
}
explicit constexpr mat(const std::array<avec<float, X_Cols>, Y_Rows>& rows) {
this->rows = rows;
}
template<int Y_Rows2, int X_Cols2>
explicit constexpr mat(const mat<Y_Rows2, X_Cols2>& 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);
}
template <int Y_Rows2, int X_Cols2>
explicit constexpr mat(const mat<Y_Rows2, X_Cols2>& 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]; }
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<float, X_Cols>& b_colvec) {
avec<float, Y_Rows> c_colvec;
for (int i = 0; i < y_rows; i++) {
c_colvec[i] = dot(a.rows.at(i), b_colvec);
friend auto operator*(const mat& a, const avec<float, X_Cols>& b_colvec) {
avec<float, Y_Rows> 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_colvec;
}
}
return c;
}
friend auto operator/(const mat& a, const float b) { return a * (1 / b); }
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;
}
template <int BCols, int BRows = X_Cols>
friend auto operator*(const mat& a, const mat<BRows, BCols>& b) {
const auto bt = transpose(b);
const auto& b_cols = bt.rows;
mat<Y_Rows, BCols> 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;
}
friend auto operator/(const mat& a, const float b) {
return a * (1 / b);
}
template<int BCols, int BRows = X_Cols>
friend auto operator*(const mat& a,
const mat<BRows, BCols>& b) {
const auto bt = transpose(b);
const auto& b_cols = bt.rows;
mat<Y_Rows, BCols> 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;
}
}
return c;
}
};
using mat3 = mat<3,3>;
using mat4 = mat<4,4>;
using mat3 = mat<3, 3>;
using mat4 = mat<4, 4>;
inline float determinant(const mat<1,1>& m) {
return m.at(0,0);
}
template<class T>
inline float determinant(const mat<1, 1>& m) { return m.at(0, 0); }
template <class T>
float determinant(const T& m) {
static_assert(T::x_cols == T::y_rows);
@ -478,13 +446,13 @@ float determinant(const T& m) {
// -
template<class T>
template <class T>
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) {
if ((x_col + y_row) % 2 == 1) {
cofactor *= -1;
}
return cofactor;
@ -495,13 +463,13 @@ float cofactor(const T& m, const int x_col, const int y_row) {
// 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<class T>
template <class T>
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<T::y_rows-1, T::x_cols-1> b;
mat<T::y_rows - 1, T::x_cols - 1> b;
int x_skips = 0;
for (int ax = 0; ax < T::x_cols; ax++) {
@ -528,7 +496,7 @@ float minor_val(const T& a, const int skip_x, const int skip_y) {
// -
/// The matrix of cofactors.
template<class T>
template <class T>
auto comatrix(const T& a) {
auto b = T{};
for (int x = 0; x < T::x_cols; x++) {
@ -541,7 +509,7 @@ auto comatrix(const T& a) {
// -
template<class T>
template <class T>
auto transpose(const T& a) {
auto b = mat<T::x_cols, T::y_rows>{};
for (int x = 0; x < T::x_cols; x++) {
@ -554,7 +522,7 @@ auto transpose(const T& a) {
// -
template<class T>
template <class T>
inline T inverse(const T& a) {
const auto det = determinant(a);
const auto comat = comatrix(a);
@ -565,76 +533,72 @@ inline T inverse(const T& a) {
// -
template<class F>
template <class F>
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);
}
}
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<class F>
template <class F>
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);
});
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<vec3> data;
ivec3 size;
std::vector<vec3> data;
// -
// -
static Lut3 Create(const ivec3 size) {
Lut3 lut;
lut.size = size;
lut.data.resize(size.x() * size.y() * size.z());
return lut;
}
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);
}
/// 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<class F>
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 {
template <class F>
void SetMap(const F& dstFromSrc01) {
ForEachIntWithin(size, [&](const ivec3 p) {
const auto i = Index(p);
return data.at(i);
}
const auto src01 = vec3(p) / vec3(size - 1);
const auto dstVal = dstFromSrc01(src01);
data.at(i) = dstVal;
});
}
/// in01: [0.0, 1.0] (clamps)
vec3 Sample(vec3 in01) const;
// -
/// 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;
};
// -
@ -660,31 +624,31 @@ before adding complexity!
**/
struct ColorspaceTransform final {
ColorspaceDesc srcSpace;
ColorspaceDesc dstSpace;
mat4 srcRgbTfFromSrc;
std::optional<PiecewiseGammaDesc> srcTf;
mat3 dstRgbLinFromSrcRgbLin;
std::optional<PiecewiseGammaDesc> dstTf;
mat4 dstFromDstRgbTf;
ColorspaceDesc srcSpace;
ColorspaceDesc dstSpace;
mat4 srcRgbTfFromSrc;
std::optional<PiecewiseGammaDesc> srcTf;
mat3 dstRgbLinFromSrcRgbLin;
std::optional<PiecewiseGammaDesc> dstTf;
mat4 dstFromDstRgbTf;
static ColorspaceTransform Create(const ColorspaceDesc& src,
const ColorspaceDesc& dst);
static ColorspaceTransform Create(const ColorspaceDesc& src,
const ColorspaceDesc& dst);
// -
// -
vec3 DstFromSrc(vec3 src) const;
vec3 DstFromSrc(vec3 src) const;
std::optional<mat4> ToMat4() const;
std::optional<mat4> 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);
}
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

View File

@ -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"
@ -129,13 +130,14 @@ const char* const kFragSample_OnePlane = R"(
)";
// Ideally this would just change the color-matrix it uses, but this is
// acceptable debt for now.
const char* const kFragSample_OnePlane_YUV_via_GBR = R"(
// `extern` so that we don't get ifdef-dependent const-var-unused Werrors.
extern const char* const kFragSample_OnePlane_YUV_via_GBR = R"(
VARYING mediump vec2 vTexCoord0;
uniform PRECISION SAMPLER uTex0;
vec4 metaSample() {
vec4 src = TEXTURE(uTex0, vTexCoord0);
return src;
vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
return yuva;
}
)";
const char* const kFragSample_TwoPlane = R"(
@ -274,7 +276,7 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl,
MOZ_CRASH();
}
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)) {
@ -286,7 +288,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)) {
@ -488,7 +493,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<GLint>(*args.texUnitForColorLut));
}
MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
@ -601,9 +607,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 {
@ -718,7 +726,8 @@ const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg(
}
parts.push_back(kFragBody);
}
mGL->fShaderSource(fs, parts.size(), parts.data(), nullptr);
mGL->fShaderSource(fs, AssertedCast<GLint>(parts.size()), parts.data(),
nullptr);
mGL->fCompileShader(fs);
const auto prog = mGL->fCreateProgram();
@ -938,7 +947,7 @@ bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture,
const auto srcOrigin = OriginPos::TopLeft;
const bool yFlip = (srcOrigin != destOrigin);
const auto& prog = GetDrawBlitProg(
{kFragHeader_TexExt, kFragSample_OnePlane, kFragConvert_None});
{kFragHeader_TexExt, {kFragSample_OnePlane, kFragConvert_None}});
const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, destSize,
Nothing()};
prog->Draw(baseArgs, nullptr);
@ -1227,8 +1236,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;
@ -1529,15 +1537,15 @@ std::shared_ptr<gl::Texture> 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;

View File

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

View File

@ -6,6 +6,8 @@
#include "gtest/gtest.h"
#include "Colorspaces.h"
#include <limits>
namespace mozilla::color {
mat4 YuvFromYcbcr(const YcbcrDesc&);
float TfFromLinear(const PiecewiseGammaDesc&, float linear);
@ -33,13 +35,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 +49,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 +58,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 +76,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 +162,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 +186,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(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(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(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(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 +264,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 +286,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 +317,10 @@ TEST(Colorspaces, SrgbFromDisplayP3)
struct Stats {
double mean = 0;
double variance = 0;
double min = 1.0/0;
double max = -1.0/0;
double min = std::numeric_limits<double>::infinity();
double max = -std::numeric_limits<double>::infinity();
template<class T>
template <class T>
static Stats For(const T& iterable) {
auto ret = Stats{};
for (const auto& cur : iterable) {
@ -332,17 +336,14 @@ struct Stats {
return ret;
}
constexpr double standardDeviation() const {
return sqrt(variance);
}
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<double> quantErrors;
quantErrors.reserve(srcQuants.x() * srcQuants.y() * srcQuants.z());
@ -366,14 +367,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 +387,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});

View File

@ -80,10 +80,10 @@ already_AddRefed<IDirect3DSurface9> 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 ||

View File

@ -52,7 +52,7 @@ static inline bool WaitForFrameGPUQuery(ID3D11Device* aDevice,
}
inline void ClearResource(ID3D11Device* const device, ID3D11Resource* const res,
const std::array<float,4>& vals) {
const std::array<float, 4>& vals) {
RefPtr<ID3D11RenderTargetView> rtv;
(void)device->CreateRenderTargetView(res, nullptr, getter_AddRefs(rtv));

View File

@ -963,9 +963,9 @@ void DXGITextureHostD3D11::UnlockInternal() {
void DXGITextureHostD3D11::CreateRenderTexture(
const wr::ExternalImageId& aExternalImageId) {
RefPtr<wr::RenderTextureHost> texture = new wr::RenderDXGITextureHost(
mHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mColorSpace,
mColorRange, mSize);
RefPtr<wr::RenderTextureHost> texture =
new wr::RenderDXGITextureHost(mHandle, mGpuProcessTextureId, mArrayIndex,
mFormat, mColorSpace, mColorRange, mSize);
wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId,
texture.forget());
}

View File

@ -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<gfx::Matrix> 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<gfx::Matrix> 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);

View File

@ -22,8 +22,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL {
RenderDXGITextureHost(WindowsHandle aHandle,
Maybe<uint64_t>& 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;