| OLD | NEW |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/color_transform.h" | 5 #include "ui/gfx/color_transform.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 #include <list> | 9 #include <list> |
| 10 #include <memory> | 10 #include <memory> |
| 11 #include <sstream> | 11 #include <sstream> |
| 12 | 12 |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "third_party/qcms/src/qcms.h" | 16 #include "third_party/skia/include/core/SkColor.h" |
| 17 #include "third_party/skia/include/core/SkColorSpaceXform.h" |
| 17 #include "ui/gfx/color_space.h" | 18 #include "ui/gfx/color_space.h" |
| 18 #include "ui/gfx/icc_profile.h" | 19 #include "ui/gfx/icc_profile.h" |
| 19 #include "ui/gfx/skia_color_space_util.h" | 20 #include "ui/gfx/skia_color_space_util.h" |
| 20 #include "ui/gfx/transform.h" | 21 #include "ui/gfx/transform.h" |
| 21 | 22 |
| 22 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H | |
| 23 extern "C" { | |
| 24 #include "third_party/qcms/src/chain.h" | |
| 25 }; | |
| 26 #endif | |
| 27 | |
| 28 using std::abs; | 23 using std::abs; |
| 29 using std::copysign; | 24 using std::copysign; |
| 30 using std::exp; | 25 using std::exp; |
| 31 using std::log; | 26 using std::log; |
| 32 using std::max; | 27 using std::max; |
| 33 using std::min; | 28 using std::min; |
| 34 using std::pow; | 29 using std::pow; |
| 35 using std::sqrt; | 30 using std::sqrt; |
| 36 using std::endl; | 31 using std::endl; |
| 37 | 32 |
| 38 namespace gfx { | 33 namespace gfx { |
| 39 | 34 |
| 40 namespace { | 35 namespace { |
| 41 | 36 |
| 42 void InitStringStream(std::stringstream* ss) { | 37 void InitStringStream(std::stringstream* ss) { |
| 43 ss->imbue(std::locale::classic()); | 38 ss->imbue(std::locale::classic()); |
| 44 ss->precision(8); | 39 ss->precision(8); |
| 45 *ss << std::scientific; | 40 *ss << std::scientific; |
| 46 } | 41 } |
| 47 | 42 |
| 48 std::string Str(float f) { | 43 std::string Str(float f) { |
| 49 std::stringstream ss; | 44 std::stringstream ss; |
| 50 InitStringStream(&ss); | 45 InitStringStream(&ss); |
| 51 ss << f; | 46 ss << f; |
| 52 return ss.str(); | 47 return ss.str(); |
| 53 } | 48 } |
| 54 | 49 |
| 55 // Helper for scoped QCMS profiles. | |
| 56 struct QcmsProfileDeleter { | |
| 57 void operator()(qcms_profile* p) { | |
| 58 if (p) { | |
| 59 qcms_profile_release(p); | |
| 60 } | |
| 61 } | |
| 62 }; | |
| 63 using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>; | |
| 64 | |
| 65 Transform Invert(const Transform& t) { | 50 Transform Invert(const Transform& t) { |
| 66 Transform ret = t; | 51 Transform ret = t; |
| 67 if (!t.GetInverse(&ret)) { | 52 if (!t.GetInverse(&ret)) { |
| 68 LOG(ERROR) << "Inverse should always be possible."; | 53 LOG(ERROR) << "Inverse should always be possible."; |
| 69 } | 54 } |
| 70 return ret; | 55 return ret; |
| 71 } | 56 } |
| 72 | 57 |
| 73 float FromLinear(ColorSpace::TransferID id, float v) { | 58 float FromLinear(ColorSpace::TransferID id, float v) { |
| 74 switch (id) { | 59 switch (id) { |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 } | 220 } |
| 236 | 221 |
| 237 } // namespace | 222 } // namespace |
| 238 | 223 |
| 239 class ColorTransformMatrix; | 224 class ColorTransformMatrix; |
| 240 class ColorTransformSkTransferFn; | 225 class ColorTransformSkTransferFn; |
| 241 class ColorTransformFromLinear; | 226 class ColorTransformFromLinear; |
| 242 class ColorTransformToBT2020CL; | 227 class ColorTransformToBT2020CL; |
| 243 class ColorTransformFromBT2020CL; | 228 class ColorTransformFromBT2020CL; |
| 244 class ColorTransformNull; | 229 class ColorTransformNull; |
| 245 class QCMSColorTransform; | 230 class SkiaColorTransform; |
| 246 | 231 |
| 247 class ColorTransformStep { | 232 class ColorTransformStep { |
| 248 public: | 233 public: |
| 249 ColorTransformStep() {} | 234 ColorTransformStep() {} |
| 250 virtual ~ColorTransformStep() {} | 235 virtual ~ColorTransformStep() {} |
| 251 virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; } | 236 virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; } |
| 252 virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; } | 237 virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; } |
| 253 virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; } | 238 virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; } |
| 254 virtual ColorTransformSkTransferFn* GetSkTransferFn() { return nullptr; } | 239 virtual ColorTransformSkTransferFn* GetSkTransferFn() { return nullptr; } |
| 255 virtual ColorTransformMatrix* GetMatrix() { return nullptr; } | 240 virtual ColorTransformMatrix* GetMatrix() { return nullptr; } |
| 256 virtual ColorTransformNull* GetNull() { return nullptr; } | 241 virtual ColorTransformNull* GetNull() { return nullptr; } |
| 257 virtual QCMSColorTransform* GetQCMS() { return nullptr; } | 242 virtual SkiaColorTransform* GetSkia() { return nullptr; } |
| 258 | 243 |
| 259 // Join methods, returns true if the |next| transform was successfully | 244 // Join methods, returns true if the |next| transform was successfully |
| 260 // assimilated into |this|. | 245 // assimilated into |this|. |
| 261 // If Join() returns true, |next| is no longer needed and can be deleted. | 246 // If Join() returns true, |next| is no longer needed and can be deleted. |
| 262 virtual bool Join(ColorTransformStep* next) { return false; } | 247 virtual bool Join(ColorTransformStep* next) { return false; } |
| 263 | 248 |
| 264 // Return true if this is a null transform. | 249 // Return true if this is a null transform. |
| 265 virtual bool IsNull() { return false; } | 250 virtual bool IsNull() { return false; } |
| 266 virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0; | 251 virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0; |
| 267 virtual bool CanAppendShaderSource() { return false; } | 252 virtual bool CanAppendShaderSource() { return false; } |
| 268 // In the shader, |hdr| will appear before |src|, so any helper functions that | 253 // In the shader, |hdr| will appear before |src|, so any helper functions that |
| 269 // are created should be put in |hdr|. Any helper functions should have | 254 // are created should be put in |hdr|. Any helper functions should have |
| 270 // |step_index| included in the function name, to ensure that there are no | 255 // |step_index| included in the function name, to ensure that there are no |
| 271 // naming conflicts. | 256 // naming conflicts. |
| 272 virtual void AppendShaderSource(std::stringstream* hdr, | 257 virtual void AppendShaderSource(std::stringstream* hdr, |
| 273 std::stringstream* src, | 258 std::stringstream* src, |
| 274 size_t step_index) const { | 259 size_t step_index) const { |
| 275 NOTREACHED(); | 260 NOTREACHED(); |
| 276 } | 261 } |
| 277 | 262 |
| 278 private: | 263 private: |
| 279 DISALLOW_COPY_AND_ASSIGN(ColorTransformStep); | 264 DISALLOW_COPY_AND_ASSIGN(ColorTransformStep); |
| 280 }; | 265 }; |
| 281 | 266 |
| 282 class ColorTransformInternal : public ColorTransform { | 267 class ColorTransformInternal : public ColorTransform { |
| 283 public: | 268 public: |
| 284 ColorTransformInternal(const ColorSpace& from, | 269 ColorTransformInternal(const ColorSpace& src, |
| 285 const ColorSpace& to, | 270 const ColorSpace& dst, |
| 286 Intent intent); | 271 Intent intent); |
| 287 ~ColorTransformInternal() override; | 272 ~ColorTransformInternal() override; |
| 288 | 273 |
| 289 gfx::ColorSpace GetSrcColorSpace() const override { return src_; }; | 274 gfx::ColorSpace GetSrcColorSpace() const override { return src_; }; |
| 290 gfx::ColorSpace GetDstColorSpace() const override { return dst_; }; | 275 gfx::ColorSpace GetDstColorSpace() const override { return dst_; }; |
| 291 | 276 |
| 292 void Transform(TriStim* colors, size_t num) const override { | 277 void Transform(TriStim* colors, size_t num) const override { |
| 293 for (const auto& step : steps_) | 278 for (const auto& step : steps_) |
| 294 step->Transform(colors, num); | 279 step->Transform(colors, num); |
| 295 } | 280 } |
| 296 bool CanGetShaderSource() const override; | 281 bool CanGetShaderSource() const override; |
| 297 std::string GetShaderSource() const override; | 282 std::string GetShaderSource() const override; |
| 298 bool IsIdentity() const override { return steps_.empty(); } | 283 bool IsIdentity() const override { return steps_.empty(); } |
| 299 size_t NumberOfStepsForTesting() const override { return steps_.size(); } | 284 size_t NumberOfStepsForTesting() const override { return steps_.size(); } |
| 300 | 285 |
| 301 private: | 286 private: |
| 302 void AppendColorSpaceToColorSpaceTransform(ColorSpace from, | 287 void AppendColorSpaceToColorSpaceTransform(ColorSpace src, |
| 303 const ColorSpace& to, | 288 const ColorSpace& dst, |
| 304 ColorTransform::Intent intent); | 289 ColorTransform::Intent intent); |
| 305 void Simplify(); | 290 void Simplify(); |
| 306 | 291 |
| 307 // Retrieve the ICC profile from which |color_space| was created, only if that | 292 // Retrieve the SkColorSpace for the ICC profile from which |color_space| was |
| 308 // is a more precise representation of the color space than the primaries and | 293 // created, only if that is a more precise than the parametric representation. |
| 309 // transfer function in |color_space|. | 294 sk_sp<SkColorSpace> GetSkColorSpaceIfNecessary(const ColorSpace& color_space); |
| 310 ScopedQcmsProfile GetQCMSProfileIfNecessary(const ColorSpace& color_space); | |
| 311 | 295 |
| 312 std::list<std::unique_ptr<ColorTransformStep>> steps_; | 296 std::list<std::unique_ptr<ColorTransformStep>> steps_; |
| 313 gfx::ColorSpace src_; | 297 gfx::ColorSpace src_; |
| 314 gfx::ColorSpace dst_; | 298 gfx::ColorSpace dst_; |
| 315 }; | 299 }; |
| 316 | 300 |
| 317 class ColorTransformNull : public ColorTransformStep { | 301 class ColorTransformNull : public ColorTransformStep { |
| 318 public: | 302 public: |
| 319 ColorTransformNull* GetNull() override { return this; } | 303 ColorTransformNull* GetNull() override { return this; } |
| 320 bool IsNull() override { return true; } | 304 bool IsNull() override { return true; } |
| (...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 810 | 794 |
| 811 *src << " color.rgb = BT2020_YUV_to_RYB_Step" << step_index | 795 *src << " color.rgb = BT2020_YUV_to_RYB_Step" << step_index |
| 812 << "(color.rgb);" << endl; | 796 << "(color.rgb);" << endl; |
| 813 } | 797 } |
| 814 | 798 |
| 815 private: | 799 private: |
| 816 bool null_ = false; | 800 bool null_ = false; |
| 817 }; | 801 }; |
| 818 | 802 |
| 819 void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( | 803 void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( |
| 820 ColorSpace from, | 804 ColorSpace src, |
| 821 const ColorSpace& to, | 805 const ColorSpace& dst, |
| 822 ColorTransform::Intent intent) { | 806 ColorTransform::Intent intent) { |
| 823 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { | 807 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { |
| 824 switch (from.transfer_) { | 808 switch (src.transfer_) { |
| 825 case ColorSpace::TransferID::BT709: | 809 case ColorSpace::TransferID::BT709: |
| 826 case ColorSpace::TransferID::SMPTE170M: | 810 case ColorSpace::TransferID::SMPTE170M: |
| 827 // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709 | 811 // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709 |
| 828 // content. However, most displays actually use a gamma of 2.2, and | 812 // content. However, most displays actually use a gamma of 2.2, and |
| 829 // user studies shows that users don't really care. Using the same | 813 // user studies shows that users don't really care. Using the same |
| 830 // gamma as the display will let us optimize a lot more, so lets stick | 814 // gamma as the display will let us optimize a lot more, so lets stick |
| 831 // with using the SRGB transfer function. | 815 // with using the SRGB transfer function. |
| 832 from.transfer_ = ColorSpace::TransferID::IEC61966_2_1; | 816 src.transfer_ = ColorSpace::TransferID::IEC61966_2_1; |
| 833 break; | 817 break; |
| 834 | 818 |
| 835 case ColorSpace::TransferID::SMPTEST2084: | 819 case ColorSpace::TransferID::SMPTEST2084: |
| 836 if (!to.IsHDR()) { | 820 if (!dst.IsHDR()) { |
| 837 // We don't have an HDR display, so replace SMPTE 2084 with | 821 // We don't have an HDR display, so replace SMPTE 2084 with |
| 838 // something that returns ranges more or less suitable for a normal | 822 // something that returns ranges more or less suitable for a normal |
| 839 // display. | 823 // display. |
| 840 from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR; | 824 src.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR; |
| 841 } | 825 } |
| 842 break; | 826 break; |
| 843 | 827 |
| 844 case ColorSpace::TransferID::ARIB_STD_B67: | 828 case ColorSpace::TransferID::ARIB_STD_B67: |
| 845 if (!to.IsHDR()) { | 829 if (!dst.IsHDR()) { |
| 846 // Interpreting HLG using a gamma 2.4 works reasonably well for SDR | 830 // Interpreting HLG using a gamma 2.4 works reasonably well for SDR |
| 847 // displays. | 831 // displays. |
| 848 from.transfer_ = ColorSpace::TransferID::GAMMA24; | 832 src.transfer_ = ColorSpace::TransferID::GAMMA24; |
| 849 } | 833 } |
| 850 break; | 834 break; |
| 851 | 835 |
| 852 default: // Do nothing | 836 default: // Do nothing |
| 853 break; | 837 break; |
| 854 } | 838 } |
| 855 | 839 |
| 856 // TODO(hubbe): shrink gamuts here (never stretch gamuts) | 840 // TODO(hubbe): shrink gamuts here (never stretch gamuts) |
| 857 } | 841 } |
| 858 | 842 |
| 859 steps_.push_back( | 843 steps_.push_back( |
| 860 base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(from))); | 844 base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(src))); |
| 861 | 845 |
| 862 steps_.push_back( | 846 steps_.push_back( |
| 863 base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(from)))); | 847 base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(src)))); |
| 864 | 848 |
| 865 // If the target color space is not defined, just apply the adjust and | 849 // If the target color space is not defined, just apply the adjust and |
| 866 // tranfer matrices. This path is used by YUV to RGB color conversion | 850 // tranfer matrices. This path is used by YUV to RGB color conversion |
| 867 // when full color conversion is not enabled. | 851 // when full color conversion is not enabled. |
| 868 if (!to.IsValid()) | 852 if (!dst.IsValid()) |
| 869 return; | 853 return; |
| 870 | 854 |
| 871 SkColorSpaceTransferFn to_linear_fn; | 855 SkColorSpaceTransferFn src_to_linear_fn; |
| 872 if (from.GetTransferFunction(&to_linear_fn)) { | 856 if (src.GetTransferFunction(&src_to_linear_fn)) { |
| 873 steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( | 857 steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( |
| 874 to_linear_fn, from.HasExtendedSkTransferFn())); | 858 src_to_linear_fn, src.HasExtendedSkTransferFn())); |
| 875 } else if (from.transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { | 859 } else if (src.transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { |
| 876 steps_.push_back( | 860 steps_.push_back( |
| 877 base::MakeUnique<ColorTransformSMPTEST2048NonHdrToLinear>()); | 861 base::MakeUnique<ColorTransformSMPTEST2048NonHdrToLinear>()); |
| 878 } else { | 862 } else { |
| 879 steps_.push_back(base::MakeUnique<ColorTransformToLinear>(from.transfer_)); | 863 steps_.push_back(base::MakeUnique<ColorTransformToLinear>(src.transfer_)); |
| 880 } | 864 } |
| 881 | 865 |
| 882 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 866 if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
| 883 // BT2020 CL is a special case. | 867 // BT2020 CL is a special case. |
| 884 steps_.push_back(base::MakeUnique<ColorTransformFromBT2020CL>()); | 868 steps_.push_back(base::MakeUnique<ColorTransformFromBT2020CL>()); |
| 885 } | 869 } |
| 886 steps_.push_back( | 870 steps_.push_back( |
| 887 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); | 871 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(src))); |
| 888 | 872 |
| 889 steps_.push_back( | 873 steps_.push_back( |
| 890 base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(to)))); | 874 base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst)))); |
| 891 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 875 if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
| 892 // BT2020 CL is a special case. | 876 // BT2020 CL is a special case. |
| 893 steps_.push_back(base::MakeUnique<ColorTransformToBT2020CL>()); | 877 steps_.push_back(base::MakeUnique<ColorTransformToBT2020CL>()); |
| 894 } | 878 } |
| 895 | 879 |
| 896 SkColorSpaceTransferFn from_linear_fn; | 880 SkColorSpaceTransferFn dst_from_linear_fn; |
| 897 if (to.GetInverseTransferFunction(&from_linear_fn)) { | 881 if (dst.GetInverseTransferFunction(&dst_from_linear_fn)) { |
| 898 steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( | 882 steps_.push_back(base::MakeUnique<ColorTransformSkTransferFn>( |
| 899 from_linear_fn, to.HasExtendedSkTransferFn())); | 883 dst_from_linear_fn, dst.HasExtendedSkTransferFn())); |
| 900 } else { | 884 } else { |
| 901 steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(to.transfer_)); | 885 steps_.push_back(base::MakeUnique<ColorTransformFromLinear>(dst.transfer_)); |
| 902 } | 886 } |
| 903 | 887 |
| 904 steps_.push_back( | 888 steps_.push_back( |
| 905 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to))); | 889 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(dst))); |
| 906 | 890 |
| 907 steps_.push_back( | 891 steps_.push_back(base::MakeUnique<ColorTransformMatrix>( |
| 908 base::MakeUnique<ColorTransformMatrix>(Invert(GetRangeAdjustMatrix(to)))); | 892 Invert(GetRangeAdjustMatrix(dst)))); |
| 909 } | 893 } |
| 910 | 894 |
| 911 // TODO(ccameron): Change this to SkColorSpaceXform. | 895 class SkiaColorTransform : public ColorTransformStep { |
| 912 class QCMSColorTransform : public ColorTransformStep { | |
| 913 public: | 896 public: |
| 914 // Takes ownership of the profiles | 897 // Takes ownership of the profiles |
| 915 QCMSColorTransform(ScopedQcmsProfile from, ScopedQcmsProfile to) | 898 SkiaColorTransform(sk_sp<SkColorSpace> src, sk_sp<SkColorSpace> dst) |
| 916 : from_(std::move(from)), to_(std::move(to)) {} | 899 : src_(src), dst_(dst) {} |
| 917 ~QCMSColorTransform() override {} | 900 ~SkiaColorTransform() override { |
| 918 QCMSColorTransform* GetQCMS() override { return this; } | 901 src_ = nullptr; |
| 902 dst_ = nullptr; |
| 903 } |
| 904 SkiaColorTransform* GetSkia() override { return this; } |
| 919 bool Join(ColorTransformStep* next_untyped) override { | 905 bool Join(ColorTransformStep* next_untyped) override { |
| 920 QCMSColorTransform* next = next_untyped->GetQCMS(); | 906 SkiaColorTransform* next = next_untyped->GetSkia(); |
| 921 if (!next) | 907 if (!next) |
| 922 return false; | 908 return false; |
| 923 if (qcms_profile_match(to_.get(), next->from_.get())) { | 909 if (SkColorSpace::Equals(dst_.get(), next->src_.get())) { |
| 924 to_ = std::move(next->to_); | 910 dst_ = next->dst_; |
| 925 return true; | 911 return true; |
| 926 } | 912 } |
| 927 return false; | 913 return false; |
| 928 } | 914 } |
| 929 bool IsNull() override { | 915 bool IsNull() override { |
| 930 if (qcms_profile_match(from_.get(), to_.get())) | 916 if (SkColorSpace::Equals(src_.get(), dst_.get())) |
| 931 return true; | 917 return true; |
| 932 return false; | 918 return false; |
| 933 } | 919 } |
| 934 void Transform(ColorTransform::TriStim* colors, size_t num) const override { | 920 void Transform(ColorTransform::TriStim* colors, size_t num) const override { |
| 935 CHECK(sizeof(ColorTransform::TriStim) == sizeof(float[3])); | 921 // Transform to SkColors. |
| 936 // QCMS doesn't like numbers outside 0..1 | 922 std::vector<uint8_t> sk_colors(4 * num); |
| 937 for (size_t i = 0; i < num; i++) { | 923 for (size_t i = 0; i < num; ++i) { |
| 938 colors[i].set_x(min(1.0f, max(0.0f, colors[i].x()))); | 924 float rgb[3] = {colors[i].x(), colors[i].y(), colors[i].z()}; |
| 939 colors[i].set_y(min(1.0f, max(0.0f, colors[i].y()))); | 925 for (size_t c = 0; c < 3; ++c) { |
| 940 colors[i].set_z(min(1.0f, max(0.0f, colors[i].z()))); | 926 int value_int = static_cast<int>(255.f * rgb[c] + 0.5f); |
| 927 value_int = min(value_int, 255); |
| 928 value_int = max(value_int, 0); |
| 929 sk_colors[4 * i + c] = value_int; |
| 930 } |
| 931 sk_colors[4 * i + 3] = 255; |
| 941 } | 932 } |
| 942 qcms_chain_transform(from_.get(), to_.get(), | 933 |
| 943 reinterpret_cast<float*>(colors), | 934 // Perform the transform. |
| 944 reinterpret_cast<float*>(colors), num * 3); | 935 std::unique_ptr<SkColorSpaceXform> xform = |
| 936 SkColorSpaceXform::New(src_.get(), dst_.get()); |
| 937 DCHECK(xform); |
| 938 if (!xform) |
| 939 return; |
| 940 std::vector<uint8_t> sk_colors_transformed(4 * num); |
| 941 bool xform_apply_result = xform->apply( |
| 942 SkColorSpaceXform::kRGBA_8888_ColorFormat, sk_colors_transformed.data(), |
| 943 SkColorSpaceXform::kRGBA_8888_ColorFormat, sk_colors.data(), num, |
| 944 kOpaque_SkAlphaType); |
| 945 DCHECK(xform_apply_result); |
| 946 sk_colors = sk_colors_transformed; |
| 947 |
| 948 // Convert back to TriStim. |
| 949 for (size_t i = 0; i < num; ++i) { |
| 950 colors[i].set_x(sk_colors[4 * i + 0] / 255.f); |
| 951 colors[i].set_y(sk_colors[4 * i + 1] / 255.f); |
| 952 colors[i].set_z(sk_colors[4 * i + 2] / 255.f); |
| 953 } |
| 945 } | 954 } |
| 946 | 955 |
| 947 private: | 956 private: |
| 948 ScopedQcmsProfile from_; | 957 sk_sp<SkColorSpace> src_; |
| 949 ScopedQcmsProfile to_; | 958 sk_sp<SkColorSpace> dst_; |
| 950 }; | 959 }; |
| 951 | 960 |
| 952 ScopedQcmsProfile ColorTransformInternal::GetQCMSProfileIfNecessary( | 961 sk_sp<SkColorSpace> ColorTransformInternal::GetSkColorSpaceIfNecessary( |
| 953 const ColorSpace& color_space) { | 962 const ColorSpace& color_space) { |
| 954 if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED && | 963 if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED && |
| 955 color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) { | 964 color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) { |
| 956 return nullptr; | 965 return nullptr; |
| 957 } | 966 } |
| 958 // TODO(ccameron): Use SkColorSpaceXform here to avoid looking up the | 967 DCHECK(color_space.icc_profile_sk_color_space_); |
| 959 // ICCProfile. | 968 return color_space.icc_profile_sk_color_space_; |
| 960 ICCProfile icc_profile; | |
| 961 if (!ICCProfile::FromId(color_space.icc_profile_id_, &icc_profile)) { | |
| 962 // We needed the original ICC profile to construct this transform, but it | |
| 963 // has been flushed from our cache. Fall back to using the ICC profile's | |
| 964 // inaccurate, so spam the console. | |
| 965 // TODO(ccameron): This will go away when we switch to SkColorSpaceXform. | |
| 966 LOG(ERROR) << "Failed to retrieve original ICC profile, using sRGB"; | |
| 967 return ScopedQcmsProfile(qcms_profile_sRGB()); | |
| 968 } | |
| 969 return ScopedQcmsProfile(qcms_profile_from_memory( | |
| 970 icc_profile.GetData().data(), icc_profile.GetData().size())); | |
| 971 } | 969 } |
| 972 | |
| 973 ScopedQcmsProfile GetXYZD50Profile() { | |
| 974 // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects | |
| 975 // is in fact not xyY color coordinates, it just wants the x/y values of the | |
| 976 // primaries with Y equal to 1.0. | |
| 977 qcms_CIE_xyYTRIPLE xyz; | |
| 978 qcms_CIE_xyY w; | |
| 979 xyz.red.x = 1.0f; | |
| 980 xyz.red.y = 0.0f; | |
| 981 xyz.red.Y = 1.0f; | |
| 982 xyz.green.x = 0.0f; | |
| 983 xyz.green.y = 1.0f; | |
| 984 xyz.green.Y = 1.0f; | |
| 985 xyz.blue.x = 0.0f; | |
| 986 xyz.blue.y = 0.0f; | |
| 987 xyz.blue.Y = 1.0f; | |
| 988 w.x = 0.34567f; | |
| 989 w.y = 0.35850f; | |
| 990 w.Y = 1.0f; | |
| 991 return ScopedQcmsProfile(qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f)); | |
| 992 } | |
| 993 | |
| 994 ColorTransformInternal::ColorTransformInternal(const ColorSpace& src, | 970 ColorTransformInternal::ColorTransformInternal(const ColorSpace& src, |
| 995 const ColorSpace& dst, | 971 const ColorSpace& dst, |
| 996 Intent intent) | 972 Intent intent) |
| 997 : src_(src), dst_(dst) { | 973 : src_(src), dst_(dst) { |
| 998 // If no source color space is specified, do no transformation. | 974 // If no source color space is specified, do no transformation. |
| 999 // TODO(ccameron): We may want dst assume sRGB at some point in the future. | 975 // TODO(ccameron): We may want dst assume sRGB at some point in the future. |
| 1000 if (!src_.IsValid()) | 976 if (!src_.IsValid()) |
| 1001 return; | 977 return; |
| 1002 | 978 |
| 1003 // If the target color space is not defined, just apply the adjust and | 979 // If the target color space is not defined, just apply the adjust and |
| 1004 // tranfer matrices. This path is used by YUV to RGB color conversion | 980 // tranfer matrices. This path is used by YUV to RGB color conversion |
| 1005 // when full color conversion is not enabled. | 981 // when full color conversion is not enabled. |
| 1006 ScopedQcmsProfile src_profile; | 982 sk_sp<SkColorSpace> src_sk_color_space; |
| 1007 ScopedQcmsProfile dst_profile; | 983 sk_sp<SkColorSpace> dst_sk_color_space; |
| 984 |
| 985 bool has_src_profile = false; |
| 986 bool has_dst_profile = false; |
| 1008 if (dst.IsValid()) { | 987 if (dst.IsValid()) { |
| 1009 src_profile = GetQCMSProfileIfNecessary(src_); | 988 src_sk_color_space = GetSkColorSpaceIfNecessary(src_); |
| 1010 dst_profile = GetQCMSProfileIfNecessary(dst_); | 989 dst_sk_color_space = GetSkColorSpaceIfNecessary(dst_); |
| 1011 } | 990 } |
| 1012 bool has_src_profile = !!src_profile; | 991 has_src_profile = !!src_sk_color_space; |
| 1013 bool has_dst_profile = !!dst_profile; | 992 has_dst_profile = !!dst_sk_color_space; |
| 1014 | 993 |
| 1015 if (src_profile) { | 994 if (has_src_profile) { |
| 1016 steps_.push_back(base::MakeUnique<QCMSColorTransform>( | 995 steps_.push_back(base::MakeUnique<SkiaColorTransform>( |
| 1017 std::move(src_profile), GetXYZD50Profile())); | 996 std::move(src_sk_color_space), |
| 997 ColorSpace::CreateXYZD50().ToSkColorSpace())); |
| 1018 } | 998 } |
| 1019 | |
| 1020 AppendColorSpaceToColorSpaceTransform( | 999 AppendColorSpaceToColorSpaceTransform( |
| 1021 has_src_profile ? ColorSpace::CreateXYZD50() : src_, | 1000 has_src_profile ? ColorSpace::CreateXYZD50() : src_, |
| 1022 has_dst_profile ? ColorSpace::CreateXYZD50() : dst_, intent); | 1001 has_dst_profile ? ColorSpace::CreateXYZD50() : dst_, intent); |
| 1023 | 1002 if (has_dst_profile) { |
| 1024 if (dst_profile) { | 1003 steps_.push_back(base::MakeUnique<SkiaColorTransform>( |
| 1025 steps_.push_back(base::MakeUnique<QCMSColorTransform>( | 1004 ColorSpace::CreateXYZD50().ToSkColorSpace(), |
| 1026 GetXYZD50Profile(), std::move(dst_profile))); | 1005 std::move(dst_sk_color_space))); |
| 1027 } | 1006 } |
| 1028 | 1007 |
| 1029 if (intent != Intent::TEST_NO_OPT) | 1008 if (intent != Intent::TEST_NO_OPT) |
| 1030 Simplify(); | 1009 Simplify(); |
| 1031 } | 1010 } |
| 1032 | 1011 |
| 1033 std::string ColorTransformInternal::GetShaderSource() const { | 1012 std::string ColorTransformInternal::GetShaderSource() const { |
| 1034 std::stringstream hdr; | 1013 std::stringstream hdr; |
| 1035 std::stringstream src; | 1014 std::stringstream src; |
| 1036 InitStringStream(&hdr); | 1015 InitStringStream(&hdr); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1080 --iter; | 1059 --iter; |
| 1081 continue; | 1060 continue; |
| 1082 } | 1061 } |
| 1083 | 1062 |
| 1084 ++iter; | 1063 ++iter; |
| 1085 } | 1064 } |
| 1086 } | 1065 } |
| 1087 | 1066 |
| 1088 // static | 1067 // static |
| 1089 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( | 1068 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( |
| 1090 const ColorSpace& from, | 1069 const ColorSpace& src, |
| 1091 const ColorSpace& to, | 1070 const ColorSpace& dst, |
| 1092 Intent intent) { | 1071 Intent intent) { |
| 1093 return std::unique_ptr<ColorTransform>( | 1072 return std::unique_ptr<ColorTransform>( |
| 1094 new ColorTransformInternal(from, to, intent)); | 1073 new ColorTransformInternal(src, dst, intent)); |
| 1095 } | 1074 } |
| 1096 | 1075 |
| 1097 ColorTransform::ColorTransform() {} | 1076 ColorTransform::ColorTransform() {} |
| 1098 ColorTransform::~ColorTransform() {} | 1077 ColorTransform::~ColorTransform() {} |
| 1099 | 1078 |
| 1100 } // namespace gfx | 1079 } // namespace gfx |
| OLD | NEW |