| 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 case ColorSpace::TransferID::LINEAR_HDR: | |
| 205 return v; | |
| 206 | |
| 207 case ColorSpace::TransferID::LOG: | 46 case ColorSpace::TransferID::LOG: |
| 208 if (v < 0.01f) | 47 if (v < 0.01f) |
| 209 return 0.0f; | 48 return 0.0f; |
| 210 return 1.0f + log(v) / log(10.0f) / 2.0f; | 49 return 1.0f + log(v) / log(10.0f) / 2.0f; |
| 211 | 50 |
| 212 case ColorSpace::TransferID::LOG_SQRT: | 51 case ColorSpace::TransferID::LOG_SQRT: |
| 213 if (v < sqrt(10.0f) / 1000.0f) | 52 if (v < sqrt(10.0f) / 1000.0f) |
| 214 return 0.0f; | 53 return 0.0f; |
| 215 return 1.0f + log(v) / log(10.0f) / 2.5f; | 54 return 1.0f + log(v) / log(10.0f) / 2.5f; |
| 216 | 55 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 232 float l = 0.0045f; | 71 float l = 0.0045f; |
| 233 if (v < -l) { | 72 if (v < -l) { |
| 234 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; |
| 235 } else if (v <= b) { | 74 } else if (v <= b) { |
| 236 return 4.5f * v; | 75 return 4.5f * v; |
| 237 } else { | 76 } else { |
| 238 return a * powf(v, 0.45f) - (a - 1.0f); | 77 return a * powf(v, 0.45f) - (a - 1.0f); |
| 239 } | 78 } |
| 240 } | 79 } |
| 241 | 80 |
| 242 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
| 243 v = fmax(0.0f, v); | |
| 244 float a = 1.055f; | |
| 245 float b = 0.0031308f; | |
| 246 if (v < b) { | |
| 247 return 12.92f * v; | |
| 248 } else { | |
| 249 return a * powf(v, 1.0f / 2.4f) - (a - 1.0f); | |
| 250 } | |
| 251 } | |
| 252 case ColorSpace::TransferID::SMPTEST2084: { | 81 case ColorSpace::TransferID::SMPTEST2084: { |
| 253 // Go from scRGB levels to 0-1. | 82 // Go from scRGB levels to 0-1. |
| 254 v *= 80.0f / 10000.0f; | 83 v *= 80.0f / 10000.0f; |
| 255 v = fmax(0.0f, v); | 84 v = fmax(0.0f, v); |
| 256 float m1 = (2610.0f / 4096.0f) / 4.0f; | 85 float m1 = (2610.0f / 4096.0f) / 4.0f; |
| 257 float m2 = (2523.0f / 4096.0f) * 128.0f; | 86 float m2 = (2523.0f / 4096.0f) * 128.0f; |
| 258 float c1 = 3424.0f / 4096.0f; | 87 float c1 = 3424.0f / 4096.0f; |
| 259 float c2 = (2413.0f / 4096.0f) * 32.0f; | 88 float c2 = (2413.0f / 4096.0f) * 32.0f; |
| 260 float c3 = (2392.0f / 4096.0f) * 32.0f; | 89 float c3 = (2392.0f / 4096.0f) * 32.0f; |
| 261 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); |
| 262 } | 91 } |
| 263 | 92 |
| 264 case ColorSpace::TransferID::SMPTEST428_1: | |
| 265 v = fmax(0.0f, v); | |
| 266 return powf(48.0f * v + 52.37f, 1.0f / 2.6f); | |
| 267 | |
| 268 // 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 |
| 269 case ColorSpace::TransferID::ARIB_STD_B67: { | 94 case ColorSpace::TransferID::ARIB_STD_B67: { |
| 270 const float a = 0.17883277f; | 95 const float a = 0.17883277f; |
| 271 const float b = 0.28466892f; | 96 const float b = 0.28466892f; |
| 272 const float c = 0.55991073f; | 97 const float c = 0.55991073f; |
| 273 v = fmax(0.0f, v); | 98 v = fmax(0.0f, v); |
| 274 if (v <= 1) | 99 if (v <= 1) |
| 275 return 0.5f * sqrtf(v); | 100 return 0.5f * sqrtf(v); |
| 276 else | 101 else |
| 277 return a * log(v - b) + c; | 102 return a * log(v - b) + c; |
| 278 } | 103 } |
| 279 | 104 |
| 280 // Chrome-specific values below | 105 default: |
| 281 case ColorSpace::TransferID::GAMMA24: | 106 // Handled by SkColorSpaceTransferFn. |
| 282 v = fmax(0.0f, v); | 107 break; |
| 283 return powf(v, 1.0f / 2.4f); | |
| 284 } | 108 } |
| 285 | 109 NOTREACHED(); |
| 286 v = fmax(0.0f, v); | 110 return 0; |
| 287 float a = 1.099296826809442f; | |
| 288 float b = 0.018053968510807f; | |
| 289 if (v <= b) { | |
| 290 return 4.5f * v; | |
| 291 } else { | |
| 292 return a * powf(v, 0.45f) - (a - 1.0f); | |
| 293 } | |
| 294 } | 111 } |
| 295 | 112 |
| 296 GFX_EXPORT float ToLinear(ColorSpace::TransferID id, float v) { | 113 float ToLinear(ColorSpace::TransferID id, float v) { |
| 297 switch (id) { | 114 switch (id) { |
| 298 case ColorSpace::TransferID::CUSTOM: | |
| 299 // TODO(hubbe): Actually implement custom transfer functions. | |
| 300 case ColorSpace::TransferID::RESERVED0: | |
| 301 case ColorSpace::TransferID::RESERVED: | |
| 302 case ColorSpace::TransferID::UNSPECIFIED: | |
| 303 case ColorSpace::TransferID::UNKNOWN: | |
| 304 // All unknown values default to BT709 | |
| 305 | |
| 306 case ColorSpace::TransferID::BT709: | |
| 307 case ColorSpace::TransferID::SMPTE170M: | |
| 308 case ColorSpace::TransferID::BT2020_10: | |
| 309 case ColorSpace::TransferID::BT2020_12: | |
| 310 // BT709 is our "default" cause, so put the code after the switch | |
| 311 // to avoid "control reaches end of non-void function" errors. | |
| 312 break; | |
| 313 | |
| 314 case ColorSpace::TransferID::GAMMA22: | |
| 315 v = fmax(0.0f, v); | |
| 316 return powf(v, 2.2f); | |
| 317 | |
| 318 case ColorSpace::TransferID::GAMMA28: | |
| 319 v = fmax(0.0f, v); | |
| 320 return powf(v, 2.8f); | |
| 321 | |
| 322 case ColorSpace::TransferID::SMPTE240M: { | |
| 323 v = fmax(0.0f, v); | |
| 324 float a = 1.11157219592173128753f; | |
| 325 float b = 0.02282158552944503135f; | |
| 326 if (v <= FromLinear(ColorSpace::TransferID::SMPTE240M, b)) { | |
| 327 return v / 4.0f; | |
| 328 } else { | |
| 329 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 case ColorSpace::TransferID::LINEAR: | |
| 334 case ColorSpace::TransferID::LINEAR_HDR: | |
| 335 return v; | |
| 336 | |
| 337 case ColorSpace::TransferID::LOG: | 115 case ColorSpace::TransferID::LOG: |
| 338 if (v < 0.0f) | 116 if (v < 0.0f) |
| 339 return 0.0f; | 117 return 0.0f; |
| 340 return powf(10.0f, (v - 1.0f) * 2.0f); | 118 return powf(10.0f, (v - 1.0f) * 2.0f); |
| 341 | 119 |
| 342 case ColorSpace::TransferID::LOG_SQRT: | 120 case ColorSpace::TransferID::LOG_SQRT: |
| 343 if (v < 0.0f) | 121 if (v < 0.0f) |
| 344 return 0.0f; | 122 return 0.0f; |
| 345 return powf(10.0f, (v - 1.0f) * 2.5f); | 123 return powf(10.0f, (v - 1.0f) * 2.5f); |
| 346 | 124 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 362 float l = 0.0045f; | 140 float l = 0.0045f; |
| 363 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { | 141 if (v < FromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { |
| 364 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; |
| 365 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { | 143 } else if (v <= FromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { |
| 366 return v / 4.5f; | 144 return v / 4.5f; |
| 367 } else { | 145 } else { |
| 368 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | 146 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); |
| 369 } | 147 } |
| 370 } | 148 } |
| 371 | 149 |
| 372 case ColorSpace::TransferID::IEC61966_2_1: { // SRGB | |
| 373 v = fmax(0.0f, v); | |
| 374 float a = 1.055f; | |
| 375 float b = 0.0031308f; | |
| 376 if (v < FromLinear(ColorSpace::TransferID::IEC61966_2_1, b)) { | |
| 377 return v / 12.92f; | |
| 378 } else { | |
| 379 return powf((v + a - 1.0f) / a, 2.4f); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 case ColorSpace::TransferID::SMPTEST2084: { | 150 case ColorSpace::TransferID::SMPTEST2084: { |
| 384 v = fmax(0.0f, v); | 151 v = fmax(0.0f, v); |
| 385 float m1 = (2610.0f / 4096.0f) / 4.0f; | 152 float m1 = (2610.0f / 4096.0f) / 4.0f; |
| 386 float m2 = (2523.0f / 4096.0f) * 128.0f; | 153 float m2 = (2523.0f / 4096.0f) * 128.0f; |
| 387 float c1 = 3424.0f / 4096.0f; | 154 float c1 = 3424.0f / 4096.0f; |
| 388 float c2 = (2413.0f / 4096.0f) * 32.0f; | 155 float c2 = (2413.0f / 4096.0f) * 32.0f; |
| 389 float c3 = (2392.0f / 4096.0f) * 32.0f; | 156 float c3 = (2392.0f / 4096.0f) * 32.0f; |
| 390 v = powf( | 157 v = powf( |
| 391 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)), |
| 392 1.0f / m1); | 159 1.0f / m1); |
| 393 // This matches the scRGB definition that 1.0 means 80 nits. | 160 // This matches the scRGB definition that 1.0 means 80 nits. |
| 394 // 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 |
| 395 // that might be difficult to do right now. | 162 // that might be difficult to do right now. |
| 396 v *= 10000.0f / 80.0f; | 163 v *= 10000.0f / 80.0f; |
| 397 return v; | 164 return v; |
| 398 } | 165 } |
| 399 | 166 |
| 400 case ColorSpace::TransferID::SMPTEST428_1: | |
| 401 return (powf(v, 2.6f) - 52.37f) / 48.0f; | |
| 402 | |
| 403 // Chrome-specific values below | |
| 404 case ColorSpace::TransferID::GAMMA24: | |
| 405 v = fmax(0.0f, v); | |
| 406 return powf(v, 2.4f); | |
| 407 | |
| 408 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: | 167 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
| 409 v = fmax(0.0f, v); | 168 v = fmax(0.0f, v); |
| 410 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); |
| 411 | 170 |
| 412 // 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 |
| 413 case ColorSpace::TransferID::ARIB_STD_B67: { | 172 case ColorSpace::TransferID::ARIB_STD_B67: { |
| 414 v = fmax(0.0f, v); | 173 v = fmax(0.0f, v); |
| 415 const float a = 0.17883277f; | 174 const float a = 0.17883277f; |
| 416 const float b = 0.28466892f; | 175 const float b = 0.28466892f; |
| 417 const float c = 0.55991073f; | 176 const float c = 0.55991073f; |
| 418 float v_ = 0.0f; | 177 float v_ = 0.0f; |
| 419 if (v <= 0.5f) { | 178 if (v <= 0.5f) { |
| 420 v_ = (v * 2.0f) * (v * 2.0f); | 179 v_ = (v * 2.0f) * (v * 2.0f); |
| 421 } else { | 180 } else { |
| 422 v_ = exp((v - c) / a) + b; | 181 v_ = exp((v - c) / a) + b; |
| 423 } | 182 } |
| 424 return v_; | 183 return v_; |
| 425 } | 184 } |
| 185 |
| 186 default: |
| 187 // Handled by SkColorSpaceTransferFn. |
| 188 break; |
| 426 } | 189 } |
| 427 | 190 NOTREACHED(); |
| 428 v = fmax(0.0f, v); | 191 return 0; |
| 429 float a = 1.099296826809442f; | |
| 430 float b = 0.018053968510807f; | |
| 431 if (v < FromLinear(ColorSpace::TransferID::BT709, b)) { | |
| 432 return v / 4.5f; | |
| 433 } else { | |
| 434 return powf((v + a - 1.0f) / a, 1.0f / 0.45f); | |
| 435 } | |
| 436 } | 192 } |
| 437 | 193 |
| 438 GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { | 194 GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { |
| 439 // Default values for BT709; | 195 // Default values for BT709; |
| 440 float Kr = 0.2126f; | 196 float Kr = 0.2126f; |
| 441 float Kb = 0.0722f; | 197 float Kb = 0.0722f; |
| 442 switch (id) { | 198 switch (id) { |
| 443 case ColorSpace::MatrixID::RGB: | 199 case ColorSpace::MatrixID::RGB: |
| 444 return Transform(); | 200 return Transform(); |
| 445 | 201 |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 612 for (size_t i = 0; i < num; i++) | 368 for (size_t i = 0; i < num; i++) |
| 613 matrix_.TransformPoint(colors + i); | 369 matrix_.TransformPoint(colors + i); |
| 614 } | 370 } |
| 615 | 371 |
| 616 private: | 372 private: |
| 617 Transform matrix_; | 373 Transform matrix_; |
| 618 }; | 374 }; |
| 619 | 375 |
| 620 class ColorTransformFromLinear : public ColorTransformInternal { | 376 class ColorTransformFromLinear : public ColorTransformInternal { |
| 621 public: | 377 public: |
| 622 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer) | 378 explicit ColorTransformFromLinear(ColorSpace::TransferID transfer, |
| 623 : transfer_(transfer) { | 379 const SkColorSpaceTransferFn& fn, |
| 624 // Map LINEAR_HDR to LINEAR for optimizations. | 380 bool fn_valid) |
| 625 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) { | 381 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) { |
| 382 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) |
| 626 transfer_ = ColorSpace::TransferID::LINEAR; | 383 transfer_ = ColorSpace::TransferID::LINEAR; |
| 627 } | |
| 628 } | 384 } |
| 629 bool Prepend(ColorTransformInternal* prev) override { | 385 bool Prepend(ColorTransformInternal* prev) override { |
| 630 return prev->Join(*this); | 386 return prev->Join(*this); |
| 631 } | 387 } |
| 632 | 388 |
| 633 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } | 389 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } |
| 634 | 390 |
| 635 void transform(ColorTransform::TriStim* colors, size_t num) override { | 391 void transform(ColorTransform::TriStim* colors, size_t num) override { |
| 636 for (size_t i = 0; i < num; i++) { | 392 if (fn_valid_) { |
| 637 colors[i].set_x(FromLinear(transfer_, colors[i].x())); | 393 for (size_t i = 0; i < num; i++) { |
| 638 colors[i].set_y(FromLinear(transfer_, colors[i].y())); | 394 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); |
| 639 colors[i].set_z(FromLinear(transfer_, colors[i].z())); | 395 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); |
| 396 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); |
| 397 } |
| 398 } else { |
| 399 for (size_t i = 0; i < num; i++) { |
| 400 colors[i].set_x(FromLinear(transfer_, colors[i].x())); |
| 401 colors[i].set_y(FromLinear(transfer_, colors[i].y())); |
| 402 colors[i].set_z(FromLinear(transfer_, colors[i].z())); |
| 403 } |
| 640 } | 404 } |
| 641 } | 405 } |
| 642 | 406 |
| 643 private: | 407 private: |
| 644 friend class ColorTransformToLinear; | 408 friend class ColorTransformToLinear; |
| 645 ColorSpace::TransferID transfer_; | 409 ColorSpace::TransferID transfer_; |
| 410 SkColorSpaceTransferFn fn_; |
| 411 bool fn_valid_ = false; |
| 646 }; | 412 }; |
| 647 | 413 |
| 648 class ColorTransformToLinear : public ColorTransformInternal { | 414 class ColorTransformToLinear : public ColorTransformInternal { |
| 649 public: | 415 public: |
| 650 explicit ColorTransformToLinear(ColorSpace::TransferID transfer) | 416 explicit ColorTransformToLinear(ColorSpace::TransferID transfer, |
| 651 : transfer_(transfer) { | 417 const SkColorSpaceTransferFn& fn, |
| 652 // Map LINEAR_HDR to LINEAR for optimizations. | 418 bool fn_valid) |
| 653 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) { | 419 : transfer_(transfer), fn_(fn), fn_valid_(fn_valid) { |
| 420 if (transfer_ == ColorSpace::TransferID::LINEAR_HDR) |
| 654 transfer_ = ColorSpace::TransferID::LINEAR; | 421 transfer_ = ColorSpace::TransferID::LINEAR; |
| 655 } | |
| 656 } | 422 } |
| 657 | 423 |
| 658 bool Prepend(ColorTransformInternal* prev) override { | 424 bool Prepend(ColorTransformInternal* prev) override { |
| 659 return prev->Join(*this); | 425 return prev->Join(*this); |
| 660 } | 426 } |
| 661 | 427 |
| 662 static bool IsGamma22(ColorSpace::TransferID transfer) { | 428 static bool IsGamma22(ColorSpace::TransferID transfer) { |
| 663 switch (transfer) { | 429 switch (transfer) { |
| 664 // We don't need to check BT709 here because it's been translated into | 430 // We don't need to check BT709 here because it's been translated into |
| 665 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below. | 431 // SRGB in ColorSpaceToColorSpaceTransform::ColorSpaceToLinear below. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 682 } | 448 } |
| 683 | 449 |
| 684 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } | 450 bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; } |
| 685 | 451 |
| 686 // Assumes BT2020 primaries. | 452 // Assumes BT2020 primaries. |
| 687 static float Luma(const ColorTransform::TriStim& c) { | 453 static float Luma(const ColorTransform::TriStim& c) { |
| 688 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f; | 454 return c.x() * 0.2627f + c.y() * 0.6780f + c.z() * 0.0593f; |
| 689 } | 455 } |
| 690 | 456 |
| 691 void transform(ColorTransform::TriStim* colors, size_t num) override { | 457 void transform(ColorTransform::TriStim* colors, size_t num) override { |
| 692 if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { | 458 if (fn_valid_) { |
| 459 for (size_t i = 0; i < num; i++) { |
| 460 colors[i].set_x(EvalSkTransferFn(fn_, colors[i].x())); |
| 461 colors[i].set_y(EvalSkTransferFn(fn_, colors[i].y())); |
| 462 colors[i].set_z(EvalSkTransferFn(fn_, colors[i].z())); |
| 463 } |
| 464 } else if (transfer_ == ColorSpace::TransferID::SMPTEST2084_NON_HDR) { |
| 693 for (size_t i = 0; i < num; i++) { | 465 for (size_t i = 0; i < num; i++) { |
| 694 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()), | 466 ColorTransform::TriStim ret(ToLinear(transfer_, colors[i].x()), |
| 695 ToLinear(transfer_, colors[i].y()), | 467 ToLinear(transfer_, colors[i].y()), |
| 696 ToLinear(transfer_, colors[i].z())); | 468 ToLinear(transfer_, colors[i].z())); |
| 697 if (Luma(ret) > 0.0) { | 469 if (Luma(ret) > 0.0) { |
| 698 ColorTransform::TriStim smpte2084( | 470 ColorTransform::TriStim smpte2084( |
| 699 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].x()), | 471 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].x()), |
| 700 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].y()), | 472 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].y()), |
| 701 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].z())); | 473 ToLinear(ColorSpace::TransferID::SMPTEST2084, colors[i].z())); |
| 702 smpte2084.Scale(Luma(ret) / Luma(smpte2084)); | 474 smpte2084.Scale(Luma(ret) / Luma(smpte2084)); |
| 703 ret = smpte2084; | 475 ret = smpte2084; |
| 704 } | 476 } |
| 705 colors[i] = ret; | 477 colors[i] = ret; |
| 706 } | 478 } |
| 707 } else { | 479 } else { |
| 708 for (size_t i = 0; i < num; i++) { | 480 for (size_t i = 0; i < num; i++) { |
| 709 colors[i].set_x(ToLinear(transfer_, colors[i].x())); | 481 colors[i].set_x(ToLinear(transfer_, colors[i].x())); |
| 710 colors[i].set_y(ToLinear(transfer_, colors[i].y())); | 482 colors[i].set_y(ToLinear(transfer_, colors[i].y())); |
| 711 colors[i].set_z(ToLinear(transfer_, colors[i].z())); | 483 colors[i].set_z(ToLinear(transfer_, colors[i].z())); |
| 712 } | 484 } |
| 713 } | 485 } |
| 714 } | 486 } |
| 715 | 487 |
| 716 private: | 488 private: |
| 717 ColorSpace::TransferID transfer_; | 489 ColorSpace::TransferID transfer_; |
| 490 SkColorSpaceTransferFn fn_; |
| 491 bool fn_valid_ = false; |
| 718 }; | 492 }; |
| 719 | 493 |
| 720 // BT2020 Constant Luminance is different than most other | 494 // BT2020 Constant Luminance is different than most other |
| 721 // ways to encode RGB values as YUV. The basic idea is that | 495 // ways to encode RGB values as YUV. The basic idea is that |
| 722 // transfer functions are applied on the Y value instead of | 496 // transfer functions are applied on the Y value instead of |
| 723 // on the RGB values. However, running the transfer function | 497 // on the RGB values. However, running the transfer function |
| 724 // on the U and V values doesn't make any sense since they | 498 // on the U and V values doesn't make any sense since they |
| 725 // are centered at 0.5. To work around this, the transfer function | 499 // are centered at 0.5. To work around this, the transfer function |
| 726 // is applied to the Y, R and B values, and then the U and V | 500 // is applied to the Y, R and B values, and then the U and V |
| 727 // values are calculated from that. | 501 // values are calculated from that. |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 863 void disable_optimizations() { disable_optimizations_ = true; } | 637 void disable_optimizations() { disable_optimizations_ = true; } |
| 864 | 638 |
| 865 private: | 639 private: |
| 866 bool disable_optimizations_ = false; | 640 bool disable_optimizations_ = false; |
| 867 std::vector<std::unique_ptr<ColorTransformInternal>> transforms_; | 641 std::vector<std::unique_ptr<ColorTransformInternal>> transforms_; |
| 868 }; | 642 }; |
| 869 | 643 |
| 870 class ColorSpaceToColorSpaceTransform { | 644 class ColorSpaceToColorSpaceTransform { |
| 871 public: | 645 public: |
| 872 static Transform GetPrimaryTransform(const ColorSpace& c) { | 646 static Transform GetPrimaryTransform(const ColorSpace& c) { |
| 873 if (c.primaries_ == ColorSpace::PrimaryID::CUSTOM) { | 647 SkMatrix44 sk_matrix; |
| 874 return Transform(c.custom_primary_matrix_[0], c.custom_primary_matrix_[1], | 648 c.GetPrimaryMatrix(&sk_matrix); |
| 875 c.custom_primary_matrix_[2], c.custom_primary_matrix_[3], | 649 return Transform(sk_matrix); |
| 876 c.custom_primary_matrix_[4], c.custom_primary_matrix_[5], | |
| 877 c.custom_primary_matrix_[6], c.custom_primary_matrix_[7], | |
| 878 c.custom_primary_matrix_[8], c.custom_primary_matrix_[9], | |
| 879 c.custom_primary_matrix_[10], | |
| 880 c.custom_primary_matrix_[11], 0.0f, 0.0f, 0.0f, 1.0f); | |
| 881 } else { | |
| 882 return GetPrimaryMatrix(c.primaries_); | |
| 883 } | |
| 884 } | 650 } |
| 885 | 651 |
| 886 static void ColorSpaceToColorSpace(ColorSpace from, | 652 static void ColorSpaceToColorSpace(ColorSpace from, |
| 887 ColorSpace to, | 653 ColorSpace to, |
| 888 ColorTransform::Intent intent, | 654 ColorTransform::Intent intent, |
| 889 TransformBuilder* builder) { | 655 TransformBuilder* builder) { |
| 890 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { | 656 if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { |
| 891 switch (from.transfer_) { | 657 switch (from.transfer_) { |
| 892 case ColorSpace::TransferID::UNSPECIFIED: | 658 case ColorSpace::TransferID::UNSPECIFIED: |
| 893 case ColorSpace::TransferID::BT709: | 659 case ColorSpace::TransferID::BT709: |
| (...skipping 22 matching lines...) Expand all Loading... |
| 916 from.transfer_ = ColorSpace::TransferID::GAMMA24; | 682 from.transfer_ = ColorSpace::TransferID::GAMMA24; |
| 917 } | 683 } |
| 918 break; | 684 break; |
| 919 | 685 |
| 920 default: // Do nothing | 686 default: // Do nothing |
| 921 break; | 687 break; |
| 922 } | 688 } |
| 923 | 689 |
| 924 // TODO(hubbe): shrink gamuts here (never stretch gamuts) | 690 // TODO(hubbe): shrink gamuts here (never stretch gamuts) |
| 925 } | 691 } |
| 692 |
| 926 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 693 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
| 927 GetRangeAdjustMatrix(from.range_, from.matrix_))); | 694 GetRangeAdjustMatrix(from.range_, from.matrix_))); |
| 695 |
| 928 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 696 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
| 929 Invert(GetTransferMatrix(from.matrix_)))); | 697 Invert(GetTransferMatrix(from.matrix_)))); |
| 930 builder->Append(base::MakeUnique<ColorTransformToLinear>(from.transfer_)); | 698 |
| 699 SkColorSpaceTransferFn to_linear_fn; |
| 700 bool to_linear_fn_valid = from.GetTransferFunction(&to_linear_fn); |
| 701 builder->Append(base::MakeUnique<ColorTransformToLinear>( |
| 702 from.transfer_, to_linear_fn, to_linear_fn_valid)); |
| 703 |
| 931 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 704 if (from.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
| 932 // BT2020 CL is a special case. | 705 // BT2020 CL is a special case. |
| 933 builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>()); | 706 builder->Append(base::MakeUnique<ColorTransformFromBT2020CL>()); |
| 934 } | 707 } |
| 935 builder->Append( | 708 builder->Append( |
| 936 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); | 709 base::MakeUnique<ColorTransformMatrix>(GetPrimaryTransform(from))); |
| 937 | 710 |
| 938 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 711 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
| 939 Invert(GetPrimaryTransform(to)))); | 712 Invert(GetPrimaryTransform(to)))); |
| 940 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { | 713 if (to.matrix_ == ColorSpace::MatrixID::BT2020_CL) { |
| 941 // BT2020 CL is a special case. | 714 // BT2020 CL is a special case. |
| 942 builder->Append(base::MakeUnique<ColorTransformToBT2020CL>()); | 715 builder->Append(base::MakeUnique<ColorTransformToBT2020CL>()); |
| 943 } | 716 } |
| 944 | 717 |
| 945 builder->Append(base::MakeUnique<ColorTransformFromLinear>(to.transfer_)); | 718 SkColorSpaceTransferFn from_linear_fn; |
| 719 bool from_linear_fn_valid = to.GetInverseTransferFunction(&from_linear_fn); |
| 720 builder->Append(base::MakeUnique<ColorTransformFromLinear>( |
| 721 to.transfer_, from_linear_fn, from_linear_fn_valid)); |
| 722 |
| 946 builder->Append( | 723 builder->Append( |
| 947 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to.matrix_))); | 724 base::MakeUnique<ColorTransformMatrix>(GetTransferMatrix(to.matrix_))); |
| 725 |
| 948 builder->Append(base::MakeUnique<ColorTransformMatrix>( | 726 builder->Append(base::MakeUnique<ColorTransformMatrix>( |
| 949 Invert(GetRangeAdjustMatrix(to.range_, to.matrix_)))); | 727 Invert(GetRangeAdjustMatrix(to.range_, to.matrix_)))); |
| 950 } | 728 } |
| 951 }; | 729 }; |
| 952 | 730 |
| 953 class QCMSColorTransform : public ColorTransformInternal { | 731 class QCMSColorTransform : public ColorTransformInternal { |
| 954 public: | 732 public: |
| 955 // Takes ownership of the profiles | 733 // Takes ownership of the profiles |
| 956 QCMSColorTransform(qcms_profile* from, qcms_profile* to) | 734 QCMSColorTransform(qcms_profile* from, qcms_profile* to) |
| 957 : from_(from), to_(to) {} | 735 : from_(from), to_(to) {} |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1033 from_profile ? ColorSpace::CreateXYZD50() : from, | 811 from_profile ? ColorSpace::CreateXYZD50() : from, |
| 1034 to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder); | 812 to_profile ? ColorSpace::CreateXYZD50() : to, intent, &builder); |
| 1035 if (to_profile) { | 813 if (to_profile) { |
| 1036 builder.Append(std::unique_ptr<ColorTransformInternal>( | 814 builder.Append(std::unique_ptr<ColorTransformInternal>( |
| 1037 new QCMSColorTransform(GetXYZD50Profile(), to_profile))); | 815 new QCMSColorTransform(GetXYZD50Profile(), to_profile))); |
| 1038 } | 816 } |
| 1039 | 817 |
| 1040 return builder.GetTransform(); | 818 return builder.GetTransform(); |
| 1041 } | 819 } |
| 1042 | 820 |
| 821 // static |
| 822 float ColorTransform::ToLinearForTesting(ColorSpace::TransferID transfer, |
| 823 float v) { |
| 824 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, |
| 825 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
| 826 SkColorSpaceTransferFn to_linear_fn; |
| 827 bool to_linear_fn_valid = space.GetTransferFunction(&to_linear_fn); |
| 828 ColorTransformToLinear to_linear_transform(transfer, to_linear_fn, |
| 829 to_linear_fn_valid); |
| 830 TriStim color(v, v, v); |
| 831 to_linear_transform.transform(&color, 1); |
| 832 return color.x(); |
| 833 } |
| 834 |
| 835 // static |
| 836 float ColorTransform::FromLinearForTesting(ColorSpace::TransferID transfer, |
| 837 float v) { |
| 838 ColorSpace space(ColorSpace::PrimaryID::BT709, transfer, |
| 839 ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); |
| 840 SkColorSpaceTransferFn from_linear_fn; |
| 841 bool from_linear_fn_valid = space.GetInverseTransferFunction(&from_linear_fn); |
| 842 |
| 843 ColorTransformFromLinear from_linear_transform(transfer, from_linear_fn, |
| 844 from_linear_fn_valid); |
| 845 TriStim color(v, v, v); |
| 846 from_linear_transform.transform(&color, 1); |
| 847 return color.x(); |
| 848 } |
| 849 |
| 1043 } // namespace gfx | 850 } // namespace gfx |
| OLD | NEW |