weston/libweston/color.h
Leandro Ribeiro 9002667aa0 color: add support to parametric curves in weston_color_curve
Until now, all the curves would be represented with 3x1D LUT's. Now we
support LINPOW and POWLIN curves (arbitrary names that we've picked).
We can use these curves to represent LittleCMS curves type 1, 4 and
their inverses -1, -4. The reason why we want that is because we gain
precision using the parametric curves (compared to the LUT's);

Surprisingly we had to increase the tolerance of the sRGB->adobeRGB MAT
test. Our analysis is that the inverse EOTF power-law curve with
exponent 1.0 / 2.2 amplifies errors more than the LUT, specially for
input (optical) values closer to zero.

That makes sense, because this curve is more sensible to input values
closer to zero (i.e. little input variation results in lots of output
variation). And this model makes sense, as humans are more capable of
perceiving changes of light intensity in the dark.

But the downside of all that is that for input values closer to zero, a
little bit of noise increases significantly the error.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
2024-03-26 11:23:26 +00:00

475 lines
15 KiB
C

/*
* Copyright 2021 Collabora, Ltd.
* Copyright 2021 Advanced Micro Devices, Inc.
*
* 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_H
#define WESTON_COLOR_H
#include <stdbool.h>
#include <stdint.h>
#include <libweston/libweston.h>
/**
* Represents a color profile description (an ICC color profile)
*
* Sub-classed by the color manager that created this.
*/
struct weston_color_profile {
struct weston_color_manager *cm;
int ref_count;
char *description;
/* Unique id to be used by the CM&HDR protocol extension. */
uint32_t id;
};
/** Type or formula for a curve */
enum weston_color_curve_type {
/** Identity function, no-op */
WESTON_COLOR_CURVE_TYPE_IDENTITY = 0,
/** Three-channel, one-dimensional look-up table */
WESTON_COLOR_CURVE_TYPE_LUT_3x1D,
/** Transfer function named LINPOW
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | 0 <= x < d
*
* We gave it the name LINPOW because the first operation with the input
* x is a linear one, and then the result is raised to g.
*
* As all parametric curves, this one should be represented using struct
* weston_color_curve_parametric. For each color channel RGB we may have
* different params, see weston_color_curve_parametric::params.
*
* For LINPOW, the params g, a, b, c, and d are respectively
* params[channel][0], ... , params[channel][4].
*
* The input for all color channels may be clamped to [0.0, 1.0]. In
* such case, weston_color_curve_parametric::clamped_input is true.
* If the input is not clamped and LINPOW needs to evaluate a negative
* input value, it uses mirroring (i.e. -f(-x)).
*/
WESTON_COLOR_CURVE_TYPE_LINPOW,
/** Transfer function named POWLIN
*
* y = (a * (x ^ g)) + b | x >= d
* y = c * x | 0 <= x < d
*
* We gave it the name POWLIN because the first operation with the input
* x is an exponential one, and then the result is multiplied by a.
*
* As all parametric curves, this one should be represented using struct
* weston_color_curve_parametric. For each color channel RGB we may have
* different params, see weston_color_curve_parametric::params.
*
* For POWLIN, the params g, a, b, c, and d are respectively
* params[channel][0], ... , params[channel][4].
*
* The input for all color channels may be clamped to [0.0, 1.0]. In
* such case, weston_color_curve_parametric::clamped_input is true.
* If the input is not clamped and POWLIN needs to evaluate a negative
* input value, it uses mirroring (i.e. -f(-x)).
*/
WESTON_COLOR_CURVE_TYPE_POWLIN,
};
/** LUT_3x1D parameters */
struct weston_color_curve_lut_3x1d {
/**
* Approximate a color curve with three 1D LUTs
*
* A 1D LUT is a mapping from [0.0, 1.0] to arbitrary values. The first
* element in the LUT corresponds to input value 0.0, and the last
* element corresponds to input value 1.0. The step from one element
* to the next in input space is 1.0 / (len - 1). When input value is
* between two elements, linear interpolation should be used.
*
* This function fills in the given array with the LUT values.
*
* \param xform This color transformation object.
* \param len The number of elements in each 1D LUT.
* \param values Array of 3 x len elements. First R channel
* LUT, immediately followed by G channel LUT, and then B channel LUT.
*/
void
(*fill_in)(struct weston_color_transform *xform,
float *values, unsigned len);
/** Optimal 1D LUT length for storage vs. precision */
unsigned optimal_len;
};
/** Parametric color curve parameters */
struct weston_color_curve_parametric {
/* For each color channel we may have different curves. For each of
* them, we can have up to 10 params, depending on the curve type. The
* channels are in RGB order. */
float params[3][10];
/* The input of the curve should be clamped from 0.0 to 1.0? */
bool clamped_input;
};
/**
* A scalar function for color encoding and decoding
*
* This object can represent a one-dimensional function that is applied
* independently to each of the color channels. Depending on the type and
* parameterization of the curve, all color channels may use the
* same function or each may have separate parameters.
*
* This is usually used for EOTF or EOTF^-1 and to optimize a 3D LUT size
* without sacrificing precision, both in one step.
*/
struct weston_color_curve {
/** Which member of 'u' defines the curve. */
enum weston_color_curve_type type;
/** Parameters for the curve. */
union {
/* identity: no parameters */
struct weston_color_curve_lut_3x1d lut_3x1d;
struct weston_color_curve_parametric parametric;
} u;
};
/** Type or formula for a color mapping */
enum weston_color_mapping_type {
/** Identity function, no-op */
WESTON_COLOR_MAPPING_TYPE_IDENTITY = 0,
/** 3D-dimensional look-up table */
WESTON_COLOR_MAPPING_TYPE_3D_LUT,
/** matrix */
WESTON_COLOR_MAPPING_TYPE_MATRIX,
};
/**
* A three-dimensional look-up table
*
* A 3D LUT is a three-dimensional array where each element is an RGB triplet.
* A 3D LUT is usually an approximation of some arbitrary color mapping
* function that cannot be represented in any simpler form. The array contains
* samples from the approximated function, and values between samples are
* estimated by interpolation. The array is accessed with three indices, one
* for each input dimension (color channel).
*
* Color channel values in the range [0.0, 1.0] are mapped linearly to
* 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps
* exactly to the last element in each dimension.
*
* This object represents a 3D LUT and offers an interface for realizing it
* as a data array with a custom size.
*/
struct weston_color_mapping_3dlut {
/**
* Create a 3D LUT data array
*
* \param xform This color transformation object.
* \param values Memory to hold the resulting data array.
* \param len The number of elements in each dimension.
*
* The array \c values must be at least 3 * len * len * len elements
* in size.
*
* Given the red index ri, green index gi and blue index bi, the
* corresponding array element index
*
* i = 3 * (len * len * bi + len * gi + ri) + c
*
* where
*
* c = 0 for red output value,
* c = 1 for green output value, and
* c = 2 for blue output value
*/
void
(*fill_in)(struct weston_color_transform *xform,
float *values, unsigned len);
/** Optimal 3D LUT size along each dimension */
unsigned optimal_len;
};
/**
* A 3x3 matrix and data is arranged as column major
*/
struct weston_color_mapping_matrix {
float matrix[9];
};
/**
* Color mapping function
*
* This object can represent a 3D LUT to do a color space conversion
*
*/
struct weston_color_mapping {
/** Which member of 'u' defines the color mapping type */
enum weston_color_mapping_type type;
/** Parameters for the color mapping function */
union {
/* identity: no parameters */
struct weston_color_mapping_3dlut lut3d;
struct weston_color_mapping_matrix mat;
} u;
};
/**
* Describes a color transformation formula
*
* Guaranteed unique, de-duplicated.
*
* Sub-classed by the color manager that created this.
*
* For a renderer to support WESTON_CAP_COLOR_OPS it must implement everything
* that this structure can represent.
*/
struct weston_color_transform {
struct weston_color_manager *cm;
int ref_count;
uint32_t id; /* For debug */
/* for renderer or backend to attach their own cached objects */
struct wl_signal destroy_signal;
/* Color transform is the series of steps: */
/** Step 1: color model change */
/* YCbCr→RGB conversion, but that is done elsewhere */
/** Step 2: color curve before color mapping */
struct weston_color_curve pre_curve;
/** Step 3: color mapping */
struct weston_color_mapping mapping;
/** Step 4: color curve after color mapping */
struct weston_color_curve post_curve;
};
/**
* How content color needs to be transformed
*
* This object is specific to the color properties of the weston_surface and
* weston_output it was created for. It is automatically destroyed if any
* relevant color properties change.
*
* Fundamentally this contains the color transformation from content color
* space to an output's blending color space. This is stored in field
* 'transform' with NULL value corresponding to identity transformation.
*
* For graphics pipeline optimization purposes, the field 'identity_pipeline'
* indicates whether the combination of 'transform' here and the output's
* blending color space to monitor color space transformation total to
* identity transformation. This helps detecting cases where renderer bypass
* (direct scanout) is possible.
*/
struct weston_surface_color_transform {
/** Transformation from source to blending space */
struct weston_color_transform *transform;
/** True, if source colorspace is identical to monitor color space */
bool identity_pipeline;
};
struct cm_image_desc_info;
struct weston_color_manager {
/** Identifies this CMS component */
const char *name;
/** This compositor instance */
struct weston_compositor *compositor;
/** Supports the Wayland CM&HDR protocol extension? */
bool supports_client_protocol;
/**
* Supported color features from Wayland CM&HDR protocol extension.
*
* If v (v being enum weston_color_feature v) is a supported color
* feature, the bit v of this will be set to 1.
*/
uint32_t supported_color_features;
/**
* Supported rendering intents from Wayland CM&HDR protocol extension.
*
* If v (v being enum weston_render_intent v) is a supported rendering
* intent, the bit v of this will be set to 1.
*/
uint32_t supported_rendering_intents;
/** Initialize color manager */
bool
(*init)(struct weston_color_manager *cm);
/** Destroy color manager */
void
(*destroy)(struct weston_color_manager *cm);
/** Destroy a color profile after refcount fell to zero */
void
(*destroy_color_profile)(struct weston_color_profile *cprof);
/** Gets a new reference to the stock sRGB color profile
*
* \param cm The color manager.
* \return A new reference to the stock sRGB profile, never returns NULL.
*/
struct weston_color_profile *
(*ref_stock_sRGB_color_profile)(struct weston_color_manager *cm);
/** Create a color profile from ICC data
*
* \param cm The color manager.
* \param icc_data Pointer to the ICC binary data.
* \param icc_len Length of the ICC data in bytes.
* \param name_part A string to be used in describing the profile.
* \param cprof_out On success, the created object is returned here.
* On failure, untouched.
* \param errmsg On success, untouched. On failure, a pointer to a
* string describing the error is stored here. The string must be
* free()'d.
* \return True on success, false on failure.
*
* This may return a new reference to an existing color profile if
* that profile is identical to the one that would be created, apart
* from name_part.
*/
bool
(*get_color_profile_from_icc)(struct weston_color_manager *cm,
const void *icc_data,
size_t icc_len,
const char *name_part,
struct weston_color_profile **cprof_out,
char **errmsg);
/** Send image description to clients.
*
* \param cm_image_desc_info The image description info object
* \param cprof_base The color profile that backs the image description
* \return True on success, false on failure
*
* This should be used only by the CM&HDR protocol extension
* implementation.
*
* The color manager implementing this function should use the helpers
* from color-management.c (weston_cm_send_primaries(), etc) to send the
* information to clients.
*/
bool
(*send_image_desc_info)(struct cm_image_desc_info *cm_image_desc_info,
struct weston_color_profile *cprof_base);
/** Destroy a color transform after refcount fell to zero */
void
(*destroy_color_transform)(struct weston_color_transform *xform);
/** Get surface to output's blending space transformation
*
* \param cm The color manager.
* \param surface The surface for the source color space.
* \param output The output for the destination blending color space.
* \param surf_xform For storing the color transformation and
* additional information.
*
* The callee is responsible for increasing the reference count on the
* weston_color_transform it stores into surf_xform.
*/
bool
(*get_surface_color_transform)(struct weston_color_manager *cm,
struct weston_surface *surface,
struct weston_output *output,
struct weston_surface_color_transform *surf_xform);
/** Compute derived color properties for an output
*
* \param cm The color manager.
* \param output The output.
* \return A new color_outcome object on success, NULL on failure.
*
* The callee (color manager) must inspect the weston_output (color
* profile, EOTF mode, etc.) and create a fully populated
* weston_output_color_outcome object.
*/
struct weston_output_color_outcome *
(*create_output_color_outcome)(struct weston_color_manager *cm,
struct weston_output *output);
};
void
weston_color_profile_init(struct weston_color_profile *cprof,
struct weston_color_manager *cm);
struct weston_color_transform *
weston_color_transform_ref(struct weston_color_transform *xform);
void
weston_color_transform_unref(struct weston_color_transform *xform);
void
weston_color_transform_init(struct weston_color_transform *xform,
struct weston_color_manager *cm);
char *
weston_color_transform_string(const struct weston_color_transform *xform);
void
weston_surface_color_transform_copy(struct weston_surface_color_transform *dst,
const struct weston_surface_color_transform *src);
void
weston_surface_color_transform_fini(struct weston_surface_color_transform *surf_xform);
struct weston_paint_node;
void
weston_paint_node_ensure_color_transform(struct weston_paint_node *pnode);
struct weston_color_manager *
weston_color_manager_noop_create(struct weston_compositor *compositor);
/* DSO module entrypoint */
struct weston_color_manager *
weston_color_manager_create(struct weston_compositor *compositor);
const char *
weston_eotf_mode_to_str(enum weston_eotf_mode e);
char *
weston_eotf_mask_to_str(uint32_t eotf_mask);
void
weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco);
#endif /* WESTON_COLOR_H */