mirror of
https://gitlab.freedesktop.org/wayland/weston
synced 2024-11-02 19:00:26 +00:00
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>
This commit is contained in:
parent
98454720be
commit
4b9fa23ec6
3 changed files with 50 additions and 66 deletions
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -88,17 +88,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);
|
||||
|
|
Loading…
Reference in a new issue