mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2026-02-04 01:20:37 +01:00
Merge branch 'mr/stmeta' into 'main'
Remove weston_color_characteristics See merge request wayland/weston!1956
This commit is contained in:
commit
dced8ecfeb
24 changed files with 895 additions and 1745 deletions
165
frontend/main.c
165
frontend/main.c
|
|
@ -2104,13 +2104,7 @@ wet_output_set_color_profile(struct weston_output *output,
|
|||
} else if (parent_winsys_profile) {
|
||||
cprof = weston_color_profile_ref(parent_winsys_profile);
|
||||
} else {
|
||||
/*
|
||||
* TODO: Once parametric color profiles are fully supported
|
||||
* and interoperable with ICC profiles, the default profile
|
||||
* would be created like this:
|
||||
* cprof = wet_create_output_color_profile(output, wc, "auto:");
|
||||
*/
|
||||
return 0;
|
||||
cprof = wet_create_output_color_profile(output, wc, "auto:");
|
||||
}
|
||||
|
||||
if (!cprof)
|
||||
|
|
@ -2248,156 +2242,6 @@ wet_output_set_colorimetry_mode(struct weston_output *output,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct wet_color_characteristics_keys {
|
||||
const char *name;
|
||||
enum weston_color_characteristics_groups group;
|
||||
float minval;
|
||||
float maxval;
|
||||
};
|
||||
|
||||
#define COLOR_CHARAC_NAME "color_characteristics"
|
||||
|
||||
static int
|
||||
parse_color_characteristics(struct weston_color_characteristics *cc_out,
|
||||
struct weston_config_section *section)
|
||||
{
|
||||
static const struct wet_color_characteristics_keys keys[] = {
|
||||
{ "red_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "red_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "green_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "green_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "blue_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "blue_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "white_x", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
|
||||
{ "white_y", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
|
||||
{ "max_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL, 0.0f, 1e5f },
|
||||
{ "min_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MINL, 0.0f, 1e5f },
|
||||
{ "maxFALL", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL, 0.0f, 1e5f },
|
||||
};
|
||||
static const char *msgpfx = "Config error in weston.ini [" COLOR_CHARAC_NAME "]";
|
||||
struct weston_color_characteristics cc = {};
|
||||
float *const keyvalp[ARRAY_LENGTH(keys)] = {
|
||||
/* These must be in the same order as keys[]. */
|
||||
&cc.primary[0].x, &cc.primary[0].y,
|
||||
&cc.primary[1].x, &cc.primary[1].y,
|
||||
&cc.primary[2].x, &cc.primary[2].y,
|
||||
&cc.white.x, &cc.white.y,
|
||||
&cc.max_luminance,
|
||||
&cc.min_luminance,
|
||||
&cc.maxFALL,
|
||||
};
|
||||
bool found[ARRAY_LENGTH(keys)] = {};
|
||||
uint32_t missing_group_mask = 0;
|
||||
unsigned i;
|
||||
char *section_name;
|
||||
int ret = 0;
|
||||
|
||||
weston_config_section_get_string(section, "name",
|
||||
§ion_name, "<unnamed>");
|
||||
if (strchr(section_name, ':') != NULL) {
|
||||
ret = -1;
|
||||
weston_log("%s name=%s is a reserved name. Do not use ':' character in the name.\n",
|
||||
msgpfx, section_name);
|
||||
}
|
||||
|
||||
/* Parse keys if they exist */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
double value;
|
||||
|
||||
if (weston_config_section_get_double(section, keys[i].name,
|
||||
&value, NAN) == 0) {
|
||||
float f = value;
|
||||
|
||||
found[i] = true;
|
||||
|
||||
/* Range check, NaN shall not pass. */
|
||||
if (f >= keys[i].minval && f <= keys[i].maxval) {
|
||||
/* Key found, parsed, and good value. */
|
||||
*keyvalp[i] = f;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: %s value %f is outside of the range %f - %f.\n",
|
||||
msgpfx, section_name, keys[i].name, value,
|
||||
keys[i].minval, keys[i].maxval);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == EINVAL) {
|
||||
found[i] = true;
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: failed to parse the value of key %s.\n",
|
||||
msgpfx, section_name, keys[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Collect set and unset groups */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
uint32_t group = keys[i].group;
|
||||
|
||||
if (found[i])
|
||||
cc.group_mask |= group;
|
||||
else
|
||||
missing_group_mask |= group;
|
||||
}
|
||||
|
||||
/* Ensure groups are given fully or not at all. */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
uint32_t group = keys[i].group;
|
||||
|
||||
if ((cc.group_mask & group) && (missing_group_mask & group)) {
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: group %d key %s is %s. "
|
||||
"You must set either none or all keys of a group.\n",
|
||||
msgpfx, section_name, ffs(group), keys[i].name,
|
||||
found[i] ? "set" : "missing");
|
||||
}
|
||||
}
|
||||
|
||||
free(section_name);
|
||||
|
||||
if (ret == 0)
|
||||
*cc_out = cc;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
WESTON_EXPORT_FOR_TESTS int
|
||||
wet_output_set_color_characteristics(struct weston_output *output,
|
||||
struct weston_config *wc,
|
||||
struct weston_config_section *section)
|
||||
{
|
||||
char *cc_name = NULL;
|
||||
struct weston_config_section *cc_section;
|
||||
struct weston_color_characteristics cc;
|
||||
|
||||
weston_config_section_get_string(section, COLOR_CHARAC_NAME,
|
||||
&cc_name, NULL);
|
||||
if (!cc_name)
|
||||
return 0;
|
||||
|
||||
cc_section = weston_config_get_section(wc, COLOR_CHARAC_NAME,
|
||||
"name", cc_name);
|
||||
if (!cc_section) {
|
||||
weston_log("Config error in weston.ini, output %s: "
|
||||
"no [" COLOR_CHARAC_NAME "] section with 'name=%s' found.\n",
|
||||
output->name, cc_name);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (parse_color_characteristics(&cc, cc_section) < 0)
|
||||
goto out_error;
|
||||
|
||||
weston_output_set_color_characteristics(output, &cc);
|
||||
free(cc_name);
|
||||
return 0;
|
||||
|
||||
out_error:
|
||||
free(cc_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
wet_output_set_color_format(struct weston_output *output,
|
||||
struct weston_config_section *section)
|
||||
|
|
@ -3203,10 +3047,6 @@ drm_backend_output_configure(struct weston_output *output,
|
|||
if (wet_output_set_color_format(output, section) < 0)
|
||||
return -1;
|
||||
|
||||
if (wet_output_set_color_characteristics(output,
|
||||
wet->config, section) < 0)
|
||||
return -1;
|
||||
|
||||
if (wet_output_set_vrr_mode(output, section) < 0)
|
||||
return -1;
|
||||
|
||||
|
|
@ -4287,9 +4127,6 @@ headless_backend_output_configure(struct weston_output *output)
|
|||
if (wet_output_set_colorimetry_mode(output, section, wet->use_color_manager) < 0)
|
||||
return -1;
|
||||
|
||||
if (wet_output_set_color_characteristics(output, wc, section) < 0)
|
||||
return -1;
|
||||
|
||||
return wet_configure_windowed_output_from_config(output, &defaults,
|
||||
WESTON_WINDOWED_OUTPUT_HEADLESS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,6 @@ bool
|
|||
get_renderer_from_string(const char *name,
|
||||
enum weston_renderer_type *renderer);
|
||||
|
||||
int
|
||||
wet_output_set_color_characteristics(struct weston_output *output,
|
||||
struct weston_config *wc,
|
||||
struct weston_config_section *section);
|
||||
|
||||
int
|
||||
wet_output_set_eotf_mode(struct weston_output *output,
|
||||
struct weston_config_section *section,
|
||||
|
|
|
|||
|
|
@ -286,57 +286,6 @@ weston_color_profile_param_builder_create_color_profile(struct weston_color_prof
|
|||
enum weston_color_profile_param_builder_error *err,
|
||||
char **err_msg);
|
||||
|
||||
enum weston_color_characteristics_groups {
|
||||
/** weston_color_characteristics::primary is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES = 0x01,
|
||||
|
||||
/** weston_color_characteristics::white is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE = 0x02,
|
||||
|
||||
/** weston_color_characteristics::max_luminance is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL = 0x04,
|
||||
|
||||
/** weston_color_characteristics::min_luminance is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MINL = 0x08,
|
||||
|
||||
/** weston_color_characteristics::maxFALL is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL = 0x10,
|
||||
|
||||
/** all valid bits */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK = 0x1f
|
||||
};
|
||||
|
||||
/** Basic display color characteristics
|
||||
*
|
||||
* This is a simple description of a display or output (monitor) color
|
||||
* characteristics. The parameters can be found in EDID, with caveats. They
|
||||
* are particularly useful with HDR monitors.
|
||||
*/
|
||||
struct weston_color_characteristics {
|
||||
/** Which fields are valid
|
||||
*
|
||||
* A bitmask of values from enum weston_color_characteristics_groups.
|
||||
*/
|
||||
uint32_t group_mask;
|
||||
|
||||
/* EOTF is tracked externally with enum weston_eotf_mode */
|
||||
|
||||
/** Chromaticities of the primaries */
|
||||
struct weston_CIExy primary[3];
|
||||
|
||||
/** White point chromaticity */
|
||||
struct weston_CIExy white;
|
||||
|
||||
/** Display's desired maximum content peak luminance, cd/m² */
|
||||
float max_luminance;
|
||||
|
||||
/** Display's desired minimum content luminance, cd/m² */
|
||||
float min_luminance;
|
||||
|
||||
/** Display's desired maximum frame-average light level, cd/m² */
|
||||
float maxFALL;
|
||||
};
|
||||
|
||||
struct weston_color_profile *
|
||||
weston_color_profile_ref(struct weston_color_profile *cprof);
|
||||
|
||||
|
|
|
|||
|
|
@ -520,7 +520,6 @@ struct weston_output {
|
|||
bool from_blend_to_output_by_backend;
|
||||
enum weston_eotf_mode eotf_mode;
|
||||
enum weston_colorimetry_mode colorimetry_mode;
|
||||
struct weston_color_characteristics color_characteristics;
|
||||
|
||||
struct weston_output_color_outcome *color_outcome;
|
||||
uint64_t color_outcome_serial;
|
||||
|
|
@ -2695,13 +2694,6 @@ weston_output_set_colorimetry_mode(struct weston_output *output,
|
|||
enum weston_colorimetry_mode
|
||||
weston_output_get_colorimetry_mode(const struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_set_color_characteristics(struct weston_output *output,
|
||||
const struct weston_color_characteristics *cc);
|
||||
|
||||
const struct weston_color_characteristics *
|
||||
weston_output_get_color_characteristics(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_init(struct weston_output *output,
|
||||
struct weston_compositor *compositor,
|
||||
|
|
|
|||
|
|
@ -531,7 +531,7 @@ join_powerlaw_curvesets(cmsContext context_id,
|
|||
ret = cmsStageAllocToneCurves(context_id, ARRAY_LENGTH(arr), arr);
|
||||
abort_oom_if_null(ret);
|
||||
cmsFreeToneCurveTriple(arr);
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -573,6 +573,56 @@ curveset_print(cmsStage *stage, struct weston_log_scope *scope)
|
|||
}
|
||||
}
|
||||
|
||||
static const cmsCurveSegment *
|
||||
get_defining_curve_segment(cmsToneCurve *from, bool *clamped_input)
|
||||
{
|
||||
const cmsCurveSegment *seg0, *seg1, *seg2;
|
||||
|
||||
/* We handle curves with 1 or 3 segments. No more, no less. */
|
||||
seg0 = cmsGetToneCurveSegment(0, from);
|
||||
seg1 = cmsGetToneCurveSegment(1, from);
|
||||
seg2 = cmsGetToneCurveSegment(2, from);
|
||||
if (seg0 && !seg1) {
|
||||
/* Case 1: we have a single segment (seg0). */
|
||||
|
||||
/* If the domain is (-inf, inf), the curve is unbounded. */
|
||||
if (are_segment_breaks_equal(seg0->x0, -INFINITY) &&
|
||||
are_segment_breaks_equal(seg0->x1, INFINITY)) {
|
||||
*clamped_input = false;
|
||||
return seg0;
|
||||
}
|
||||
|
||||
/* If the domain is [0.0, 1.0], the curve is bounded. */
|
||||
if (are_segment_breaks_equal(seg0->x0, 0.0) &&
|
||||
are_segment_breaks_equal(seg0->x1, 1.0)) {
|
||||
*clamped_input = true;
|
||||
return seg0;
|
||||
}
|
||||
|
||||
/* We don't handle anything else. */
|
||||
return NULL;
|
||||
} else if (seg0 && seg1 && seg2) {
|
||||
/* Case 2: we have three segments. Clamped input.
|
||||
*
|
||||
* Ensure that the domain breaks are (-inf, 0.0],
|
||||
* (0.0, 1.0] and (1.0, inf].
|
||||
*/
|
||||
if (!are_segment_breaks_equal(seg0->x0, -INFINITY) ||
|
||||
!are_segment_breaks_equal(seg0->x1, 0.0) ||
|
||||
!are_segment_breaks_equal(seg1->x0, 0.0) ||
|
||||
!are_segment_breaks_equal(seg1->x1, 1.0) ||
|
||||
!are_segment_breaks_equal(seg2->x0, 1.0) ||
|
||||
!are_segment_breaks_equal(seg2->x1, INFINITY))
|
||||
return NULL;
|
||||
*clamped_input = true;
|
||||
return seg1;
|
||||
} else {
|
||||
/* Neither 1 or 3 segments. So we don't define the
|
||||
* curveset as parametric. */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
get_parametric_curveset_params(struct weston_compositor *compositor,
|
||||
_cmsStageToneCurvesData *trc_data,
|
||||
|
|
@ -580,7 +630,7 @@ get_parametric_curveset_params(struct weston_compositor *compositor,
|
|||
float curveset_params[3][MAX_PARAMS_LCMS_PARAM_CURVE],
|
||||
bool *clamped_input)
|
||||
{
|
||||
const cmsCurveSegment *seg, *seg0, *seg1, *seg2;
|
||||
const cmsCurveSegment *seg;
|
||||
cmsInt32Number curve_types[3];
|
||||
unsigned int i, j;
|
||||
|
||||
|
|
@ -596,44 +646,18 @@ get_parametric_curveset_params(struct weston_compositor *compositor,
|
|||
*clamped_input = false;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* We handle curves with 1 or 3 segments. No more, no less. */
|
||||
seg0 = cmsGetToneCurveSegment(0, trc_data->TheCurves[i]);
|
||||
seg1 = cmsGetToneCurveSegment(1, trc_data->TheCurves[i]);
|
||||
seg2 = cmsGetToneCurveSegment(2, trc_data->TheCurves[i]);
|
||||
bool clamp_this;
|
||||
|
||||
if (seg0 && !seg1) {
|
||||
/* Case 1: we have a single segment (seg0).
|
||||
*
|
||||
* Ensure that the domain is (-inf, inf) and that the
|
||||
* seg type is not 0 (the type of sampled segments).
|
||||
*/
|
||||
if (!are_segment_breaks_equal(seg0->x0, -INFINITY) ||
|
||||
!are_segment_breaks_equal(seg0->x1, INFINITY) ||
|
||||
seg0->Type == 0)
|
||||
return false;
|
||||
seg = seg0;
|
||||
} else if (seg0 && seg1 && seg2) {
|
||||
/* Case 2: we have three segments. Clamped input.
|
||||
*
|
||||
* Ensure that the domain breaks are (-inf, 0.0],
|
||||
* (0.0, 1.0] and (1.0, inf] and that the 2nd segment
|
||||
* type is not 0 (the type of sampled segments).
|
||||
*/
|
||||
if (!are_segment_breaks_equal(seg0->x0, -INFINITY) ||
|
||||
!are_segment_breaks_equal(seg0->x1, 0.0) ||
|
||||
!are_segment_breaks_equal(seg1->x0, 0.0) ||
|
||||
!are_segment_breaks_equal(seg1->x1, 1.0) ||
|
||||
!are_segment_breaks_equal(seg2->x0, 1.0) ||
|
||||
!are_segment_breaks_equal(seg2->x1, INFINITY) ||
|
||||
seg1->Type == 0)
|
||||
return false;
|
||||
seg = seg1;
|
||||
*clamped_input = true;
|
||||
} else {
|
||||
/* Neither 1 or 3 segments. So we don't define the
|
||||
* curveset as parametric. */
|
||||
seg = get_defining_curve_segment(trc_data->TheCurves[i], &clamp_this);
|
||||
if (!seg)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reject tabulated (LUT) segments. */
|
||||
if (seg->Type == 0)
|
||||
return false;
|
||||
|
||||
if (clamp_this)
|
||||
*clamped_input = true;
|
||||
|
||||
/* Copy the type and params from the segment that matters. We
|
||||
* don't use memcpy because we need to cast each cmsFloat64Number
|
||||
|
|
@ -652,3 +676,58 @@ get_parametric_curveset_params(struct weston_compositor *compositor,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Create a matrix stage equivalent to the CurveSet stage.
|
||||
*
|
||||
* A tabulated curve segment with 2 samples is equivalent to a matrix
|
||||
* (scaling and offset), ignoring possible input clamping and allowing
|
||||
* extrapolation.
|
||||
*
|
||||
* \param context_id The matrix stage is created in this context.
|
||||
* \param stage An arbitrary stage, can be NULL.
|
||||
* \return A new matrix stage equivalent to the given (CurveSet) stage, or
|
||||
* NULL otherwise.
|
||||
*/
|
||||
cmsStage *
|
||||
lcms_matrix_stage_from_curve(cmsContext context_id, cmsStage *stage)
|
||||
{
|
||||
const _cmsStageToneCurvesData *data;
|
||||
cmsFloat64Number Matrix[3 * 3] = {}; /* row-major */
|
||||
cmsFloat64Number Offset[3];
|
||||
unsigned i;
|
||||
|
||||
if (!stage || cmsStageType(stage) != cmsSigCurveSetElemType)
|
||||
return NULL;
|
||||
|
||||
data = cmsStageData(stage);
|
||||
if (data->nCurves != 3)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
const cmsCurveSegment *seg;
|
||||
bool clamped_input;
|
||||
double y0, y1, k;
|
||||
|
||||
seg = get_defining_curve_segment(data->TheCurves[i], &clamped_input);
|
||||
if (!seg)
|
||||
return NULL;
|
||||
|
||||
/* Type 0 is tabulated. */
|
||||
if (seg->Type != 0)
|
||||
return NULL;
|
||||
|
||||
if (seg->nGridPoints != 2)
|
||||
return NULL;
|
||||
|
||||
y0 = seg->SampledPoints[0];
|
||||
y1 = seg->SampledPoints[1];
|
||||
|
||||
/* y = k * x + Offset */
|
||||
|
||||
k = (y1 - y0) / (seg->x1 - seg->x0);
|
||||
Offset[i] = y0 - k * seg->x0;
|
||||
Matrix[3 * i + i] = k;
|
||||
}
|
||||
|
||||
return cmsStageAllocMatrix(context_id, 3, 3, Matrix, Offset);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ get_parametric_curveset_params(struct weston_compositor *compositor,
|
|||
float curveset_params[3][MAX_PARAMS_LCMS_PARAM_CURVE],
|
||||
bool *clamped_input);
|
||||
|
||||
cmsStage *
|
||||
lcms_matrix_stage_from_curve(cmsContext context_id, cmsStage *stage);
|
||||
|
||||
void
|
||||
curveset_print(cmsStage *stage, struct weston_log_scope *scope);
|
||||
|
||||
|
|
@ -64,6 +67,12 @@ get_parametric_curveset_params(struct weston_compositor *compositor,
|
|||
return false;
|
||||
}
|
||||
|
||||
cmsStage *
|
||||
lcms_matrix_stage_from_curve(cmsContext ContextID, cmsStage *stage)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
curveset_print(cmsStage *stage, struct weston_log_scope *scope)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
|
|||
|
||||
static float
|
||||
meta_clamp(float value, const char *valname, float min, float max,
|
||||
struct weston_output *output)
|
||||
const struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
float ret = value;
|
||||
|
||||
|
|
@ -207,78 +207,71 @@ meta_clamp(float value, const char *valname, float min, float max,
|
|||
ret = max;
|
||||
|
||||
if (ret != value) {
|
||||
weston_log("output '%s' clamping %s value from %f to %f.\n",
|
||||
output->name, valname, value, ret);
|
||||
weston_log("Output profile p%u '%s': clamping %s value from %f to %f.\n",
|
||||
cprof->base.id, cprof->base.description, valname, value, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_hdr_meta(struct weston_output *output,
|
||||
struct weston_hdr_metadata_type1 *hdr_meta)
|
||||
static void
|
||||
cmlcms_get_hdr_meta(struct weston_hdr_metadata_type1 *hdr_meta,
|
||||
const struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
const struct weston_color_characteristics *cc;
|
||||
|
||||
/* TODO: get color characteristics from color profiles instead. */
|
||||
const struct weston_color_profile_params *cpp = NULL;
|
||||
unsigned i;
|
||||
|
||||
hdr_meta->group_mask = 0;
|
||||
|
||||
/* Only SMPTE ST 2084 mode uses HDR Static Metadata Type 1 */
|
||||
if (weston_output_get_eotf_mode(output) != WESTON_EOTF_MODE_ST2084)
|
||||
return true;
|
||||
switch (cprof->type) {
|
||||
case CMLCMS_PROFILE_TYPE_ICC:
|
||||
/* No HDR metadata */
|
||||
return;
|
||||
|
||||
cc = weston_output_get_color_characteristics(output);
|
||||
|
||||
/* Target content chromaticity */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
hdr_meta->primary[i].x = meta_clamp(cc->primary[i].x,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
hdr_meta->primary[i].y = meta_clamp(cc->primary[i].y,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
}
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES;
|
||||
case CMLCMS_PROFILE_TYPE_PARAMS:
|
||||
cpp = cprof->params;
|
||||
break;
|
||||
}
|
||||
weston_assert_ptr_not_null(cprof->base.cm->compositor, cpp);
|
||||
|
||||
/* Target content white point */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE) {
|
||||
hdr_meta->white.x = meta_clamp(cc->white.x, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->white.y = meta_clamp(cc->white.y, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
hdr_meta->primary[i].x = meta_clamp(cpp->target_primaries.primary[i].x,
|
||||
"primary", 0.0, 1.0, cprof);
|
||||
hdr_meta->primary[i].y = meta_clamp(cpp->target_primaries.primary[i].y,
|
||||
"primary", 0.0, 1.0, cprof);
|
||||
}
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES;
|
||||
|
||||
/* Target content peak and max mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL) {
|
||||
hdr_meta->maxDML = meta_clamp(cc->max_luminance, "maxDML",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->maxCLL = meta_clamp(cc->max_luminance, "maxCLL",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->white.x = meta_clamp(cpp->target_primaries.white_point.x,
|
||||
"white", 0.0, 1.0, cprof);
|
||||
hdr_meta->white.y = meta_clamp(cpp->target_primaries.white_point.y,
|
||||
"white", 0.0, 1.0, cprof);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE;
|
||||
|
||||
if (cpp->target_max_luminance > 0.0f) {
|
||||
hdr_meta->maxDML = meta_clamp(cpp->target_max_luminance,
|
||||
"maxDML", 1.0, 65535.0, cprof);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML;
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL;
|
||||
}
|
||||
|
||||
/* Target content min mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MINL) {
|
||||
hdr_meta->minDML = meta_clamp(cc->min_luminance, "minDML",
|
||||
0.0001, 6.5535, output);
|
||||
if (cpp->target_min_luminance > 0.0f) {
|
||||
hdr_meta->minDML = meta_clamp(cpp->target_min_luminance,
|
||||
"minDML", 0.0001, 6.5535, cprof);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MINDML;
|
||||
}
|
||||
|
||||
/* Target content max frame-average luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL) {
|
||||
hdr_meta->maxFALL = meta_clamp(cc->maxFALL, "maxFALL",
|
||||
1.0, 65535.0, output);
|
||||
if (cpp->maxFALL > 0.0f) {
|
||||
hdr_meta->maxFALL = meta_clamp(cpp->maxFALL,
|
||||
"maxFALL", 1.0, 65535.0, cprof);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (cpp->maxCLL > 0.0f) {
|
||||
hdr_meta->maxCLL = meta_clamp(cpp->maxCLL,
|
||||
"maxCLL", 1.0, 65535.0, cprof);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct weston_output_color_outcome *
|
||||
|
|
@ -288,14 +281,13 @@ cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base,
|
|||
struct weston_color_manager_lcms *cm = to_cmlcms(cm_base);
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
weston_assert_ptr_not_null(cm->base.compositor, output->color_profile);
|
||||
|
||||
co = zalloc(sizeof *co);
|
||||
if (!co)
|
||||
return NULL;
|
||||
|
||||
if (!cmlcms_get_hdr_meta(output, &co->hdr_meta))
|
||||
goto out_fail;
|
||||
|
||||
assert(output->color_profile);
|
||||
cmlcms_get_hdr_meta(&co->hdr_meta, to_cmlcms_cprof(output->color_profile));
|
||||
|
||||
/* TODO: take container color space into account */
|
||||
|
||||
|
|
@ -345,6 +337,12 @@ transforms_scope_new_sub(struct weston_log_subscription *subs, void *data)
|
|||
weston_log_subscription_printf(subs, "%s", str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
str = cmlcms_color_transformer_string(4, &xform->transformer);
|
||||
if (str) {
|
||||
weston_log_subscription_printf(subs, "%s", str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,19 +421,13 @@ cmlcms_init(struct weston_color_manager *cm_base)
|
|||
|
||||
cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger);
|
||||
|
||||
if (!cmlcms_create_stock_profile(cm)) {
|
||||
weston_log("color-lcms: error: cmlcms_create_stock_profile failed\n");
|
||||
goto out_err;
|
||||
}
|
||||
cm->sRGB_profile = cmlcms_create_stock_profile(cm);
|
||||
|
||||
weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion());
|
||||
|
||||
return true;
|
||||
|
||||
out_err:
|
||||
if (cm->lcms_ctx)
|
||||
cmsDeleteContext(cm->lcms_ctx);
|
||||
cm->lcms_ctx = NULL;
|
||||
|
||||
weston_log_scope_destroy(cm->transforms_scope);
|
||||
cm->transforms_scope = NULL;
|
||||
weston_log_scope_destroy(cm->optimizer_scope);
|
||||
|
|
@ -514,7 +506,6 @@ weston_color_manager_create(struct weston_compositor *compositor)
|
|||
cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform;
|
||||
cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome;
|
||||
|
||||
/* We support all color features. */
|
||||
cm->base.supported_color_features = (1 << WESTON_COLOR_FEATURE_ICC) |
|
||||
(1 << WESTON_COLOR_FEATURE_PARAMETRIC) |
|
||||
(1 << WESTON_COLOR_FEATURE_SET_PRIMARIES) |
|
||||
|
|
@ -523,14 +514,12 @@ weston_color_manager_create(struct weston_compositor *compositor)
|
|||
(1 << WESTON_COLOR_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES) |
|
||||
(1 << WESTON_COLOR_FEATURE_EXTENDED_TARGET_VOLUME);
|
||||
|
||||
/* We support all rendering intents. */
|
||||
cm->base.supported_rendering_intents = (1 << WESTON_RENDER_INTENT_PERCEPTUAL) |
|
||||
(1 << WESTON_RENDER_INTENT_SATURATION) |
|
||||
/* (1 << WESTON_RENDER_INTENT_SATURATION) | */
|
||||
(1 << WESTON_RENDER_INTENT_ABSOLUTE) |
|
||||
(1 << WESTON_RENDER_INTENT_RELATIVE) |
|
||||
(1 << WESTON_RENDER_INTENT_RELATIVE_BPC);
|
||||
|
||||
/* We support all primaries named. */
|
||||
cm->base.supported_primaries_named = (1 << WESTON_PRIMARIES_CICP_SRGB) |
|
||||
(1 << WESTON_PRIMARIES_CICP_PAL_M) |
|
||||
(1 << WESTON_PRIMARIES_CICP_PAL) |
|
||||
|
|
@ -542,18 +531,10 @@ weston_color_manager_create(struct weston_compositor *compositor)
|
|||
(1 << WESTON_PRIMARIES_CICP_DISPLAY_P3) |
|
||||
(1 << WESTON_PRIMARIES_ADOBE_RGB);
|
||||
|
||||
/**
|
||||
* TODO: this is a lie just to make the color-management-parametric
|
||||
* tests to work. Without this the tests would be much more limited. We
|
||||
* actually need to implement such TF's. There's no problem doing that,
|
||||
* as parametric color profiles themselves are still unsupported.
|
||||
*/
|
||||
|
||||
/* We need to implement each tf, and we support only a few of them. */
|
||||
cm->base.supported_tf_named = (1 << WESTON_TF_GAMMA22) |
|
||||
(1 << WESTON_TF_GAMMA28) |
|
||||
(1 << WESTON_TF_SRGB) |
|
||||
(1 << WESTON_TF_ST2084_PQ);
|
||||
(1 << WESTON_TF_ST2084_PQ) |
|
||||
(1 << WESTON_TF_EXT_LINEAR);
|
||||
|
||||
wl_list_init(&cm->color_transform_list);
|
||||
wl_list_init(&cm->color_profile_list);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <lcms2.h>
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/weston-log.h>
|
||||
#include <libweston/linalg-3.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "shared/helpers.h"
|
||||
|
|
@ -200,6 +201,26 @@ struct color_transform_steps_mask {
|
|||
uint8_t steps;
|
||||
};
|
||||
|
||||
enum cmlcms_color_transformer_elem {
|
||||
CMLCMS_TRANSFORMER_CURVE1 = 1 << 0,
|
||||
CMLCMS_TRANSFORMER_LIN1 = 1 << 1,
|
||||
CMLCMS_TRANSFORMER_ICC_CHAIN = 1 << 2,
|
||||
CMLCMS_TRANSFORMER_LIN2 = 1 << 3,
|
||||
CMLCMS_TRANSFORMER_CURVE2 = 1 << 4,
|
||||
};
|
||||
|
||||
/** A complete color transformation to be computed on the CPU */
|
||||
struct cmlcms_color_transformer {
|
||||
/** Or'd together from enum cmlcms_color_transformer_elem */
|
||||
uint8_t element_mask;
|
||||
|
||||
struct weston_color_curve curve1;
|
||||
struct weston_color_mapping_matrix lin1;
|
||||
cmsHTRANSFORM icc_chain;
|
||||
struct weston_color_mapping_matrix lin2;
|
||||
struct weston_color_curve curve2;
|
||||
};
|
||||
|
||||
struct cmlcms_color_transform_recipe {
|
||||
enum cmlcms_category category;
|
||||
struct cmlcms_color_profile *input_profile;
|
||||
|
|
@ -228,13 +249,11 @@ struct cmlcms_color_transform {
|
|||
cmsToneCurve *post_curve[3];
|
||||
|
||||
/**
|
||||
* 3D LUT color mapping part of the transformation, if needed by the
|
||||
* weston_color_transform. This is used as a fallback when an
|
||||
* arbitrary LittleCMS pipeline cannot be translated into a more
|
||||
* specific form or when the backend/renderer is not able to use
|
||||
* such optimized form.
|
||||
* For evaluating points through the complete color transformation,
|
||||
* even when base.steps_valid is false. This is used for the 3D LUT
|
||||
* path.
|
||||
*/
|
||||
cmsHTRANSFORM cmap_3dlut;
|
||||
struct cmlcms_color_transformer transformer;
|
||||
|
||||
/**
|
||||
* Certain categories of transformations need their own LittleCMS
|
||||
|
|
@ -273,7 +292,7 @@ ref_cprof(struct cmlcms_color_profile *cprof);
|
|||
void
|
||||
unref_cprof(struct cmlcms_color_profile *cprof);
|
||||
|
||||
bool
|
||||
struct cmlcms_color_profile *
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm);
|
||||
|
||||
void
|
||||
|
|
@ -298,4 +317,18 @@ cmsToneCurve *
|
|||
lcmsJoinToneCurve(cmsContext context_id, const cmsToneCurve *X,
|
||||
const cmsToneCurve *Y, unsigned int resulting_points);
|
||||
|
||||
void
|
||||
cmlcms_color_transformer_fini(struct cmlcms_color_transformer *t);
|
||||
|
||||
void
|
||||
cmlcms_color_transformer_eval(struct weston_compositor *compositor,
|
||||
const struct cmlcms_color_transformer *t,
|
||||
struct weston_vec3f *dst,
|
||||
const struct weston_vec3f *src,
|
||||
size_t len);
|
||||
|
||||
char *
|
||||
cmlcms_color_transformer_string(int indent,
|
||||
const struct cmlcms_color_transformer *t);
|
||||
|
||||
#endif /* WESTON_COLOR_LCMS_H */
|
||||
|
|
|
|||
|
|
@ -558,57 +558,36 @@ make_icc_file_description(struct lcmsProfilePtr profile,
|
|||
* BT.709 primaries with gamma-2.2 transfer characteristic. This is the
|
||||
* expected sRGB display response.
|
||||
*/
|
||||
bool
|
||||
struct cmlcms_color_profile *
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
|
||||
{
|
||||
static const cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 };
|
||||
static const cmsCIExyYTRIPLE bt709 = {
|
||||
{ 0.6400, 0.3300, 1.0 },
|
||||
{ 0.3000, 0.6000, 1.0 },
|
||||
{ 0.1500, 0.0600, 1.0 }
|
||||
};
|
||||
cmsToneCurve *gamma22[3];
|
||||
struct lcmsProfilePtr profile = { NULL };
|
||||
struct cmlcms_md5_sum md5sum;
|
||||
struct weston_compositor *compositor = cm->base.compositor;
|
||||
struct weston_color_profile_params p = {};
|
||||
struct cmlcms_color_profile *stock;
|
||||
char *desc = NULL;
|
||||
const char *err_msg = NULL;
|
||||
|
||||
gamma22[0] = gamma22[1] = gamma22[2] = cmsBuildGamma(cm->lcms_ctx, 2.2);
|
||||
if (gamma22[0])
|
||||
profile.p = cmsCreateRGBProfileTHR(cm->lcms_ctx, &D65, &bt709, gamma22);
|
||||
cmsFreeToneCurve(gamma22[0]);
|
||||
if (!profile.p) {
|
||||
weston_log("color-lcms: error: failed to create stock sRGB profile.\n");
|
||||
return false;
|
||||
}
|
||||
if (!cmsMD5computeID(profile.p)) {
|
||||
weston_log("Failed to compute MD5 for stock sRGB profile.\n");
|
||||
goto err_close;
|
||||
}
|
||||
p.primaries_info = weston_color_primaries_info_from(compositor,
|
||||
WESTON_PRIMARIES_CICP_SRGB);
|
||||
p.primaries = p.primaries_info->color_gamut;
|
||||
p.tf.info = weston_color_tf_info_from(compositor, WESTON_TF_GAMMA22);
|
||||
p.reference_white_luminance = 80.0;
|
||||
p.min_luminance = 0.2;
|
||||
p.max_luminance = 80.0;
|
||||
p.target_primaries = p.primaries;
|
||||
p.target_min_luminance = p.min_luminance;
|
||||
p.target_max_luminance = p.max_luminance;
|
||||
p.maxCLL = -1.0f;
|
||||
p.maxFALL = -1.0f;
|
||||
|
||||
cmsGetHeaderProfileID(profile.p, md5sum.bytes);
|
||||
desc = make_icc_file_description(profile, &md5sum, "sRGB stock");
|
||||
if (!desc)
|
||||
goto err_close;
|
||||
str_printf(&desc, "default sRGB: %s primaries, %s transfer function",
|
||||
p.primaries_info->desc, p.tf.info->desc);
|
||||
abort_oom_if_null(desc);
|
||||
|
||||
cm->sRGB_profile = cmlcms_color_profile_alloc(cm, CMLCMS_PROFILE_TYPE_ICC, desc);
|
||||
cm->sRGB_profile->icc.profile = profile;
|
||||
cm->sRGB_profile->icc.md5sum = md5sum;
|
||||
stock = cmlcms_color_profile_alloc(cm, CMLCMS_PROFILE_TYPE_PARAMS, desc);
|
||||
*stock->params = p;
|
||||
cmlcms_color_profile_register(stock);
|
||||
|
||||
if (!ensure_output_profile_extract(cm->sRGB_profile, cm->lcms_ctx,
|
||||
cmlcms_reasonable_1D_points(), &err_msg))
|
||||
goto err_close;
|
||||
|
||||
cmlcms_color_profile_register(cm->sRGB_profile);
|
||||
|
||||
return true;
|
||||
|
||||
err_close:
|
||||
if (err_msg)
|
||||
weston_log("%s\n", err_msg);
|
||||
|
||||
cmlcms_color_profile_destroy(cm->sRGB_profile);
|
||||
return false;
|
||||
return stock;
|
||||
}
|
||||
|
||||
struct weston_color_profile *
|
||||
|
|
@ -719,84 +698,47 @@ cmlcms_get_color_profile_from_params(struct weston_color_manager *cm_base,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_send_icc_info(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
int32_t fd;
|
||||
uint32_t len;
|
||||
|
||||
/* ICC-based color profile, so just send the ICC file fd. If we
|
||||
* get an error (negative fd), the helper will send the proper
|
||||
* error to the client. */
|
||||
fd = os_ro_anonymous_file_get_fd(cprof->icc.prof_rofile,
|
||||
RO_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
if (fd < 0) {
|
||||
weston_cm_send_icc_file(cm_image_desc_info, -1, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = os_ro_anonymous_file_size(cprof->icc.prof_rofile);
|
||||
weston_assert_u32_gt(cprof->base.cm->compositor, len, 0);
|
||||
|
||||
weston_cm_send_icc_file(cm_image_desc_info, fd, len);
|
||||
|
||||
os_ro_anonymous_file_put_fd(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info,
|
||||
struct weston_color_profile *cprof_base)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = to_cmlcms(cprof_base->cm);
|
||||
struct weston_compositor *compositor = cm->base.compositor;
|
||||
struct cmlcms_color_profile *cprof = to_cmlcms_cprof(cprof_base);
|
||||
const struct weston_color_primaries_info *primaries_info;
|
||||
const struct weston_color_tf_info *tf_info;
|
||||
int32_t fd;
|
||||
uint32_t len;
|
||||
|
||||
/**
|
||||
* TODO: when we convert the stock sRGB profile to a parametric profile
|
||||
* instead of an ICC one, we'll be able to change the if/else below to
|
||||
* a switch/case.
|
||||
*/
|
||||
|
||||
if (cprof->type == CMLCMS_PROFILE_TYPE_ICC && cprof != cm->sRGB_profile) {
|
||||
/* ICC-based color profile, so just send the ICC file fd. If we
|
||||
* get an error (negative fd), the helper will send the proper
|
||||
* error to the client. */
|
||||
fd = os_ro_anonymous_file_get_fd(cprof->icc.prof_rofile,
|
||||
RO_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
if (fd < 0) {
|
||||
weston_cm_send_icc_file(cm_image_desc_info, -1, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = os_ro_anonymous_file_size(cprof->icc.prof_rofile);
|
||||
weston_assert_u32_gt(compositor, len, 0);
|
||||
|
||||
weston_cm_send_icc_file(cm_image_desc_info, fd, len);
|
||||
|
||||
os_ro_anonymous_file_put_fd(fd);
|
||||
} else {
|
||||
/* TODO: we still don't support parametric color profiles that
|
||||
* are not the stock one. This should change when we start
|
||||
* advertising parametric image description support in our
|
||||
* color-management protocol implementation. */
|
||||
if (cprof != cm->sRGB_profile)
|
||||
weston_assert_not_reached(compositor, "we don't support parametric " \
|
||||
"cprof's that are not the stock sRGB one");
|
||||
|
||||
/* Stock sRGB color profile. TODO: when we add support for
|
||||
* parametric color profiles, the stock sRGB will be crafted
|
||||
* using parameters, instead of cmsCreate_sRGBProfileTHR()
|
||||
* (which we currently use). So we'll get the parameters
|
||||
* directly from it, instead of hardcoding as we are doing here.
|
||||
* We don't get the parameters from the stock sRGB color profile
|
||||
* because it is not trivial to retrieve that from LittleCMS. */
|
||||
|
||||
/* Send the H.273 ColourPrimaries code point that matches the
|
||||
* Rec709 primaries and the D65 white point. */
|
||||
primaries_info = weston_color_primaries_info_from(compositor,
|
||||
WESTON_PRIMARIES_CICP_SRGB);
|
||||
weston_cm_send_primaries_named(cm_image_desc_info, primaries_info);
|
||||
|
||||
/* These are the Rec709 primaries and D65 white point. */
|
||||
weston_cm_send_primaries(cm_image_desc_info,
|
||||
&primaries_info->color_gamut);
|
||||
|
||||
/* Target primaries, equal to the primary primaries. */
|
||||
weston_cm_send_target_primaries(cm_image_desc_info,
|
||||
&primaries_info->color_gamut);
|
||||
|
||||
/* sRGB transfer function. */
|
||||
tf_info = weston_color_tf_info_from(compositor, WESTON_TF_GAMMA22);
|
||||
weston_cm_send_tf_named(cm_image_desc_info, tf_info);
|
||||
|
||||
/* Primary luminance, default values from the protocol. */
|
||||
weston_cm_send_luminances(cm_image_desc_info, 0.2, 80.0, 80.0);
|
||||
|
||||
/* Target luminance, min/max equals primary luminance min/max. */
|
||||
weston_cm_send_target_luminances(cm_image_desc_info, 0.2, 80.0);
|
||||
switch (cprof->type) {
|
||||
case CMLCMS_PROFILE_TYPE_ICC:
|
||||
return cmlcms_send_icc_info(cm_image_desc_info, cprof);
|
||||
case CMLCMS_PROFILE_TYPE_PARAMS:
|
||||
weston_cm_send_parametric_info(cm_image_desc_info, cprof->params);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct weston_color_profile *
|
||||
|
|
|
|||
|
|
@ -121,11 +121,8 @@ cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
|
|||
wl_list_remove(&xform->link);
|
||||
|
||||
cmsFreeToneCurveTriple(xform->pre_curve);
|
||||
|
||||
if (xform->cmap_3dlut)
|
||||
cmsDeleteTransform(xform->cmap_3dlut);
|
||||
|
||||
cmsFreeToneCurveTriple(xform->post_curve);
|
||||
cmlcms_color_transformer_fini(&xform->transformer);
|
||||
|
||||
if (xform->lcms_ctx)
|
||||
cmsDeleteContext(xform->lcms_ctx);
|
||||
|
|
@ -390,6 +387,30 @@ merge_curvesets(cmsPipeline **lut, cmsContext context_id)
|
|||
return modified;
|
||||
}
|
||||
|
||||
static void
|
||||
linear_curvesets_to_matrices(cmsPipeline **lut, cmsContext context_id)
|
||||
{
|
||||
cmsPipeline *pipe;
|
||||
cmsStage *elem;
|
||||
cmsStage *matrix;
|
||||
|
||||
pipe = cmsPipelineAlloc(context_id, 3, 3);
|
||||
abort_oom_if_null(pipe);
|
||||
|
||||
elem = cmsPipelineGetPtrToFirstStage(*lut);
|
||||
for (; elem; elem = cmsStageNext(elem)) {
|
||||
matrix = lcms_matrix_stage_from_curve(context_id, elem);
|
||||
if (matrix) {
|
||||
cmsPipelineInsertStage(pipe, cmsAT_END, matrix);
|
||||
} else {
|
||||
cmsPipelineInsertStage(pipe, cmsAT_END, cmsStageDup(elem));
|
||||
}
|
||||
}
|
||||
|
||||
cmsPipelineFree(*lut);
|
||||
*lut = pipe;
|
||||
}
|
||||
|
||||
static const struct weston_color_tf_info *
|
||||
lcms_curve_matches_any_tf(struct weston_compositor *compositor,
|
||||
uint32_t lcms_curve_type, bool clamped_input,
|
||||
|
|
@ -1016,6 +1037,8 @@ lcms_optimize_pipeline(cmsPipeline **lut, cmsContext context_id)
|
|||
{
|
||||
bool cont_opt;
|
||||
|
||||
linear_curvesets_to_matrices(lut, context_id);
|
||||
|
||||
/**
|
||||
* This optimization loop will delete identity stages. Deleting
|
||||
* identity matrix stages is harmless, but deleting identity
|
||||
|
|
@ -1378,11 +1401,14 @@ init_icc_to_icc_chain(struct cmlcms_color_transform *xform)
|
|||
|
||||
assert(chain_len <= ARRAY_LENGTH(chain));
|
||||
|
||||
weston_assert_ptr_null(cm->base.compositor, xform->cmap_3dlut);
|
||||
xform->cmap_3dlut = xform_realize_icc_chain(xform, chain, chain_len,
|
||||
render_intent, allowed);
|
||||
weston_assert_ptr_null(cm->base.compositor, xform->transformer.icc_chain);
|
||||
xform->transformer.icc_chain = xform_realize_icc_chain(xform, chain, chain_len,
|
||||
render_intent, allowed);
|
||||
if (!xform->transformer.icc_chain)
|
||||
return false;
|
||||
|
||||
return !!xform->cmap_3dlut;
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_ICC_CHAIN;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1390,18 +1416,26 @@ weston_color_curve_set_from_params(struct weston_color_curve *curve,
|
|||
const struct weston_color_profile_params *p,
|
||||
enum weston_tf_direction dir)
|
||||
{
|
||||
curve->type = WESTON_COLOR_CURVE_TYPE_ENUM;
|
||||
curve->u.enumerated.tf = p->tf;
|
||||
curve->u.enumerated.tf_direction = dir;
|
||||
if (p->tf.info->tf == WESTON_TF_EXT_LINEAR) {
|
||||
curve->type = WESTON_COLOR_CURVE_TYPE_IDENTITY;
|
||||
} else {
|
||||
curve->type = WESTON_COLOR_CURVE_TYPE_ENUM;
|
||||
curve->u.enumerated.tf = p->tf;
|
||||
curve->u.enumerated.tf_direction = dir;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_color_mapping_set_from_m4f(struct weston_color_mapping *mapping,
|
||||
struct weston_mat4f mat)
|
||||
{
|
||||
mapping->type = WESTON_COLOR_MAPPING_TYPE_MATRIX;
|
||||
mapping->u.mat.matrix = weston_m3f_from_m4f_xyz(mat);
|
||||
mapping->u.mat.offset = weston_v3f_from_v4f_xyz(mat.col[3]);
|
||||
if (matrix_is_identity(mat, MATRIX_PRECISION_BITS)) {
|
||||
mapping->type = WESTON_COLOR_MAPPING_TYPE_IDENTITY;
|
||||
} else {
|
||||
mapping->type = WESTON_COLOR_MAPPING_TYPE_MATRIX;
|
||||
mapping->u.mat.matrix = weston_m3f_from_m4f_xyz(mat);
|
||||
mapping->u.mat.offset = weston_v3f_from_v4f_xyz(mat.col[3]);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1426,6 +1460,9 @@ init_blend_to_parametric(struct cmlcms_color_transform *xform)
|
|||
xform->base.post_curve.type = WESTON_COLOR_CURVE_TYPE_IDENTITY;
|
||||
xform->base.steps_valid = true;
|
||||
|
||||
xform->transformer.curve1 = xform->base.pre_curve;
|
||||
xform->transformer.element_mask = CMLCMS_TRANSFORMER_CURVE1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1643,6 +1680,8 @@ init_parametric_to_parametric(struct cmlcms_color_transform *xform)
|
|||
*/
|
||||
weston_color_curve_set_from_params(&xform->base.pre_curve,
|
||||
recipe->input_profile->params, WESTON_FORWARD_TF);
|
||||
xform->transformer.curve1 = xform->base.pre_curve;
|
||||
xform->transformer.element_mask = CMLCMS_TRANSFORMER_CURVE1;
|
||||
|
||||
if (!rgb_to_rgb_matrix(&mat,
|
||||
recipe->input_profile->params,
|
||||
|
|
@ -1655,6 +1694,14 @@ init_parametric_to_parametric(struct cmlcms_color_transform *xform)
|
|||
}
|
||||
|
||||
weston_color_mapping_set_from_m4f(&xform->base.mapping, mat);
|
||||
switch (xform->base.mapping.type) {
|
||||
case WESTON_COLOR_MAPPING_TYPE_IDENTITY:
|
||||
break;
|
||||
case WESTON_COLOR_MAPPING_TYPE_MATRIX:
|
||||
xform->transformer.lin1 = xform->base.mapping.u.mat;
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_LIN1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: Use HLG OOTF for gamma correction? */
|
||||
/* TODO: try https://gitlab.freedesktop.org/pq/color-and-hdr/-/issues/45 */
|
||||
|
|
@ -1667,6 +1714,8 @@ init_parametric_to_parametric(struct cmlcms_color_transform *xform)
|
|||
weston_color_curve_set_from_params(&xform->base.post_curve,
|
||||
recipe->output_profile->params,
|
||||
WESTON_INVERSE_TF);
|
||||
xform->transformer.curve2 = xform->base.post_curve;
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_CURVE2;
|
||||
break;
|
||||
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
|
||||
weston_assert_not_reached(xform->base.cm->compositor,
|
||||
|
|
@ -1678,6 +1727,261 @@ init_parametric_to_parametric(struct cmlcms_color_transform *xform)
|
|||
return true;
|
||||
}
|
||||
|
||||
static cmsCIExyY
|
||||
lcms_xyY_from(struct weston_CIExy p)
|
||||
{
|
||||
return (cmsCIExyY){ p.x, p.y, 1.0f };
|
||||
}
|
||||
|
||||
/** Create LittleCMS profile for an optical space
|
||||
*
|
||||
* \param cm The color manager, for the LittleCMS context.
|
||||
* \param gm The primaries and the white point.
|
||||
* \param rel_black_level The relative black level, when white maximum
|
||||
* luminance is 1.0.
|
||||
*
|
||||
* \return A LittleCMS RGB Display profile where the transfer characteristic
|
||||
* is linear from rel_black_level to 1.0.
|
||||
*/
|
||||
static struct lcmsProfilePtr
|
||||
optical_profile(struct weston_color_manager_lcms *cm,
|
||||
const struct weston_color_gamut *gm,
|
||||
float rel_black_level)
|
||||
{
|
||||
cmsCIExyY wp = lcms_xyY_from(gm->white_point);
|
||||
cmsCIExyYTRIPLE prim = {
|
||||
lcms_xyY_from(gm->primary[0]),
|
||||
lcms_xyY_from(gm->primary[1]),
|
||||
lcms_xyY_from(gm->primary[2])
|
||||
};
|
||||
cmsHPROFILE hnd;
|
||||
cmsToneCurve *trc[3];
|
||||
cmsFloat32Number points[2] = { rel_black_level, 1.0f };
|
||||
|
||||
trc[2] = trc[1] = trc[0] =
|
||||
cmsBuildTabulatedToneCurveFloat(cm->lcms_ctx, 2, points);
|
||||
abort_oom_if_null(trc[0]);
|
||||
|
||||
hnd = cmsCreateRGBProfileTHR(cm->lcms_ctx, &wp, &prim, trc);
|
||||
weston_assert_ptr_not_null(cm->base.compositor, hnd);
|
||||
|
||||
cmsFreeToneCurve(trc[0]);
|
||||
return (struct lcmsProfilePtr){ hnd };
|
||||
}
|
||||
|
||||
enum matrix_order {
|
||||
/** Add new matrix to the right of the existing matrix. */
|
||||
MATRIX_PREPEND,
|
||||
/** Add new matrix to the left of the existing matrix. */
|
||||
MATRIX_APPEND,
|
||||
};
|
||||
|
||||
static void
|
||||
patch_color_mapping_matrix(struct weston_color_mapping *mapping,
|
||||
struct weston_mat4f M, enum matrix_order order)
|
||||
{
|
||||
struct weston_mat4f cmap;
|
||||
struct weston_color_mapping_matrix *mapmat = NULL;
|
||||
|
||||
switch (mapping->type) {
|
||||
case WESTON_COLOR_MAPPING_TYPE_IDENTITY:
|
||||
weston_color_mapping_set_from_m4f(mapping, M);
|
||||
return;
|
||||
case WESTON_COLOR_MAPPING_TYPE_MATRIX:
|
||||
mapmat = &mapping->u.mat;
|
||||
break;
|
||||
}
|
||||
|
||||
cmap = weston_m4f_from_m3f_v3f(mapmat->matrix, mapmat->offset);
|
||||
switch (order) {
|
||||
case MATRIX_PREPEND:
|
||||
cmap = weston_m4f_mul_m4f(cmap, M);
|
||||
break;
|
||||
case MATRIX_APPEND:
|
||||
cmap = weston_m4f_mul_m4f(M, cmap);
|
||||
break;
|
||||
}
|
||||
|
||||
weston_color_mapping_set_from_m4f(mapping, cmap);
|
||||
}
|
||||
|
||||
static bool
|
||||
init_icc_to_parametric(struct cmlcms_color_transform *xform)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = to_cmlcms(xform->base.cm);
|
||||
struct cmlcms_color_profile *in_prof = xform->search_key.input_profile;
|
||||
struct cmlcms_color_profile *out_prof = xform->search_key.output_profile;
|
||||
const struct weston_color_profile_params *out = out_prof->params;
|
||||
const struct weston_render_intent_info *render_intent;
|
||||
struct color_transform_steps_mask allowed = {
|
||||
STEP_PRE_CURVE | STEP_MAPPING
|
||||
};
|
||||
struct lcmsProfilePtr chain[2];
|
||||
cmsHTRANSFORM icc_chain;
|
||||
struct weston_mat4f M;
|
||||
float v;
|
||||
|
||||
weston_assert_u32_eq(cm->base.compositor, in_prof->type, CMLCMS_PROFILE_TYPE_ICC);
|
||||
weston_assert_u32_eq(cm->base.compositor, out_prof->type, CMLCMS_PROFILE_TYPE_PARAMS);
|
||||
|
||||
render_intent = xform->search_key.render_intent;
|
||||
|
||||
/*
|
||||
* The ICC chain converts input device RGB to optical output RGB
|
||||
* with relative luminance. The input reference luminance
|
||||
* is 1.0, and implicitly it is also the input peak luminance.
|
||||
* The TRC adds target_min_luminance as necessary, meaning that
|
||||
* optical output RGB 0,0,0 corresponds to target_min_luminance.
|
||||
* Optical output RGB 1,1,1 corresponds to reference white luminance.
|
||||
*/
|
||||
chain[0] = in_prof->icc.profile;
|
||||
chain[1] = optical_profile(cm, &out->primaries,
|
||||
out->target_min_luminance / out->reference_white_luminance);
|
||||
|
||||
icc_chain = xform_realize_icc_chain(xform, chain, 2, render_intent, allowed);
|
||||
cmsCloseProfile(chain[1].p);
|
||||
if (!icc_chain)
|
||||
return false;
|
||||
|
||||
/* Map [0, 1] to output [target_min, reference]. */
|
||||
v = out->reference_white_luminance - out->target_min_luminance;
|
||||
M = weston_m4f_scaling(v, v, v);
|
||||
v = out->target_min_luminance; /* applied below */
|
||||
|
||||
/* Convert cd/m² to output [0, 1]. */
|
||||
v -= out->min_luminance;
|
||||
M = weston_m4f_mul_m4f(weston_m4f_translation(v, v, v), M);
|
||||
v = 1.0f / (out->max_luminance - out->min_luminance);
|
||||
M = weston_m4f_mul_m4f(weston_m4f_scaling(v, v, v), M);
|
||||
|
||||
/* TODO: Dynamic range adjustment */
|
||||
|
||||
if (xform->base.steps_valid) {
|
||||
weston_assert_u32_eq(cm->base.compositor,
|
||||
xform->base.post_curve.type,
|
||||
WESTON_COLOR_CURVE_TYPE_IDENTITY);
|
||||
|
||||
patch_color_mapping_matrix(&xform->base.mapping, M, MATRIX_APPEND);
|
||||
|
||||
if (xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT) {
|
||||
weston_color_curve_set_from_params(&xform->base.post_curve,
|
||||
out, WESTON_INVERSE_TF);
|
||||
}
|
||||
}
|
||||
|
||||
xform->transformer.icc_chain = icc_chain;
|
||||
xform->transformer.element_mask = CMLCMS_TRANSFORMER_ICC_CHAIN;
|
||||
|
||||
if (!matrix_is_identity(M, MATRIX_PRECISION_BITS)) {
|
||||
xform->transformer.lin2.matrix = weston_m3f_from_m4f_xyz(M);
|
||||
xform->transformer.lin2.offset = weston_v3f_from_v4f_xyz(M.col[3]);
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_LIN2;
|
||||
}
|
||||
|
||||
if (xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT) {
|
||||
weston_color_curve_set_from_params(&xform->transformer.curve2,
|
||||
out, WESTON_INVERSE_TF);
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_CURVE2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
init_parametric_to_icc(struct cmlcms_color_transform *xform)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = to_cmlcms(xform->base.cm);
|
||||
struct cmlcms_color_profile *in_prof = xform->search_key.input_profile;
|
||||
struct cmlcms_color_profile *out_prof = xform->search_key.output_profile;
|
||||
const struct weston_color_profile_params *in = in_prof->params;
|
||||
const struct weston_render_intent_info *render_intent;
|
||||
struct color_transform_steps_mask allowed = {
|
||||
STEP_MAPPING | STEP_POST_CURVE
|
||||
};
|
||||
struct lcmsProfilePtr optical_prof;
|
||||
struct lcmsProfilePtr chain[5];
|
||||
cmsHTRANSFORM icc_chain;
|
||||
unsigned chain_len = 0;
|
||||
struct weston_mat4f M;
|
||||
float v;
|
||||
|
||||
weston_assert_u32_eq(cm->base.compositor, in_prof->type, CMLCMS_PROFILE_TYPE_PARAMS);
|
||||
weston_assert_u32_eq(cm->base.compositor, out_prof->type, CMLCMS_PROFILE_TYPE_ICC);
|
||||
|
||||
render_intent = xform->search_key.render_intent;
|
||||
|
||||
/*
|
||||
* Pre-curve shall have EOTF to convert electrical device RGB to
|
||||
* min-max relative optical device RGB.
|
||||
*/
|
||||
|
||||
/* TODO: Dynamic range adjustment */
|
||||
|
||||
/* Convert input [0, 1] to cd/m² */
|
||||
v = in->max_luminance - in->min_luminance;
|
||||
M = weston_m4f_scaling(v, v, v);
|
||||
v = in->min_luminance; /* applied below */
|
||||
|
||||
/* Map input [target_min_luminance, reference] to [0, 1] */
|
||||
v -= in->target_min_luminance;
|
||||
M = weston_m4f_mul_m4f(weston_m4f_translation(v, v, v), M);
|
||||
v = 1.0f / (in->reference_white_luminance - in->target_min_luminance);
|
||||
M = weston_m4f_mul_m4f(weston_m4f_scaling(v, v, v), M);
|
||||
|
||||
/* The above is the input to the ICC chain. */
|
||||
optical_prof = optical_profile(cm, &in->primaries,
|
||||
in->target_min_luminance / in->reference_white_luminance);
|
||||
|
||||
/* see init_icc_to_icc_chain() */
|
||||
chain[chain_len++] = optical_prof;
|
||||
switch (xform->search_key.category) {
|
||||
case CMLCMS_CATEGORY_INPUT_TO_BLEND:
|
||||
chain[chain_len++] = out_prof->icc.profile;
|
||||
chain[chain_len++] = out_prof->extract.eotf;
|
||||
break;
|
||||
case CMLCMS_CATEGORY_INPUT_TO_OUTPUT:
|
||||
chain[chain_len++] = out_prof->icc.profile;
|
||||
if (out_prof->extract.vcgt.p)
|
||||
chain[chain_len++] = out_prof->extract.vcgt;
|
||||
break;
|
||||
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
|
||||
weston_assert_not_reached(xform->base.cm->compositor,
|
||||
"blend-to-output handled elsewhere");
|
||||
}
|
||||
|
||||
assert(chain_len <= ARRAY_LENGTH(chain));
|
||||
|
||||
icc_chain = xform_realize_icc_chain(xform, chain, chain_len, render_intent, allowed);
|
||||
cmsCloseProfile(optical_prof.p);
|
||||
if (!icc_chain)
|
||||
return false;
|
||||
|
||||
if (xform->base.steps_valid) {
|
||||
weston_assert_u32_eq(cm->base.compositor,
|
||||
xform->base.pre_curve.type,
|
||||
WESTON_COLOR_CURVE_TYPE_IDENTITY);
|
||||
|
||||
weston_color_curve_set_from_params(&xform->base.pre_curve,
|
||||
in, WESTON_FORWARD_TF);
|
||||
patch_color_mapping_matrix(&xform->base.mapping, M, MATRIX_PREPEND);
|
||||
}
|
||||
|
||||
weston_color_curve_set_from_params(&xform->transformer.curve1,
|
||||
in, WESTON_FORWARD_TF);
|
||||
xform->transformer.element_mask = CMLCMS_TRANSFORMER_CURVE1;
|
||||
|
||||
if (!matrix_is_identity(M, MATRIX_PRECISION_BITS)) {
|
||||
xform->transformer.lin1.matrix = weston_m3f_from_m4f_xyz(M);
|
||||
xform->transformer.lin1.offset = weston_v3f_from_v4f_xyz(M.col[3]);
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_LIN1;
|
||||
}
|
||||
|
||||
xform->transformer.icc_chain = icc_chain;
|
||||
xform->transformer.element_mask |= CMLCMS_TRANSFORMER_ICC_CHAIN;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum cmlcms_color_transform_type {
|
||||
CMLCMS_BLEND_TO_ICC = 0x0,
|
||||
CMLCMS_BLEND_TO_PARAM = 0x1,
|
||||
|
|
@ -1754,7 +2058,8 @@ cmlcms_color_transform_recipe_string(const struct cmlcms_color_transform_recipe
|
|||
}
|
||||
|
||||
static bool
|
||||
build_3d_lut(struct weston_compositor *compositor, cmsHTRANSFORM cmap_3dlut,
|
||||
build_3d_lut(struct weston_compositor *compositor,
|
||||
const struct cmlcms_color_transformer *transformer,
|
||||
unsigned int len_shaper, const float *shaper,
|
||||
unsigned int len_lut3d, float *lut3d)
|
||||
{
|
||||
|
|
@ -1836,7 +2141,9 @@ build_3d_lut(struct weston_compositor *compositor, cmsHTRANSFORM cmap_3dlut,
|
|||
|
||||
index_r = 0;
|
||||
i = 3 * (index_r + len_lut3d * (index_g + len_lut3d * index_b));
|
||||
cmsDoTransform(cmap_3dlut, rgb_in, &lut3d[i], len_lut3d);
|
||||
cmlcms_color_transformer_eval(compositor, transformer,
|
||||
(struct weston_vec3f *)&lut3d[i],
|
||||
rgb_in, len_lut3d);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1876,12 +2183,15 @@ is_monotonic(const float *lut, unsigned len)
|
|||
}
|
||||
|
||||
static bool
|
||||
build_shaper(cmsContext lcms_ctx, cmsHTRANSFORM cmap_3dlut,
|
||||
unsigned int len_shaper, float *shaper)
|
||||
build_shaper(struct weston_compositor *compositor,
|
||||
cmsContext lcms_ctx,
|
||||
const struct cmlcms_color_transformer *transformer,
|
||||
unsigned int len_shaper,
|
||||
float *shaper)
|
||||
{
|
||||
float *curves[3];
|
||||
float divider = len_shaper - 1;
|
||||
float rgb_in[3], rgb_out[3];
|
||||
struct weston_vec3f rgb_in, rgb_out;
|
||||
cmsToneCurve *tc[3] = { NULL };
|
||||
unsigned int ch, i;
|
||||
float smoothing_param;
|
||||
|
|
@ -1904,10 +2214,11 @@ build_shaper(cmsContext lcms_ctx, cmsHTRANSFORM cmap_3dlut,
|
|||
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);
|
||||
rgb_in.r = rgb_in.g = rgb_in.b = (float)i / divider;
|
||||
cmlcms_color_transformer_eval(compositor, transformer,
|
||||
&rgb_out, &rgb_in, 1);
|
||||
for (ch = 0; ch < 3; ch++)
|
||||
curves[ch][i] = ensure_unorm(rgb_out[ch]);
|
||||
curves[ch][i] = ensure_unorm(rgb_out.el[ch]);
|
||||
}
|
||||
|
||||
for (ch = 0; ch < 3; ch++) {
|
||||
|
|
@ -1944,10 +2255,10 @@ out:
|
|||
}
|
||||
|
||||
/**
|
||||
* Based on [1]. We get cmsHTRANSFORM cmap_3dlut and decompose into a shaper
|
||||
* Based on [1]. We get the transformer 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.
|
||||
* without losing precision. 3D LUT dimension size is problematic because it
|
||||
* demands n³ memory.
|
||||
*
|
||||
* [1] https://www.littlecms.com/ASICprelinerization_CGIV08.pdf
|
||||
*/
|
||||
|
|
@ -1960,12 +2271,12 @@ xform_to_shaper_plus_3dlut(struct weston_color_transform *xform_base,
|
|||
struct weston_compositor *compositor = xform_base->cm->compositor;
|
||||
bool ret;
|
||||
|
||||
ret = build_shaper(xform->lcms_ctx, xform->cmap_3dlut,
|
||||
ret = build_shaper(compositor, xform->lcms_ctx, &xform->transformer,
|
||||
len_shaper, shaper);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
ret = build_3d_lut(compositor, xform->cmap_3dlut,
|
||||
ret = build_3d_lut(compositor, &xform->transformer,
|
||||
len_shaper, shaper, len_lut3d, lut3d);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
|
@ -2019,8 +2330,10 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
|||
ret = init_icc_to_icc_chain(xform);
|
||||
break;
|
||||
case CMLCMS_ICC_TO_PARAM:
|
||||
ret = init_icc_to_parametric(xform);
|
||||
break;
|
||||
case CMLCMS_PARAM_TO_ICC:
|
||||
ret = init_parametric_to_icc(xform);
|
||||
break;
|
||||
case CMLCMS_PARAM_TO_PARAM:
|
||||
ret = init_parametric_to_parametric(xform);
|
||||
|
|
@ -2044,6 +2357,12 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
|||
free(str);
|
||||
}
|
||||
|
||||
str = cmlcms_color_transformer_string(4, &xform->transformer);
|
||||
if (str) {
|
||||
weston_log_scope_printf(cm->transforms_scope, "%s", str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
return xform;
|
||||
|
||||
error:
|
||||
|
|
|
|||
182
libweston/color-lcms/color-transformer.c
Normal file
182
libweston/color-lcms/color-transformer.c
Normal file
|
|
@ -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 <libweston/libweston.h>
|
||||
#include <libweston/linalg-3.h>
|
||||
#include <lcms2.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "color-properties.h"
|
||||
#include "color-operations.h"
|
||||
#include "shared/xalloc.h"
|
||||
#include "shared/weston-assert.h"
|
||||
#include "color-lcms.h"
|
||||
|
||||
/** Release all transformer members. */
|
||||
void
|
||||
cmlcms_color_transformer_fini(struct cmlcms_color_transformer *t)
|
||||
{
|
||||
if (t->icc_chain) {
|
||||
cmsDeleteTransform(t->icc_chain);
|
||||
t->icc_chain = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** Push the given points through the transformer
|
||||
*
|
||||
* \param compositor The compositor, for logging assertions.
|
||||
* \param t The transformer to execute.
|
||||
* \param[out] dst The destination array.
|
||||
* \param[in] src The source array.
|
||||
* \param len The length of both arrays.
|
||||
*/
|
||||
void
|
||||
cmlcms_color_transformer_eval(struct weston_compositor *compositor,
|
||||
const struct cmlcms_color_transformer *t,
|
||||
struct weston_vec3f *dst,
|
||||
const struct weston_vec3f *src,
|
||||
size_t len)
|
||||
{
|
||||
const struct weston_vec3f *in = src;
|
||||
struct weston_vec3f *end = dst + len;
|
||||
struct weston_vec3f *out;
|
||||
|
||||
weston_assert_u8_ne(compositor, t->element_mask, 0);
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_CURVE1) {
|
||||
weston_color_curve_sample(compositor, &t->curve1, in, dst, len);
|
||||
in = dst;
|
||||
}
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_LIN1) {
|
||||
for (out = dst; out < end; out++, in++) {
|
||||
*out = weston_v3f_add_v3f(weston_m3f_mul_v3f(t->lin1.matrix, *in),
|
||||
t->lin1.offset);
|
||||
}
|
||||
in = dst;
|
||||
}
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_ICC_CHAIN) {
|
||||
cmsDoTransform(t->icc_chain, in, dst, len);
|
||||
in = dst;
|
||||
}
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_LIN2) {
|
||||
for (out = dst; out < end; out++, in++) {
|
||||
*out = weston_v3f_add_v3f(weston_m3f_mul_v3f(t->lin2.matrix, *in),
|
||||
t->lin2.offset);
|
||||
}
|
||||
in = dst;
|
||||
}
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_CURVE2) {
|
||||
weston_color_curve_sample(compositor, &t->curve2, in, dst, len);
|
||||
in = dst;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
transformer_curve_fprint(FILE *fp,
|
||||
int indent,
|
||||
const char *step,
|
||||
const struct weston_color_curve *curve)
|
||||
{
|
||||
const struct weston_color_curve_enum *en;
|
||||
unsigned i;
|
||||
|
||||
if (curve->type != WESTON_COLOR_CURVE_TYPE_ENUM) {
|
||||
fprintf(fp, "%*s[unexpectedly not enum]\n", indent, "");
|
||||
return;
|
||||
}
|
||||
en = &curve->u.enumerated;
|
||||
|
||||
fprintf(fp, "%*s%s, %s", indent, "", step, en->tf.info->desc);
|
||||
if (en->tf.info->count_parameters > 0) {
|
||||
fprintf(fp, ": ");
|
||||
for (i = 0; i < en->tf.info->count_parameters; i++)
|
||||
fprintf(fp, " % .4f", en->tf.params[i]);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
static void
|
||||
transformer_linear_fprint(FILE *fp,
|
||||
int indent,
|
||||
const char *step,
|
||||
const struct weston_color_mapping_matrix *lin)
|
||||
{
|
||||
unsigned r, c;
|
||||
|
||||
fprintf(fp, "%*s%s\n", indent, "", step);
|
||||
for (r = 0; r < 3; r++) {
|
||||
fprintf(fp, "%*s", indent + 1, "");
|
||||
for (c = 0; c < 3; c++)
|
||||
fprintf(fp, " %8.4f", lin->matrix.col[c].el[r]);
|
||||
fprintf(fp, " %8.4f\n", lin->offset.el[r]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_color_transformer_details_fprint(FILE *fp,
|
||||
int indent,
|
||||
const struct cmlcms_color_transformer *t)
|
||||
{
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_CURVE1)
|
||||
transformer_curve_fprint(fp, indent, "curve1", &t->curve1);
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_LIN1)
|
||||
transformer_linear_fprint(fp, indent, "lin1", &t->lin1);
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_ICC_CHAIN)
|
||||
fprintf(fp, "%*sICC-to-ICC transform pipeline\n", indent, "");
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_LIN2)
|
||||
transformer_linear_fprint(fp, indent, "lin2", &t->lin2);
|
||||
|
||||
if (t->element_mask & CMLCMS_TRANSFORMER_CURVE2)
|
||||
transformer_curve_fprint(fp, indent, "curve2", &t->curve2);
|
||||
}
|
||||
|
||||
char *
|
||||
cmlcms_color_transformer_string(int indent,
|
||||
const struct cmlcms_color_transformer *t)
|
||||
{
|
||||
FILE *fp;
|
||||
char *str = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
fp = open_memstream(&str, &size);
|
||||
abort_oom_if_null(fp);
|
||||
|
||||
fprintf(fp, "%*sColor transform sampler for 3D LUT\n", indent, "");
|
||||
cmlcms_color_transformer_details_fprint(fp, indent + 2, t);
|
||||
|
||||
fclose(fp);
|
||||
abort_oom_if_null(str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ srcs_color_lcms = [
|
|||
'color-lcms.c',
|
||||
'color-profile.c',
|
||||
'color-transform.c',
|
||||
'color-transformer.c',
|
||||
]
|
||||
if (has_function_cmsGetToneCurveSegment)
|
||||
srcs_color_lcms += 'color-curve-segments.c'
|
||||
|
|
|
|||
|
|
@ -209,15 +209,34 @@ weston_cm_send_target_primaries(struct cm_image_desc_info *cm_image_desc_info,
|
|||
* This is a helper function that should be used by the color plugin
|
||||
* that owns the color profile and has information about it.
|
||||
*
|
||||
* \param cm_image_desc_info The image description info object
|
||||
* \param tf_info The tf_info object
|
||||
* \param cm_image_desc_info The image description info object.
|
||||
* \param tf The color transfer function to send.
|
||||
*/
|
||||
WL_EXPORT void
|
||||
weston_cm_send_tf_named(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_tf_info *tf_info)
|
||||
weston_cm_send_tf(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_tf *tf)
|
||||
{
|
||||
wp_image_description_info_v1_send_tf_named(cm_image_desc_info->owner,
|
||||
tf_info->protocol_tf);
|
||||
switch (tf->info->tf) {
|
||||
case WESTON_TF_BT1886:
|
||||
case WESTON_TF_GAMMA22:
|
||||
case WESTON_TF_GAMMA28:
|
||||
case WESTON_TF_SRGB:
|
||||
case WESTON_TF_EXT_SRGB:
|
||||
case WESTON_TF_ST240:
|
||||
case WESTON_TF_ST428:
|
||||
case WESTON_TF_ST2084_PQ:
|
||||
case WESTON_TF_EXT_LINEAR:
|
||||
case WESTON_TF_LOG_100:
|
||||
case WESTON_TF_LOG_316:
|
||||
case WESTON_TF_XVYCC:
|
||||
case WESTON_TF_HLG:
|
||||
wp_image_description_info_v1_send_tf_named(cm_image_desc_info->owner,
|
||||
tf->info->protocol_tf);
|
||||
break;
|
||||
case WESTON_TF_POWER:
|
||||
wp_image_description_info_v1_send_tf_power(cm_image_desc_info->owner,
|
||||
tf->params[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -261,6 +280,37 @@ weston_cm_send_target_luminances(struct cm_image_desc_info *cm_image_desc_info,
|
|||
max_lum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send complete parametric image description information to the client.
|
||||
*/
|
||||
WL_EXPORT void
|
||||
weston_cm_send_parametric_info(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_profile_params *par)
|
||||
{
|
||||
if (par->primaries_info)
|
||||
weston_cm_send_primaries_named(cm_image_desc_info, par->primaries_info);
|
||||
weston_cm_send_primaries(cm_image_desc_info, &par->primaries);
|
||||
weston_cm_send_target_primaries(cm_image_desc_info, &par->target_primaries);
|
||||
|
||||
weston_cm_send_tf(cm_image_desc_info, &par->tf);
|
||||
|
||||
weston_cm_send_luminances(cm_image_desc_info, par->min_luminance,
|
||||
par->max_luminance, par->reference_white_luminance);
|
||||
weston_cm_send_target_luminances(cm_image_desc_info,
|
||||
par->target_min_luminance,
|
||||
par->target_max_luminance);
|
||||
|
||||
if (par->maxCLL > 0.0f) {
|
||||
wp_image_description_info_v1_send_target_max_cll(cm_image_desc_info->owner,
|
||||
par->maxCLL);
|
||||
}
|
||||
|
||||
if (par->maxFALL > 0.0f) {
|
||||
wp_image_description_info_v1_send_target_max_fall(cm_image_desc_info->owner,
|
||||
par->maxFALL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an image description info object.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ weston_cm_send_target_primaries(struct cm_image_desc_info *cm_image_desc_info,
|
|||
const struct weston_color_gamut *color_gamut);
|
||||
|
||||
void
|
||||
weston_cm_send_tf_named(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_tf_info *tf_info);
|
||||
weston_cm_send_tf(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_tf *tf);
|
||||
|
||||
void
|
||||
weston_cm_send_luminances(struct cm_image_desc_info *cm_image_desc_info,
|
||||
|
|
@ -67,4 +67,8 @@ void
|
|||
weston_cm_send_target_luminances(struct cm_image_desc_info *cm_image_desc_info,
|
||||
float min_lum, float max_lum);
|
||||
|
||||
void
|
||||
weston_cm_send_parametric_info(struct cm_image_desc_info *cm_image_desc_info,
|
||||
const struct weston_color_profile_params *par);
|
||||
|
||||
#endif /* WESTON_COLOR_MANAGEMENT_H */
|
||||
|
|
|
|||
|
|
@ -192,9 +192,9 @@ sample_pq(enum weston_tf_direction tf_direction,
|
|||
* @param out The output array of length @c len .
|
||||
* @param len The in and out arrays' length.
|
||||
*/
|
||||
void
|
||||
WL_EXPORT void
|
||||
weston_color_curve_sample(struct weston_compositor *compositor,
|
||||
struct weston_color_curve *curve,
|
||||
const struct weston_color_curve *curve,
|
||||
const struct weston_vec3f *in,
|
||||
struct weston_vec3f *out,
|
||||
size_t len)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
void
|
||||
weston_color_curve_sample(struct weston_compositor *compositor,
|
||||
struct weston_color_curve *curve,
|
||||
const struct weston_color_curve *curve,
|
||||
const struct weston_vec3f *in,
|
||||
struct weston_vec3f *out,
|
||||
size_t len);
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct weston_hdr_metadata_type1 {
|
|||
float maxFALL;
|
||||
};
|
||||
|
||||
/** Output properties derived from its color characteristics and profile
|
||||
/** Output properties derived from its color profile
|
||||
*
|
||||
* These are constructed by a color manager.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8150,50 +8150,6 @@ weston_output_get_hdr_metadata_type1(const struct weston_output *output)
|
|||
return &output->color_outcome->hdr_meta;
|
||||
}
|
||||
|
||||
/** Set display or monitor basic color characteristics
|
||||
*
|
||||
* \param output The output to modify, must be in disabled state.
|
||||
* \param cc The new characteristics to set, or NULL to unset everything.
|
||||
*
|
||||
* This sets the metadata that describes the color characteristics of the
|
||||
* output in a very simple manner. If a non-NULL color profile is set for the
|
||||
* output, that will always take precedence.
|
||||
*
|
||||
* The initial value has everything unset.
|
||||
*
|
||||
* This function is meant to be used by compositor frontends.
|
||||
*
|
||||
* \ingroup output
|
||||
* \sa weston_output_set_color_profile
|
||||
*/
|
||||
WL_EXPORT void
|
||||
weston_output_set_color_characteristics(struct weston_output *output,
|
||||
const struct weston_color_characteristics *cc)
|
||||
{
|
||||
assert(!output->enabled);
|
||||
|
||||
if (cc)
|
||||
output->color_characteristics = *cc;
|
||||
else
|
||||
output->color_characteristics.group_mask = 0;
|
||||
}
|
||||
|
||||
/** Get display or monitor basic color characteristics
|
||||
*
|
||||
* \param output The output to query.
|
||||
* \return Pointer to the metadata stored in weston_output.
|
||||
*
|
||||
* This function is meant to be used by color manager modules.
|
||||
*
|
||||
* \ingroup output
|
||||
* \sa weston_output_set_color_characteristics
|
||||
*/
|
||||
WL_EXPORT const struct weston_color_characteristics *
|
||||
weston_output_get_color_characteristics(struct weston_output *output)
|
||||
{
|
||||
return &output->color_characteristics;
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
weston_output_set_single_mode(struct weston_output *output,
|
||||
struct weston_mode *target)
|
||||
|
|
|
|||
|
|
@ -597,11 +597,6 @@ on DRM, headless, wayland, and x11 backends, and for remoting and pipewire
|
|||
outputs.
|
||||
.TP 7
|
||||
.BI "color-profile=" name
|
||||
|
||||
.B NOTE:
|
||||
This feature is not fully implemented yet, and therefore it is not expected to
|
||||
work.
|
||||
|
||||
Color profiles describe how the display is assumed to behave. Knowing the
|
||||
display color behavior, Weston can adapt window contents to produce the
|
||||
presumed appearance. The option
|
||||
|
|
@ -619,7 +614,7 @@ section by referring to it by its
|
|||
value. Alternatively this key can use one of the special profile names that
|
||||
contain a colon (:).
|
||||
|
||||
The default, once implemented, will be
|
||||
The default is
|
||||
.BR "color-profile=auto:" .
|
||||
|
||||
The following special names are accepted:
|
||||
|
|
@ -786,13 +781,6 @@ The mode can be one of the following strings:
|
|||
Defaults to
|
||||
.BR sdr ". Non-SDR modes require " "color-management=true" .
|
||||
.TP 7
|
||||
.BI "color_characteristics=" name
|
||||
Sets the basic output color characteristics by loading the parameters from the
|
||||
.B color_characteristics
|
||||
section with the key
|
||||
.BI "name=" name
|
||||
\&. If an ICC profile is also set, the ICC profile takes precedence.
|
||||
.TP 7
|
||||
.BI "mirror-of=" ouput_name
|
||||
Makes the remote output overlap (mirror) the native output identified by the
|
||||
mirror-of value. This is useful for sharing or mirroring a native DRM output.
|
||||
|
|
@ -924,69 +912,6 @@ parallel to Weston, so it does not have to immediately exit. Defaults to empty.
|
|||
If set to true, quit Weston after the auto-launched executable exits. Set to false
|
||||
by default.
|
||||
.\"---------------------------------------------------------------------
|
||||
.SH "COLOR_CHARACTERISTICS SECTION"
|
||||
Each
|
||||
.B color_characteristics
|
||||
section records one set of basic display or monitor color characterisation
|
||||
parameters. The parameters are defined in CTA-861-H specification as Static
|
||||
Metadata Type 1, and they can also be found in EDID. The parameters are
|
||||
divided into groups. Each group must be given either fully or not at all.
|
||||
.PP
|
||||
Each section should be named with
|
||||
.B name
|
||||
key by which it can be referenced from other sections. A metadata section is
|
||||
just a collection of parameter values and does nothing on its own. It has an
|
||||
effect only when referenced from elsewhere.
|
||||
.PP
|
||||
See
|
||||
.BR output " section key " color_characteristics .
|
||||
.TP 7
|
||||
.BI "name=" name
|
||||
An arbitrary name for this section. You can choose any name you want as long as
|
||||
it does not contain the colon
|
||||
.RB ( : )
|
||||
character. Names with at least one colon are reserved.
|
||||
.SS Primaries group
|
||||
.TP 7
|
||||
.BI "red_x=" x
|
||||
.TQ
|
||||
.BI "red_y=" y
|
||||
.TQ
|
||||
.BI "green_x=" x
|
||||
.TQ
|
||||
.BI "green_y=" y
|
||||
.TQ
|
||||
.BI "blue_x=" x
|
||||
.TQ
|
||||
.BI "blue_y=" y
|
||||
The CIE 1931 xy chromaticity coordinates of the display primaries.
|
||||
These floating point values must reside between 0.0 and 1.0, inclusive.
|
||||
.SS White point group
|
||||
.TP 7
|
||||
.BI "white_x=" x
|
||||
.TQ
|
||||
.BI "white_y=" y
|
||||
The CIE 1931 xy chromaticity coordinates of the display white point.
|
||||
These floating point values must reside between 0.0 and 1.0, inclusive.
|
||||
.SS Independent parameters
|
||||
Each parameter listed here has its own group and therefore can be given
|
||||
alone.
|
||||
.TP 7
|
||||
.BI "max_L=" L
|
||||
Display's desired maximum content luminance (peak)
|
||||
.IR L \~cd/m²,
|
||||
a floating point value in the range 0.0\(en100000.0.
|
||||
.TP 7
|
||||
.BI "min_L=" L
|
||||
Display's desired minimum content luminance
|
||||
.IR L \~cd/m²,
|
||||
a floating point value in the range 0.0\(en100000.0.
|
||||
.TP 7
|
||||
.BI "maxFALL=" L
|
||||
Display's desired maximum frame-average light level
|
||||
.IR L \~cd/m²,
|
||||
a floating point value in the range 0.0\(en100000.0.
|
||||
.\"---------------------------------------------------------------------
|
||||
.SH "COLOR-PROFILE SECTION"
|
||||
Each
|
||||
.B color-profile
|
||||
|
|
|
|||
|
|
@ -130,10 +130,10 @@ static const struct weston_color_gamut color_gamut_invalid_white_point = {
|
|||
|
||||
static const struct test_case good_test_cases[] = {
|
||||
{
|
||||
/* sRGB primaries with sRGB TF; succeeds. */
|
||||
/* sRGB primaries with gamma22; succeeds. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -147,10 +147,10 @@ static const struct test_case good_test_cases[] = {
|
|||
.error_point = ERROR_POINT_NONE,
|
||||
},
|
||||
{
|
||||
/* Custom primaries with sRGB TF; succeeds. */
|
||||
/* Custom primaries with gamma22; succeeds. */
|
||||
.primaries_named = NOT_SET,
|
||||
.primaries = &color_gamut_sRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -164,10 +164,10 @@ static const struct test_case good_test_cases[] = {
|
|||
.error_point = ERROR_POINT_NONE,
|
||||
},
|
||||
{
|
||||
/* sRGB primaries, sRGB TF and valid luminance values; succeeds. */
|
||||
/* sRGB primaries, gamma22 and valid luminance values; succeeds. */
|
||||
.primaries_named = NOT_SET,
|
||||
.primaries = &color_gamut_sRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = 0.5,
|
||||
.primaries_max_lum = 2000,
|
||||
|
|
@ -198,10 +198,10 @@ static const struct test_case good_test_cases[] = {
|
|||
.error_point = ERROR_POINT_NONE,
|
||||
},
|
||||
{
|
||||
/* sRGB primaries, sRGB TF and valid target primaries; succeeds. */
|
||||
/* sRGB primaries, gamma22 and valid target primaries; succeeds. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -344,7 +344,7 @@ static const struct test_case bad_test_cases[] = {
|
|||
* protocol error. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -362,7 +362,7 @@ static const struct test_case bad_test_cases[] = {
|
|||
* protocol error. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -416,7 +416,7 @@ static const struct test_case bad_test_cases[] = {
|
|||
* defined range); graceful failure. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -434,7 +434,7 @@ static const struct test_case bad_test_cases[] = {
|
|||
* graceful failure. */
|
||||
.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB,
|
||||
.primaries = NULL,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
|
||||
.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
|
||||
.tf_power = NOT_SET,
|
||||
.primaries_min_lum = NOT_SET,
|
||||
.primaries_max_lum = NOT_SET,
|
||||
|
|
@ -609,7 +609,7 @@ color_manager_init(struct color_manager *cm, struct client *client)
|
|||
test_assert_u32_eq(cm->supported_rendering_intents,
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION) |
|
||||
/* (1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION) | */
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_ABSOLUTE) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE_BPC));
|
||||
|
||||
|
|
@ -631,7 +631,7 @@ color_manager_init(struct color_manager *cm, struct client *client)
|
|||
test_assert_u32_eq(cm->supported_tf,
|
||||
(1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22) |
|
||||
(1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28) |
|
||||
(1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB) |
|
||||
(1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR) |
|
||||
(1 << WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ));
|
||||
|
||||
test_assert_true(cm->done);
|
||||
|
|
@ -996,10 +996,10 @@ TEST(set_tf_named_twice)
|
|||
image_desc_creator_param =
|
||||
wp_color_manager_v1_create_parametric_creator(cm.manager);
|
||||
wp_image_description_creator_params_v1_set_tf_named(image_desc_creator_param,
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
|
||||
client_roundtrip(client); /* make sure connection is still valid */
|
||||
wp_image_description_creator_params_v1_set_tf_named(image_desc_creator_param,
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
|
||||
expect_protocol_error(client, &wp_image_description_creator_params_v1_interface,
|
||||
WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET);
|
||||
wp_image_description_creator_params_v1_destroy(image_desc_creator_param);
|
||||
|
|
@ -1025,7 +1025,7 @@ TEST(set_tf_power_then_tf_named)
|
|||
2.4 * 10000);
|
||||
client_roundtrip(client); /* make sure connection is still valid */
|
||||
wp_image_description_creator_params_v1_set_tf_named(image_desc_creator_param,
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
|
||||
expect_protocol_error(client, &wp_image_description_creator_params_v1_interface,
|
||||
WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET);
|
||||
wp_image_description_creator_params_v1_destroy(image_desc_creator_param);
|
||||
|
|
@ -1048,7 +1048,7 @@ TEST(set_tf_named_then_tf_power)
|
|||
image_desc_creator_param =
|
||||
wp_color_manager_v1_create_parametric_creator(cm.manager);
|
||||
wp_image_description_creator_params_v1_set_tf_named(image_desc_creator_param,
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
|
||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
|
||||
client_roundtrip(client); /* make sure connection is still valid */
|
||||
wp_image_description_creator_params_v1_set_tf_power(image_desc_creator_param,
|
||||
2.4 * 10000);
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ color_manager_init(struct color_manager *cm, struct client *client)
|
|||
test_assert_u32_eq(cm->supported_rendering_intents,
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION) |
|
||||
/* (1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_SATURATION) | */
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_ABSOLUTE) |
|
||||
(1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE_BPC));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,971 +0,0 @@
|
|||
/*
|
||||
* Copyright 2022 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 <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "weston-test-client-helper.h"
|
||||
#include "weston-test-fixture-compositor.h"
|
||||
#include "weston-test-assert.h"
|
||||
|
||||
#include "weston-private.h"
|
||||
#include "libweston-internal.h"
|
||||
#include "backend.h"
|
||||
#include "color.h"
|
||||
#include "id-number-allocator.h"
|
||||
#include "shared/string-helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
struct config_testcase {
|
||||
bool has_characteristics_key;
|
||||
const char *output_characteristics_name;
|
||||
const char *characteristics_name;
|
||||
const char *red_x;
|
||||
const char *green_y;
|
||||
const char *white_y;
|
||||
const char *min_L;
|
||||
int expected_retval;
|
||||
const char *expected_error;
|
||||
};
|
||||
|
||||
static const struct config_testcase config_cases[] = {
|
||||
{
|
||||
false, "fred", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", 0,
|
||||
""
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "min_L=1e-4", 0,
|
||||
""
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "", 0,
|
||||
""
|
||||
},
|
||||
{
|
||||
true, "notexisting", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini, output mockoutput: no [color_characteristics] section with 'name=notexisting' found.\n"
|
||||
},
|
||||
{
|
||||
true, "fr:ed", "fr:ed", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini [color_characteristics] name=fr:ed is a reserved name. Do not use ':' character in the name.\n"
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "red_x=-5", "green_y=1.01", "white_y=0.323", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini [color_characteristics] name=fred: red_x value -5.000000 is outside of the range 0.000000 - 1.000000.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: green_y value 1.010000 is outside of the range 0.000000 - 1.000000.\n"
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "red_x=haahaa", "green_y=-", "white_y=0.323", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key red_x.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key green_y.\n"
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "", "", "white_y=0.323", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key red_x is missing. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key red_y is set. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key green_x is set. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key green_y is missing. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_x is set. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_y is set. You must set either none or all keys of a group.\n"
|
||||
},
|
||||
{
|
||||
true, "fred", "fred", "red_x=0.9", "green_y=0.8", "", "min_L=1e-4", -1,
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 2 key white_x is set. You must set either none or all keys of a group.\n"
|
||||
"Config error in weston.ini [color_characteristics] name=fred: group 2 key white_y is missing. You must set either none or all keys of a group.\n"
|
||||
},
|
||||
};
|
||||
|
||||
static FILE *logfile;
|
||||
|
||||
static int
|
||||
logger(const char *fmt, va_list arg)
|
||||
{
|
||||
return vfprintf(logfile, fmt, arg);
|
||||
}
|
||||
|
||||
static int
|
||||
no_logger(const char *fmt, va_list arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct weston_config *
|
||||
create_config(const struct config_testcase *t)
|
||||
{
|
||||
struct compositor_setup setup;
|
||||
struct weston_config *wc;
|
||||
|
||||
compositor_setup_defaults(&setup);
|
||||
weston_ini_setup(&setup,
|
||||
cfgln("[output]"),
|
||||
cfgln("name=mockoutput"),
|
||||
t->has_characteristics_key ?
|
||||
cfgln("color_characteristics=%s", t->output_characteristics_name) :
|
||||
cfgln(""),
|
||||
cfgln("eotf-mode=st2084"),
|
||||
|
||||
cfgln("[color_characteristics]"),
|
||||
cfgln("name=%s", t->characteristics_name),
|
||||
cfgln("maxFALL=1000"),
|
||||
cfgln("%s", t->red_x),
|
||||
cfgln("red_y=0.3"),
|
||||
cfgln("blue_x=0.1"),
|
||||
cfgln("blue_y=0.11"),
|
||||
cfgln("green_x=0.1771"),
|
||||
cfgln("%s", t->green_y),
|
||||
cfgln("white_x=0.313"),
|
||||
cfgln("%s", t->white_y),
|
||||
cfgln("%s", t->min_L),
|
||||
cfgln("max_L=65535.0"),
|
||||
|
||||
cfgln("[core]"),
|
||||
cfgln("color-management=true"));
|
||||
|
||||
wc = weston_config_parse(setup.config_file);
|
||||
free(setup.config_file);
|
||||
|
||||
return wc;
|
||||
}
|
||||
|
||||
struct mock_color_manager {
|
||||
struct weston_color_manager base;
|
||||
struct weston_hdr_metadata_type1 *test_hdr_meta;
|
||||
};
|
||||
|
||||
static struct weston_output_color_outcome *
|
||||
mock_create_output_color_outcome(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct mock_color_manager *cm = container_of(cm_base, typeof(*cm), base);
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
co = xzalloc(sizeof *co);
|
||||
|
||||
co->hdr_meta = *cm->test_hdr_meta;
|
||||
|
||||
return co;
|
||||
}
|
||||
|
||||
static struct weston_color_profile *
|
||||
mock_cm_ref_stock_sRGB_color_profile(struct weston_color_manager *mock_cm)
|
||||
{
|
||||
struct weston_color_profile *mock_cprof;
|
||||
|
||||
mock_cprof = xzalloc(sizeof(*mock_cprof));
|
||||
|
||||
weston_color_profile_init(mock_cprof, mock_cm);
|
||||
str_printf(&mock_cprof->description, "mock cprof");
|
||||
|
||||
return mock_cprof;
|
||||
}
|
||||
|
||||
static bool
|
||||
mock_cm_get_color_profile_from_params(struct weston_color_manager *cm,
|
||||
const struct weston_color_profile_params *params,
|
||||
const char *name_part,
|
||||
struct weston_color_profile **cprof_out,
|
||||
char **errmsg)
|
||||
{
|
||||
test_assert_not_reached("This cannot be a valid parametric profile.");
|
||||
}
|
||||
|
||||
static void
|
||||
mock_cm_destroy_color_profile(struct weston_color_profile *mock_cprof)
|
||||
{
|
||||
free(mock_cprof->description);
|
||||
free(mock_cprof);
|
||||
}
|
||||
|
||||
/*
|
||||
* Manufacture various weston.ini and check what
|
||||
* wet_output_set_color_characteristics() says. Tests for the return value and
|
||||
* the error messages logged.
|
||||
*/
|
||||
TEST_P(color_characteristics_config_error, config_cases)
|
||||
{
|
||||
const struct config_testcase *t = data;
|
||||
struct weston_config *wc;
|
||||
struct weston_config_section *section;
|
||||
int retval;
|
||||
char *logbuf;
|
||||
size_t logsize;
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.create_output_color_outcome = mock_create_output_color_outcome,
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
struct weston_output mock_output = {};
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
|
||||
logfile = open_memstream(&logbuf, &logsize);
|
||||
weston_log_set_handler(logger, logger);
|
||||
|
||||
wc = create_config(t);
|
||||
section = weston_config_get_section(wc, "output", "name", "mockoutput");
|
||||
test_assert_ptr_not_null(section);
|
||||
|
||||
retval = wet_output_set_color_characteristics(&mock_output, wc, section);
|
||||
|
||||
test_assert_int_eq(fclose(logfile), 0);
|
||||
logfile = NULL;
|
||||
|
||||
testlog("retval %d, logs:\n%s\n", retval, logbuf);
|
||||
|
||||
test_assert_int_eq(retval, t->expected_retval);
|
||||
test_assert_int_eq(strcmp(logbuf, t->expected_error), 0);
|
||||
|
||||
weston_config_destroy(wc);
|
||||
free(logbuf);
|
||||
weston_output_release(&mock_output);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/* Setting NULL resets group_mask */
|
||||
TEST(weston_output_set_color_characteristics_null)
|
||||
{
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.create_output_color_outcome = mock_create_output_color_outcome,
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
struct weston_output mock_output = {};
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
|
||||
mock_output.color_characteristics.group_mask = 1;
|
||||
weston_output_set_color_characteristics(&mock_output, NULL);
|
||||
test_assert_u32_eq(mock_output.color_characteristics.group_mask, 0);
|
||||
|
||||
weston_output_release(&mock_output);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
struct value_testcase {
|
||||
unsigned field_index;
|
||||
float value;
|
||||
bool retval;
|
||||
};
|
||||
|
||||
static const struct value_testcase value_cases[] = {
|
||||
{ 0, 0.0, true },
|
||||
{ 0, 1.0, true },
|
||||
{ 0, -0.001, false },
|
||||
{ 0, 1.01, false },
|
||||
{ 0, NAN, false },
|
||||
{ 0, HUGE_VALF, false },
|
||||
{ 0, -HUGE_VALF, false },
|
||||
{ 1, -1.0, false },
|
||||
{ 2, 2.0, false },
|
||||
{ 3, 2.0, false },
|
||||
{ 4, 2.0, false },
|
||||
{ 5, 2.0, false },
|
||||
{ 6, 2.0, false },
|
||||
{ 7, 2.0, false },
|
||||
{ 8, 0.99, false },
|
||||
{ 8, 65535.1, false },
|
||||
{ 9, 0.000099, false },
|
||||
{ 9, 6.55351, false },
|
||||
{ 10, 0.99, false },
|
||||
{ 10, 65535.1, false },
|
||||
{ 11, 0.99, false },
|
||||
{ 11, 65535.1, false },
|
||||
};
|
||||
|
||||
/*
|
||||
* Modify one value in a known good metadata structure, and see how
|
||||
* validation reacts to it.
|
||||
*/
|
||||
TEST_P(hdr_metadata_type1_errors, value_cases)
|
||||
{
|
||||
struct value_testcase *t = data;
|
||||
struct weston_hdr_metadata_type1 meta = {
|
||||
.group_mask = WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK,
|
||||
.primary[0] = { 0.6650, 0.3261 },
|
||||
.primary[1] = { 0.2890, 0.6435 },
|
||||
.primary[2] = { 0.1491, 0.0507 },
|
||||
.white = { 0.3134, 0.3291 },
|
||||
.maxDML = 600.0,
|
||||
.minDML = 0.0001,
|
||||
.maxCLL = 600.0,
|
||||
.maxFALL = 400.0,
|
||||
};
|
||||
float *fields[] = {
|
||||
&meta.primary[0].x, &meta.primary[0].y,
|
||||
&meta.primary[1].x, &meta.primary[1].y,
|
||||
&meta.primary[2].x, &meta.primary[2].y,
|
||||
&meta.white.x, &meta.white.y,
|
||||
&meta.maxDML, &meta.minDML,
|
||||
&meta.maxCLL, &meta.maxFALL,
|
||||
};
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.create_output_color_outcome = mock_create_output_color_outcome,
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
.test_hdr_meta = &meta,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
struct weston_output mock_output = {};
|
||||
bool ret;
|
||||
|
||||
weston_log_set_handler(no_logger, no_logger);
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
|
||||
test_assert_uint_lt(t->field_index, ARRAY_LENGTH(fields));
|
||||
*fields[t->field_index] = t->value;
|
||||
ret = weston_output_set_color_outcome(&mock_output);
|
||||
test_assert_int_eq(ret, t->retval);
|
||||
|
||||
weston_output_color_outcome_destroy(&mock_output.color_outcome);
|
||||
weston_output_release(&mock_output);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/* Unflagged members are ignored in validity check */
|
||||
TEST(hdr_metadata_type1_ignore_unflagged)
|
||||
{
|
||||
/* All values invalid, but also empty mask so none actually used. */
|
||||
struct weston_hdr_metadata_type1 meta = {
|
||||
.group_mask = 0,
|
||||
.primary[0] = { -1.0, -1.0 },
|
||||
.primary[1] = { -1.0, -1.0 },
|
||||
.primary[2] = { -1.0, -1.0 },
|
||||
.white = { -1.0, -1.0 },
|
||||
.maxDML = -1.0,
|
||||
.minDML = -1.0,
|
||||
.maxCLL = -1.0,
|
||||
.maxFALL = -1.0,
|
||||
};
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.create_output_color_outcome = mock_create_output_color_outcome,
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
.test_hdr_meta = &meta,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
struct weston_output mock_output = {};
|
||||
bool ret;
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
weston_log_set_handler(no_logger, no_logger);
|
||||
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
|
||||
ret = weston_output_set_color_outcome(&mock_output);
|
||||
test_assert_true(ret);
|
||||
|
||||
weston_output_color_outcome_destroy(&mock_output.color_outcome);
|
||||
weston_output_release(&mock_output);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
struct mode_testcase {
|
||||
bool color_management;
|
||||
uint32_t supported_eotf_mask;
|
||||
uint32_t supported_colorimetry_mask;
|
||||
const char *eotf_mode;
|
||||
const char *colorimetry_mode;
|
||||
enum weston_eotf_mode expected_eotf_mode;
|
||||
enum weston_colorimetry_mode expected_colorimetry_mode;
|
||||
int expected_retval;
|
||||
const char *expected_error;
|
||||
};
|
||||
|
||||
static const struct mode_testcase mode_config_cases[] = {
|
||||
/* Defaults */
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT, NULL, NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
/* Color management off, EOTF modes */
|
||||
{
|
||||
false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "sdr", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hdr-gamma", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: EOTF mode hdr-gamma on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "st2084", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: EOTF mode st2084 on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: EOTF mode hlg on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "nonosense", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error in config for output 'mockoutput': 'nonosense' is not a valid EOTF mode. Try one of: sdr hdr-gamma st2084 hlg\n"
|
||||
},
|
||||
/* Color management on, EOTF modes */
|
||||
{
|
||||
true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "sdr", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hdr-gamma", NULL,
|
||||
WESTON_EOTF_MODE_TRADITIONAL_HDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "st2084", NULL,
|
||||
WESTON_EOTF_MODE_ST2084, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL,
|
||||
WESTON_EOTF_MODE_HLG, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_ALL_MASK, WESTON_COLORIMETRY_MODE_DEFAULT, "nonosense", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error in config for output 'mockoutput': 'nonosense' is not a valid EOTF mode. Try one of: sdr hdr-gamma st2084 hlg\n"
|
||||
},
|
||||
/* unsupported EOTF mode */
|
||||
{
|
||||
true,
|
||||
WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | WESTON_EOTF_MODE_ST2084,
|
||||
WESTON_COLORIMETRY_MODE_DEFAULT, "hlg", NULL,
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: output 'mockoutput' does not support EOTF mode hlg.\n"
|
||||
},
|
||||
/* Color management off, colorimetry modes */
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "default",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020cycc",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode bt2020cycc on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020ycc",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode bt2020ycc on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020rgb",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode bt2020rgb on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3d65",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode p3d65 on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3dci",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode p3dci on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "ictcp",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: Colorimetry mode ictcp on output 'mockoutput' requires color-management=true in weston.ini\n"
|
||||
},
|
||||
{
|
||||
false, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "imagine that",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error in config for output 'mockoutput': 'imagine that' is not a valid colorimetry mode. Try one of: default bt2020cycc bt2020ycc bt2020rgb p3d65 p3dci ictcp\n"
|
||||
},
|
||||
/* Color management on, colorimetry modes */
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "default",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020cycc",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_CYCC,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020ycc",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_YCC,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "bt2020rgb",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_BT2020_RGB,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3d65",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_P3D65,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "p3dci",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_P3DCI,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "ictcp",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ICTCP,
|
||||
0, ""
|
||||
},
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_ALL_MASK, NULL, "imagine that",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error in config for output 'mockoutput': 'imagine that' is not a valid colorimetry mode. Try one of: default bt2020cycc bt2020ycc bt2020rgb p3d65 p3dci ictcp\n"
|
||||
},
|
||||
/* Unsupported colorimetry mode */
|
||||
{
|
||||
true, WESTON_EOTF_MODE_SDR,
|
||||
WESTON_COLORIMETRY_MODE_DEFAULT | WESTON_COLORIMETRY_MODE_BT2020_RGB | WESTON_COLORIMETRY_MODE_BT2020_CYCC | WESTON_COLORIMETRY_MODE_P3D65,
|
||||
NULL, "ictcp",
|
||||
WESTON_EOTF_MODE_SDR, WESTON_COLORIMETRY_MODE_DEFAULT,
|
||||
-1, "Error: output 'mockoutput' does not support colorimetry mode ictcp.\n"
|
||||
},
|
||||
};
|
||||
|
||||
static struct weston_config *
|
||||
create_mode_config(const struct mode_testcase *t)
|
||||
{
|
||||
struct compositor_setup setup;
|
||||
struct weston_config *wc;
|
||||
|
||||
compositor_setup_defaults(&setup);
|
||||
weston_ini_setup(&setup,
|
||||
cfgln("[output]"),
|
||||
cfgln("name=mockoutput"),
|
||||
|
||||
t->eotf_mode ?
|
||||
cfgln("eotf-mode=%s", t->eotf_mode) :
|
||||
cfgln(""),
|
||||
|
||||
t->colorimetry_mode ?
|
||||
cfgln("colorimetry-mode=%s", t->colorimetry_mode) :
|
||||
cfgln("")
|
||||
);
|
||||
|
||||
wc = weston_config_parse(setup.config_file);
|
||||
free(setup.config_file);
|
||||
|
||||
return wc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Manufacture various weston.ini and check what
|
||||
* wet_output_set_eotf_mode() and wet_output_set_colorimetry_mode() says.
|
||||
* Tests for the return value and the error messages logged.
|
||||
*/
|
||||
TEST_P(mode_config_error, mode_config_cases)
|
||||
{
|
||||
const struct mode_testcase *t = data;
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.create_output_color_outcome = mock_create_output_color_outcome,
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
|
||||
struct weston_config *wc;
|
||||
struct weston_config_section *section;
|
||||
int retval;
|
||||
int attached;
|
||||
char *logbuf;
|
||||
size_t logsize;
|
||||
struct weston_head mock_head = {};
|
||||
struct weston_output mock_output = {};
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
weston_head_init(&mock_head, "mockhead");
|
||||
weston_head_set_supported_eotf_mask(&mock_head, t->supported_eotf_mask);
|
||||
weston_head_set_supported_colorimetry_mask(&mock_head, t->supported_colorimetry_mask);
|
||||
attached = weston_output_attach_head(&mock_output, &mock_head);
|
||||
test_assert_int_eq(attached, 0);
|
||||
|
||||
logfile = open_memstream(&logbuf, &logsize);
|
||||
weston_log_set_handler(logger, logger);
|
||||
|
||||
wc = create_mode_config(t);
|
||||
section = weston_config_get_section(wc, "output", "name", "mockoutput");
|
||||
test_assert_ptr_not_null(section);
|
||||
|
||||
retval = wet_output_set_eotf_mode(&mock_output, section, t->color_management);
|
||||
if (retval == 0) {
|
||||
retval = wet_output_set_colorimetry_mode(&mock_output, section,
|
||||
t->color_management);
|
||||
}
|
||||
|
||||
test_assert_int_eq(fclose(logfile), 0);
|
||||
logfile = NULL;
|
||||
|
||||
testlog("retval %d, logs:\n%s\n", retval, logbuf);
|
||||
|
||||
test_assert_int_eq(retval, t->expected_retval);
|
||||
test_assert_int_eq(strcmp(logbuf, t->expected_error), 0);
|
||||
test_assert_enum(weston_output_get_eotf_mode(&mock_output), t->expected_eotf_mode);
|
||||
test_assert_enum(weston_output_get_colorimetry_mode(&mock_output), t->expected_colorimetry_mode);
|
||||
|
||||
weston_config_destroy(wc);
|
||||
free(logbuf);
|
||||
weston_output_release(&mock_output);
|
||||
weston_head_release(&mock_head);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
test_creating_output_color_profile(struct weston_config *wc,
|
||||
const char *profile_name,
|
||||
uint32_t supported_color_features,
|
||||
uint32_t supported_primaries_named,
|
||||
uint32_t supported_tf_named,
|
||||
const char *expected_error)
|
||||
{
|
||||
struct weston_color_profile *cprof;
|
||||
char *logbuf;
|
||||
size_t logsize;
|
||||
struct mock_color_manager mock_cm = {
|
||||
.base.ref_stock_sRGB_color_profile = mock_cm_ref_stock_sRGB_color_profile,
|
||||
.base.get_color_profile_from_params = mock_cm_get_color_profile_from_params,
|
||||
.base.destroy_color_profile = mock_cm_destroy_color_profile,
|
||||
.base.supported_color_features = supported_color_features,
|
||||
.base.supported_primaries_named = supported_primaries_named,
|
||||
.base.supported_tf_named = supported_tf_named,
|
||||
};
|
||||
struct weston_compositor mock_compositor = {
|
||||
.color_manager = &mock_cm.base,
|
||||
.color_profile_id_generator = weston_idalloc_create(&mock_compositor),
|
||||
};
|
||||
struct weston_output mock_output = {};
|
||||
|
||||
mock_cm.base.compositor = &mock_compositor;
|
||||
|
||||
wl_list_init(&mock_compositor.plane_list);
|
||||
|
||||
logfile = open_memstream(&logbuf, &logsize);
|
||||
weston_log_set_handler(logger, logger);
|
||||
|
||||
weston_output_init(&mock_output, &mock_compositor, "mockoutput");
|
||||
|
||||
cprof = wet_create_output_color_profile(&mock_output, wc, profile_name);
|
||||
test_assert_ptr_null(cprof);
|
||||
|
||||
test_assert_int_eq(fclose(logfile), 0);
|
||||
logfile = NULL;
|
||||
|
||||
testlog("logs:\n%s\n------\n", logbuf);
|
||||
|
||||
test_assert_str_eq(logbuf, expected_error);
|
||||
|
||||
free(logbuf);
|
||||
weston_output_release(&mock_output);
|
||||
weston_idalloc_destroy(mock_compositor.color_profile_id_generator);
|
||||
}
|
||||
|
||||
struct color_profile_name_testcase {
|
||||
const char *profile_name;
|
||||
const char *expected_error;
|
||||
};
|
||||
|
||||
static const struct color_profile_name_testcase color_profile_name_cases[] = {
|
||||
{
|
||||
"notexists",
|
||||
"Config error in weston.ini, output mockoutput: no [color-profile] section with 'name=notexists' found.\n",
|
||||
},
|
||||
{
|
||||
"boo:faa",
|
||||
"Config error in weston.ini, output mockoutput, color-profile=boo:faa is illegal. The ':' character is legal only for 'srgb:' and 'auto:'.\n",
|
||||
},
|
||||
{
|
||||
"auto:kek",
|
||||
"Config error in weston.ini, output mockoutput, key color-profile=auto: invalid flag 'kek'.\n",
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Manufacture various weston.ini and check the error messages that
|
||||
* wet_create_output_color_profile() generates for bad color-profile names.
|
||||
*/
|
||||
TEST_P(parametric_color_profile_name_errors, color_profile_name_cases)
|
||||
{
|
||||
const struct color_profile_name_testcase *t = data;
|
||||
|
||||
test_creating_output_color_profile(NULL, t->profile_name,
|
||||
0xffffffff, 0xffffffff, 0xffffffff,
|
||||
t->expected_error);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
struct parameters_testcase {
|
||||
const char *profile_string;
|
||||
const char *expected_error;
|
||||
};
|
||||
|
||||
static const struct parameters_testcase param_config_cases[] = {
|
||||
{
|
||||
"",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" primaries not set\n"
|
||||
" transfer function not set\n",
|
||||
},
|
||||
{
|
||||
"tf_named=gamma22\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" primaries not set\n"
|
||||
},
|
||||
{
|
||||
"prim_named=srgb\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" transfer function not set\n",
|
||||
},
|
||||
{
|
||||
"tf_named=kukkuu\n"
|
||||
"prim_named=jeejee\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, prim_named has unknown value 'jeejee'.\n"
|
||||
"Config error in weston.ini [color-profile] name=mydisp, tf_named has unknown value 'kukkuu'.\n",
|
||||
},
|
||||
{
|
||||
"prim_named=pal\n"
|
||||
"tf_named=gamma28\n"
|
||||
"tf_power=2.4\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" tf was already set\n",
|
||||
},
|
||||
{
|
||||
"prim_named=pal_m\n"
|
||||
"prim_red=0.67 0.33\n"
|
||||
"prim_green=0.21 0.71\n"
|
||||
"prim_blue=0.14 0.08\n"
|
||||
"prim_white=0.31 0.32\n"
|
||||
"tf_power=2.4\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" primaries were already set\n",
|
||||
},
|
||||
{
|
||||
"prim_red=0.6 0.3\n"
|
||||
"prim_blue=0.1 0.05\n"
|
||||
"min_lum=0\n"
|
||||
"target_white=0.33 0.33\n"
|
||||
"target_max_lum=1200\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp:\n"
|
||||
" group: signaling primaries\n"
|
||||
" prim_red is set.\n"
|
||||
" prim_green is missing.\n"
|
||||
" prim_blue is set.\n"
|
||||
" prim_white is missing.\n"
|
||||
" group: signaling luminances\n"
|
||||
" min_lum is set.\n"
|
||||
" max_lum is missing.\n"
|
||||
" ref_lum is missing.\n"
|
||||
" group: target primaries\n"
|
||||
" target_red is missing.\n"
|
||||
" target_green is missing.\n"
|
||||
" target_blue is missing.\n"
|
||||
" target_white is set.\n"
|
||||
" group: target luminances\n"
|
||||
" target_min_lum is missing.\n"
|
||||
" target_max_lum is set.\n"
|
||||
"You must set either none or all keys of a group.\n",
|
||||
},
|
||||
{
|
||||
"prim_red=0.67 0.33 0.4\n"
|
||||
"prim_green=0.21\n"
|
||||
"prim_blue=0,14 k\n"
|
||||
"prim_white=\n"
|
||||
"tf_power=xx\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, parsing prim_red: Needed exactly 2 numbers separated by whitespace, got 3.\n"
|
||||
"Config error in weston.ini [color-profile] name=mydisp, parsing prim_green: Needed exactly 2 numbers separated by whitespace, got 1.\n"
|
||||
"Config error in weston.ini [color-profile] name=mydisp, parsing prim_blue: '0,14' is not a number.\n"
|
||||
"Config error in weston.ini [color-profile] name=mydisp, parsing prim_white: Needed exactly 2 numbers separated by whitespace, got 0.\n"
|
||||
"Config error in weston.ini [color-profile] name=mydisp, parsing tf_power: 'xx' is not a number.\n",
|
||||
},
|
||||
{
|
||||
"tf_power=50\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" tf power exponent 50.000000 is not in the range [1.0, 10.0]\n"
|
||||
" primaries not set\n"
|
||||
" transfer function not set\n",
|
||||
},
|
||||
{
|
||||
"prim_red=Inf 0.33\n"
|
||||
"prim_green=0.21 7\n"
|
||||
"prim_blue=-1 NaN\n"
|
||||
"prim_white=0 -2\n"
|
||||
"tf_power=3\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" invalid primary color volume, the red primary CIE x value inf is out of range [-1.0, 2.0]\n"
|
||||
" invalid primary color volume, the green primary CIE y value 7.000000 is out of range [-1.0, 2.0]\n"
|
||||
" invalid primary color volume, the blue primary CIE y value nan is out of range [-1.0, 2.0]\n"
|
||||
" invalid primary color volume, the white point CIE y value -2.000000 is out of range [-1.0, 2.0]\n"
|
||||
" white point out of primary volume\n"
|
||||
},
|
||||
{
|
||||
"prim_named=bt2020\n"
|
||||
"tf_named=bt1886\n"
|
||||
"min_lum=10\n"
|
||||
"ref_lum=5\n"
|
||||
"max_lum=2\n"
|
||||
"target_min_lum=55\n"
|
||||
"target_max_lum=1\n"
|
||||
"max_fall=-7\n"
|
||||
"max_cll=0\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" reference luminance (5.000000) must be greater than primary minimum luminance (10.000000)\n"
|
||||
" primary minimum luminance (10.000000) must be less than primary maximum luminance (2.000000)\n"
|
||||
" target min luminance (55.000000) must be less than target max luminance (1.000000)\n"
|
||||
" maxCLL (0.000000) must be in the range (0.0, 1e+6]\n"
|
||||
" maxCLL (0.000000) should be greater than target min luminance (0.010000)\n"
|
||||
" maxFALL (-7.000000) must be in the range (0.0, 1e+6]\n"
|
||||
" maxFALL (-7.000000) must be greater than min luminance (0.010000)\n",
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Manufacture various weston.ini and check the error messages that
|
||||
* wet_create_output_color_profile() generates for invalid
|
||||
* color-profile sections.
|
||||
*/
|
||||
TEST_P(parametric_color_profile_parsing_errors, param_config_cases)
|
||||
{
|
||||
const struct parameters_testcase *t = data;
|
||||
struct compositor_setup setup;
|
||||
struct weston_config *wc;
|
||||
|
||||
compositor_setup_defaults(&setup);
|
||||
weston_ini_setup(&setup,
|
||||
cfgln("[color-profile]"),
|
||||
cfgln("name=mydisp"),
|
||||
cfgln("%s", t->profile_string));
|
||||
|
||||
wc = weston_config_parse(setup.config_file);
|
||||
test_assert_ptr_not_null(wc);
|
||||
free(setup.config_file);
|
||||
|
||||
test_creating_output_color_profile(wc, "mydisp",
|
||||
0xffffffff, 0xffffffff, 0xffffffff,
|
||||
t->expected_error);
|
||||
weston_config_destroy(wc);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
static const struct parameters_testcase param_unsupported_cases[] = {
|
||||
{
|
||||
"prim_named=ntsc\n"
|
||||
"tf_named=log100\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" primaries named NTSC (BT.601) not supported by the color manager\n"
|
||||
" logarithmic 100:1 not supported by the color manager\n"
|
||||
" primaries not set\n"
|
||||
" transfer function not set\n",
|
||||
},
|
||||
{
|
||||
"prim_named=srgb\n"
|
||||
"tf_power=2.3\n",
|
||||
"Config error in weston.ini [color-profile] name=mydisp, invalid parameter set:\n"
|
||||
" set_tf_power not supported by the color manager\n"
|
||||
" transfer function not set\n",
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Manufacture various weston.ini and check the error messages that
|
||||
* wet_create_output_color_profile() generates for valid
|
||||
* color-profile sections that use things the color manager does not
|
||||
* support.
|
||||
*/
|
||||
TEST_P(parametric_color_profile_parsing_unsupported, param_unsupported_cases)
|
||||
{
|
||||
const struct parameters_testcase *t = data;
|
||||
struct compositor_setup setup;
|
||||
struct weston_config *wc;
|
||||
|
||||
compositor_setup_defaults(&setup);
|
||||
weston_ini_setup(&setup,
|
||||
cfgln("[color-profile]"),
|
||||
cfgln("name=mydisp"),
|
||||
cfgln("%s", t->profile_string));
|
||||
|
||||
wc = weston_config_parse(setup.config_file);
|
||||
test_assert_ptr_not_null(wc);
|
||||
free(setup.config_file);
|
||||
|
||||
test_creating_output_color_profile(wc, "mydisp",
|
||||
0, (1u << WESTON_PRIMARIES_CICP_SRGB), 0,
|
||||
t->expected_error);
|
||||
weston_config_destroy(wc);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright 2022 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 <string.h>
|
||||
|
||||
#include "weston-test-client-helper.h"
|
||||
#include "weston-test-fixture-compositor.h"
|
||||
#include "weston-test-assert.h"
|
||||
#include "backend.h"
|
||||
#include "color.h"
|
||||
|
||||
static enum test_result_code
|
||||
fixture_setup(struct weston_test_harness *harness)
|
||||
{
|
||||
struct compositor_setup setup;
|
||||
|
||||
compositor_setup_defaults(&setup);
|
||||
setup.renderer = WESTON_RENDERER_GL;
|
||||
setup.shell = SHELL_TEST_DESKTOP;
|
||||
|
||||
weston_ini_setup(&setup,
|
||||
cfgln("[output]"),
|
||||
cfgln("name=headless"),
|
||||
cfgln("color_characteristics=my-awesome-color"),
|
||||
cfgln("colorimetry-mode=bt2020rgb"),
|
||||
cfgln("eotf-mode=st2084"),
|
||||
|
||||
cfgln("[color_characteristics]"),
|
||||
cfgln("name=my-awesome-color"),
|
||||
cfgln("maxFALL=1000"),
|
||||
cfgln("red_x=0.9999"),
|
||||
cfgln("red_y=0.3"),
|
||||
cfgln("blue_x=0.1"),
|
||||
cfgln("blue_y=0.11"),
|
||||
cfgln("green_x=0.1771"),
|
||||
cfgln("green_y=0.80001"),
|
||||
cfgln("white_x=0.313"),
|
||||
cfgln("white_y=0.323"),
|
||||
cfgln("min_L=0.0001"),
|
||||
cfgln("max_L=65535.0"),
|
||||
|
||||
cfgln("[core]"),
|
||||
cfgln("color-management=true"));
|
||||
|
||||
return weston_test_harness_execute_as_plugin(harness, &setup);
|
||||
}
|
||||
DECLARE_FIXTURE_SETUP(fixture_setup);
|
||||
|
||||
PLUGIN_TEST(color_characteristics_from_weston_ini)
|
||||
{
|
||||
struct weston_output *output = NULL;
|
||||
struct weston_output *it;
|
||||
enum weston_eotf_mode mode;
|
||||
enum weston_colorimetry_mode colorimetry_mode;
|
||||
const struct weston_color_characteristics *cc;
|
||||
const struct weston_hdr_metadata_type1 *hdr_meta;
|
||||
|
||||
wl_list_for_each(it, &compositor->output_list, link) {
|
||||
if (strcmp(it->name, "headless") == 0) {
|
||||
output = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
test_assert_ptr_not_null(output);
|
||||
|
||||
mode = weston_output_get_eotf_mode(output);
|
||||
test_assert_enum(mode, WESTON_EOTF_MODE_ST2084);
|
||||
|
||||
colorimetry_mode = weston_output_get_colorimetry_mode(output);
|
||||
test_assert_enum(colorimetry_mode, WESTON_COLORIMETRY_MODE_BT2020_RGB);
|
||||
|
||||
cc = weston_output_get_color_characteristics(output);
|
||||
test_assert_enum(cc->group_mask, WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK);
|
||||
test_assert_f32_eq(cc->primary[0].x, 0.9999f);
|
||||
test_assert_f32_eq(cc->primary[0].y, 0.3f);
|
||||
test_assert_f32_eq(cc->primary[1].x, 0.1771f);
|
||||
test_assert_f32_eq(cc->primary[1].y, 0.80001f);
|
||||
test_assert_f32_eq(cc->primary[2].x, 0.1f);
|
||||
test_assert_f32_eq(cc->primary[2].y, 0.11f);
|
||||
test_assert_f32_eq(cc->white.x, 0.313f);
|
||||
test_assert_f32_eq(cc->white.y, 0.323f);
|
||||
test_assert_f32_eq(cc->min_luminance, 0.0001f);
|
||||
test_assert_f32_eq(cc->max_luminance, 65535.0f);
|
||||
test_assert_f32_eq(cc->maxFALL, 1000.0f);
|
||||
|
||||
/* The below is color manager policy. */
|
||||
hdr_meta = weston_output_get_hdr_metadata_type1(output);
|
||||
test_assert_enum(hdr_meta->group_mask, WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK);
|
||||
test_assert_f32_eq(hdr_meta->primary[0].x, 0.9999f);
|
||||
test_assert_f32_eq(hdr_meta->primary[0].y, 0.3f);
|
||||
test_assert_f32_eq(hdr_meta->primary[1].x, 0.1771f);
|
||||
test_assert_f32_eq(hdr_meta->primary[1].y, 0.80001f);
|
||||
test_assert_f32_eq(hdr_meta->primary[2].x, 0.1f);
|
||||
test_assert_f32_eq(hdr_meta->primary[2].y, 0.11f);
|
||||
test_assert_f32_eq(hdr_meta->white.x, 0.313f);
|
||||
test_assert_f32_eq(hdr_meta->white.y, 0.323f);
|
||||
test_assert_f32_eq(hdr_meta->minDML, 0.0001f);
|
||||
test_assert_f32_eq(hdr_meta->maxDML, 65535.0f);
|
||||
test_assert_f32_eq(hdr_meta->maxCLL, 65535.0f);
|
||||
test_assert_f32_eq(hdr_meta->maxFALL, 1000.0f);
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
|
@ -131,10 +131,6 @@ tests = [
|
|||
'dep_objs': dep_libm,
|
||||
},
|
||||
{ 'name': 'color-effects', },
|
||||
{
|
||||
'name': 'color-metadata-errors',
|
||||
'dep_objs': dep_libexec_weston,
|
||||
},
|
||||
{
|
||||
'name': 'color-output-parsing',
|
||||
'dep_objs': [
|
||||
|
|
@ -311,7 +307,6 @@ if get_option('color-management-lcms')
|
|||
color_management_v1_protocol_c,
|
||||
],
|
||||
},
|
||||
{ 'name': 'color-metadata-parsing' },
|
||||
{
|
||||
'name': 'lcms-util',
|
||||
'dep_objs': [ dep_lcms_util ]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue