Backed out 3 changesets (bug 1799258) for causing GTest-1proc failures on Span. CLOSED TREE

Backed out changeset 0facab7b9a1d (bug 1799258)
Backed out changeset 4f9ee3537468 (bug 1799258)
Backed out changeset 12e98a3054d0 (bug 1799258)
This commit is contained in:
Marian-Vasile Laza 2022-12-21 01:41:43 +02:00
Родитель fef4d286b9
Коммит 74bda87ddf
21 изменённых файлов: 98 добавлений и 1439 удалений

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

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

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

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

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

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

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

@ -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,294 +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) {
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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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(&params, 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)
}

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

@ -30,10 +30,6 @@
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WINBLUE
// We also need this, or dcomp.h won't give us e.g. IDCompositionDevice3:
#undef _WIN32_WINNT_WINTHRESHOLD
#define _WIN32_WINNT_WINTHRESHOLD _WIN32_WINNT
#include <d3d11.h>
#include <dcomp.h>
#include <ddraw.h>
@ -54,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 =
@ -122,7 +117,7 @@ bool DeviceManagerDx::LoadDcomp() {
MOZ_ASSERT(gfxVars::UseWebRenderDCompWin());
if (sDcompCreateDevice2Fn) {
return true; // Already loaded.
return true;
}
nsModuleHandle module(LoadLibrarySystem32(L"dcomp.dll"));
@ -132,8 +127,6 @@ bool DeviceManagerDx::LoadDcomp() {
sDcompCreateDevice2Fn = (decltype(DCompositionCreateDevice2)*)GetProcAddress(
module, "DCompositionCreateDevice2");
sDcompCreateDevice3Fn = (decltype(DCompositionCreateDevice3)*)GetProcAddress(
module, "DCompositionCreateDevice3");
if (!sDcompCreateDevice2Fn) {
return false;
}
@ -142,6 +135,9 @@ bool DeviceManagerDx::LoadDcomp() {
sDcompCreateSurfaceHandleFn =
(decltype(DCompositionCreateSurfaceHandle)*)::GetProcAddress(
module, "DCompositionCreateSurfaceHandle");
if (!sDcompCreateDevice2Fn) {
return false;
}
mDcompModule.steal(module);
return true;
@ -452,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; }

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

@ -2030,10 +2030,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>();
}

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

@ -751,9 +751,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
@ -865,15 +863,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

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

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

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

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

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

@ -6,7 +6,6 @@
#include "DCLayerTree.h"
#include "gfxWindowsPlatform.h"
#include "GLContext.h"
#include "GLContextEGL.h"
#include "mozilla/gfx/DeviceManagerDx.h"
@ -29,10 +28,6 @@
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WINBLUE
// We also need this, or dcomp.h won't give us e.g. IDCompositionFilterEffect:
#undef _WIN32_WINNT_WINTHRESHOLD
#define _WIN32_WINNT_WINTHRESHOLD _WIN32_WINNT
#include <d3d11.h>
#include <d3d11_1.h>
#include <dcomp.h>
@ -572,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) {
@ -606,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();
}
@ -1706,114 +1588,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{};
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);
return ret;
}
ColorManagementChain::~ColorManagementChain() = default;
} // namespace wr
} // namespace mozilla

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

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

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

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

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

@ -5720,11 +5720,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
@ -5732,7 +5727,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
@ -5757,16 +5752,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

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

@ -456,6 +456,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);