Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/color.c (11.8K)
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <wlr/render/color.h>
5 #include "render/color.h"
6 #include "util/matrix.h"
7
8 // See H.273 ColourPrimaries
9
10 static const struct wlr_color_primaries COLOR_PRIMARIES_SRGB = { // code point 1
11 .red = { 0.640, 0.330 },
12 .green = { 0.300, 0.600 },
13 .blue = { 0.150, 0.060 },
14 .white = { 0.3127, 0.3290 },
15 };
16
17 static const struct wlr_color_primaries COLOR_PRIMARIES_BT2020 = { // code point 9
18 .red = { 0.708, 0.292 },
19 .green = { 0.170, 0.797 },
20 .blue = { 0.131, 0.046 },
21 .white = { 0.3127, 0.3290 },
22 };
23
24 void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type) {
25 *tr = (struct wlr_color_transform){
26 .type = type,
27 .ref_count = 1,
28 };
29 wlr_addon_set_init(&tr->addons);
30 }
31
32 struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
33 enum wlr_color_transfer_function tf) {
34 struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx));
35 if (!tx) {
36 return NULL;
37 }
38 wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_INVERSE_EOTF);
39 tx->tf = tf;
40 return &tx->base;
41 }
42
43 struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim,
44 const uint16_t *r, const uint16_t *g, const uint16_t *b) {
45 uint16_t *lut_3x1d = malloc(3 * dim * sizeof(lut_3x1d[0]));
46 if (lut_3x1d == NULL) {
47 return NULL;
48 }
49
50 memcpy(&lut_3x1d[0 * dim], r, dim * sizeof(lut_3x1d[0]));
51 memcpy(&lut_3x1d[1 * dim], g, dim * sizeof(lut_3x1d[0]));
52 memcpy(&lut_3x1d[2 * dim], b, dim * sizeof(lut_3x1d[0]));
53
54 struct wlr_color_transform_lut_3x1d *tx = calloc(1, sizeof(*tx));
55 if (!tx) {
56 free(lut_3x1d);
57 return NULL;
58 }
59 wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LUT_3X1D);
60 tx->lut_3x1d = lut_3x1d;
61 tx->dim = dim;
62 return &tx->base;
63 }
64
65 struct wlr_color_transform *wlr_color_transform_init_matrix(const float matrix[static 9]) {
66 struct wlr_color_transform_matrix *tx = calloc(1, sizeof(*tx));
67 if (!tx) {
68 return NULL;
69 }
70 wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_MATRIX);
71 memcpy(tx->matrix, matrix, sizeof(tx->matrix));
72 return &tx->base;
73 }
74
75 struct wlr_color_transform *wlr_color_transform_init_pipeline(
76 struct wlr_color_transform **transforms, size_t len) {
77 assert(len > 0);
78
79 struct wlr_color_transform **copy = calloc(len, sizeof(copy[0]));
80 if (copy == NULL) {
81 return NULL;
82 }
83
84 struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx));
85 if (!tx) {
86 free(copy);
87 return NULL;
88 }
89 wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE);
90
91 for (size_t i = 0; i < len; i++) {
92 assert(transforms[i] != NULL);
93 copy[i] = wlr_color_transform_ref(transforms[i]);
94 }
95
96 tx->transforms = copy;
97 tx->len = len;
98
99 return &tx->base;
100 }
101
102 static void color_transform_destroy(struct wlr_color_transform *tr) {
103 switch (tr->type) {
104 case COLOR_TRANSFORM_INVERSE_EOTF:
105 case COLOR_TRANSFORM_MATRIX:
106 break;
107 case COLOR_TRANSFORM_LCMS2:
108 color_transform_lcms2_finish(color_transform_lcms2_from_base(tr));
109 break;
110 case COLOR_TRANSFORM_LUT_3X1D:;
111 struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr);
112 free(lut_3x1d->lut_3x1d);
113 break;
114 case COLOR_TRANSFORM_PIPELINE:;
115 struct wlr_color_transform_pipeline *pipeline =
116 wl_container_of(tr, pipeline, base);
117 for (size_t i = 0; i < pipeline->len; i++) {
118 wlr_color_transform_unref(pipeline->transforms[i]);
119 }
120 free(pipeline->transforms);
121 break;
122 }
123 wlr_addon_set_finish(&tr->addons);
124 free(tr);
125 }
126
127 struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *tr) {
128 tr->ref_count += 1;
129 return tr;
130 }
131
132 void wlr_color_transform_unref(struct wlr_color_transform *tr) {
133 if (!tr) {
134 return;
135 }
136 assert(tr->ref_count > 0);
137 tr->ref_count -= 1;
138 if (tr->ref_count == 0) {
139 color_transform_destroy(tr);
140 }
141 }
142
143 struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
144 struct wlr_color_transform *tr) {
145 assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF);
146 struct wlr_color_transform_inverse_eotf *inverse_eotf = wl_container_of(tr, inverse_eotf, base);
147 return inverse_eotf;
148 }
149
150 struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
151 struct wlr_color_transform *tr) {
152 assert(tr->type == COLOR_TRANSFORM_LUT_3X1D);
153 struct wlr_color_transform_lut_3x1d *lut_3x1d = wl_container_of(tr, lut_3x1d, base);
154 return lut_3x1d;
155 }
156
157 static float srgb_eval_inverse_eotf(float x) {
158 // See https://www.w3.org/Graphics/Color/srgb
159 if (x <= 0.0031308) {
160 return 12.92 * x;
161 } else {
162 return 1.055 * powf(x, 1.0 / 2.4) - 0.055;
163 }
164 }
165
166 static float st2084_pq_eval_inverse_eotf(float x) {
167 // H.273 TransferCharacteristics code point 16
168 float c1 = 0.8359375;
169 float c2 = 18.8515625;
170 float c3 = 18.6875;
171 float m = 78.84375;
172 float n = 0.1593017578125;
173 if (x < 0) {
174 x = 0;
175 }
176 if (x > 1) {
177 x = 1;
178 }
179 float pow_n = powf(x, n);
180 return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m);
181 }
182
183 static float bt1886_eval_inverse_eotf(float x) {
184 float lb = powf(0.0001, 1.0 / 2.4);
185 float lw = powf(1.0, 1.0 / 2.4);
186 float a = powf(lw - lb, 2.4);
187 float b = lb / (lw - lb);
188 return powf(x / a, 1.0 / 2.4) - b;
189 }
190
191 static float transfer_function_eval_inverse_eotf(
192 enum wlr_color_transfer_function tf, float x) {
193 switch (tf) {
194 case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
195 return srgb_eval_inverse_eotf(x);
196 case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
197 return st2084_pq_eval_inverse_eotf(x);
198 case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
199 return x;
200 case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
201 return powf(x, 1.0 / 2.2);
202 case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
203 return bt1886_eval_inverse_eotf(x);
204 }
205 abort(); // unreachable
206 }
207
208 static void color_transform_inverse_eotf_eval(
209 struct wlr_color_transform_inverse_eotf *tr,
210 float out[static 3], const float in[static 3]) {
211 for (size_t i = 0; i < 3; i++) {
212 out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]);
213 }
214 }
215
216 static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) {
217 if (i >= len) {
218 i = len - 1;
219 }
220 return (float) lut[i] / UINT16_MAX;
221 }
222
223 static float lut_1d_eval(const uint16_t *lut, size_t len, float x) {
224 double pos = x * (len - 1);
225 double int_part;
226 double frac_part = modf(pos, &int_part);
227 size_t i = (size_t) int_part;
228 double a = lut_1d_get(lut, len, i);
229 double b = lut_1d_get(lut, len, i + 1);
230 return a * (1 - frac_part) + b * frac_part;
231 }
232
233 static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr,
234 float out[static 3], const float in[static 3]) {
235 for (size_t i = 0; i < 3; i++) {
236 out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]);
237 }
238 }
239
240 static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]);
241
242 void wlr_color_transform_eval(struct wlr_color_transform *tr,
243 float out[static 3], const float in[static 3]) {
244 switch (tr->type) {
245 case COLOR_TRANSFORM_INVERSE_EOTF:
246 color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in);
247 break;
248 case COLOR_TRANSFORM_LCMS2:
249 color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in);
250 break;
251 case COLOR_TRANSFORM_LUT_3X1D:
252 color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in);
253 break;
254 case COLOR_TRANSFORM_MATRIX:;
255 struct wlr_color_transform_matrix *matrix = wl_container_of(tr, matrix, base);
256 multiply_matrix_vector(out, matrix->matrix, in);
257 break;
258 case COLOR_TRANSFORM_PIPELINE:;
259 struct wlr_color_transform_pipeline *pipeline =
260 wl_container_of(tr, pipeline, base);
261 float color[3];
262 memcpy(color, in, sizeof(color));
263 for (size_t i = 0; i < pipeline->len; i++) {
264 wlr_color_transform_eval(pipeline->transforms[i], color, color);
265 }
266 memcpy(out, color, sizeof(color));
267 break;
268 }
269 }
270
271 static size_t color_transform_compose_collect(struct wlr_color_transform **out,
272 size_t out_capacity, struct wlr_color_transform **transforms, size_t len) {
273 size_t count = 0;
274 for (size_t i = 0; i < len; i++) {
275 struct wlr_color_transform *transform = transforms[i];
276 if (transform == NULL) {
277 continue;
278 }
279
280 if (transform->type == COLOR_TRANSFORM_PIPELINE) {
281 struct wlr_color_transform_pipeline *pipeline = wl_container_of(transform,
282 pipeline, base);
283 count += color_transform_compose_collect(out, out_capacity,
284 pipeline->transforms, pipeline->len);
285 } else {
286 if (out_capacity > 0) {
287 *out = wlr_color_transform_ref(transform);
288 out++;
289 out_capacity--;
290 }
291 count++;
292 }
293 }
294 return count;
295 }
296
297 bool color_transform_compose(struct wlr_color_transform **result,
298 struct wlr_color_transform **transforms, size_t len) {
299 // The normalized form has the following properties :
300 // - No NULL transform in a pipeline
301 // - No pipeline of length 1
302 // - No nested pipelines
303 bool status = false;
304
305 size_t result_len = color_transform_compose_collect(NULL, 0, transforms, len);
306 if (result_len == 0) {
307 *result = NULL;
308 return true;
309 }
310
311 struct wlr_color_transform **result_transforms = calloc(result_len,
312 sizeof(result_transforms[0]));
313 if (result_transforms == NULL) {
314 return false;
315 }
316 color_transform_compose_collect(result_transforms, result_len, transforms, len);
317
318 if (result_len == 1) {
319 *result = wlr_color_transform_ref(result_transforms[0]);
320 } else {
321 *result = wlr_color_transform_init_pipeline(result_transforms, result_len);
322 if (*result == NULL) {
323 goto cleanup_transforms;
324 }
325 }
326 status = true;
327
328 cleanup_transforms:
329 for (size_t i = 0; i < result_len; i++) {
330 wlr_color_transform_unref(result_transforms[i]);
331 }
332 free(result_transforms);
333 return status;
334 }
335
336 void wlr_color_primaries_from_named(struct wlr_color_primaries *out,
337 enum wlr_color_named_primaries named) {
338 switch (named) {
339 case WLR_COLOR_NAMED_PRIMARIES_SRGB:
340 *out = COLOR_PRIMARIES_SRGB;
341 return;
342 case WLR_COLOR_NAMED_PRIMARIES_BT2020:
343 *out = COLOR_PRIMARIES_BT2020;
344 return;
345 }
346 abort();
347 }
348
349 static void multiply_matrix_vector(float out[static 3], float m[static 9], const float v[static 3]) {
350 float result[3] = {
351 m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
352 m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
353 m[6] * v[0] + m[7] * v[1] + m[8] * v[2],
354 };
355 memcpy(out, result, sizeof(result));
356 }
357
358 static void xy_to_xyz(float out[static 3], struct wlr_color_cie1931_xy src) {
359 if (src.y == 0) {
360 out[0] = out[1] = out[2] = 0;
361 return;
362 }
363
364 out[0] = src.x / src.y;
365 out[1] = 1;
366 out[2] = (1 - src.x - src.y) / src.y;
367 }
368
369 void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]) {
370 // See: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
371
372 float r[3], g[3], b[3], w[3];
373 xy_to_xyz(r, primaries->red);
374 xy_to_xyz(g, primaries->green);
375 xy_to_xyz(b, primaries->blue);
376 xy_to_xyz(w, primaries->white);
377
378 float xyz_matrix[9] = {
379 r[0], g[0], b[0],
380 r[1], g[1], b[1],
381 r[2], g[2], b[2],
382 };
383 matrix_invert(xyz_matrix, xyz_matrix);
384
385 float S[3];
386 multiply_matrix_vector(S, xyz_matrix, w);
387
388 float result[] = {
389 S[0] * r[0], S[1] * g[0], S[2] * b[0],
390 S[0] * r[1], S[1] * g[1], S[2] * b[1],
391 S[0] * r[2], S[1] * g[2], S[2] * b[2],
392 };
393 memcpy(matrix, result, sizeof(result));
394 }
395
396 void wlr_color_primaries_transform_absolute_colorimetric(
397 const struct wlr_color_primaries *source,
398 const struct wlr_color_primaries *destination, float matrix[static 9]) {
399 float source_to_xyz[9];
400 wlr_color_primaries_to_xyz(source, source_to_xyz);
401 float destination_to_xyz[9];
402 wlr_color_primaries_to_xyz(destination, destination_to_xyz);
403 float xyz_to_destination[9];
404 matrix_invert(xyz_to_destination, destination_to_xyz);
405 wlr_matrix_multiply(matrix, xyz_to_destination, source_to_xyz);
406 }
407
408 void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf,
409 struct wlr_color_luminances *lum) {
410 switch (tf) {
411 case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
412 *lum = (struct wlr_color_luminances){
413 .min = 0.005,
414 .max = 10000,
415 .reference = 203,
416 };
417 break;
418 case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
419 *lum = (struct wlr_color_luminances){
420 .min = 0.01,
421 .max = 100,
422 .reference = 100,
423 };
424 break;
425 default:
426 *lum = (struct wlr_color_luminances){
427 .min = 0.2,
428 .max = 80,
429 .reference = 80,
430 };
431 break;
432 }
433 }