mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2026-01-01 18:50:14 +01:00
color: add to_shaper_plus_3dlut() vfunc to struct weston_color_transform
This function allow us to get a shaper (3x1D LUT) + 3D LUT that is equivalent to a certain xform. With that, we allow backend/renderer that are not capable of handling the color steps of the xform to fallback to something else. E.g. when we introduce the code to offload color xform to KMS using the DRM-backend, we'll depend on the driver/hardware implementing the color steps. With this patch, DRM-backend should be able to fallback to a shaper (3x1D LUT) + 3DLUT, color operations that should be commonly supported by drivers/hardwares. Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
This commit is contained in:
parent
9f1cf0db24
commit
0945ac3fa8
3 changed files with 233 additions and 0 deletions
|
|
@ -1442,6 +1442,144 @@ cmlcms_color_transform_search_param_string(const struct cmlcms_color_transform_s
|
|||
return str;
|
||||
}
|
||||
|
||||
static bool
|
||||
build_3d_lut(struct weston_compositor *compositor, cmsHTRANSFORM cmap_3dlut,
|
||||
unsigned int len_shaper, float *shaper,
|
||||
unsigned int len_lut3d, float *lut3d)
|
||||
{
|
||||
float divider = len_lut3d - 1;
|
||||
float rgb_in[3], rgb_out[3];
|
||||
uint32_t index, index_r, index_g, index_b;
|
||||
float *curves[3];
|
||||
|
||||
curves[0] = &shaper[0];
|
||||
curves[1] = &shaper[len_shaper];
|
||||
curves[2] = &shaper[2 * len_shaper];
|
||||
|
||||
for (index_b = 0; index_b < len_lut3d; index_b++) {
|
||||
for (index_g = 0; index_g < len_lut3d; index_g++) {
|
||||
for (index_r = 0; index_r < len_lut3d; index_r++) {
|
||||
/**
|
||||
* For each channel, use the shaper to compute
|
||||
* the value x such that y(x) = index / divider.
|
||||
* As the shapper is a LUT, we find the closest
|
||||
* neighbors of such point (x, y) and then use
|
||||
* linear interpolation to estimate x.
|
||||
*/
|
||||
rgb_in[0] = weston_inverse_evaluate_lut1d(compositor,len_shaper,
|
||||
curves[0],
|
||||
(float)index_r / divider);
|
||||
rgb_in[1] = weston_inverse_evaluate_lut1d(compositor, len_shaper,
|
||||
curves[1],
|
||||
(float)index_g / divider);
|
||||
rgb_in[2] = weston_inverse_evaluate_lut1d(compositor, len_shaper,
|
||||
curves[2],
|
||||
(float)index_b / divider);
|
||||
|
||||
cmsDoTransform(cmap_3dlut, rgb_in, rgb_out, 1);
|
||||
|
||||
index = 3 * (index_r + len_lut3d * (index_g + len_lut3d * index_b));
|
||||
lut3d[index ] = rgb_out[0];
|
||||
lut3d[index + 1] = rgb_out[1];
|
||||
lut3d[index + 2] = rgb_out[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
build_shaper(cmsContext lcms_ctx, cmsHTRANSFORM cmap_3dlut,
|
||||
unsigned int len_shaper, float *shaper)
|
||||
{
|
||||
float *curves[3];
|
||||
float divider = len_shaper - 1;
|
||||
float rgb_in[3], rgb_out[3];
|
||||
cmsToneCurve *tc[3] = { NULL };
|
||||
unsigned int ch, i;
|
||||
float smoothing_param;
|
||||
bool ret = true;
|
||||
|
||||
/**
|
||||
* We use cmsSmoothToneCurve() for:
|
||||
*
|
||||
* a) checking monotonicity and degenerated curves;
|
||||
* b) getting rid of abrupt changes;
|
||||
*
|
||||
* A lambda between 0.0 and 1.0 is usually enough. 1.0 means moderate to
|
||||
* high smooth. We just want a mild smoothing, so we arbitrarily
|
||||
* hardcoded this value.
|
||||
*/
|
||||
smoothing_param = 0.3f;
|
||||
|
||||
curves[0] = &shaper[0];
|
||||
curves[1] = &shaper[len_shaper];
|
||||
curves[2] = &shaper[2 * len_shaper];
|
||||
|
||||
for (i = 0; i < len_shaper; i++) {
|
||||
rgb_in[0] = rgb_in[1] = rgb_in[2] = (float)i / divider;
|
||||
cmsDoTransform(cmap_3dlut, rgb_in, rgb_out, 1);
|
||||
for (ch = 0; ch < 3; ch++)
|
||||
curves[ch][i] = ensure_unorm(rgb_out[ch]);
|
||||
}
|
||||
|
||||
for (ch = 0; ch < 3; ch++) {
|
||||
tc[ch] = cmsBuildTabulatedToneCurveFloat(lcms_ctx, len_shaper,
|
||||
curves[ch]);
|
||||
if (!tc[ch]) {
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: that should fail if the curves are not monotonic. Try
|
||||
* to make curve monotonic if possible before calling this.
|
||||
*/
|
||||
ret = cmsSmoothToneCurve(tc[ch], smoothing_param);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < len_shaper; i++)
|
||||
curves[ch][i] = cmsEvalToneCurveFloat(tc[ch],
|
||||
(float)i / divider);
|
||||
}
|
||||
|
||||
out:
|
||||
cmsFreeToneCurveTriple(tc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on [1]. We get cmsHTRANSFORM cmap_3dlut and decompose into a shaper
|
||||
* (3x1D LUT) + 3D LUT. With that, we can reduce the 3D LUT dimension size
|
||||
* without loosing precision. 3D LUT dimension size is problematic because it
|
||||
* demands n³ memory. In this function we construct such shaper.
|
||||
*
|
||||
* [1] https://www.littlecms.com/ASICprelinerization_CGIV08.pdf
|
||||
*/
|
||||
static bool
|
||||
xform_to_shaper_plus_3dlut(struct weston_color_transform *xform_base,
|
||||
uint32_t len_shaper, float *shaper,
|
||||
uint32_t len_lut3d, float *lut3d)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = to_cmlcms_xform(xform_base);
|
||||
struct weston_compositor *compositor = xform_base->cm->compositor;
|
||||
bool ret;
|
||||
|
||||
ret = build_shaper(xform->lcms_ctx, xform->cmap_3dlut,
|
||||
len_shaper, shaper);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
ret = build_3d_lut(compositor, xform->cmap_3dlut,
|
||||
len_shaper, shaper, len_lut3d, lut3d);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct cmlcms_color_transform *
|
||||
cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
||||
const struct cmlcms_color_transform_search_param *search_param)
|
||||
|
|
@ -1453,6 +1591,7 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
|||
xform = xzalloc(sizeof *xform);
|
||||
weston_color_transform_init(&xform->base, &cm->base);
|
||||
wl_list_init(&xform->link);
|
||||
xform->base.to_shaper_plus_3dlut = xform_to_shaper_plus_3dlut;
|
||||
xform->search_key = *search_param;
|
||||
xform->search_key.input_profile = ref_cprof(search_param->input_profile);
|
||||
xform->search_key.output_profile = ref_cprof(search_param->output_profile);
|
||||
|
|
|
|||
|
|
@ -408,6 +408,73 @@ weston_color_curve_to_3x1D_LUT(struct weston_compositor *compositor,
|
|||
weston_assert_not_reached(compositor, "unkown color curve");
|
||||
}
|
||||
|
||||
static float
|
||||
linear_interpolation(float x, float x0, float y0, float x1, float y1)
|
||||
{
|
||||
float delta = x1 - x0;
|
||||
|
||||
/* x0 == x1, 5 digits precision. */
|
||||
if (fabs(delta) < 1e-5)
|
||||
return (y0 + y1) / 2.0f;
|
||||
|
||||
return y0 * ((x1 - x) / delta) + y1 * ((x - x0) / delta);
|
||||
}
|
||||
|
||||
WESTON_EXPORT_FOR_TESTS void
|
||||
find_neighbors(struct weston_compositor *compositor, uint32_t len, float *array,
|
||||
float val, uint32_t *neigh_A_index, uint32_t *neigh_B_index)
|
||||
{
|
||||
bool ascendent = (array[0] <= array[len - 1]);
|
||||
int32_t left = 0;
|
||||
int32_t right = len - 1;
|
||||
int32_t mid;
|
||||
|
||||
/* We need at least 2 elements in the array. */
|
||||
weston_assert_uint32_gt(compositor, len, 1);
|
||||
|
||||
while (right - left > 1) {
|
||||
mid = left + ((right - left) / 2);
|
||||
|
||||
if ((ascendent && array[mid] < val) ||
|
||||
(!ascendent && array[mid] > val))
|
||||
left = mid;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
|
||||
*neigh_A_index = left;
|
||||
*neigh_B_index = right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a 1D LUT, this evaluates a given input using the inverse of the LUT.
|
||||
*
|
||||
* If the input is out of the LUT range, this extrapolates using the two closest
|
||||
* elements present in the LUT.
|
||||
*
|
||||
* \param compositor The compositor instance.
|
||||
* \param len_lut The size of the 1D LUT.
|
||||
* \param lut The 1D lut.
|
||||
* \param input The input to evaluate
|
||||
* \return The evaluation result.
|
||||
*/
|
||||
WL_EXPORT float
|
||||
weston_inverse_evaluate_lut1d(struct weston_compositor *compositor,
|
||||
uint32_t len_lut, float *lut, float input)
|
||||
{
|
||||
float divider = len_lut - 1;
|
||||
uint32_t neighbor_A_index, neighbor_B_index;
|
||||
|
||||
find_neighbors(compositor, len_lut, lut, input,
|
||||
&neighbor_A_index, &neighbor_B_index);
|
||||
|
||||
return linear_interpolation(input,
|
||||
lut[neighbor_A_index],
|
||||
(float)neighbor_A_index / divider,
|
||||
lut[neighbor_B_index],
|
||||
(float)neighbor_B_index / divider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase reference count of the color transform object
|
||||
*
|
||||
|
|
|
|||
|
|
@ -441,6 +441,25 @@ struct weston_color_transform {
|
|||
|
||||
/** Step 4: color curve after color mapping */
|
||||
struct weston_color_curve post_curve;
|
||||
|
||||
/**
|
||||
* Decompose the color transformation into a shaper (3x1D LUT) and a 3D
|
||||
* LUT.
|
||||
*
|
||||
* \param xform_base The color transformation to decompose.
|
||||
* \param len_shaper Number of taps in each of the 1D LUT.
|
||||
* \param shaper Where the shaper is saved, caller's responsibility to
|
||||
* allocate.
|
||||
* \param len_lut3d The 3D LUT's length for each dimension.
|
||||
* \param lut3d Where the 3D LUT is saved, caller's responsibility to
|
||||
* allocate. Its layout on memory is: lut3d[B][G][R], i.e. R is the
|
||||
* innermost and its index grow faster, followed by G and then B.
|
||||
* \return True on success, false otherwise.
|
||||
*/
|
||||
bool
|
||||
(*to_shaper_plus_3dlut)(struct weston_color_transform *xform_base,
|
||||
uint32_t len_shaper, float *shaper,
|
||||
uint32_t len_lut3d, float *lut3d);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -678,6 +697,14 @@ weston_color_curve_to_3x1D_LUT(struct weston_compositor *compositor,
|
|||
enum weston_color_curve_step step,
|
||||
uint32_t lut_size, char **err_msg);
|
||||
|
||||
void
|
||||
find_neighbors(struct weston_compositor *compositor, uint32_t len, float *array,
|
||||
float val, uint32_t *neigh_A_index, uint32_t *neigh_B_index);
|
||||
|
||||
float
|
||||
weston_inverse_evaluate_lut1d(struct weston_compositor *compositor,
|
||||
uint32_t len_lut, float *lut, float input);
|
||||
|
||||
struct weston_color_transform *
|
||||
weston_color_transform_ref(struct weston_color_transform *xform);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue