Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(935)

Unified Diff: ui/gfx/color_transform.cc

Issue 2696603003: color: Towards ColorTransform optimizations and code generation (Closed)
Patch Set: Remove unneeded refactor Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/color_transform.cc
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index ecc8aaf2297789a328609a0347b3757a2bbe0307..a2c4d635f0ee0b587f17c2779a1cec69662c8474 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -5,14 +5,16 @@
#include "ui/gfx/color_transform.h"
#include <algorithm>
+#include <cmath>
hubbe 2017/02/13 22:03:18 verbose :)
ccameron 2017/02/13 23:18:42 Agree :/ ...but I got a whole bunch of presubmit
#include <vector>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "third_party/qcms/src/qcms.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/icc_profile.h"
+#include "ui/gfx/skia_color_space_util.h"
#include "ui/gfx/transform.h"
-#include "third_party/qcms/src/qcms.h"
#ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H
extern "C" {
@@ -22,13 +24,17 @@ extern "C" {
namespace gfx {
-float EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x) {
- if (x < 0)
- return 0;
- if (x < fn.fD)
- return fn.fC * x + fn.fF;
- return powf(fn.fA * x + fn.fB, fn.fG) + fn.fE;
-}
+namespace {
+
+// Helper for scoped QCMS profiles.
+struct QcmsProfileDeleter {
+ void operator()(qcms_profile* p) {
+ if (p) {
+ qcms_profile_release(p);
+ }
+ }
+};
+using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>;
Transform Invert(const Transform& t) {
Transform ret = t;
@@ -47,22 +53,22 @@ float FromLinear(ColorSpace::TransferID id, float v) {
case ColorSpace::TransferID::LOG:
if (v < 0.01f)
return 0.0f;
- return 1.0f + log(v) / log(10.0f) / 2.0f;
+ return 1.0f + std::log(v) / std::log(10.0f) / 2.0f;
case ColorSpace::TransferID::LOG_SQRT:
- if (v < sqrt(10.0f) / 1000.0f)
+ if (v < std::sqrt(10.0f) / 1000.0f)
return 0.0f;
- return 1.0f + log(v) / log(10.0f) / 2.5f;
+ return 1.0f + std::log(v) / std::log(10.0f) / 2.5f;
case ColorSpace::TransferID::IEC61966_2_4: {
float a = 1.099296826809442f;
float b = 0.018053968510807f;
if (v < -b) {
- return -a * powf(-v, 0.45f) + (a - 1.0f);
+ return -a * std::pow(-v, 0.45f) + (a - 1.0f);
} else if (v <= b) {
return 4.5f * v;
} else {
- return a * powf(v, 0.45f) - (a - 1.0f);
+ return a * std::pow(v, 0.45f) - (a - 1.0f);
}
}
@@ -71,24 +77,25 @@ float FromLinear(ColorSpace::TransferID id, float v) {
float b = 0.018f;
float l = 0.0045f;
if (v < -l) {
- return -(a * powf(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
+ return -(a * std::pow(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
} else if (v <= b) {
return 4.5f * v;
} else {
- return a * powf(v, 0.45f) - (a - 1.0f);
+ return a * std::pow(v, 0.45f) - (a - 1.0f);
}
}
case ColorSpace::TransferID::SMPTEST2084: {
// Go from scRGB levels to 0-1.
v *= 80.0f / 10000.0f;
- v = fmax(0.0f, v);
+ v = std::max(0.0f, v);
float m1 = (2610.0f / 4096.0f) / 4.0f;
float m2 = (2523.0f / 4096.0f) * 128.0f;
float c1 = 3424.0f / 4096.0f;
float c2 = (2413.0f / 4096.0f) * 32.0f;
float c3 = (2392.0f / 4096.0f) * 32.0f;
- return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2);
+ return std::pow(
+ (c1 + c2 * std::pow(v, m1)) / (1.0f + c3 * std::pow(v, m1)), m2);
}
// Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
@@ -96,11 +103,11 @@ float FromLinear(ColorSpace::TransferID id, float v) {
const float a = 0.17883277f;
const float b = 0.28466892f;
const float c = 0.55991073f;
- v = fmax(0.0f, v);
+ v = std::max(0.0f, v);
if (v <= 1)
return 0.5f * sqrtf(v);
else
- return a * log(v - b) + c;
+ return a * std::log(v - b) + c;
}
default:
@@ -116,48 +123,49 @@ float ToLinear(ColorSpace::TransferID id, float v) {
case ColorSpace::TransferID::LOG:
if (v < 0.0f)
return 0.0f;
- return powf(10.0f, (v - 1.0f) * 2.0f);
+ return std::pow(10.0f, (v - 1.0f) * 2.0f);
case ColorSpace::TransferID::LOG_SQRT:
if (v < 0.0f)
return 0.0f;
- return powf(10.0f, (v - 1.0f) * 2.5f);
+ return std::pow(10.0f, (v - 1.0f) * 2.5f);
case ColorSpace::TransferID::IEC61966_2_4: {
float a = 1.099296826809442f;
- float b = 0.018053968510807f;
- if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) {
- return -powf((a - 1.0f - v) / a, 1.0f / 0.45f);
- } else if (v <= FromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) {
+ float c = -1.047844f; // FromLinear(IEC61966_2_4, -a)
+ float d = 0.081243f; // FromLinear(IEC61966_2_4, b));
+ if (v < c) {
+ return -std::pow((a - 1.0f - v) / a, 1.0f / 0.45f);
+ } else if (v <= d) {
return v / 4.5f;
} else {
- return powf((v + a - 1.0f) / a, 1.0f / 0.45f);
+ return std::pow((v + a - 1.0f) / a, 1.0f / 0.45f);
}
}
case ColorSpace::TransferID::BT1361_ECG: {
float a = 1.099f;
- float b = 0.018f;
- float l = 0.0045f;
- if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) {
- return -powf((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
- } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) {
+ float c = -0.02025f; // FromLinear(BT1361_ECG, -l)
hubbe 2017/02/13 22:03:18 Not a fan of refactoring and adding slight modific
ccameron 2017/02/13 23:18:42 This should be functionally identical -- it just r
+ float d = 0.081f; // FromLinear(BT1361_ECG, b)
+ if (v < c) {
+ return -std::pow((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
+ } else if (v <= d) {
return v / 4.5f;
} else {
- return powf((v + a - 1.0f) / a, 1.0f / 0.45f);
+ return std::pow((v + a - 1.0f) / a, 1.0f / 0.45f);
}
}
case ColorSpace::TransferID::SMPTEST2084: {
- v = fmax(0.0f, v);
+ v = std::max(0.0f, v);
float m1 = (2610.0f / 4096.0f) / 4.0f;
float m2 = (2523.0f / 4096.0f) * 128.0f;
float c1 = 3424.0f / 4096.0f;
float c2 = (2413.0f / 4096.0f) * 32.0f;
float c3 = (2392.0f / 4096.0f) * 32.0f;
- v = powf(
- fmax(powf(v, 1.0f / m2) - c1, 0) / (c2 - c3 * powf(v, 1.0f / m2)),
- 1.0f / m1);
+ v = std::pow(std::max(std::pow(v, 1.0f / m2) - c1, 0.0f) /
+ (c2 - c3 * std::pow(v, 1.0f / m2)),
+ 1.0f / m1);
// This matches the scRGB definition that 1.0 means 80 nits.
// TODO(hubbe): It would be *nice* if 1.0 meant more than that, but
// that might be difficult to do right now.
@@ -166,12 +174,12 @@ float ToLinear(ColorSpace::TransferID id, float v) {
}
case ColorSpace::TransferID::SMPTEST2084_NON_HDR:
- v = fmax(0.0f, v);
- return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f);
+ v = std::max(0.0f, v);
+ return std::min(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f);
// Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
case ColorSpace::TransferID::ARIB_STD_B67: {
- v = fmax(0.0f, v);
+ v = std::max(0.0f, v);
const float a = 0.17883277f;
const float b = 0.28466892f;
const float c = 0.55991073f;
@@ -179,7 +187,7 @@ float ToLinear(ColorSpace::TransferID id, float v) {
if (v <= 0.5f) {
v_ = (v * 2.0f) * (v * 2.0f);
} else {
- v_ = exp((v - c) / a) + b;
+ v_ = std::exp((v - c) / a) + b;
}
return v_;
}
@@ -204,67 +212,65 @@ Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space) {
return Transform(range_adjust_matrix);
}
+Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
+ SkMatrix44 primary_matrix;
+ color_space.GetPrimaryMatrix(&primary_matrix);
+ return Transform(primary_matrix);
+}
+
+} // namespace
+
class ColorTransformMatrix;
-class ColorTransformToLinear;
class ColorTransformFromLinear;
class ColorTransformToBT2020CL;
class ColorTransformFromBT2020CL;
class ColorTransformNull;
+class QCMSColorTransform;
-class ColorTransformInternal : public ColorTransform {
+class ColorTransform::Step {
public:
- // Visitor pattern, Prepend() calls return prev->Join(this).
- virtual bool Prepend(ColorTransformInternal* prev) = 0;
+ virtual ~Step() {}
+ virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; }
+ virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; }
+ virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; }
+ virtual ColorTransformMatrix* GetMatrix() { return nullptr; }
+ virtual ColorTransformNull* GetNull() { return nullptr; }
+ virtual QCMSColorTransform* GetQCMS() { return nullptr; }
// Join methods, returns true if the |next| transform was successfully
// assimilated into |this|.
// If Join() returns true, |next| is no longer needed and can be deleted.
- virtual bool Join(const ColorTransformToLinear& next) { return false; }
- virtual bool Join(const ColorTransformFromLinear& next) { return false; }
- virtual bool Join(const ColorTransformToBT2020CL& next) { return false; }
- virtual bool Join(const ColorTransformFromBT2020CL& next) { return false; }
- virtual bool Join(const ColorTransformMatrix& next) { return false; }
- virtual bool Join(const ColorTransformNull& next) { return true; }
+ virtual bool Join(Step* next) { return false; }
// Return true if this is a null transform.
virtual bool IsNull() { return false; }
+
+ virtual void transform(ColorTransform::TriStim* color, size_t num) = 0;
};
-class ColorTransformNull : public ColorTransformInternal {
+class ColorTransformNull : public ColorTransform::Step {
public:
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
+ ColorTransformNull* GetNull() override { return this; }
bool IsNull() override { return true; }
void transform(ColorTransform::TriStim* color, size_t num) override {}
};
-class ColorTransformMatrix : public ColorTransformInternal {
+class ColorTransformMatrix : public ColorTransform::Step {
public:
explicit ColorTransformMatrix(const Transform& matrix) : matrix_(matrix) {}
-
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
-
- bool Join(const ColorTransformMatrix& next) override {
- Transform tmp = next.matrix_;
+ ColorTransformMatrix* GetMatrix() override { return this; }
+ bool Join(ColorTransform::Step* next_untyped) override {
+ ColorTransformMatrix* next = next_untyped->GetMatrix();
+ if (!next)
+ return false;
+ Transform tmp = next->matrix_;
tmp *= matrix_;
matrix_ = tmp;
return true;
}
bool IsNull() override {
- // Returns true if we're very close to an identity matrix.
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- float expected = i == j ? 1.0f : 0.0f;
- if (fabs(matrix_.matrix().get(i, j) - expected) > 0.00001f) {
- return false;
- }
- }
- }
- return true;
+ return SkMatrixIsApproximatelyIdentity(matrix_.matrix());
}
void transform(ColorTransform::TriStim* colors, size_t num) override {
@@ -276,7 +282,7 @@ class ColorTransformMatrix : public ColorTransformInternal {
Transform matrix_;
};
-class ColorTransformFromLinear : public ColorTransformInternal {
+class ColorTransformFromLinear : public ColorTransform::Step {
public:
explicit ColorTransformFromLinear(ColorSpace::TransferID transfer,
const SkColorSpaceTransferFn& fn,
@@ -285,12 +291,8 @@ class ColorTransformFromLinear : public ColorTransformInternal {
if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
transfer_ = ColorSpace::TransferID::LINEAR;
}
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
-
+ ColorTransformFromLinear* GetFromLinear() override { return this; }
bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
-
void transform(ColorTransform::TriStim* colors, size_t num) override {
if (fn_valid_) {
for (size_t i = 0; i < num; i++) {
@@ -314,7 +316,7 @@ class ColorTransformFromLinear : public ColorTransformInternal {
bool fn_valid_ = false;
};
-class ColorTransformToLinear : public ColorTransformInternal {
+class ColorTransformToLinear : public ColorTransform::Step {
public:
explicit ColorTransformToLinear(ColorSpace::TransferID transfer,
const SkColorSpaceTransferFn& fn,
@@ -324,10 +326,6 @@ class ColorTransformToLinear : public ColorTransformInternal {
transfer_ = ColorSpace::TransferID::LINEAR;
}
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
-
static bool IsGamma22(ColorSpace::TransferID transfer) {
switch (transfer) {
// We don't need to check BT709 here because it's been translated into
@@ -341,9 +339,15 @@ class ColorTransformToLinear : public ColorTransformInternal {
}
}
- bool Join(const ColorTransformFromLinear& next) override {
- if (transfer_ == next.transfer_ ||
- (IsGamma22(transfer_) && IsGamma22(next.transfer_))) {
+ bool Join(ColorTransform::Step* 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_))) {
transfer_ = ColorSpace::TransferID::LINEAR;
return true;
}
@@ -420,13 +424,12 @@ class ColorTransformToLinear : public ColorTransformInternal {
// Then we run the transfer function like normal, and finally
// this class is inserted as an extra step which takes calculates
// the U and V values.
-class ColorTransformToBT2020CL : public ColorTransformInternal {
+class ColorTransformToBT2020CL : public ColorTransform::Step {
public:
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
-
- bool Join(const ColorTransformFromBT2020CL& next) override {
+ bool Join(ColorTransform::Step* next_untyped) override {
+ ColorTransformFromBT2020CL* next = next_untyped->GetFromBT2020CL();
+ if (!next)
+ return false;
if (null_)
return false;
null_ = true;
@@ -459,13 +462,12 @@ class ColorTransformToBT2020CL : public ColorTransformInternal {
};
// Inverse of ColorTransformToBT2020CL, see comment above for more info.
-class ColorTransformFromBT2020CL : public ColorTransformInternal {
+class ColorTransformFromBT2020CL : public ColorTransform::Step {
public:
- bool Prepend(ColorTransformInternal* prev) override {
- return prev->Join(*this);
- }
-
- bool Join(const ColorTransformToBT2020CL& next) override {
+ bool Join(ColorTransform::Step* next_untyped) override {
+ ColorTransformToBT2020CL* next = next_untyped->GetToBT2020CL();
+ if (!next)
+ return false;
if (null_)
return false;
null_ = true;
@@ -501,188 +503,134 @@ class ColorTransformFromBT2020CL : public ColorTransformInternal {
bool null_ = false;
};
-class ChainColorTransform : public ColorTransform {
- public:
- ChainColorTransform(std::unique_ptr<ColorTransform> a,
- std::unique_ptr<ColorTransform> b)
- : a_(std::move(a)), b_(std::move(b)) {}
-
- private:
- void transform(TriStim* colors, size_t num) override {
- a_->transform(colors, num);
- b_->transform(colors, num);
- }
- std::unique_ptr<ColorTransform> a_;
- std::unique_ptr<ColorTransform> b_;
-};
+// static
+void ColorTransform::Append(ColorSpace from,
+ const ColorSpace& to,
+ ColorTransform::Intent intent,
+ StepList* builder) {
+ if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
+ switch (from.transfer_) {
+ case ColorSpace::TransferID::UNSPECIFIED:
+ case ColorSpace::TransferID::BT709:
+ case ColorSpace::TransferID::SMPTE170M:
+ // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
+ // content. However, most displays actually use a gamma of 2.2, and
+ // user studies shows that users don't really care. Using the same
+ // gamma as the display will let us optimize a lot more, so lets stick
+ // with using the SRGB transfer function.
+ from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
+ break;
-class TransformBuilder {
- public:
- void Append(std::unique_ptr<ColorTransformInternal> transform) {
- if (!disable_optimizations_ && transform->IsNull())
- return; // Null transform
- transforms_.push_back(std::move(transform));
- if (disable_optimizations_)
- return;
- while (transforms_.size() >= 2 &&
- transforms_.back()->Prepend(
- transforms_[transforms_.size() - 2].get())) {
- transforms_.pop_back();
- if (transforms_.back()->IsNull()) {
- transforms_.pop_back();
+ case ColorSpace::TransferID::SMPTEST2084:
+ if (!to.IsHDR()) {
+ // We don't have an HDR display, so replace SMPTE 2084 with
+ // something that returns ranges more or less suitable for a normal
+ // display.
+ from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
+ }
break;
- }
- }
- }
- std::unique_ptr<ColorTransform> GetTransform() {
- if (transforms_.empty())
- return base::MakeUnique<ColorTransformNull>();
- std::unique_ptr<ColorTransform> ret(std::move(transforms_.back()));
- transforms_.pop_back();
+ case ColorSpace::TransferID::ARIB_STD_B67:
+ if (!to.IsHDR()) {
+ // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
+ // displays.
+ from.transfer_ = ColorSpace::TransferID::GAMMA24;
+ }
+ break;
- while (!transforms_.empty()) {
- ret = std::unique_ptr<ColorTransform>(new ChainColorTransform(
- std::move(transforms_.back()), std::move(ret)));
- transforms_.pop_back();
+ default: // Do nothing
+ break;
}
- return ret;
- }
-
- void disable_optimizations() { disable_optimizations_ = true; }
-
- private:
- bool disable_optimizations_ = false;
- std::vector<std::unique_ptr<ColorTransformInternal>> transforms_;
-};
-
-class ColorSpaceToColorSpaceTransform {
- public:
- static Transform GetPrimaryTransform(const ColorSpace& c) {
- SkMatrix44 sk_matrix;
- c.GetPrimaryMatrix(&sk_matrix);
- return Transform(sk_matrix);
+ // TODO(hubbe): shrink gamuts here (never stretch gamuts)
}
- static void ColorSpaceToColorSpace(ColorSpace from,
- ColorSpace to,
- ColorTransform::Intent intent,
- TransformBuilder* builder) {
- if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
- switch (from.transfer_) {
- case ColorSpace::TransferID::UNSPECIFIED:
- case ColorSpace::TransferID::BT709:
- case ColorSpace::TransferID::SMPTE170M:
- // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
- // content. However, most displays actually use a gamma of 2.2, and
- // user studies shows that users don't really care. Using the same
- // gamma as the display will let us optimize a lot more, so lets stick
- // with using the SRGB transfer function.
- from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
- break;
-
- case ColorSpace::TransferID::SMPTEST2084:
- if (!to.IsHDR()) {
- // We don't have an HDR display, so replace SMPTE 2084 with
- // something that returns ranges more or less suitable for a normal
- // display.
- from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
- }
- break;
-
- case ColorSpace::TransferID::ARIB_STD_B67:
- if (!to.IsHDR()) {
- // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
- // displays.
- from.transfer_ = ColorSpace::TransferID::GAMMA24;
- }
- break;
-
- default: // Do nothing
- break;
- }
-
- // TODO(hubbe): shrink gamuts here (never stretch gamuts)
- }
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(from)));
- builder->Append(base::MakeUnique<ColorTransformMatrix>(
- GetRangeAdjustMatrix(from)));
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(from))));
- builder->Append(base::MakeUnique<ColorTransformMatrix>(
- Invert(GetTransferMatrix(from))));
+ SkColorSpaceTransferFn to_linear_fn;
+ bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
+ builder->push_back(base::MakeUnique<ColorTransformToLinear>(
+ from.transfer_, to_linear_fn, to_linear_fn_valid));
- SkColorSpaceTransferFn to_linear_fn;
- bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
- builder->Append(base::MakeUnique<ColorTransformToLinear>(
- from.transfer_, to_linear_fn, to_linear_fn_valid));
+ if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+ // BT2020 CL is a special case.
+ builder->push_back(base::MakeUnique<ColorTransformFromBT2020CL>());
+ }
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
- if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
- // BT2020 CL is a special case.
- builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>());
- }
- builder->Append(
- base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
-
- builder->Append(base::MakeUnique<ColorTransformMatrix>(
- Invert(GetPrimaryTransform(to))));
- if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
- // BT2020 CL is a special case.
- builder->Append(base::MakeUnique<ColorTransformToBT2020CL>());
- }
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(to))));
+ if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+ // BT2020 CL is a special case.
+ builder->push_back(base::MakeUnique<ColorTransformToBT2020CL>());
+ }
- SkColorSpaceTransferFn from_linear_fn;
- bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
- builder->Append(base::MakeUnique<ColorTransformFromLinear>(
- to.transfer_, from_linear_fn, from_linear_fn_valid));
+ SkColorSpaceTransferFn from_linear_fn;
+ bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
+ builder->push_back(base::MakeUnique<ColorTransformFromLinear>(
+ to.transfer_, from_linear_fn, from_linear_fn_valid));
- builder->Append(
- base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
- builder->Append(base::MakeUnique<ColorTransformMatrix>(
- Invert(GetRangeAdjustMatrix(to))));
- }
-};
+ builder->push_back(
+ base::MakeUnique<ColorTransformMatrix>(Invert(GetRangeAdjustMatrix(to))));
+}
-class QCMSColorTransform : public ColorTransformInternal {
+class QCMSColorTransform : public ColorTransform::Step {
public:
// Takes ownership of the profiles
- QCMSColorTransform(qcms_profile* from, qcms_profile* to)
- : from_(from), to_(to) {}
- ~QCMSColorTransform() override {
- qcms_profile_release(from_);
- qcms_profile_release(to_);
+ QCMSColorTransform(ScopedQcmsProfile from, ScopedQcmsProfile to)
+ : from_(std::move(from)), to_(std::move(to)) {}
+ ~QCMSColorTransform() override {}
+ QCMSColorTransform* GetQCMS() override { return this; }
+ bool Join(ColorTransform::Step* next_untyped) override {
+ QCMSColorTransform* next = next_untyped->GetQCMS();
+ if (!next)
+ return false;
+ if (qcms_profile_match(to_.get(), next->from_.get())) {
+ to_ = std::move(next->to_);
+ return true;
+ }
+ return false;
}
- bool Prepend(ColorTransformInternal* prev) override {
- // Not currently optimizable.
+ bool IsNull() override {
+ if (qcms_profile_match(from_.get(), to_.get()))
+ return true;
return false;
}
- bool IsNull() override { return from_ == to_; }
- void transform(TriStim* colors, size_t num) override {
- CHECK(sizeof(TriStim) == sizeof(float[3]));
+ void transform(ColorTransform::TriStim* colors, size_t num) override {
+ CHECK(sizeof(ColorTransform::TriStim) == sizeof(float[3]));
// QCMS doesn't like numbers outside 0..1
for (size_t i = 0; i < num; i++) {
- colors[i].set_x(fmin(1.0f, fmax(0.0f, colors[i].x())));
- colors[i].set_y(fmin(1.0f, fmax(0.0f, colors[i].y())));
- colors[i].set_z(fmin(1.0f, fmax(0.0f, colors[i].z())));
+ colors[i].set_x(std::min(1.0f, std::max(0.0f, colors[i].x())));
+ colors[i].set_y(std::min(1.0f, std::max(0.0f, colors[i].y())));
+ colors[i].set_z(std::min(1.0f, std::max(0.0f, colors[i].z())));
}
- qcms_chain_transform(from_, to_, reinterpret_cast<float*>(colors),
+ qcms_chain_transform(from_.get(), to_.get(),
+ reinterpret_cast<float*>(colors),
reinterpret_cast<float*>(colors), num * 3);
}
private:
- qcms_profile *from_, *to_;
+ ScopedQcmsProfile from_;
+ ScopedQcmsProfile to_;
};
-qcms_profile* GetQCMSProfileIfAvailable(const ColorSpace& color_space) {
+ScopedQcmsProfile GetQCMSProfileIfAvailable(const ColorSpace& color_space) {
ICCProfile icc_profile = ICCProfile::FromColorSpace(color_space);
if (icc_profile.GetData().empty())
return nullptr;
- return qcms_profile_from_memory(icc_profile.GetData().data(),
- icc_profile.GetData().size());
+ return ScopedQcmsProfile(qcms_profile_from_memory(
+ icc_profile.GetData().data(), icc_profile.GetData().size()));
}
-qcms_profile* GetXYZD50Profile() {
+ScopedQcmsProfile GetXYZD50Profile() {
// QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects
// is in fact not xyY color coordinates, it just wants the x/y values of the
// primaries with Y equal to 1.0.
@@ -700,67 +648,80 @@ qcms_profile* GetXYZD50Profile() {
w.x = 0.34567f;
w.y = 0.35850f;
w.Y = 1.0f;
- return qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f);
+ return ScopedQcmsProfile(qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f));
}
std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform(
const ColorSpace& from,
const ColorSpace& to,
Intent intent) {
- TransformBuilder builder;
- if (intent == Intent::TEST_NO_OPT) {
- builder.disable_optimizations();
- }
+ std::list<std::unique_ptr<Step>> builder;
- qcms_profile* from_profile = GetQCMSProfileIfAvailable(from);
- qcms_profile* to_profile = GetQCMSProfileIfAvailable(to);
+ ScopedQcmsProfile from_profile = GetQCMSProfileIfAvailable(from);
+ ScopedQcmsProfile to_profile = GetQCMSProfileIfAvailable(to);
+ bool has_from_profile = !!from_profile;
+ bool has_to_profile = !!to_profile;
- if (from_profile && to_profile) {
- return std::unique_ptr<ColorTransform>(
- new QCMSColorTransform(from_profile, to_profile));
- }
if (from_profile) {
- builder.Append(std::unique_ptr<ColorTransformInternal>(
- new QCMSColorTransform(from_profile, GetXYZD50Profile())));
+ builder.push_back(base::MakeUnique<QCMSColorTransform>(
+ std::move(from_profile), GetXYZD50Profile()));
}
- ColorSpaceToColorSpaceTransform::ColorSpaceToColorSpace(
- from_profile ? ColorSpace::CreateXYZD50() : from,
- to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder);
+
+ Append(has_from_profile ? ColorSpace::CreateXYZD50() : from,
+ has_to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder);
+
if (to_profile) {
- builder.Append(std::unique_ptr<ColorTransformInternal>(
- new QCMSColorTransform(GetXYZD50Profile(), to_profile)));
+ builder.push_back(base::MakeUnique<QCMSColorTransform>(
+ GetXYZD50Profile(), std::move(to_profile)));
}
- return builder.GetTransform();
-}
+ if (intent != Intent::TEST_NO_OPT)
+ Simplify(&builder);
-// static
-float ColorTransform::ToLinearForTesting(ColorSpace::TransferID transfer,
- float v) {
- ColorSpace space(ColorSpace::PrimaryID::BT709, transfer,
- ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
- SkColorSpaceTransferFn to_linear_fn;
- bool to_linear_fn_valid = space.GetTransferFunction(&to_linear_fn);
- ColorTransformToLinear to_linear_transform(transfer, to_linear_fn,
- to_linear_fn_valid);
- TriStim color(v, v, v);
- to_linear_transform.transform(&color, 1);
- return color.x();
+ return std::unique_ptr<ColorTransform>(
+ new ColorTransform(std::move(builder)));
}
// static
-float ColorTransform::FromLinearForTesting(ColorSpace::TransferID transfer,
- float v) {
- ColorSpace space(ColorSpace::PrimaryID::BT709, transfer,
- ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
- SkColorSpaceTransferFn from_linear_fn;
- bool from_linear_fn_valid = space.GetInverseTransferFunction(&from_linear_fn);
+void ColorTransform::Simplify(StepList* steps) {
+ for (auto iter = steps->begin(); iter != steps->end();) {
+ std::unique_ptr<Step>& this_step = *iter;
+
+ // Try to Join |next_step| into |this_step|. If successful, re-visit the
+ // step before |this_step|.
+ auto iter_next = iter;
+ iter_next++;
+ if (iter_next != steps->end()) {
+ std::unique_ptr<Step>& next_step = *iter_next;
+ if (this_step->Join(next_step.get())) {
+ steps->erase(iter_next);
+ if (iter != steps->begin())
+ --iter;
+ continue;
+ }
+ }
+
+ // If |this_step| step is a no-op, remove it, and re-visit the step before
+ // |this_step|.
+ if (this_step->IsNull()) {
+ iter = steps->erase(iter);
+ if (iter != steps->begin())
+ --iter;
+ continue;
+ }
- ColorTransformFromLinear from_linear_transform(transfer, from_linear_fn,
- from_linear_fn_valid);
- TriStim color(v, v, v);
- from_linear_transform.transform(&color, 1);
- return color.x();
+ ++iter;
+ }
+}
+
+void ColorTransform::transform(ColorTransform::TriStim* colors, size_t num) {
+ for (const auto& step : steps_)
+ step->transform(colors, num);
}
+ColorTransform::ColorTransform(ColorTransform::StepList steps)
+ : steps_(std::move(steps)) {}
+
+ColorTransform::~ColorTransform() {}
+
} // namespace gfx

Powered by Google App Engine
This is Rietveld 408576698