Commit c59fe416 authored by Prashant Nevase's avatar Prashant Nevase Committed by Commit Bot

Remove blink dependency in Lab color space.

Replace blink::FloatPoint3D by SkV3 and blink::TransformationMatrix by
SkM44.

Bug: 1091095

Change-Id: I4d5c386757365cd2e7dee8d5dcd5cb27c4576131
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2212226Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Prashant Nevase <prashant.n@samsung.com>
Cr-Commit-Position: refs/heads/master@{#802409}
parent 7c3fe555
......@@ -60,18 +60,16 @@ class LabColorFilter : public DarkModeColorFilter {
}
SkColor InvertColor(SkColor color) const override {
blink::FloatPoint3D rgb = {SkColorGetR(color) / 255.0f,
SkColorGetG(color) / 255.0f,
SkColorGetB(color) / 255.0f};
blink::FloatPoint3D lab = transformer_.sRGBToLab(rgb);
float invertedL = std::min(110.0f - lab.X(), 100.0f);
lab.SetX(invertedL);
SkV3 rgb = {SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f,
SkColorGetB(color) / 255.0f};
SkV3 lab = transformer_.sRGBToLab(rgb);
lab.x = std::min(110.0f - lab.x, 100.0f);
rgb = transformer_.LabToSRGB(lab);
SkColor inverted_color = SkColorSetARGB(
SkColorGetA(color), static_cast<unsigned int>(rgb.X() * 255 + 0.5),
static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
static_cast<unsigned int>(rgb.Z() * 255 + 0.5));
SkColorGetA(color), static_cast<unsigned int>(rgb.x * 255 + 0.5),
static_cast<unsigned int>(rgb.y * 255 + 0.5),
static_cast<unsigned int>(rgb.z * 255 + 0.5));
return AdjustGray(inverted_color);
}
......
......@@ -3,167 +3,176 @@
#include <algorithm>
#include <cmath>
#include <initializer_list>
#include <iterator>
#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
#include "base/check.h"
#include "third_party/skia/include/core/SkM44.h"
// Class to handle color transformation between RGB and CIE L*a*b* color spaces.
namespace LabColorSpace {
using blink::FloatPoint3D;
using blink::TransformationMatrix;
static constexpr FloatPoint3D kIlluminantD50 =
FloatPoint3D(0.964212f, 1.0f, 0.825188f);
static constexpr FloatPoint3D kIlluminantD65 =
FloatPoint3D(0.95042855f, 1.0f, 1.0889004f);
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 blink::TransformationMatrix which is 4x4 matrix in the
// following form.
// 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|
inline TransformationMatrix mul3x3Diag(const FloatPoint3D& lhs,
const TransformationMatrix& rhs) {
return TransformationMatrix(
lhs.X() * rhs.M11(), lhs.Y() * rhs.M12(), lhs.Z() * rhs.M13(), 0.0f,
lhs.X() * rhs.M21(), lhs.Y() * rhs.M22(), lhs.Z() * rhs.M23(), 0.0f,
lhs.X() * rhs.M31(), lhs.Y() * rhs.M32(), lhs.Z() * rhs.M33(), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
template <typename T>
inline constexpr T clamp(T x, T min, T max) {
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 TransformationMatrix chromaticAdaptation(
const TransformationMatrix& matrix,
const FloatPoint3D& srcWhitePoint,
const FloatPoint3D& dstWhitePoint) {
FloatPoint3D srcLMS = matrix.MapPoint(srcWhitePoint);
FloatPoint3D dstLMS = matrix.MapPoint(dstWhitePoint);
// LMS is a diagonal matrix stored as a float[3]
FloatPoint3D LMS = {dstLMS.X() / srcLMS.X(), dstLMS.Y() / srcLMS.Y(),
dstLMS.Z() / srcLMS.Z()};
return matrix.Inverse() * mul3x3Diag(LMS, matrix);
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 sRGBColorSpace {
public:
FloatPoint3D toLinear(const FloatPoint3D& v) const {
sRGBColorSpace() {
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);
? 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())};
return {EOTF(v.x), EOTF(v.y), EOTF(v.z)};
}
FloatPoint3D fromLinear(const FloatPoint3D& v) const {
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));
? 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())};
return {OETF(v.x), OETF(v.y), OETF(v.z)};
}
// See https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation.
FloatPoint3D toXYZ(const FloatPoint3D& rgb) const {
return transform_.MapPoint(toLinear(rgb));
}
SkV3 ToXYZ(const SkV3& rgb) const { return transform_ * ToLinear(rgb); }
// See
// https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_(CIE_XYZ_to_sRGB).
FloatPoint3D fromXYZ(const FloatPoint3D& xyz) const {
return fromLinear(inverseTransform_.MapPoint(xyz));
SkV3 FromXYZ(const SkV3& xyz) const {
return FromLinear(inverseTransform_ * xyz);
}
private:
TransformationMatrix kBradford = TransformationMatrix(
0.8951f, -0.7502f, 0.0389f, 0.0f,
0.2664f, 1.7135f, -0.0685f, 0.0f,
-0.1614f, 0.0367f, 1.0296f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
TransformationMatrix xyzTransform = TransformationMatrix(
0.41238642f, 0.21263677f, 0.019330615f, 0.0f,
0.3575915f, 0.715183f, 0.11919712f, 0.0f,
0.18045056f, 0.07218022f, 0.95037293f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
TransformationMatrix transform_ =
chromaticAdaptation(kBradford, kIlluminantD65, kIlluminantD50) *
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;
TransformationMatrix inverseTransform_ = transform_.Inverse();
SkM44 inverseTransform_;
};
class LABColorSpace {
public:
// See
// https://en.wikipedia.org/wiki/CIELAB_color_space#Reverse_transformation.
FloatPoint3D fromXYZ(const FloatPoint3D& v) const {
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 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)};
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.
FloatPoint3D toXYZ(const FloatPoint3D& lab) const {
SkV3 ToXYZ(const SkV3& lab) const {
auto invf = [](float x) {
return x > kSigma ? pow(x, 3) : 3 * kSigma2 * (x - 4.0f / 29.0f);
};
FloatPoint3D v = {clamp(lab.X(), 0.0f, 100.0f),
clamp(lab.Y(), -128.0f, 128.0f),
clamp(lab.Z(), -128.0f, 128.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()};
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 constexpr float kSigma = 6.0f / 29.0f;
static constexpr float kSigma2 = 36.0f / 841.0f;
static constexpr float kSigma3 = 216.0f / 24389.0f;
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 RGBLABTransformer {
public:
FloatPoint3D sRGBToLab(const FloatPoint3D& rgb) const {
FloatPoint3D xyz = rcs.toXYZ(rgb);
return lcs.fromXYZ(xyz);
SkV3 sRGBToLab(const SkV3& rgb) const {
SkV3 xyz = rgb_space_.ToXYZ(rgb);
return lab_space_.FromXYZ(xyz);
}
FloatPoint3D LabToSRGB(const FloatPoint3D& lab) const {
FloatPoint3D xyz = lcs.toXYZ(lab);
return rcs.fromXYZ(xyz);
SkV3 LabToSRGB(const SkV3& lab) const {
SkV3 xyz = lab_space_.ToXYZ(lab);
return rgb_space_.FromXYZ(xyz);
}
private:
sRGBColorSpace rcs = sRGBColorSpace();
LABColorSpace lcs = LABColorSpace();
sRGBColorSpace rgb_space_ = sRGBColorSpace();
LABColorSpace lab_space_ = LABColorSpace();
};
} // namespace LabColorSpace
......
......@@ -3,21 +3,16 @@
namespace LabColorSpace {
using blink::FloatPoint3D;
static constexpr FloatPoint3D rgbReferenceWhite =
FloatPoint3D(1.0f, 1.0f, 1.0f);
static constexpr FloatPoint3D labReferenceWhite =
FloatPoint3D(100.0f, 0.0f, 0.0f);
static constexpr SkV3 rgbReferenceWhite = {1.0f, 1.0f, 1.0f};
static constexpr SkV3 labReferenceWhite = {100.0f, 0.0f, 0.0f};
static constexpr float epsilon = 0.0001;
class LabColorSpaceTest : public testing::Test {
public:
void AssertColorsEqual(const FloatPoint3D& color1,
const FloatPoint3D& color2) {
EXPECT_NEAR(color1.X(), color2.X(), epsilon);
EXPECT_NEAR(color1.Y(), color2.Y(), epsilon);
EXPECT_NEAR(color1.Z(), color2.Z(), epsilon);
void AssertColorsEqual(const SkV3& color1, const SkV3& color2) {
EXPECT_NEAR(color1.x, color2.x, epsilon);
EXPECT_NEAR(color1.y, color2.y, epsilon);
EXPECT_NEAR(color1.z, color2.z, epsilon);
}
};
......@@ -25,10 +20,10 @@ TEST_F(LabColorSpaceTest, XYZTranslation) {
sRGBColorSpace colorSpace = sRGBColorSpace();
// Check whether white transformation is correct
FloatPoint3D xyzWhite = colorSpace.toXYZ(rgbReferenceWhite);
SkV3 xyzWhite = colorSpace.ToXYZ(rgbReferenceWhite);
AssertColorsEqual(xyzWhite, kIlluminantD50);
FloatPoint3D rgbWhite = colorSpace.fromXYZ(kIlluminantD50);
SkV3 rgbWhite = colorSpace.FromXYZ(kIlluminantD50);
AssertColorsEqual(rgbWhite, rgbReferenceWhite);
// Check whether transforming sRGB to XYZ and back gives the same RGB values
......@@ -36,9 +31,9 @@ TEST_F(LabColorSpaceTest, XYZTranslation) {
for (unsigned r = 0; r <= 255; r += 40) {
for (unsigned g = 0; r <= 255; r += 50) {
for (unsigned b = 0; r <= 255; r += 60) {
FloatPoint3D rgb = FloatPoint3D(r / 255.0f, g / 255.0f, b / 255.0f);
FloatPoint3D xyz = colorSpace.toXYZ(rgb);
AssertColorsEqual(rgb, colorSpace.fromXYZ(xyz));
SkV3 rgb = {r / 255.0f, g / 255.0f, b / 255.0f};
SkV3 xyz = colorSpace.ToXYZ(rgb);
AssertColorsEqual(rgb, colorSpace.FromXYZ(xyz));
}
}
}
......@@ -48,10 +43,10 @@ TEST_F(LabColorSpaceTest, LabTranslation) {
RGBLABTransformer transformer = RGBLABTransformer();
// Check whether white transformation is correct
FloatPoint3D labWhite = transformer.sRGBToLab(rgbReferenceWhite);
SkV3 labWhite = transformer.sRGBToLab(rgbReferenceWhite);
AssertColorsEqual(labWhite, labReferenceWhite);
FloatPoint3D rgbWhite = transformer.LabToSRGB(labReferenceWhite);
SkV3 rgbWhite = transformer.LabToSRGB(labReferenceWhite);
AssertColorsEqual(rgbWhite, rgbReferenceWhite);
// Check whether transforming sRGB to Lab and back gives the same RGB values
......@@ -59,8 +54,8 @@ TEST_F(LabColorSpaceTest, LabTranslation) {
for (unsigned r = 0; r <= 255; r += 40) {
for (unsigned g = 0; r <= 255; r += 50) {
for (unsigned b = 0; r <= 255; r += 60) {
FloatPoint3D rgb = FloatPoint3D(r / 255.0f, g / 255.0f, b / 255.0f);
FloatPoint3D lab = transformer.sRGBToLab(rgb);
SkV3 rgb = {r / 255.0f, g / 255.0f, b / 255.0f};
SkV3 lab = transformer.sRGBToLab(rgb);
AssertColorsEqual(rgb, transformer.LabToSRGB(lab));
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment