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