You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
8.0 KiB
287 lines
8.0 KiB
/*
|
|
* 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"
|
|
|
|
/**
|
|
* The method is used in linearization of an arbitrary color profile
|
|
* when EOTF is retrieved we want to know a generic way to decide the number
|
|
* of points
|
|
*/
|
|
unsigned int
|
|
cmlcms_reasonable_1D_points(void)
|
|
{
|
|
return 1024;
|
|
}
|
|
|
|
static unsigned int
|
|
cmlcms_reasonable_3D_points(void)
|
|
{
|
|
return 33;
|
|
}
|
|
|
|
static void
|
|
fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len)
|
|
{
|
|
float *R_lut = values;
|
|
float *G_lut = R_lut + len;
|
|
float *B_lut = G_lut + len;
|
|
unsigned i;
|
|
cmsFloat32Number x;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
x = (double)i / (len - 1);
|
|
R_lut[i] = cmsEvalToneCurveFloat(curves[0], x);
|
|
G_lut[i] = cmsEvalToneCurveFloat(curves[1], x);
|
|
B_lut[i] = cmsEvalToneCurveFloat(curves[2], x);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base,
|
|
float *values, unsigned len)
|
|
{
|
|
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
|
|
|
assert(xform->search_key.category == CMLCMS_CATEGORY_BLEND_TO_OUTPUT);
|
|
|
|
assert(len > 1);
|
|
|
|
fill_in_curves(xform->search_key.output_profile->output_inv_eotf_vcgt,
|
|
values, len);
|
|
}
|
|
|
|
/**
|
|
* 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 void
|
|
cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base,
|
|
float *lut, unsigned int len)
|
|
{
|
|
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
|
float rgb_in[3];
|
|
float rgb_out[3];
|
|
unsigned int index;
|
|
unsigned int value_b, value_r, value_g;
|
|
float divider = len - 1;
|
|
|
|
assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND ||
|
|
xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT);
|
|
|
|
for (value_b = 0; value_b < len; value_b++) {
|
|
for (value_g = 0; value_g < len; value_g++) {
|
|
for (value_r = 0; value_r < len; value_r++) {
|
|
rgb_in[0] = (float)value_r / divider;
|
|
rgb_in[1] = (float)value_g / divider;
|
|
rgb_in[2] = (float)value_b / divider;
|
|
|
|
cmsDoTransform(xform->cmap_3dlut, rgb_in, rgb_out, 1);
|
|
|
|
index = 3 * (value_r + len * (value_g + len * value_b));
|
|
lut[index ] = ensure_unorm(rgb_out[0]);
|
|
lut[index + 1] = ensure_unorm(rgb_out[1]);
|
|
lut[index + 2] = ensure_unorm(rgb_out[2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
|
|
{
|
|
wl_list_remove(&xform->link);
|
|
|
|
if (xform->cmap_3dlut)
|
|
cmsDeleteTransform(xform->cmap_3dlut);
|
|
|
|
unref_cprof(xform->search_key.input_profile);
|
|
unref_cprof(xform->search_key.output_profile);
|
|
free(xform);
|
|
}
|
|
|
|
static bool
|
|
xform_set_cmap_3dlut(struct cmlcms_color_transform *xform,
|
|
cmsHPROFILE input_profile,
|
|
cmsHPROFILE output_profile,
|
|
cmsToneCurve *curves[3],
|
|
cmsUInt32Number intent)
|
|
{
|
|
struct weston_color_manager_lcms *cm = get_cmlcms(xform->base.cm);
|
|
cmsHPROFILE arr_prof[3] = { input_profile, output_profile, NULL };
|
|
int num_profiles = 2;
|
|
|
|
if (curves[0]) {
|
|
arr_prof[2] = cmsCreateLinearizationDeviceLinkTHR(cm->lcms_ctx,
|
|
cmsSigRgbData,
|
|
curves);
|
|
if (!arr_prof[2])
|
|
return false;
|
|
|
|
num_profiles = 3;
|
|
}
|
|
|
|
xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(cm->lcms_ctx,
|
|
arr_prof,
|
|
num_profiles,
|
|
TYPE_RGB_FLT,
|
|
TYPE_RGB_FLT,
|
|
intent,
|
|
0);
|
|
if (!xform->cmap_3dlut) {
|
|
cmsCloseProfile(arr_prof[2]);
|
|
weston_log("color-lcms error: fail cmsCreateMultiprofileTransformTHR.\n");
|
|
return false;
|
|
}
|
|
xform->base.mapping.type = WESTON_COLOR_MAPPING_TYPE_3D_LUT;
|
|
xform->base.mapping.u.lut3d.fill_in = cmlcms_fill_in_3dlut;
|
|
xform->base.mapping.u.lut3d.optimal_len =
|
|
cmlcms_reasonable_3D_points();
|
|
cmsCloseProfile(arr_prof[2]);
|
|
|
|
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)
|
|
{
|
|
struct cmlcms_color_profile *input_profile = search_param->input_profile;
|
|
struct cmlcms_color_profile *output_profile = search_param->output_profile;
|
|
struct cmlcms_color_transform *xform;
|
|
bool ok = false;
|
|
|
|
xform = zalloc(sizeof *xform);
|
|
if (!xform)
|
|
return NULL;
|
|
|
|
weston_color_transform_init(&xform->base, &cm->base);
|
|
wl_list_init(&xform->link);
|
|
xform->search_key = *search_param;
|
|
xform->search_key.input_profile = ref_cprof(input_profile);
|
|
xform->search_key.output_profile = ref_cprof(output_profile);
|
|
|
|
/* Ensure the linearization etc. have been extracted. */
|
|
if (!output_profile->output_eotf[0]) {
|
|
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
|
|
output_profile->profile,
|
|
output_profile->output_eotf,
|
|
output_profile->output_inv_eotf_vcgt,
|
|
output_profile->vcgt,
|
|
cmlcms_reasonable_1D_points()))
|
|
goto error;
|
|
}
|
|
|
|
switch (search_param->category) {
|
|
case CMLCMS_CATEGORY_INPUT_TO_BLEND:
|
|
/* Use EOTF to linearize the result. */
|
|
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
|
output_profile->profile,
|
|
output_profile->output_eotf,
|
|
search_param->intent_output);
|
|
break;
|
|
|
|
case CMLCMS_CATEGORY_INPUT_TO_OUTPUT:
|
|
/* Apply also VCGT if it exists. */
|
|
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
|
output_profile->profile,
|
|
output_profile->vcgt,
|
|
search_param->intent_output);
|
|
break;
|
|
|
|
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
|
|
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
|
|
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve;
|
|
xform->base.pre_curve.u.lut_3x1d.optimal_len =
|
|
cmlcms_reasonable_1D_points();
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
if (!ok)
|
|
goto error;
|
|
|
|
wl_list_insert(&cm->color_transform_list, &xform->link);
|
|
return xform;
|
|
|
|
error:
|
|
cmlcms_color_transform_destroy(xform);
|
|
weston_log("CM cmlcms_color_transform_create failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
static bool
|
|
transform_matches_params(const struct cmlcms_color_transform *xform,
|
|
const struct cmlcms_color_transform_search_param *param)
|
|
{
|
|
if (xform->search_key.category != param->category)
|
|
return false;
|
|
|
|
if (xform->search_key.intent_output != param->intent_output ||
|
|
xform->search_key.output_profile != param->output_profile ||
|
|
xform->search_key.input_profile != param->input_profile)
|
|
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;
|
|
}
|
|
|