color-lcms: merge power-law curve sets
At the moment, when we merge two curve sets it becomes a sampled one. With this change, we start merging power-law curve sets and keeping them as parametric, as we'd rather have a parametric curve than a sampled one. Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
This commit is contained in:
parent
e4baf5ba09
commit
884579bc3c
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "color-curve-segments.h"
|
||||
#include "color-lcms.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
/**
|
||||
* LCMS internally defines MINUS_INF and PLUS_INF arbitrarily to -1e22 and 1e22.
|
||||
|
@ -404,6 +405,119 @@ are_curvesets_inverse(cmsStage *set_A, cmsStage *set_B)
|
|||
return true;
|
||||
}
|
||||
|
||||
static cmsToneCurve *
|
||||
join_powerlaw_curves(cmsContext context_id,
|
||||
cmsToneCurve *curve_A, cmsToneCurve *curve_B)
|
||||
{
|
||||
const float PRECISION = 1e-5;
|
||||
cmsCurveSegment segment;
|
||||
const cmsCurveSegment *seg_A, *seg_B;
|
||||
|
||||
seg_A = cmsGetToneCurveSegment(0, curve_A);
|
||||
seg_B = cmsGetToneCurveSegment(0, curve_B);
|
||||
if (!seg_A || !seg_B)
|
||||
return NULL;
|
||||
|
||||
/* TODO: handle certain multi-segmented curves, In such cases, we need
|
||||
* to pay attention to the segment breaks, and it is harder to merge the
|
||||
* curves.
|
||||
*
|
||||
* To merge the curves, we need to compute B(A(x)). This curve has the
|
||||
* same domain (input range) of curve A(x). So we already have the
|
||||
* segment breaks of A(x) in the domain of B(A(x)), but we need to
|
||||
* compute the breaks of B(x) in the same domain. To do that, we can use
|
||||
* the info that B(x) domain equals the output range of A(x). So feeding
|
||||
* the breaks of B(x) to the inverse of A(x) gives us the breaks of B(x)
|
||||
* in the domain of B(A(x)) as well. With the segments of A(x) and B(x)
|
||||
* and their breaks in the domain of B(A(x)), it is simple to compute
|
||||
* the segments of B(A(x)).
|
||||
*
|
||||
* Addressing the generic case described above is too much work for a
|
||||
* case that probably won't happen. But an easy multi-segmented case
|
||||
* that we'd be able to merge but are not handling here is when both
|
||||
* curves are like that:
|
||||
*
|
||||
* segment 1: (-inf, 0.0] - constant 0.0
|
||||
* segment 2: (0.0, 1.0] - some parametric curve
|
||||
* segment 3: (1.0, inf] - constant 1.0
|
||||
*
|
||||
* The meaning of such curves is: the values out of the range (0.0, 1.0]
|
||||
* don't matter. So if both curves have 3 segments like that and the 2nd
|
||||
* segment of both is power law, we can easily merge them. */
|
||||
if (cmsGetToneCurveSegment(1, curve_A) ||
|
||||
cmsGetToneCurveSegment(1, curve_B))
|
||||
return NULL;
|
||||
|
||||
/* Ensure that the segment breaks are equal to (-inf, inf). */
|
||||
if (!are_segment_breaks_equal(seg_A->x0, -INFINITY) ||
|
||||
!are_segment_breaks_equal(seg_A->x1, INFINITY) ||
|
||||
!are_segment_breaks_equal(seg_B->x0, -INFINITY) ||
|
||||
!are_segment_breaks_equal(seg_B->x1, INFINITY))
|
||||
return NULL;
|
||||
|
||||
/* Power law curves are type 1 and -1, and we only merge these curves
|
||||
* here. See segment_print() to know more about the curves types. */
|
||||
if (abs(seg_A->Type) != 1 || abs(seg_A->Type) != abs(seg_B->Type))
|
||||
return NULL;
|
||||
|
||||
segment.x0 = seg_A->x0;
|
||||
segment.x1 = seg_A->x1;
|
||||
segment.Type = seg_A->Type;
|
||||
|
||||
/* Being seg_A the curve f and seg_B the curve g, we need to compute
|
||||
* g(f(x)). Type 1 is the power law curve and type -1 is its inverse.
|
||||
*
|
||||
* So if seg_A has exponent j and seg_B has exponent j', g(f(x)) has
|
||||
* exponent k:
|
||||
*
|
||||
* k = j * j', if seg_A and seg_B have the same sign.
|
||||
* k = j / j', if they have opposite signs.
|
||||
*
|
||||
* If the resulting curve type (seg_A->Type) is positive, LCMS
|
||||
* internally handles it as x^k, and if it is negative it will use
|
||||
* x^(1/k). */
|
||||
if (seg_A->Type == seg_B->Type) {
|
||||
segment.Params[0] = seg_A->Params[0] * seg_B->Params[0];
|
||||
} else {
|
||||
assert(seg_A->Type == - seg_B->Type);
|
||||
if (fabs(seg_B->Params[0]) < PRECISION)
|
||||
return NULL;
|
||||
segment.Params[0] = seg_A->Params[0] / seg_B->Params[0];
|
||||
}
|
||||
|
||||
return cmsBuildSegmentedToneCurve(context_id, 1, &segment);
|
||||
}
|
||||
|
||||
cmsStage *
|
||||
join_powerlaw_curvesets(cmsContext context_id,
|
||||
cmsToneCurve **set_A, cmsToneCurve **set_B)
|
||||
{
|
||||
cmsToneCurve *arr[3];
|
||||
int i;
|
||||
cmsStage *ret;
|
||||
bool powerlaw = true;
|
||||
|
||||
for (i = 0; (uint32_t)i < ARRAY_LENGTH(arr); i++) {
|
||||
arr[i] = join_powerlaw_curves(context_id, set_A[i], set_B[i]);
|
||||
if (!arr[i]) {
|
||||
powerlaw = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!powerlaw) {
|
||||
for (; i >= 0; i--)
|
||||
cmsFreeToneCurve(arr[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The CurveSet's are powerlaw functions that we were able to merge. */
|
||||
ret = cmsStageAllocToneCurves(context_id, ARRAY_LENGTH(arr), arr);
|
||||
abort_oom_if_null(ret);
|
||||
cmsFreeToneCurveTriple(arr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
curve_set_print(cmsStage *stage, struct weston_log_scope *scope)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,10 @@ curve_set_print(cmsStage *stage, struct weston_log_scope *scope);
|
|||
bool
|
||||
are_curvesets_inverse(cmsStage *set_A, cmsStage *set_B);
|
||||
|
||||
cmsStage *
|
||||
join_powerlaw_curvesets(cmsContext context_id,
|
||||
cmsToneCurve **set_A, cmsToneCurve **set_B);
|
||||
|
||||
# else /* HAVE_CMS_GET_TONE_CURVE_SEGMENT */
|
||||
|
||||
static inline void
|
||||
|
@ -53,6 +57,13 @@ are_curvesets_inverse(cmsStage *set_A, cmsStage *set_B)
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline cmsStage *
|
||||
join_powerlaw_curvesets(cmsContext context_id,
|
||||
cmsToneCurve **set_A, cmsToneCurve **set_B)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* HAVE_CMS_GET_TONE_CURVE_SEGMENT */
|
||||
|
||||
#endif /* COLOR_CURVE_SEGMENTS_H */
|
||||
|
|
|
@ -376,6 +376,15 @@ join_curvesets(cmsContext context_id, const cmsStage *prev,
|
|||
assert(prev_->nCurves == ARRAY_LENGTH(arr));
|
||||
assert(next_->nCurves == ARRAY_LENGTH(arr));
|
||||
|
||||
/* If the CurveSet's are parametric powerlaw curves that we know how to
|
||||
* merge (preserving them as parametric powerlaw curves), we do that. We
|
||||
* want to avoid transforming parametric curves into sampled curves. */
|
||||
ret = join_powerlaw_curvesets(context_id,
|
||||
prev_->TheCurves, next_->TheCurves);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Transform both CurveSet's into a single sampled one. */
|
||||
for (i = 0; i < ARRAY_LENGTH(arr); i++) {
|
||||
arr[i] = lcmsJoinToneCurve(context_id, prev_->TheCurves[i],
|
||||
next_->TheCurves[i], num_samples);
|
||||
|
|
Loading…
Reference in New Issue