Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/gfx/color_transform.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "ui/gfx/color_space.h" | |
| 11 #include "ui/gfx/transform.h" | |
| 12 | |
| 13 namespace gfx { | |
| 14 | |
| 15 Transform Invert(const Transform& t) { | |
| 16 Transform ret = t; | |
| 17 if (!t.GetInverse(&ret)) { | |
| 18 LOG(ERROR) << "Inverse should alsways be possible."; | |
| 19 } | |
| 20 return ret; | |
| 21 } | |
| 22 | |
| 23 ColorTransform::TriStim Map(const Transform& t, ColorTransform::TriStim color) { | |
| 24 t.TransformPoint(&color); | |
| 25 return color; | |
| 26 } | |
| 27 | |
| 28 // XY color coordinate | |
| 29 typedef std::pair<float, float> XY; | |
|
ccameron
2016/08/02 02:47:38
use gfx::PointF instead of std::pair<float,float>
hubbe
2016/08/02 08:10:57
Gone.
| |
| 30 | |
| 31 std::vector<XY> GetPrimaries(ColorSpace::PrimaryID id) { | |
|
ccameron
2016/08/02 02:47:38
Don't return a std::vector here -- pass the return
hubbe
2016/08/02 08:10:57
Calling the arguments red/green/blue is somewhat m
ccameron
2016/08/02 17:12:57
Yeah, I was wondering about that ... in particular
| |
| 32 std::vector<XY> ret(4); | |
| 33 switch (id) { | |
| 34 default: | |
| 35 // If we don't know, assume BT709 | |
| 36 | |
| 37 case ColorSpace::PrimaryID::BT709: | |
| 38 // Red | |
| 39 ret[0].first = 0.640; | |
| 40 ret[0].second = 0.330; | |
| 41 // Green | |
| 42 ret[1].first = 0.300; | |
| 43 ret[1].second = 0.600; | |
| 44 // Blue | |
| 45 ret[2].first = 0.150; | |
| 46 ret[2].second = 0.060; | |
| 47 // Whitepoint (D65) | |
| 48 ret[3].first = 0.3127; | |
| 49 ret[3].second = 0.3290; | |
| 50 break; | |
| 51 | |
| 52 case ColorSpace::PrimaryID::BT470M: | |
| 53 // Red | |
| 54 ret[0].first = 0.67; | |
| 55 ret[0].second = 0.33; | |
| 56 // Green | |
| 57 ret[1].first = 0.21; | |
| 58 ret[1].second = 0.71; | |
| 59 // Blue | |
| 60 ret[2].first = 0.14; | |
| 61 ret[2].second = 0.08; | |
| 62 // Whitepoint | |
| 63 ret[3].first = 0.31; | |
| 64 ret[3].second = 0.316; | |
| 65 break; | |
| 66 | |
| 67 case ColorSpace::PrimaryID::BT470BG: | |
| 68 // Red | |
| 69 ret[0].first = 0.64; | |
| 70 ret[0].second = 0.33; | |
| 71 // Green | |
| 72 ret[1].first = 0.29; | |
| 73 ret[1].second = 0.60; | |
| 74 // Blue | |
| 75 ret[2].first = 0.15; | |
| 76 ret[2].second = 0.06; | |
| 77 // Whitepoint (D65) | |
| 78 ret[3].first = 0.3127; | |
| 79 ret[3].second = 0.3290; | |
| 80 break; | |
| 81 | |
| 82 case ColorSpace::PrimaryID::SMPTE170M: | |
| 83 case ColorSpace::PrimaryID::SMPTE240M: | |
| 84 // Red | |
| 85 ret[0].first = 0.630; | |
| 86 ret[0].second = 0.340; | |
| 87 // Green | |
| 88 ret[1].first = 0.310; | |
| 89 ret[1].second = 0.595; | |
| 90 // Blue | |
| 91 ret[2].first = 0.155; | |
| 92 ret[2].second = 0.070; | |
| 93 // Whitepoint (D65) | |
| 94 ret[3].first = 0.3127; | |
| 95 ret[3].second = 0.3290; | |
| 96 break; | |
| 97 | |
| 98 case ColorSpace::PrimaryID::FILM: | |
| 99 // Red | |
| 100 ret[0].first = 0.681; | |
| 101 ret[0].second = 0.319; | |
| 102 // Green | |
| 103 ret[1].first = 0.243; | |
| 104 ret[1].second = 0.692; | |
| 105 // Blue | |
| 106 ret[2].first = 0.145; | |
| 107 ret[2].second = 0.049; | |
| 108 // Whitepoint (C) | |
| 109 ret[3].first = 0.310; | |
| 110 ret[3].second = 0.136; | |
| 111 break; | |
| 112 | |
| 113 case ColorSpace::PrimaryID::BT2020: | |
| 114 // Red | |
| 115 ret[0].first = 0.708; | |
| 116 ret[0].second = 0.292; | |
| 117 // Green | |
| 118 ret[1].first = 0.170; | |
| 119 ret[1].second = 0.797; | |
| 120 // Blue | |
| 121 ret[2].first = 0.131; | |
| 122 ret[2].second = 0.046; | |
| 123 // Whitepoint (D65) | |
| 124 ret[3].first = 0.3127; | |
| 125 ret[3].second = 0.3290; | |
| 126 break; | |
| 127 | |
| 128 case ColorSpace::PrimaryID::SMPTEST428_1: | |
| 129 // X | |
| 130 ret[0].first = 1.0; | |
| 131 ret[0].second = 0.0; | |
| 132 // Y | |
| 133 ret[1].first = 0.0; | |
| 134 ret[1].second = 1.0; | |
| 135 // Z | |
| 136 ret[2].first = 0.0; | |
| 137 ret[2].second = 0.0; | |
| 138 // Whitepoint (E) | |
| 139 ret[3].first = 1.0f / 3.0f; | |
| 140 ret[3].second = 1.0f / 3.0f; | |
| 141 break; | |
| 142 | |
| 143 case ColorSpace::PrimaryID::SMPTEST431_2: | |
| 144 // Red | |
| 145 ret[0].first = 0.680; | |
| 146 ret[0].second = 0.320; | |
| 147 // Green | |
| 148 ret[1].first = 0.265; | |
| 149 ret[1].second = 0.690; | |
| 150 // Blue | |
| 151 ret[2].first = 0.150; | |
| 152 ret[2].second = 0.060; | |
| 153 // Whitepoint | |
| 154 ret[3].first = 0.314; | |
| 155 ret[3].second = 0.351; | |
| 156 break; | |
| 157 | |
| 158 case ColorSpace::PrimaryID::SMPTEST432_1: | |
| 159 // Red | |
| 160 ret[0].first = 0.680; | |
| 161 ret[0].second = 0.320; | |
| 162 // Green | |
| 163 ret[1].first = 0.265; | |
| 164 ret[1].second = 0.690; | |
| 165 // Blue | |
| 166 ret[2].first = 0.150; | |
| 167 ret[2].second = 0.060; | |
| 168 // Whitepoint (D65) | |
| 169 ret[3].first = 0.3127; | |
| 170 ret[3].second = 0.3290; | |
| 171 break; | |
| 172 | |
| 173 case ColorSpace::PrimaryID::XYZ_D50: | |
| 174 // X | |
| 175 ret[0].first = 1.0; | |
| 176 ret[0].second = 0.0; | |
| 177 // Y | |
| 178 ret[1].first = 0.0; | |
| 179 ret[1].second = 1.0; | |
| 180 // Z | |
| 181 ret[2].first = 0.0; | |
| 182 ret[2].second = 0.0; | |
| 183 // D50 | |
| 184 ret[3].first = 0.34567; | |
| 185 ret[3].second = 0.35850; | |
| 186 break; | |
| 187 } | |
| 188 return ret; | |
| 189 } | |
| 190 | |
| 191 ColorTransform::TriStim xy2xyz(const XY& xy) { | |
|
ccameron
2016/08/02 02:47:39
naming nit: rename to XYToTriStim
Function names
hubbe
2016/08/02 08:10:57
That doesn't specify what color space the tristim
| |
| 192 return ColorTransform::TriStim(xy.first, xy.second, | |
| 193 1.0f - xy.first - xy.second); | |
| 194 } | |
| 195 | |
| 196 GFX_EXPORT Transform GetPrimaryMatrix(ColorSpace::PrimaryID id) { | |
|
ccameron
2016/08/02 02:47:38
This can be made simpler by using the existing mat
hubbe
2016/08/02 08:10:57
I like Scale3d, but I think the order in your func
ccameron
2016/08/02 17:12:57
Makes sense.
| |
| 197 std::vector<XY> primaries = GetPrimaries(id); | |
| 198 ColorTransform::TriStim Rxyz = xy2xyz(primaries[0]); | |
| 199 ColorTransform::TriStim Gxyz = xy2xyz(primaries[1]); | |
| 200 ColorTransform::TriStim Bxyz = xy2xyz(primaries[2]); | |
| 201 ColorTransform::TriStim Wxyz = xy2xyz(primaries[3]); | |
|
ccameron
2016/08/02 17:12:57
Y was wondering why we didn't call them just "prim
| |
| 202 ColorTransform::TriStim WXYZ(Wxyz.x() / Wxyz.y(), 1.0f, Wxyz.z() / Wxyz.y()); | |
| 203 | |
| 204 Transform tmp(Rxyz.x(), Gxyz.x(), Bxyz.x(), 0.0f, Rxyz.y(), Gxyz.y(), | |
| 205 Bxyz.y(), 0.0f, Rxyz.z(), Gxyz.z(), Bxyz.z(), 0.0f, 0.0f, 0.0f, | |
| 206 0.0f, 1.0f); | |
| 207 | |
| 208 ColorTransform::TriStim conv = Map(Invert(tmp), WXYZ); | |
| 209 Transform ret( | |
| 210 conv.x() * tmp.matrix().get(0, 0), conv.y() * tmp.matrix().get(0, 1), | |
| 211 conv.z() * tmp.matrix().get(0, 2), 0.0f, | |
| 212 conv.x() * tmp.matrix().get(1, 0), conv.y() * tmp.matrix().get(1, 1), | |
| 213 conv.z() * tmp.matrix().get(1, 2), 0.0f, | |
| 214 conv.x() * tmp.matrix().get(2, 0), conv.y() * tmp.matrix().get(2, 1), | |
| 215 conv.z() * tmp.matrix().get(2, 2), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); | |
| 216 | |
| 217 // Chromatic adaptation. | |
| 218 Transform bradford(0.8951000f, 0.2664000f, -0.1614000f, 0.0f, -0.7502000f, | |
| 219 1.7135000f, 0.0367000f, 0.0f, 0.0389000f, -0.0685000f, | |
| 220 1.0296000f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); | |
| 221 | |
| 222 Transform bradford_inv = Invert(bradford); | |
| 223 | |
| 224 ColorTransform::TriStim D50(0.9642, 1.0000, 0.8249); | |
| 225 ColorTransform::TriStim source_response = Map(bradford, WXYZ); | |
| 226 ColorTransform::TriStim dest_response = Map(bradford, D50); | |
| 227 | |
| 228 Transform adapter(dest_response.x() / source_response.x(), 0.0f, 0.0f, 0.0f, | |
| 229 0.0f, dest_response.y() / source_response.y(), 0.0f, 0.0f, | |
| 230 0.0f, 0.0f, dest_response.z() / source_response.z(), 0.0f, | |
| 231 0.0f, 0.0f, 0.0f, 1.0f); | |
| 232 | |
| 233 return bradford * adapter * bradford_inv * ret; | |
| 234 } | |
| 235 | |
| 236 GFX_EXPORT float fromLinear(ColorSpace::TransferID id, float v) { | |
|
ccameron
2016/08/02 02:47:39
Nit: capitalization: FromLinear.
hubbe
2016/08/02 08:10:57
Done.
| |
| 237 switch (id) { | |
| 238 default: | |
| 239 case ColorSpace::TransferID::BT709: | |
| 240 case ColorSpace::TransferID::SMPTE170M: | |
| 241 case ColorSpace::TransferID::BT2020_10: | |
| 242 case ColorSpace::TransferID::BT2020_12: { | |
| 243 v = fmax(0.0f, v); | |
| 244 float a = 1.099296826809442f; | |
| 245 float b = 0.018053968510807; | |
| 246 if (v <= b) { | |
| 247 return 4.5f * v; | |
| 248 } else { | |
| 249 return a * pow(v, 0.45f) - (a - 1.0f); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 case ColorSpace::TransferID::GAMMA22: | |
| 254 v = fmax(0.0f, v); | |
| 255 return pow(v, 1.0f / 2.2f); | |
| 256 | |
| 257 case ColorSpace::TransferID::GAMMA28: | |
| 258 v = fmax(0.0f, v); | |
| 259 return pow(v, 1.0f / 2.8f); | |
| 260 | |
| 261 case ColorSpace::TransferID::SMPTE240M: { | |
| 262 v = fmax(0.0f, v); | |
| 263 float a = 1.11157219592173128753f; | |
| 264 float b = 0.02282158552944503135f; | |
| 265 if (v <= b) { | |
| 266 return 4.0f * v; | |
| 267 } else { | |
| 268 return a * pow(v, 0.45f) - (a - 1.0f); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 case ColorSpace::TransferID::LINEAR: | |
| 273 return v; | |
| 274 | |
| 275 case ColorSpace::TransferID::LOG: | |
| 276 if (v < 0.01) | |
| 277 return 0.0; | |
| 278 return 1.0 + log(v) / log(10.0f) / 2.0f; | |
| 279 | |
| 280 case ColorSpace::TransferID::LOG_SQRT: | |
| 281 if (v < sqrt(10.0f) / 1000.0) | |
| 282 return 0.0; | |
| 283 return 1.0 + log(v) / log(10.0f) / 2.5f; | |
| 284 | |
| 285 case ColorSpace::TransferID::IEC61966_2_4: { | |
| 286 float a = 1.099296826809442f; | |
| 287 float b = 0.018053968510807f; | |
| 288 if (v < -b) { | |
| 289 return -a * pow(-v, 0.45) + (a - 1.0f); | |
| 290 } else if (v <= b) { | |
| 291 return 4.5 * v; | |
| 292 } else { | |
| 293 return a * pow(v, 0.45) - (a - 1.0f); | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 case ColorSpace::TransferID::BT1361_ECG: { | |
| 298 float a = 1.099; | |
| 299 float b = 0.018; | |
| 300 float l = 0.0045; | |
| 301 if (v < -l) { | |
| 302 return -(a * pow(-4.0f * v, 0.45) + (a - 1.0f)) / 4.0f; | |
| 303 } else if (v <= b) { | |
| 304 return 4.5 * v; | |
| 305 } else { | |
| 306 return a * pow(v, 0.45) - (a - 1.0f); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
| 311 v = fmax(0.0f, v); | |
| 312 float a = 1.055f; | |
| 313 float b = 0.0031308f; | |
| 314 if (v < b) { | |
| 315 return 12.92f * v; | |
| 316 } else { | |
| 317 return a * pow(v, 1.0f / 2.4f) - (a - 1.0f); | |
| 318 } | |
| 319 } | |
| 320 case ColorSpace::TransferID::SMPTEST2084: { | |
| 321 v = fmax(0.0f, v); | |
| 322 float m1 = (2610.0 / 4096.0) / 4.0; | |
| 323 float m2 = (2523.0 / 4096.0) * 128.0; | |
| 324 float c1 = 3424.0 / 4096.0; | |
| 325 float c2 = (2413.0 / 4096.0) * 32.0; | |
| 326 float c3 = (2392.0 / 4096.0) * 32.0; | |
| 327 return pow((c1 + c2 * pow(v, m1)) / (1.0f + c3 * pow(v, m1)), m2); | |
| 328 } | |
| 329 | |
| 330 case ColorSpace::TransferID::SMPTEST428_1: | |
| 331 v = fmax(0.0f, v); | |
| 332 return pow(48.0f * v + 52.37f, 1.0f / 2.6f); | |
| 333 | |
| 334 // Chrome-specific values below | |
| 335 case ColorSpace::TransferID::GAMMA24: | |
| 336 v = fmax(0.0f, v); | |
| 337 return pow(v, 1.0f / 2.4f); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 GFX_EXPORT float toLinear(ColorSpace::TransferID id, float v) { | |
|
ccameron
2016/08/02 02:47:38
nit: ToLinear
hubbe
2016/08/02 08:10:57
Done.
| |
| 342 switch (id) { | |
| 343 default: | |
| 344 case ColorSpace::TransferID::BT709: | |
| 345 case ColorSpace::TransferID::SMPTE170M: | |
| 346 case ColorSpace::TransferID::BT2020_10: | |
| 347 case ColorSpace::TransferID::BT2020_12: { | |
| 348 v = fmax(0.0f, v); | |
| 349 float a = 1.099296826809442f; | |
| 350 float b = 0.018053968510807; | |
| 351 if (v < fromLinear(ColorSpace::TransferID::BT709, b)) { | |
| 352 return v / 4.5f; | |
| 353 } else { | |
| 354 return pow((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 case ColorSpace::TransferID::GAMMA22: | |
| 359 v = fmax(0.0f, v); | |
| 360 return pow(v, 2.2f); | |
| 361 | |
| 362 case ColorSpace::TransferID::GAMMA28: | |
| 363 v = fmax(0.0f, v); | |
| 364 return pow(v, 2.8f); | |
| 365 | |
| 366 case ColorSpace::TransferID::SMPTE240M: { | |
| 367 v = fmax(0.0f, v); | |
| 368 float a = 1.11157219592173128753f; | |
| 369 float b = 0.02282158552944503135f; | |
| 370 if (v <= fromLinear(ColorSpace::TransferID::SMPTE240M, b)) { | |
| 371 return v / 4.0f; | |
| 372 } else { | |
| 373 return pow((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 case ColorSpace::TransferID::LINEAR: | |
| 378 return v; | |
| 379 | |
| 380 case ColorSpace::TransferID::LOG: | |
| 381 if (v < 0.0) | |
| 382 return 0.0; | |
| 383 return pow(10.0, (v - 1.0f) * 2.0f); | |
| 384 | |
| 385 case ColorSpace::TransferID::LOG_SQRT: | |
| 386 if (v < 0.0) | |
| 387 return 0.0; | |
| 388 return pow(10.0, (v - 1.0f) * 2.5f); | |
| 389 | |
| 390 case ColorSpace::TransferID::IEC61966_2_4: { | |
| 391 float a = 1.099296826809442f; | |
| 392 float b = 0.018053968510807f; | |
| 393 if (v < fromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) { | |
| 394 return -pow((a - 1.0f - v) / a, 1.0f / 0.45f); | |
| 395 } else if (v <= fromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) { | |
| 396 return v / 4.5f; | |
| 397 } else { | |
| 398 return pow((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 case ColorSpace::TransferID::BT1361_ECG: { | |
| 403 float a = 1.099; | |
| 404 float b = 0.018; | |
| 405 float l = 0.0045; | |
| 406 if (v < fromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { | |
| 407 return -pow((1.0f - a - v * 4.0) / a, 1.0f / 0.45f) / 4.0f; | |
| 408 } else if (v <= fromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { | |
| 409 return v / 4.5f; | |
| 410 } else { | |
| 411 return pow((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
| 416 v = fmax(0.0f, v); | |
| 417 float a = 1.055f; | |
| 418 float b = 0.0031308f; | |
| 419 if (v < fromLinear(ColorSpace::TransferID::IEC61966_2_1, b)) { | |
| 420 return v / 12.92f; | |
| 421 } else { | |
| 422 return pow((v + a - 1.0f) / a, 2.4f); | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 case ColorSpace::TransferID::SMPTEST2084: { | |
| 427 v = fmax(0.0f, v); | |
| 428 float m1 = (2610.0f / 4096.0f) / 4.0f; | |
| 429 float m2 = (2523.0f / 4096.0f) * 128.0f; | |
| 430 float c1 = 3424.0f / 4096.0f; | |
| 431 float c2 = (2413.0f / 4096.0f) * 32.0f; | |
| 432 float c3 = (2392.0f / 4096.0f) * 32.0f; | |
| 433 return pow(fmax(pow(v, 1.0 / m2) - c1, 0) / (c2 - c3 * pow(v, 1.0 / m2)), | |
| 434 1.0f / m1); | |
| 435 } | |
| 436 | |
| 437 case ColorSpace::TransferID::SMPTEST428_1: | |
| 438 return (pow(v, 2.6f) - 52.37f) / 48.0f; | |
| 439 | |
| 440 // Chrome-specific values below | |
| 441 case ColorSpace::TransferID::GAMMA24: | |
| 442 v = fmax(0.0f, v); | |
| 443 return pow(v, 2.4f); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { | |
| 448 float Kr, Kb; | |
| 449 switch (id) { | |
| 450 case ColorSpace::MatrixID::RGB: | |
| 451 return Transform(); | |
| 452 | |
| 453 case ColorSpace::MatrixID::BT709: | |
| 454 case ColorSpace::MatrixID::UNSPECIFIED: | |
| 455 case ColorSpace::MatrixID::RESERVED: | |
| 456 Kr = 0.2126f; | |
| 457 Kb = 0.0722f; | |
| 458 break; | |
| 459 | |
| 460 case ColorSpace::MatrixID::FCC: | |
| 461 Kr = 0.30f; | |
| 462 Kb = 0.11f; | |
| 463 break; | |
| 464 | |
| 465 case ColorSpace::MatrixID::BT470BG: | |
| 466 case ColorSpace::MatrixID::SMPTE170M: | |
| 467 Kr = 0.299f; | |
| 468 Kb = 0.144f; | |
| 469 break; | |
| 470 | |
| 471 case ColorSpace::MatrixID::SMPTE240M: | |
| 472 Kr = 0.212f; | |
| 473 Kb = 0.087f; | |
| 474 break; | |
| 475 | |
| 476 case ColorSpace::MatrixID::YCOCG: | |
| 477 return Transform(0.25f, 0.5f, 0.25f, 0.5, // 1 | |
| 478 -0.25f, 0.5f, -0.25f, 0.5, // 2 | |
| 479 0.5f, 0.0f, -0.5f, 0.0, // 3 | |
| 480 0.0f, 0.0f, 0.0f, 1.0f); // 4 | |
| 481 | |
| 482 // TODO(hubbe): Check if the CL equation is right. | |
| 483 case ColorSpace::MatrixID::BT2020_NCL: | |
| 484 case ColorSpace::MatrixID::BT2020_CL: | |
| 485 Kr = 0.2627f; | |
| 486 Kb = 0.0593f; | |
| 487 break; | |
| 488 | |
| 489 case ColorSpace::MatrixID::YDZDX: | |
| 490 return Transform(0.0f, 1.0f, 0.0, 0.0f, // 1 | |
| 491 0.0f, -0.5f, 0.986566f / 2.0f, 0.5f, // 2 | |
| 492 0.5f, -0.991902f / 2.0f, 0.0f, 0.5f, // 3 | |
| 493 0.0f, 0.0f, 0.0f, 1.0f); // 4 | |
| 494 } | |
| 495 float u_m = 0.5f / (1.0f - Kb); | |
| 496 float v_m = 0.5f / (1.0f - Kr); | |
| 497 return Transform( | |
| 498 Kr, 1.0f - Kr - Kb, Kb, 0.0f, // 1 | |
| 499 u_m * -Kr, u_m * -(1.0f - Kr - Kb), u_m * (1.0f - Kb), 0.5f, // 2 | |
| 500 v_m * (1.0f - Kr), v_m * -(1.0f - Kr - Kb), v_m * -Kb, 0.5f, // 3 | |
| 501 0.0f, 0.0f, 0.0f, 1.0f); // 4 | |
| 502 } | |
| 503 | |
| 504 Transform GetRangeAdjustMatrix(ColorSpace::RangeID range, | |
| 505 ColorSpace::MatrixID matrix) { | |
| 506 switch (range) { | |
| 507 case ColorSpace::RangeID::FULL: | |
| 508 return Transform(); | |
| 509 | |
| 510 case ColorSpace::RangeID::LIMITED: | |
| 511 break; | |
| 512 } | |
| 513 switch (matrix) { | |
| 514 case ColorSpace::MatrixID::RGB: | |
| 515 case ColorSpace::MatrixID::YCOCG: | |
| 516 return Transform(255.0f / 219.0f, 0.0f, 0.0f, -16.0f / 219.0f, // 1 | |
| 517 0.0f, 255.0f / 219.0f, 0.0f, -16.0f / 219.0f, // 2 | |
| 518 0.0f, 0.0f, 255.0f / 219.0f, -16.0f / 219.0f, // 3 | |
| 519 0.0f, 0.0f, 0.0f, 1.0f); // 4 | |
| 520 | |
| 521 case ColorSpace::MatrixID::BT709: | |
| 522 case ColorSpace::MatrixID::UNSPECIFIED: | |
| 523 case ColorSpace::MatrixID::RESERVED: | |
| 524 case ColorSpace::MatrixID::FCC: | |
| 525 case ColorSpace::MatrixID::BT470BG: | |
| 526 case ColorSpace::MatrixID::SMPTE170M: | |
| 527 case ColorSpace::MatrixID::SMPTE240M: | |
| 528 case ColorSpace::MatrixID::BT2020_NCL: | |
| 529 case ColorSpace::MatrixID::BT2020_CL: | |
| 530 case ColorSpace::MatrixID::YDZDX: | |
| 531 return Transform(255.0f / 219.0f, 0.0f, 0.0f, -16.0f / 219.0f, // 1 | |
| 532 0.0f, 255.0f / 224.0f, 0.0f, -15.5f / 224.0f, // 2 | |
| 533 0.0f, 0.0f, 255.0f / 224.0f, -15.5f / 224.0f, // 3 | |
| 534 0.0f, 0.0f, 0.0f, 1.0f); // 4 | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 class ColorSpaceToColorSpaceTransform : public ColorTransform { | |
| 539 public: | |
| 540 ColorSpaceToColorSpaceTransform(const ColorSpace& from, | |
| 541 const ColorSpace& to, | |
| 542 Intent intent) | |
| 543 : from_(from), to_(to) { | |
| 544 if (intent == Intent::PERCEPTUAL) { | |
| 545 switch (from_.transfer_) { | |
| 546 case ColorSpace::TransferID::UNSPECIFIED: | |
| 547 case ColorSpace::TransferID::BT709: | |
| 548 case ColorSpace::TransferID::SMPTE170M: | |
| 549 // See SMPTE 1886 | |
| 550 from_.transfer_ = ColorSpace::TransferID::GAMMA24; | |
| 551 break; | |
| 552 | |
| 553 default: // Do nothing | |
| 554 break; | |
| 555 } | |
| 556 | |
| 557 // TODO(hubbe): shrink gamuts here (never stretch gamuts) | |
| 558 } | |
| 559 | |
| 560 Transform* from_transfer_matrix = | |
| 561 from_.matrix_ == ColorSpace::MatrixID::BT2020_CL ? &b_ : &a_; | |
| 562 Transform* to_transfer_matrix = | |
| 563 to_.matrix_ == ColorSpace::MatrixID::BT2020_CL ? &b_ : &c_; | |
| 564 | |
| 565 c_ *= Invert(GetRangeAdjustMatrix(to_.range_, to_.matrix_)); | |
| 566 *to_transfer_matrix *= GetTransferMatrix(to_.matrix_); | |
| 567 b_ *= Invert(GetPrimaryMatrix(to_.primaries_)); | |
| 568 b_ *= GetPrimaryMatrix(from_.primaries_); | |
| 569 *from_transfer_matrix *= Invert(GetTransferMatrix(from_.matrix_)); | |
| 570 a_ *= GetRangeAdjustMatrix(from_.range_, from_.matrix_); | |
| 571 } | |
| 572 | |
| 573 void transform(TriStim* colors, size_t num) override { | |
| 574 for (size_t i = 0; i < num; i++) { | |
| 575 TriStim c = colors[i]; | |
| 576 a_.TransformPoint(&c); | |
| 577 c.set_x(toLinear(from_.transfer_, c.x())); | |
| 578 c.set_y(toLinear(from_.transfer_, c.y())); | |
| 579 c.set_z(toLinear(from_.transfer_, c.z())); | |
| 580 b_.TransformPoint(&c); | |
| 581 c.set_x(fromLinear(to_.transfer_, c.x())); | |
| 582 c.set_y(fromLinear(to_.transfer_, c.y())); | |
| 583 c.set_z(fromLinear(to_.transfer_, c.z())); | |
| 584 c_.TransformPoint(&c); | |
| 585 colors[i] = c; | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 private: | |
| 590 ColorSpace from_; | |
| 591 ColorSpace to_; | |
| 592 | |
| 593 // a_ -> tolinear -> b_ -> fromlinear -> c_; | |
| 594 Transform a_; | |
| 595 Transform b_; | |
| 596 Transform c_; | |
| 597 }; | |
| 598 | |
| 599 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( | |
| 600 const ColorSpace& from, | |
| 601 const ColorSpace& to, | |
| 602 Intent intent) { | |
| 603 // TODO(Hubbe): Check if from and/or to can be mapped to ICC profiles and | |
| 604 // provide better transforms in those cases. | |
| 605 return std::unique_ptr<ColorTransform>( | |
| 606 new ColorSpaceToColorSpaceTransform(from, to, intent)); | |
| 607 } | |
| 608 | |
| 609 } // namespace gfx | |
| OLD | NEW |