From fbb7b8307dc1e110cc8104b03c04cb9ed5d21d14 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 Jun 2025 11:17:23 +0300 Subject: [PATCH 01/12] color-lcms: move code into cmlcms_send_icc_info() Pure refactoring to clean up cmlcms_send_image_desc_info() ahead of implementing generic parametric information sending. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-profile.c | 45 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index c5751ba85..7e01621d9 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -719,6 +719,32 @@ 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) @@ -728,8 +754,6 @@ cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info, 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 @@ -738,22 +762,7 @@ cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info, */ 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); + return cmlcms_send_icc_info(cm_image_desc_info, cprof); } else { /* TODO: we still don't support parametric color profiles that * are not the stock one. This should change when we start From 7c4df878ebc6a07eb988a764c2bc791f11b119fc Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 Jun 2025 14:22:45 +0300 Subject: [PATCH 02/12] color: weston_cm_send_tf() handles power-law The power-law TF uses a different protocol event than others. Adding support for it requires passing in the parameters. Now we have a single send function that handles all protocol TFs, not just those without parameters. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-profile.c | 8 ++++--- libweston/color-management.c | 31 ++++++++++++++++++++++------ libweston/color-management.h | 4 ++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 7e01621d9..e742ebcdb 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -753,7 +753,6 @@ cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info, 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; /** * TODO: when we convert the stock sRGB profile to a parametric profile @@ -795,8 +794,11 @@ cmlcms_send_image_desc_info(struct cm_image_desc_info *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); + struct weston_color_tf tf = { + .info = weston_color_tf_info_from(compositor, WESTON_TF_GAMMA22), + .params = {}, + }; + weston_cm_send_tf(cm_image_desc_info, &tf); /* Primary luminance, default values from the protocol. */ weston_cm_send_luminances(cm_image_desc_info, 0.2, 80.0, 80.0); diff --git a/libweston/color-management.c b/libweston/color-management.c index 10cf7a305..79572cc1d 100644 --- a/libweston/color-management.c +++ b/libweston/color-management.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]); + } } /** diff --git a/libweston/color-management.h b/libweston/color-management.h index 5ae38198e..0086e1207 100644 --- a/libweston/color-management.h +++ b/libweston/color-management.h @@ -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, From 720f69c44d230a915d6674226cadd5c10aa953bc Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 Jun 2025 14:43:04 +0300 Subject: [PATCH 03/12] color: add weston_cm_send_parametric_info() Implementation of sending the protocol info events for an arbitrary parametric image description. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-profile.c | 11 ++++------ libweston/color-management.c | 31 ++++++++++++++++++++++++++++ libweston/color-management.h | 4 ++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index e742ebcdb..01c0d032e 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -763,13 +763,10 @@ cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info, if (cprof->type == CMLCMS_PROFILE_TYPE_ICC && cprof != cm->sRGB_profile) { return cmlcms_send_icc_info(cm_image_desc_info, cprof); } 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"); + if (cprof != cm->sRGB_profile) { + weston_cm_send_parametric_info(cm_image_desc_info, cprof->params); + return true; + } /* Stock sRGB color profile. TODO: when we add support for * parametric color profiles, the stock sRGB will be crafted diff --git a/libweston/color-management.c b/libweston/color-management.c index 79572cc1d..fbf59caee 100644 --- a/libweston/color-management.c +++ b/libweston/color-management.c @@ -280,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. */ diff --git a/libweston/color-management.h b/libweston/color-management.h index 0086e1207..d83ed39c4 100644 --- a/libweston/color-management.h +++ b/libweston/color-management.h @@ -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 */ From cca907c1d4b37673aa5f70a0fe6efa0bca1b7c9d Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 19 Jun 2025 13:28:46 +0300 Subject: [PATCH 04/12] color-lcms: refactor into get_defining_curve_segment() I need the new function for another purpose later, and this makes get_parametric_curveset_params() easier to read. Pure refactoring: no change in behavior. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-curve-segments.c | 91 ++++++++++++--------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/libweston/color-lcms/color-curve-segments.c b/libweston/color-lcms/color-curve-segments.c index 14f61dcd3..c879eebbf 100644 --- a/libweston/color-lcms/color-curve-segments.c +++ b/libweston/color-lcms/color-curve-segments.c @@ -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,47 @@ 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). + * + * Ensure that the domain is (-inf, inf). + */ + if (!are_segment_breaks_equal(seg0->x0, -INFINITY) || + !are_segment_breaks_equal(seg0->x1, INFINITY)) + return NULL; + *clamped_input = false; + return 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]. + */ + 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 +621,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 +637,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 From 35631907242d0c0a4a270c7cff20d6d5c10d9df1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 19 Jun 2025 13:35:50 +0300 Subject: [PATCH 05/12] color-lcms: recognize single bounded curve segment I don't have a specific use case in my mind for this, but it is something we can easily handle. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-curve-segments.c | 27 ++++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libweston/color-lcms/color-curve-segments.c b/libweston/color-lcms/color-curve-segments.c index c879eebbf..51aae583e 100644 --- a/libweston/color-lcms/color-curve-segments.c +++ b/libweston/color-lcms/color-curve-segments.c @@ -583,15 +583,24 @@ get_defining_curve_segment(cmsToneCurve *from, bool *clamped_input) seg1 = cmsGetToneCurveSegment(1, from); seg2 = cmsGetToneCurveSegment(2, from); if (seg0 && !seg1) { - /* Case 1: we have a single segment (seg0). - * - * Ensure that the domain is (-inf, inf). - */ - if (!are_segment_breaks_equal(seg0->x0, -INFINITY) || - !are_segment_breaks_equal(seg0->x1, INFINITY)) - return NULL; - *clamped_input = false; - return seg0; + /* 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. * From 55b0d471abde6ebcea6d2dbd68062037b0918f06 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 19 Jun 2025 14:15:41 +0300 Subject: [PATCH 06/12] color-lcms: optimize linear TRCs The icc<->parametric color transformation code uses "optical ICC profiles" as part of the ICC pipeline. These profiles use a linear TRC to encode the black point. When such TRC survives all optimizations, it will cause the 3D LUT fallback path to be taken. Detect such curve sets on the ICC pipeline optimizer, and convert them to matrix stages. The matrix stages will then be optimized as usual, often eliminating the stage completely. The results can be seen in color-icc-output test after the stock sRGB profile has been changed into a parametric one, causing all cases in the test to hit the parametric-to-icc path. Some tests fail when they suddenly start using the 3D LUT path which causes the errors to rise. This patch fixes those (future) cases, and the errors remain the same as before changing the stock profile. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-curve-segments.c | 55 +++++++++++++++++++++ libweston/color-lcms/color-curve-segments.h | 9 ++++ libweston/color-lcms/color-transform.c | 26 ++++++++++ 3 files changed, 90 insertions(+) diff --git a/libweston/color-lcms/color-curve-segments.c b/libweston/color-lcms/color-curve-segments.c index 51aae583e..95e30f6ed 100644 --- a/libweston/color-lcms/color-curve-segments.c +++ b/libweston/color-lcms/color-curve-segments.c @@ -676,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); +} diff --git a/libweston/color-lcms/color-curve-segments.h b/libweston/color-lcms/color-curve-segments.h index adb20dab4..b761a3264 100644 --- a/libweston/color-lcms/color-curve-segments.h +++ b/libweston/color-lcms/color-curve-segments.h @@ -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) { diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index 6f4ba3b8c..d3c20c048 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -387,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, @@ -1013,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 From 8e7f8125709bd91db40950ea9ceda702299652be Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 16 Jun 2025 15:49:56 +0300 Subject: [PATCH 07/12] color-lcms: switch default sRGB profile to parametric This avoids ICC paths when no ICC profiles are explicitly used. We can simplify the profile information sending and use the generic path for the stock profile as well, no longer hard-coding the stock profile in two places. Since the stock profile creation cannot fail, we can streamline cmlcms_init() a little, too. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 10 +-- libweston/color-lcms/color-lcms.h | 2 +- libweston/color-lcms/color-profile.c | 124 +++++++-------------------- 3 files changed, 32 insertions(+), 104 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 8317febfd..3229ba08d 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -429,19 +429,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); diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index 0c64c5f10..56fb4ad07 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -292,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 diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 01c0d032e..295b20bfa 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -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 * @@ -749,62 +728,17 @@ 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; - /** - * 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) { + switch (cprof->type) { + case CMLCMS_PROFILE_TYPE_ICC: return cmlcms_send_icc_info(cm_image_desc_info, cprof); - } else { - if (cprof != cm->sRGB_profile) { - weston_cm_send_parametric_info(cm_image_desc_info, cprof->params); - return true; - } - - /* 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. */ - struct weston_color_tf tf = { - .info = weston_color_tf_info_from(compositor, WESTON_TF_GAMMA22), - .params = {}, - }; - weston_cm_send_tf(cm_image_desc_info, &tf); - - /* 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); + 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 * From 318005c9bcb90954e5ad6ec55b6c83ba4a6ef368 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 9 Dec 2025 14:24:59 +0200 Subject: [PATCH 08/12] color-lcms: do not advertise saturation intent Weston does not support the saturation rendering intent for parametric image descriptions yet. Not really, Weston would just do the same as media-relative with BPC does. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 3 +-- tests/color-management-parametric-test.c | 2 +- tests/color-management-test.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 3229ba08d..0effb74f3 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -523,9 +523,8 @@ 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); diff --git a/tests/color-management-parametric-test.c b/tests/color-management-parametric-test.c index 9ad7f2384..f001bd60d 100644 --- a/tests/color-management-parametric-test.c +++ b/tests/color-management-parametric-test.c @@ -433,7 +433,7 @@ color_manager_get(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)); diff --git a/tests/color-management-test.c b/tests/color-management-test.c index f4bf46f09..fd18b32e3 100644 --- a/tests/color-management-test.c +++ b/tests/color-management-test.c @@ -73,7 +73,7 @@ color_manager_get(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)); From 5c87fe21b151db8255680f74ff3d877371e1f43f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 9 Dec 2025 14:30:03 +0200 Subject: [PATCH 09/12] color-lcms: adjust supported TF mask TF_SRGB will be deprecated, best to never advertise it. The test can simply use gamma22 instead. TF_EXT_LINEAR has an implementation and should be usable nowadays. TF_ST2084_PQ, GAMMA22 and GAMMA28 likewise. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 14 ++-------- tests/color-management-parametric-test.c | 34 ++++++++++++------------ 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 0effb74f3..aae6a2722 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -514,7 +514,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) | @@ -529,7 +528,6 @@ weston_color_manager_create(struct weston_compositor *compositor) (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) | @@ -541,18 +539,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); diff --git a/tests/color-management-parametric-test.c b/tests/color-management-parametric-test.c index f001bd60d..4e2317044 100644 --- a/tests/color-management-parametric-test.c +++ b/tests/color-management-parametric-test.c @@ -95,10 +95,10 @@ static const struct test_case test_cases[] = { /******** Successful 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, @@ -112,10 +112,10 @@ static const struct test_case 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, @@ -129,10 +129,10 @@ static const struct test_case 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, @@ -163,10 +163,10 @@ static const struct test_case 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, @@ -309,7 +309,7 @@ static const struct test_case 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, @@ -327,7 +327,7 @@ static const struct test_case 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, @@ -381,7 +381,7 @@ static const struct test_case 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, @@ -399,7 +399,7 @@ static const struct test_case 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, @@ -455,7 +455,7 @@ color_manager_get(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->init_done); @@ -736,10 +736,10 @@ TEST(set_tf_named_twice) image_desc_creator_param = color_manager_create_param(cm); 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); @@ -763,7 +763,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); @@ -784,7 +784,7 @@ TEST(set_tf_named_then_tf_power) image_desc_creator_param = color_manager_create_param(cm); 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); From 275df28f2af031a37e23f297074887c4c8424a16 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 26 Jan 2026 16:54:29 +0200 Subject: [PATCH 10/12] frontend: default color-profile to auto: Now that ICC<->parametric image description interoperability is implemented, and the stock sRGB profile is parametric, we can change the default to what it should be. Signed-off-by: Pekka Paalanen --- frontend/main.c | 8 +------- man/weston.ini.man | 7 +------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/frontend/main.c b/frontend/main.c index e8c215cd5..a4047c2a4 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -2110,13 +2110,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) diff --git a/man/weston.ini.man b/man/weston.ini.man index 2938b563b..f52e566dc 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -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: From b27043c96cdf79d64ffbf96bc0d6bc31bafae4fd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 30 Jan 2026 15:17:59 +0200 Subject: [PATCH 11/12] color-lcms: extract HDR static metadata from profile Extract the HDR static metadata type 1 from the output color profile directly, instead of relying on a separate weston.ini section to provide the metadata separately. Weston should tell the monitor what target color volume it is rendering for. I don't see a reason to be able to control the metadata separately, and it would add complexity. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 100 +++++++++++++--------------- tests/color-metadata-parsing-test.c | 17 ----- 2 files changed, 46 insertions(+), 71 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index aae6a2722..605699a31 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -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 */ diff --git a/tests/color-metadata-parsing-test.c b/tests/color-metadata-parsing-test.c index 94112e73e..2c0fcfe4b 100644 --- a/tests/color-metadata-parsing-test.c +++ b/tests/color-metadata-parsing-test.c @@ -77,7 +77,6 @@ PLUGIN_TEST(color_characteristics_from_weston_ini) 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) { @@ -108,21 +107,5 @@ PLUGIN_TEST(color_characteristics_from_weston_ini) 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; } From ece987a808f45d7968190fe3e96a9f66e65ac3ea Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 30 Jan 2026 15:33:01 +0200 Subject: [PATCH 12/12] frontend,libweston: remove color_characteristics Since the commit "color-lcms: extract HDR static metadata from profile" this was all dead code. Signed-off-by: Pekka Paalanen --- frontend/main.c | 157 ----- frontend/weston-private.h | 5 - include/libweston/colorimetry.h | 51 -- include/libweston/libweston.h | 8 - libweston/color.h | 2 +- libweston/compositor.c | 44 -- man/weston.ini.man | 70 -- tests/color-metadata-errors-test.c | 971 ---------------------------- tests/color-metadata-parsing-test.c | 111 ---- tests/meson.build | 5 - 10 files changed, 1 insertion(+), 1423 deletions(-) delete mode 100644 tests/color-metadata-errors-test.c delete mode 100644 tests/color-metadata-parsing-test.c diff --git a/frontend/main.c b/frontend/main.c index a4047c2a4..e450bf5b9 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -2248,156 +2248,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, ""); - 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) @@ -3201,10 +3051,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; @@ -4283,9 +4129,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); } diff --git a/frontend/weston-private.h b/frontend/weston-private.h index f82d66b79..f91daa26d 100644 --- a/frontend/weston-private.h +++ b/frontend/weston-private.h @@ -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, diff --git a/include/libweston/colorimetry.h b/include/libweston/colorimetry.h index 5bc5ccc32..357d733dd 100644 --- a/include/libweston/colorimetry.h +++ b/include/libweston/colorimetry.h @@ -293,57 +293,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); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index a35568b8d..98b8a01f6 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -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; @@ -2720,13 +2719,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, diff --git a/libweston/color.h b/libweston/color.h index 464aae333..e2afcfc47 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -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. * diff --git a/libweston/compositor.c b/libweston/compositor.c index b1bd4e8e7..cc34a72f5 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -8224,50 +8224,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) diff --git a/man/weston.ini.man b/man/weston.ini.man index f52e566dc..f07b4ecfc 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -784,13 +784,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. @@ -922,69 +915,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 diff --git a/tests/color-metadata-errors-test.c b/tests/color-metadata-errors-test.c deleted file mode 100644 index 7b91b78c3..000000000 --- a/tests/color-metadata-errors-test.c +++ /dev/null @@ -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 -#include - -#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; -} diff --git a/tests/color-metadata-parsing-test.c b/tests/color-metadata-parsing-test.c deleted file mode 100644 index 2c0fcfe4b..000000000 --- a/tests/color-metadata-parsing-test.c +++ /dev/null @@ -1,111 +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 - -#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; - - 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); - - return RESULT_OK; -} diff --git a/tests/meson.build b/tests/meson.build index d0037b38c..52e92354f 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -20,10 +20,6 @@ tests = [ 'dep_objs': dep_libm, }, { 'name': 'color-effects', }, - { - 'name': 'color-metadata-errors', - 'dep_objs': dep_libexec_weston, - }, { 'name': 'color-output-parsing', 'dep_objs': [ @@ -204,7 +200,6 @@ if get_option('color-management-lcms') color_management_v1_protocol_c, ], }, - { 'name': 'color-metadata-parsing' }, { 'name': 'lcms-util', 'dep_objs': [ dep_lcms_util ]