color-lcms: use sRGB EOTF

Initialize LittleCMS and use it to generate the sRGB EOTF and inverse
curves. Use these curves to define the blending color space as optical
(linear) sRGB by assuming that both content and output color spaces are
sRGB.

As a consequence, this causes Weston to do "gamma correct blending", as
in, blend in light linear space which should avoid distorting colors in
alpha gradients, when color-lcms is active.

This makes use of the 3x1D LUT support added in gl-renderer earlier, and
shows how the color manager is responsible for re-using existing color
transformation objects.

Co-authored-by: Vitaly Prosyak <vitaly.prosyak@amd.com>
Signed-off-by: Vitaly Prosyak <vitaly.prosyak@amd.com>
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
dev
Pekka Paalanen 4 years ago committed by Pekka Paalanen
parent b12d4fcb53
commit 7c13c4a476
  1. 85
      libweston/color-lcms/color-lcms.c
  2. 44
      libweston/color-lcms/color-lcms.h
  3. 157
      libweston/color-lcms/color-transform.c
  4. 1
      libweston/color-lcms/meson.build

@ -1,5 +1,6 @@
/* /*
* Copyright 2021 Collabora, Ltd. * Copyright 2021 Collabora, Ltd.
* Copyright 2021 Advanced Micro Devices, Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -25,8 +26,7 @@
#include "config.h" #include "config.h"
#include <lcms2.h> #include <assert.h>
#include <libweston/libweston.h> #include <libweston/libweston.h>
#include "color.h" #include "color.h"
@ -36,6 +36,9 @@
static void static void
cmlcms_destroy_color_transform(struct weston_color_transform *xform_base) cmlcms_destroy_color_transform(struct weston_color_transform *xform_base)
{ {
struct cmlcms_color_transform *xform = get_xform(xform_base);
cmlcms_color_transform_destroy(xform);
} }
static bool static bool
@ -44,8 +47,21 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output, struct weston_output *output,
struct weston_surface_color_transform *surf_xform) struct weston_surface_color_transform *surf_xform)
{ {
/* Identity transform */ struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
surf_xform->transform = NULL; struct cmlcms_color_transform_search_param param = {
/*
* Assumes both content and output color spaces are sRGB SDR.
* This defines the blending space as optical sRGB SDR.
*/
.type = CMLCMS_TYPE_EOTF_sRGB,
};
struct cmlcms_color_transform *xform;
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
return false;
surf_xform->transform = &xform->base;
surf_xform->identity_pipeline = true; surf_xform->identity_pipeline = true;
return true; return true;
@ -56,9 +72,21 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output, struct weston_output *output,
struct weston_color_transform **xform_out) struct weston_color_transform **xform_out)
{ {
/* Identity transform */ struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
*xform_out = NULL; struct cmlcms_color_transform_search_param param = {
/*
* Assumes blending space is optical sRGB SDR and
* output color space is sRGB SDR.
*/
.type = CMLCMS_TYPE_EOTF_sRGB_INV,
};
struct cmlcms_color_transform *xform;
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
return false;
*xform_out = &xform->base;
return true; return true;
} }
@ -67,6 +95,7 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output, struct weston_output *output,
struct weston_color_transform **xform_out) struct weston_color_transform **xform_out)
{ {
/* Assumes output color space is sRGB SDR */
/* Identity transform */ /* Identity transform */
*xform_out = NULL; *xform_out = NULL;
@ -78,29 +107,61 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output, struct weston_output *output,
struct weston_color_transform **xform_out) struct weston_color_transform **xform_out)
{ {
/* Identity transform */ struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
*xform_out = NULL; struct cmlcms_color_transform_search_param param = {
/* Assumes blending space is optical sRGB SDR */
.type = CMLCMS_TYPE_EOTF_sRGB,
};
struct cmlcms_color_transform *xform;
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
return false;
*xform_out = &xform->base;
return true; return true;
} }
static void
lcms_error_logger(cmsContext context_id,
cmsUInt32Number error_code,
const char *text)
{
weston_log("LittleCMS error: %s\n", text);
}
static bool static bool
cmlcms_init(struct weston_color_manager *cm_base) cmlcms_init(struct weston_color_manager *cm_base)
{ {
if (!(cm_base->compositor->capabilities & WESTON_CAP_COLOR_OPS)) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
if (!(cm->base.compositor->capabilities & WESTON_CAP_COLOR_OPS)) {
weston_log("color-lcms: error: color operations capability missing. Is GL-renderer not in use?\n"); weston_log("color-lcms: error: color operations capability missing. Is GL-renderer not in use?\n");
return false; return false;
} }
cm->lcms_ctx = cmsCreateContext(NULL, cm);
if (!cm->lcms_ctx) {
weston_log("color-lcms error: creating LittCMS context failed.\n");
return false;
}
cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger);
weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion());
return true; return true;
} }
static void static void
cmlcms_destroy(struct weston_color_manager *cm_base) cmlcms_destroy(struct weston_color_manager *cm_base)
{ {
struct weston_color_manager_lcms *cmlcms = get_cmlcms(cm_base); struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
free(cmlcms); assert(wl_list_empty(&cm->color_transform_list));
cmsDeleteContext(cm->lcms_ctx);
free(cm);
} }
WL_EXPORT struct weston_color_manager * WL_EXPORT struct weston_color_manager *
@ -126,5 +187,7 @@ weston_color_manager_create(struct weston_compositor *compositor)
cm->base.get_sRGB_to_blend_color_transform = cm->base.get_sRGB_to_blend_color_transform =
cmlcms_get_sRGB_to_blend_color_transform; cmlcms_get_sRGB_to_blend_color_transform;
wl_list_init(&cm->color_transform_list);
return &cm->base; return &cm->base;
} }

