diff --git a/dom/media/test/reftest/color_quads/reftest.list b/dom/media/test/reftest/color_quads/reftest.list index a5ad475b879b6498d3d9a08a072636791c25f85e..98df160be672cae30012192d7d60d9ee6030cd03 100644 --- a/dom/media/test/reftest/color_quads/reftest.list +++ b/dom/media/test/reftest/color_quads/reftest.list @@ -17,7 +17,7 @@ defaults pref(media.av1.enabled,true) fuzzy(16-51,5234-5622) fuzzy-if(swgl,32-38,1600-91746) fuzzy-if(useDrawSnapshot,16-16,11600-11600) fuzzy-if(OSX,16-73,5212-5622) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(winWidget&&!swgl,0-1,0-78) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm fuzzy-if(winWidget,0-1,0-78) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm -skip-if(winWidget&&isCoverageBuild) fuzzy(0-16,75-1941) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,30-32,187326-187407) fuzzy-if(appleSilicon,30-48,1835-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm +skip-if(winWidget&&isCoverageBuild) fuzzy(0-16,75-1861) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,30-32,187326-187407) fuzzy-if(appleSilicon,30-48,1835-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(winWidget&&!swgl,0-1,0-78) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm skip-if(Android) fuzzy(16-48,8107-8818) fuzzy-if(winWidget&&swgl,31-38,8240-184080) fuzzy-if(appleSilicon,33-38,8819-11705) fuzzy-if(useDrawSnapshot,20-20,187200-187200) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 5f027eb6d3943519a834af2d9a265d72397eb156..fc3a910daa3c4c9ee2dc70375cbbb6dfb80b84ef 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -498,8 +498,7 @@ enum class YUVRangedColorSpace : uint8_t { // one. // Some times Worse Is Better. enum class ColorSpace2 : uint8_t { - Display, - UNKNOWN = Display, // We feel sufficiently bad about this TODO. + UNKNOWN, // Really "DISPLAY". Eventually we will remove this. SRGB, DISPLAY_P3, BT601_525, // aka smpte170m NTSC @@ -507,7 +506,7 @@ enum class ColorSpace2 : uint8_t { BT601_625 = BT709, // aka bt470bg PAL. Basically BT709, just Xg is 0.290 not 0.300. BT2020, - _First = Display, + _First = UNKNOWN, _Last = BT2020, }; diff --git a/gfx/gl/Colorspaces.cpp b/gfx/gl/Colorspaces.cpp index c7c40b2abaa8da8a9e79e7063d3c9d4bda9c3717..8ef15a58d77d2486186be46b8d851641410c0821 100644 --- a/gfx/gl/Colorspaces.cpp +++ b/gfx/gl/Colorspaces.cpp @@ -7,9 +7,6 @@ #include "Colorspaces.h" -#include "nsDebug.h" -#include "qcms.h" - namespace mozilla::color { // tf = { k * linear | linear < b @@ -79,12 +76,6 @@ mat4 YuvFromYcbcr(const YcbcrDesc& d) { return yuvFromYcbcr; } -inline vec3 CIEXYZ_from_CIExyY(const vec2 xy, const float Y = 1) { - const auto xyz = vec3(xy, 1 - xy.x() - xy.y()); - const auto XYZ = xyz * (Y / xy.y()); - return XYZ; -} - mat3 XyzFromLinearRgb(const Chromaticities& c) { // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html @@ -124,7 +115,7 @@ mat3 XyzFromLinearRgb(const Chromaticities& c) { 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}) / c.wy; + 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}); @@ -192,29 +183,6 @@ vec3 ColorspaceTransform::DstFromSrc(const vec3 src) const { // - -mat3 XyzAFromXyzB_BradfordLinear(const vec2 xyA, const vec2 xyB) { - // This is what ICC profiles use to do whitepoint transforms, - // because ICC also requires D50 for the Profile Connection Space. - - // From https://www.color.org/specification/ICC.1-2022-05.pdf - // E.3 "Linearized Bradford transformation": - - constexpr auto M_BFD = mat3{{ - vec3{{0.8951, 0.2664f, -0.1614f}}, - vec3{{-0.7502f, 1.7135f, 0.0367f}}, - vec3{{0.0389f, -0.0685f, 1.0296f}}, - }}; - // NB: They use rho/gamma/beta, but we'll use R/G/B here. - const auto XYZDst = CIEXYZ_from_CIExyY(xyA); // "XYZ_W", WP of PCS - const auto XYZSrc = CIEXYZ_from_CIExyY(xyB); // "XYZ_NAW", WP of src - const auto rgbSrc = M_BFD * XYZSrc; // "RGB_SRC" - const auto rgbDst = M_BFD * XYZDst; // "RGB_PCS" - const auto rgbDstOverSrc = rgbDst / rgbSrc; - const auto M_dstOverSrc = mat3::Scale(rgbDstOverSrc); - const auto M_adapt = inverse(M_BFD) * M_dstOverSrc * M_BFD; - return M_adapt; -} - std::optional<mat4> ColorspaceTransform::ToMat4() const { mat4 fromSrc = srcRgbTfFromSrc; if (srcTf) return {}; @@ -258,178 +226,4 @@ vec3 Lut3::Sample(const vec3 in01) const { return fxyz; } -// - - -ColorProfileDesc ColorProfileDesc::From(const ColorspaceDesc& cspace) { - auto ret = ColorProfileDesc{}; - - if (cspace.yuv) { - const auto yuvFromYcbcr = YuvFromYcbcr(cspace.yuv->ycbcr); - const auto yuvFromRgb = YuvFromRgb(cspace.yuv->yCoeffs); - const auto rgbFromYuv = inverse(yuvFromRgb); - ret.rgbFromYcbcr = mat4(rgbFromYuv) * yuvFromYcbcr; - } - - if (cspace.tf) { - const size_t tableSize = 256; - auto& tableR = ret.linearFromTf.r; - tableR.resize(tableSize); - for (size_t i = 0; i < tableR.size(); i++) { - const float tfVal = i / float(tableR.size() - 1); - const float linearVal = LinearFromTf(*cspace.tf, tfVal); - tableR[i] = linearVal; - } - ret.linearFromTf.g = tableR; - ret.linearFromTf.b = tableR; - } - - ret.xyzd65FromLinearRgb = XyzFromLinearRgb(cspace.chrom); - - return ret; -} - -// - - -template <class T> -constexpr inline T NewtonEstimateX(const T x1, const T y1, const T dydx, - const T y2 = 0) { - // Estimate x s.t. y=0 - // y = y0 + x*dydx; - // y0 = y - x*dydx; - // y1 - x1*dydx = y2 - x2*dydx - // x2*dydx = y2 - y1 + x1*dydx - // x2 = (y2 - y1)/dydx + x1 - return (y2 - y1) / dydx + x1; -} - -float GuessGamma(const std::vector<float>& vals, float exp_guess) { - // Approximate (signed) error = 0.0. - constexpr float d_exp = 0.001; - constexpr float error_tolerance = 0.001; - struct Samples { - float y1, y2; - }; - const auto Sample = [&](const float exp) { - int i = -1; - auto samples = Samples{}; - for (const auto& expected : vals) { - i += 1; - const auto in = i / float(vals.size() - 1); - samples.y1 += powf(in, exp) - expected; - samples.y2 += powf(in, exp + d_exp) - expected; - } - samples.y1 /= vals.size(); // Normalize by val count. - samples.y2 /= vals.size(); - return samples; - }; - constexpr int MAX_ITERS = 10; - for (int i = 1;; i++) { - const auto err = Sample(exp_guess); - const auto derr = err.y2 - err.y1; - exp_guess = NewtonEstimateX(exp_guess, err.y1, derr / d_exp); - // Check if we were close before, because then this last round of estimation - // should get us pretty much right on it. - if (std::abs(err.y1) < error_tolerance) { - return exp_guess; - } - if (i >= MAX_ITERS) { - printf_stderr("GuessGamma() -> %f after %i iterations (avg err %f)\n", - exp_guess, i, err.y1); - MOZ_ASSERT(false, "GuessGamma failed."); - return exp_guess; - } - } -} - -// - - -ColorProfileDesc ColorProfileDesc::From(const qcms_profile& qcmsProfile) { - ColorProfileDesc ret; - - qcms_profile_data data = {}; - qcms_profile_get_data(&qcmsProfile, &data); - - auto xyzd50FromLinearRgb = mat3{}; - // X contributions from [R,G,B] - xyzd50FromLinearRgb.at(0, 0) = data.red_colorant_xyzd50[0]; - xyzd50FromLinearRgb.at(1, 0) = data.green_colorant_xyzd50[0]; - xyzd50FromLinearRgb.at(2, 0) = data.blue_colorant_xyzd50[0]; - // Y contributions from [R,G,B] - xyzd50FromLinearRgb.at(0, 1) = data.red_colorant_xyzd50[1]; - xyzd50FromLinearRgb.at(1, 1) = data.green_colorant_xyzd50[1]; - xyzd50FromLinearRgb.at(2, 1) = data.blue_colorant_xyzd50[1]; - // Z contributions from [R,G,B] - xyzd50FromLinearRgb.at(0, 2) = data.red_colorant_xyzd50[2]; - xyzd50FromLinearRgb.at(1, 2) = data.green_colorant_xyzd50[2]; - xyzd50FromLinearRgb.at(2, 2) = data.blue_colorant_xyzd50[2]; - - const auto d65FromD50 = XyzAFromXyzB_BradfordLinear(D65, D50); - ret.xyzd65FromLinearRgb = d65FromD50 * xyzd50FromLinearRgb; - - // - - - const auto Fn = [&](std::vector<float>* const linearFromTf, - int32_t claimed_samples, - const qcms_color_channel channel) { - if (claimed_samples == 0) return; // No tf. - - if (claimed_samples == -1) { - claimed_samples = 4096; // Ask it to generate a bunch. - claimed_samples = 256; // Ask it to generate a bunch. - } - - linearFromTf->resize(AssertedCast<size_t>(claimed_samples)); - - const auto begin = linearFromTf->data(); - qcms_profile_get_lut(&qcmsProfile, channel, begin, - begin + linearFromTf->size()); - }; - - Fn(&ret.linearFromTf.r, data.linear_from_trc_red_samples, - qcms_color_channel::Red); - Fn(&ret.linearFromTf.b, data.linear_from_trc_blue_samples, - qcms_color_channel::Blue); - Fn(&ret.linearFromTf.g, data.linear_from_trc_green_samples, - qcms_color_channel::Green); - - // - - - return ret; -} - -// - - -ColorProfileConversionDesc ColorProfileConversionDesc::From( - const FromDesc& desc) { - const auto dstLinearRgbFromXyzd65 = inverse(desc.dst.xyzd65FromLinearRgb); - auto ret = ColorProfileConversionDesc{ - .srcRgbFromSrcYuv = desc.src.rgbFromYcbcr, - .srcLinearFromSrcTf = desc.src.linearFromTf, - .dstLinearFromSrcLinear = - dstLinearRgbFromXyzd65 * desc.src.xyzd65FromLinearRgb, - .dstTfFromDstLinear = {}, - }; - bool sameTF = true; - sameTF &= desc.src.linearFromTf.r == desc.dst.linearFromTf.r; - sameTF &= desc.src.linearFromTf.g == desc.dst.linearFromTf.g; - sameTF &= desc.src.linearFromTf.b == desc.dst.linearFromTf.b; - if (sameTF) { - ret.srcLinearFromSrcTf = {}; - ret.dstTfFromDstLinear = {}; - } else { - const auto Invert = [](const std::vector<float>& linearFromTf, - std::vector<float>* const tfFromLinear) { - const auto size = linearFromTf.size(); - MOZ_ASSERT(size != 1); // Less than two is uninvertable. - if (size < 2) return; - (*tfFromLinear).resize(size); - InvertLut(linearFromTf, &*tfFromLinear); - }; - Invert(desc.dst.linearFromTf.r, &ret.dstTfFromDstLinear.r); - Invert(desc.dst.linearFromTf.g, &ret.dstTfFromDstLinear.g); - Invert(desc.dst.linearFromTf.b, &ret.dstTfFromDstLinear.b); - } - return ret; -} - } // namespace mozilla::color diff --git a/gfx/gl/Colorspaces.h b/gfx/gl/Colorspaces.h index 8f36854d2d4f3ab277b597401a723638fa5b676d..04fe9e678fe000c0e26f0ebe1014563e1d15a4a6 100644 --- a/gfx/gl/Colorspaces.h +++ b/gfx/gl/Colorspaces.h @@ -15,16 +15,12 @@ #include <algorithm> #include <array> #include <cmath> -#include <cstdint> #include <cstdlib> -#include <functional> #include <optional> #include <vector> #include "AutoMappable.h" -#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -#include "mozilla/Span.h" #ifdef DEBUG # define ASSERT(EXPR) \ @@ -37,9 +33,6 @@ # define ASSERT(EXPR) (void)(EXPR) #endif -struct _qcms_profile; -typedef struct _qcms_profile qcms_profile; - namespace mozilla::color { struct YuvLumaCoeffs final { @@ -81,7 +74,8 @@ struct PiecewiseGammaDesc final { 4.5, }; } - // FYI: static constexpr auto Rec2020_10bit() { return Rec709(); } + static constexpr auto Rec2020_10bit() { return Rec709(); } + static constexpr auto Rec2020_12bit() { return PiecewiseGammaDesc{ 1.0993, @@ -296,7 +290,6 @@ struct avec final { return eq; } }; -using vec2 = avec<float, 2>; using vec3 = avec<float, 3>; using vec4 = avec<float, 4>; using ivec3 = avec<int32_t, 3>; @@ -371,15 +364,10 @@ struct mat final { static constexpr auto Identity() { auto ret = mat{}; - for (int i = 0; i < std::min(x_cols, y_rows); i++) { - ret.at(i, i) = 1; - } - return ret; - } - static constexpr auto Scale(const avec<float, std::min(x_cols, y_rows)>& v) { - auto ret = mat{}; - for (int i = 0; i < v.N; i++) { - ret.at(i, i) = v[i]; + 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; } @@ -439,30 +427,7 @@ struct mat final { } return c; } - - // For e.g. similarity evaluation - friend auto operator-(const mat& a, const mat& b) { - mat c; - for (int y = 0; y < y_rows; y++) { - c.rows[y] = a.rows[y] - b.rows[y]; - } - return c; - } }; - -template <class M> -inline float dotDifference(const M& a, const M& b) { - const auto c = a - b; - const auto d = c * avec<float, M::x_cols>(1); - const auto d2 = dot(d, d); - return d2; -} -template <class M> -inline bool approx(const M& a, const M& b, const float eps = 0.0001) { - const auto errSquared = dotDifference(a, b); - return errSquared <= (eps * eps); -} - using mat3 = mat<3, 3>; using mat4 = mat<4, 4>; @@ -686,300 +651,6 @@ struct ColorspaceTransform final { } }; -// - - -struct RgbTransferTables { - std::vector<float> r; - std::vector<float> g; - std::vector<float> b; -}; -float GuessGamma(const std::vector<float>& vals, float exp_guess = 1.0); - -static constexpr auto D65 = vec2{{0.3127, 0.3290}}; -static constexpr auto D50 = vec2{{0.34567, 0.35850}}; -mat3 XyzAFromXyzB_BradfordLinear(const vec2 xyA, const vec2 xyB); - -// - - -struct ColorProfileDesc { - // ICC profiles are phrased as PCS-from-encoded (PCS is CIEXYZ-D50) - // However, all of our colorspaces are D65, so let's normalize to that, - // even though it's a reversible transform. - color::mat4 rgbFromYcbcr = color::mat4::Identity(); - RgbTransferTables linearFromTf; - color::mat3 xyzd65FromLinearRgb = color::mat3::Identity(); - - static ColorProfileDesc From(const ColorspaceDesc&); - static ColorProfileDesc From(const qcms_profile&); -}; - -template <class C> -inline float SampleOutByIn(const C& outByIn, const float in) { - switch (outByIn.size()) { - case 0: - return in; - case 1: - return outByIn.at(0); - } - MOZ_ASSERT(outByIn.size() >= 2); - const auto begin = outByIn.begin(); - - const auto in0i = size_t(floorf(in * (outByIn.size() - 1))); - const auto out0_itr = begin + std::min(in0i, outByIn.size() - 2); - - const auto in0 = float(out0_itr - begin) / (outByIn.size() - 1); - const auto out0 = *out0_itr; - const auto d_in = float(1) / (outByIn.size() - 1); - const auto d_out = *(out0_itr + 1) - *out0_itr; - - const auto out = out0 + (d_out / d_in) * (in - in0); - // printf("SampleOutByIn(%f)->%f\n", in, out); - return out; -} - -template <class C> -inline float SampleInByOut(const C& outByIn, const float out) { - MOZ_ASSERT(outByIn.size() >= 2); - const auto begin = outByIn.begin(); - - const auto out0_itr = std::lower_bound(begin + 1, outByIn.end() - 1, out) - 1; - - const auto in0 = float(out0_itr - begin) / (outByIn.size() - 1); - const auto out0 = *out0_itr; - const auto d_in = float(1) / (outByIn.size() - 1); - const auto d_out = *(out0_itr + 1) - *out0_itr; - - // printf("%f + (%f / %f) * (%f - %f)\n", in0, d_in, d_out, out, out0); - const auto in = in0 + (d_in / d_out) * (out - out0); - // printf("SampleInByOut(%f)->%f\n", out, in); - return in; -} - -template <class C, class FnLessEqualT = std::less_equal<typename C::value_type>> -inline bool IsMonotonic(const C& vals, const FnLessEqualT& LessEqual = {}) { - bool ok = true; - const auto begin = vals.begin(); - for (size_t i = 1; i < vals.size(); i++) { - const auto itr = begin + i; - ok &= LessEqual(*(itr - 1), *itr); - // Assert(true, [&]() { - // return prints("[%zu]->%f <= [%zu]->%f", i-1, *(itr-1), i, *itr); - // }); - } - return ok; -} - -template <class T, class I> -inline std::optional<I> SeekNeq(const T& ref, const I first, const I last) { - const auto inc = (last - first) > 0 ? 1 : -1; - auto itr = first; - while (true) { - if (*itr != ref) return itr; - if (itr == last) return {}; - itr += inc; - } -} - -template <class T> -struct TwoPoints { - struct { - T x; - T y; - } p0; - struct { - T x; - T y; - } p1; - - T y(const T x) const { - const auto dx = p1.x - p0.x; - const auto dy = p1.y - p0.y; - return p0.y + dy / dx * (x - p0.x); - } -}; - -/// Fills `vals` with `x:[0..vals.size()-1] => line.y(x)`. -template <class T> -static void LinearFill(T& vals, const TwoPoints<float>& line) { - float x = -1; - for (auto& val : vals) { - x += 1; - val = line.y(x); - } -} - -// - - -inline void DequantizeMonotonic(const Span<float> vals) { - MOZ_ASSERT(IsMonotonic(vals)); - - const auto first = vals.begin(); - const auto end = vals.end(); - if (first == end) return; - const auto last = end - 1; - if (first == last) return; - - // Three monotonic cases: - // 1. [0,0,0,0] - // 2. [0,0,1,1] - // 3. [0,1,1,2] - - const auto body_first = SeekNeq(*first, first, last); - if (!body_first) { - // E.g. [0,0,0,0] - return; - } - - const auto body_last = SeekNeq(*last, last, *body_first); - if (!body_last) { - // E.g. [0,0,1,1] - // This isn't the most accurate, but close enough. - // print("#2: %s", to_str(vals).c_str()); - LinearFill(vals, { - {0, *first}, - {float(vals.size() - 1), *last}, - }); - // print(" -> %s\n", to_str(vals).c_str()); - return; - } - - // E.g. [0,1,1,2] - // ^^^ body - // => f(0.5)->0.5, f(2.5)->1.5 - // => f(x) = f(x0) + (x-x0) * (f(x1) - f(x0)) / (x1-x0) - // => f(x) = f(x0) + (x-x0) * dfdx - - const auto head_end = *body_first; - const auto head = vals.subspan(0, head_end - vals.begin()); - const auto tail_begin = *body_last + 1; - const auto tail = vals.subspan(tail_begin - vals.begin()); - // print("head tail: %s %s\n", - // to_str(head).c_str(), - // to_str(tail).c_str()); - - // const auto body = vals->subspan(head.size(), vals->size()-tail.size()); - auto next_part_first = head_end; - while (next_part_first != tail_begin) { - const auto part_first = next_part_first; - // print("part_first: %f\n", *part_first); - next_part_first = *SeekNeq(*part_first, part_first, tail_begin); - // print("next_part_first: %f\n", *next_part_first); - const auto part = - Span<float>{part_first, size_t(next_part_first - part_first)}; - // print("part: %s\n", to_str(part).c_str()); - const auto prev_part_last = part_first - 1; - const auto part_last = next_part_first - 1; - const auto line = TwoPoints<float>{ - {-0.5, (*prev_part_last + *part_first) / 2}, - {part.size() - 0.5f, (*part_last + *next_part_first) / 2}, - }; - LinearFill(part, line); - } - - static constexpr bool INFER_HEAD_TAIL_FROM_BODY_EDGE = false; - // Basically ignore contents of head and tail, and infer from edges of body. - // print("3: %s\n", to_str(vals).c_str()); - if (!IsMonotonic(head, std::less<float>{})) { - if (!INFER_HEAD_TAIL_FROM_BODY_EDGE) { - LinearFill(head, - { - {0, *head.begin()}, - {head.size() - 0.5f, (*(head.end() - 1) + *head_end) / 2}, - }); - } else { - LinearFill(head, { - {head.size() + 0.0f, *head_end}, - {head.size() + 1.0f, *(head_end + 1)}, - }); - } - } - if (!IsMonotonic(tail, std::less<float>{})) { - if (!INFER_HEAD_TAIL_FROM_BODY_EDGE) { - LinearFill(tail, { - {-0.5, (*(tail_begin - 1) + *tail.begin()) / 2}, - {tail.size() - 1.0f, *(tail.end() - 1)}, - }); - } else { - LinearFill(tail, { - {-2.0f, *(tail_begin - 2)}, - {-1.0f, *(tail_begin - 1)}, - }); - } - } - // print("3: %s\n", to_str(vals).c_str()); - MOZ_ASSERT(IsMonotonic(vals, std::less<float>{})); - - // Rescale, because we tend to lose range. - static constexpr bool RESCALE = false; - if (RESCALE) { - const auto firstv = *first; - const auto lastv = *last; - for (auto& val : vals) { - val = (val - firstv) / (lastv - firstv); - } - } - // print("4: %s\n", to_str(vals).c_str()); -} - -template <class In, class Out> -static void InvertLut(const In& lut, Out* const out_invertedLut) { - MOZ_ASSERT(IsMonotonic(lut)); - auto plut = &lut; - auto vec = std::vector<float>{}; - if (!IsMonotonic(lut, std::less<float>{})) { - // print("Not strictly monotonic...\n"); - vec.assign(lut.begin(), lut.end()); - DequantizeMonotonic(vec); - plut = &vec; - // print(" Now strictly monotonic: %i: %s\n", - // int(IsMonotonic(*plut, std::less<float>{})), to_str(*plut).c_str()); - MOZ_ASSERT(IsMonotonic(*plut, std::less<float>{})); - } - MOZ_ASSERT(plut->size() >= 2); - - auto& ret = *out_invertedLut; - for (size_t i_out = 0; i_out < ret.size(); i_out++) { - const auto f_out = i_out / float(ret.size() - 1); - const auto f_in = SampleInByOut(*plut, f_out); - ret[i_out] = f_in; - } - - MOZ_ASSERT(IsMonotonic(ret)); - MOZ_ASSERT(IsMonotonic(ret, std::less<float>{})); -} - -// - - -struct ColorProfileConversionDesc { - // ICC profiles are phrased as PCS-from-encoded (PCS is CIEXYZ-D50) - color::mat4 srcRgbFromSrcYuv = color::mat4::Identity(); - RgbTransferTables srcLinearFromSrcTf; - color::mat3 dstLinearFromSrcLinear = color::mat3::Identity(); - RgbTransferTables dstTfFromDstLinear; - - struct FromDesc { - ColorProfileDesc src; - ColorProfileDesc dst; - }; - static ColorProfileConversionDesc From(const FromDesc&); - - vec3 Apply(const vec3 src) const { - const auto srcRgb = vec3(srcRgbFromSrcYuv * vec4(src, 1)); - const auto srcLinear = vec3{{ - SampleOutByIn(srcLinearFromSrcTf.r, srcRgb.x()), - SampleOutByIn(srcLinearFromSrcTf.g, srcRgb.y()), - SampleOutByIn(srcLinearFromSrcTf.b, srcRgb.z()), - }}; - const auto dstLinear = dstLinearFromSrcLinear * srcLinear; - const auto dstRgb = vec3{{ - SampleOutByIn(dstTfFromDstLinear.r, dstLinear.x()), - SampleOutByIn(dstTfFromDstLinear.g, dstLinear.y()), - SampleOutByIn(dstTfFromDstLinear.b, dstLinear.z()), - }}; - return dstRgb; - } -}; - } // namespace mozilla::color #undef ASSERT diff --git a/gfx/gl/gtest/TestColorspaces.cpp b/gfx/gl/gtest/TestColorspaces.cpp index c437e204af02038b016406fe16bd6ebb71c608a1..af204c7fc913c8b1a35a01cf3f3f0729cc53ef2b 100644 --- a/gfx/gl/gtest/TestColorspaces.cpp +++ b/gfx/gl/gtest/TestColorspaces.cpp @@ -6,14 +6,12 @@ #include "gtest/gtest.h" #include "Colorspaces.h" -#include <array> #include <limits> namespace mozilla::color { mat4 YuvFromYcbcr(const YcbcrDesc&); float TfFromLinear(const PiecewiseGammaDesc&, float linear); float LinearFromTf(const PiecewiseGammaDesc&, float tf); -mat3 XyzFromLinearRgb(const Chromaticities&); } // namespace mozilla::color using namespace mozilla::color; @@ -316,24 +314,6 @@ TEST(Colorspaces, SrgbFromDisplayP3) // - -template <class Fn, class Tuple, size_t... I> -constexpr auto map_tups_seq(const Tuple& a, const Tuple& b, const Fn& fn, - std::index_sequence<I...>) { - return std::tuple{fn(std::get<I>(a), std::get<I>(b))...}; -} -template <class Fn, class Tuple> -constexpr auto map_tups(const Tuple& a, const Tuple& b, const Fn& fn) { - return map_tups_seq(a, b, fn, - std::make_index_sequence<std::tuple_size_v<Tuple>>{}); -} - -template <class Fn, class Tuple> -constexpr auto cmp_tups_all(const Tuple& a, const Tuple& b, const Fn& fn) { - bool all = true; - map_tups(a, b, [&](const auto& a, const auto& b) { return all &= fn(a, b); }); - return all; -} - struct Stats { double mean = 0; double variance = 0; @@ -349,7 +329,6 @@ struct Stats { ret.max = std::max(ret.max, cur); } ret.mean /= iterable.size(); - // Gather mean first before we can calc variance. for (const auto& cur : iterable) { ret.variance += pow(cur - ret.mean, 2); } @@ -357,61 +336,8 @@ struct Stats { return ret; } - template <class T, class U> - static Stats Diff(const T& a, const U& b) { - MOZ_ASSERT(a.size() == b.size()); - std::vector<double> diff; - diff.reserve(a.size()); - for (size_t i = 0; i < diff.capacity(); i++) { - diff.push_back(a[i] - b[i]); - } - return Stats::For(diff); - } - double standardDeviation() const { return sqrt(variance); } - - friend std::ostream& operator<<(std::ostream& s, const Stats& a) { - return s << "Stats" - << "{ mean:" << a.mean << ", stddev:" << a.standardDeviation() - << ", min:" << a.min << ", max:" << a.max << " }"; - } - - struct Error { - double absmean = std::numeric_limits<double>::infinity(); - double stddev = std::numeric_limits<double>::infinity(); - double absmax = std::numeric_limits<double>::infinity(); - - constexpr auto Fields() const { return std::tie(absmean, stddev, absmax); } - - template <class Fn> - friend constexpr bool cmp_all(const Error& a, const Error& b, - const Fn& fn) { - return cmp_tups_all(a.Fields(), b.Fields(), fn); - } - friend constexpr bool operator<(const Error& a, const Error& b) { - return cmp_all(a, b, [](const auto& a, const auto& b) { return a < b; }); - } - friend constexpr bool operator<=(const Error& a, const Error& b) { - return cmp_all(a, b, [](const auto& a, const auto& b) { return a <= b; }); - } - - friend std::ostream& operator<<(std::ostream& s, const Error& a) { - return s << "Stats::Error" - << "{ absmean:" << a.absmean << ", stddev:" << a.stddev - << ", absmax:" << a.absmax << " }"; - } - }; - - operator Error() const { - return {abs(mean), standardDeviation(), std::max(abs(min), abs(max))}; - } }; -static_assert(Stats::Error{0, 0, 0} < Stats::Error{1, 1, 1}); -static_assert(!(Stats::Error{0, 1, 0} < Stats::Error{1, 1, 1})); -static_assert(Stats::Error{0, 1, 0} <= Stats::Error{1, 1, 1}); -static_assert(!(Stats::Error{0, 2, 0} <= Stats::Error{1, 1, 1})); - -// - static Stats StatsForLutError(const ColorspaceTransform& ct, const ivec3 srcQuants, const ivec3 dstQuants) { @@ -477,222 +403,3 @@ TEST(Colorspaces, LutError_Rec709Full_Srgb) EXPECT_NEAR(stats.min, 0, 0.001); EXPECT_NEAR(stats.max, 17, 0.001); } - -// - -// https://www.reedbeta.com/blog/python-like-enumerate-in-cpp17/ - -template <typename T, typename TIter = decltype(std::begin(std::declval<T>())), - typename = decltype(std::end(std::declval<T>()))> -constexpr auto enumerate(T&& iterable) { - struct iterator { - size_t i; - TIter iter; - bool operator!=(const iterator& other) const { return iter != other.iter; } - void operator++() { - ++i; - ++iter; - } - auto operator*() const { return std::tie(i, *iter); } - }; - struct iterable_wrapper { - T iterable; - auto begin() { return iterator{0, std::begin(iterable)}; } - auto end() { return iterator{0, std::end(iterable)}; } - }; - return iterable_wrapper{std::forward<T>(iterable)}; -} - -inline auto MakeLinear(const float from, const float to, const int n) { - std::vector<float> ret; - ret.resize(n); - for (auto [i, val] : enumerate(ret)) { - const auto t = i / float(ret.size() - 1); - val = from + (to - from) * t; - } - return ret; -}; - -inline auto MakeGamma(const float exp, const int n) { - std::vector<float> ret; - ret.resize(n); - for (auto [i, val] : enumerate(ret)) { - const auto t = i / float(ret.size() - 1); - val = powf(t, exp); - } - return ret; -}; - -// - - -TEST(Colorspaces, GuessGamma) -{ - EXPECT_NEAR(GuessGamma(MakeGamma(1, 11)), 1.0, 0); - EXPECT_NEAR(GuessGamma(MakeGamma(2.2, 11)), 2.2, 4.8e-8); - EXPECT_NEAR(GuessGamma(MakeGamma(1 / 2.2, 11)), 1 / 2.2, 1.7e-7); -} - -// - - -template <class T, class U> -float StdDev(const T& test, const U& ref) { - float sum = 0; - for (size_t i = 0; i < test.size(); i++) { - const auto diff = test[i] - ref[i]; - sum += diff * diff; - } - const auto variance = sum / test.size(); - return sqrt(variance); -} - -template <class T> -inline void AutoLinearFill(T& vals) { - LinearFill(vals, { - {0, 0}, - {vals.size() - 1.0f, 1}, - }); -} - -template <class T, class... More> -auto MakeArray(const T& a0, const More&... args) { - return std::array<T, 1 + sizeof...(More)>{a0, static_cast<float>(args)...}; -} - -TEST(Colorspaces, LinearFill) -{ - EXPECT_NEAR(StdDev(MakeLinear(0, 1, 3), MakeArray<float>(0, 0.5, 1)), 0, - 0.001); - - auto vals = std::vector<float>(3); - LinearFill(vals, { - {0, 0}, - {vals.size() - 1.0f, 1}, - }); - EXPECT_NEAR(StdDev(vals, MakeArray<float>(0, 0.5, 1)), 0, 0.001); - - LinearFill(vals, { - {0, 1}, - {vals.size() - 1.0f, 0}, - }); - EXPECT_NEAR(StdDev(vals, MakeArray<float>(1, 0.5, 0)), 0, 0.001); -} - -TEST(Colorspaces, DequantizeMonotonic) -{ - auto orig = std::vector<float>{0, 0, 0, 1, 1, 2}; - auto vals = orig; - EXPECT_TRUE(IsMonotonic(vals)); - EXPECT_TRUE(!IsMonotonic(vals, std::less<float>{})); - DequantizeMonotonic(vals); - EXPECT_TRUE(IsMonotonic(vals, std::less<float>{})); - EXPECT_LT(StdDev(vals, orig), - StdDev(MakeLinear(orig.front(), orig.back(), vals.size()), orig)); -} - -TEST(Colorspaces, InvertLut) -{ - const auto linear = MakeLinear(0, 1, 256); - auto linearFromSrgb = linear; - for (auto& val : linearFromSrgb) { - val = powf(val, 2.2); - } - auto srgbFromLinearExpected = linear; - for (auto& val : srgbFromLinearExpected) { - val = powf(val, 1 / 2.2); - } - - auto srgbFromLinearViaInvert = linearFromSrgb; - InvertLut(linearFromSrgb, &srgbFromLinearViaInvert); - // I just want to appreciate that InvertLut is a non-analytical approximation, - // and yet it's extraordinarily close to the analytical inverse. - EXPECT_LE(Stats::Diff(srgbFromLinearViaInvert, srgbFromLinearExpected), - (Stats::Error{3e-6, 3e-6, 3e-5})); - - const auto srcSrgb = MakeLinear(0, 1, 256); - auto roundtripSrgb = srcSrgb; - for (auto& srgb : roundtripSrgb) { - const auto linear = SampleOutByIn(linearFromSrgb, srgb); - const auto srgb2 = SampleOutByIn(srgbFromLinearViaInvert, linear); - // printf("[%f] %f -> %f -> %f\n", srgb2-srgb, srgb, linear, srgb2); - srgb = srgb2; - } - EXPECT_LE(Stats::Diff(roundtripSrgb, srcSrgb), - (Stats::Error{0.0013, 0.0046, 0.023})); -} - -TEST(Colorspaces, XyzFromLinearRgb) -{ - const auto xyzd65FromLinearRgb = XyzFromLinearRgb(Chromaticities::Srgb()); - - // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html - const auto XYZD65_FROM_LINEAR_RGB = mat3({ - vec3{{0.4124564, 0.3575761, 0.1804375}}, - vec3{{0.2126729, 0.7151522, 0.0721750}}, - vec3{{0.0193339, 0.1191920, 0.9503041}}, - }); - EXPECT_NEAR(sqrt(dotDifference(xyzd65FromLinearRgb, XYZD65_FROM_LINEAR_RGB)), - 0, 0.001); -} - -TEST(Colorspaces, ColorProfileConversionDesc_SrgbFromRec709) -{ - const auto srgb = ColorProfileDesc::From({ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - }); - const auto rec709 = ColorProfileDesc::From({ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - }); - - { - const auto conv = ColorProfileConversionDesc::From({ - .src = srgb, - .dst = srgb, - }); - auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; - - const auto tfa = PiecewiseGammaDesc::Srgb(); - const auto tfb = PiecewiseGammaDesc::Srgb(); - const auto expected = - TfFromLinear(tfb, LinearFromTf(tfa, src.x() / 255)) * 255; - - printf("%f %f %f\n", src.x(), src.y(), src.z()); - printf("%f %f %f\n", dst.x(), dst.y(), dst.z()); - EXPECT_LT(Stats::Diff(dst.data, vec3(expected).data), (Stats::Error{0.42})); - } - { - const auto conv = ColorProfileConversionDesc::From({ - .src = rec709, - .dst = rec709, - }); - auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; - - const auto tfa = PiecewiseGammaDesc::Rec709(); - const auto tfb = PiecewiseGammaDesc::Rec709(); - const auto expected = - TfFromLinear(tfb, LinearFromTf(tfa, src.x() / 255)) * 255; - - printf("%f %f %f\n", src.x(), src.y(), src.z()); - printf("%f %f %f\n", dst.x(), dst.y(), dst.z()); - EXPECT_LT(Stats::Diff(dst.data, vec3(expected).data), (Stats::Error{1e-6})); - } - { - const auto conv = ColorProfileConversionDesc::From({ - .src = rec709, - .dst = srgb, - }); - auto src = vec3(16.0); - auto dst = conv.Apply(src / 255) * 255; - - const auto tfa = PiecewiseGammaDesc::Rec709(); - const auto tfb = PiecewiseGammaDesc::Srgb(); - const auto expected = - TfFromLinear(tfb, LinearFromTf(tfa, src.x() / 255)) * 255; - printf("expected: %f\n", expected); - printf("%f %f %f\n", src.x(), src.y(), src.z()); - printf("%f %f %f\n", dst.x(), dst.y(), dst.z()); - EXPECT_LT(Stats::Diff(dst.data, vec3(expected).data), (Stats::Error{0.12})); - } -} diff --git a/gfx/qcms/qcms.h b/gfx/qcms/qcms.h index c23346006cd30bda98f1c8995d264382e6344782..380d0f48026a4d2d057e4ffd0204a609b4d84da4 100644 --- a/gfx/qcms/qcms.h +++ b/gfx/qcms/qcms.h @@ -11,35 +11,35 @@ extern "C" { #ifndef ICC_H /* icc34 defines */ -/***************************************************************** +/***************************************************************** Copyright (c) 1994-1996 SunSoft, Inc. Rights Reserved -Permission is hereby granted, free of charge, to any person +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restrict- -ion, including without limitation the rights to use, copy, modify, -merge, publish distribute, sublicense, and/or sell copies of the -Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +files (the "Software"), to deal in the Software without restrict- +ion, including without limitation the rights to use, copy, modify, +merge, publish distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- -INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT -COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of SunSoft, Inc. -shall not be used in advertising or otherwise to promote the -sale, use or other dealings in this Software without written -authorization from SunSoft Inc. +INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT +COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of SunSoft, Inc. +shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without written +authorization from SunSoft Inc. ******************************************************************/ /* @@ -48,11 +48,11 @@ authorization from SunSoft Inc. * don't use the same objects on different threads at the same time. */ -/* +/* * Color Space Signatures * Note that only icSigXYZData and icSigLabData are valid * Profile Connection Spaces (PCSs) - */ + */ typedef enum { icSigXYZData = 0x58595A20L, /* 'XYZ ' */ icSigLabData = 0x4C616220L, /* 'Lab ' */ @@ -79,13 +79,12 @@ typedef enum { icSig13colorData = 0x44434C52L, /* 'DCLR' */ icSig14colorData = 0x45434C52L, /* 'ECLR' */ icSig15colorData = 0x46434C52L, /* 'FCLR' */ - icMaxEnumData = 0xFFFFFFFFL + icMaxEnumData = 0xFFFFFFFFL } icColorSpaceSignature; #endif #include <stdio.h> #include <stdbool.h> -#include <cstdint> struct _qcms_transform; typedef struct _qcms_transform qcms_transform; @@ -197,42 +196,6 @@ void qcms_enable_iccv4(); void qcms_enable_neon(); void qcms_enable_avx(); - - -// - -/* -struct qcms_mat3r3 { - struct row { - float cols[3]; - }; - row rows[3]; -}; -*/ -struct qcms_profile_data { - uint32_t class_type; - uint32_t color_space; - uint32_t pcs; - qcms_intent rendering_intent; - float red_colorant_xyzd50[3]; - float blue_colorant_xyzd50[3]; - float green_colorant_xyzd50[3]; - int32_t linear_from_trc_red_samples; - int32_t linear_from_trc_blue_samples; - int32_t linear_from_trc_green_samples; -}; - -void qcms_profile_get_data(const qcms_profile*, qcms_profile_data* out_data); - - -enum class qcms_color_channel : uint8_t { - Red, - Green, - Blue, -}; - -void qcms_profile_get_lut(const qcms_profile*, qcms_color_channel, - float* begin, float* end); - #ifdef __cplusplus } #endif diff --git a/gfx/qcms/src/c_bindings.rs b/gfx/qcms/src/c_bindings.rs index efe6674fe5a5ada33fd536f4cc4286e1f3d1237c..ba0431f675b05fe4fbf61e42858be99b435cb6b0 100644 --- a/gfx/qcms/src/c_bindings.rs +++ b/gfx/qcms/src/c_bindings.rs @@ -7,11 +7,9 @@ use libc::{fclose, fopen, fread, free, malloc, memset, FILE}; use crate::{ double_to_s15Fixed16Number, iccread::*, - s15Fixed16Number_to_float, transform::get_rgb_colorants, transform::DataType, transform::{qcms_transform, transform_create}, - transform_util, Intent, }; @@ -377,124 +375,6 @@ pub unsafe extern "C" fn qcms_transform_data( length, ); } -/* -use crate::matrix; -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct qcms_mat3r3 { - pub rows: [[f32; 3] ; 3], -} -impl qcms_mat3r3 { - fn from(m: matrix::Matrix) -> qcms_mat3r3 { - qcms_mat3r3{ - rows: [ - m.row(0), - m.row(1), - m.row(2), - ], - } - } -} -*/ -#[repr(C)] -#[derive(Clone, Debug, Default)] -#[allow(clippy::upper_case_acronyms)] -pub struct qcms_profile_data { - pub class_type: u32, - pub color_space: u32, - pub pcs: u32, - pub rendering_intent: Intent, - pub red_colorant_xyzd50: [f32; 3], - pub blue_colorant_xyzd50: [f32; 3], - pub green_colorant_xyzd50: [f32; 3], - // Number of samples in the e.g. gamma->linear LUT. - pub linear_from_trc_red_samples: i32, - pub linear_from_trc_blue_samples: i32, - pub linear_from_trc_green_samples: i32, -} - -pub use crate::iccread::Profile as qcms_profile; - -#[no_mangle] -pub extern "C" fn qcms_profile_get_data( - profile: &qcms_profile, - out_data: &mut qcms_profile_data, -) { - out_data.class_type = profile.class_type; - out_data.color_space = profile.color_space; - out_data.pcs = profile.pcs; - out_data.rendering_intent = profile.rendering_intent; - - fn colorant(c: &XYZNumber) -> [f32;3] { - [c.X, c.Y, c.Z].map(s15Fixed16Number_to_float) - } - out_data.red_colorant_xyzd50 = colorant(&profile.redColorant); - out_data.blue_colorant_xyzd50 = colorant(&profile.blueColorant); - out_data.green_colorant_xyzd50 = colorant(&profile.greenColorant); - - fn trc_to_samples(trc: &Option<Box<curveType>>) -> i32 { - if let Some(ref trc) = *trc { - match &**trc { - curveType::Curve(v) => { - let len = v.len(); - if len <= 1 { - -1 - } else { - len as i32 - } - }, - curveType::Parametric(_) => -1, - } - } else { - 0 - } - } - out_data.linear_from_trc_red_samples = trc_to_samples(&profile.redTRC); - out_data.linear_from_trc_blue_samples = trc_to_samples(&profile.blueTRC); - out_data.linear_from_trc_green_samples = trc_to_samples(&profile.greenTRC); -} - -#[repr(u8)] -pub enum qcms_color_channel { - Red, - Green, - Blue, -} - -#[no_mangle] -pub extern "C" fn qcms_profile_get_lut( - profile: &qcms_profile, - channel: qcms_color_channel, // FYI: UB if you give Rust something out of range! - out_begin: *mut f32, - out_end: *mut f32, -) { - let out_slice = unsafe { - std::slice::from_raw_parts_mut(out_begin, out_end.offset_from(out_begin) as usize) - }; - - let trc = match channel { - qcms_color_channel::Red => &profile.redTRC, - qcms_color_channel::Green => &profile.greenTRC, - qcms_color_channel::Blue => &profile.blueTRC, - }; - - let samples_u16 = if let Some(trc) = trc { - let trc = &*trc; - // Yes, sub-optimal, but easier to implement, and these aren't big or hot: - // 1. Ask for a new vec<u16> lut based on the trc. - // * (eat the extra alloc) - // 2. Convert the u16s back out to f32s in our slice. - // * (eat the copy and quantization error from f32->u16->f32 roundtrip) - transform_util::build_lut_for_linear_from_tf(trc, Some(out_slice.len())) - } else { - Vec::new() - }; - - assert_eq!(samples_u16.len(), out_slice.len()); - for (d, s) in out_slice.iter_mut().zip(samples_u16.into_iter()) { - *d = (s as f32) / (u16::MAX as f32); - } -} pub type icColorSpaceSignature = u32; pub const icSigGrayData: icColorSpaceSignature = 0x47524159; // 'GRAY' @@ -502,6 +382,7 @@ pub const icSigRgbData: icColorSpaceSignature = 0x52474220; // 'RGB ' pub const icSigCmykData: icColorSpaceSignature = 0x434d594b; // 'CMYK' pub use crate::iccread::qcms_profile_is_bogus; +pub use crate::iccread::Profile as qcms_profile; pub use crate::transform::{ qcms_enable_iccv4, qcms_profile_precache_output_transform, qcms_transform_release, }; diff --git a/gfx/qcms/src/iccread.rs b/gfx/qcms/src/iccread.rs index d86e9742d4b192bf864db649dee7bd0734637b86..67cc1d38ffe3de73aec6d8fe68ab91610e89d7fd 100644 --- a/gfx/qcms/src/iccread.rs +++ b/gfx/qcms/src/iccread.rs @@ -50,8 +50,6 @@ pub struct Profile { pub(crate) redColorant: XYZNumber, pub(crate) blueColorant: XYZNumber, pub(crate) greenColorant: XYZNumber, - // "TRC" is EOTF, e.g. gamma->linear transfer function. - // Because ICC profiles are phrased as decodings to the xyzd50-linear PCS. pub(crate) redTRC: Option<Box<curveType>>, pub(crate) blueTRC: Option<Box<curveType>>, pub(crate) greenTRC: Option<Box<curveType>>, @@ -95,7 +93,7 @@ pub(crate) struct lutmABType { } #[derive(Clone, Debug)] pub(crate) enum curveType { - Curve(Vec<uInt16Number>), // len=0 => Linear, len=1 => Gamma(v[0]), _ => lut + Curve(Vec<uInt16Number>), /// The ICC parametricCurveType is specified in terms of s15Fixed16Number, /// so it's possible to use this variant to specify greater precision than /// any raw ICC profile could diff --git a/gfx/qcms/src/lib.rs b/gfx/qcms/src/lib.rs index c311964ee3f493c8004b0eeafdf9f03af266ea9c..25b18ad1bb53c285fcd3dfc8c125eb38427dca0a 100644 --- a/gfx/qcms/src/lib.rs +++ b/gfx/qcms/src/lib.rs @@ -11,11 +11,10 @@ #![cfg_attr( feature = "neon", feature(arm_target_feature, raw_ref_op) - )] /// These values match the Rendering Intent values from the ICC spec -#[repr(C)] +#[repr(u32)] #[derive(Clone, Copy, Debug)] pub enum Intent { AbsoluteColorimetric = 3, diff --git a/gfx/qcms/src/matrix.rs b/gfx/qcms/src/matrix.rs index 8cd450241ef0ce6b40e9f2a1e27a6ddcd44a924f..4793f38071a921f2acaba8d8d12ef9cd627823ca 100644 --- a/gfx/qcms/src/matrix.rs +++ b/gfx/qcms/src/matrix.rs @@ -22,7 +22,7 @@ #[derive(Copy, Clone, Debug, Default)] pub struct Matrix { - pub m: [[f32; 3]; 3], // Three rows of three elems. + pub m: [[f32; 3]; 3], } #[derive(Copy, Clone)] @@ -39,10 +39,6 @@ impl Matrix { result } - pub fn row(&self, r: usize) -> [f32; 3] { - self.m[r] - } - //probably reuse this computation in matrix_invert pub fn det(&self) -> f32 { let det: f32 = self.m[0][0] * self.m[1][1] * self.m[2][2] diff --git a/gfx/qcms/src/transform_util.rs b/gfx/qcms/src/transform_util.rs index 75fd2ca0e2c076e25592962cf000783ad73cceaf..5c198010539e78dd8da91549f8bc511e6079c9b3 100644 --- a/gfx/qcms/src/transform_util.rs +++ b/gfx/qcms/src/transform_util.rs @@ -440,10 +440,10 @@ invert_lut will produce an inverse of: which has an maximum error of about 9855 (pixel difference of ~38.346) For now, we punt the decision of output size to the caller. */ -fn invert_lut(table: &[u16], out_length: usize) -> Vec<u16> { +fn invert_lut(table: &[u16], out_length: i32) -> Vec<u16> { /* for now we invert the lut by creating a lut of size out_length * and attempting to lookup a value for each entry using lut_inverse_interp16 */ - let mut output = Vec::with_capacity(out_length); + let mut output = Vec::with_capacity(out_length as usize); for i in 0..out_length { let x: f64 = i as f64 * 65535.0f64 / (out_length - 1) as f64; let input: uint16_fract_t = (x + 0.5f64).floor() as uint16_fract_t; @@ -476,7 +476,7 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU curveType::Parametric(params) => { let mut gamma_table_uint: [u16; 256] = [0; 256]; - let mut inverted_size: usize = 256; + let mut inverted_size: i32 = 256; let gamma_table = compute_curve_gamma_table_type_parametric(params); let mut i: u16 = 0u16; while (i as i32) < 256 { @@ -498,7 +498,7 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU 0 => compute_precache_linear(output), 1 => compute_precache_pow(output, 1. / u8Fixed8Number_to_float(data[0])), _ => { - let mut inverted_size = data.len(); + let mut inverted_size = data.len() as i32; //XXX: the choice of a minimum of 256 here is not backed by any theory, // measurement or data, however it is what lcms uses. // the maximum number we would need is 65535 because that's the @@ -514,8 +514,8 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU } true } -fn build_linear_table(length: usize) -> Vec<u16> { - let mut output = Vec::with_capacity(length); +fn build_linear_table(length: i32) -> Vec<u16> { + let mut output = Vec::with_capacity(length as usize); for i in 0..length { let x: f64 = i as f64 * 65535.0f64 / (length - 1) as f64; let input: uint16_fract_t = (x + 0.5f64).floor() as uint16_fract_t; @@ -523,8 +523,8 @@ fn build_linear_table(length: usize) -> Vec<u16> { } output } -fn build_pow_table(gamma: f32, length: usize) -> Vec<u16> { - let mut output = Vec::with_capacity(length); +fn build_pow_table(gamma: f32, length: i32) -> Vec<u16> { + let mut output = Vec::with_capacity(length as usize); for i in 0..length { let mut x: f64 = i as f64 / (length - 1) as f64; x = x.powf(gamma as f64); @@ -534,75 +534,36 @@ fn build_pow_table(gamma: f32, length: usize) -> Vec<u16> { output } -fn to_lut(params: &Param, len: usize) -> Vec<u16> { - let mut output = Vec::with_capacity(len); - for i in 0..len { - let X = i as f32 / (len-1) as f32; - output.push((params.eval(X) * 65535.) as u16); - } - output -} - -pub(crate) fn build_lut_for_linear_from_tf(trc: &curveType, - lut_len: Option<usize>) -> Vec<u16> { +pub(crate) fn build_output_lut(trc: &curveType) -> Option<Vec<u16>> { match trc { curveType::Parametric(params) => { - let lut_len = lut_len.unwrap_or(256); let params = Param::new(params); - to_lut(¶ms, lut_len) - }, - curveType::Curve(data) => { - let autogen_lut_len = lut_len.unwrap_or(4096); - match data.len() { - 0 => build_linear_table(autogen_lut_len), - 1 => { - let gamma = u8Fixed8Number_to_float(data[0]); - build_pow_table(gamma, autogen_lut_len) - } - _ => { - let lut_len = lut_len.unwrap_or(data.len()); - assert_eq!(lut_len, data.len()); - data.clone() // I feel bad about this. - } - } - }, - } -} + let inv_params = params.invert()?; -pub(crate) fn build_lut_for_tf_from_linear(trc: &curveType) -> Option<Vec<u16>> { - match trc { - curveType::Parametric(params) => { - let lut_len = 256; - let params = Param::new(params); - if let Some(inv_params) = params.invert() { - return Some(to_lut(&inv_params, lut_len)); + let mut output = Vec::with_capacity(256); + for i in 0..256 { + let X = i as f32 / 255.; + output.push((inv_params.eval(X) * 65535.) as u16); } - // else return None instead of fallthrough to generic lut inversion. - return None; - }, + Some(output) + } curveType::Curve(data) => { - let autogen_lut_len = 4096; match data.len() { - 0 => { - return Some(build_linear_table(autogen_lut_len)); - }, + 0 => Some(build_linear_table(4096)), 1 => { let gamma = 1. / u8Fixed8Number_to_float(data[0]); - return Some(build_pow_table(gamma, autogen_lut_len)); - }, - _ => {}, + Some(build_pow_table(gamma, 4096)) + } + _ => { + //XXX: the choice of a minimum of 256 here is not backed by any theory, + // measurement or data, however it is what lcms uses. + let mut output_gamma_lut_length = data.len(); + if output_gamma_lut_length < 256 { + output_gamma_lut_length = 256 + } + Some(invert_lut(data, output_gamma_lut_length as i32)) + } } - }, + } } - - let linear_from_tf = build_lut_for_linear_from_tf(trc, None); - - //XXX: the choice of a minimum of 256 here is not backed by any theory, - // measurement or data, however it is what lcms uses. - let inverted_lut_len = std::cmp::max(linear_from_tf.len(), 256); - Some(invert_lut(&linear_from_tf, inverted_lut_len)) -} - -pub(crate) fn build_output_lut(trc: &curveType) -> Option<Vec<u16>> { - build_lut_for_tf_from_linear(trc) } diff --git a/gfx/thebes/AllOfDcomp.h b/gfx/thebes/AllOfDcomp.h deleted file mode 100644 index bd4448b68baaf8b3c0de52e9d5260f895a2fd439..0000000000000000000000000000000000000000 --- a/gfx/thebes/AllOfDcomp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_gfx_AllOfDcomp_h -#define mozilla_gfx_AllOfDcomp_h - -// Getting everything that we need in dcomp.h defined means messing with some -// defines. - -#if (_WIN32_WINNT < _WIN32_WINNT_WIN10) - -# define XSTR(x) STR(x) -# define STR(x) #x -// clang-format off - -# pragma message "IDCompositionFilterEffect in dcomp.h requires _WIN32_WINNT >= _WIN32_WINNT_WIN10." -// Pedantically, it actually requires _WIN32_WINNT_WINTHRESHOLD, but that's the -// same as _WIN32_WINNT_WIN10. - -# pragma message "Forcing NTDDI_VERSION " XSTR(NTDDI_VERSION) " -> " XSTR(NTDDI_WIN10) -# undef NTDDI_VERSION -# define NTDDI_VERSION NTDDI_WIN10 - -# pragma message "Forcing _WIN32_WINNT " XSTR(_WIN32_WINNT) " -> " XSTR(_WIN32_WINNT_WIN10) -# undef _WIN32_WINNT -# define _WIN32_WINNT _WIN32_WINNT_WIN10 - -// clang-format on -# undef STR -# undef XSTR - -#endif - -// - - -#include <dcomp.h> - -#endif // mozilla_gfx_AllOfDcomp_h diff --git a/gfx/thebes/DeviceManagerDx.cpp b/gfx/thebes/DeviceManagerDx.cpp index 100cd3192afc6431b2d74cacddbfd23975537bff..d8bfc0d43d1f6a266ea4f156d9c6723cb485f446 100644 --- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -25,10 +25,13 @@ #include "nsPrintfCString.h" #include "nsString.h" -// - +#undef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WINBLUE +#undef NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WINBLUE -#include "mozilla/gfx/AllOfDcomp.h" #include <d3d11.h> +#include <dcomp.h> #include <ddraw.h> #include <dxgi.h> @@ -47,7 +50,6 @@ decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr; // It should only be used within CreateDirectCompositionDevice. decltype(DCompositionCreateDevice2)* sDcompCreateDevice2Fn = nullptr; -decltype(DCompositionCreateDevice3)* sDcompCreateDevice3Fn = nullptr; // It should only be used within CreateDCompSurfaceHandle decltype(DCompositionCreateSurfaceHandle)* sDcompCreateSurfaceHandleFn = @@ -115,7 +117,7 @@ bool DeviceManagerDx::LoadDcomp() { MOZ_ASSERT(gfxVars::UseWebRenderDCompWin()); if (sDcompCreateDevice2Fn) { - return true; // Already loaded. + return true; } nsModuleHandle module(LoadLibrarySystem32(L"dcomp.dll")); @@ -125,8 +127,6 @@ bool DeviceManagerDx::LoadDcomp() { sDcompCreateDevice2Fn = (decltype(DCompositionCreateDevice2)*)GetProcAddress( module, "DCompositionCreateDevice2"); - sDcompCreateDevice3Fn = (decltype(DCompositionCreateDevice3)*)GetProcAddress( - module, "DCompositionCreateDevice3"); if (!sDcompCreateDevice2Fn) { return false; } @@ -135,6 +135,9 @@ bool DeviceManagerDx::LoadDcomp() { sDcompCreateSurfaceHandleFn = (decltype(DCompositionCreateSurfaceHandle)*)::GetProcAddress( module, "DCompositionCreateSurfaceHandle"); + if (!sDcompCreateDevice2Fn) { + return false; + } mDcompModule.steal(module); return true; @@ -445,16 +448,10 @@ void DeviceManagerDx::CreateDirectCompositionDeviceLocked() { HRESULT hr; RefPtr<IDCompositionDesktopDevice> desktopDevice; MOZ_SEH_TRY { - hr = sDcompCreateDevice3Fn( + hr = sDcompCreateDevice2Fn( dxgiDevice.get(), IID_PPV_ARGS( (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice))); - if (!desktopDevice) { - hr = sDcompCreateDevice2Fn( - dxgiDevice.get(), - IID_PPV_ARGS( - (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice))); - } } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return; } diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 75c6d2b4fd72d2e58bcc707821e868b1a41fa3e1..2d0b4f7d6d5eeaedf07ed3da5728a0203ebfe3c1 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2034,10 +2034,15 @@ DeviceColor gfxPlatform::TransformPixel(const sRGBColor& in, return DeviceColor(in.r, in.g, in.b, in.a); } +nsTArray<uint8_t> gfxPlatform::GetPlatformCMSOutputProfileData() { + return GetPrefCMSOutputProfileData(); +} + nsTArray<uint8_t> gfxPlatform::GetPrefCMSOutputProfileData() { - const auto mirror = StaticPrefs::gfx_color_management_display_profile(); - const auto fname = *mirror; - if (fname == "") { + nsAutoCString fname; + Preferences::GetCString("gfx.color_management.display_profile", fname); + + if (fname.IsEmpty()) { return nsTArray<uint8_t>(); } diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index db9b8fd436027a0293cce1cfe293aa52bd339183..b93d94603a58079bc1c47e84aeb87eef0d57b33b 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -753,9 +753,7 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener { * Returns a buffer containing the CMS output profile data. The way this * is obtained is platform-specific. */ - virtual nsTArray<uint8_t> GetPlatformCMSOutputProfileData() { - return GetPrefCMSOutputProfileData(); - } + virtual nsTArray<uint8_t> GetPlatformCMSOutputProfileData(); /** * Return information on how child processes should initialize graphics @@ -867,15 +865,13 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener { virtual void ImportContentDeviceData( const mozilla::gfx::ContentDeviceData& aData); - public: /** * Returns the contents of the file pointed to by the * gfx.color_management.display_profile pref, if set. * Returns an empty array if not set, or if an error occurs */ - static nsTArray<uint8_t> GetPrefCMSOutputProfileData(); + nsTArray<uint8_t> GetPrefCMSOutputProfileData(); - protected: /** * If inside a child process and currently being initialized by the * SetXPCOMProcessAttributes message, this can be used by subclasses to diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 6c88c524c8d77e5be206a1e065a2c3ac705e16ef..a7194a81276a73f2506b1425415aaf7d6137ab6e 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -1036,21 +1036,16 @@ nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData() { return result; } - return GetPlatformCMSOutputProfileData_Impl(); -} + if (!mCachedOutputColorProfile.IsEmpty()) { + return mCachedOutputColorProfile.Clone(); + } -nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl() { - static nsTArray<uint8_t> sCached = [&] { - // Check override pref first: - nsTArray<uint8_t> prefProfileData = - gfxPlatform::GetPrefCMSOutputProfileData(); + mCachedOutputColorProfile = [&] { + nsTArray<uint8_t> prefProfileData = GetPrefCMSOutputProfileData(); if (!prefProfileData.IsEmpty()) { return prefProfileData; } - // - - // Otherwise, create a dummy DC and pull from that. - HDC dc = ::GetDC(nullptr); if (!dc) { return nsTArray<uint8_t>(); @@ -1083,7 +1078,7 @@ nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl() { return result; }(); - return sCached.Clone(); + return mCachedOutputColorProfile.Clone(); } void gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath, diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 95315bcaa950be38aa311ae8c66733eaba1a8bab..a05b690c90eaba6b4669c72f26e4f98272a849a7 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -200,13 +200,7 @@ class gfxWindowsPlatform final : public gfxPlatform { protected: bool AccelerateLayersByDefault() override { return true; } - nsTArray<uint8_t> GetPlatformCMSOutputProfileData() override; - - public: - static nsTArray<uint8_t> GetPlatformCMSOutputProfileData_Impl(); - - protected: void GetPlatformDisplayInfo(mozilla::widget::InfoObject& aObj) override; void ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData) override; diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 9a87ea6aa0b082a8268b7e62638a409ef24e4f9a..176cc3c7e5dba33db636159486b54a71f99b2064 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -67,7 +67,6 @@ EXPORTS += [ ] EXPORTS.mozilla.gfx += [ - "AllOfDcomp.h", "D3D11Checks.h", "DeviceManagerDx.h", "DisplayConfigWindows.h", diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index f6c05112026137eac4dcf34a10af3f87f139d1d0..8d192ea091620bd63093a2f27868f5548080ae86 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -6,16 +6,6 @@ #include "DCLayerTree.h" -// - - -#include "mozilla/gfx/AllOfDcomp.h" -#include <d3d11.h> -#include <d3d11_1.h> -#include <dxgi1_2.h> - -// - - -#include "gfxWindowsPlatform.h" #include "GLContext.h" #include "GLContextEGL.h" #include "mozilla/gfx/DeviceManagerDx.h" @@ -33,15 +23,15 @@ #include "nsPrintfCString.h" #include "WinUtils.h" -// - +#undef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WINBLUE +#undef NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WINBLUE -#if defined(__MINGW32__) // 64 defines both 32 and 64 -// We need to fake some things, while we wait on updates to mingw's dcomp.h -// header. Just enough that we can successfully fail to work there. -# define MOZ_MINGW_DCOMP_H_INCOMPLETE -struct IDCompositionColorMatrixEffect : public IDCompositionFilterEffect {}; -struct IDCompositionTableTransferEffect : public IDCompositionFilterEffect {}; -#endif // defined(__MINGW32__) +#include <d3d11.h> +#include <d3d11_1.h> +#include <dcomp.h> +#include <dxgi1_2.h> namespace mozilla { namespace wr { @@ -577,16 +567,6 @@ void DCExternalSurfaceWrapper::AttachExternalImage( } } -template <class ToT> -struct QI { - template <class FromT> - [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) { - RefPtr<ToT> to; - (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to))); - return to; - } -}; - DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage( wr::ExternalImageId aExternalImage) { if (mSurface) { @@ -611,117 +591,14 @@ DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage( mSurface = nullptr; } } - if (!mSurface) { + + if (mSurface) { + // Add surface's visual which will contain video data to our root visual. + mVisual->AddVisual(mSurface->GetVisual(), TRUE, nullptr); + } else { gfxCriticalNote << "Failed to create a surface for external image: " << gfx::hexa(texture); - return nullptr; } - const auto textureSwgl = texture->AsRenderTextureHostSWGL(); - MOZ_ASSERT(textureSwgl); // Covered above. - - // Add surface's visual which will contain video data to our root visual. - const auto surfaceVisual = mSurface->GetVisual(); - mVisual->AddVisual(surfaceVisual, true, nullptr); - - // - - // Apply color management. - - [&]() { - const auto cmsMode = GfxColorManagementMode(); - if (cmsMode == CMSMode::Off) return; - - const auto dcomp = mDCLayerTree->GetCompositionDevice(); - const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp); - if (!dcomp3) { - NS_WARNING( - "No IDCompositionDevice3, cannot use dcomp for color management."); - return; - } - - // - - - const auto cspace = [&]() { - const auto rangedCspace = textureSwgl->GetYUVColorSpace(); - const auto info = FromYUVRangedColorSpace(rangedCspace); - auto ret = ToColorSpace2(info.space); - if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) { - ret = gfx::ColorSpace2::SRGB; - } - return ret; - }(); - - const bool rec709GammaAsSrgb = - StaticPrefs::gfx_color_management_rec709_gamma_as_srgb(); - const bool rec2020GammaAsRec709 = - StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709(); - - auto cspaceDesc = color::ColorspaceDesc{}; - switch (cspace) { - case gfx::ColorSpace2::Display: - return; // No color management needed! - case gfx::ColorSpace2::SRGB: - cspaceDesc.chrom = color::Chromaticities::Srgb(); - cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); - break; - - case gfx::ColorSpace2::DISPLAY_P3: - cspaceDesc.chrom = color::Chromaticities::DisplayP3(); - cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3(); - break; - - case gfx::ColorSpace2::BT601_525: - cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc(); - if (rec709GammaAsSrgb) { - cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); - } else { - cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); - } - break; - - case gfx::ColorSpace2::BT709: - cspaceDesc.chrom = color::Chromaticities::Rec709(); - if (rec709GammaAsSrgb) { - cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); - } else { - cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); - } - break; - - case gfx::ColorSpace2::BT2020: - cspaceDesc.chrom = color::Chromaticities::Rec2020(); - if (rec2020GammaAsRec709 && rec709GammaAsSrgb) { - cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); - } else if (rec2020GammaAsRec709) { - cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); - } else { - // Just Rec709 with slightly more precision. - cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit(); - } - break; - } - - const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc); - auto cprofileOut = mDCLayerTree->OutputColorProfile(); - bool pretendSrgb = StaticPrefs::gfx_color_management_native_srgb(); - if (pretendSrgb) { - cprofileOut = color::ColorProfileDesc::From({ - color::Chromaticities::Srgb(), - color::PiecewiseGammaDesc::Srgb(), - }); - } - const auto conversion = color::ColorProfileConversionDesc::From({ - .src = cprofileIn, - .dst = cprofileOut, - }); - - // - - - auto chain = ColorManagementChain::From(*dcomp3, conversion); - mCManageChain = Some(chain); - - surfaceVisual->SetEffect(mCManageChain->last.get()); - }(); - return mSurface.get(); } @@ -1714,119 +1591,6 @@ void DCLayerTree::DestroyEGLSurface() { } } -// - - -color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() { - // GPU process can't simply init gfxPlatform, (and we don't need most of it) - // but we do need gfxPlatform::GetCMSOutputProfile(). - // So we steal what we need through the window: - const auto outputProfileData = - gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl(); - - const auto qcmsProfile = qcms_profile_from_memory( - outputProfileData.Elements(), outputProfileData.Length()); - MOZ_ASSERT(qcmsProfile); - const auto release = - MakeScopeExit([&]() { qcms_profile_release(qcmsProfile); }); - - const auto ret = color::ColorProfileDesc::From(*qcmsProfile); - bool print = gfxEnv::MOZ_GL_SPEW(); - if (print) { - const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r); - printf_stderr( - "Display profile:\n" - " Approx Gamma: %f\n" - " XYZ-D65 Red : %f, %f, %f\n" - " XYZ-D65 Green: %f, %f, %f\n" - " XYZ-D65 Blue : %f, %f, %f\n", - gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0), - ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2), - - ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1), - ret.xyzd65FromLinearRgb.at(1, 2), - - ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1), - ret.xyzd65FromLinearRgb.at(2, 2)); - } - - return ret; -} - -inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) { - return D2D1_MATRIX_5X4_F{{{ - m.rows[0][0], - m.rows[1][0], - m.rows[2][0], - m.rows[3][0], - m.rows[0][1], - m.rows[1][1], - m.rows[2][1], - m.rows[3][1], - m.rows[0][2], - m.rows[1][2], - m.rows[2][2], - m.rows[3][2], - m.rows[0][3], - m.rows[1][3], - m.rows[2][3], - m.rows[3][3], - 0, - 0, - 0, - 0, - }}}; -} - -ColorManagementChain ColorManagementChain::From( - IDCompositionDevice3& dcomp, - const color::ColorProfileConversionDesc& conv) { - auto ret = ColorManagementChain{}; - -#if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE) - - const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) { - if (ret.last) { - afterLast->SetInput(0, ret.last, 0); - } - ret.last = afterLast; - }; - - const auto MaybeAppendColorMatrix = [&](const color::mat4& m) { - RefPtr<IDCompositionColorMatrixEffect> e; - if (approx(m, color::mat4::Identity())) return e; - dcomp.CreateColorMatrixEffect(getter_AddRefs(e)); - MOZ_ASSERT(e); - if (!e) return e; - e->SetMatrix(to_D2D1_MATRIX_5X4_F(m)); - Append(e); - return e; - }; - const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) { - RefPtr<IDCompositionTableTransferEffect> e; - if (!t.r.size() && !t.g.size() && !t.b.size()) return e; - dcomp.CreateTableTransferEffect(getter_AddRefs(e)); - MOZ_ASSERT(e); - if (!e) return e; - e->SetRedTable(t.r.data(), t.r.size()); - e->SetGreenTable(t.g.data(), t.g.size()); - e->SetBlueTable(t.b.data(), t.b.size()); - Append(e); - return e; - }; - - ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv); - ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf); - ret.dstLinearFromSrcLinear = - MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear)); - ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear); - -#endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE) - - return ret; -} - -ColorManagementChain::~ColorManagementChain() = default; - } // namespace wr } // namespace mozilla diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index 921a9389aebbf267929f6eba8f6b72b0a056f988..25076444a967dc99301dde8c7e07f4157b74300e 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -12,7 +12,6 @@ #include <vector> #include <windows.h> -#include "Colorspaces.h" #include "GLTypes.h" #include "mozilla/HashFunctions.h" #include "mozilla/layers/OverlayInfo.h" @@ -28,11 +27,7 @@ struct ID3D11VideoContext; struct ID3D11VideoProcessor; struct ID3D11VideoProcessorEnumerator; struct ID3D11VideoProcessorOutputView; -struct IDCompositionColorMatrixEffect; -struct IDCompositionFilterEffect; -struct IDCompositionTableTransferEffect; struct IDCompositionDevice2; -struct IDCompositionDevice3; struct IDCompositionSurface; struct IDCompositionTarget; struct IDCompositionVisual2; @@ -71,23 +66,6 @@ struct GpuOverlayInfo { UINT mRgb10a2OverlaySupportFlags = 0; }; -// - - -struct ColorManagementChain { - RefPtr<IDCompositionColorMatrixEffect> srcRgbFromSrcYuv; - RefPtr<IDCompositionTableTransferEffect> srcLinearFromSrcTf; - RefPtr<IDCompositionColorMatrixEffect> dstLinearFromSrcLinear; - RefPtr<IDCompositionTableTransferEffect> dstTfFromDstLinear; - RefPtr<IDCompositionFilterEffect> last; - - static ColorManagementChain From(IDCompositionDevice3& dcomp, - const color::ColorProfileConversionDesc&); - - ~ColorManagementChain(); -}; - -// - - /** * DCLayerTree manages direct composition layers. * It does not manage gecko's layers::Layer. @@ -232,19 +210,6 @@ class DCLayerTree { bool mPendingCommit; - static color::ColorProfileDesc QueryOutputColorProfile(); - - mutable Maybe<color::ColorProfileDesc> mOutputColorProfile; - - public: - const color::ColorProfileDesc& OutputColorProfile() const { - if (!mOutputColorProfile) { - mOutputColorProfile = Some(QueryOutputColorProfile()); - } - return *mOutputColorProfile; - } - - protected: static UniquePtr<GpuOverlayInfo> sGpuOverlayInfo; }; @@ -357,7 +322,6 @@ class DCExternalSurfaceWrapper : public DCSurface { UniquePtr<DCSurface> mSurface; const bool mIsOpaque; - Maybe<ColorManagementChain> mCManageChain; }; class DCSurfaceVideo : public DCSurface { diff --git a/mfbt/Span.h b/mfbt/Span.h index bcbd492d35edbb02fb2a64b7bf60af8a02f3d1c7..5d8b3255f49daf3e87e58559c8c93cfded377bf3 100644 --- a/mfbt/Span.h +++ b/mfbt/Span.h @@ -105,7 +105,7 @@ class span_iterator { public: using iterator_category = std::random_access_iterator_tag; using value_type = std::remove_const_t<element_type_>; - using difference_type = ptrdiff_t; + using difference_type = typename SpanT::index_type; using reference = std::conditional_t<IsConst, const element_type_, element_type_>&; @@ -366,7 +366,6 @@ class Span { public: // constants and types using element_type = ElementType; - using value_type = std::remove_cv_t<element_type>; using index_type = size_t; using pointer = element_type*; using reference = element_type&; @@ -421,16 +420,6 @@ class Span { aEnd) : storage_(aBegin == aEnd ? nullptr : &*aBegin, aEnd - aBegin) {} - /** - * Constructor for {iterator,size_t} - */ - template <typename OtherElementType, size_t OtherExtent, bool IsConst> - constexpr Span( - span_details::span_iterator<Span<OtherElementType, OtherExtent>, IsConst> - aBegin, - index_type aLength) - : storage_(!aLength ? nullptr : &*aBegin, aLength) {} - /** * Constructor for C array. */ @@ -675,16 +664,6 @@ class Span { return Subspan(0, aEnd); } - /// std::span-compatible method name - constexpr auto subspan(index_type aStart, - index_type aLength = dynamic_extent) const { - return Subspan(aStart, aLength); - } - /// std::span-compatible method name - constexpr auto from(index_type aStart) const { return From(aStart); } - /// std::span-compatible method name - constexpr auto to(index_type aEnd) const { return To(aEnd); } - /** * Subspan with run-time start index and exclusive end index. * (Rust's &foo[start..end]) diff --git a/mfbt/tests/gtest/TestSpan.cpp b/mfbt/tests/gtest/TestSpan.cpp index c5f04db0ddbd94601084a850d001167ae15cbcea..405a87ee2cf7d9dde045fa56fcb953c30488d01b 100644 --- a/mfbt/tests/gtest/TestSpan.cpp +++ b/mfbt/tests/gtest/TestSpan.cpp @@ -1630,16 +1630,16 @@ SPAN_TEST(begin_end) { ASSERT_NE(it, beyond); CHECK_THROW(*beyond, fail_fast); - ASSERT_EQ(beyond - first, 4); - ASSERT_EQ(first - first, 0); - ASSERT_EQ(beyond - beyond, 0); + ASSERT_EQ(beyond - first, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); ++it; - ASSERT_EQ(it - first, 1); + ASSERT_EQ(it - first, 1U); ASSERT_EQ(*it, 2); *it = 22; ASSERT_EQ(*it, 22); - ASSERT_EQ(beyond - it, 3); + ASSERT_EQ(beyond - it, 3U); it = first; ASSERT_EQ(it, first); @@ -1649,7 +1649,7 @@ SPAN_TEST(begin_end) { } ASSERT_EQ(it, beyond); - ASSERT_EQ(it - beyond, 0); + ASSERT_EQ(it - beyond, 0U); for (auto& n : s) { ASSERT_EQ(n, 5); @@ -1685,14 +1685,14 @@ SPAN_TEST(cbegin_cend) { ASSERT_NE(it, beyond); CHECK_THROW(*beyond, fail_fast); - ASSERT_EQ(beyond - first, 4); - ASSERT_EQ(first - first, 0); - ASSERT_EQ(beyond - beyond, 0); + ASSERT_EQ(beyond - first, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); ++it; - ASSERT_EQ(it - first, 1); + ASSERT_EQ(it - first, 1U); ASSERT_EQ(*it, 2); - ASSERT_EQ(beyond - it, 3); + ASSERT_EQ(beyond - it, 3U); int last = 0; it = first; @@ -1705,7 +1705,7 @@ SPAN_TEST(cbegin_cend) { } ASSERT_EQ(it, beyond); - ASSERT_EQ(it - beyond, 0); + ASSERT_EQ(it - beyond, 0U); } } @@ -1723,16 +1723,16 @@ SPAN_TEST(rbegin_rend) { ASSERT_NE(it, beyond); CHECK_THROW(*beyond, fail_fast); - ASSERT_EQ(beyond - first, 4); - ASSERT_EQ(first - first, 0); - ASSERT_EQ(beyond - beyond, 0); + ASSERT_EQ(beyond - first, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); ++it; - ASSERT_EQ(it - first, 1); + ASSERT_EQ(it - first, 1U); ASSERT_EQ(*it, 3); *it = 22; ASSERT_EQ(*it, 22); - ASSERT_EQ(beyond - it, 3); + ASSERT_EQ(beyond - it, 3U); it = first; ASSERT_EQ(it, first); @@ -1742,7 +1742,7 @@ SPAN_TEST(rbegin_rend) { } ASSERT_EQ(it, beyond); - ASSERT_EQ(it - beyond, 0); + ASSERT_EQ(it - beyond, 0U); for (auto& n : s) { ASSERT_EQ(n, 5); @@ -1764,14 +1764,14 @@ SPAN_TEST(crbegin_crend) { ASSERT_NE(it, beyond); CHECK_THROW(*beyond, fail_fast); - ASSERT_EQ(beyond - first, 4); - ASSERT_EQ(first - first, 0); - ASSERT_EQ(beyond - beyond, 0); + ASSERT_EQ(beyond - first, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); ++it; - ASSERT_EQ(it - first, 1); + ASSERT_EQ(it - first, 1U); ASSERT_EQ(*it, 3); - ASSERT_EQ(beyond - it, 3); + ASSERT_EQ(beyond - it, 3U); it = first; ASSERT_EQ(it, first); @@ -1784,7 +1784,7 @@ SPAN_TEST(crbegin_crend) { } ASSERT_EQ(it, beyond); - ASSERT_EQ(it - beyond, 0); + ASSERT_EQ(it - beyond, 0U); } } diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 8abc7026f13840298551f73e8e9c3d586aa21c3d..6ba7e8a6cf4abd635e3a853e438609d1b5a5e8dd 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -5775,11 +5775,6 @@ #endif mirror: always -- name: gfx.color_management.display_profile - type: DataMutexString - value: "" - mirror: always # But be warned: We cache the result. - - name: gfx.color_management.force_srgb type: RelaxedAtomicBool value: false @@ -5787,7 +5782,7 @@ - name: gfx.color_management.native_srgb type: RelaxedAtomicBool -#if defined(XP_MACOSX) || defined(XP_WIN) +#if defined(XP_MACOSX) value: true #else value: false @@ -5812,16 +5807,6 @@ value: 0 mirror: always -- name: gfx.color_management.rec709_gamma_as_srgb - type: RelaxedAtomicBool - value: true # Tragic backwards compat. - mirror: always - -- name: gfx.color_management.rec2020_gamma_as_rec709 - type: RelaxedAtomicBool - value: true # Match naive behavior, but hopefully we can stop soon! - mirror: always - - name: gfx.compositor.clearstate type: RelaxedAtomicBool value: false diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index fe9c9b0ee842dd17c62e1f0a1ab9baec4715f990..955fe0bd077d1530aaed94e615a513b0077ed5ee 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -454,6 +454,8 @@ pref("formhelper.autozoom.force-disable.test-only", false); pref("gfx.hidpi.enabled", 2); #endif +pref("gfx.color_management.display_profile", ""); + pref("gfx.downloadable_fonts.enabled", true); pref("gfx.downloadable_fonts.fallback_delay", 3000); pref("gfx.downloadable_fonts.fallback_delay_short", 100); diff --git a/netwerk/dns/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp index 1822e396ca41f7c8488604b17bfb01e9c16fd329..4d28889294829dbbf8978f9b26c1459a6a00af05 100644 --- a/netwerk/dns/DNSPacket.cpp +++ b/netwerk/dns/DNSPacket.cpp @@ -1177,7 +1177,7 @@ bool ODoHDNSPacket::ParseODoHConfigs(Span<const uint8_t> aData, } nsTArray<ObliviousDoHConfig> result; - static const int kMinimumConfigContentLength = 12; + static const uint32_t kMinimumConfigContentLength = 12; while (std::distance(it, aData.cend()) > kMinimumConfigContentLength) { ObliviousDoHConfig config; if (!get16bit(aData, it, config.mVersion)) {