Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: ui/gfx/color_transform.cc

Issue 2696603003: color: Towards ColorTransform optimizations and code generation (Closed)
Patch Set: Add sub-class Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/gfx/color_transform.h ('k') | ui/gfx/color_transform_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 <vector> 8 #include <cmath>
9 #include <list>
10 #include <memory>
9 11
10 #include "base/logging.h" 12 #include "base/logging.h"
11 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "third_party/qcms/src/qcms.h"
12 #include "ui/gfx/color_space.h" 15 #include "ui/gfx/color_space.h"
13 #include "ui/gfx/icc_profile.h" 16 #include "ui/gfx/icc_profile.h"
17 #include "ui/gfx/skia_color_space_util.h"
14 #include "ui/gfx/transform.h" 18 #include "ui/gfx/transform.h"
15 #include "third_party/qcms/src/qcms.h"
16 19
17 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H 20 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H
18 extern "C" { 21 extern "C" {
19 #include "third_party/qcms/src/chain.h" 22 #include "third_party/qcms/src/chain.h"
20 }; 23 };
21 #endif 24 #endif
22 25
23 namespace gfx { 26 namespace gfx {
24 27
25 float EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x) { 28 namespace {
26 if (x < 0) 29
27 return 0; 30 // Helper for scoped QCMS profiles.
28 if (x < fn.fD) 31 struct QcmsProfileDeleter {
29 return fn.fC * x + fn.fF; 32 void operator()(qcms_profile* p) {
30 return powf(fn.fA * x + fn.fB, fn.fG) + fn.fE; 33 if (p) {
31 } 34 qcms_profile_release(p);
35 }
36 }
37 };
38 using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>;
32 39
33 Transform Invert(const Transform& t) { 40 Transform Invert(const Transform& t) {
34 Transform ret = t; 41 Transform ret = t;
35 if (!t.GetInverse(&ret)) { 42 if (!t.GetInverse(&ret)) {
36 LOG(ERROR) << "Inverse should alsways be possible."; 43 LOG(ERROR) << "Inverse should alsways be possible.";
37 } 44 }
38 return ret; 45 return ret;
39 } 46 }
40 47
41 float FromLinear(ColorSpace::TransferID id, float v) { 48 float FromLinear(ColorSpace::TransferID id, float v) {
42 switch (id) { 49 switch (id) {
43 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: 50 case ColorSpace::TransferID::SMPTEST2084_NON_HDR:
44 // Should already be handled. 51 // Should already be handled.
45 break; 52 break;
46 53
47 case ColorSpace::TransferID::LOG: 54 case ColorSpace::TransferID::LOG:
48 if (v < 0.01f) 55 if (v < 0.01f)
49 return 0.0f; 56 return 0.0f;
50 return 1.0f + log(v) / log(10.0f) / 2.0f; 57 return 1.0f + std::log(v) / std::log(10.0f) / 2.0f;
51 58
52 case ColorSpace::TransferID::LOG_SQRT: 59 case ColorSpace::TransferID::LOG_SQRT:
53 if (v < sqrt(10.0f) / 1000.0f) 60 if (v < std::sqrt(10.0f) / 1000.0f)
54 return 0.0f; 61 return 0.0f;
55 return 1.0f + log(v) / log(10.0f) / 2.5f; 62 return 1.0f + std::log(v) / std::log(10.0f) / 2.5f;
56 63
57 case ColorSpace::TransferID::IEC61966_2_4: { 64 case ColorSpace::TransferID::IEC61966_2_4: {
58 float a = 1.099296826809442f; 65 float a = 1.099296826809442f;
59 float b = 0.018053968510807f; 66 float b = 0.018053968510807f;
60 if (v < -b) { 67 if (v < -b) {
61 return -a * powf(-v, 0.45f) + (a - 1.0f); 68 return -a * std::pow(-v, 0.45f) + (a - 1.0f);
62 } else if (v <= b) { 69 } else if (v <= b) {
63 return 4.5f * v; 70 return 4.5f * v;
64 } else { 71 } else {
65 return a * powf(v, 0.45f) - (a - 1.0f); 72 return a * std::pow(v, 0.45f) - (a - 1.0f);
66 } 73 }
67 } 74 }
68 75
69 case ColorSpace::TransferID::BT1361_ECG: { 76 case ColorSpace::TransferID::BT1361_ECG: {
70 float a = 1.099f; 77 float a = 1.099f;
71 float b = 0.018f; 78 float b = 0.018f;
72 float l = 0.0045f; 79 float l = 0.0045f;
73 if (v < -l) { 80 if (v < -l) {
74 return -(a * powf(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f; 81 return -(a * std::pow(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
75 } else if (v <= b) { 82 } else if (v <= b) {
76 return 4.5f * v; 83 return 4.5f * v;
77 } else { 84 } else {
78 return a * powf(v, 0.45f) - (a - 1.0f); 85 return a * std::pow(v, 0.45f) - (a - 1.0f);
79 } 86 }
80 } 87 }
81 88
82 case ColorSpace::TransferID::SMPTEST2084: { 89 case ColorSpace::TransferID::SMPTEST2084: {
83 // Go from scRGB levels to 0-1. 90 // Go from scRGB levels to 0-1.
84 v *= 80.0f / 10000.0f; 91 v *= 80.0f / 10000.0f;
85 v = fmax(0.0f, v); 92 v = std::max(0.0f, v);
86 float m1 = (2610.0f / 4096.0f) / 4.0f; 93 float m1 = (2610.0f / 4096.0f) / 4.0f;
87 float m2 = (2523.0f / 4096.0f) * 128.0f; 94 float m2 = (2523.0f / 4096.0f) * 128.0f;
88 float c1 = 3424.0f / 4096.0f; 95 float c1 = 3424.0f / 4096.0f;
89 float c2 = (2413.0f / 4096.0f) * 32.0f; 96 float c2 = (2413.0f / 4096.0f) * 32.0f;
90 float c3 = (2392.0f / 4096.0f) * 32.0f; 97 float c3 = (2392.0f / 4096.0f) * 32.0f;
91 return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2); 98 return std::pow(
99 (c1 + c2 * std::pow(v, m1)) / (1.0f + c3 * std::pow(v, m1)), m2);
92 } 100 }
93 101
94 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf 102 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
95 case ColorSpace::TransferID::ARIB_STD_B67: { 103 case ColorSpace::TransferID::ARIB_STD_B67: {
96 const float a = 0.17883277f; 104 const float a = 0.17883277f;
97 const float b = 0.28466892f; 105 const float b = 0.28466892f;
98 const float c = 0.55991073f; 106 const float c = 0.55991073f;
99 v = fmax(0.0f, v); 107 v = std::max(0.0f, v);
100 if (v <= 1) 108 if (v <= 1)
101 return 0.5f * sqrtf(v); 109 return 0.5f * sqrtf(v);
102 else 110 else
103 return a * log(v - b) + c; 111 return a * std::log(v - b) + c;
104 } 112 }
105 113
106 default: 114 default:
107 // Handled by SkColorSpaceTransferFn. 115 // Handled by SkColorSpaceTransferFn.
108 break; 116 break;
109 } 117 }
110 NOTREACHED(); 118 NOTREACHED();
111 return 0; 119 return 0;
112 } 120 }
113 121
114 float ToLinear(ColorSpace::TransferID id, float v) { 122 float ToLinear(ColorSpace::TransferID id, float v) {
115 switch (id) { 123 switch (id) {
116 case ColorSpace::TransferID::LOG: 124 case ColorSpace::TransferID::LOG:
117 if (v < 0.0f) 125 if (v < 0.0f)
118 return 0.0f; 126 return 0.0f;
119 return powf(10.0f, (v - 1.0f) * 2.0f); 127 return std::pow(10.0f, (v - 1.0f) * 2.0f);
120 128
121 case ColorSpace::TransferID::LOG_SQRT: 129 case ColorSpace::TransferID::LOG_SQRT:
122 if (v < 0.0f) 130 if (v < 0.0f)
123 return 0.0f; 131 return 0.0f;
124 return powf(10.0f, (v - 1.0f) * 2.5f); 132 return std::pow(10.0f, (v - 1.0f) * 2.5f);
125 133
126 case ColorSpace::TransferID::IEC61966_2_4: { 134 case ColorSpace::TransferID::IEC61966_2_4: {
127 float a = 1.099296826809442f; 135 float a = 1.099296826809442f;
128 float b = 0.018053968510807f; 136 float b = 0.018053968510807f;
129 if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) { 137 if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) {
130 return -powf((a - 1.0f - v) / a, 1.0f / 0.45f); 138 return -std::pow((a - 1.0f - v) / a, 1.0f / 0.45f);
131 } else if (v <= FromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) { 139 } else if (v <= FromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) {
132 return v / 4.5f; 140 return v / 4.5f;
133 } else { 141 } else {
134 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); 142 return std::pow((v + a - 1.0f) / a, 1.0f / 0.45f);
135 } 143 }
136 } 144 }
137 145
138 case ColorSpace::TransferID::BT1361_ECG: { 146 case ColorSpace::TransferID::BT1361_ECG: {
139 float a = 1.099f; 147 float a = 1.099f;
140 float b = 0.018f; 148 float b = 0.018f;
141 float l = 0.0045f; 149 float l = 0.0045f;
142 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { 150 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) {
143 return -powf((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f; 151 return -std::pow((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
144 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { 152 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) {
145 return v / 4.5f; 153 return v / 4.5f;
146 } else { 154 } else {
147 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); 155 return std::pow((v + a - 1.0f) / a, 1.0f / 0.45f);
148 } 156 }
149 } 157 }
150 158
151 case ColorSpace::TransferID::SMPTEST2084: { 159 case ColorSpace::TransferID::SMPTEST2084: {
152 v = fmax(0.0f, v); 160 v = std::max(0.0f, v);
153 float m1 = (2610.0f / 4096.0f) / 4.0f; 161 float m1 = (2610.0f / 4096.0f) / 4.0f;
154 float m2 = (2523.0f / 4096.0f) * 128.0f; 162 float m2 = (2523.0f / 4096.0f) * 128.0f;
155 float c1 = 3424.0f / 4096.0f; 163 float c1 = 3424.0f / 4096.0f;
156 float c2 = (2413.0f / 4096.0f) * 32.0f; 164 float c2 = (2413.0f / 4096.0f) * 32.0f;
157 float c3 = (2392.0f / 4096.0f) * 32.0f; 165 float c3 = (2392.0f / 4096.0f) * 32.0f;
158 v = powf( 166 v = std::pow(std::max(std::pow(v, 1.0f / m2) - c1, 0.0f) /
159 fmax(powf(v, 1.0f / m2) - c1, 0) / (c2 - c3 * powf(v, 1.0f / m2)), 167 (c2 - c3 * std::pow(v, 1.0f / m2)),
160 1.0f / m1); 168 1.0f / m1);
161 // This matches the scRGB definition that 1.0 means 80 nits. 169 // This matches the scRGB definition that 1.0 means 80 nits.
162 // TODO(hubbe): It would be *nice* if 1.0 meant more than that, but 170 // TODO(hubbe): It would be *nice* if 1.0 meant more than that, but
163 // that might be difficult to do right now. 171 // that might be difficult to do right now.
164 v *= 10000.0f / 80.0f; 172 v *= 10000.0f / 80.0f;
165 return v; 173 return v;
166 } 174 }
167 175
168 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: 176 case ColorSpace::TransferID::SMPTEST2084_NON_HDR:
169 v = fmax(0.0f, v); 177 v = std::max(0.0f, v);
170 return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f); 178 return std::min(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f);
171 179
172 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf 180 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
173 case ColorSpace::TransferID::ARIB_STD_B67: { 181 case ColorSpace::TransferID::ARIB_STD_B67: {
174 v = fmax(0.0f, v); 182 v = std::max(0.0f, v);
175 const float a = 0.17883277f; 183 const float a = 0.17883277f;
176 const float b = 0.28466892f; 184 const float b = 0.28466892f;
177 const float c = 0.55991073f; 185 const float c = 0.55991073f;
178 float v_ = 0.0f; 186 float v_ = 0.0f;
179 if (v <= 0.5f) { 187 if (v <= 0.5f) {
180 v_ = (v * 2.0f) * (v * 2.0f); 188 v_ = (v * 2.0f) * (v * 2.0f);
181 } else { 189 } else {
182 v_ = exp((v - c) / a) + b; 190 v_ = std::exp((v - c) / a) + b;
183 } 191 }
184 return v_; 192 return v_;
185 } 193 }
186 194
187 default: 195 default:
188 // Handled by SkColorSpaceTransferFn. 196 // Handled by SkColorSpaceTransferFn.
189 break; 197 break;
190 } 198 }
191 NOTREACHED(); 199 NOTREACHED();
192 return 0; 200 return 0;
193 } 201 }
194 202
195 Transform GetTransferMatrix(const gfx::ColorSpace& color_space) { 203 Transform GetTransferMatrix(const gfx::ColorSpace& color_space) {
196 SkMatrix44 transfer_matrix; 204 SkMatrix44 transfer_matrix;
197 color_space.GetTransferMatrix(&transfer_matrix); 205 color_space.GetTransferMatrix(&transfer_matrix);
198 return Transform(transfer_matrix); 206 return Transform(transfer_matrix);
199 } 207 }
200 208
201 Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space) { 209 Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space) {
202 SkMatrix44 range_adjust_matrix; 210 SkMatrix44 range_adjust_matrix;
203 color_space.GetRangeAdjustMatrix(&range_adjust_matrix); 211 color_space.GetRangeAdjustMatrix(&range_adjust_matrix);
204 return Transform(range_adjust_matrix); 212 return Transform(range_adjust_matrix);
205 } 213 }
206 214
215 Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
216 SkMatrix44 primary_matrix;
217 color_space.GetPrimaryMatrix(&primary_matrix);
218 return Transform(primary_matrix);
219 }
220
221 } // namespace
222
207 class ColorTransformMatrix; 223 class ColorTransformMatrix;
208 class ColorTransformToLinear;
209 class ColorTransformFromLinear; 224 class ColorTransformFromLinear;
210 class ColorTransformToBT2020CL; 225 class ColorTransformToBT2020CL;
211 class ColorTransformFromBT2020CL; 226 class ColorTransformFromBT2020CL;
212 class ColorTransformNull; 227 class ColorTransformNull;
228 class QCMSColorTransform;
213 229
214 class ColorTransformInternal : public ColorTransform { 230 class ColorTransformStep {
215 public: 231 public:
216 // Visitor pattern, Prepend() calls return prev->Join(this). 232 virtual ~ColorTransformStep() {}
217 virtual bool Prepend(ColorTransformInternal* prev) = 0; 233 virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; }
hubbe 2017/02/14 19:04:51 When I first saw this, I thought: Why'd you go and
ccameron 2017/02/15 00:11:31 Sure -- updated the CL description (after skimming
234 virtual ColorTransformToBT2020CL* GetToBT2020CL() { return nullptr; }
235 virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; }
236 virtual ColorTransformMatrix* GetMatrix() { return nullptr; }
237 virtual ColorTransformNull* GetNull() { return nullptr; }
238 virtual QCMSColorTransform* GetQCMS() { return nullptr; }
218 239
219 // Join methods, returns true if the |next| transform was successfully 240 // Join methods, returns true if the |next| transform was successfully
220 // assimilated into |this|. 241 // assimilated into |this|.
221 // If Join() returns true, |next| is no longer needed and can be deleted. 242 // If Join() returns true, |next| is no longer needed and can be deleted.
222 virtual bool Join(const ColorTransformToLinear& next) { return false; } 243 virtual bool Join(ColorTransformStep* next) { return false; }
223 virtual bool Join(const ColorTransformFromLinear& next) { return false; }
224 virtual bool Join(const ColorTransformToBT2020CL& next) { return false; }
225 virtual bool Join(const ColorTransformFromBT2020CL& next) { return false; }
226 virtual bool Join(const ColorTransformMatrix& next) { return false; }
227 virtual bool Join(const ColorTransformNull& next) { return true; }
228 244
229 // Return true if this is a null transform. 245 // Return true if this is a null transform.
230 virtual bool IsNull() { return false; } 246 virtual bool IsNull() { return false; }
247
248 virtual void Transform(ColorTransform::TriStim* color, size_t num) = 0;
231 }; 249 };
232 250
233 class ColorTransformNull : public ColorTransformInternal { 251 typedef std::list<std::unique_ptr<ColorTransformStep>> ColorTransformStepList;
252
253 class GFX_EXPORT ColorTransformInternal : public ColorTransform {
234 public: 254 public:
235 bool Prepend(ColorTransformInternal* prev) override { 255 ColorTransformInternal(ColorTransformStepList steps)
236 return prev->Join(*this); 256 : steps_(std::move(steps)) {}
257 ~ColorTransformInternal() override {}
258
259 // Perform transformation of colors, |colors| is both input and output.
260 void Transform(TriStim* colors, size_t num) override {
261 for (const auto& step : steps_)
262 step->Transform(colors, num);
237 } 263 }
238 bool IsNull() override { return true; } 264
239 void transform(ColorTransform::TriStim* color, size_t num) override {} 265 size_t NumberOfStepsForTesting() const override { return steps_.size(); }
266
267 static void Append(ColorSpace from,
hubbe 2017/02/14 19:04:51 How about making this non-static and have it appen
ccameron 2017/02/15 00:11:31 That makes sense -- done.
268 const ColorSpace& to,
269 ColorTransform::Intent intent,
270 ColorTransformStepList* builder);
271
272 static void Simplify(ColorTransformStepList* steps);
hubbe 2017/02/14 19:04:51 Make non-static and operate on steps_ ?
ccameron 2017/02/15 00:11:31 (same deal).
273
274 private:
275 ColorTransformStepList steps_;
240 }; 276 };
241 277
242 class ColorTransformMatrix : public ColorTransformInternal { 278 class ColorTransformNull : public ColorTransformStep {
243 public: 279 public:
244 explicit ColorTransformMatrix(const Transform& matrix) : matrix_(matrix) {} 280 ColorTransformNull* GetNull() override { return this; }
281 bool IsNull() override { return true; }
282 void Transform(ColorTransform::TriStim* color, size_t num) override {}
283 };
245 284
246 bool Prepend(ColorTransformInternal* prev) override { 285 class ColorTransformMatrix : public ColorTransformStep {
247 return prev->Join(*this); 286 public:
248 } 287 explicit ColorTransformMatrix(const class Transform& matrix)
249 288 : matrix_(matrix) {}
250 bool Join(const ColorTransformMatrix& next) override { 289 ColorTransformMatrix* GetMatrix() override { return this; }
251 Transform tmp = next.matrix_; 290 bool Join(ColorTransformStep* next_untyped) override {
291 ColorTransformMatrix* next = next_untyped->GetMatrix();
292 if (!next)
293 return false;
294 class Transform tmp = next->matrix_;
252 tmp *= matrix_; 295 tmp *= matrix_;
253 matrix_ = tmp; 296 matrix_ = tmp;
254 return true; 297 return true;
255 } 298 }
256 299
257 bool IsNull() override { 300 bool IsNull() override {
258 // Returns true if we're very close to an identity matrix. 301 return SkMatrixIsApproximatelyIdentity(matrix_.matrix());
259 for (int i = 0; i < 4; i++) {
260 for (int j = 0; j < 4; j++) {
261 float expected = i == j ? 1.0f : 0.0f;
262 if (fabs(matrix_.matrix().get(i, j) - expected) > 0.00001f) {
263 return false;
264 }
265 }
266 }
267 return true;
268 } 302 }
269 303
270 void transform(ColorTransform::TriStim* colors, size_t num) override { 304 void Transform(ColorTransform::TriStim* colors, size_t num) override {
271 for (size_t i = 0; i < num; i++) 305 for (size_t i = 0; i < num; i++)
272 matrix_.TransformPoint(colors + i); 306 matrix_.TransformPoint(colors + i);
273 } 307 }
274 308
275 private: 309 private:
276 Transform matrix_; 310 class Transform matrix_;
277 }; 311 };
278 312
279 class ColorTransformFromLinear : public ColorTransformInternal { 313 class ColorTransformFromLinear : public ColorTransformStep {
280 public: 314 public:
281 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer, 315 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer,
282 const SkColorSpaceTransferFn& fn, 316 const SkColorSpaceTransferFn& fn,
283 bool fn_valid) 317 bool fn_valid)
284 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) { 318 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {
285 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) 319 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
286 transfer_ = ColorSpace::TransferID::LINEAR; 320 transfer_ = ColorSpace::TransferID::LINEAR;
287 } 321 }
288 bool Prepend(ColorTransformInternal* prev) override { 322 ColorTransformFromLinear* GetFromLinear() override { return this; }
289 return prev->Join(*this);
290 }
291
292 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } 323 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
293 324 void Transform(ColorTransform::TriStim* colors, size_t num) override {
294 void transform(ColorTransform::TriStim* colors, size_t num) override {
295 if (fn_valid_) { 325 if (fn_valid_) {
296 for (size_t i = 0; i < num; i++) { 326 for (size_t i = 0; i < num; i++) {
297 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); 327 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
298 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); 328 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y()));
299 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); 329 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z()));
300 } 330 }
301 } else { 331 } else {
302 for (size_t i = 0; i < num; i++) { 332 for (size_t i = 0; i < num; i++) {
303 colors[i].set_x(FromLinear(transfer_, colors[i].x())); 333 colors[i].set_x(FromLinear(transfer_, colors[i].x()));
304 colors[i].set_y(FromLinear(transfer_, colors[i].y())); 334 colors[i].set_y(FromLinear(transfer_, colors[i].y()));
305 colors[i].set_z(FromLinear(transfer_, colors[i].z())); 335 colors[i].set_z(FromLinear(transfer_, colors[i].z()));
306 } 336 }
307 } 337 }
308 } 338 }
309 339
310 private: 340 private:
311 friend class ColorTransformToLinear; 341 friend class ColorTransformToLinear;
312 ColorSpace::TransferID transfer_; 342 ColorSpace::TransferID transfer_;
313 SkColorSpaceTransferFn fn_; 343 SkColorSpaceTransferFn fn_;
314 bool fn_valid_ = false; 344 bool fn_valid_ = false;
315 }; 345 };
316 346
317 class ColorTransformToLinear : public ColorTransformInternal { 347 class ColorTransformToLinear : public ColorTransformStep {
318 public: 348 public:
319 explicit ColorTransformToLinear(ColorSpace::TransferID transfer, 349 explicit ColorTransformToLinear(ColorSpace::TransferID transfer,
320 const SkColorSpaceTransferFn& fn, 350 const SkColorSpaceTransferFn& fn,
321 bool fn_valid) 351 bool fn_valid)
322 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) { 352 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {
323 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) 353 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR)
324 transfer_ = ColorSpace::TransferID::LINEAR; 354 transfer_ = ColorSpace::TransferID::LINEAR;
325 } 355 }
326 356
327 bool Prepend(ColorTransformInternal* prev) override {
328 return prev->Join(*this);
329 }
330
331 static bool IsGamma22(ColorSpace::TransferID transfer) { 357 static bool IsGamma22(ColorSpace::TransferID transfer) {
332 switch (transfer) { 358 switch (transfer) {
333 // We don't need to check BT709 here because it's been translated into 359 // We don't need to check BT709 here because it's been translated into
334 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below. 360 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below.
335 case ColorSpace::TransferID::GAMMA22: 361 case ColorSpace::TransferID::GAMMA22:
336 case ColorSpace::TransferID::IEC61966_2_1: // SRGB 362 case ColorSpace::TransferID::IEC61966_2_1: // SRGB
337 return true; 363 return true;
338 364
339 default: 365 default:
340 return false; 366 return false;
341 } 367 }
342 } 368 }
343 369
344 bool Join(const ColorTransformFromLinear& next) override { 370 bool Join(ColorTransformStep* next_untyped) override {
345 if (transfer_ == next.transfer_ || 371 ColorTransformFromLinear* next = next_untyped->GetFromLinear();
346 (IsGamma22(transfer_) && IsGamma22(next.transfer_))) { 372 if (!next)
373 return false;
374 // TODO(ccameron): Use SkTransferFnsApproximatelyCancel and
375 // SkTransferFnIsApproximatelyIdentity to merge parametric transfer
376 // functions.
377 if (transfer_ == next->transfer_ ||
378 (IsGamma22(transfer_) && IsGamma22(next->transfer_))) {
347 transfer_ = ColorSpace::TransferID::LINEAR; 379 transfer_ = ColorSpace::TransferID::LINEAR;
348 return true; 380 return true;
349 } 381 }
350 return false; 382 return false;
351 } 383 }
352 384
353 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } 385 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
354 386
355 // Assumes BT2020 primaries. 387 // Assumes BT2020 primaries.
356 static float Luma(const ColorTransform::TriStim& c) { 388 static float Luma(const ColorTransform::TriStim& c) {
357 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f; 389 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f;
358 } 390 }
359 391
360 ColorTransform::TriStim ClipToWhite(ColorTransform::TriStim& c) { 392 ColorTransform::TriStim ClipToWhite(ColorTransform::TriStim& c) {
361 float maximum = std::max(std::max(c.x(), c.y()), c.z()); 393 float maximum = std::max(std::max(c.x(), c.y()), c.z());
362 if (maximum > 1.0f) { 394 if (maximum > 1.0f) {
363 float l = Luma(c); 395 float l = Luma(c);
364 c.Scale(1.0f / maximum); 396 c.Scale(1.0f / maximum);
365 ColorTransform::TriStim white(1.0f, 1.0f, 1.0f); 397 ColorTransform::TriStim white(1.0f, 1.0f, 1.0f);
366 white.Scale((1.0f - 1.0f / maximum) * l / Luma(white)); 398 white.Scale((1.0f - 1.0f / maximum) * l / Luma(white));
367 ColorTransform::TriStim black(0.0f, 0.0f, 0.0f); 399 ColorTransform::TriStim black(0.0f, 0.0f, 0.0f);
368 c += white - black; 400 c += white - black;
369 } 401 }
370 return c; 402 return c;
371 } 403 }
372 404
373 void transform(ColorTransform::TriStim* colors, size_t num) override { 405 void Transform(ColorTransform::TriStim* colors, size_t num) override {
374 if (fn_valid_) { 406 if (fn_valid_) {
375 for (size_t i = 0; i < num; i++) { 407 for (size_t i = 0; i < num; i++) {
376 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); 408 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x()));
377 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); 409 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y()));
378 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); 410 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z()));
379 } 411 }
380 } else if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { 412 } else if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) {
381 for (size_t i = 0; i < num; i++) { 413 for (size_t i = 0; i < num; i++) {
382 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()), 414 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()),
383 ToLinear(transfer_, colors[i].y()), 415 ToLinear(transfer_, colors[i].y()),
(...skipping 29 matching lines...) Expand all
413 // on the RGB values. However, running the transfer function 445 // on the RGB values. However, running the transfer function
414 // on the U and V values doesn't make any sense since they 446 // on the U and V values doesn't make any sense since they
415 // are centered at 0.5. To work around this, the transfer function 447 // are centered at 0.5. To work around this, the transfer function
416 // is applied to the Y, R and B values, and then the U and V 448 // is applied to the Y, R and B values, and then the U and V
417 // values are calculated from that. 449 // values are calculated from that.
418 // In our implementation, the YUV->RGB matrix is used to 450 // In our implementation, the YUV->RGB matrix is used to
419 // convert YUV to RYB (the G value is replaced with an Y value.) 451 // convert YUV to RYB (the G value is replaced with an Y value.)
420 // Then we run the transfer function like normal, and finally 452 // Then we run the transfer function like normal, and finally
421 // this class is inserted as an extra step which takes calculates 453 // this class is inserted as an extra step which takes calculates
422 // the U and V values. 454 // the U and V values.
423 class ColorTransformToBT2020CL : public ColorTransformInternal { 455 class ColorTransformToBT2020CL : public ColorTransformStep {
424 public: 456 public:
425 bool Prepend(ColorTransformInternal* prev) override { 457 bool Join(ColorTransformStep* next_untyped) override {
426 return prev->Join(*this); 458 ColorTransformFromBT2020CL* next = next_untyped->GetFromBT2020CL();
427 } 459 if (!next)
428 460 return false;
429 bool Join(const ColorTransformFromBT2020CL& next) override {
430 if (null_) 461 if (null_)
431 return false; 462 return false;
432 null_ = true; 463 null_ = true;
433 return true; 464 return true;
434 } 465 }
435 466
436 bool IsNull() override { return null_; } 467 bool IsNull() override { return null_; }
437 468
438 void transform(ColorTransform::TriStim* RYB, size_t num) override { 469 void Transform(ColorTransform::TriStim* RYB, size_t num) override {
439 for (size_t i = 0; i < num; i++) { 470 for (size_t i = 0; i < num; i++) {
440 float U, V; 471 float U, V;
441 float B_Y = RYB[i].z() - RYB[i].y(); 472 float B_Y = RYB[i].z() - RYB[i].y();
442 if (B_Y <= 0) { 473 if (B_Y <= 0) {
443 U = B_Y / (-2.0 * -0.9702); 474 U = B_Y / (-2.0 * -0.9702);
444 } else { 475 } else {
445 U = B_Y / (2.0 * 0.7910); 476 U = B_Y / (2.0 * 0.7910);
446 } 477 }
447 float R_Y = RYB[i].x() - RYB[i].y(); 478 float R_Y = RYB[i].x() - RYB[i].y();
448 if (R_Y <= 0) { 479 if (R_Y <= 0) {
449 V = R_Y / (-2.0 * -0.8591); 480 V = R_Y / (-2.0 * -0.8591);
450 } else { 481 } else {
451 V = R_Y / (2.0 * 0.4969); 482 V = R_Y / (2.0 * 0.4969);
452 } 483 }
453 RYB[i] = ColorTransform::TriStim(RYB[i].y(), U, V); 484 RYB[i] = ColorTransform::TriStim(RYB[i].y(), U, V);
454 } 485 }
455 } 486 }
456 487
457 private: 488 private:
458 bool null_ = false; 489 bool null_ = false;
459 }; 490 };
460 491
461 // Inverse of ColorTransformToBT2020CL, see comment above for more info. 492 // Inverse of ColorTransformToBT2020CL, see comment above for more info.
462 class ColorTransformFromBT2020CL : public ColorTransformInternal { 493 class ColorTransformFromBT2020CL : public ColorTransformStep {
463 public: 494 public:
464 bool Prepend(ColorTransformInternal* prev) override { 495 bool Join(ColorTransformStep* next_untyped) override {
465 return prev->Join(*this); 496 ColorTransformToBT2020CL* next = next_untyped->GetToBT2020CL();
466 } 497 if (!next)
467 498 return false;
468 bool Join(const ColorTransformToBT2020CL& next) override {
469 if (null_) 499 if (null_)
470 return false; 500 return false;
471 null_ = true; 501 null_ = true;
472 return true; 502 return true;
473 } 503 }
474 504
475 bool IsNull() override { return null_; } 505 bool IsNull() override { return null_; }
476 506
477 void transform(ColorTransform::TriStim* YUV, size_t num) override { 507 void Transform(ColorTransform::TriStim* YUV, size_t num) override {
478 if (null_) 508 if (null_)
479 return; 509 return;
480 for (size_t i = 0; i < num; i++) { 510 for (size_t i = 0; i < num; i++) {
481 float Y = YUV[i].x(); 511 float Y = YUV[i].x();
482 float U = YUV[i].y(); 512 float U = YUV[i].y();
483 float V = YUV[i].z(); 513 float V = YUV[i].z();
484 float B_Y, R_Y; 514 float B_Y, R_Y;
485 if (U <= 0) { 515 if (U <= 0) {
486 B_Y = Y * (-2.0 * -0.9702); 516 B_Y = Y * (-2.0 * -0.9702);
487 } else { 517 } else {
488 B_Y = U * (2.0 * 0.7910); 518 B_Y = U * (2.0 * 0.7910);
489 } 519 }
490 if (V <= 0) { 520 if (V <= 0) {
491 R_Y = V * (-2.0 * -0.8591); 521 R_Y = V * (-2.0 * -0.8591);
492 } else { 522 } else {
493 R_Y = V * (2.0 * 0.4969); 523 R_Y = V * (2.0 * 0.4969);
494 } 524 }
495 // Return an RYB value, later steps will fix it. 525 // Return an RYB value, later steps will fix it.
496 YUV[i] = ColorTransform::TriStim(R_Y + Y, YUV[i].x(), B_Y + Y); 526 YUV[i] = ColorTransform::TriStim(R_Y + Y, YUV[i].x(), B_Y + Y);
497 } 527 }
498 } 528 }
499 529
500 private: 530 private:
501 bool null_ = false; 531 bool null_ = false;
502 }; 532 };
503 533
504 class ChainColorTransform : public ColorTransform { 534 // static
505 public: 535 void ColorTransformInternal::Append(ColorSpace from,
hubbe 2017/02/14 19:04:51 Append is kind of a strange name for this. Maybe A
ccameron 2017/02/15 00:11:31 Done.
506 ChainColorTransform(std::unique_ptr<ColorTransform> a, 536 const ColorSpace& to,
507 std::unique_ptr<ColorTransform> b) 537 ColorTransform::Intent intent,
508 : a_(std::move(a)), b_(std::move(b)) {} 538 ColorTransformStepList* builder) {
539 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
540 switch (from.transfer_) {
541 case ColorSpace::TransferID::UNSPECIFIED:
542 case ColorSpace::TransferID::BT709:
543 case ColorSpace::TransferID::SMPTE170M:
544 // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
545 // content. However, most displays actually use a gamma of 2.2, and
546 // user studies shows that users don't really care. Using the same
547 // gamma as the display will let us optimize a lot more, so lets stick
548 // with using the SRGB transfer function.
549 from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
550 break;
509 551
510 private: 552 case ColorSpace::TransferID::SMPTEST2084:
511 void transform(TriStim* colors, size_t num) override { 553 if (!to.IsHDR()) {
512 a_->transform(colors, num); 554 // We don't have an HDR display, so replace SMPTE 2084 with
513 b_->transform(colors, num); 555 // something that returns ranges more or less suitable for a normal
514 } 556 // display.
515 std::unique_ptr<ColorTransform> a_; 557 from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
516 std::unique_ptr<ColorTransform> b_; 558 }
517 }; 559 break;
518 560
519 class TransformBuilder { 561 case ColorSpace::TransferID::ARIB_STD_B67:
520 public: 562 if (!to.IsHDR()) {
521 void Append(std::unique_ptr<ColorTransformInternal> transform) { 563 // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
522 if (!disable_optimizations_ && transform->IsNull()) 564 // displays.
523 return; // Null transform 565 from.transfer_ = ColorSpace::TransferID::GAMMA24;
524 transforms_.push_back(std::move(transform)); 566 }
525 if (disable_optimizations_)
526 return;
527 while (transforms_.size() >= 2 &&
528 transforms_.back()->Prepend(
529 transforms_[transforms_.size() - 2].get())) {
530 transforms_.pop_back();
531 if (transforms_.back()->IsNull()) {
532 transforms_.pop_back();
533 break; 567 break;
534 } 568
569 default: // Do nothing
570 break;
535 } 571 }
572
573 // TODO(hubbe): shrink gamuts here (never stretch gamuts)
536 } 574 }
537 575
538 std::unique_ptr<ColorTransform> GetTransform() { 576 builder->push_back(
539 if (transforms_.empty()) 577 base::MakeUnique<ColorTransformMatrix>(GetRangeAdjustMatrix(from)));
540 return base::MakeUnique<ColorTransformNull>();
541 std::unique_ptr<ColorTransform> ret(std::move(transforms_.back()));
542 transforms_.pop_back();
543 578
544 while (!transforms_.empty()) { 579 builder->push_back(
545 ret = std::unique_ptr<ColorTransform>(new ChainColorTransform( 580 base::MakeUnique<ColorTransformMatrix>(Invert(GetTransferMatrix(from))));
546 std::move(transforms_.back()), std::move(ret)));
547 transforms_.pop_back();
548 }
549 581
550 return ret; 582 SkColorSpaceTransferFn to_linear_fn;
583 bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
584 builder->push_back(base::MakeUnique<ColorTransformToLinear>(
585 from.transfer_, to_linear_fn, to_linear_fn_valid));
586
587 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
588 // BT2020 CL is a special case.
589 builder->push_back(base::MakeUnique<ColorTransformFromBT2020CL>());
590 }
591 builder->push_back(
592 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
593
594 builder->push_back(
595 base::MakeUnique<ColorTransformMatrix>(Invert(GetPrimaryTransform(to))));
596 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
597 // BT2020 CL is a special case.
598 builder->push_back(base::MakeUnique<ColorTransformToBT2020CL>());
551 } 599 }
552 600
553 void disable_optimizations() { disable_optimizations_ = true; } 601 SkColorSpaceTransferFn from_linear_fn;
602 bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
603 builder->push_back(base::MakeUnique<ColorTransformFromLinear>(
604 to.transfer_, from_linear_fn, from_linear_fn_valid));
554 605
555 private: 606 builder->push_back(
556 bool disable_optimizations_ = false; 607 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
557 std::vector<std::unique_ptr<ColorTransformInternal>> transforms_;
558 };
559 608
560 class ColorSpaceToColorSpaceTransform { 609 builder->push_back(
561 public: 610 base::MakeUnique<ColorTransformMatrix>(Invert(GetRangeAdjustMatrix(to))));
562 static Transform GetPrimaryTransform(const ColorSpace& c) { 611 }
563 SkMatrix44 sk_matrix;
564 c.GetPrimaryMatrix(&sk_matrix);
565 return Transform(sk_matrix);
566 }
567 612
568 static void ColorSpaceToColorSpace(ColorSpace from, 613 class QCMSColorTransform : public ColorTransformStep {
569 ColorSpace to,
570 ColorTransform::Intent intent,
571 TransformBuilder* builder) {
572 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) {
573 switch (from.transfer_) {
574 case ColorSpace::TransferID::UNSPECIFIED:
575 case ColorSpace::TransferID::BT709:
576 case ColorSpace::TransferID::SMPTE170M:
577 // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
578 // content. However, most displays actually use a gamma of 2.2, and
579 // user studies shows that users don't really care. Using the same
580 // gamma as the display will let us optimize a lot more, so lets stick
581 // with using the SRGB transfer function.
582 from.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
583 break;
584
585 case ColorSpace::TransferID::SMPTEST2084:
586 if (!to.IsHDR()) {
587 // We don't have an HDR display, so replace SMPTE 2084 with
588 // something that returns ranges more or less suitable for a normal
589 // display.
590 from.transfer_ = ColorSpace::TransferID::SMPTEST2084_NON_HDR;
591 }
592 break;
593
594 case ColorSpace::TransferID::ARIB_STD_B67:
595 if (!to.IsHDR()) {
596 // Interpreting HLG using a gamma 2.4 works reasonably well for SDR
597 // displays.
598 from.transfer_ = ColorSpace::TransferID::GAMMA24;
599 }
600 break;
601
602 default: // Do nothing
603 break;
604 }
605
606 // TODO(hubbe): shrink gamuts here (never stretch gamuts)
607 }
608
609 builder->Append(base::MakeUnique<ColorTransformMatrix>(
610 GetRangeAdjustMatrix(from)));
611
612 builder->Append(base::MakeUnique<ColorTransformMatrix>(
613 Invert(GetTransferMatrix(from))));
614
615 SkColorSpaceTransferFn to_linear_fn;
616 bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn);
617 builder->Append(base::MakeUnique<ColorTransformToLinear>(
618 from.transfer_, to_linear_fn, to_linear_fn_valid));
619
620 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
621 // BT2020 CL is a special case.
622 builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>());
623 }
624 builder->Append(
625 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from)));
626
627 builder->Append(base::MakeUnique<ColorTransformMatrix>(
628 Invert(GetPrimaryTransform(to))));
629 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
630 // BT2020 CL is a special case.
631 builder->Append(base::MakeUnique<ColorTransformToBT2020CL>());
632 }
633
634 SkColorSpaceTransferFn from_linear_fn;
635 bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn);
636 builder->Append(base::MakeUnique<ColorTransformFromLinear>(
637 to.transfer_, from_linear_fn, from_linear_fn_valid));
638
639 builder->Append(
640 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to)));
641
642 builder->Append(base::MakeUnique<ColorTransformMatrix>(
643 Invert(GetRangeAdjustMatrix(to))));
644 }
645 };
646
647 class QCMSColorTransform : public ColorTransformInternal {
648 public: 614 public:
649 // Takes ownership of the profiles 615 // Takes ownership of the profiles
650 QCMSColorTransform(qcms_profile* from, qcms_profile* to) 616 QCMSColorTransform(ScopedQcmsProfile from, ScopedQcmsProfile to)
651 : from_(from), to_(to) {} 617 : from_(std::move(from)), to_(std::move(to)) {}
652 ~QCMSColorTransform() override { 618 ~QCMSColorTransform() override {}
653 qcms_profile_release(from_); 619 QCMSColorTransform* GetQCMS() override { return this; }
654 qcms_profile_release(to_); 620 bool Join(ColorTransformStep* next_untyped) override {
655 } 621 QCMSColorTransform* next = next_untyped->GetQCMS();
656 bool Prepend(ColorTransformInternal* prev) override { 622 if (!next)
657 // Not currently optimizable. 623 return false;
624 if (qcms_profile_match(to_.get(), next->from_.get())) {
625 to_ = std::move(next->to_);
626 return true;
627 }
658 return false; 628 return false;
659 } 629 }
660 bool IsNull() override { return from_ == to_; } 630 bool IsNull() override {
661 void transform(TriStim* colors, size_t num) override { 631 if (qcms_profile_match(from_.get(), to_.get()))
662 CHECK(sizeof(TriStim) == sizeof(float[3])); 632 return true;
633 return false;
634 }
635 void Transform(ColorTransform::TriStim* colors, size_t num) override {
636 CHECK(sizeof(ColorTransform::TriStim) == sizeof(float[3]));
663 // QCMS doesn't like numbers outside 0..1 637 // QCMS doesn't like numbers outside 0..1
664 for (size_t i = 0; i < num; i++) { 638 for (size_t i = 0; i < num; i++) {
665 colors[i].set_x(fmin(1.0f, fmax(0.0f, colors[i].x()))); 639 colors[i].set_x(std::min(1.0f, std::max(0.0f, colors[i].x())));
666 colors[i].set_y(fmin(1.0f, fmax(0.0f, colors[i].y()))); 640 colors[i].set_y(std::min(1.0f, std::max(0.0f, colors[i].y())));
667 colors[i].set_z(fmin(1.0f, fmax(0.0f, colors[i].z()))); 641 colors[i].set_z(std::min(1.0f, std::max(0.0f, colors[i].z())));
668 } 642 }
669 qcms_chain_transform(from_, to_, reinterpret_cast<float*>(colors), 643 qcms_chain_transform(from_.get(), to_.get(),
644 reinterpret_cast<float*>(colors),
670 reinterpret_cast<float*>(colors), num * 3); 645 reinterpret_cast<float*>(colors), num * 3);
671 } 646 }
672 647
673 private: 648 private:
674 qcms_profile *from_, *to_; 649 ScopedQcmsProfile from_;
650 ScopedQcmsProfile to_;
675 }; 651 };
676 652
677 qcms_profile* GetQCMSProfileIfAvailable(const ColorSpace& color_space) { 653 ScopedQcmsProfile GetQCMSProfileIfAvailable(const ColorSpace& color_space) {
678 ICCProfile icc_profile = ICCProfile::FromColorSpace(color_space); 654 ICCProfile icc_profile = ICCProfile::FromColorSpace(color_space);
679 if (icc_profile.GetData().empty()) 655 if (icc_profile.GetData().empty())
680 return nullptr; 656 return nullptr;
681 return qcms_profile_from_memory(icc_profile.GetData().data(), 657 return ScopedQcmsProfile(qcms_profile_from_memory(
682 icc_profile.GetData().size()); 658 icc_profile.GetData().data(), icc_profile.GetData().size()));
683 } 659 }
684 660
685 qcms_profile* GetXYZD50Profile() { 661 ScopedQcmsProfile GetXYZD50Profile() {
686 // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects 662 // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects
687 // is in fact not xyY color coordinates, it just wants the x/y values of the 663 // is in fact not xyY color coordinates, it just wants the x/y values of the
688 // primaries with Y equal to 1.0. 664 // primaries with Y equal to 1.0.
689 qcms_CIE_xyYTRIPLE xyz; 665 qcms_CIE_xyYTRIPLE xyz;
690 qcms_CIE_xyY w; 666 qcms_CIE_xyY w;
691 xyz.red.x = 1.0f; 667 xyz.red.x = 1.0f;
692 xyz.red.y = 0.0f; 668 xyz.red.y = 0.0f;
693 xyz.red.Y = 1.0f; 669 xyz.red.Y = 1.0f;
694 xyz.green.x = 0.0f; 670 xyz.green.x = 0.0f;
695 xyz.green.y = 1.0f; 671 xyz.green.y = 1.0f;
696 xyz.green.Y = 1.0f; 672 xyz.green.Y = 1.0f;
697 xyz.blue.x = 0.0f; 673 xyz.blue.x = 0.0f;
698 xyz.blue.y = 0.0f; 674 xyz.blue.y = 0.0f;
699 xyz.blue.Y = 1.0f; 675 xyz.blue.Y = 1.0f;
700 w.x = 0.34567f; 676 w.x = 0.34567f;
701 w.y = 0.35850f; 677 w.y = 0.35850f;
702 w.Y = 1.0f; 678 w.Y = 1.0f;
703 return qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f); 679 return ScopedQcmsProfile(qcms_profile_create_rgb_with_gamma(w, xyz, 1.0f));
704 } 680 }
705 681
682 // static
706 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( 683 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform(
hubbe 2017/02/14 19:04:51 Probably move all the code in this function into C
ccameron 2017/02/15 00:11:31 Good point -- done.
707 const ColorSpace& from, 684 const ColorSpace& from,
708 const ColorSpace& to, 685 const ColorSpace& to,
709 Intent intent) { 686 Intent intent) {
710 TransformBuilder builder; 687 ColorTransformStepList builder;
711 if (intent == Intent::TEST_NO_OPT) { 688
712 builder.disable_optimizations(); 689 ScopedQcmsProfile from_profile = GetQCMSProfileIfAvailable(from);
690 ScopedQcmsProfile to_profile = GetQCMSProfileIfAvailable(to);
691 bool has_from_profile = !!from_profile;
692 bool has_to_profile = !!to_profile;
693
694 if (from_profile) {
695 builder.push_back(base::MakeUnique<QCMSColorTransform>(
696 std::move(from_profile), GetXYZD50Profile()));
713 } 697 }
714 698
715 qcms_profile* from_profile = GetQCMSProfileIfAvailable(from); 699 ColorTransformInternal::Append(
716 qcms_profile* to_profile = GetQCMSProfileIfAvailable(to); 700 has_from_profile ? ColorSpace::CreateXYZD50() : from,
701 has_to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder);
717 702
718 if (from_profile && to_profile) {
719 return std::unique_ptr<ColorTransform>(
720 new QCMSColorTransform(from_profile, to_profile));
721 }
722 if (from_profile) {
723 builder.Append(std::unique_ptr<ColorTransformInternal>(
724 new QCMSColorTransform(from_profile, GetXYZD50Profile())));
725 }
726 ColorSpaceToColorSpaceTransform::ColorSpaceToColorSpace(
727 from_profile ? ColorSpace::CreateXYZD50() : from,
728 to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder);
729 if (to_profile) { 703 if (to_profile) {
730 builder.Append(std::unique_ptr<ColorTransformInternal>( 704 builder.push_back(base::MakeUnique<QCMSColorTransform>(
731 new QCMSColorTransform(GetXYZD50Profile(), to_profile))); 705 GetXYZD50Profile(), std::move(to_profile)));
732 } 706 }
733 707
734 return builder.GetTransform(); 708 if (intent != Intent::TEST_NO_OPT)
709 ColorTransformInternal::Simplify(&builder);
710
711 return std::unique_ptr<ColorTransform>(
712 new ColorTransformInternal(std::move(builder)));
735 } 713 }
736 714
737 // static 715 // static
738 float ColorTransform::ToLinearForTesting(ColorSpace::TransferID transfer, 716 void ColorTransformInternal::Simplify(ColorTransformStepList* steps) {
739 float v) { 717 for (auto iter = steps->begin(); iter != steps->end();) {
740 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, 718 std::unique_ptr<ColorTransformStep>& this_step = *iter;
741 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
742 SkColorSpaceTransferFn to_linear_fn;
743 bool to_linear_fn_valid = space.GetTransferFunction(&to_linear_fn);
744 ColorTransformToLinear to_linear_transform(transfer, to_linear_fn,
745 to_linear_fn_valid);
746 TriStim color(v, v, v);
747 to_linear_transform.transform(&color, 1);
748 return color.x();
749 }
750 719
751 // static 720 // Try to Join |next_step| into |this_step|. If successful, re-visit the
752 float ColorTransform::FromLinearForTesting(ColorSpace::TransferID transfer, 721 // step before |this_step|.
753 float v) { 722 auto iter_next = iter;
754 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, 723 iter_next++;
755 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); 724 if (iter_next != steps->end()) {
756 SkColorSpaceTransferFn from_linear_fn; 725 std::unique_ptr<ColorTransformStep>& next_step = *iter_next;
757 bool from_linear_fn_valid = space.GetInverseTransferFunction(&from_linear_fn); 726 if (this_step->Join(next_step.get())) {
727 steps->erase(iter_next);
728 if (iter != steps->begin())
729 --iter;
730 continue;
731 }
732 }
758 733
759 ColorTransformFromLinear from_linear_transform(transfer, from_linear_fn, 734 // If |this_step| step is a no-op, remove it, and re-visit the step before
760 from_linear_fn_valid); 735 // |this_step|.
761 TriStim color(v, v, v); 736 if (this_step->IsNull()) {
762 from_linear_transform.transform(&color, 1); 737 iter = steps->erase(iter);
763 return color.x(); 738 if (iter != steps->begin())
739 --iter;
740 continue;
741 }
742
743 ++iter;
744 }
764 } 745 }
765 746
766 } // namespace gfx 747 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/color_transform.h ('k') | ui/gfx/color_transform_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698