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 <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "ui/gfx/color_space.h" | 10 #include "ui/gfx/color_space.h" |
| 11 #include "ui/gfx/icc_profile.h" |
11 #include "ui/gfx/transform.h" | 12 #include "ui/gfx/transform.h" |
| 13 #include "third_party/qcms/src/qcms.h" |
| 14 |
| 15 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
| 16 extern "C" { |
| 17 #include "third_party/qcms/src/chain.h" |
| 18 }; |
| 19 #endif |
12 | 20 |
13 namespace gfx { | 21 namespace gfx { |
14 | 22 |
15 Transform Invert(const Transform& t) { | 23 Transform Invert(const Transform& t) { |
16 Transform ret = t; | 24 Transform ret = t; |
17 if (!t.GetInverse(&ret)) { | 25 if (!t.GetInverse(&ret)) { |
18 LOG(ERROR) << "Inverse should alsways be possible."; | 26 LOG(ERROR) << "Inverse should alsways be possible."; |
19 } | 27 } |
20 return ret; | 28 return ret; |
21 } | 29 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 | 179 |
172 ColorTransform::TriStim D50(0.9642f, 1.0f, 0.8249f); | 180 ColorTransform::TriStim D50(0.9642f, 1.0f, 0.8249f); |
173 ColorTransform::TriStim source_response = Map(bradford, WXYZ); | 181 ColorTransform::TriStim source_response = Map(bradford, WXYZ); |
174 ColorTransform::TriStim dest_response = Map(bradford, D50); | 182 ColorTransform::TriStim dest_response = Map(bradford, D50); |
175 | 183 |
176 Transform adapter; | 184 Transform adapter; |
177 adapter.Scale3d(dest_response.x() / source_response.x(), | 185 adapter.Scale3d(dest_response.x() / source_response.x(), |
178 dest_response.y() / source_response.y(), | 186 dest_response.y() / source_response.y(), |
179 dest_response.z() / source_response.z()); | 187 dest_response.z() / source_response.z()); |
180 | 188 |
181 return bradford * adapter * Invert(bradford) * ret; | 189 return Invert(bradford) * adapter * bradford * ret; |
182 } | 190 } |
183 | 191 |
184 GFX_EXPORT float FromLinear(ColorSpace::TransferID id, float v) { | 192 GFX_EXPORT float FromLinear(ColorSpace::TransferID id, float v) { |
185 switch (id) { | 193 switch (id) { |
186 default: | 194 default: |
187 case ColorSpace::TransferID::BT709: | 195 case ColorSpace::TransferID::BT709: |
188 case ColorSpace::TransferID::SMPTE170M: | 196 case ColorSpace::TransferID::SMPTE170M: |
189 case ColorSpace::TransferID::BT2020_10: | 197 case ColorSpace::TransferID::BT2020_10: |
190 case ColorSpace::TransferID::BT2020_12: { | 198 case ColorSpace::TransferID::BT2020_12: { |
191 v = fmax(0.0f, v); | 199 v = fmax(0.0f, v); |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 private: | 548 private: |
541 ColorSpace from_; | 549 ColorSpace from_; |
542 ColorSpace to_; | 550 ColorSpace to_; |
543 | 551 |
544 // a_ -> tolinear -> b_ -> fromlinear -> c_; | 552 // a_ -> tolinear -> b_ -> fromlinear -> c_; |
545 Transform a_; | 553 Transform a_; |
546 Transform b_; | 554 Transform b_; |
547 Transform c_; | 555 Transform c_; |
548 }; | 556 }; |
549 | 557 |
| 558 class QCMSColorTransform : public ColorTransform { |
| 559 public: |
| 560 // Takes ownership of the profiles |
| 561 QCMSColorTransform(qcms_profile* from, qcms_profile* to) |
| 562 : from_(from), to_(to) {} |
| 563 ~QCMSColorTransform() { |
| 564 qcms_profile_release(from_); |
| 565 qcms_profile_release(to_); |
| 566 } |
| 567 void transform(TriStim* colors, size_t num) override { |
| 568 CHECK(sizeof(TriStim) == sizeof(float[3])); |
| 569 // QCMS doesn't like numbers outside 0..1 |
| 570 for (size_t i = 0; i < num; i++) { |
| 571 colors[i].set_x(fmin(1.0f, fmax(0.0f, colors[i].x()))); |
| 572 colors[i].set_y(fmin(1.0f, fmax(0.0f, colors[i].y()))); |
| 573 colors[i].set_z(fmin(1.0f, fmax(0.0f, colors[i].z()))); |
| 574 } |
| 575 qcms_chain_transform(from_, to_, reinterpret_cast<float*>(colors), |
| 576 reinterpret_cast<float*>(colors), num * 3); |
| 577 } |
| 578 |
| 579 private: |
| 580 qcms_profile *from_, *to_; |
| 581 }; |
| 582 |
| 583 class ChainColorTransform : public ColorTransform { |
| 584 public: |
| 585 ChainColorTransform(std::unique_ptr<ColorTransform> a, |
| 586 std::unique_ptr<ColorTransform> b) |
| 587 : a_(std::move(a)), b_(std::move(b)) {} |
| 588 |
| 589 private: |
| 590 void transform(TriStim* colors, size_t num) override { |
| 591 a_->transform(colors, num); |
| 592 b_->transform(colors, num); |
| 593 } |
| 594 std::unique_ptr<ColorTransform> a_; |
| 595 std::unique_ptr<ColorTransform> b_; |
| 596 }; |
| 597 |
| 598 qcms_profile* GetQCMSProfileIfAvailable(const ColorSpace& color_space) { |
| 599 ICCProfile icc_profile = ICCProfile::FromColorSpace(color_space); |
| 600 if (icc_profile.GetData().empty()) |
| 601 return nullptr; |
| 602 return qcms_profile_from_memory(icc_profile.GetData().data(), |
| 603 icc_profile.GetData().size()); |
| 604 } |
| 605 |
| 606 qcms_profile* GetXYZD50Profile() { |
| 607 // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects |
| 608 // is in fact not xyY color coordinates, it just wants the x/y values of the |
| 609 // primaries with Y equal to 1.0. |
| 610 qcms_CIE_xyYTRIPLE xyz; |
| 611 qcms_CIE_xyY w; |
| 612 xyz.red.x = 1.0f; |
| 613 xyz.red.y = 0.0f; |
| 614 xyz.red.Y = 1.0f; |
| 615 xyz.green.x = 0.0f; |
| 616 xyz.green.y = 1.0f; |
| 617 xyz.green.Y = 1.0f; |
| 618 xyz.blue.x = 0.0f; |
| 619 xyz.blue.y = 0.0f; |
| 620 xyz.blue.Y = 1.0f; |
| 621 w.x = 0.34567f; |
| 622 w.y = 0.35850f; |
| 623 w.Y = 1.0f; |
| 624 return qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f); |
| 625 } |
| 626 |
550 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( | 627 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( |
551 const ColorSpace& from, | 628 const ColorSpace& from, |
552 const ColorSpace& to, | 629 const ColorSpace& to, |
553 Intent intent) { | 630 Intent intent) { |
554 // TODO(Hubbe): Check if from and/or to can be mapped to ICC profiles and | 631 qcms_profile* from_profile = GetQCMSProfileIfAvailable(from); |
555 // provide better transforms in those cases. | 632 qcms_profile* to_profile = GetQCMSProfileIfAvailable(to); |
556 return std::unique_ptr<ColorTransform>( | 633 if (from_profile) { |
557 new ColorSpaceToColorSpaceTransform(from, to, intent)); | 634 if (to_profile) { |
| 635 return std::unique_ptr<ColorTransform>( |
| 636 new QCMSColorTransform(from_profile, to_profile)); |
| 637 } else { |
| 638 return std::unique_ptr<ColorTransform>(new ChainColorTransform( |
| 639 std::unique_ptr<ColorTransform>( |
| 640 new QCMSColorTransform(from_profile, GetXYZD50Profile())), |
| 641 std::unique_ptr<ColorTransform>(new ColorSpaceToColorSpaceTransform( |
| 642 ColorSpace::CreateXYZD50(), to, intent)))); |
| 643 } |
| 644 } else { |
| 645 if (to_profile) { |
| 646 return std::unique_ptr<ColorTransform>(new ChainColorTransform( |
| 647 std::unique_ptr<ColorTransform>(new ColorSpaceToColorSpaceTransform( |
| 648 from, ColorSpace::CreateXYZD50(), intent)), |
| 649 std::unique_ptr<ColorTransform>( |
| 650 new QCMSColorTransform(GetXYZD50Profile(), to_profile)))); |
| 651 } else { |
| 652 return std::unique_ptr<ColorTransform>( |
| 653 new ColorSpaceToColorSpaceTransform(from, to, intent)); |
| 654 } |
| 655 } |
558 } | 656 } |
559 | 657 |
560 } // namespace gfx | 658 } // namespace gfx |
OLD | NEW |