render/color: introduce wlr_color_transform_eotf

This commit is contained in:
Félix Poisot 2026-01-31 21:04:51 +00:00
parent 35c35530a3
commit 42cf99a037
4 changed files with 99 additions and 0 deletions

View file

@ -11,6 +11,7 @@ enum wlr_color_transform_type {
COLOR_TRANSFORM_LUT_3X1D,
COLOR_TRANSFORM_MATRIX,
COLOR_TRANSFORM_PIPELINE,
COLOR_TRANSFORM_EOTF,
};
struct wlr_color_transform {
@ -20,6 +21,12 @@ struct wlr_color_transform {
enum wlr_color_transform_type type;
};
struct wlr_color_transform_eotf {
struct wlr_color_transform base;
enum wlr_color_transfer_function tf;
};
struct wlr_color_transform_inverse_eotf {
struct wlr_color_transform base;
@ -72,6 +79,13 @@ void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr);
void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
float out[static 3], const float in[static 3]);
/**
* Gets a wlr_color_transform_eotf from a generic wlr_color_transform.
* Asserts that the base type is COLOR_TRANSFORM_EOTF
*/
struct wlr_color_transform_eotf *wlr_color_transform_eotf_from_base(
struct wlr_color_transform *tr);
/**
* Gets a wlr_color_transform_inverse_eotf from a generic wlr_color_transform.
* Asserts that the base type is COLOR_TRANSFORM_INVERSE_EOTF

View file

@ -127,6 +127,13 @@ struct wlr_color_transform;
struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
const void *data, size_t size);
/**
* Initialize a color transformation to apply EOTF decoding. Returns
* NULL on failure.
*/
struct wlr_color_transform *wlr_color_transform_init_eotf_to_linear(
enum wlr_color_transfer_function tf);
/**
* Initialize a color transformation to apply EOTF¹ encoding. Returns
* NULL on failure.

View file

@ -29,6 +29,17 @@ void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_tra
wlr_addon_set_init(&tr->addons);
}
struct wlr_color_transform *wlr_color_transform_init_eotf_to_linear(
enum wlr_color_transfer_function tf) {
struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx));
if (!tx) {
return NULL;
}
wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_EOTF);
tx->tf = tf;
return &tx->base;
}
struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
enum wlr_color_transfer_function tf) {
struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx));
@ -103,6 +114,7 @@ static void color_transform_destroy(struct wlr_color_transform *tr) {
switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF:
case COLOR_TRANSFORM_MATRIX:
case COLOR_TRANSFORM_EOTF:
break;
case COLOR_TRANSFORM_LCMS2:
color_transform_lcms2_finish(color_transform_lcms2_from_base(tr));
@ -140,6 +152,13 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) {
}
}
struct wlr_color_transform_eotf *wlr_color_transform_eotf_from_base(
struct wlr_color_transform *tr) {
assert(tr->type == COLOR_TRANSFORM_EOTF);
struct wlr_color_transform_eotf *eotf = wl_container_of(tr, eotf, base);
return eotf;
}
struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
struct wlr_color_transform *tr) {
assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF);
@ -154,6 +173,14 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base(
return lut_3x1d;
}
static float srgb_eval_eotf(float x) {
if (x <= 0.04045) {
return x / 12.92;
} else {
return powf((x + 0.055) / 1.055, 2.4);
}
}
static float srgb_eval_inverse_eotf(float x) {
// See https://www.w3.org/Graphics/Color/srgb
if (x <= 0.0031308) {
@ -163,6 +190,21 @@ static float srgb_eval_inverse_eotf(float x) {
}
}
static float st2084_pq_eval_eotf(float x) {
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
float inv_m2 = 1.0 / 78.84375;
float inv_m1 = 1.0 / 0.1593017578125;
float pow_m2 = powf(x, inv_m2);
float num = pow_m2 - c1;
if (num < 0) {
num = 0;
}
float denom = c2 - c3 * pow_m2;
return powf(num / denom, inv_m1);
}
static float st2084_pq_eval_inverse_eotf(float x) {
// H.273 TransferCharacteristics code point 16
float c1 = 0.8359375;
@ -180,6 +222,14 @@ static float st2084_pq_eval_inverse_eotf(float x) {
return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m);
}
static float bt1886_eval_eotf(float x) {
float lb = powf(0.0001, 1.0 / 2.4);
float lw = powf(1.0, 1.0 / 2.4);
float a = powf(lw - lb, 2.4);
float b = lb / (lw - lb);
return a * powf(x + b, 2.4);
}
static float bt1886_eval_inverse_eotf(float x) {
float lb = powf(0.0001, 1.0 / 2.4);
float lw = powf(1.0, 1.0 / 2.4);
@ -188,6 +238,22 @@ static float bt1886_eval_inverse_eotf(float x) {
return powf(x / a, 1.0 / 2.4) - b;
}
static float transfer_function_eval_eotf(enum wlr_color_transfer_function tf, float x) {
switch (tf) {
case WLR_COLOR_TRANSFER_FUNCTION_SRGB:
return srgb_eval_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ:
return st2084_pq_eval_eotf(x);
case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR:
return x;
case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22:
return powf(x, 2.2);
case WLR_COLOR_TRANSFER_FUNCTION_BT1886:
return bt1886_eval_eotf(x);
}
abort(); // unreachable
}
static float transfer_function_eval_inverse_eotf(
enum wlr_color_transfer_function tf, float x) {
switch (tf) {
@ -205,6 +271,14 @@ static float transfer_function_eval_inverse_eotf(
abort(); // unreachable
}
static void color_transform_eotf_eval(
struct wlr_color_transform_eotf *tr,
float out[static 3], const float in[static 3]) {
for (size_t i = 0; i < 3; i++) {
out[i] = transfer_function_eval_eotf(tr->tf, in[i]);
}
}
static void color_transform_inverse_eotf_eval(
struct wlr_color_transform_inverse_eotf *tr,
float out[static 3], const float in[static 3]) {
@ -265,6 +339,9 @@ void wlr_color_transform_eval(struct wlr_color_transform *tr,
}
memcpy(out, color, sizeof(color));
break;
case COLOR_TRANSFORM_EOTF:
color_transform_eotf_eval(wlr_color_transform_eotf_from_base(tr), out, in);
break;
}
}

View file

@ -169,6 +169,7 @@ static bool unwrap_color_transform(struct wlr_color_transform *transform,
return true;
case COLOR_TRANSFORM_LCMS2:
case COLOR_TRANSFORM_LUT_3X1D:
case COLOR_TRANSFORM_EOTF:
return false;
}
return false;