blob: 4b571cb872ab889d369167f6a459581776904831 [file] [log] [blame]
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_LAB_COLOR_SPACE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_LAB_COLOR_SPACE_H_
#include <algorithm>
#include <cmath>
#include "base/check.h"
#include "third_party/skia/include/core/SkM44.h"
namespace blink {
// TODO(prashant.n): Hide implementation details to .cc file.
// Namespace to handle color transformation between RGB and CIE L*a*b* color
// spaces.
namespace lab {
static constexpr SkV3 kIlluminantD50 = {0.964212f, 1.0f, 0.825188f};
static constexpr SkV3 kIlluminantD65 = {0.95042855f, 1.0f, 1.0889004f};
// All matrices here are 3x3 matrices.
// They are stored in SkM44 which is 4x4 matrix in the following form.
// |a b c 0|
// |d e f 0|
// |g h i 0|
// |0 0 0 1|
template <typename T>
inline constexpr T Clamp(T x, T min, T max) {
return x < min ? min : x > max ? max : x;
}
// See https://en.wikipedia.org/wiki/Chromatic_adaptation#Von_Kries_transform.
inline SkM44 ChromaticAdaptation(const SkM44& matrix,
const SkV3& src_white_point,
const SkV3& dst_white_point) {
SkV3 src_lms = matrix * src_white_point;
SkV3 dst_lms = matrix * dst_white_point;
// |lms| is a diagonal matrix stored as a float[3].
SkV3 lms = {dst_lms.x / src_lms.x, dst_lms.y / src_lms.y,
dst_lms.z / src_lms.z};
SkM44 inverse;
bool success = matrix.invert(&inverse);
DCHECK(success);
return inverse * (SkM44::Scale(lms.x, lms.y, lms.z) * matrix);
}
class DarkModeSRGBColorSpace {
public:
DarkModeSRGBColorSpace() {
bool success = transform_.invert(&inverseTransform_);
DCHECK(success);
}
SkV3 ToLinear(const SkV3& v) const {
auto EOTF = [](float u) {
return u < 0.04045f
? Clamp(u / 12.92f, .0f, 1.0f)
: Clamp(std::pow((u + 0.055f) / 1.055f, 2.4f), .0f, 1.0f);
};
return {EOTF(v.x), EOTF(v.y), EOTF(v.z)};
}
SkV3 FromLinear(const SkV3& v) const {
auto OETF = [](float u) {
return (u < 0.0031308f
? Clamp(12.92 * u, .0, 1.0)
: Clamp(1.055 * std::pow(u, 1.0 / 2.4) - 0.055, .0, 1.0));
};
return {OETF(v.x), OETF(v.y), OETF(v.z)};
}
// See https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation.
SkV3 ToXYZ(const SkV3& rgb) const { return transform_ * ToLinear(rgb); }
// See
// https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_(CIE_XYZ_to_sRGB).
SkV3 FromXYZ(const SkV3& xyz) const {
return FromLinear(inverseTransform_ * xyz);
}
private:
SkM44 kBradford = SkM44(0.8951f,
0.2664f,
-0.1614f,
0.0f,
-0.7502f,
1.7135f,
0.0367f,
0.0f,
0.0389f,
0.0685f,
1.0296f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f);
SkM44 xyzTransform = SkM44(0.41238642f,
0.3575915f,
0.18045056f,
0.0f,
0.21263677f,
0.715183f,
0.07218022f,
0.0f,
0.019330615f,
0.11919712f,
0.95037293f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f);
SkM44 transform_ =
ChromaticAdaptation(kBradford, kIlluminantD65, kIlluminantD50) *
xyzTransform;
SkM44 inverseTransform_;
};
class DarkModeLABColorSpace {
public:
// See
// https://en.wikipedia.org/wiki/CIELAB_color_space#Reverse_transformation.
SkV3 FromXYZ(const SkV3& v) const {
auto f = [](float x) {
return x > kSigma3 ? pow(x, 1.0f / 3.0f)
: x / (3 * kSigma2) + 4.0f / 29.0f;
};
float fx = f(v.x / kIlluminantD50.x);
float fy = f(v.y / kIlluminantD50.y);
float fz = f(v.z / kIlluminantD50.z);
float L = 116.0f * fy - 16.0f;
float a = 500.0f * (fx - fy);
float b = 200.0f * (fy - fz);
return {Clamp(L, 0.0f, 100.0f), Clamp(a, -128.0f, 128.0f),
Clamp(b, -128.0f, 128.0f)};
}
// See
// https://en.wikipedia.org/wiki/CIELAB_color_space#Forward_transformation.
SkV3 ToXYZ(const SkV3& lab) const {
auto invf = [](float x) {
return x > kSigma ? pow(x, 3) : 3 * kSigma2 * (x - 4.0f / 29.0f);
};
SkV3 v = {Clamp(lab.x, 0.0f, 100.0f), Clamp(lab.y, -128.0f, 128.0f),
Clamp(lab.z, -128.0f, 128.0f)};
return {invf((v.x + 16.0f) / 116.0f + (v.y * 0.002f)) * kIlluminantD50.x,
invf((v.x + 16.0f) / 116.0f) * kIlluminantD50.y,
invf((v.x + 16.0f) / 116.0f - (v.z * 0.005f)) * kIlluminantD50.z};
}
private:
static const constexpr float kSigma = 6.0f / 29.0f;
static const constexpr float kSigma2 = 36.0f / 841.0f;
static const constexpr float kSigma3 = 216.0f / 24389.0f;
};
class DarkModeSRGBLABTransformer {
public:
SkV3 SRGBToLAB(const SkV3& rgb) const {
SkV3 xyz = srgb_space_.ToXYZ(rgb);
return lab_space_.FromXYZ(xyz);
}
SkV3 LABToSRGB(const SkV3& lab) const {
SkV3 xyz = lab_space_.ToXYZ(lab);
return srgb_space_.FromXYZ(xyz);
}
private:
DarkModeSRGBColorSpace srgb_space_ = DarkModeSRGBColorSpace();
DarkModeLABColorSpace lab_space_ = DarkModeLABColorSpace();
};
} // namespace lab
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_LAB_COLOR_SPACE_H_