This adds a new test helper library that depends on LittleCMS 2. For starters, the library implements conversion from enum transfer_fn to ICC multiProcessingElements compatible LittleCMS curve object. That conversion allows encoding transfer funtions in ICC files and LittleCMS pipelines with full float32 precision instead of forcing a conversion to a 1D LUT which for power-type curves is surprisingly imprecise. This also adds CI tests to make sure the conversion matches our hand-coded transfer functions. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>dev
parent
0225453fb1
commit
44c3079567
@ -0,0 +1,85 @@ |
|||||||
|
/*
|
||||||
|
* 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 <math.h> |
||||||
|
#include <lcms2.h> |
||||||
|
|
||||||
|
#include "weston-test-client-helper.h" |
||||||
|
#include "color_util.h" |
||||||
|
#include "lcms_util.h" |
||||||
|
|
||||||
|
static void |
||||||
|
compare_pipeline_to_transfer_fn(cmsPipeline *pipeline, enum transfer_fn fn, |
||||||
|
struct scalar_stat *stat) |
||||||
|
{ |
||||||
|
const unsigned N = 100000; |
||||||
|
unsigned i; |
||||||
|
|
||||||
|
for (i = 0; i < N; i++) { |
||||||
|
float x = (double)i / N; |
||||||
|
float ref = apply_tone_curve(fn, x); |
||||||
|
float y; |
||||||
|
|
||||||
|
cmsPipelineEvalFloat(&x, &y, pipeline); |
||||||
|
scalar_stat_update(stat, y - ref, &(struct color_float){ .r = x }); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static const enum transfer_fn build_MPE_curves_test_set[] = { |
||||||
|
TRANSFER_FN_SRGB_EOTF, |
||||||
|
TRANSFER_FN_SRGB_EOTF_INVERSE, |
||||||
|
TRANSFER_FN_ADOBE_RGB_EOTF, |
||||||
|
TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, |
||||||
|
TRANSFER_FN_POWER2_4_EOTF, |
||||||
|
TRANSFER_FN_POWER2_4_EOTF_INVERSE, |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_P(build_MPE_curves, build_MPE_curves_test_set) |
||||||
|
{ |
||||||
|
const enum transfer_fn *fn = data; |
||||||
|
const cmsContext ctx = 0; |
||||||
|
cmsToneCurve *curve; |
||||||
|
cmsStage *stage; |
||||||
|
cmsPipeline *pipeline; |
||||||
|
struct scalar_stat stat = {}; |
||||||
|
|
||||||
|
curve = build_MPE_curve(ctx, *fn); |
||||||
|
stage = cmsStageAllocToneCurves(ctx, 1, &curve); |
||||||
|
cmsFreeToneCurve(curve); |
||||||
|
|
||||||
|
pipeline = cmsPipelineAlloc(ctx, 1, 1); |
||||||
|
cmsPipelineInsertStage(pipeline, cmsAT_END, stage); |
||||||
|
|
||||||
|
compare_pipeline_to_transfer_fn(pipeline, *fn, &stat); |
||||||
|
testlog("Transfer function %s as a segmented curve element, error:\n", |
||||||
|
transfer_fn_name(*fn)); |
||||||
|
scalar_stat_print_float(&stat); |
||||||
|
assert(fabs(stat.max) < 1e-7); |
||||||
|
assert(fabs(stat.min) < 1e-7); |
||||||
|
|
||||||
|
cmsPipelineFree(pipeline); |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
/*
|
||||||
|
* 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 <math.h> |
||||||
|
#include <lcms2.h> |
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
#include "shared/helpers.h" |
||||||
|
#include "color_util.h" |
||||||
|
#include "lcms_util.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
* MPE tone curves can only use LittleCMS parametric curve types 6-8 and not |
||||||
|
* inverses. |
||||||
|
* type 6: Y = (aX + b)^g + c; params [g, a, b, c] |
||||||
|
* type 7: Y = a log(bX^g + c) + d; params [g, a, b, c, d] |
||||||
|
* type 8: Y = a b^(cX + d) + e; params [a, b, c, d, e] |
||||||
|
* Additionally, type 0 is sampled segment. |
||||||
|
* |
||||||
|
* cmsCurveSegment.x1 is the breakpoint stored in ICC files, except for the |
||||||
|
* last segment. First segment always begins at -Inf, and last segment always |
||||||
|
* ends at Inf. |
||||||
|
*/ |
||||||
|
|
||||||
|
static cmsToneCurve * |
||||||
|
build_MPE_curve_sRGB(cmsContext ctx) |
||||||
|
{ |
||||||
|
cmsCurveSegment segments[] = { |
||||||
|
{ |
||||||
|
/* Constant zero segment */ |
||||||
|
.x0 = -HUGE_VAL, |
||||||
|
.x1 = 0.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 0.0 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Linear segment y = x / 12.92 */ |
||||||
|
.x0 = 0.0, |
||||||
|
.x1 = 0.04045, |
||||||
|
.Type = 0, |
||||||
|
.nGridPoints = 2, |
||||||
|
.SampledPoints = (float[]){ 0.0, 0.04045 / 12.92 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Power segment y = ((x + 0.055) / 1.055)^2.4
|
||||||
|
* which is translated to |
||||||
|
* y = (1/1.055 * x + 0.055 / 1.055)^2.4 + 0.0 |
||||||
|
*/ |
||||||
|
.x0 = 0.04045, |
||||||
|
.x1 = 1.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 0.0 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Constant one segment */ |
||||||
|
.x0 = 1.0, |
||||||
|
.x1 = HUGE_VAL, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 1.0 }, |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); |
||||||
|
} |
||||||
|
|
||||||
|
static cmsToneCurve * |
||||||
|
build_MPE_curve_sRGB_inv(cmsContext ctx) |
||||||
|
{ |
||||||
|
cmsCurveSegment segments[] = { |
||||||
|
{ |
||||||
|
/* Constant zero segment */ |
||||||
|
.x0 = -HUGE_VAL, |
||||||
|
.x1 = 0.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 0.0 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Linear segment y = x * 12.92 */ |
||||||
|
.x0 = 0.0, |
||||||
|
.x1 = 0.04045 / 12.92, |
||||||
|
.Type = 0, |
||||||
|
.nGridPoints = 2, |
||||||
|
.SampledPoints = (float[]){ 0.0, 0.04045 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Power segment y = 1.055 * x^(1/2.4) - 0.055
|
||||||
|
* which is translated to |
||||||
|
* y = (1.055^2.4 * x + 0.0)^(1/2.4) - 0.055 |
||||||
|
*/ |
||||||
|
.x0 = 0.04045 / 12.92, |
||||||
|
.x1 = 1.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0 / 2.4, pow(1.055, 2.4), 0.0, -0.055 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Constant one segment */ |
||||||
|
.x0 = 1.0, |
||||||
|
.x1 = HUGE_VAL, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 1.0 }, |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); |
||||||
|
} |
||||||
|
|
||||||
|
static cmsToneCurve * |
||||||
|
build_MPE_curve_power(cmsContext ctx, double exponent) |
||||||
|
{ |
||||||
|
cmsCurveSegment segments[] = { |
||||||
|
{ |
||||||
|
/* Constant zero segment */ |
||||||
|
.x0 = -HUGE_VAL, |
||||||
|
.x1 = 0.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 0.0 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Power segment y = x^exponent
|
||||||
|
* which is translated to |
||||||
|
* y = (1.0 * x + 0.0)^exponent + 0.0 |
||||||
|
*/ |
||||||
|
.x0 = 0.0, |
||||||
|
.x1 = 1.0, |
||||||
|
.Type = 6, |
||||||
|
.Params = { exponent, 1.0, 0.0, 0.0 }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
/* Constant one segment */ |
||||||
|
.x0 = 1.0, |
||||||
|
.x1 = HUGE_VAL, |
||||||
|
.Type = 6, |
||||||
|
.Params = { 1.0, 0.0, 0.0, 1.0 }, |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); |
||||||
|
} |
||||||
|
|
||||||
|
cmsToneCurve * |
||||||
|
build_MPE_curve(cmsContext ctx, enum transfer_fn fn) |
||||||
|
{ |
||||||
|
switch (fn) { |
||||||
|
case TRANSFER_FN_ADOBE_RGB_EOTF: |
||||||
|
return build_MPE_curve_power(ctx, 563.0 / 256.0); |
||||||
|
case TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE: |
||||||
|
return build_MPE_curve_power(ctx, 256.0 / 563.0); |
||||||
|
case TRANSFER_FN_POWER2_4_EOTF: |
||||||
|
return build_MPE_curve_power(ctx, 2.4); |
||||||
|
case TRANSFER_FN_POWER2_4_EOTF_INVERSE: |
||||||
|
return build_MPE_curve_power(ctx, 1.0 / 2.4); |
||||||
|
case TRANSFER_FN_SRGB_EOTF: |
||||||
|
return build_MPE_curve_sRGB(ctx); |
||||||
|
case TRANSFER_FN_SRGB_EOTF_INVERSE: |
||||||
|
return build_MPE_curve_sRGB_inv(ctx); |
||||||
|
default: |
||||||
|
assert(0 && "unimplemented MPE curve"); |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
cmsStage * |
||||||
|
build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn) |
||||||
|
{ |
||||||
|
cmsToneCurve *c; |
||||||
|
cmsStage *stage; |
||||||
|
|
||||||
|
c = build_MPE_curve(context_id, fn); |
||||||
|
stage = cmsStageAllocToneCurves(context_id, 3, |
||||||
|
(cmsToneCurve *[3]){ c, c, c }); |
||||||
|
assert(stage); |
||||||
|
cmsFreeToneCurve(c); |
||||||
|
|
||||||
|
return stage; |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
/*
|
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <lcms2.h> |
||||||
|
|
||||||
|
#include "color_util.h" |
||||||
|
|
||||||
|
cmsToneCurve * |
||||||
|
build_MPE_curve(cmsContext ctx, enum transfer_fn fn); |
||||||
|
|
||||||
|
cmsStage * |
||||||
|
build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn); |
Loading…
Reference in new issue