mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1771374 - Fix build and lint warnings. r=gfx-reviewers,aosmond
Differential Revision: https://phabricator.services.mozilla.com/D150620
This commit is contained in:
parent
5ebff89891
commit
0bd05e8c5c
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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});
|
||||
|
@ -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 ||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user