Index: ui/gfx/color_transform.cc |
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc |
index 15c7ac4e3df90af437215b41a968b8d8367f414c..043515a62573c4de4d4b2f94137addceecfcfa48 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("%f", 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; } |
@@ -348,72 +354,120 @@ class ColorTransformMatrix : public ColorTransformStep { |
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) { |
hubbe
2017/02/16 23:06:38
Please add some tests for this.
It would also help
ccameron
2017/02/16 23:51:58
Mmh, good point -- this was hit a little bit by th
hubbe
2017/02/17 00:06:00
Could I get one of two tests on the form:
string
ccameron
2017/02/19 22:28:07
I don't feel that that sort of test is particularl
hubbe
2017/02/20 05:01:02
That is exactly the point. I don't want a lot of t
ccameron
2017/02/21 22:32:42
Ah, that's a good idea.
I've added one test which
|
+ 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, as needed. |
+ SRC("// Apply transfer function."); |
+ 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"); |
hubbe
2017/02/16 23:06:38
Is the compiler smart enough to vectorize these?
I
ccameron
2017/02/16 23:51:58
I can't speak to anything but NVIDIA GPUs, but I'm
hubbe
2017/02/17 00:06:00
That's very interesting. It's my impression that s
|
+ 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; |
} |
@@ -441,13 +495,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()), |
@@ -473,8 +521,6 @@ class ColorTransformToLinear : public ColorTransformStep { |
private: |
ColorSpace::TransferID transfer_; |
- SkColorSpaceTransferFn fn_; |
- bool fn_valid_ = false; |
}; |
// BT2020 Constant Luminance is different than most other |
@@ -621,9 +667,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. |
@@ -640,9 +689,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))); |