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_LUT_3X1D,
COLOR_TRANSFORM_MATRIX, COLOR_TRANSFORM_MATRIX,
COLOR_TRANSFORM_PIPELINE, COLOR_TRANSFORM_PIPELINE,
COLOR_TRANSFORM_EOTF,
}; };
struct wlr_color_transform { struct wlr_color_transform {
@ -20,6 +21,12 @@ struct wlr_color_transform {
enum wlr_color_transform_type type; 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_inverse_eotf {
struct wlr_color_transform base; 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, void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
float out[static 3], const float in[static 3]); 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. * Gets a wlr_color_transform_inverse_eotf from a generic wlr_color_transform.
* Asserts that the base type is COLOR_TRANSFORM_INVERSE_EOTF * 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( struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
const void *data, size_t size); 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 * Initialize a color transformation to apply EOTF¹ encoding. Returns
* NULL on failure. * 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); 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( struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf(
enum wlr_color_transfer_function tf) { enum wlr_color_transfer_function tf) {
struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx)); 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) { switch (tr->type) {
case COLOR_TRANSFORM_INVERSE_EOTF: case COLOR_TRANSFORM_INVERSE_EOTF:
case COLOR_TRANSFORM_MATRIX: case COLOR_TRANSFORM_MATRIX:
case COLOR_TRANSFORM_EOTF:
break; break;
case COLOR_TRANSFORM_LCMS2: case COLOR_TRANSFORM_LCMS2:
color_transform_lcms2_finish(color_transform_lcms2_from_base(tr)); 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_inverse_eotf *wlr_color_transform_inverse_eotf_from_base(
struct wlr_color_transform *tr) { struct wlr_color_transform *tr) {
assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF); 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; 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) { static float srgb_eval_inverse_eotf(float x) {
// See https://www.w3.org/Graphics/Color/srgb // See https://www.w3.org/Graphics/Color/srgb
if (x <= 0.0031308) { 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) { static float st2084_pq_eval_inverse_eotf(float x) {
// H.273 TransferCharacteristics code point 16 // H.273 TransferCharacteristics code point 16
float c1 = 0.8359375; 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); 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) { static float bt1886_eval_inverse_eotf(float x) {
float lb = powf(0.0001, 1.0 / 2.4); float lb = powf(0.0001, 1.0 / 2.4);
float lw = powf(1.0, 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; 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( static float transfer_function_eval_inverse_eotf(
enum wlr_color_transfer_function tf, float x) { enum wlr_color_transfer_function tf, float x) {
switch (tf) { switch (tf) {
@ -205,6 +271,14 @@ static float transfer_function_eval_inverse_eotf(
abort(); // unreachable 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( static void color_transform_inverse_eotf_eval(
struct wlr_color_transform_inverse_eotf *tr, struct wlr_color_transform_inverse_eotf *tr,
float out[static 3], const float in[static 3]) { 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)); memcpy(out, color, sizeof(color));
break; 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; return true;
case COLOR_TRANSFORM_LCMS2: case COLOR_TRANSFORM_LCMS2:
case COLOR_TRANSFORM_LUT_3X1D: case COLOR_TRANSFORM_LUT_3X1D:
case COLOR_TRANSFORM_EOTF:
return false; return false;
} }
return false; return false;