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 |