@ -1,5 +1,6 @@
/* /*
* Copyright 2021 Collabora, Ltd. * Copyright 2021 Collabora, Ltd.
* Copyright 2021 Advanced Micro Devices, Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -26,6 +27,7 @@
#ifndef WESTON_COLOR_LCMS_H #ifndef WESTON_COLOR_LCMS_H
#define WESTON_COLOR_LCMS_H #define WESTON_COLOR_LCMS_H
#include <lcms2.h>
#include <libweston/libweston.h> #include <libweston/libweston.h>
#include "color.h" #include "color.h"
@ -33,6 +35,9 @@
struct weston_color_manager_lcms { struct weston_color_manager_lcms {
struct weston_color_manager base; struct weston_color_manager base;
cmsContext lcms_ctx;
struct wl_list color_transform_list; /* cmlcms_color_transform::link */
}; };
static inline struct weston_color_manager_lcms * static inline struct weston_color_manager_lcms *
@ -41,4 +46,43 @@ get_cmlcms(struct weston_color_manager *cm_base)
return container_of(cm_base, struct weston_color_manager_lcms, base); return container_of(cm_base, struct weston_color_manager_lcms, base);
} }
/*
* Perhaps a placeholder, until we get actual color spaces involved and
* see how this would work better.
*/
enum cmlcms_color_transform_type {
CMLCMS_TYPE_EOTF_sRGB = 0,
CMLCMS_TYPE_EOTF_sRGB_INV,
CMLCMS_TYPE__END,
};
struct cmlcms_color_transform_search_param {
enum cmlcms_color_transform_type type;
};
struct cmlcms_color_transform {
struct weston_color_transform base;
/* weston_color_manager_lcms::color_transform_list */
struct wl_list link;
struct cmlcms_color_transform_search_param search_key;
/* for EOTF types */
cmsToneCurve *curve;
};
static inline struct cmlcms_color_transform *
get_xform(struct weston_color_transform *xform_base)
{
return container_of(xform_base, struct cmlcms_color_transform, base);
}
struct cmlcms_color_transform *
cmlcms_color_transform_get(struct weston_color_manager_lcms *cm,
const struct cmlcms_color_transform_search_param *param);
void
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform);
#endif /* WESTON_COLOR_LCMS_H */ #endif /* WESTON_COLOR_LCMS_H */

