Index: ui/gfx/color_transform.cc |
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc |
index af0e058c9f644574859eba1c03bc45c13e4b2be1..d2570c28671f124dee7b5789e38d47b75763eb28 100644 |
--- a/ui/gfx/color_transform.cc |
+++ b/ui/gfx/color_transform.cc |
@@ -13,18 +13,13 @@ |
#include "base/logging.h" |
#include "base/memory/ptr_util.h" |
#include "base/strings/stringprintf.h" |
-#include "third_party/qcms/src/qcms.h" |
+#include "third_party/skia/include/core/SkColor.h" |
+#include "third_party/skia/include/core/SkColorSpaceXform.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" |
-#ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
-extern "C" { |
-#include "third_party/qcms/src/chain.h" |
-}; |
-#endif |
- |
using std::abs; |
using std::copysign; |
using std::exp; |
@@ -52,16 +47,6 @@ std::string Str(float f) { |
return ss.str(); |
} |
-// 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; |
if (!t.GetInverse(&ret)) { |
@@ -242,7 +227,7 @@ class ColorTransformFromLinear; |
class ColorTransformToBT2020CL; |
class ColorTransformFromBT2020CL; |
class ColorTransformNull; |
-class QCMSColorTransform; |
+class SkiaColorTransform; |
class ColorTransformStep { |
public: |
@@ -254,7 +239,7 @@ class ColorTransformStep { |
virtual ColorTransformSkTransferFn* GetSkTransferFn() { return nullptr; } |
virtual ColorTransformMatrix* GetMatrix() { return nullptr; } |
virtual ColorTransformNull* GetNull() { return nullptr; } |
- virtual QCMSColorTransform* GetQCMS() { return nullptr; } |
+ virtual SkiaColorTransform* GetSkia() { return nullptr; } |
// Join methods, returns true if the |next| transform was successfully |
// assimilated into |this|. |
@@ -281,8 +266,8 @@ class ColorTransformStep { |
class ColorTransformInternal : public ColorTransform { |
public: |
- ColorTransformInternal(const ColorSpace& from, |
- const ColorSpace& to, |
+ ColorTransformInternal(const ColorSpace& src, |
+ const ColorSpace& dst, |
Intent intent); |
~ColorTransformInternal() override; |
@@ -299,15 +284,14 @@ class ColorTransformInternal : public ColorTransform { |
size_t NumberOfStepsForTesting() const override { return steps_.size(); } |
private: |
- void AppendColorSpaceToColorSpaceTransform(ColorSpace from, |
- const ColorSpace& to, |
+ void AppendColorSpaceToColorSpaceTransform(ColorSpace src, |
+ const ColorSpace& dst, |
ColorTransform::Intent intent); |
void Simplify(); |
- // Retrieve the ICC profile from which |color_space| was created, only if that |
- // is a more precise representation of the color space than the primaries and |
- // transfer function in |color_space|. |
- ScopedQcmsProfile GetQCMSProfileIfNecessary(const ColorSpace& color_space); |
+ // Retrieve the SkColorSpace for the ICC profile from which |color_space| was |
+ // created, only if that is a more precise than the parametric representation. |
+ sk_sp<SkColorSpace> GetSkColorSpaceIfNecessary(const ColorSpace& color_space); |
std::list<std::unique_ptr<ColorTransformStep>> steps_; |
gfx::ColorSpace src_; |
@@ -817,11 +801,11 @@ class ColorTransformFromBT2020CL : public ColorTransformStep { |
}; |
void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( |
- ColorSpace from, |
- const ColorSpace& to, |
+ ColorSpace src, |
+ const ColorSpace& dst, |
ColorTransform::Intent intent) { |
if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { |
- switch (from.transfer_) { |
+ switch (src.transfer_) { |
case ColorSpace::TransferID::BT709: |
case ColorSpace::TransferID::SMPTE170M: |
// SMPTE 1886 suggests that we should be using gamma 2.4 for BT709 |
@@ -829,23 +813,23 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( |
// 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; |
+ src.transfer_ = ColorSpace::TransferID::IEC61966_2_1; |
break; |
case ColorSpace::TransferID::SMPTEST2084: |
- if (!to.IsHDR()) { |
+ if (!dst.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; |
+ src.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR; |
} |
break; |
case ColorSpace::TransferID::ARIB_STD_B67: |
- if (!to.IsHDR()) { |
+ if (!dst.IsHDR()) { |
// Interpreting HLG using a gamma 2.4 works reasonably well for SDR |
// displays. |
- from.transfer_ = ColorSpace::TransferID::GAMMA24; |
+ src.transfer_ = ColorSpace::TransferID::GAMMA24; |
} |
break; |
@@ -857,140 +841,132 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( |
} |
steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(from))); |
+ base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(src))); |
steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(from)))); |
+ base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(src)))); |
// If the target color space is not defined, just apply the adjust and |
// tranfer matrices. This path is used by YUV to RGB color conversion |
// when full color conversion is not enabled. |
- if (!to.IsValid()) |
+ if (!dst.IsValid()) |
return; |
- SkColorSpaceTransferFn to_linear_fn; |
- if (from.GetTransferFunction(&to_linear_fn)) { |
+ SkColorSpaceTransferFn src_to_linear_fn; |
+ if (src.GetTransferFunction(&src_to_linear_fn)) { |
steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( |
- to_linear_fn, from.HasExtendedSkTransferFn())); |
- } else if (from.transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { |
+ src_to_linear_fn, src.HasExtendedSkTransferFn())); |
+ } else if (src.transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { |
steps_.push_back( |
base::MakeUnique<ColorTransformSMPTEST2048NonHdrToLinear>()); |
} else { |
- steps_.push_back(base::MakeUnique<ColorTransformToLinear>(from.transfer_)); |
+ steps_.push_back(base::MakeUnique<ColorTransformToLinear>(src.transfer_)); |
} |
- if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
+ if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
// BT2020 CL is a special case. |
steps_.push_back(base::MakeUnique<ColorTransformFromBT2020CL>()); |
} |
steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); |
+ base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(src))); |
steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(to)))); |
- if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
+ base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst)))); |
+ if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
// BT2020 CL is a special case. |
steps_.push_back(base::MakeUnique<ColorTransformToBT2020CL>()); |
} |
- SkColorSpaceTransferFn from_linear_fn; |
- if (to.GetInverseTransferFunction(&from_linear_fn)) { |
+ SkColorSpaceTransferFn dst_from_linear_fn; |
+ if (dst.GetInverseTransferFunction(&dst_from_linear_fn)) { |
steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( |
- from_linear_fn, to.HasExtendedSkTransferFn())); |
+ dst_from_linear_fn, dst.HasExtendedSkTransferFn())); |
} else { |
- steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(to.transfer_)); |
+ steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(dst.transfer_)); |
} |
steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to))); |
+ base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(dst))); |
- steps_.push_back( |
- base::MakeUnique<ColorTransformMatrix>(Invert(GetRangeAdjustMatrix(to)))); |
+ steps_.push_back(base::MakeUnique<ColorTransformMatrix>( |
+ Invert(GetRangeAdjustMatrix(dst)))); |
} |
-// TODO(ccameron): Change this to SkColorSpaceXform. |
-class QCMSColorTransform : public ColorTransformStep { |
+class SkiaColorTransform : public ColorTransformStep { |
public: |
// Takes ownership of the profiles |
- QCMSColorTransform(ScopedQcmsProfile from, ScopedQcmsProfile to) |
- : from_(std::move(from)), to_(std::move(to)) {} |
- ~QCMSColorTransform() override {} |
- QCMSColorTransform* GetQCMS() override { return this; } |
+ SkiaColorTransform(sk_sp<SkColorSpace> src, sk_sp<SkColorSpace> dst) |
+ : src_(src), dst_(dst) {} |
+ ~SkiaColorTransform() override { |
+ src_ = nullptr; |
+ dst_ = nullptr; |
+ } |
+ SkiaColorTransform* GetSkia() override { return this; } |
bool Join(ColorTransformStep* next_untyped) override { |
- QCMSColorTransform* next = next_untyped->GetQCMS(); |
+ SkiaColorTransform* next = next_untyped->GetSkia(); |
if (!next) |
return false; |
- if (qcms_profile_match(to_.get(), next->from_.get())) { |
- to_ = std::move(next->to_); |
+ if (SkColorSpace::Equals(dst_.get(), next->src_.get())) { |
+ dst_ = next->dst_; |
return true; |
} |
return false; |
} |
bool IsNull() override { |
- if (qcms_profile_match(from_.get(), to_.get())) |
+ if (SkColorSpace::Equals(src_.get(), dst_.get())) |
return true; |
return false; |
} |
void Transform(ColorTransform::TriStim* colors, size_t num) const 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(min(1.0f, max(0.0f, colors[i].x()))); |
- colors[i].set_y(min(1.0f, max(0.0f, colors[i].y()))); |
- colors[i].set_z(min(1.0f, max(0.0f, colors[i].z()))); |
+ // Transform to SkColors. |
+ std::vector<uint8_t> sk_colors(4 * num); |
+ for (size_t i = 0; i < num; ++i) { |
+ float rgb[3] = {colors[i].x(), colors[i].y(), colors[i].z()}; |
+ for (size_t c = 0; c < 3; ++c) { |
+ int value_int = static_cast<int>(255.f * rgb[c] + 0.5f); |
+ value_int = min(value_int, 255); |
+ value_int = max(value_int, 0); |
+ sk_colors[4 * i + c] = value_int; |
+ } |
+ sk_colors[4 * i + 3] = 255; |
+ } |
+ |
+ // Perform the transform. |
+ std::unique_ptr<SkColorSpaceXform> xform = |
+ SkColorSpaceXform::New(src_.get(), dst_.get()); |
+ DCHECK(xform); |
+ if (!xform) |
+ return; |
+ std::vector<uint8_t> sk_colors_transformed(4 * num); |
+ bool xform_apply_result = xform->apply( |
+ SkColorSpaceXform::kRGBA_8888_ColorFormat, sk_colors_transformed.data(), |
+ SkColorSpaceXform::kRGBA_8888_ColorFormat, sk_colors.data(), num, |
+ kOpaque_SkAlphaType); |
+ DCHECK(xform_apply_result); |
+ sk_colors = sk_colors_transformed; |
+ |
+ // Convert back to TriStim. |
+ for (size_t i = 0; i < num; ++i) { |
+ colors[i].set_x(sk_colors[4 * i + 0] / 255.f); |
+ colors[i].set_y(sk_colors[4 * i + 1] / 255.f); |
+ colors[i].set_z(sk_colors[4 * i + 2] / 255.f); |
} |
- qcms_chain_transform(from_.get(), to_.get(), |
- reinterpret_cast<float*>(colors), |
- reinterpret_cast<float*>(colors), num * 3); |
} |
private: |
- ScopedQcmsProfile from_; |
- ScopedQcmsProfile to_; |
+ sk_sp<SkColorSpace> src_; |
+ sk_sp<SkColorSpace> dst_; |
}; |
-ScopedQcmsProfile ColorTransformInternal::GetQCMSProfileIfNecessary( |
+sk_sp<SkColorSpace> ColorTransformInternal::GetSkColorSpaceIfNecessary( |
const ColorSpace& color_space) { |
if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED && |
color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) { |
return nullptr; |
} |
- // TODO(ccameron): Use SkColorSpaceXform here to avoid looking up the |
- // ICCProfile. |
- ICCProfile icc_profile; |
- if (!ICCProfile::FromId(color_space.icc_profile_id_, &icc_profile)) { |
- // We needed the original ICC profile to construct this transform, but it |
- // has been flushed from our cache. Fall back to using the ICC profile's |
- // inaccurate, so spam the console. |
- // TODO(ccameron): This will go away when we switch to SkColorSpaceXform. |
- LOG(ERROR) << "Failed to retrieve original ICC profile, using sRGB"; |
- return ScopedQcmsProfile(qcms_profile_sRGB()); |
- } |
- return ScopedQcmsProfile(qcms_profile_from_memory( |
- icc_profile.GetData().data(), icc_profile.GetData().size())); |
-} |
- |
-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. |
- qcms_CIE_xyYTRIPLE xyz; |
- qcms_CIE_xyY w; |
- xyz.red.x = 1.0f; |
- xyz.red.y = 0.0f; |
- xyz.red.Y = 1.0f; |
- xyz.green.x = 0.0f; |
- xyz.green.y = 1.0f; |
- xyz.green.Y = 1.0f; |
- xyz.blue.x = 0.0f; |
- xyz.blue.y = 0.0f; |
- xyz.blue.Y = 1.0f; |
- w.x = 0.34567f; |
- w.y = 0.35850f; |
- w.Y = 1.0f; |
- return ScopedQcmsProfile(qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f)); |
+ DCHECK(color_space.icc_profile_sk_color_space_); |
+ return color_space.icc_profile_sk_color_space_; |
} |
- |
ColorTransformInternal::ColorTransformInternal(const ColorSpace& src, |
const ColorSpace& dst, |
Intent intent) |
@@ -1003,27 +979,30 @@ ColorTransformInternal::ColorTransformInternal(const ColorSpace& src, |
// If the target color space is not defined, just apply the adjust and |
// tranfer matrices. This path is used by YUV to RGB color conversion |
// when full color conversion is not enabled. |
- ScopedQcmsProfile src_profile; |
- ScopedQcmsProfile dst_profile; |
+ sk_sp<SkColorSpace> src_sk_color_space; |
+ sk_sp<SkColorSpace> dst_sk_color_space; |
+ |
+ bool has_src_profile = false; |
+ bool has_dst_profile = false; |
if (dst.IsValid()) { |
- src_profile = GetQCMSProfileIfNecessary(src_); |
- dst_profile = GetQCMSProfileIfNecessary(dst_); |
+ src_sk_color_space = GetSkColorSpaceIfNecessary(src_); |
+ dst_sk_color_space = GetSkColorSpaceIfNecessary(dst_); |
} |
- bool has_src_profile = !!src_profile; |
- bool has_dst_profile = !!dst_profile; |
+ has_src_profile = !!src_sk_color_space; |
+ has_dst_profile = !!dst_sk_color_space; |
- if (src_profile) { |
- steps_.push_back(base::MakeUnique<QCMSColorTransform>( |
- std::move(src_profile), GetXYZD50Profile())); |
+ if (has_src_profile) { |
+ steps_.push_back(base::MakeUnique<SkiaColorTransform>( |
+ std::move(src_sk_color_space), |
+ ColorSpace::CreateXYZD50().ToSkColorSpace())); |
} |
- |
AppendColorSpaceToColorSpaceTransform( |
has_src_profile ? ColorSpace::CreateXYZD50() : src_, |
has_dst_profile ? ColorSpace::CreateXYZD50() : dst_, intent); |
- |
- if (dst_profile) { |
- steps_.push_back(base::MakeUnique<QCMSColorTransform>( |
- GetXYZD50Profile(), std::move(dst_profile))); |
+ if (has_dst_profile) { |
+ steps_.push_back(base::MakeUnique<SkiaColorTransform>( |
+ ColorSpace::CreateXYZD50().ToSkColorSpace(), |
+ std::move(dst_sk_color_space))); |
} |
if (intent != Intent::TEST_NO_OPT) |
@@ -1087,11 +1066,11 @@ void ColorTransformInternal::Simplify() { |
// static |
std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( |
- const ColorSpace& from, |
- const ColorSpace& to, |
+ const ColorSpace& src, |
+ const ColorSpace& dst, |
Intent intent) { |
return std::unique_ptr<ColorTransform>( |
- new ColorTransformInternal(from, to, intent)); |
+ new ColorTransformInternal(src, dst, intent)); |
} |
ColorTransform::ColorTransform() {} |