Index: ui/gfx/color_transform.cc |
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc |
index 4d36fe29ca5b4bd32e6e3831c5479dec8c6e390c..3a63643c2a0fea8e6f8743984522a09a6e2e61d5 100644 |
--- a/ui/gfx/color_transform.cc |
+++ b/ui/gfx/color_transform.cc |
@@ -21,14 +21,6 @@ |
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; |
-} |
- |
Transform Invert(const Transform& t) { |
Transform ret = t; |
if (!t.GetInverse(&ret)) { |
@@ -37,11 +29,179 @@ |
return ret; |
} |
-float FromLinear(ColorSpace::TransferID id, float v) { |
+GFX_EXPORT Transform GetPrimaryMatrix(ColorSpace::PrimaryID id) { |
+ SkColorSpacePrimaries primaries = {0}; |
+ switch (id) { |
+ case ColorSpace::PrimaryID::CUSTOM: |
+ NOTREACHED(); |
+ |
+ case ColorSpace::PrimaryID::RESERVED0: |
+ case ColorSpace::PrimaryID::RESERVED: |
+ case ColorSpace::PrimaryID::UNSPECIFIED: |
+ case ColorSpace::PrimaryID::UNKNOWN: |
+ case ColorSpace::PrimaryID::BT709: |
+ // BT709 is our default case. Put it after the switch just |
+ // in case we somehow get an id which is not listed in the switch. |
+ // (We don't want to use "default", because we want the compiler |
+ // to tell us if we forgot some enum values.) |
+ primaries.fRX = 0.640f; |
+ primaries.fRY = 0.330f; |
+ primaries.fGX = 0.300f; |
+ primaries.fGY = 0.600f; |
+ primaries.fBX = 0.150f; |
+ primaries.fBY = 0.060f; |
+ primaries.fWX = 0.3127f; |
+ primaries.fWY = 0.3290f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::BT470M: |
+ primaries.fRX = 0.67f; |
+ primaries.fRY = 0.33f; |
+ primaries.fGX = 0.21f; |
+ primaries.fGY = 0.71f; |
+ primaries.fBX = 0.14f; |
+ primaries.fBY = 0.08f; |
+ primaries.fWX = 0.31f; |
+ primaries.fWY = 0.316f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::BT470BG: |
+ primaries.fRX = 0.64f; |
+ primaries.fRY = 0.33f; |
+ primaries.fGX = 0.29f; |
+ primaries.fGY = 0.60f; |
+ primaries.fBX = 0.15f; |
+ primaries.fBY = 0.06f; |
+ primaries.fWX = 0.3127f; |
+ primaries.fWY = 0.3290f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::SMPTE170M: |
+ case ColorSpace::PrimaryID::SMPTE240M: |
+ primaries.fRX = 0.630f; |
+ primaries.fRY = 0.340f; |
+ primaries.fGX = 0.310f; |
+ primaries.fGY = 0.595f; |
+ primaries.fBX = 0.155f; |
+ primaries.fBY = 0.070f; |
+ primaries.fWX = 0.3127f; |
+ primaries.fWY = 0.3290f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::FILM: |
+ primaries.fRX = 0.681f; |
+ primaries.fRY = 0.319f; |
+ primaries.fGX = 0.243f; |
+ primaries.fGY = 0.692f; |
+ primaries.fBX = 0.145f; |
+ primaries.fBY = 0.049f; |
+ primaries.fWX = 0.310f; |
+ primaries.fWY = 0.136f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::BT2020: |
+ primaries.fRX = 0.708f; |
+ primaries.fRY = 0.292f; |
+ primaries.fGX = 0.170f; |
+ primaries.fGY = 0.797f; |
+ primaries.fBX = 0.131f; |
+ primaries.fBY = 0.046f; |
+ primaries.fWX = 0.3127f; |
+ primaries.fWY = 0.3290f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::SMPTEST428_1: |
+ primaries.fRX = 1.0f; |
+ primaries.fRY = 0.0f; |
+ primaries.fGX = 0.0f; |
+ primaries.fGY = 1.0f; |
+ primaries.fBX = 0.0f; |
+ primaries.fBY = 0.0f; |
+ primaries.fWX = 1.0f / 3.0f; |
+ primaries.fWY = 1.0f / 3.0f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::SMPTEST431_2: |
+ primaries.fRX = 0.680f; |
+ primaries.fRY = 0.320f; |
+ primaries.fGX = 0.265f; |
+ primaries.fGY = 0.690f; |
+ primaries.fBX = 0.150f; |
+ primaries.fBY = 0.060f; |
+ primaries.fWX = 0.314f; |
+ primaries.fWY = 0.351f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::SMPTEST432_1: |
+ primaries.fRX = 0.680f; |
+ primaries.fRY = 0.320f; |
+ primaries.fGX = 0.265f; |
+ primaries.fGY = 0.690f; |
+ primaries.fBX = 0.150f; |
+ primaries.fBY = 0.060f; |
+ primaries.fWX = 0.3127f; |
+ primaries.fWY = 0.3290f; |
+ break; |
+ |
+ case ColorSpace::PrimaryID::XYZ_D50: |
+ primaries.fRX = 1.0f; |
+ primaries.fRY = 0.0f; |
+ primaries.fGX = 0.0f; |
+ primaries.fGY = 1.0f; |
+ primaries.fBX = 0.0f; |
+ primaries.fBY = 0.0f; |
+ primaries.fWX = 0.34567f; |
+ primaries.fWY = 0.35850f; |
+ break; |
+ } |
+ |
+ SkMatrix44 matrix; |
+ primaries.toXYZD50(&matrix); |
+ return Transform(matrix); |
+} |
+ |
+GFX_EXPORT float FromLinear(ColorSpace::TransferID id, float v) { |
switch (id) { |
case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
// Should already be handled. |
- break; |
+ NOTREACHED(); |
+ case ColorSpace::TransferID::CUSTOM: |
+ // TODO(hubbe): Actually implement custom transfer functions. |
+ case ColorSpace::TransferID::RESERVED0: |
+ case ColorSpace::TransferID::RESERVED: |
+ case ColorSpace::TransferID::UNSPECIFIED: |
+ case ColorSpace::TransferID::UNKNOWN: |
+ // All unknown values default to BT709 |
+ |
+ case ColorSpace::TransferID::BT709: |
+ case ColorSpace::TransferID::SMPTE170M: |
+ case ColorSpace::TransferID::BT2020_10: |
+ case ColorSpace::TransferID::BT2020_12: |
+ // BT709 is our "default" cause, so put the code after the switch |
+ // to avoid "control reaches end of non-void function" errors. |
+ break; |
+ |
+ case ColorSpace::TransferID::GAMMA22: |
+ v = fmax(0.0f, v); |
+ return powf(v, 1.0f / 2.2f); |
+ |
+ case ColorSpace::TransferID::GAMMA28: |
+ v = fmax(0.0f, v); |
+ return powf(v, 1.0f / 2.8f); |
+ |
+ case ColorSpace::TransferID::SMPTE240M: { |
+ v = fmax(0.0f, v); |
+ float a = 1.11157219592173128753f; |
+ float b = 0.02282158552944503135f; |
+ if (v <= b) { |
+ return 4.0f * v; |
+ } else { |
+ return a * powf(v, 0.45f) - (a - 1.0f); |
+ } |
+ } |
+ |
+ case ColorSpace::TransferID::LINEAR: |
+ return v; |
case ColorSpace::TransferID::LOG: |
if (v < 0.01f) |
@@ -78,6 +238,16 @@ |
} |
} |
+ case ColorSpace::TransferID::IEC61966_2_1: { // SRGB |
+ v = fmax(0.0f, v); |
+ float a = 1.055f; |
+ float b = 0.0031308f; |
+ if (v < b) { |
+ return 12.92f * v; |
+ } else { |
+ return a * powf(v, 1.0f / 2.4f) - (a - 1.0f); |
+ } |
+ } |
case ColorSpace::TransferID::SMPTEST2084: { |
// Go from scRGB levels to 0-1. |
v *= 80.0f / 10000.0f; |
@@ -90,6 +260,10 @@ |
return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2); |
} |
+ case ColorSpace::TransferID::SMPTEST428_1: |
+ v = fmax(0.0f, v); |
+ return powf(48.0f * v + 52.37f, 1.0f / 2.6f); |
+ |
// Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf |
case ColorSpace::TransferID::ARIB_STD_B67: { |
const float a = 0.17883277f; |
@@ -102,16 +276,62 @@ |
return a * log(v - b) + c; |
} |
- default: |
- // Handled by SkColorSpaceTransferFn. |
- break; |
- } |
- NOTREACHED(); |
- return 0; |
+ // Chrome-specific values below |
+ case ColorSpace::TransferID::GAMMA24: |
+ v = fmax(0.0f, v); |
+ return powf(v, 1.0f / 2.4f); |
+ } |
+ |
+ v = fmax(0.0f, v); |
+ float a = 1.099296826809442f; |
+ float b = 0.018053968510807f; |
+ if (v <= b) { |
+ return 4.5f * v; |
+ } else { |
+ return a * powf(v, 0.45f) - (a - 1.0f); |
+ } |
} |
-float ToLinear(ColorSpace::TransferID id, float v) { |
+GFX_EXPORT float ToLinear(ColorSpace::TransferID id, float v) { |
switch (id) { |
+ case ColorSpace::TransferID::CUSTOM: |
+ // TODO(hubbe): Actually implement custom transfer functions. |
+ case ColorSpace::TransferID::RESERVED0: |
+ case ColorSpace::TransferID::RESERVED: |
+ case ColorSpace::TransferID::UNSPECIFIED: |
+ case ColorSpace::TransferID::UNKNOWN: |
+ // All unknown values default to BT709 |
+ |
+ case ColorSpace::TransferID::BT709: |
+ case ColorSpace::TransferID::SMPTE170M: |
+ case ColorSpace::TransferID::BT2020_10: |
+ case ColorSpace::TransferID::BT2020_12: |
+ // BT709 is our "default" cause, so put the code after the switch |
+ // to avoid "control reaches end of non-void function" errors. |
+ break; |
+ |
+ case ColorSpace::TransferID::GAMMA22: |
+ v = fmax(0.0f, v); |
+ return powf(v, 2.2f); |
+ |
+ case ColorSpace::TransferID::GAMMA28: |
+ v = fmax(0.0f, v); |
+ return powf(v, 2.8f); |
+ |
+ case ColorSpace::TransferID::SMPTE240M: { |
+ v = fmax(0.0f, v); |
+ float a = 1.11157219592173128753f; |
+ float b = 0.02282158552944503135f; |
+ if (v <= FromLinear(ColorSpace::TransferID::SMPTE240M, b)) { |
+ return v / 4.0f; |
+ } else { |
+ return powf((v + a - 1.0f) / a, 1.0f / 0.45f); |
+ } |
+ } |
+ |
+ case ColorSpace::TransferID::LINEAR: |
+ return v; |
+ |
case ColorSpace::TransferID::LOG: |
if (v < 0.0f) |
return 0.0f; |
@@ -144,6 +364,17 @@ |
return v / 4.5f; |
} else { |
return powf((v + a - 1.0f) / a, 1.0f / 0.45f); |
+ } |
+ } |
+ |
+ case ColorSpace::TransferID::IEC61966_2_1: { // SRGB |
+ v = fmax(0.0f, v); |
+ float a = 1.055f; |
+ float b = 0.0031308f; |
+ if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_1, b)) { |
+ return v / 12.92f; |
+ } else { |
+ return powf((v + a - 1.0f) / a, 2.4f); |
} |
} |
@@ -164,6 +395,14 @@ |
return v; |
} |
+ case ColorSpace::TransferID::SMPTEST428_1: |
+ return (powf(v, 2.6f) - 52.37f) / 48.0f; |
+ |
+ // Chrome-specific values below |
+ case ColorSpace::TransferID::GAMMA24: |
+ v = fmax(0.0f, v); |
+ return powf(v, 2.4f); |
+ |
case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
v = fmax(0.0f, v); |
return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f); |
@@ -182,13 +421,16 @@ |
} |
return v_; |
} |
- |
- default: |
- // Handled by SkColorSpaceTransferFn. |
- break; |
- } |
- NOTREACHED(); |
- return 0; |
+ } |
+ |
+ v = fmax(0.0f, v); |
+ float a = 1.099296826809442f; |
+ float b = 0.018053968510807f; |
+ if (v < FromLinear(ColorSpace::TransferID::BT709, b)) { |
+ return v / 4.5f; |
+ } else { |
+ return powf((v + a - 1.0f) / a, 1.0f / 0.45f); |
+ } |
} |
GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { |
@@ -375,10 +617,8 @@ |
class ColorTransformFromLinear : public ColorTransformInternal { |
public: |
- explicit ColorTransformFromLinear(ColorSpace::TransferID transfer, |
- const SkColorSpaceTransferFn& fn, |
- bool fn_valid) |
- : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {} |
+ explicit ColorTransformFromLinear(ColorSpace::TransferID transfer) |
+ : transfer_(transfer) {} |
bool Prepend(ColorTransformInternal* prev) override { |
return prev->Join(*this); |
} |
@@ -386,34 +626,22 @@ |
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++) { |
- 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 { |
- 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())); |
- } |
+ 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())); |
} |
} |
private: |
friend class ColorTransformToLinear; |
ColorSpace::TransferID transfer_; |
- SkColorSpaceTransferFn fn_; |
- bool fn_valid_ = false; |
}; |
class ColorTransformToLinear : public ColorTransformInternal { |
public: |
- explicit ColorTransformToLinear(ColorSpace::TransferID transfer, |
- const SkColorSpaceTransferFn& fn, |
- bool fn_valid) |
- : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {} |
+ explicit ColorTransformToLinear(ColorSpace::TransferID transfer) |
+ : transfer_(transfer) {} |
bool Prepend(ColorTransformInternal* prev) override { |
return prev->Join(*this); |
@@ -449,13 +677,7 @@ |
} |
void transform(ColorTransform::TriStim* colors, size_t num) 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()), |
@@ -481,8 +703,6 @@ |
private: |
ColorSpace::TransferID transfer_; |
- SkColorSpaceTransferFn fn_; |
- bool fn_valid_ = false; |
}; |
// BT2020 Constant Luminance is different than most other |
@@ -638,9 +858,17 @@ |
class ColorSpaceToColorSpaceTransform { |
public: |
static Transform GetPrimaryTransform(const ColorSpace& c) { |
- SkMatrix44 sk_matrix; |
- c.GetPrimaryMatrix(&sk_matrix); |
- return Transform(sk_matrix); |
+ if (c.primaries_ == ColorSpace::PrimaryID::CUSTOM) { |
+ return Transform(c.custom_primary_matrix_[0], c.custom_primary_matrix_[1], |
+ c.custom_primary_matrix_[2], c.custom_primary_matrix_[3], |
+ c.custom_primary_matrix_[4], c.custom_primary_matrix_[5], |
+ c.custom_primary_matrix_[6], c.custom_primary_matrix_[7], |
+ c.custom_primary_matrix_[8], c.custom_primary_matrix_[9], |
+ c.custom_primary_matrix_[10], |
+ c.custom_primary_matrix_[11], 0.0f, 0.0f, 0.0f, 1.0f); |
+ } else { |
+ return GetPrimaryMatrix(c.primaries_); |
+ } |
} |
static void ColorSpaceToColorSpace(ColorSpace from, |
@@ -683,18 +911,11 @@ |
// TODO(hubbe): shrink gamuts here (never stretch gamuts) |
} |
- |
builder->Append(base::MakeUnique<ColorTransformMatrix>( |
GetRangeAdjustMatrix(from.range_, from.matrix_))); |
- |
builder->Append(base::MakeUnique<ColorTransformMatrix>( |
Invert(GetTransferMatrix(from.matrix_)))); |
- |
- 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)); |
- |
+ builder->Append(base::MakeUnique<ColorTransformToLinear>(from.transfer_)); |
if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
// BT2020 CL is a special case. |
builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>()); |
@@ -709,14 +930,9 @@ |
builder->Append(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)); |
- |
+ builder->Append(base::MakeUnique<ColorTransformFromLinear>(to.transfer_)); |
builder->Append( |
base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to.matrix_))); |
- |
builder->Append(base::MakeUnique<ColorTransformMatrix>( |
Invert(GetRangeAdjustMatrix(to.range_, to.matrix_)))); |
} |
@@ -812,33 +1028,4 @@ |
return builder.GetTransform(); |
} |
-// 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(); |
-} |
- |
-// 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); |
- |
- 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(); |
-} |
- |
} // namespace gfx |