git.lucas.co / cce-compositor
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 }