| Index: ui/gfx/color_transform.cc
|
| diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
|
| index 1d562d48401ec84c1b60a4d3f80bfb59954f310a..8a43239e4d3eb8ead0a0b20f74fe7b96e4edb0db 100644
|
| --- a/ui/gfx/color_transform.cc
|
| +++ b/ui/gfx/color_transform.cc
|
| @@ -35,6 +35,10 @@ namespace gfx {
|
|
|
| namespace {
|
|
|
| +std::string Str(float f) {
|
| + return base::StringPrintf("%+1.8e", f);
|
| +}
|
| +
|
| // Helper for scoped QCMS profiles.
|
| struct QcmsProfileDeleter {
|
| void operator()(qcms_profile* p) {
|
| @@ -227,6 +231,7 @@ Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
|
| } // namespace
|
|
|
| class ColorTransformMatrix;
|
| +class ColorTransformSkTransferFn;
|
| class ColorTransformFromLinear;
|
| class ColorTransformToBT2020CL;
|
| class ColorTransformFromBT2020CL;
|
| @@ -240,6 +245,7 @@ class ColorTransformStep {
|
| virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; }
|
| virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; }
|
| virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; }
|
| + virtual ColorTransformSkTransferFn* GetSkTransferFn() { return nullptr; }
|
| virtual ColorTransformMatrix* GetMatrix() { return nullptr; }
|
| virtual ColorTransformNull* GetNull() { return nullptr; }
|
| virtual QCMSColorTransform* GetQCMS() { return nullptr; }
|
| @@ -337,85 +343,133 @@ class ColorTransformMatrix : public ColorTransformStep {
|
|
|
| void AppendShaderSource(std::string* result) override {
|
| const SkMatrix44& m = matrix_.matrix();
|
| - SRC("color = mat3(%1.8e, %1.8e, %1.8e, %1.8e, %1.8e, %1.8e, %1.8e, %1.8e, "
|
| - "%1.8e) * color;",
|
| - m.get(0, 0), m.get(1, 0), m.get(2, 0), // column 1
|
| - m.get(0, 1), m.get(1, 1), m.get(2, 1), // column 2
|
| - m.get(0, 2), m.get(1, 2), m.get(2, 2)); // column 3
|
| - SRC("color += vec3(%1.8e, %1.8e, %1.8e);", m.get(0, 3), m.get(1, 3),
|
| - m.get(2, 3));
|
| + SRC("color = mat3(%+1.8e, %+1.8e, %+1.8e,", // column 1
|
| + m.get(0, 0), m.get(1, 0), m.get(2, 0));
|
| + SRC(" %+1.8e, %+1.8e, %+1.8e,", // column 2
|
| + m.get(0, 1), m.get(1, 1), m.get(2, 1));
|
| + SRC(" %+1.8e, %+1.8e, %+1.8e) * color;", // column 3
|
| + m.get(0, 2), m.get(1, 2), m.get(2, 2));
|
| + SRC("color = vec3(%+1.8e, %+1.8e, %+1.8e) + color;", // column 4
|
| + m.get(0, 3), m.get(1, 3), m.get(2, 3));
|
| }
|
|
|
| private:
|
| class Transform matrix_;
|
| };
|
|
|
| -class ColorTransformFromLinear : public ColorTransformStep {
|
| +class ColorTransformSkTransferFn : public ColorTransformStep {
|
| public:
|
| - explicit ColorTransformFromLinear(ColorSpace::TransferID transfer,
|
| - const SkColorSpaceTransferFn& fn,
|
| - bool fn_valid)
|
| - : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {
|
| - if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
|
| - transfer_ = ColorSpace::TransferID::LINEAR;
|
| + explicit ColorTransformSkTransferFn(const SkColorSpaceTransferFn& fn)
|
| + : fn_(fn) {}
|
| + ColorTransformSkTransferFn* GetSkTransferFn() override { return this; }
|
| +
|
| + bool Join(ColorTransformStep* next_untyped) override {
|
| + ColorTransformSkTransferFn* next = next_untyped->GetSkTransferFn();
|
| + if (!next)
|
| + return false;
|
| + if (SkTransferFnsApproximatelyCancel(fn_, next->fn_)) {
|
| + // Set to be the identity.
|
| + fn_.fA = 1;
|
| + fn_.fB = 0;
|
| + fn_.fC = 1;
|
| + fn_.fD = 0;
|
| + fn_.fE = 0;
|
| + fn_.fF = 0;
|
| + fn_.fG = 1;
|
| + return true;
|
| + }
|
| + return false;
|
| }
|
| - ColorTransformFromLinear* GetFromLinear() override { return this; }
|
| - bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
|
| +
|
| + bool IsNull() override { return SkTransferFnIsApproximatelyIdentity(fn_); }
|
| +
|
| void Transform(ColorTransform::TriStim* colors, size_t num) const override {
|
| - if (fn_valid_) {
|
| - for (size_t i = 0; i < num; i++) {
|
| - colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
|
| - colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y()));
|
| - colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z()));
|
| - }
|
| + for (size_t i = 0; i < num; i++) {
|
| + colors[i].set_x(SkTransferFnEval(fn_, colors[i].x()));
|
| + colors[i].set_y(SkTransferFnEval(fn_, colors[i].y()));
|
| + colors[i].set_z(SkTransferFnEval(fn_, colors[i].z()));
|
| + }
|
| + }
|
| +
|
| + bool CanAppendShaderSource() override { return true; }
|
| +
|
| + void AppendShaderSourceChannel(std::string* result, const char* value) {
|
| + const float kEpsilon = 1.f / 1024.f;
|
| +
|
| + // Construct the linear segment
|
| + // linear = C * x + F
|
| + // Elide operations that will be close to the identity.
|
| + std::string linear = value;
|
| + if (std::abs(fn_.fC - 1.f) > kEpsilon)
|
| + linear = Str(fn_.fC) + " * " + linear;
|
| + if (std::abs(fn_.fF) > kEpsilon)
|
| + linear = linear + " + " + Str(fn_.fF);
|
| +
|
| + // Construct the nonlinear segment.
|
| + // nonlinear = pow(A * x + B, G) + E
|
| + // Elide operations (especially the pow) that will be close to the
|
| + // identity.
|
| + std::string nonlinear = value;
|
| + if (std::abs(fn_.fA - 1.f) > kEpsilon)
|
| + nonlinear = Str(fn_.fA) + " * " + nonlinear;
|
| + if (std::abs(fn_.fB) > kEpsilon)
|
| + nonlinear = nonlinear + " + " + Str(fn_.fB);
|
| + if (std::abs(fn_.fG - 1.f) > kEpsilon)
|
| + nonlinear = "pow(" + nonlinear + ", " + Str(fn_.fG) + ")";
|
| + if (std::abs(fn_.fE) > kEpsilon)
|
| + nonlinear = nonlinear + " + " + Str(fn_.fE);
|
| +
|
| + // Add both parts, skpping the if clause if possible.
|
| + if (fn_.fD > kEpsilon) {
|
| + SRC("if (%s < %f)", value, fn_.fD);
|
| + SRC(" %s = %s;", value, linear.c_str());
|
| + SRC("else");
|
| + SRC(" %s = %s;", value, nonlinear.c_str());
|
| } else {
|
| - for (size_t i = 0; i < num; i++) {
|
| - colors[i].set_x(FromLinear(transfer_, colors[i].x()));
|
| - colors[i].set_y(FromLinear(transfer_, colors[i].y()));
|
| - colors[i].set_z(FromLinear(transfer_, colors[i].z()));
|
| - }
|
| + SRC("%s = %s;", value, nonlinear.c_str());
|
| }
|
| }
|
|
|
| + void AppendShaderSource(std::string* result) override {
|
| + // Append the transfer function for each channel.
|
| + AppendShaderSourceChannel(result, "color.r");
|
| + AppendShaderSourceChannel(result, "color.g");
|
| + AppendShaderSourceChannel(result, "color.b");
|
| + }
|
| +
|
| private:
|
| - friend class ColorTransformToLinear;
|
| - ColorSpace::TransferID transfer_;
|
| SkColorSpaceTransferFn fn_;
|
| - bool fn_valid_ = false;
|
| };
|
|
|
| -class ColorTransformToLinear : public ColorTransformStep {
|
| +class ColorTransformFromLinear : public ColorTransformStep {
|
| public:
|
| - explicit ColorTransformToLinear(ColorSpace::TransferID transfer,
|
| - const SkColorSpaceTransferFn& fn,
|
| - bool fn_valid)
|
| - : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {
|
| - if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
|
| - transfer_ = ColorSpace::TransferID::LINEAR;
|
| + explicit ColorTransformFromLinear(ColorSpace::TransferID transfer)
|
| + : transfer_(transfer) {}
|
| + ColorTransformFromLinear* GetFromLinear() override { return this; }
|
| + bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
|
| + void Transform(ColorTransform::TriStim* colors, size_t num) const override {
|
| + for (size_t i = 0; i < num; i++) {
|
| + colors[i].set_x(FromLinear(transfer_, colors[i].x()));
|
| + colors[i].set_y(FromLinear(transfer_, colors[i].y()));
|
| + colors[i].set_z(FromLinear(transfer_, colors[i].z()));
|
| + }
|
| }
|
|
|
| - static bool IsGamma22(ColorSpace::TransferID transfer) {
|
| - switch (transfer) {
|
| - // We don't need to check BT709 here because it's been translated into
|
| - // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below.
|
| - case ColorSpace::TransferID::GAMMA22:
|
| - case ColorSpace::TransferID::IEC61966_2_1: // SRGB
|
| - return true;
|
| + private:
|
| + friend class ColorTransformToLinear;
|
| + ColorSpace::TransferID transfer_;
|
| +};
|
|
|
| - default:
|
| - return false;
|
| - }
|
| - }
|
| +class ColorTransformToLinear : public ColorTransformStep {
|
| + public:
|
| + explicit ColorTransformToLinear(ColorSpace::TransferID transfer)
|
| + : transfer_(transfer) {}
|
|
|
| bool Join(ColorTransformStep* next_untyped) override {
|
| ColorTransformFromLinear* next = next_untyped->GetFromLinear();
|
| if (!next)
|
| return false;
|
| - // TODO(ccameron): Use SkTransferFnsApproximatelyCancel and
|
| - // SkTransferFnIsApproximatelyIdentity to merge parametric transfer
|
| - // functions.
|
| - if (transfer_ == next->transfer_ ||
|
| - (IsGamma22(transfer_) && IsGamma22(next->transfer_))) {
|
| + if (transfer_ == next->transfer_) {
|
| transfer_ = ColorSpace::TransferID::LINEAR;
|
| return true;
|
| }
|
| @@ -443,13 +497,7 @@ class ColorTransformToLinear : public ColorTransformStep {
|
| }
|
|
|
| void Transform(ColorTransform::TriStim* colors, size_t num) const override {
|
| - if (fn_valid_) {
|
| - for (size_t i = 0; i < num; i++) {
|
| - colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
|
| - colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y()));
|
| - colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z()));
|
| - }
|
| - } else if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) {
|
| + if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) {
|
| for (size_t i = 0; i < num; i++) {
|
| ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()),
|
| ToLinear(transfer_, colors[i].y()),
|
| @@ -475,8 +523,6 @@ class ColorTransformToLinear : public ColorTransformStep {
|
|
|
| private:
|
| ColorSpace::TransferID transfer_;
|
| - SkColorSpaceTransferFn fn_;
|
| - bool fn_valid_ = false;
|
| };
|
|
|
| // BT2020 Constant Luminance is different than most other
|
| @@ -623,9 +669,12 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
|
| return;
|
|
|
| SkColorSpaceTransferFn to_linear_fn;
|
| - bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
|
| - steps_.push_back(base::MakeUnique<ColorTransformToLinear>(
|
| - from.transfer_, to_linear_fn, to_linear_fn_valid));
|
| + if (from.GetTransferFunction(&to_linear_fn)) {
|
| + steps_.push_back(
|
| + base::MakeUnique<ColorTransformSkTransferFn>(to_linear_fn));
|
| + } else {
|
| + steps_.push_back(base::MakeUnique<ColorTransformToLinear>(from.transfer_));
|
| + }
|
|
|
| if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
|
| // BT2020 CL is a special case.
|
| @@ -642,9 +691,12 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
|
| }
|
|
|
| SkColorSpaceTransferFn from_linear_fn;
|
| - bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
|
| - steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(
|
| - to.transfer_, from_linear_fn, from_linear_fn_valid));
|
| + if (to.GetInverseTransferFunction(&from_linear_fn)) {
|
| + steps_.push_back(
|
| + base::MakeUnique<ColorTransformSkTransferFn>(from_linear_fn));
|
| + } else {
|
| + steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(to.transfer_));
|
| + }
|
|
|
| steps_.push_back(
|
| base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
|
|
|