Chromium Code Reviews| Index: ui/gfx/color_transform.cc |
| diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fdf4a2926af36ea997be107532494a1e9efadc67 |
| --- /dev/null |
| +++ b/ui/gfx/color_transform.cc |
| @@ -0,0 +1,609 @@ |
| +// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ui/gfx/color_transform.h" |
| + |
| +#include <vector> |
| + |
| +#include "base/logging.h" |
| +#include "ui/gfx/color_space.h" |
| +#include "ui/gfx/transform.h" |
| + |
| +namespace gfx { |
| + |
| +Transform Invert(const Transform& t) { |
| + Transform ret = t; |
| + if (!t.GetInverse(&ret)) { |
| + LOG(ERROR) << "Inverse should alsways be possible."; |
| + } |
| + return ret; |
| +} |
| + |
| +ColorTransform::TriStim Map(const Transform& t, ColorTransform::TriStim color) { |
| + t.TransformPoint(&color); |
| + return color; |
| +} |
| + |
| +// XY color coordinate |
| +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.
|
| + |
| +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
|
| + std::vector<XY> ret(4); |
| + switch (id) { |
| + default: |
| + // If we don't know, assume BT709 |
| + |
| + case ColorSpace::PrimaryID::BT709: |
| + // Red |
| + ret[0].first = 0.640; |
| + ret[0].second = 0.330; |
| + // Green |
| + ret[1].first = 0.300; |
| + ret[1].second = 0.600; |
| + // Blue |
| + ret[2].first = 0.150; |
| + ret[2].second = 0.060; |
| + // Whitepoint (D65) |
| + ret[3].first = 0.3127; |
| + ret[3].second = 0.3290; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::BT470M: |
| + // Red |
| + ret[0].first = 0.67; |
| + ret[0].second = 0.33; |
| + // Green |
| + ret[1].first = 0.21; |
| + ret[1].second = 0.71; |
| + // Blue |
| + ret[2].first = 0.14; |
| + ret[2].second = 0.08; |
| + // Whitepoint |
| + ret[3].first = 0.31; |
| + ret[3].second = 0.316; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::BT470BG: |
| + // Red |
| + ret[0].first = 0.64; |
| + ret[0].second = 0.33; |
| + // Green |
| + ret[1].first = 0.29; |
| + ret[1].second = 0.60; |
| + // Blue |
| + ret[2].first = 0.15; |
| + ret[2].second = 0.06; |
| + // Whitepoint (D65) |
| + ret[3].first = 0.3127; |
| + ret[3].second = 0.3290; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::SMPTE170M: |
| + case ColorSpace::PrimaryID::SMPTE240M: |
| + // Red |
| + ret[0].first = 0.630; |
| + ret[0].second = 0.340; |
| + // Green |
| + ret[1].first = 0.310; |
| + ret[1].second = 0.595; |
| + // Blue |
| + ret[2].first = 0.155; |
| + ret[2].second = 0.070; |
| + // Whitepoint (D65) |
| + ret[3].first = 0.3127; |
| + ret[3].second = 0.3290; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::FILM: |
| + // Red |
| + ret[0].first = 0.681; |
| + ret[0].second = 0.319; |
| + // Green |
| + ret[1].first = 0.243; |
| + ret[1].second = 0.692; |
| + // Blue |
| + ret[2].first = 0.145; |
| + ret[2].second = 0.049; |
| + // Whitepoint (C) |
| + ret[3].first = 0.310; |
| + ret[3].second = 0.136; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::BT2020: |
| + // Red |
| + ret[0].first = 0.708; |
| + ret[0].second = 0.292; |
| + // Green |
| + ret[1].first = 0.170; |
| + ret[1].second = 0.797; |
| + // Blue |
| + ret[2].first = 0.131; |
| + ret[2].second = 0.046; |
| + // Whitepoint (D65) |
| + ret[3].first = 0.3127; |
| + ret[3].second = 0.3290; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::SMPTEST428_1: |
| + // X |
| + ret[0].first = 1.0; |
| + ret[0].second = 0.0; |
| + // Y |
| + ret[1].first = 0.0; |
| + ret[1].second = 1.0; |
| + // Z |
| + ret[2].first = 0.0; |
| + ret[2].second = 0.0; |
| + // Whitepoint (E) |
| + ret[3].first = 1.0f / 3.0f; |
| + ret[3].second = 1.0f / 3.0f; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::SMPTEST431_2: |
| + // Red |
| + ret[0].first = 0.680; |
| + ret[0].second = 0.320; |
| + // Green |
| + ret[1].first = 0.265; |
| + ret[1].second = 0.690; |
| + // Blue |
| + ret[2].first = 0.150; |
| + ret[2].second = 0.060; |
| + // Whitepoint |
| + ret[3].first = 0.314; |
| + ret[3].second = 0.351; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::SMPTEST432_1: |
| + // Red |
| + ret[0].first = 0.680; |
| + ret[0].second = 0.320; |
| + // Green |
| + ret[1].first = 0.265; |
| + ret[1].second = 0.690; |
| + // Blue |
| + ret[2].first = 0.150; |
| + ret[2].second = 0.060; |
| + // Whitepoint (D65) |
| + ret[3].first = 0.3127; |
| + ret[3].second = 0.3290; |
| + break; |
| + |
| + case ColorSpace::PrimaryID::XYZ_D50: |
| + // X |
| + ret[0].first = 1.0; |
| + ret[0].second = 0.0; |
| + // Y |
| + ret[1].first = 0.0; |
| + ret[1].second = 1.0; |
| + // Z |
| + ret[2].first = 0.0; |
| + ret[2].second = 0.0; |
| + // D50 |
| + ret[3].first = 0.34567; |
| + ret[3].second = 0.35850; |
| + break; |
| + } |
| + return ret; |
| +} |
| + |
| +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
|
| + return ColorTransform::TriStim(xy.first, xy.second, |
| + 1.0f - xy.first - xy.second); |
| +} |
| + |
| +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.
|
| + std::vector<XY> primaries = GetPrimaries(id); |
| + ColorTransform::TriStim Rxyz = xy2xyz(primaries[0]); |
| + ColorTransform::TriStim Gxyz = xy2xyz(primaries[1]); |
| + ColorTransform::TriStim Bxyz = xy2xyz(primaries[2]); |
| + ColorTransform::TriStim Wxyz = xy2xyz(primaries[3]); |
|
ccameron
2016/08/02 17:12:57
Y was wondering why we didn't call them just "prim
|
| + ColorTransform::TriStim WXYZ(Wxyz.x() / Wxyz.y(), 1.0f, Wxyz.z() / Wxyz.y()); |
| + |
| + Transform tmp(Rxyz.x(), Gxyz.x(), Bxyz.x(), 0.0f, Rxyz.y(), Gxyz.y(), |
| + Bxyz.y(), 0.0f, Rxyz.z(), Gxyz.z(), Bxyz.z(), 0.0f, 0.0f, 0.0f, |
| + 0.0f, 1.0f); |
| + |
| + ColorTransform::TriStim conv = Map(Invert(tmp), WXYZ); |
| + Transform ret( |
| + conv.x() * tmp.matrix().get(0, 0), conv.y() * tmp.matrix().get(0, 1), |
| + conv.z() * tmp.matrix().get(0, 2), 0.0f, |
| + conv.x() * tmp.matrix().get(1, 0), conv.y() * tmp.matrix().get(1, 1), |
| + conv.z() * tmp.matrix().get(1, 2), 0.0f, |
| + conv.x() * tmp.matrix().get(2, 0), conv.y() * tmp.matrix().get(2, 1), |
| + conv.z() * tmp.matrix().get(2, 2), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); |
| + |
| + // Chromatic adaptation. |
| + Transform bradford(0.8951000f, 0.2664000f, -0.1614000f, 0.0f, -0.7502000f, |
| + 1.7135000f, 0.0367000f, 0.0f, 0.0389000f, -0.0685000f, |
| + 1.0296000f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); |
| + |
| + Transform bradford_inv = Invert(bradford); |
| + |
| + ColorTransform::TriStim D50(0.9642, 1.0000, 0.8249); |
| + ColorTransform::TriStim source_response = Map(bradford, WXYZ); |
| + ColorTransform::TriStim dest_response = Map(bradford, D50); |
| + |
| + Transform adapter(dest_response.x() / source_response.x(), 0.0f, 0.0f, 0.0f, |
| + 0.0f, dest_response.y() / source_response.y(), 0.0f, 0.0f, |
| + 0.0f, 0.0f, dest_response.z() / source_response.z(), 0.0f, |
| + 0.0f, 0.0f, 0.0f, 1.0f); |
| + |
| + return bradford * adapter * bradford_inv * ret; |
| +} |
| + |
| +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.
|
| + switch (id) { |
| + default: |
| + case ColorSpace::TransferID::BT709: |
| + case ColorSpace::TransferID::SMPTE170M: |
| + case ColorSpace::TransferID::BT2020_10: |
| + case ColorSpace::TransferID::BT2020_12: { |
| + v = fmax(0.0f, v); |
| + float a = 1.099296826809442f; |
| + float b = 0.018053968510807; |
| + if (v <= b) { |
| + return 4.5f * v; |
| + } else { |
| + return a * pow(v, 0.45f) - (a - 1.0f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::GAMMA22: |
| + v = fmax(0.0f, v); |
| + return pow(v, 1.0f / 2.2f); |
| + |
| + case ColorSpace::TransferID::GAMMA28: |
| + v = fmax(0.0f, v); |
| + return pow(v, 1.0f / 2.8f); |
| + |
| + case ColorSpace::TransferID::SMPTE240M: { |
| + v = fmax(0.0f, v); |
| + float a = 1.11157219592173128753f; |
| + float b = 0.02282158552944503135f; |
| + if (v <= b) { |
| + return 4.0f * v; |
| + } else { |
| + return a * pow(v, 0.45f) - (a - 1.0f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::LINEAR: |
| + return v; |
| + |
| + case ColorSpace::TransferID::LOG: |
| + if (v < 0.01) |
| + return 0.0; |
| + return 1.0 + log(v) / log(10.0f) / 2.0f; |
| + |
| + case ColorSpace::TransferID::LOG_SQRT: |
| + if (v < sqrt(10.0f) / 1000.0) |
| + return 0.0; |
| + return 1.0 + log(v) / log(10.0f) / 2.5f; |
| + |
| + case ColorSpace::TransferID::IEC61966_2_4: { |
| + float a = 1.099296826809442f; |
| + float b = 0.018053968510807f; |
| + if (v < -b) { |
| + return -a * pow(-v, 0.45) + (a - 1.0f); |
| + } else if (v <= b) { |
| + return 4.5 * v; |
| + } else { |
| + return a * pow(v, 0.45) - (a - 1.0f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::BT1361_ECG: { |
| + float a = 1.099; |
| + float b = 0.018; |
| + float l = 0.0045; |
| + if (v < -l) { |
| + return -(a * pow(-4.0f * v, 0.45) + (a - 1.0f)) / 4.0f; |
| + } else if (v <= b) { |
| + return 4.5 * v; |
| + } else { |
| + return a * pow(v, 0.45) - (a - 1.0f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::IEC61966_2_1: { // SRGB |
| + v = fmax(0.0f, v); |
| + float a = 1.055f; |
| + float b = 0.0031308f; |
| + if (v < b) { |
| + return 12.92f * v; |
| + } else { |
| + return a * pow(v, 1.0f / 2.4f) - (a - 1.0f); |
| + } |
| + } |
| + case ColorSpace::TransferID::SMPTEST2084: { |
| + v = fmax(0.0f, v); |
| + float m1 = (2610.0 / 4096.0) / 4.0; |
| + float m2 = (2523.0 / 4096.0) * 128.0; |
| + float c1 = 3424.0 / 4096.0; |
| + float c2 = (2413.0 / 4096.0) * 32.0; |
| + float c3 = (2392.0 / 4096.0) * 32.0; |
| + return pow((c1 + c2 * pow(v, m1)) / (1.0f + c3 * pow(v, m1)), m2); |
| + } |
| + |
| + case ColorSpace::TransferID::SMPTEST428_1: |
| + v = fmax(0.0f, v); |
| + return pow(48.0f * v + 52.37f, 1.0f / 2.6f); |
| + |
| + // Chrome-specific values below |
| + case ColorSpace::TransferID::GAMMA24: |
| + v = fmax(0.0f, v); |
| + return pow(v, 1.0f / 2.4f); |
| + } |
| +} |
| + |
| +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.
|
| + switch (id) { |
| + default: |
| + case ColorSpace::TransferID::BT709: |
| + case ColorSpace::TransferID::SMPTE170M: |
| + case ColorSpace::TransferID::BT2020_10: |
| + case ColorSpace::TransferID::BT2020_12: { |
| + v = fmax(0.0f, v); |
| + float a = 1.099296826809442f; |
| + float b = 0.018053968510807; |
| + if (v < fromLinear(ColorSpace::TransferID::BT709, b)) { |
| + return v / 4.5f; |
| + } else { |
| + return pow((v + a - 1.0f) / a, 1.0f / 0.45f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::GAMMA22: |
| + v = fmax(0.0f, v); |
| + return pow(v, 2.2f); |
| + |
| + case ColorSpace::TransferID::GAMMA28: |
| + v = fmax(0.0f, v); |
| + return pow(v, 2.8f); |
| + |
| + case ColorSpace::TransferID::SMPTE240M: { |
| + v = fmax(0.0f, v); |
| + float a = 1.11157219592173128753f; |
| + float b = 0.02282158552944503135f; |
| + if (v <= fromLinear(ColorSpace::TransferID::SMPTE240M, b)) { |
| + return v / 4.0f; |
| + } else { |
| + return pow((v + a - 1.0f) / a, 1.0f / 0.45f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::LINEAR: |
| + return v; |
| + |
| + case ColorSpace::TransferID::LOG: |
| + if (v < 0.0) |
| + return 0.0; |
| + return pow(10.0, (v - 1.0f) * 2.0f); |
| + |
| + case ColorSpace::TransferID::LOG_SQRT: |
| + if (v < 0.0) |
| + return 0.0; |
| + return pow(10.0, (v - 1.0f) * 2.5f); |
| + |
| + case ColorSpace::TransferID::IEC61966_2_4: { |
| + float a = 1.099296826809442f; |
| + float b = 0.018053968510807f; |
| + if (v < fromLinear(ColorSpace::TransferID::IEC61966_2_4, -a)) { |
| + return -pow((a - 1.0f - v) / a, 1.0f / 0.45f); |
| + } else if (v <= fromLinear(ColorSpace::TransferID::IEC61966_2_4, b)) { |
| + return v / 4.5f; |
| + } else { |
| + return pow((v + a - 1.0f) / a, 1.0f / 0.45f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::BT1361_ECG: { |
| + float a = 1.099; |
| + float b = 0.018; |
| + float l = 0.0045; |
| + if (v < fromLinear(ColorSpace::TransferID::BT1361_ECG, -l)) { |
| + return -pow((1.0f - a - v * 4.0) / a, 1.0f / 0.45f) / 4.0f; |
| + } else if (v <= fromLinear(ColorSpace::TransferID::BT1361_ECG, b)) { |
| + return v / 4.5f; |
| + } else { |
| + return pow((v + a - 1.0f) / a, 1.0f / 0.45f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::IEC61966_2_1: { // SRGB |
| + v = fmax(0.0f, v); |
| + float a = 1.055f; |
| + float b = 0.0031308f; |
| + if (v < fromLinear(ColorSpace::TransferID::IEC61966_2_1, b)) { |
| + return v / 12.92f; |
| + } else { |
| + return pow((v + a - 1.0f) / a, 2.4f); |
| + } |
| + } |
| + |
| + case ColorSpace::TransferID::SMPTEST2084: { |
| + v = fmax(0.0f, v); |
| + float m1 = (2610.0f / 4096.0f) / 4.0f; |
| + float m2 = (2523.0f / 4096.0f) * 128.0f; |
| + float c1 = 3424.0f / 4096.0f; |
| + float c2 = (2413.0f / 4096.0f) * 32.0f; |
| + float c3 = (2392.0f / 4096.0f) * 32.0f; |
| + return pow(fmax(pow(v, 1.0 / m2) - c1, 0) / (c2 - c3 * pow(v, 1.0 / m2)), |
| + 1.0f / m1); |
| + } |
| + |
| + case ColorSpace::TransferID::SMPTEST428_1: |
| + return (pow(v, 2.6f) - 52.37f) / 48.0f; |
| + |
| + // Chrome-specific values below |
| + case ColorSpace::TransferID::GAMMA24: |
| + v = fmax(0.0f, v); |
| + return pow(v, 2.4f); |
| + } |
| +} |
| + |
| +GFX_EXPORT Transform GetTransferMatrix(ColorSpace::MatrixID id) { |
| + float Kr, Kb; |
| + switch (id) { |
| + case ColorSpace::MatrixID::RGB: |
| + return Transform(); |
| + |
| + case ColorSpace::MatrixID::BT709: |
| + case ColorSpace::MatrixID::UNSPECIFIED: |
| + case ColorSpace::MatrixID::RESERVED: |
| + Kr = 0.2126f; |
| + Kb = 0.0722f; |
| + break; |
| + |
| + case ColorSpace::MatrixID::FCC: |
| + Kr = 0.30f; |
| + Kb = 0.11f; |
| + break; |
| + |
| + case ColorSpace::MatrixID::BT470BG: |
| + case ColorSpace::MatrixID::SMPTE170M: |
| + Kr = 0.299f; |
| + Kb = 0.144f; |
| + break; |
| + |
| + case ColorSpace::MatrixID::SMPTE240M: |
| + Kr = 0.212f; |
| + Kb = 0.087f; |
| + break; |
| + |
| + case ColorSpace::MatrixID::YCOCG: |
| + return Transform(0.25f, 0.5f, 0.25f, 0.5, // 1 |
| + -0.25f, 0.5f, -0.25f, 0.5, // 2 |
| + 0.5f, 0.0f, -0.5f, 0.0, // 3 |
| + 0.0f, 0.0f, 0.0f, 1.0f); // 4 |
| + |
| + // TODO(hubbe): Check if the CL equation is right. |
| + case ColorSpace::MatrixID::BT2020_NCL: |
| + case ColorSpace::MatrixID::BT2020_CL: |
| + Kr = 0.2627f; |
| + Kb = 0.0593f; |
| + break; |
| + |
| + case ColorSpace::MatrixID::YDZDX: |
| + return Transform(0.0f, 1.0f, 0.0, 0.0f, // 1 |
| + 0.0f, -0.5f, 0.986566f / 2.0f, 0.5f, // 2 |
| + 0.5f, -0.991902f / 2.0f, 0.0f, 0.5f, // 3 |
| + 0.0f, 0.0f, 0.0f, 1.0f); // 4 |
| + } |
| + float u_m = 0.5f / (1.0f - Kb); |
| + float v_m = 0.5f / (1.0f - Kr); |
| + return Transform( |
| + Kr, 1.0f - Kr - Kb, Kb, 0.0f, // 1 |
| + u_m * -Kr, u_m * -(1.0f - Kr - Kb), u_m * (1.0f - Kb), 0.5f, // 2 |
| + v_m * (1.0f - Kr), v_m * -(1.0f - Kr - Kb), v_m * -Kb, 0.5f, // 3 |
| + 0.0f, 0.0f, 0.0f, 1.0f); // 4 |
| +} |
| + |
| +Transform GetRangeAdjustMatrix(ColorSpace::RangeID range, |
| + ColorSpace::MatrixID matrix) { |
| + switch (range) { |
| + case ColorSpace::RangeID::FULL: |
| + return Transform(); |
| + |
| + case ColorSpace::RangeID::LIMITED: |
| + break; |
| + } |
| + switch (matrix) { |
| + case ColorSpace::MatrixID::RGB: |
| + case ColorSpace::MatrixID::YCOCG: |
| + return Transform(255.0f / 219.0f, 0.0f, 0.0f, -16.0f / 219.0f, // 1 |
| + 0.0f, 255.0f / 219.0f, 0.0f, -16.0f / 219.0f, // 2 |
| + 0.0f, 0.0f, 255.0f / 219.0f, -16.0f / 219.0f, // 3 |
| + 0.0f, 0.0f, 0.0f, 1.0f); // 4 |
| + |
| + case ColorSpace::MatrixID::BT709: |
| + case ColorSpace::MatrixID::UNSPECIFIED: |
| + case ColorSpace::MatrixID::RESERVED: |
| + case ColorSpace::MatrixID::FCC: |
| + case ColorSpace::MatrixID::BT470BG: |
| + case ColorSpace::MatrixID::SMPTE170M: |
| + case ColorSpace::MatrixID::SMPTE240M: |
| + case ColorSpace::MatrixID::BT2020_NCL: |
| + case ColorSpace::MatrixID::BT2020_CL: |
| + case ColorSpace::MatrixID::YDZDX: |
| + return Transform(255.0f / 219.0f, 0.0f, 0.0f, -16.0f / 219.0f, // 1 |
| + 0.0f, 255.0f / 224.0f, 0.0f, -15.5f / 224.0f, // 2 |
| + 0.0f, 0.0f, 255.0f / 224.0f, -15.5f / 224.0f, // 3 |
| + 0.0f, 0.0f, 0.0f, 1.0f); // 4 |
| + } |
| +} |
| + |
| +class ColorSpaceToColorSpaceTransform : public ColorTransform { |
| + public: |
| + ColorSpaceToColorSpaceTransform(const ColorSpace& from, |
| + const ColorSpace& to, |
| + Intent intent) |
| + : from_(from), to_(to) { |
| + if (intent == Intent::PERCEPTUAL) { |
| + switch (from_.transfer_) { |
| + case ColorSpace::TransferID::UNSPECIFIED: |
| + case ColorSpace::TransferID::BT709: |
| + case ColorSpace::TransferID::SMPTE170M: |
| + // See SMPTE 1886 |
| + from_.transfer_ = ColorSpace::TransferID::GAMMA24; |
| + break; |
| + |
| + default: // Do nothing |
| + break; |
| + } |
| + |
| + // TODO(hubbe): shrink gamuts here (never stretch gamuts) |
| + } |
| + |
| + Transform* from_transfer_matrix = |
| + from_.matrix_ == ColorSpace::MatrixID::BT2020_CL ? &b_ : &a_; |
| + Transform* to_transfer_matrix = |
| + to_.matrix_ == ColorSpace::MatrixID::BT2020_CL ? &b_ : &c_; |
| + |
| + c_ *= Invert(GetRangeAdjustMatrix(to_.range_, to_.matrix_)); |
| + *to_transfer_matrix *= GetTransferMatrix(to_.matrix_); |
| + b_ *= Invert(GetPrimaryMatrix(to_.primaries_)); |
| + b_ *= GetPrimaryMatrix(from_.primaries_); |
| + *from_transfer_matrix *= Invert(GetTransferMatrix(from_.matrix_)); |
| + a_ *= GetRangeAdjustMatrix(from_.range_, from_.matrix_); |
| + } |
| + |
| + void transform(TriStim* colors, size_t num) override { |
| + for (size_t i = 0; i < num; i++) { |
| + TriStim c = colors[i]; |
| + a_.TransformPoint(&c); |
| + c.set_x(toLinear(from_.transfer_, c.x())); |
| + c.set_y(toLinear(from_.transfer_, c.y())); |
| + c.set_z(toLinear(from_.transfer_, c.z())); |
| + b_.TransformPoint(&c); |
| + c.set_x(fromLinear(to_.transfer_, c.x())); |
| + c.set_y(fromLinear(to_.transfer_, c.y())); |
| + c.set_z(fromLinear(to_.transfer_, c.z())); |
| + c_.TransformPoint(&c); |
| + colors[i] = c; |
| + } |
| + } |
| + |
| + private: |
| + ColorSpace from_; |
| + ColorSpace to_; |
| + |
| + // a_ -> tolinear -> b_ -> fromlinear -> c_; |
| + Transform a_; |
| + Transform b_; |
| + Transform c_; |
| +}; |
| + |
| +std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform( |
| + const ColorSpace& from, |
| + const ColorSpace& to, |
| + Intent intent) { |
| + // TODO(Hubbe): Check if from and/or to can be mapped to ICC profiles and |
| + // provide better transforms in those cases. |
| + return std::unique_ptr<ColorTransform>( |
| + new ColorSpaceToColorSpaceTransform(from, to, intent)); |
| +} |
| + |
| +} // namespace gfx |