diff --git a/libweston/color-operations.c b/libweston/color-operations.c new file mode 100644 index 000000000..7e1f75804 --- /dev/null +++ b/libweston/color-operations.c @@ -0,0 +1,182 @@ +/* + * Copyright 2025 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include "color.h" +#include "color-operations.h" + +#include "shared/helpers.h" +#include "shared/weston-assert.h" + +/** + * Clamp value to [0.0, 1.0], except pass NaN through. + * + * This function is not intended for hiding NaN. + */ +static float +ensure_unorm(float v) +{ + if (v <= 0.0f) + return 0.0f; + if (v > 1.0f) + return 1.0f; + return v; +} + +static float +linpow(float x, float g, float a, float b, float c, float d) +{ + /* See WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW for details about LINPOW. */ + + if (x >= d) + return pow((a * x) + b, g); + + return c * x; +} + +static void +sample_linpow(float params[3][MAX_PARAMS_PARAM_CURVE], uint32_t ch, + uint32_t len, bool clamp_input, float *in, float *out) +{ + float g, a, b, c, d; + float x; + unsigned int i; + + g = params[ch][0]; + a = params[ch][1]; + b = params[ch][2]; + c = params[ch][3]; + d = params[ch][4]; + + for (i = 0; i < len; i++) { + x = in[i]; + if (clamp_input) + x = ensure_unorm(x); + + /* LINPOW uses mirroring for negative input values. */ + if (x < 0.0) + out[i] = -linpow(-x, g, a, b, c, d); + else + out[i] = linpow(x, g, a, b, c, d); + } +} + +static float +powlin(float x, float g, float a, float b, float c, float d) +{ + /* See WESTON_COLOR_CURVE_PARAMETRIC_TYPE_POWLIN for details about POWLIN. */ + + if (x >= d) + return a * pow(x, g) + b; + + return c * x; +} + +static void +sample_powlin(float params[3][MAX_PARAMS_PARAM_CURVE], uint32_t ch, + uint32_t len, bool clamp_input, float *in, float *out) +{ + float g, a, b, c, d; + float x; + unsigned int i; + + g = params[ch][0]; + a = params[ch][1]; + b = params[ch][2]; + c = params[ch][3]; + d = params[ch][4]; + + for (i = 0; i < len; i++) { + x = in[i]; + if (clamp_input) + x = ensure_unorm(x); + + /* POWLIN uses mirroring for negative input values. */ + if (x < 0.0) + out[i] = -powlin(-x, g, a, b, c, d); + else + out[i] = powlin(x, g, a, b, c, d); + } +} + +/** +* Given a color curve and a channel, sample an input. +* +* This handles the parametric curves (LINPOW, POWLIN, etc) and enumerated color +* curves. Others should result in failure. +* +* @param compositor The Weston compositor +* @param curve The color curve to be used to sample +* @param ch The curve color channel to sample from +* @param len The in and out arrays length +* @param in The input array to sample +* @param out The resulting array from sampling +* @returns True on success, false otherwise +*/ +bool +weston_color_curve_sample(struct weston_compositor *compositor, + struct weston_color_curve *curve, + uint32_t ch, uint32_t len, float *in, float *out) +{ + struct weston_color_curve_parametric parametric; + bool ret; + + switch(curve->type) { + case WESTON_COLOR_CURVE_TYPE_ENUM: + /* Lower the enum curve to a param curve and we'll handle that below. */ + ret = weston_color_curve_enum_get_parametric(compositor, + &curve->u.enumerated, + ¶metric); + if (!ret) + return false; + goto param; + case WESTON_COLOR_CURVE_TYPE_PARAMETRIC: + /* Parametric curve, let's copy it and we'll handle that below. */ + parametric = curve->u.parametric; + goto param; + case WESTON_COLOR_CURVE_TYPE_IDENTITY: + weston_assert_not_reached(compositor, + "no need to sample identity"); + case WESTON_COLOR_CURVE_TYPE_LUT_3x1D: + weston_assert_not_reached(compositor, + "function does not handle LUT 3x1D"); + } + + weston_assert_not_reached(compositor, "unknown color curve"); + +param: + /* Sample from parametric curves. */ + switch(parametric.type) { + case WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW: + sample_linpow(parametric.params, ch, len, parametric.clamped_input, in, out); + return true; + case WESTON_COLOR_CURVE_PARAMETRIC_TYPE_POWLIN: + sample_powlin(parametric.params, ch, len, parametric.clamped_input, in, out); + return true; + } + + weston_assert_not_reached(compositor, "unknown parametric color curve"); +} diff --git a/libweston/color-operations.h b/libweston/color-operations.h new file mode 100644 index 000000000..b332035c5 --- /dev/null +++ b/libweston/color-operations.h @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_COLOR_OPERATIONS_H +#define WESTON_COLOR_OPERATIONS_H + +#include + +bool +weston_color_curve_sample(struct weston_compositor *compositor, + struct weston_color_curve *curve, + uint32_t ch, uint32_t len, float *in, float *out); + +#endif /* WESTON_COLOR_OPERATIONS_H */ diff --git a/libweston/color.c b/libweston/color.c index 5d42b0653..9cd712c8c 100644 --- a/libweston/color.c +++ b/libweston/color.c @@ -37,12 +37,14 @@ #include #include "color.h" +#include "color-operations.h" #include "color-properties.h" #include "id-number-allocator.h" #include "libweston-internal.h" #include #include "shared/string-helpers.h" #include "shared/helpers.h" +#include "shared/weston-assert.h" #include "shared/xalloc.h" #include "shared/weston-assert.h" @@ -266,6 +268,147 @@ weston_color_curve_enum_get_parametric(struct weston_compositor *compositor, return true; } +static bool +curve_to_lut_has_good_precision(struct weston_color_curve *curve) +{ + struct weston_color_curve_enum *e = &curve->u.enumerated; + struct weston_color_curve_parametric *p = &curve->u.parametric; + float g; + unsigned int i; + + if (curve->type == WESTON_COLOR_CURVE_TYPE_ENUM) { + if (e->tf_direction == WESTON_INVERSE_TF) { + if (e->tf->tf == WESTON_TF_ST2084_PQ || + e->tf->tf == WESTON_TF_GAMMA22 || + e->tf->tf == WESTON_TF_GAMMA28) { + /** + * These have bad precision in the indirect + * direction. + */ + return false; + } + + if (e->tf->tf == WESTON_TF_POWER) { + /** + * Same as the above, but for parametric + * power-law transfer function. If g > 1.0 + * it would result in bad precision. + */ + for (i = 0; i < 3; i++) { + g = e->params[i][0]; + if (g > 1.0f) + return false; + } + } + } else { + if (e->tf->tf == WESTON_TF_POWER) { + /** + * For parametric power-law transfer function + * in the forward direction, g < 1.0 would + * result in bad precision. + */ + for (i = 0; i < 3; i++) { + g = e->params[i][0]; + if (g < 1.0f) + return false; + } + } + } + } else if (curve->type == WESTON_COLOR_CURVE_TYPE_PARAMETRIC) { + switch(p->type) { + case WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW: + case WESTON_COLOR_CURVE_PARAMETRIC_TYPE_POWLIN: + /** + * Both LINPOW and POWLIN have bad precision if g < 1.0. + */ + for (i = 0; i < 3; i++) { + g = p->params[i][0]; + if (g < 1.0f) + return false; + } + break; + } + } + + return true; +} + +/** + * Given a xform and an enum corresponding to one of its curves (pre or post), + * returns a 3x1D LUT that corresponds to such curve. + * + * The 3x1D LUT returned looks like this: the first lut_size elements compose + * the LUT for the R channel, the next lut_size elements compose the LUT for the + * G channel and the last lut_size elements compose the LUT for the B channel. + * + * @param compositor The Weston compositor. + * @param xform The color transformation that owns the curve. + * @param step The curve step (pre or post) from the xform. + * @param lut_size The size of each LUT. + * @param err_msg Set on failure, untouched otherwise. Must be free()'d by caller. + * @return NULL on failure, the 3x1D LUT on success. + */ +WL_EXPORT float * +weston_color_curve_to_3x1D_LUT(struct weston_compositor *compositor, + struct weston_color_transform *xform, + enum weston_color_curve_step step, + uint32_t lut_size, char **err_msg) +{ + struct weston_color_curve *curve; + float divider = lut_size - 1; + float *in, *lut; + unsigned int i, ch; + bool ret; + + switch(step) { + case WESTON_COLOR_CURVE_STEP_PRE: + curve = &xform->pre_curve; + break; + case WESTON_COLOR_CURVE_STEP_POST: + curve = &xform->post_curve; + break; + default: + weston_assert_not_reached(compositor, "unknown curve step"); + } + + if (!curve_to_lut_has_good_precision(curve)) { + str_printf(err_msg, "can't create color LUT from curve, it would " \ + "result in bad precision"); + return NULL; + } + + switch(curve->type) { + case WESTON_COLOR_CURVE_TYPE_LUT_3x1D: + lut = xzalloc(3 * lut_size * sizeof(*lut)); + curve->u.lut_3x1d.fill_in(xform, lut, lut_size); + return lut; + case WESTON_COLOR_CURVE_TYPE_ENUM: + case WESTON_COLOR_CURVE_TYPE_PARAMETRIC: + lut = xzalloc(3 * lut_size * sizeof(*lut)); + in = xzalloc(lut_size * sizeof(*lut)); + for (i = 0; i < lut_size; i++) + in[i] = (float)i / divider; + for (ch = 0; ch < 3; ch++) { + ret = weston_color_curve_sample(compositor, curve, ch, lut_size, + in, &lut[ch * lut_size]); + if (!ret) { + free(lut); + lut = NULL; + str_printf(err_msg, "can't create color LUT from " \ + "curve, failed to sample color curve"); + break; + } + } + free(in); + return lut; + case WESTON_COLOR_CURVE_TYPE_IDENTITY: + weston_assert_not_reached(compositor, + "no reason to create LUT for identity curve"); + } + + weston_assert_not_reached(compositor, "unkown color curve"); +} + /** * Increase reference count of the color transform object * diff --git a/libweston/color.h b/libweston/color.h index 3c0b83b41..7451d35c2 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -653,6 +653,17 @@ weston_color_curve_from_tf_info(struct weston_color_curve *curve, const float tf_params[MAX_PARAMS_TF], enum weston_tf_direction tf_direction); +enum weston_color_curve_step { + WESTON_COLOR_CURVE_STEP_PRE, + WESTON_COLOR_CURVE_STEP_POST, +}; + +float * +weston_color_curve_to_3x1D_LUT(struct weston_compositor *compositor, + struct weston_color_transform *xform, + enum weston_color_curve_step step, + uint32_t lut_size, char **err_msg); + struct weston_color_transform * weston_color_transform_ref(struct weston_color_transform *xform); diff --git a/libweston/meson.build b/libweston/meson.build index b99ff39a7..8b6955534 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -18,6 +18,7 @@ srcs_libweston = [ 'color-properties.c', 'color-management.c', 'color-noop.c', + 'color-operations.c', 'color-profile-param-builder.c', 'compositor.c', 'content-protection.c',