summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/lcms_util.c131
1 files changed, 119 insertions, 12 deletions
diff --git a/tests/lcms_util.c b/tests/lcms_util.c
index 3fcd5712..eef32e61 100644
--- a/tests/lcms_util.c
+++ b/tests/lcms_util.c
@@ -1,7 +1,7 @@
/*
* Copyright 2021 Advanced Micro Devices, Inc.
* Copyright 2022 Collabora, Ltd.
- * Copyright (c) 1998-2022 Marti Maria Saguer
+ * Copyright (c) 1998-2023 Marti Maria Saguer
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -281,23 +281,98 @@ roundtrip_verification(cmsPipeline *DToB, cmsPipeline *BToD, float tolerance)
assert(stat.two_norm.max < tolerance);
}
+static const struct weston_vector ZEROS = {
+ .f = { 0.0, 0.0, 0.0, 1.0 }
+};
+static const struct weston_vector PCS_BLACK = {
+ .f = {
+ cmsPERCEPTUAL_BLACK_X,
+ cmsPERCEPTUAL_BLACK_Y,
+ cmsPERCEPTUAL_BLACK_Z,
+ 1.0
+ }
+};
+
+/* Whether BPC matrix applies never, after or before transformation */
+enum bpc_dir {
+ BPC_DIR_NONE,
+ BPC_DIR_DTOB,
+ BPC_DIR_BTOD,
+};
+
struct transform_sampler_context {
cmsHTRANSFORM t;
+ struct weston_matrix bpc;
+ enum bpc_dir dir;
};
static cmsInt32Number
transform_sampler(const float src[], float dst[], void *cargo)
{
const struct transform_sampler_context *tsc = cargo;
+ struct weston_vector stmp = { .f = { src[0], src[1], src[2], 1.0 } };
+ struct weston_vector dtmp = { .f = { 0.0, 0.0, 0.0, 1.0 } };
+
+ if (tsc->dir == BPC_DIR_BTOD)
+ weston_matrix_transform(&tsc->bpc, &stmp);
+
+ cmsDoTransform(tsc->t, stmp.f, dtmp.f, 1);
- cmsDoTransform(tsc->t, src, dst, 1);
+ if (tsc->dir == BPC_DIR_DTOB)
+ weston_matrix_transform(&tsc->bpc, &dtmp);
- return 1;
+ for (int i = 0; i < 3; i++)
+ dst[i] = dtmp.f[i];
+
+ return 1; /* Success. */
+}
+
+/*
+ * Black point compensation, copied from LittleCMS 2.16, cmscnvrt.c
+ * Adapted to Weston code base.
+ */
+static void
+ComputeBlackPointCompensation(struct weston_matrix *m,
+ const struct weston_vector *src_bp,
+ const struct weston_vector *dst_bp)
+{
+ double ax, ay, az, bx, by, bz, tx, ty, tz;
+
+ // Now we need to compute a matrix plus an offset m and of such of
+ // [m]*bpin + off = bpout
+ // [m]*D50 + off = D50
+ //
+ // This is a linear scaling in the form ax+b, where
+ // a = (bpout - D50) / (bpin - D50)
+ // b = - D50* (bpout - bpin) / (bpin - D50)
+
+ tx = src_bp->f[0] - cmsD50_XYZ()->X;
+ ty = src_bp->f[1] - cmsD50_XYZ()->Y;
+ tz = src_bp->f[2] - cmsD50_XYZ()->Z;
+
+ ax = (dst_bp->f[0] - cmsD50_XYZ()->X) / tx;
+ ay = (dst_bp->f[1] - cmsD50_XYZ()->Y) / ty;
+ az = (dst_bp->f[2] - cmsD50_XYZ()->Z) / tz;
+
+ bx = - cmsD50_XYZ()-> X * (dst_bp->f[0] - src_bp->f[0]) / tx;
+ by = - cmsD50_XYZ()-> Y * (dst_bp->f[1] - src_bp->f[1]) / ty;
+ bz = - cmsD50_XYZ()-> Z * (dst_bp->f[2] - src_bp->f[2]) / tz;
+
+ /*
+ * [ax, 0, 0, bx ]
+ * m = [ 0, ay, 0, by ]
+ * [ 0, 0, az, bz ]
+ * [ 0, 0, 0, 1 ]
+ */
+ weston_matrix_init(m);
+ weston_matrix_scale(m, ax, ay, az);
+ weston_matrix_translate(m, bx, by, bz);
}
static cmsStage *
create_cLUT_from_transform(cmsContext context_id, const cmsHTRANSFORM t,
- int dim_size)
+ int dim_size,
+ enum bpc_dir dir)
{
struct transform_sampler_context tsc;
cmsStage *cLUT_stage;
@@ -305,6 +380,18 @@ create_cLUT_from_transform(cmsContext context_id, const cmsHTRANSFORM t,
assert(dim_size);
tsc.t = t;
+ tsc.dir = dir;
+ switch (tsc.dir) {
+ case BPC_DIR_NONE:
+ weston_matrix_init(&tsc.bpc);
+ break;
+ case BPC_DIR_DTOB:
+ ComputeBlackPointCompensation(&tsc.bpc, &ZEROS, &PCS_BLACK);
+ break;
+ case BPC_DIR_BTOD:
+ ComputeBlackPointCompensation(&tsc.bpc, &PCS_BLACK, &ZEROS);
+ break;
+ }
cLUT_stage = cmsStageAllocCLutFloat(context_id, dim_size, 3, 3, NULL);
cmsStageSampleCLutFloat(cLUT_stage, transform_sampler, &tsc, 0);
@@ -340,6 +427,7 @@ build_lcms_clut_profile_output(cmsContext context_id,
enum transfer_fn eotf_fn = transfer_fn_invert(inv_eotf_fn);
cmsHPROFILE hRGB;
cmsPipeline *DToB0, *BToD0;
+ cmsPipeline *DToB1, *BToD1;
cmsStage *stage;
cmsStage *stage_inv_eotf;
cmsStage *stage_eotf;
@@ -392,39 +480,58 @@ build_lcms_clut_profile_output(cmsContext context_id,
/*
* Pipeline from PCS (optical) to device (electrical)
*/
- BToD0 = cmsPipelineAlloc(context_id, 3, 3);
+ /* Perceptual PCS black point is not zeros, so we need BPC */
+ BToD0 = cmsPipelineAlloc(context_id, 3, 3);
stage = create_cLUT_from_transform(context_id, pcs_to_linear_device,
- clut_dim_size);
+ clut_dim_size, BPC_DIR_BTOD);
cmsPipelineInsertStage(BToD0, cmsAT_END, stage);
cmsPipelineInsertStage(BToD0, cmsAT_END, cmsStageDup(stage_inv_eotf));
+ /* Media-relative colorimetric does not force BPC */
+ BToD1 = cmsPipelineAlloc(context_id, 3, 3);
+ stage = create_cLUT_from_transform(context_id, pcs_to_linear_device,
+ clut_dim_size, BPC_DIR_NONE);
+ cmsPipelineInsertStage(BToD1, cmsAT_END, stage);
+ cmsPipelineInsertStage(BToD1, cmsAT_END, cmsStageDup(stage_inv_eotf));
+
cmsWriteTag(hRGB, cmsSigBToD0Tag, BToD0);
- cmsLinkTag(hRGB, cmsSigBToD1Tag, cmsSigBToD0Tag);
+ cmsWriteTag(hRGB, cmsSigBToD1Tag, BToD1);
cmsLinkTag(hRGB, cmsSigBToD2Tag, cmsSigBToD0Tag);
- cmsLinkTag(hRGB, cmsSigBToD3Tag, cmsSigBToD0Tag);
+ cmsLinkTag(hRGB, cmsSigBToD3Tag, cmsSigBToD1Tag);
/*
* Pipeline from device (electrical) to PCS (optical)
*/
- DToB0 = cmsPipelineAlloc(context_id, 3, 3);
+ /* Perceptual PCS black point is not zeros, so we need BPC */
+ DToB0 = cmsPipelineAlloc(context_id, 3, 3);
cmsPipelineInsertStage(DToB0, cmsAT_END, cmsStageDup(stage_eotf));
stage = create_cLUT_from_transform(context_id, linear_device_to_pcs,
- clut_dim_size);
+ clut_dim_size, BPC_DIR_DTOB);
cmsPipelineInsertStage(DToB0, cmsAT_END, stage);
+ /* Media-relative colorimetric does not force BPC */
+ DToB1 = cmsPipelineAlloc(context_id, 3, 3);
+ cmsPipelineInsertStage(DToB1, cmsAT_END, cmsStageDup(stage_eotf));
+ stage = create_cLUT_from_transform(context_id, linear_device_to_pcs,
+ clut_dim_size, BPC_DIR_NONE);
+ cmsPipelineInsertStage(DToB1, cmsAT_END, stage);
+
cmsWriteTag(hRGB, cmsSigDToB0Tag, DToB0);
- cmsLinkTag(hRGB, cmsSigDToB1Tag, cmsSigDToB0Tag);
+ cmsWriteTag(hRGB, cmsSigDToB1Tag, DToB1);
cmsLinkTag(hRGB, cmsSigDToB2Tag, cmsSigDToB0Tag);
- cmsLinkTag(hRGB, cmsSigDToB3Tag, cmsSigDToB0Tag);
+ cmsLinkTag(hRGB, cmsSigDToB3Tag, cmsSigDToB1Tag);
vcgt_tag_add_to_profile(context_id, hRGB, vcgt_exponents);
roundtrip_verification(DToB0, BToD0, clut_roundtrip_tolerance);
+ roundtrip_verification(DToB1, BToD1, clut_roundtrip_tolerance);
cmsPipelineFree(BToD0);
cmsPipelineFree(DToB0);
+ cmsPipelineFree(BToD1);
+ cmsPipelineFree(DToB1);
cmsStageFree(stage_eotf);
cmsStageFree(stage_inv_eotf);