@ -0,0 +1,157 @@
/*
* 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.
*/
#include "config.h"
#include <assert.h>
#include <libweston/libweston.h>
#include "color.h"
#include "color-lcms.h"
#include "shared/helpers.h"
/* Arguments to cmsBuildParametricToneCurve() */
struct tone_curve_def {
cmsInt32Number cmstype;
cmsFloat64Number params[5];
};
/*
* LCMS uses the required number of 'params' based on 'cmstype', the parametric
* tone curve number. LCMS honors negative 'cmstype' as inverse function.
* These are LCMS built-in parametric tone curves.
*/
static const struct tone_curve_def predefined_eotf_curves[] = {
[CMLCMS_TYPE_EOTF_sRGB] = {
.cmstype = 4,
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
},
[CMLCMS_TYPE_EOTF_sRGB_INV] = {
.cmstype = -4,
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
},
};
static void
cmlcms_fill_in_tone_curve(struct weston_color_transform *xform_base,
float *values, unsigned len)
{
struct cmlcms_color_transform *xform = get_xform(xform_base);
float *R_lut = values;
float *G_lut = R_lut + len;
float *B_lut = G_lut + len;
unsigned i;
cmsFloat32Number x, y;
assert(xform->curve != NULL);
assert(len > 1);
for (i = 0; i < len; i++) {
x = (double)i / (len - 1);
y = cmsEvalToneCurveFloat(xform->curve, x);
R_lut[i] = y;
G_lut[i] = y;
B_lut[i] = y;
}
}
void
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
{
wl_list_remove(&xform->link);
if (xform->curve)
cmsFreeToneCurve(xform->curve);
free(xform);
}
static struct cmlcms_color_transform *
cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
const struct cmlcms_color_transform_search_param *param)
{
struct cmlcms_color_transform *xform;
const struct tone_curve_def *tonedef;
if (param->type < 0 || param->type >= CMLCMS_TYPE__END) {
weston_log("color-lcms error: bad color transform type in %s.\n",
__func__);
return NULL;
}
tonedef = &predefined_eotf_curves[param->type];
xform = zalloc(sizeof *xform);
if (!xform)
return NULL;
xform->curve = cmsBuildParametricToneCurve(cm->lcms_ctx,
tonedef->cmstype,
tonedef->params);
if (xform->curve == NULL) {
weston_log("color-lcms error: failed to build parametric tone curve.\n");
free(xform);
return NULL;
}
weston_color_transform_init(&xform->base, &cm->base);
xform->search_key = *param;
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_tone_curve;
xform->base.pre_curve.u.lut_3x1d.optimal_len = 256;
wl_list_insert(&cm->color_transform_list, &xform->link);
return xform;
}
static bool
transform_matches_params(const struct cmlcms_color_transform *xform,
const struct cmlcms_color_transform_search_param *param)
{
if (xform->search_key.type != param->type)
return false;
return true;
}
struct cmlcms_color_transform *
cmlcms_color_transform_get(struct weston_color_manager_lcms *cm,
const struct cmlcms_color_transform_search_param *param)
{
struct cmlcms_color_transform *xform;
wl_list_for_each(xform, &cm->color_transform_list, link) {
if (transform_matches_params(xform, param)) {
weston_color_transform_ref(&xform->base);
return xform;
}
}
xform = cmlcms_color_transform_create(cm, param);
if (!xform)
weston_log("color-lcms error: failed to create a color transformation.\n");
return xform;
}

@ -9,6 +9,7 @@ endif
srcs_color_lcms = [ srcs_color_lcms = [
'color-lcms.c', 'color-lcms.c',
'color-transform.c',
] ]
deps_color_lcms = [ deps_color_lcms = [

Loading…
Cancel
Save