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 "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
11 #include "ui/gfx/color_space.h" | 11 #include "ui/gfx/color_space.h" |
12 #include "ui/gfx/icc_profile.h" | 12 #include "ui/gfx/icc_profile.h" |
13 #include "ui/gfx/transform.h" | 13 #include "ui/gfx/transform.h" |
14 #include "third_party/qcms/src/qcms.h" | 14 #include "third_party/qcms/src/qcms.h" |
15 | 15 |
16 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H | 16 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
17 extern "C" { | 17 extern "C" { |
18 #include "third_party/qcms/src/chain.h" | 18 #include "third_party/qcms/src/chain.h" |
19 }; | 19 }; |
20 #endif | 20 #endif |
21 | 21 |
22 namespace gfx { | 22 namespace gfx { |
23 | 23 |
| 24 float EvalSkTransferFn(const SkColorSpaceTransferFn& fn, float x) { |
| 25 if (x < 0) |
| 26 return 0; |
| 27 if (x < fn.fD) |
| 28 return fn.fC * x + fn.fF; |
| 29 return powf(fn.fA * x + fn.fB, fn.fG) + fn.fE; |
| 30 } |
| 31 |
24 Transform Invert(const Transform& t) { | 32 Transform Invert(const Transform& t) { |
25 Transform ret = t; | 33 Transform ret = t; |
26 if (!t.GetInverse(&ret)) { | 34 if (!t.GetInverse(&ret)) { |
27 LOG(ERROR) << "Inverse should alsways be possible."; | 35 LOG(ERROR) << "Inverse should alsways be possible."; |
28 } | 36 } |
29 return ret; | 37 return ret; |
30 } | 38 } |
31 | 39 |
32 GFX_EXPORT Transform GetPrimaryMatrix(ColorSpace::PrimaryID id) { | 40 float FromLinear(ColorSpace::TransferID id, float v) { |
33 SkColorSpacePrimaries primaries = {0}; | |
34 switch (id) { | |
35 case ColorSpace::PrimaryID::CUSTOM: | |
36 NOTREACHED(); | |
37 | |
38 case ColorSpace::PrimaryID::RESERVED0: | |
39 case ColorSpace::PrimaryID::RESERVED: | |
40 case ColorSpace::PrimaryID::UNSPECIFIED: | |
41 case ColorSpace::PrimaryID::UNKNOWN: | |
42 case ColorSpace::PrimaryID::BT709: | |
43 // BT709 is our default case. Put it after the switch just | |
44 // in case we somehow get an id which is not listed in the switch. | |
45 // (We don't want to use "default", because we want the compiler | |
46 // to tell us if we forgot some enum values.) | |
47 primaries.fRX = 0.640f; | |
48 primaries.fRY = 0.330f; | |
49 primaries.fGX = 0.300f; | |
50 primaries.fGY = 0.600f; | |
51 primaries.fBX = 0.150f; | |
52 primaries.fBY = 0.060f; | |
53 primaries.fWX = 0.3127f; | |
54 primaries.fWY = 0.3290f; | |
55 break; | |
56 | |
57 case ColorSpace::PrimaryID::BT470M: | |
58 primaries.fRX = 0.67f; | |
59 primaries.fRY = 0.33f; | |
60 primaries.fGX = 0.21f; | |
61 primaries.fGY = 0.71f; | |
62 primaries.fBX = 0.14f; | |
63 primaries.fBY = 0.08f; | |
64 primaries.fWX = 0.31f; | |
65 primaries.fWY = 0.316f; | |
66 break; | |
67 | |
68 case ColorSpace::PrimaryID::BT470BG: | |
69 primaries.fRX = 0.64f; | |
70 primaries.fRY = 0.33f; | |
71 primaries.fGX = 0.29f; | |
72 primaries.fGY = 0.60f; | |
73 primaries.fBX = 0.15f; | |
74 primaries.fBY = 0.06f; | |
75 primaries.fWX = 0.3127f; | |
76 primaries.fWY = 0.3290f; | |
77 break; | |
78 | |
79 case ColorSpace::PrimaryID::SMPTE170M: | |
80 case ColorSpace::PrimaryID::SMPTE240M: | |
81 primaries.fRX = 0.630f; | |
82 primaries.fRY = 0.340f; | |
83 primaries.fGX = 0.310f; | |
84 primaries.fGY = 0.595f; | |
85 primaries.fBX = 0.155f; | |
86 primaries.fBY = 0.070f; | |
87 primaries.fWX = 0.3127f; | |
88 primaries.fWY = 0.3290f; | |
89 break; | |
90 | |
91 case ColorSpace::PrimaryID::FILM: | |
92 primaries.fRX = 0.681f; | |
93 primaries.fRY = 0.319f; | |
94 primaries.fGX = 0.243f; | |
95 primaries.fGY = 0.692f; | |
96 primaries.fBX = 0.145f; | |
97 primaries.fBY = 0.049f; | |
98 primaries.fWX = 0.310f; | |
99 primaries.fWY = 0.136f; | |
100 break; | |
101 | |
102 case ColorSpace::PrimaryID::BT2020: | |
103 primaries.fRX = 0.708f; | |
104 primaries.fRY = 0.292f; | |
105 primaries.fGX = 0.170f; | |
106 primaries.fGY = 0.797f; | |
107 primaries.fBX = 0.131f; | |
108 primaries.fBY = 0.046f; | |
109 primaries.fWX = 0.3127f; | |
110 primaries.fWY = 0.3290f; | |
111 break; | |
112 | |
113 case ColorSpace::PrimaryID::SMPTEST428_1: | |
114 primaries.fRX = 1.0f; | |
115 primaries.fRY = 0.0f; | |
116 primaries.fGX = 0.0f; | |
117 primaries.fGY = 1.0f; | |
118 primaries.fBX = 0.0f; | |
119 primaries.fBY = 0.0f; | |
120 primaries.fWX = 1.0f / 3.0f; | |
121 primaries.fWY = 1.0f / 3.0f; | |
122 break; | |
123 | |
124 case ColorSpace::PrimaryID::SMPTEST431_2: | |
125 primaries.fRX = 0.680f; | |
126 primaries.fRY = 0.320f; | |
127 primaries.fGX = 0.265f; | |
128 primaries.fGY = 0.690f; | |
129 primaries.fBX = 0.150f; | |
130 primaries.fBY = 0.060f; | |
131 primaries.fWX = 0.314f; | |
132 primaries.fWY = 0.351f; | |
133 break; | |
134 | |
135 case ColorSpace::PrimaryID::SMPTEST432_1: | |
136 primaries.fRX = 0.680f; | |
137 primaries.fRY = 0.320f; | |
138 primaries.fGX = 0.265f; | |
139 primaries.fGY = 0.690f; | |
140 primaries.fBX = 0.150f; | |
141 primaries.fBY = 0.060f; | |
142 primaries.fWX = 0.3127f; | |
143 primaries.fWY = 0.3290f; | |
144 break; | |
145 | |
146 case ColorSpace::PrimaryID::XYZ_D50: | |
147 primaries.fRX = 1.0f; | |
148 primaries.fRY = 0.0f; | |
149 primaries.fGX = 0.0f; | |
150 primaries.fGY = 1.0f; | |
151 primaries.fBX = 0.0f; | |
152 primaries.fBY = 0.0f; | |
153 primaries.fWX = 0.34567f; | |
154 primaries.fWY = 0.35850f; | |
155 break; | |
156 } | |
157 | |
158 SkMatrix44 matrix; | |
159 primaries.toXYZD50(&matrix); | |
160 return Transform(matrix); | |
161 } | |
162 | |
163 GFX_EXPORT float FromLinear(ColorSpace::TransferID id, float v) { | |
164 switch (id) { | 41 switch (id) { |
165 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: | 42 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
166 // Should already be handled. | 43 // Should already be handled. |
167 NOTREACHED(); | |
168 case ColorSpace::TransferID::CUSTOM: | |
169 // TODO(hubbe): Actually implement custom transfer functions. | |
170 case ColorSpace::TransferID::RESERVED0: | |
171 case ColorSpace::TransferID::RESERVED: | |
172 case ColorSpace::TransferID::UNSPECIFIED: | |
173 case ColorSpace::TransferID::UNKNOWN: | |
174 // All unknown values default to BT709 | |
175 | |
176 case ColorSpace::TransferID::BT709: | |
177 case ColorSpace::TransferID::SMPTE170M: | |
178 case ColorSpace::TransferID::BT2020_10: | |
179 case ColorSpace::TransferID::BT2020_12: | |
180 // BT709 is our "default" cause, so put the code after the switch | |
181 // to avoid "control reaches end of non-void function" errors. | |
182 break; | 44 break; |
183 | 45 |
184 case ColorSpace::TransferID::GAMMA22: | |
185 v = fmax(0.0f, v); | |
186 return powf(v, 1.0f / 2.2f); | |
187 | |
188 case ColorSpace::TransferID::GAMMA28: | |
189 v = fmax(0.0f, v); | |
190 return powf(v, 1.0f / 2.8f); | |
191 | |
192 case ColorSpace::TransferID::SMPTE240M: { | |
193 v = fmax(0.0f, v); | |
194 float a = 1.11157219592173128753f; | |
195 float b = 0.02282158552944503135f; | |
196 if (v <= b) { | |
197 return 4.0f * v; | |
198 } else { | |
199 return a * powf(v, 0.45f) - (a - 1.0f); | |
200 } | |
201 } | |
202 | |
203 case ColorSpace::TransferID::LINEAR: | |
204 return v; | |
205 | |
206 case ColorSpace::TransferID::LOG: | 46 case ColorSpace::TransferID::LOG: |
207 if (v < 0.01f) | 47 if (v < 0.01f) |
208 return 0.0f; | 48 return 0.0f; |
209 return 1.0f + log(v) / log(10.0f) / 2.0f; | 49 return 1.0f + log(v) / log(10.0f) / 2.0f; |
210 | 50 |
211 case ColorSpace::TransferID::LOG_SQRT: | 51 case ColorSpace::TransferID::LOG_SQRT: |
212 if (v < sqrt(10.0f) / 1000.0f) | 52 if (v < sqrt(10.0f) / 1000.0f) |
213 return 0.0f; | 53 return 0.0f; |
214 return 1.0f + log(v) / log(10.0f) / 2.5f; | 54 return 1.0f + log(v) / log(10.0f) / 2.5f; |
215 | 55 |
(...skipping 15 matching lines...) Expand all Loading... |
231 float l = 0.0045f; | 71 float l = 0.0045f; |
232 if (v < -l) { | 72 if (v < -l) { |
233 return -(a * powf(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f; | 73 return -(a * powf(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f; |
234 } else if (v <= b) { | 74 } else if (v <= b) { |
235 return 4.5f * v; | 75 return 4.5f * v; |
236 } else { | 76 } else { |
237 return a * powf(v, 0.45f) - (a - 1.0f); | 77 return a * powf(v, 0.45f) - (a - 1.0f); |
238 } | 78 } |
239 } | 79 } |
240 | 80 |
241 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
242 v = fmax(0.0f, v); | |
243 float a = 1.055f; | |
244 float b = 0.0031308f; | |
245 if (v < b) { | |
246 return 12.92f * v; | |
247 } else { | |
248 return a * powf(v, 1.0f / 2.4f) - (a - 1.0f); | |
249 } | |
250 } | |
251 case ColorSpace::TransferID::SMPTEST2084: { | 81 case ColorSpace::TransferID::SMPTEST2084: { |
252 // Go from scRGB levels to 0-1. | 82 // Go from scRGB levels to 0-1. |
253 v *= 80.0f / 10000.0f; | 83 v *= 80.0f / 10000.0f; |
254 v = fmax(0.0f, v); | 84 v = fmax(0.0f, v); |
255 float m1 = (2610.0f / 4096.0f) / 4.0f; | 85 float m1 = (2610.0f / 4096.0f) / 4.0f; |
256 float m2 = (2523.0f / 4096.0f) * 128.0f; | 86 float m2 = (2523.0f / 4096.0f) * 128.0f; |
257 float c1 = 3424.0f / 4096.0f; | 87 float c1 = 3424.0f / 4096.0f; |
258 float c2 = (2413.0f / 4096.0f) * 32.0f; | 88 float c2 = (2413.0f / 4096.0f) * 32.0f; |
259 float c3 = (2392.0f / 4096.0f) * 32.0f; | 89 float c3 = (2392.0f / 4096.0f) * 32.0f; |
260 return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2); | 90 return powf((c1 + c2 * powf(v, m1)) / (1.0f + c3 * powf(v, m1)), m2); |
261 } | 91 } |
262 | 92 |
263 case ColorSpace::TransferID::SMPTEST428_1: | |
264 v = fmax(0.0f, v); | |
265 return powf(48.0f * v + 52.37f, 1.0f / 2.6f); | |
266 | |
267 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf | 93 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf |
268 case ColorSpace::TransferID::ARIB_STD_B67: { | 94 case ColorSpace::TransferID::ARIB_STD_B67: { |
269 const float a = 0.17883277f; | 95 const float a = 0.17883277f; |
270 const float b = 0.28466892f; | 96 const float b = 0.28466892f; |
271 const float c = 0.55991073f; | 97 const float c = 0.55991073f; |
272 v = fmax(0.0f, v); | 98 v = fmax(0.0f, v); |
273 if (v <= 1) | 99 if (v <= 1) |
274 return 0.5f * sqrtf(v); | 100 return 0.5f * sqrtf(v); |
275 else | 101 else |
276 return a * log(v - b) + c; | 102 return a * log(v - b) + c; |
277 } | 103 } |
278 | 104 |
279 // Chrome-specific values below | 105 default: |
280 case ColorSpace::TransferID::GAMMA24: | 106 // Handled by SkColorSpaceTransferFn. |
281 v = fmax(0.0f, v); | 107 break; |
282 return powf(v, 1.0f / 2.4f); | |
283 } | 108 } |
284 | 109 NOTREACHED(); |
285 v = fmax(0.0f, v); | 110 return 0; |
286 float a = 1.099296826809442f; | |
287 float b = 0.018053968510807f; | |
288 if (v <= b) { | |
289 return 4.5f * v; | |
290 } else { | |
291 return a * powf(v, 0.45f) - (a - 1.0f); | |
292 } | |
293 } | 111 } |
294 | 112 |
295 GFX_EXPORT float ToLinear(ColorSpace::TransferID id, float v) { | 113 float ToLinear(ColorSpace::TransferID id, float v) { |
296 switch (id) { | 114 switch (id) { |
297 case ColorSpace::TransferID::CUSTOM: | |
298 // TODO(hubbe): Actually implement custom transfer functions. | |
299 case ColorSpace::TransferID::RESERVED0: | |
300 case ColorSpace::TransferID::RESERVED: | |
301 case ColorSpace::TransferID::UNSPECIFIED: | |
302 case ColorSpace::TransferID::UNKNOWN: | |
303 // All unknown values default to BT709 | |
304 | |
305 case ColorSpace::TransferID::BT709: | |
306 case ColorSpace::TransferID::SMPTE170M: | |
307 case ColorSpace::TransferID::BT2020_10: | |
308 case ColorSpace::TransferID::BT2020_12: | |
309 // BT709 is our "default" cause, so put the code after the switch | |
310 // to avoid "control reaches end of non-void function" errors. | |
311 break; | |
312 | |
313 case ColorSpace::TransferID::GAMMA22: | |
314 v = fmax(0.0f, v); | |
315 return powf(v, 2.2f); | |
316 | |
317 case ColorSpace::TransferID::GAMMA28: | |
318 v = fmax(0.0f, v); | |
319 return powf(v, 2.8f); | |
320 | |
321 case ColorSpace::TransferID::SMPTE240M: { | |
322 v = fmax(0.0f, v); | |
323 float a = 1.11157219592173128753f; | |
324 float b = 0.02282158552944503135f; | |
325 if (v <= FromLinear(ColorSpace::TransferID::SMPTE240M, b)) { | |
326 return v / 4.0f; | |
327 } else { | |
328 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | |
329 } | |
330 } | |
331 | |
332 case ColorSpace::TransferID::LINEAR: | |
333 return v; | |
334 | |
335 case ColorSpace::TransferID::LOG: | 115 case ColorSpace::TransferID::LOG: |
336 if (v < 0.0f) | 116 if (v < 0.0f) |
337 return 0.0f; | 117 return 0.0f; |
338 return powf(10.0f, (v - 1.0f) * 2.0f); | 118 return powf(10.0f, (v - 1.0f) * 2.0f); |
339 | 119 |
340 case ColorSpace::TransferID::LOG_SQRT: | 120 case ColorSpace::TransferID::LOG_SQRT: |
341 if (v < 0.0f) | 121 if (v < 0.0f) |
342 return 0.0f; | 122 return 0.0f; |
343 return powf(10.0f, (v - 1.0f) * 2.5f); | 123 return powf(10.0f, (v - 1.0f) * 2.5f); |
344 | 124 |
(...skipping 15 matching lines...) Expand all Loading... |
360 float l = 0.0045f; | 140 float l = 0.0045f; |
361 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { | 141 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { |
362 return -powf((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f; | 142 return -powf((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f; |
363 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { | 143 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { |
364 return v / 4.5f; | 144 return v / 4.5f; |
365 } else { | 145 } else { |
366 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | 146 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); |
367 } | 147 } |
368 } | 148 } |
369 | 149 |
370 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
371 v = fmax(0.0f, v); | |
372 float a = 1.055f; | |
373 float b = 0.0031308f; | |
374 if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_1, b)) { | |
375 return v / 12.92f; | |
376 } else { | |
377 return powf((v + a - 1.0f) / a, 2.4f); | |
378 } | |
379 } | |
380 | |
381 case ColorSpace::TransferID::SMPTEST2084: { | 150 case ColorSpace::TransferID::SMPTEST2084: { |
382 v = fmax(0.0f, v); | 151 v = fmax(0.0f, v); |
383 float m1 = (2610.0f / 4096.0f) / 4.0f; | 152 float m1 = (2610.0f / 4096.0f) / 4.0f; |
384 float m2 = (2523.0f / 4096.0f) * 128.0f; | 153 float m2 = (2523.0f / 4096.0f) * 128.0f; |
385 float c1 = 3424.0f / 4096.0f; | 154 float c1 = 3424.0f / 4096.0f; |
386 float c2 = (2413.0f / 4096.0f) * 32.0f; | 155 float c2 = (2413.0f / 4096.0f) * 32.0f; |
387 float c3 = (2392.0f / 4096.0f) * 32.0f; | 156 float c3 = (2392.0f / 4096.0f) * 32.0f; |
388 v = powf( | 157 v = powf( |
389 fmax(powf(v, 1.0f / m2) - c1, 0) / (c2 - c3 * powf(v, 1.0f / m2)), | 158 fmax(powf(v, 1.0f / m2) - c1, 0) / (c2 - c3 * powf(v, 1.0f / m2)), |
390 1.0f / m1); | 159 1.0f / m1); |
391 // This matches the scRGB definition that 1.0 means 80 nits. | 160 // This matches the scRGB definition that 1.0 means 80 nits. |
392 // TODO(hubbe): It would be *nice* if 1.0 meant more than that, but | 161 // TODO(hubbe): It would be *nice* if 1.0 meant more than that, but |
393 // that might be difficult to do right now. | 162 // that might be difficult to do right now. |
394 v *= 10000.0f / 80.0f; | 163 v *= 10000.0f / 80.0f; |
395 return v; | 164 return v; |
396 } | 165 } |
397 | 166 |
398 case ColorSpace::TransferID::SMPTEST428_1: | |
399 return (powf(v, 2.6f) - 52.37f) / 48.0f; | |
400 | |
401 // Chrome-specific values below | |
402 case ColorSpace::TransferID::GAMMA24: | |
403 v = fmax(0.0f, v); | |
404 return powf(v, 2.4f); | |
405 | |
406 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: | 167 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
407 v = fmax(0.0f, v); | 168 v = fmax(0.0f, v); |
408 return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f); | 169 return fmin(2.3f * pow(v, 2.8f), v / 5.0f + 0.8f); |
409 | 170 |
410 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf | 171 // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf |
411 case ColorSpace::TransferID::ARIB_STD_B67: { | 172 case ColorSpace::TransferID::ARIB_STD_B67: { |
412 v = fmax(0.0f, v); | 173 v = fmax(0.0f, v); |
413 const float a = 0.17883277f; | 174 const float a = 0.17883277f; |
414 const float b = 0.28466892f; | 175 const float b = 0.28466892f; |
415 const float c = 0.55991073f; | 176 const float c = 0.55991073f; |
416 float v_ = 0.0f; | 177 float v_ = 0.0f; |
417 if (v <= 0.5f) { | 178 if (v <= 0.5f) { |
418 v_ = (v * 2.0f) * (v * 2.0f); | 179 v_ = (v * 2.0f) * (v * 2.0f); |
419 } else { | 180 } else { |
420 v_ = exp((v - c) / a) + b; | 181 v_ = exp((v - c) / a) + b; |
421 } | 182 } |
422 return v_; | 183 return v_; |
423 } | 184 } |
| 185 |
| 186 default: |
| 187 // Handled by SkColorSpaceTransferFn. |
| 188 break; |
424 } | 189 } |
425 | 190 NOTREACHED(); |
426 v = fmax(0.0f, v); | 191 return 0; |
427 float a = 1.099296826809442f; | |
428 float b = 0.018053968510807f; | |
429 if (v < FromLinear(ColorSpace::TransferID::BT709, b)) { | |
430 return v / 4.5f; | |
431 } else { | |
432 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | |
433 } | |
434 } | 192 } |
435 | 193 |
436 GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { | 194 GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { |
437 // Default values for BT709; | 195 // Default values for BT709; |
438 float Kr = 0.2126f; | 196 float Kr = 0.2126f; |
439 float Kb = 0.0722f; | 197 float Kb = 0.0722f; |
440 switch (id) { | 198 switch (id) { |
441 case ColorSpace::MatrixID::RGB: | 199 case ColorSpace::MatrixID::RGB: |
442 return Transform(); | 200 return Transform(); |
443 | 201 |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
610 for (size_t i = 0; i < num; i++) | 368 for (size_t i = 0; i < num; i++) |
611 matrix_.TransformPoint(colors + i); | 369 matrix_.TransformPoint(colors + i); |
612 } | 370 } |
613 | 371 |
614 private: | 372 private: |
615 Transform matrix_; | 373 Transform matrix_; |
616 }; | 374 }; |
617 | 375 |
618 class ColorTransformFromLinear : public ColorTransformInternal { | 376 class ColorTransformFromLinear : public ColorTransformInternal { |
619 public: | 377 public: |
620 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer) | 378 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer, |
621 : transfer_(transfer) {} | 379 const SkColorSpaceTransferFn& fn, |
| 380 bool fn_valid) |
| 381 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {} |
622 bool Prepend(ColorTransformInternal* prev) override { | 382 bool Prepend(ColorTransformInternal* prev) override { |
623 return prev->Join(*this); | 383 return prev->Join(*this); |
624 } | 384 } |
625 | 385 |
626 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } | 386 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } |
627 | 387 |
628 void transform(ColorTransform::TriStim* colors, size_t num) override { | 388 void transform(ColorTransform::TriStim* colors, size_t num) override { |
629 for (size_t i = 0; i < num; i++) { | 389 if (fn_valid_) { |
630 colors[i].set_x(FromLinear(transfer_, colors[i].x())); | 390 for (size_t i = 0; i < num; i++) { |
631 colors[i].set_y(FromLinear(transfer_, colors[i].y())); | 391 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); |
632 colors[i].set_z(FromLinear(transfer_, colors[i].z())); | 392 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); |
| 393 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); |
| 394 } |
| 395 } else { |
| 396 for (size_t i = 0; i < num; i++) { |
| 397 colors[i].set_x(FromLinear(transfer_, colors[i].x())); |
| 398 colors[i].set_y(FromLinear(transfer_, colors[i].y())); |
| 399 colors[i].set_z(FromLinear(transfer_, colors[i].z())); |
| 400 } |
633 } | 401 } |
634 } | 402 } |
635 | 403 |
636 private: | 404 private: |
637 friend class ColorTransformToLinear; | 405 friend class ColorTransformToLinear; |
638 ColorSpace::TransferID transfer_; | 406 ColorSpace::TransferID transfer_; |
| 407 SkColorSpaceTransferFn fn_; |
| 408 bool fn_valid_ = false; |
639 }; | 409 }; |
640 | 410 |
641 class ColorTransformToLinear : public ColorTransformInternal { | 411 class ColorTransformToLinear : public ColorTransformInternal { |
642 public: | 412 public: |
643 explicit ColorTransformToLinear(ColorSpace::TransferID transfer) | 413 explicit ColorTransformToLinear(ColorSpace::TransferID transfer, |
644 : transfer_(transfer) {} | 414 const SkColorSpaceTransferFn& fn, |
| 415 bool fn_valid) |
| 416 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) {} |
645 | 417 |
646 bool Prepend(ColorTransformInternal* prev) override { | 418 bool Prepend(ColorTransformInternal* prev) override { |
647 return prev->Join(*this); | 419 return prev->Join(*this); |
648 } | 420 } |
649 | 421 |
650 static bool IsGamma22(ColorSpace::TransferID transfer) { | 422 static bool IsGamma22(ColorSpace::TransferID transfer) { |
651 switch (transfer) { | 423 switch (transfer) { |
652 // We don't need to check BT709 here because it's been translated into | 424 // We don't need to check BT709 here because it's been translated into |
653 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below. | 425 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below. |
654 case ColorSpace::TransferID::GAMMA22: | 426 case ColorSpace::TransferID::GAMMA22: |
(...skipping 15 matching lines...) Expand all Loading... |
670 } | 442 } |
671 | 443 |
672 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } | 444 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } |
673 | 445 |
674 // Assumes BT2020 primaries. | 446 // Assumes BT2020 primaries. |
675 static float Luma(const ColorTransform::TriStim& c) { | 447 static float Luma(const ColorTransform::TriStim& c) { |
676 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f; | 448 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f; |
677 } | 449 } |
678 | 450 |
679 void transform(ColorTransform::TriStim* colors, size_t num) override { | 451 void transform(ColorTransform::TriStim* colors, size_t num) override { |
680 if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { | 452 if (fn_valid_) { |
| 453 for (size_t i = 0; i < num; i++) { |
| 454 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); |
| 455 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); |
| 456 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); |
| 457 } |
| 458 } else if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { |
681 for (size_t i = 0; i < num; i++) { | 459 for (size_t i = 0; i < num; i++) { |
682 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()), | 460 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()), |
683 ToLinear(transfer_, colors[i].y()), | 461 ToLinear(transfer_, colors[i].y()), |
684 ToLinear(transfer_, colors[i].z())); | 462 ToLinear(transfer_, colors[i].z())); |
685 if (Luma(ret) > 0.0) { | 463 if (Luma(ret) > 0.0) { |
686 ColorTransform::TriStim smpte2084( | 464 ColorTransform::TriStim smpte2084( |
687 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].x()), | 465 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].x()), |
688 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].y()), | 466 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].y()), |
689 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].z())); | 467 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].z())); |
690 smpte2084.Scale(Luma(ret) / Luma(smpte2084)); | 468 smpte2084.Scale(Luma(ret) / Luma(smpte2084)); |
691 ret = smpte2084; | 469 ret = smpte2084; |
692 } | 470 } |
693 colors[i] = ret; | 471 colors[i] = ret; |
694 } | 472 } |
695 } else { | 473 } else { |
696 for (size_t i = 0; i < num; i++) { | 474 for (size_t i = 0; i < num; i++) { |
697 colors[i].set_x(ToLinear(transfer_, colors[i].x())); | 475 colors[i].set_x(ToLinear(transfer_, colors[i].x())); |
698 colors[i].set_y(ToLinear(transfer_, colors[i].y())); | 476 colors[i].set_y(ToLinear(transfer_, colors[i].y())); |
699 colors[i].set_z(ToLinear(transfer_, colors[i].z())); | 477 colors[i].set_z(ToLinear(transfer_, colors[i].z())); |
700 } | 478 } |
701 } | 479 } |
702 } | 480 } |
703 | 481 |
704 private: | 482 private: |
705 ColorSpace::TransferID transfer_; | 483 ColorSpace::TransferID transfer_; |
| 484 SkColorSpaceTransferFn fn_; |
| 485 bool fn_valid_ = false; |
706 }; | 486 }; |
707 | 487 |
708 // BT2020 Constant Luminance is different than most other | 488 // BT2020 Constant Luminance is different than most other |
709 // ways to encode RGB values as YUV. The basic idea is that | 489 // ways to encode RGB values as YUV. The basic idea is that |
710 // transfer functions are applied on the Y value instead of | 490 // transfer functions are applied on the Y value instead of |
711 // on the RGB values. However, running the transfer function | 491 // on the RGB values. However, running the transfer function |
712 // on the U and V values doesn't make any sense since they | 492 // on the U and V values doesn't make any sense since they |
713 // are centered at 0.5. To work around this, the transfer function | 493 // are centered at 0.5. To work around this, the transfer function |
714 // is applied to the Y, R and B values, and then the U and V | 494 // is applied to the Y, R and B values, and then the U and V |
715 // values are calculated from that. | 495 // values are calculated from that. |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
851 void disable_optimizations() { disable_optimizations_ = true; } | 631 void disable_optimizations() { disable_optimizations_ = true; } |
852 | 632 |
853 private: | 633 private: |
854 bool disable_optimizations_ = false; | 634 bool disable_optimizations_ = false; |
855 std::vector<std::unique_ptr<ColorTransformInternal>> transforms_; | 635 std::vector<std::unique_ptr<ColorTransformInternal>> transforms_; |
856 }; | 636 }; |
857 | 637 |
858 class ColorSpaceToColorSpaceTransform { | 638 class ColorSpaceToColorSpaceTransform { |
859 public: | 639 public: |
860 static Transform GetPrimaryTransform(const ColorSpace& c) { | 640 static Transform GetPrimaryTransform(const ColorSpace& c) { |
861 if (c.primaries_ == ColorSpace::PrimaryID::CUSTOM) { | 641 SkMatrix44 sk_matrix; |
862 return Transform(c.custom_primary_matrix_[0], c.custom_primary_matrix_[1], | 642 c.GetPrimaryMatrix(&sk_matrix); |
863 c.custom_primary_matrix_[2], c.custom_primary_matrix_[3], | 643 return Transform(sk_matrix); |
864 c.custom_primary_matrix_[4], c.custom_primary_matrix_[5], | |
865 c.custom_primary_matrix_[6], c.custom_primary_matrix_[7], | |
866 c.custom_primary_matrix_[8], c.custom_primary_matrix_[9], | |
867 c.custom_primary_matrix_[10], | |
868 c.custom_primary_matrix_[11], 0.0f, 0.0f, 0.0f, 1.0f); | |
869 } else { | |
870 return GetPrimaryMatrix(c.primaries_); | |
871 } | |
872 } | 644 } |
873 | 645 |
874 static void ColorSpaceToColorSpace(ColorSpace from, | 646 static void ColorSpaceToColorSpace(ColorSpace from, |
875 ColorSpace to, | 647 ColorSpace to, |
876 ColorTransform::Intent intent, | 648 ColorTransform::Intent intent, |
877 TransformBuilder* builder) { | 649 TransformBuilder* builder) { |
878 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { | 650 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { |
879 switch (from.transfer_) { | 651 switch (from.transfer_) { |
880 case ColorSpace::TransferID::UNSPECIFIED: | 652 case ColorSpace::TransferID::UNSPECIFIED: |
881 case ColorSpace::TransferID::BT709: | 653 case ColorSpace::TransferID::BT709: |
(...skipping 22 matching lines...) Expand all Loading... |
904 from.transfer_ = ColorSpace::TransferID::GAMMA24; | 676 from.transfer_ = ColorSpace::TransferID::GAMMA24; |
905 } | 677 } |
906 break; | 678 break; |
907 | 679 |
908 default: // Do nothing | 680 default: // Do nothing |
909 break; | 681 break; |
910 } | 682 } |
911 | 683 |
912 // TODO(hubbe): shrink gamuts here (never stretch gamuts) | 684 // TODO(hubbe): shrink gamuts here (never stretch gamuts) |
913 } | 685 } |
| 686 |
914 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 687 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
915 GetRangeAdjustMatrix(from.range_, from.matrix_))); | 688 GetRangeAdjustMatrix(from.range_, from.matrix_))); |
| 689 |
916 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 690 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
917 Invert(GetTransferMatrix(from.matrix_)))); | 691 Invert(GetTransferMatrix(from.matrix_)))); |
918 builder->Append(base::MakeUnique<ColorTransformToLinear>(from.transfer_)); | 692 |
| 693 SkColorSpaceTransferFn to_linear_fn; |
| 694 bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn); |
| 695 builder->Append(base::MakeUnique<ColorTransformToLinear>( |
| 696 from.transfer_, to_linear_fn, to_linear_fn_valid)); |
| 697 |
919 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 698 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
920 // BT2020 CL is a special case. | 699 // BT2020 CL is a special case. |
921 builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>()); | 700 builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>()); |
922 } | 701 } |
923 builder->Append( | 702 builder->Append( |
924 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); | 703 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); |
925 | 704 |
926 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 705 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
927 Invert(GetPrimaryTransform(to)))); | 706 Invert(GetPrimaryTransform(to)))); |
928 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 707 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
929 // BT2020 CL is a special case. | 708 // BT2020 CL is a special case. |
930 builder->Append(base::MakeUnique<ColorTransformToBT2020CL>()); | 709 builder->Append(base::MakeUnique<ColorTransformToBT2020CL>()); |
931 } | 710 } |
932 | 711 |
933 builder->Append(base::MakeUnique<ColorTransformFromLinear>(to.transfer_)); | 712 SkColorSpaceTransferFn from_linear_fn; |
| 713 bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn); |
| 714 builder->Append(base::MakeUnique<ColorTransformFromLinear>( |
| 715 to.transfer_, from_linear_fn, from_linear_fn_valid)); |
| 716 |
934 builder->Append( | 717 builder->Append( |
935 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to.matrix_))); | 718 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to.matrix_))); |
| 719 |
936 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 720 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
937 Invert(GetRangeAdjustMatrix(to.range_, to.matrix_)))); | 721 Invert(GetRangeAdjustMatrix(to.range_, to.matrix_)))); |
938 } | 722 } |
939 }; | 723 }; |
940 | 724 |
941 class QCMSColorTransform : public ColorTransformInternal { | 725 class QCMSColorTransform : public ColorTransformInternal { |
942 public: | 726 public: |
943 // Takes ownership of the profiles | 727 // Takes ownership of the profiles |
944 QCMSColorTransform(qcms_profile* from, qcms_profile* to) | 728 QCMSColorTransform(qcms_profile* from, qcms_profile* to) |
945 : from_(from), to_(to) {} | 729 : from_(from), to_(to) {} |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1021 from_profile ? ColorSpace::CreateXYZD50() : from, | 805 from_profile ? ColorSpace::CreateXYZD50() : from, |
1022 to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder); | 806 to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder); |
1023 if (to_profile) { | 807 if (to_profile) { |
1024 builder.Append(std::unique_ptr<ColorTransformInternal>( | 808 builder.Append(std::unique_ptr<ColorTransformInternal>( |
1025 new QCMSColorTransform(GetXYZD50Profile(), to_profile))); | 809 new QCMSColorTransform(GetXYZD50Profile(), to_profile))); |
1026 } | 810 } |
1027 | 811 |
1028 return builder.GetTransform(); | 812 return builder.GetTransform(); |
1029 } | 813 } |
1030 | 814 |
| 815 // static |
| 816 float ColorTransform::ToLinearForTesting(ColorSpace::TransferID transfer, |
| 817 float v) { |
| 818 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, |
| 819 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
| 820 SkColorSpaceTransferFn to_linear_fn; |
| 821 bool to_linear_fn_valid = space.GetTransferFunction(&to_linear_fn); |
| 822 ColorTransformToLinear to_linear_transform(transfer, to_linear_fn, |
| 823 to_linear_fn_valid); |
| 824 TriStim color(v, v, v); |
| 825 to_linear_transform.transform(&color, 1); |
| 826 return color.x(); |
| 827 } |
| 828 |
| 829 // static |
| 830 float ColorTransform::FromLinearForTesting(ColorSpace::TransferID transfer, |
| 831 float v) { |
| 832 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, |
| 833 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
| 834 SkColorSpaceTransferFn from_linear_fn; |
| 835 bool from_linear_fn_valid = space.GetInverseTransferFunction(&from_linear_fn); |
| 836 |
| 837 ColorTransformFromLinear from_linear_transform(transfer, from_linear_fn, |
| 838 from_linear_fn_valid); |
| 839 TriStim color(v, v, v); |
| 840 from_linear_transform.transform(&color, 1); |
| 841 return color.x(); |
| 842 } |
| 843 |
1031 } // namespace gfx | 844 } // namespace gfx |
OLD | NEW |