Skip to content
Commits on Source (13)
......@@ -53,26 +53,16 @@ cmlcms_category_name(enum cmlcms_category cat)
}
static const struct weston_render_intent_info *
cmlcms_get_render_intent(enum cmlcms_category cat,
struct weston_surface *surface,
struct weston_output *output)
render_intent_from_surface_or_default(struct weston_color_manager_lcms *cm,
struct weston_surface *surface)
{
struct weston_color_manager *cm = output->compositor->color_manager;
/* TODO: take into account the cmlcms_category. */
if (surface && surface->render_intent)
return surface->render_intent;
/* Use default render intent. TODO: default should be
* WESTON_RENDER_INTENT_PERCEPTUAL. That requires tweaking the tests. */
if (!surface || !surface->render_intent) {
weston_assert_true(output->compositor, (cm->supported_rendering_intents >>
WESTON_RENDER_INTENT_RELATIVE) & 1);
return weston_render_intent_info_from(output->compositor,
WESTON_RENDER_INTENT_RELATIVE);
}
weston_assert_ptr(surface->compositor, surface->color_profile);
return surface->render_intent;
return weston_render_intent_info_from(cm->base.compositor,
WESTON_RENDER_INTENT_RELATIVE);
}
static struct cmlcms_color_profile *
......@@ -101,16 +91,12 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
{
struct weston_color_manager_lcms *cm = to_cmlcms(cm_base);
struct cmlcms_color_transform *xform;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
.input_profile = to_cprof_or_stock_sRGB(cm, surface->color_profile),
.output_profile = to_cprof_or_stock_sRGB(cm, output->color_profile),
.render_intent = render_intent_from_surface_or_default(cm, surface),
};
param.render_intent = cmlcms_get_render_intent(param.category,
surface, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
......@@ -118,9 +104,12 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
surf_xform->transform = &xform->base;
/*
* When we introduce LCMS plug-in we can precisely answer this question
* by examining the color pipeline using precision parameters. For now
* we just compare if it is same pointer or not.
* TODO: Instead of this, we should create the INPUT_TO_OUTPUT color
* transformation and check if that is identity. Comparing just the
* profiles will miss image adjustments if we add some.
* OTOH, that will only be useful if DRM-backend learns to do
* opportunistic direct scanout without KMS blending space
* transformations.
*/
if (xform->search_key.input_profile == xform->search_key.output_profile)
surf_xform->identity_pipeline = true;
......@@ -136,16 +125,12 @@ cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
struct weston_color_transform **xform_out)
{
struct cmlcms_color_transform *xform;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
.input_profile = NULL,
.output_profile = to_cprof_or_stock_sRGB(cm, output->color_profile),
.render_intent = NULL,
};
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
......@@ -161,16 +146,12 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm,
struct weston_color_transform **xform_out)
{
struct cmlcms_color_transform *xform;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
.input_profile = cm->sRGB_profile,
.output_profile = to_cprof_or_stock_sRGB(cm, output->color_profile),
.render_intent = render_intent_from_surface_or_default(cm, NULL),
};
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
/*
* Create a color transformation when output profile is not stock
......@@ -194,16 +175,12 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
struct weston_color_transform **xform_out)
{
struct cmlcms_color_transform *xform;
/* TODO: take weston_output::eotf_mode into account */
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
.input_profile = cm->sRGB_profile,
.output_profile = to_cprof_or_stock_sRGB(cm, output->color_profile),
.render_intent = render_intent_from_surface_or_default(cm, NULL),
};
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
if (!xform)
......
......@@ -35,6 +35,25 @@
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
/*
* Because cmsHPROFILE is a typedef of void*, it happily and implicitly
* casts to and from any pointer at all. In order to bring some type
* safety, wrap it.
*/
struct lcmsProfilePtr {
cmsHPROFILE p;
};
/* Cast an array of lcmsProfilePtr into array of cmsHPROFILE */
static inline cmsHPROFILE *
from_lcmsProfilePtr_array(struct lcmsProfilePtr *arr)
{
static_assert(sizeof(struct lcmsProfilePtr) == sizeof(cmsHPROFILE),
"arrays of cmsHPROFILE wrapper are castable");
return &arr[0].p;
}
struct weston_color_manager_lcms {
struct weston_color_manager base;
struct weston_log_scope *profiles_scope;
......@@ -57,45 +76,37 @@ struct cmlcms_md5_sum {
uint8_t bytes[16];
};
struct cmlcms_output_profile_extract {
/** The curves to decode an electrical signal
*
* For ICC profiles, if the profile type is matrix-shaper, then eotf
* contains the TRC, otherwise eotf contains an approximated EOTF.
*/
struct lcmsProfilePtr eotf;
/** The inverse of above */
struct lcmsProfilePtr inv_eotf;
/**
* VCGT tag cached from output profile, it could be null if not exist
*/
struct lcmsProfilePtr vcgt;
};
struct cmlcms_color_profile {
struct weston_color_profile base;
/* struct weston_color_manager_lcms::color_profile_list */
struct wl_list link;
cmsHPROFILE profile;
struct lcmsProfilePtr profile;
struct cmlcms_md5_sum md5sum;
/* Only for profiles created from an ICC file. */
struct ro_anonymous_file *prof_rofile;
/** The curves to decode an electrical signal
*
* For ICC profiles, if the profile type is matrix-shaper, then eotf
* contains the TRC, otherwise eotf contains an approximated EOTF if the
* profile is used for output.
* The field may be populated on demand.
*/
cmsToneCurve *eotf[3];
/**
* If the profile does support being an output profile and it is used as an
* output then 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. The field is null only if the profile is not
* usable as an output profile. The field is set when cmlcms_color_profile
* is created.
*/
cmsToneCurve *output_inv_eotf_vcgt[3];
/**
* VCGT tag cached from output profile, it could be null if not exist
*/
cmsToneCurve *vcgt[3];
/* Populated only when profile used as output profile */
struct cmlcms_output_profile_extract extract;
};
/**
......@@ -239,12 +250,10 @@ char *
cmlcms_color_profile_print(const struct cmlcms_color_profile *cprof);
bool
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
cmsHPROFILE hProfile,
cmsToneCurve *output_eotf[3],
cmsToneCurve *output_inv_eotf_vcgt[3],
cmsToneCurve *vcgt[3],
unsigned int num_points);
ensure_output_profile_extract(struct cmlcms_color_profile *cprof,
cmsContext lcms_ctx,
unsigned int num_points,
const char **err_msg);
unsigned int
cmlcms_reasonable_1D_points(void);
......
......@@ -56,14 +56,14 @@ xyz_dot_prod(const struct xyz_arr_flt a, const struct xyz_arr_flt b)
*/
static bool
build_eotf_from_clut_profile(cmsContext lcms_ctx,
cmsHPROFILE profile,
struct lcmsProfilePtr profile,
cmsToneCurve *output_eotf[3],
int num_points)
{
int ch, point;
float *curve_array[3];
float *red = NULL;
cmsHPROFILE xyz_profile = NULL;
struct lcmsProfilePtr xyz_profile = { NULL };
cmsHTRANSFORM transform_rgb_to_xyz = NULL;
bool ret = false;
const float div = num_points - 1;
......@@ -76,12 +76,12 @@ build_eotf_from_clut_profile(cmsContext lcms_ctx,
curve_array[1] = red + num_points;
curve_array[2] = red + 2 * num_points;
xyz_profile = cmsCreateXYZProfileTHR(lcms_ctx);
if (!xyz_profile)
xyz_profile.p = cmsCreateXYZProfileTHR(lcms_ctx);
if (!xyz_profile.p)
goto release;
transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile,
TYPE_RGB_FLT, xyz_profile,
transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile.p,
TYPE_RGB_FLT, xyz_profile.p,
TYPE_XYZ_FLT,
INTENT_ABSOLUTE_COLORIMETRIC,
0);
......@@ -136,8 +136,8 @@ build_eotf_from_clut_profile(cmsContext lcms_ctx,
release:
if (transform_rgb_to_xyz)
cmsDeleteTransform(transform_rgb_to_xyz);
if (xyz_profile)
cmsCloseProfile(xyz_profile);
if (xyz_profile.p)
cmsCloseProfile(xyz_profile.p);
free(red);
if (ret == false)
cmsFreeToneCurveTriple(output_eotf);
......@@ -182,86 +182,131 @@ error:
* then invert and concatenate with 'vcgt' curve if it
* is available.
*/
bool
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
cmsHPROFILE hProfile,
cmsToneCurve *output_eotf[3],
cmsToneCurve *output_inv_eotf_vcgt[3],
cmsToneCurve *vcgt[3],
unsigned int num_points)
static bool
ensure_output_profile_extract_icc(struct cmlcms_output_profile_extract *extract,
cmsContext lcms_ctx,
struct lcmsProfilePtr hProfile,
unsigned int num_points,
const char **err_msg)
{
cmsToneCurve *curve = NULL;
const cmsToneCurve * const *vcgt_curves;
cmsToneCurve **vcgt_curves;
cmsToneCurve *eotf_curves[3] = {};
cmsToneCurve *inv_eotf_curves[3] = {};
unsigned i;
cmsTagSignature tags[] = {
cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag
};
if (cmsIsMatrixShaper(hProfile)) {
if (cmsIsMatrixShaper(hProfile.p)) {
/**
* Optimization for matrix-shaper profile
* May have 1DLUT->3x3->3x3->1DLUT, 1DLUT->3x3->1DLUT
* Matrix-shaper profiles contain TRC and MatrixColumn tags.
* Assumes that AToB or DToB tags do not exist or are
* equivalent to TRC + MatrixColumn.
* We can take the TRC curves straight as EOTF.
*/
for (i = 0 ; i < 3; i++) {
curve = cmsReadTag(hProfile, tags[i]);
if (!curve)
curve = cmsReadTag(hProfile.p, tags[i]);
if (!curve) {
*err_msg = "TRC tag missing from matrix-shaper ICC profile";
goto fail;
output_eotf[i] = cmsDupToneCurve(curve);
if (!output_eotf[i])
}
eotf_curves[i] = cmsDupToneCurve(curve);
if (!eotf_curves[i]) {
*err_msg = "out of memory";
goto fail;
}
}
} else {
/**
* Linearization of cLUT profile may have 1DLUT->3DLUT->1DLUT,
* 1DLUT->3DLUT, 3DLUT
* Any other kind of profile goes through approximate
* linearization that produces sampled curves.
*/
if (!build_eotf_from_clut_profile(lcms_ctx, hProfile,
output_eotf, num_points))
eotf_curves, num_points)) {
*err_msg = "estimating EOTF failed";
goto fail;
}
}
extract->eotf.p = cmsCreateLinearizationDeviceLinkTHR(lcms_ctx, cmsSigRgbData, eotf_curves);
if (!extract->eotf.p) {
*err_msg = "out of memory";
goto fail;
}
/**
* If the caller looking for eotf only then return early.
* It could be used for input profile when identity case: EOTF + INV_EOTF
* in pipeline only.
*/
if (output_inv_eotf_vcgt == NULL)
return true;
for (i = 0; i < 3; i++) {
curve = cmsReverseToneCurve(output_eotf[i]);
if (!curve)
curve = cmsReverseToneCurve(eotf_curves[i]);
if (!curve) {
*err_msg = "inverting EOTF failed";
goto fail;
output_inv_eotf_vcgt[i] = curve;
}
inv_eotf_curves[i] = curve;
}
vcgt_curves = cmsReadTag(hProfile, cmsSigVcgtTag);
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]) {
for (i = 0; i < 3; i++) {
curve = lcmsJoinToneCurve(lcms_ctx,
output_inv_eotf_vcgt[i],
vcgt_curves[i], num_points);
if (!curve)
goto fail;
cmsFreeToneCurve(output_inv_eotf_vcgt[i]);
output_inv_eotf_vcgt[i] = curve;
if (vcgt)
vcgt[i] = cmsDupToneCurve(vcgt_curves[i]);
extract->vcgt.p = cmsCreateLinearizationDeviceLinkTHR(lcms_ctx, cmsSigRgbData, vcgt_curves);
if (!extract->vcgt.p) {
*err_msg = "out of memory";
goto fail;
}
}
cmsFreeToneCurveTriple(inv_eotf_curves);
cmsFreeToneCurveTriple(eotf_curves);
return true;
fail:
cmsFreeToneCurveTriple(output_eotf);
cmsFreeToneCurveTriple(output_inv_eotf_vcgt);
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);
return false;
}
bool
ensure_output_profile_extract(struct cmlcms_color_profile *cprof,
cmsContext lcms_ctx,
unsigned int num_points,
const char **err_msg)
{
bool ret;
/* Everything already computed */
if (cprof->extract.eotf.p)
return true;
ret = ensure_output_profile_extract_icc(&cprof->extract, lcms_ctx,
cprof->profile, num_points, err_msg);
if (ret)
weston_assert_ptr(cprof->base.cm->compositor, cprof->extract.eotf.p);
return ret;
}
/* FIXME: sync with spec! */
static bool
validate_icc_profile(cmsHPROFILE profile, char **errmsg)
validate_icc_profile(struct lcmsProfilePtr profile, char **errmsg)
{
cmsColorSpaceSignature cs = cmsGetColorSpace(profile);
cmsColorSpaceSignature cs = cmsGetColorSpace(profile.p);
uint32_t nr_channels = cmsChannelsOf(cs);
uint8_t version = cmsGetEncodedICCversion(profile) >> 24;
uint8_t version = cmsGetEncodedICCversion(profile.p) >> 24;
if (version != 2 && version != 4) {
str_printf(errmsg,
......@@ -277,7 +322,7 @@ validate_icc_profile(cmsHPROFILE profile, char **errmsg)
return false;
}
if (cmsGetDeviceClass(profile) != cmsSigDisplayClass) {
if (cmsGetDeviceClass(profile.p) != cmsSigDisplayClass) {
str_printf(errmsg, "ICC profile is required to be of Display device class, but it is not.");
return false;
}
......@@ -313,7 +358,7 @@ cmlcms_color_profile_print(const struct cmlcms_color_profile *cprof)
static struct cmlcms_color_profile *
cmlcms_color_profile_create(struct weston_color_manager_lcms *cm,
cmsHPROFILE profile,
struct lcmsProfilePtr profile,
char *desc,
char **errmsg)
{
......@@ -327,7 +372,7 @@ cmlcms_color_profile_create(struct weston_color_manager_lcms *cm,
weston_color_profile_init(&cprof->base, &cm->base);
cprof->base.description = desc;
cprof->profile = profile;
cmsGetHeaderProfileID(profile, cprof->md5sum.bytes);
cmsGetHeaderProfileID(profile.p, cprof->md5sum.bytes);
wl_list_insert(&cm->color_profile_list, &cprof->link);
weston_log_scope_printf(cm->profiles_scope,
......@@ -346,10 +391,10 @@ cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof)
struct weston_color_manager_lcms *cm = to_cmlcms(cprof->base.cm);
wl_list_remove(&cprof->link);
cmsFreeToneCurveTriple(cprof->vcgt);
cmsFreeToneCurveTriple(cprof->eotf);
cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt);
cmsCloseProfile(cprof->profile);
cmsCloseProfile(cprof->extract.vcgt.p);
cmsCloseProfile(cprof->extract.inv_eotf.p);
cmsCloseProfile(cprof->extract.eotf.p);
cmsCloseProfile(cprof->profile.p);
/* Only profiles created from ICC files have these. */
if (cprof->prof_rofile)
......@@ -382,7 +427,7 @@ unref_cprof(struct cmlcms_color_profile *cprof)
}
static char *
make_icc_file_description(cmsHPROFILE profile,
make_icc_file_description(struct lcmsProfilePtr profile,
const struct cmlcms_md5_sum *md5sum,
const char *name_part)
{
......@@ -395,7 +440,7 @@ make_icc_file_description(cmsHPROFILE profile,
"%02x", md5sum->bytes[i]);
}
str_printf(&desc, "ICCv%.1f %s %s", cmsGetProfileVersion(profile),
str_printf(&desc, "ICCv%.1f %s %s", cmsGetProfileVersion(profile.p),
name_part, md5sum_str);
return desc;
......@@ -408,21 +453,22 @@ make_icc_file_description(cmsHPROFILE profile,
bool
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
{
cmsHPROFILE profile;
struct lcmsProfilePtr profile;
struct cmlcms_md5_sum md5sum;
char *desc = NULL;
const char *err_msg = NULL;
profile = cmsCreate_sRGBProfileTHR(cm->lcms_ctx);
if (!profile) {
profile.p = cmsCreate_sRGBProfileTHR(cm->lcms_ctx);
if (!profile.p) {
weston_log("color-lcms: error: cmsCreate_sRGBProfileTHR failed\n");
return false;
}
if (!cmsMD5computeID(profile)) {
if (!cmsMD5computeID(profile.p)) {
weston_log("Failed to compute MD5 for ICC profile\n");
goto err_close;
}
cmsGetHeaderProfileID(profile, md5sum.bytes);
cmsGetHeaderProfileID(profile.p, md5sum.bytes);
desc = make_icc_file_description(profile, &md5sum, "sRGB stock");
if (!desc)
goto err_close;
......@@ -431,19 +477,18 @@ cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
if (!cm->sRGB_profile)
goto err_close;
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
cm->sRGB_profile->profile,
cm->sRGB_profile->eotf,
cm->sRGB_profile->output_inv_eotf_vcgt,
cm->sRGB_profile->vcgt,
cmlcms_reasonable_1D_points()))
if (!ensure_output_profile_extract(cm->sRGB_profile, cm->lcms_ctx,
cmlcms_reasonable_1D_points(), &err_msg))
goto err_close;
return true;
err_close:
if (err_msg)
weston_log("%s\n", err_msg);
free(desc);
cmsCloseProfile(profile);
cmsCloseProfile(profile.p);
return false;
}
......@@ -467,7 +512,7 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
char **errmsg)
{
struct weston_color_manager_lcms *cm = to_cmlcms(cm_base);
cmsHPROFILE profile;
struct lcmsProfilePtr profile;
struct cmlcms_md5_sum md5sum;
struct cmlcms_color_profile *cprof = NULL;
char *desc = NULL;
......@@ -481,8 +526,8 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
return false;
}
profile = cmsOpenProfileFromMemTHR(cm->lcms_ctx, icc_data, icc_len);
if (!profile) {
profile.p = cmsOpenProfileFromMemTHR(cm->lcms_ctx, icc_data, icc_len);
if (!profile.p) {
str_printf(errmsg, "ICC data not understood.");
return false;
}
......@@ -490,16 +535,16 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
if (!validate_icc_profile(profile, errmsg))
goto err_close;
if (!cmsMD5computeID(profile)) {
if (!cmsMD5computeID(profile.p)) {
str_printf(errmsg, "Failed to compute MD5 for ICC profile.");
goto err_close;
}
cmsGetHeaderProfileID(profile, md5sum.bytes);
cmsGetHeaderProfileID(profile.p, md5sum.bytes);
cprof = cmlcms_find_color_profile_by_md5(cm, &md5sum);
if (cprof) {
*cprof_out = weston_color_profile_ref(&cprof->base);
cmsCloseProfile(profile);
cmsCloseProfile(profile.p);
return true;
}
......@@ -522,7 +567,7 @@ err_close:
if (cprof)
cmlcms_color_profile_destroy(cprof);
free(desc);
cmsCloseProfile(profile);
cmsCloseProfile(profile.p);
return false;
}
......
......@@ -36,6 +36,7 @@
#include "color-properties.h"
#include "shared/helpers.h"
#include "shared/string-helpers.h"
#include "shared/weston-assert.h"
#include "shared/xalloc.h"
/**
......@@ -87,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->output_inv_eotf_vcgt, values, len);
}
static void
cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base,
float *values, unsigned len)
......@@ -142,9 +132,6 @@ cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base,
unsigned int value_b, value_r, value_g;
float divider = len - 1;
assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND ||
xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT);
for (value_b = 0; value_b < len; value_b++) {
for (value_g = 0; value_g < len; value_g++) {
for (value_r = 0; value_r < len; value_r++) {
......@@ -862,54 +849,55 @@ lcms_xform_error_logger(cmsContext context_id,
text);
}
static cmsHPROFILE
profile_from_rgb_curves(cmsContext ctx, cmsToneCurve *const curveset[3])
{
cmsHPROFILE p;
int i;
for (i = 0; i < 3; i++)
assert(curveset[i]);
p = cmsCreateLinearizationDeviceLinkTHR(ctx, cmsSigRgbData, curveset);
abort_oom_if_null(p);
return p;
}
static bool
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;
cmsHPROFILE chain[5];
const struct weston_render_intent_info *render_intent;
struct lcmsProfilePtr chain[5];
unsigned chain_len = 0;
cmsHPROFILE extra = NULL;
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. */
extra = profile_from_rgb_curves(cm->lcms_ctx, output_profile->eotf);
chain[chain_len++] = extra;
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. */
if (output_profile->vcgt[0]) {
extra = profile_from_rgb_curves(cm->lcms_ctx,
output_profile->vcgt);
chain[chain_len++] = extra;
}
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, render_intent);
/**
* Binding to our LittleCMS plug-in occurs here.
......@@ -922,15 +910,15 @@ 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,
chain,
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);
cmsCloseProfile(extra.p);
if (!xform->cmap_3dlut)
goto failed;
......@@ -944,7 +932,14 @@ xform_realize_chain(struct cmlcms_color_transform *xform)
case CMLCMS_TRANSFORM_FAILED:
goto failed;
case CMLCMS_TRANSFORM_OPTIMIZED:
break;
case CMLCMS_TRANSFORM_3DLUT:
/*
* Given the chain formed above, blend-to-output should never
* fall back to 3D LUT.
*/
weston_assert_uint32_neq(cm->base.compositor, xform->search_key.category,
CMLCMS_CATEGORY_BLEND_TO_OUTPUT);
break;
}
......@@ -960,7 +955,9 @@ failed:
char *
cmlcms_color_transform_search_param_string(const struct cmlcms_color_transform_search_param *search_key)
{
const char *input_prof_desc = "none", *output_prof_desc = "none";
const char *input_prof_desc = "none";
const char *output_prof_desc = "none";
const char *intent_desc = "none";
char *str;
if (search_key->input_profile)
......@@ -969,14 +966,17 @@ cmlcms_color_transform_search_param_string(const struct cmlcms_color_transform_s
if (search_key->output_profile)
output_prof_desc = search_key->output_profile->base.description;
str_printf(&str, " catergory: %s\n" \
if (search_key->render_intent)
intent_desc = search_key->render_intent->desc;
str_printf(&str, " category: %s\n" \
" input profile: %s\n" \
" output profile: %s\n" \
" selected intent from output profile: %s\n",
" render intent: %s\n",
cmlcms_category_name(search_key->category),
input_prof_desc,
output_prof_desc,
search_key->render_intent->desc);
intent_desc);
abort_oom_if_null(str);
......@@ -988,7 +988,7 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
const struct cmlcms_color_transform_search_param *search_param)
{
struct cmlcms_color_transform *xform;
const char *err_msg;
const char *err_msg = NULL;
char *str;
xform = xzalloc(sizeof *xform);
......@@ -1004,39 +1004,13 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
weston_log_scope_printf(cm->transforms_scope, "%s", str);
free(str);
/* Ensure the linearization etc. have been extracted. */
if (!search_param->output_profile->eotf[0]) {
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
search_param->output_profile->profile,
search_param->output_profile->eotf,
search_param->output_profile->output_inv_eotf_vcgt,
search_param->output_profile->vcgt,
cmlcms_reasonable_1D_points())) {
err_msg = "retrieve_eotf_and_output_inv_eotf failed";
goto error;
}
}
if (!ensure_output_profile_extract(search_param->output_profile, cm->lcms_ctx,
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);
......