Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
cielab.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping
4 * Conversion to C++ for Inkscape by Bob Jamison
5 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
6 */
7#include <algorithm>
8#include <cmath>
9#include "cielab.h"
10
11namespace Inkscape {
12namespace Trace {
13
14namespace {
15
19uint32_t getRGB(int r, int g, int b)
20{
21 r = std::clamp(r, 0, 255);
22 g = std::clamp(g, 0, 255);
23 b = std::clamp(b, 0, 255);
24 return (r << 16) | (g << 8) | b;
25}
26
30uint32_t getRGB(float r, float g, float b)
31{
32 return getRGB((int)(r * 256.0f),
33 (int)(g * 256.0f),
34 (int)(b * 256.0f));
35}
36
37//#########################################
38//# Root approximations for large speedup.
39//# By njh!
40//#########################################
41
42class Tables
43{
44public:
45 static int constexpr SIZE = 16;
46 float cbrt[SIZE + 1];
47 float qn[SIZE + 1];
48
49 static auto &get()
50 {
51 static Tables const instance;
52 return instance;
53 }
54
55private:
56 Tables();
57};
58
59template <typename T>
60auto sq(T t)
61{
62 return t * t;
63}
64
65// Cube root.
66double cbrt(double x)
67{
68 auto polish = [x] (double y) {
69 return (2.0 * y + x / sq(y)) / 3.0;
70 };
71 double y = Tables::get().cbrt[(int)(x * Tables::SIZE)]; // assuming x \in [0, 1]
72 y = polish(y);
73 y = polish(y);
74 return y;
75}
76
77// Quintic root.
78double qnrt(double x)
79{
80 auto polish = [x] (double y) {
81 return (4.0 * y + x / sq(sq(y))) / 5.0;
82 };
83 double y = Tables::get().qn[(int)(x * Tables::SIZE)]; // assuming x \in [0, 1]
84 y = polish(y);
85 y = polish(y);
86 return y;
87}
88
89double pow24(double x)
90{
91 return sq(x * qnrt(x));
92}
93
94Tables::Tables()
95{
96 auto entry = [&] (int i, float x) {
97 cbrt[i] = std::pow(x / SIZE, 0.3333f);
98 qn[i] = std::pow(x / SIZE, 0.2f);
99 };
100
101 entry(0, 0.5f);
102 for (int i = 1; i < SIZE + 1; i++) {
103 entry(i, i);
104 }
105}
106
107} // namespace
108
110{
111 uint8_t ir = (rgb >> 16) & 0xff;
112 uint8_t ig = (rgb >> 8) & 0xff;
113 uint8_t ib = (rgb ) & 0xff;
114
115 float fr = ir / 255.0f;
116 float fg = ig / 255.0f;
117 float fb = ib / 255.0f;
118
119 // printf("fr:%f fg:%f fb:%f\n", fr, fg, fb);
120
121 auto to_linear = [] (float &x) {
122 if (x > 0.04045) {
123 x = pow24((x + 0.055) / 1.055);
124 } else {
125 x /= 12.92;
126 }
127 };
128
129 to_linear(fr);
130 to_linear(fg);
131 to_linear(fb);
132
133 // Use white = D65
134 float x = fr * 0.4124 + fg * 0.3576 + fb * 0.1805;
135 float y = fr * 0.2126 + fg * 0.7152 + fb * 0.0722;
136 float z = fr * 0.0193 + fg * 0.1192 + fb * 0.9505;
137
138 float vx = x / 0.95047;
139 float vy = y;
140 float vz = z / 1.08883;
141
142 // printf("vx:%f vy:%f vz:%f\n", vx, vy, vz);
143
144 auto f = [] (float &x) {
145 if (x > 0.008856) {
146 x = cbrt(x);
147 } else {
148 x = (7.787 * x) + (16.0 / 116.0);
149 }
150 };
151
152 f(vx);
153 f(vy);
154 f(vz);
155
156 C = 0;
157 L = 116.0 * vy - 16.0;
158 A = 500.0 * (vx - vy);
159 B = 200.0 * (vy - vz);
160}
161
162uint32_t CieLab::toRGB() const
163{
164 float vy = (L + 16.0) / 116.0;
165 float vx = A / 500.0 + vy;
166 float vz = vy - B / 200.0;
167
168 auto finv = [] (float &x) {
169 float x3 = x * x * x;
170 if (x3 > 0.008856) {
171 x = x3;
172 } else {
173 x = (x - 16.0 / 116.0) / 7.787;
174 }
175 };
176
177 finv(vx);
178 finv(vy);
179 finv(vz);
180
181 vx *= 0.95047; // use white = D65
182 vz *= 1.08883;
183
184 float vr = vx * 3.2406 + vy * -1.5372 + vz * -0.4986;
185 float vg = vx * -0.9689 + vy * 1.8758 + vz * 0.0415;
186 float vb = vx * 0.0557 + vy * -0.2040 + vz * 1.0570;
187
188 auto from_linear = [] (float &x) {
189 if (x > 0.0031308) {
190 x = 1.055 * std::pow(x, 1.0 / 2.4) - 0.055;
191 } else {
192 x *= 12.92;
193 }
194 };
195
196 from_linear(vr);
197 from_linear(vg);
198 from_linear(vb);
199
200 return getRGB(vr, vg, vb);
201}
202
203float CieLab::diffSq(CieLab const &c1, CieLab const &c2)
204{
205 return sq(c1.L - c2.L)
206 + sq(c1.A - c2.A)
207 + sq(c1.B - c2.B);
208}
209
210float CieLab::diff(CieLab const &c1, CieLab const &c2)
211{
212 return std::sqrt(diffSq(c1, c2));
213}
214
215} // namespace Trace
216} // namespace Inkscape
double polish(double t, arc_length_params &alp)
float qn[SIZE+1]
Definition cielab.cpp:47
static int constexpr SIZE
Definition cielab.cpp:45
float cbrt[SIZE+1]
Definition cielab.cpp:46
uint32_t toRGB() const
Return this CieLab's value converted to an ARGB value.
Definition cielab.cpp:162
static float diff(CieLab const &c1, CieLab const &c2)
Computes euclidean distance in CieLab space between two colors.
Definition cielab.cpp:210
static float diffSq(CieLab const &c1, CieLab const &c2)
Squared Euclidean distance between two colors in CieLab space.
Definition cielab.cpp:203
Helper class to stream background task notifications as a series of messages.
RGB rgb
Definition quantize.cpp:36