diff options
author | Pekka Paalanen <pekka.paalanen@collabora.com> | 2024-02-28 14:53:59 +0200 |
---|---|---|
committer | Pekka Paalanen <pq@iki.fi> | 2024-03-07 14:50:47 +0000 |
commit | 4b9fa23ec6a3b2331fca5c88a3826ef9c8cd603a (patch) | |
tree | 1158ae1eeb84fdad2f871b69d61d08e3ce3cf61f | |
parent | 98454720be3abf03d55b6bf4f45115773f0f5630 (diff) |
color-lcms: stop hard-coding blend-to-output transformation
Stop special-casing the blend-to-output category, and pass it through
the same mechnisms and optimizations as all other transformations. In
the future, more curve types will be added to weston_color_transform,
meaning that blend-to-output does not always have to be a LUT. It could
become a parametric curve, which is more efficient and more precise to
compute, when VCGT does not exist.
Drop the special crafting of output_inv_eotf_vcgt LUT and replace it
with inv_eotf cms profile. inv_eotf will be combined with vcgt cms
profile as a chain as needed instead.
Blend-to-output transformations do not use a render intent, but we have
to tell cmsCreateMultiprofileTransformTHR() something, so arbitrarily
pick ICC-Absolute render intent for it.
Now all color transformations go through xform_realize_chain(), where
the documentation is improved.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
-rw-r--r-- | libweston/color-lcms/color-lcms.h | 12 | ||||
-rw-r--r-- | libweston/color-lcms/color-profile.c | 31 | ||||
-rw-r--r-- | libweston/color-lcms/color-transform.c | 73 |
3 files changed, 50 insertions, 66 deletions
diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index fe9e87f1..1726553c 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -84,16 +84,8 @@ struct cmlcms_output_profile_extract { */ struct lcmsProfilePtr eotf; - /** - * This field represents a concatenation of inverse EOTF + VCGT, - * if the tag exists and it can not be null. - * VCGT is part of monitor calibration which means: even though we must - * apply VCGT in the compositor, we pretend that it happens inside the - * monitor. This is how the classic color management and ICC profiles work. - * The ICC profile (ignoring the VCGT tag) characterizes the output which - * is VCGT + monitor behavior. - */ - cmsToneCurve *output_inv_eotf_vcgt[3]; + /** The inverse of above */ + struct lcmsProfilePtr inv_eotf; /** * VCGT tag cached from output profile, it could be null if not exist diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index ca4c3abb..0e36b6a3 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -192,6 +192,7 @@ ensure_output_profile_extract_icc(struct cmlcms_output_profile_extract *extract, cmsToneCurve *curve = NULL; cmsToneCurve **vcgt_curves; cmsToneCurve *eotf_curves[3] = {}; + cmsToneCurve *inv_eotf_curves[3] = {}; unsigned i; cmsTagSignature tags[] = { cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag @@ -240,8 +241,14 @@ ensure_output_profile_extract_icc(struct cmlcms_output_profile_extract *extract, *err_msg = "inverting EOTF failed"; goto fail; } - extract->output_inv_eotf_vcgt[i] = curve; + inv_eotf_curves[i] = curve; } + extract->inv_eotf.p = cmsCreateLinearizationDeviceLinkTHR(lcms_ctx, cmsSigRgbData, inv_eotf_curves); + if (!extract->inv_eotf.p) { + *err_msg = "out of memory"; + goto fail; + } + vcgt_curves = cmsReadTag(hProfile.p, cmsSigVcgtTag); if (vcgt_curves && vcgt_curves[0] && vcgt_curves[1] && vcgt_curves[2]) { extract->vcgt.p = cmsCreateLinearizationDeviceLinkTHR(lcms_ctx, cmsSigRgbData, vcgt_curves); @@ -249,20 +256,9 @@ ensure_output_profile_extract_icc(struct cmlcms_output_profile_extract *extract, *err_msg = "out of memory"; goto fail; } - - for (i = 0; i < 3; i++) { - curve = lcmsJoinToneCurve(lcms_ctx, - extract->output_inv_eotf_vcgt[i], - vcgt_curves[i], num_points); - if (!curve) { - *err_msg = "joining curves failed"; - goto fail; - } - cmsFreeToneCurve(extract->output_inv_eotf_vcgt[i]); - extract->output_inv_eotf_vcgt[i] = curve; - } } + cmsFreeToneCurveTriple(inv_eotf_curves); cmsFreeToneCurveTriple(eotf_curves); return true; @@ -271,10 +267,15 @@ fail: cmsCloseProfile(extract->vcgt.p); extract->vcgt.p = NULL; + cmsCloseProfile(extract->inv_eotf.p); + extract->inv_eotf.p = NULL; + cmsCloseProfile(extract->eotf.p); extract->eotf.p = NULL; + + cmsFreeToneCurveTriple(inv_eotf_curves); cmsFreeToneCurveTriple(eotf_curves); - cmsFreeToneCurveTriple(extract->output_inv_eotf_vcgt); + return false; } @@ -391,8 +392,8 @@ cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof) wl_list_remove(&cprof->link); cmsCloseProfile(cprof->extract.vcgt.p); + cmsCloseProfile(cprof->extract.inv_eotf.p); cmsCloseProfile(cprof->extract.eotf.p); - cmsFreeToneCurveTriple(cprof->extract.output_inv_eotf_vcgt); cmsCloseProfile(cprof->profile.p); /* Only profiles created from ICC files have these. */ diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index 4ade4932..8bd5ea44 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -89,17 +89,6 @@ fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len) } static void -cmlcms_fill_in_output_inv_eotf_vcgt(struct weston_color_transform *xform_base, - float *values, unsigned len) -{ - struct cmlcms_color_transform *xform = to_cmlcms_xform(xform_base); - struct cmlcms_color_profile *p = xform->search_key.output_profile; - - assert(p && "output_profile"); - fill_in_curves(p->extract.output_inv_eotf_vcgt, values, len); -} - -static void cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base, float *values, unsigned len) { @@ -868,31 +857,50 @@ xform_realize_chain(struct cmlcms_color_transform *xform) { struct weston_color_manager_lcms *cm = to_cmlcms(xform->base.cm); struct cmlcms_color_profile *output_profile = xform->search_key.output_profile; + const struct weston_render_intent_info *render_intent; struct lcmsProfilePtr chain[5]; unsigned chain_len = 0; struct lcmsProfilePtr extra = { NULL }; cmsUInt32Number dwFlags; - chain[chain_len++] = xform->search_key.input_profile->profile; - chain[chain_len++] = output_profile->profile; + render_intent = xform->search_key.render_intent; + + /* + * Our blending space is chosen to be the optical output color space. + * From input space, we always go to electrical output space, then + * come to optical space for blending, and finally go back to + * electrical output space. Before the image is sent to display, + * we must also apply VCGT if given, since nothing else would do that. + * + * INPUT_TO_BLEND + BLEND_TO_OUTPUT = INPUT_TO_OUTPUT + */ switch (xform->search_key.category) { case CMLCMS_CATEGORY_INPUT_TO_BLEND: - /* Add linearization step to make blending well-defined. */ + chain[chain_len++] = xform->search_key.input_profile->profile; + chain[chain_len++] = output_profile->profile; chain[chain_len++] = output_profile->extract.eotf; break; + case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: + chain[chain_len++] = output_profile->extract.inv_eotf; + if (output_profile->extract.vcgt.p) + chain[chain_len++] = output_profile->extract.vcgt; + + /* Render intent does not apply here, but need to set something. */ + weston_assert_ptr_is_null(cm->base.compositor, render_intent); + render_intent = weston_render_intent_info_from(cm->base.compositor, + WESTON_RENDER_INTENT_ABSOLUTE); + break; case CMLCMS_CATEGORY_INPUT_TO_OUTPUT: - /* Just add VCGT if it is provided. */ + chain[chain_len++] = xform->search_key.input_profile->profile; + chain[chain_len++] = output_profile->profile; if (output_profile->extract.vcgt.p) chain[chain_len++] = output_profile->extract.vcgt; break; - case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: - assert(0 && "category handled in the caller"); - return false; } assert(chain_len <= ARRAY_LENGTH(chain)); - weston_assert_ptr(cm->base.compositor, xform->search_key.render_intent); + weston_assert_ptr(cm->base.compositor, render_intent); /** * Binding to our LittleCMS plug-in occurs here. @@ -905,13 +913,13 @@ xform_realize_chain(struct cmlcms_color_transform *xform) assert(xform->status == CMLCMS_TRANSFORM_FAILED); /* transform_factory() is invoked by this call. */ - dwFlags = xform->search_key.render_intent->bps ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0; + dwFlags = render_intent->bps ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0; xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(xform->lcms_ctx, from_lcmsProfilePtr_array(chain), chain_len, TYPE_RGB_FLT, TYPE_RGB_FLT, - xform->search_key.render_intent->lcms_intent, + render_intent->lcms_intent, dwFlags); cmsCloseProfile(extra.p); @@ -996,26 +1004,9 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm, cmlcms_reasonable_1D_points(), &err_msg)) goto error; - /* - * The blending space is chosen to be the output device space but - * linearized. This means that BLEND_TO_OUTPUT only needs to - * undo the linearization and add VCGT. - */ - switch (search_param->category) { - case CMLCMS_CATEGORY_INPUT_TO_BLEND: - case CMLCMS_CATEGORY_INPUT_TO_OUTPUT: - if (!xform_realize_chain(xform)) { - err_msg = "xform_realize_chain failed"; - goto error; - } - break; - case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: - xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D; - xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_output_inv_eotf_vcgt; - xform->base.pre_curve.u.lut_3x1d.optimal_len = - cmlcms_reasonable_1D_points(); - xform->status = CMLCMS_TRANSFORM_OPTIMIZED; - break; + if (!xform_realize_chain(xform)) { + err_msg = "xform_realize_chain failed"; + goto error; } wl_list_insert(&cm->color_transform_list, &xform->link); |