Skip to content
Commits on Source (7)
  • Leandro Ribeiro's avatar
    tests: skip color-icc-output if we don't have cmsGetToneCurveSegment() · 5ddbdb7a
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    When we don't have cmsGetToneCurveSegment() at disposal, we are not able
    to inspect the LittleCMS color curves and convert them to Weston's
    internal representation of color curves. In such case, we need to
    fallback to a more generic solution (using LUT's).
    
    For now we always fallback to the LUT's, but in the next commits we'll
    add support to inspect the curves and convert them to the internal
    representations that we'll add.
    
    This will allow us to tweak the tolerance in the color-icc-output tests.
    But if we continue running these tests for systems without
    cmsGetToneCurveSegment() at disposal, they may fail.
    
    We already have a LittleCMS version in the CI that has
    cmsGetToneCurveSegment(). So skip color-icc-output when we don't have
    this function.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    5ddbdb7a
  • Leandro Ribeiro's avatar
    color-lcms: changes to the parameters of translate_curve_element() · 80d97db8
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    Not a behavior change, but this allow us to decide what function pointer
    to use within this function (instead of forcing callers to decide that).
    
    In the following commits this will be helpful. We'll add more curves
    besides 3x1D LUT's and, depending on the curve, the function pointer
    signature may differ.
    
    Also, we now pass the xform directly to the function, and it can select
    the curves depending if it is being called for a pre or a post curve.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    80d97db8
  • Leandro Ribeiro's avatar
    color-lcms: add translate_curve_element_LUT() · aff6029d
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    Currently in translate_curve_element() we always translate the curve
    into a LUT. But in the future we'll be able to translate the curves to
    parametric ones.
    
    So move the current code to a new function
    translate_curve_element_LUT(), so that in translate_curve_element() we
    are able to call one of the two functions (_LUT() or _parametric()).
    
    No behavior changes, just preparation for the upcoming patches.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    aff6029d
  • Leandro Ribeiro's avatar
    color-lcms: add function to get params from parametric curvesets · aee3e313
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    Given a certain curveset, get_parametric_curveset_params() returns true
    if the curveset contains parametric curves. Also, it returns the
    parameters of the curveset (for each curve) and if the input should be
    clamped or not.
    
    This is not a generic function, it will specifically work for some well
    behaved curveset. E.g. we return false if there are more than 3 curves
    (one per color channel).
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    aee3e313
  • Leandro Ribeiro's avatar
    gl-renderer: move lut_3x1d curves to union · d9e2eca1
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    In the next commit we'll add support for more color curves. So move
    lut_3x1d to an union.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    d9e2eca1
  • Leandro Ribeiro's avatar
    color: add support to parametric curves in weston_color_curve · 9002667a
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    Until now, all the curves would be represented with 3x1D LUT's. Now we
    support LINPOW and POWLIN curves (arbitrary names that we've picked).
    We can use these curves to represent LittleCMS curves type 1, 4 and
    their inverses -1, -4. The reason why we want that is because we gain
    precision using the parametric curves (compared to the LUT's);
    
    Surprisingly we had to increase the tolerance of the sRGB->adobeRGB MAT
    test. Our analysis is that the inverse EOTF power-law curve with
    exponent 1.0 / 2.2 amplifies errors more than the LUT, specially for
    input (optical) values closer to zero.
    
    That makes sense, because this curve is more sensible to input values
    closer to zero (i.e. little input variation results in lots of output
    variation). And this model makes sense, as humans are more capable of
    perceiving changes of light intensity in the dark.
    
    But the downside of all that is that for input values closer to zero, a
    little bit of noise increases significantly the error.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    9002667a
  • Leandro Ribeiro's avatar
    tests: reduce tolerance of sRGB->BT2020 MAT test · de8e3168
    Leandro Ribeiro authored and Pekka Paalanen's avatar Pekka Paalanen committed
    
    
    On commit "color: add support to parametric curves in
    weston_color_curve" we've added support for some parametric curves in
    Weston. This helped us to be more precise in some cases in which we'd
    have to fallback to LUT's otherwise.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    de8e3168
......@@ -572,3 +572,83 @@ curveset_print(cmsStage *stage, struct weston_log_scope *scope)
curve_print(data->TheCurves[i], scope);
}
}
bool
get_parametric_curveset_params(struct weston_compositor *compositor,
_cmsStageToneCurvesData *trc_data,
cmsInt32Number *type,
float curveset_params[3][10],
bool *clamped_input)
{
const cmsCurveSegment *seg, *seg0, *seg1, *seg2;
cmsInt32Number curve_types[3];
unsigned int i, j;
/* If there isn't a curve for each color channel, we define our curveset
* as not being a parametric one. */
if (trc_data->nCurves != 3)
return false;
/* If at least one of the curves is being clamped to [0.0, 1.0], we
* change clamped_input to true. Our expectation is that if one of the
* curves is being clamped, all of them are. It makes no sense that it
* gets clamped in a certain color channel and not in the others. */
*clamped_input = false;
for (i = 0; i < 3; i++) {
/* We handle curves with 1 or 3 segments. No more, no less. */
seg0 = cmsGetToneCurveSegment(0, trc_data->TheCurves[i]);
seg1 = cmsGetToneCurveSegment(1, trc_data->TheCurves[i]);
seg2 = cmsGetToneCurveSegment(2, trc_data->TheCurves[i]);
if (seg0 && !seg1) {
/* Case 1: we have a single segment (seg0).
*
* Ensure that the domain is (-inf, inf) and that the
* seg type is not 0 (the type of sampled segments).
*/
if (!are_segment_breaks_equal(seg0->x0, -INFINITY) ||
!are_segment_breaks_equal(seg0->x1, INFINITY) ||
seg0->Type == 0)
return false;
seg = seg0;
} else if (seg0 && seg1 && seg2) {
/* Case 2: we have three segments. Clamped input.
*
* Ensure that the domain breaks are (-inf, 0.0],
* (0.0, 1.0] and (1.0, inf] and that the 2nd segment
* type is not 0 (the type of sampled segments).
*/
if (!are_segment_breaks_equal(seg0->x0, -INFINITY) ||
!are_segment_breaks_equal(seg0->x1, 0.0) ||
!are_segment_breaks_equal(seg1->x0, 0.0) ||
!are_segment_breaks_equal(seg1->x1, 1.0) ||
!are_segment_breaks_equal(seg2->x0, 1.0) ||
!are_segment_breaks_equal(seg2->x1, INFINITY) ||
seg1->Type == 0)
return false;
seg = seg1;
*clamped_input = true;
} else {
/* Neither 1 or 3 segments. So we don't define the
* curveset as parametric. */
return false;
}
/* Copy the type and params from the segment that matters. We
* don't use memcpy because we need to cast each cmsFloat64Number
* to float. The precision loss doesn't matter. */
curve_types[i] = seg->Type;
for (j = 0; j < 10; j++)
curveset_params[i][j] = (float)seg->Params[j];
}
/* We only define our curveset as parametric when each of its curves are
* of the same type for all the 3 channels. */
if (curve_types[0] != curve_types[1] || curve_types[0] != curve_types[2])
return false;
*type = curve_types[0];
return true;
}
......@@ -32,6 +32,13 @@
#if HAVE_CMS_GET_TONE_CURVE_SEGMENT
bool
get_parametric_curveset_params(struct weston_compositor *compositor,
_cmsStageToneCurvesData *trc_data,
cmsInt32Number *type,
float curveset_params[3][10],
bool *clamped_input);
void
curveset_print(cmsStage *stage, struct weston_log_scope *scope);
......@@ -47,6 +54,16 @@ join_powerlaw_curvesets(cmsContext context_id,
# else /* HAVE_CMS_GET_TONE_CURVE_SEGMENT */
static inline bool
get_parametric_curveset_params(struct weston_compositor *compositor,
_cmsStageToneCurvesData *trc_data,
cmsInt32Number *type,
float curveset_params[3][10],
bool *clamped_input)
{
return false;
}
static inline void
curveset_print(cmsStage *stage, struct weston_log_scope *scope)
{
......
......@@ -176,12 +176,14 @@ struct cmlcms_color_transform {
struct cmlcms_color_transform_search_param search_key;
/*
* Cached data in case weston_color_transform needs them.
* Pre-curve and post-curve refer to the weston_color_transform
* pipeline elements and have no semantic meaning. They both are a
* result of optimizing an arbitrary LittleCMS pipeline, not
* e.g. EOTF or VCGT per se.
/**
* Cached data used when we can't translate the curves into parametric
* ones that we implement in the renderer. So when we need to fallback
* to LUT's, we use this data to compute them.
*
* These curves are a result of optimizing an arbitrary LittleCMS
* pipeline, so they have no semantic meaning (that means that they are
* not e.g. an EOTF).
*/
cmsToneCurve *pre_curve[3];
cmsToneCurve *post_curve[3];
......
......@@ -465,25 +465,368 @@ merge_curvesets(cmsPipeline **lut, cmsContext context_id)
}
static bool
translate_curve_element(struct weston_color_curve *curve,
cmsToneCurve *stash[3],
void (*func)(struct weston_color_transform *xform,
float *values, unsigned len),
cmsStage *elem)
linpow_from_type_1(struct weston_compositor *compositor,
struct weston_color_curve *curve,
const float type_1_params[3][10], bool clamped_input)
{
_cmsStageToneCurvesData *trc_data;
unsigned i;
struct weston_color_curve_parametric *parametric = &curve->u.parametric;
unsigned int i;
assert(cmsStageType(elem) == cmsSigCurveSetElemType);
curve->type = WESTON_COLOR_CURVE_TYPE_LINPOW;
trc_data = cmsStageData(elem);
if (trc_data->nCurves != 3)
parametric->clamped_input = clamped_input;
/* LittleCMS type 1 is the pure power-law curve, which is a special case
* of LINPOW.
*
* LINPOW is defined as:
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | 0 <= x < d
*
* So for a = 1, b = 0, c = 1 and d = 0, we have:
*
* y = x ^ g | x >= 0
*
* As the pure power-law is only defined for values x >= 0 (because
* negative values raised to fractional exponents results in complex
* numbers), this is exactly the pure power-law curve.
*/
for (i = 0; i < ARRAY_LENGTH(parametric->params); i++) {
parametric->params[i][0] = type_1_params[i][0]; /* g */
parametric->params[i][1] = 1.0f; /* a */
parametric->params[i][2] = 0.0f; /* b */
parametric->params[i][3] = 1.0f; /* c */
parametric->params[i][4] = 0.0f; /* d */
}
return true;
}
static bool
linpow_from_type_1_inverse(struct weston_compositor *compositor,
struct weston_color_curve *curve,
const float type_1_params[3][10], bool clamped_input)
{
struct weston_color_manager_lcms *cm = to_cmlcms(compositor->color_manager);
struct weston_color_curve_parametric *parametric = &curve->u.parametric;
float g;
const char *err_msg;
unsigned int i;
curve->type = WESTON_COLOR_CURVE_TYPE_LINPOW;
parametric->clamped_input = clamped_input;
/* LittleCMS type -1 (inverse of type 1) is the inverse of the pure
* power-law curve, which is a special case of LINPOW.
*
* The type 1 is defined as:
*
* y = x ^ g | x >= 0
*
* Computing its inverse, we have:
*
* y = x ^ (1 / g) | x >= 0
*
* LINPOW is defined as:
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | 0 <= x < d
*
* So for a = 1, b = 0, c = 1 and d = 0, we have:
*
* y = x ^ g | x >= 0
*
* If we take the param g from type -1 and invert it, we can fit type -1
* into the curve above.
*/
for (i = 0; i < ARRAY_LENGTH(parametric->params); i++) {
g = type_1_params[i][0];
if (g == 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type -1 curve " \
"(inverse of pure power-law) with exponent 1 " \
"divided by 0, which is invalid";
goto err;
}
parametric->params[i][0] = 1.0f / g;
parametric->params[i][1] = 1.0f; /* a */
parametric->params[i][2] = 0.0f; /* b */
parametric->params[i][3] = 1.0f; /* c */
parametric->params[i][4] = 0.0f; /* d */
}
return true;
err:
weston_log_scope_printf(cm->transforms_scope, "%s\n", err_msg);
return false;
}
static bool
linpow_from_type_4(struct weston_compositor *compositor,
struct weston_color_curve *curve,
const float type_4_params[3][10], bool clamped_input)
{
struct weston_color_manager_lcms *cm = to_cmlcms(compositor->color_manager);
struct weston_color_curve_parametric *parametric = &curve->u.parametric;
float g, a, b, c, d;
const char *err_msg;
unsigned int i;
curve->type = WESTON_COLOR_CURVE_TYPE_LINPOW;
parametric->clamped_input = clamped_input;
/* LittleCMS type 4 is almost exactly the same as LINPOW. So simply copy
* the params. No need to adjust anything.
*
* The only difference is that type 4 evaluates negative input values as
* is, and LINPOW handles negative input values using mirroring (i.e.
* for LINPOW being f(x) we'll compute -f(-x)).
*
* LINPOW is defined as:
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | 0 <= x < d
*/
for (i = 0; i < ARRAY_LENGTH(parametric->params); i++) {
g = type_4_params[i][0];
a = type_4_params[i][1];
b = type_4_params[i][2];
c = type_4_params[i][3];
d = type_4_params[i][4];
if (a < 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type 4 curve " \
"with a < 0, which is unexpected";
goto err;
}
if (d < 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type 4 curve " \
"with d < 0, which is unexpected";
goto err;
}
if (a * d + b < 0) {
err_msg = "WARNING: xform has a LittleCMS type 4 curve " \
"with a * d + b < 0, which is invalid";
goto err;
}
parametric->params[i][0] = g;
parametric->params[i][1] = a;
parametric->params[i][2] = b;
parametric->params[i][3] = c;
parametric->params[i][4] = d;
}
return true;
err:
weston_log_scope_printf(cm->transforms_scope, "%s\n", err_msg);
return false;
}
static bool
powlin_from_type_4_inverse(struct weston_compositor *compositor,
struct weston_color_curve *curve,
const float type_4_params[3][10], bool clamped_input)
{
struct weston_color_manager_lcms *cm = to_cmlcms(compositor->color_manager);
struct weston_color_curve_parametric *parametric = &curve->u.parametric;
float g, a, b, c, d;
const char *err_msg;
unsigned int i;
curve->type = WESTON_COLOR_CURVE_TYPE_POWLIN;
parametric->clamped_input = clamped_input;
/* LittleCMS type -4 (inverse of type 4) fits into POWLIN. We need to
* adjust the params that LittleCMS gives us, like below. Do not forget
* that LittleCMS gives the params of the type 4 curve whose inverse
* is the one it wants to represent.
*
* Also, type -4 evaluates negative input values as is, and POWLIN
* handles negative input values using mirroring (i.e. for POWLIN being
* f(x) we'll compute -f(-x)). We do that to avoid negative values being
* raised to fractional exponents, what would result in complex numbers.
*
* The type 4 is defined as:
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | else
*
* Computing its inverse, we have:
*
* y = ((x ^ (1 / g)) / a) - (b / a) | x >= c * d or (a * d + b) ^ g
* y = x / c | else
*
* POWLIN is defined as:
*
* y = (a * (x ^ g)) + b | x >= d
* y = c * x | 0 <= x < d
*
* So we need to take the params from LittleCMS and adjust:
*
* g ← 1 / g
* a ← 1 / a
* b ← -b / a
* c ← 1 / c
* d ← c * d
*
* Also, notice that c * d should be equal to (a * d + b) ^ g. But
* because of precision problems or a deliberate discontinuity in the
* function, that may not be true. So we may have a range of input
* values for POWLIN such that c * d <= x <= (a * d + b) ^ g. For these
* values, when evaluating POWLIN we need to decide with what segment
* we're going to evaluate the input. For the majority of POWLIN color
* curves created from type -4 we are expecting c * d ≈ (a * d + b) ^ g,
* so the different output produced by the two discontinuous segments
* would be so close that this wouldn't matter. But mathematically
* there's nothing that guarantees that the two discontinuous segments
* are close, and in this case the outputs would vary significantly.
* There's nothing we can do regarding that, so we'll arbitrarily choose
* one of the segments to compute the output.
*/
for (i = 0; i < ARRAY_LENGTH(parametric->params); i++) {
g = type_4_params[i][0];
a = type_4_params[i][1];
b = type_4_params[i][2];
c = type_4_params[i][3];
d = type_4_params[i][4];
if (g == 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type -4 curve " \
"but the param g of the original type 4 curve " \
"is zero, so the inverse is invalid";
goto err;
}
if (a == 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type -4 curve " \
"but the param a of the original type 4 curve " \
"is zero, so the inverse is invalid";
goto err;
}
if (c == 0.0f) {
err_msg = "WARNING: xform has a LittleCMS type -4 curve " \
"but the param c of the original type 4 curve " \
"is zero, so the inverse is invalid";
goto err;
}
parametric->params[i][0] = 1.0f / g;
parametric->params[i][1] = 1.0f / a;
parametric->params[i][2] = -b / a;
parametric->params[i][3] = 1.0f / c;
parametric->params[i][4] = c * d;
}
return true;
err:
weston_log_scope_printf(cm->transforms_scope, "%s\n", err_msg);
return false;
}
enum color_transform_step {
PRE_CURVE,
POST_CURVE,
};
static bool
translate_curve_element_parametric(struct cmlcms_color_transform *xform,
_cmsStageToneCurvesData *trc_data,
enum color_transform_step step)
{
struct weston_compositor *compositor = xform->base.cm->compositor;
struct weston_color_curve *curve;
cmsInt32Number type;
float lcms_curveset_params[3][10];
bool clamped_input;
bool ret;
switch(step) {
case PRE_CURVE:
curve = &xform->base.pre_curve;
break;
case POST_CURVE:
curve = &xform->base.post_curve;
break;
default:
weston_assert_not_reached(compositor,
"curve should be a pre or post curve");
}
/* The curveset may not be a parametric one, in such case we have a
* fallback path. But if it is a parametric curve, we get the params for
* each color channel and also the parametric curve type (defined by
* LittleCMS). */
if (!get_parametric_curveset_params(compositor, trc_data, &type,
lcms_curveset_params, &clamped_input))
return false;
switch (type) {
case 1:
ret = linpow_from_type_1(compositor, curve,
lcms_curveset_params, clamped_input);
break;
case -1:
ret = linpow_from_type_1_inverse(compositor, curve,
lcms_curveset_params, clamped_input);
break;
case 4:
ret = linpow_from_type_4(compositor, curve,
lcms_curveset_params, clamped_input);
break;
case -4:
ret = powlin_from_type_4_inverse(compositor, curve,
lcms_curveset_params, clamped_input);
break;
default:
/* We don't implement the curve. */
ret = false;
}
return ret;
}
static bool
translate_curve_element_LUT(struct cmlcms_color_transform *xform,
_cmsStageToneCurvesData *trc_data,
enum color_transform_step step)
{
struct weston_compositor *compositor = xform->base.cm->compositor;
struct weston_color_curve *curve;
cmsToneCurve **stash;
unsigned i;
switch(step) {
case PRE_CURVE:
curve = &xform->base.pre_curve;
curve->u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve;
stash = xform->pre_curve;
break;
case POST_CURVE:
curve = &xform->base.post_curve;
curve->u.lut_3x1d.fill_in = cmlcms_fill_in_post_curve;
stash = xform->post_curve;
break;
default:
weston_assert_not_reached(compositor,
"curve should be a pre or post curve");
}
curve->type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
curve->u.lut_3x1d.fill_in = func;
curve->u.lut_3x1d.optimal_len = cmlcms_reasonable_1D_points();
weston_assert_uint32_eq(compositor, trc_data->nCurves, 3);
for (i = 0; i < 3; i++) {
stash[i] = cmsDupToneCurve(trc_data->TheCurves[i]);
abort_oom_if_null(stash[i]);
......@@ -492,6 +835,29 @@ translate_curve_element(struct weston_color_curve *curve,
return true;
}
static bool
translate_curve_element(struct cmlcms_color_transform *xform,
cmsStage *elem, enum color_transform_step step)
{
struct weston_compositor *compositor = xform->base.cm->compositor;
_cmsStageToneCurvesData *trc_data;
weston_assert_uint64_eq(compositor, cmsStageType(elem),
cmsSigCurveSetElemType);
trc_data = cmsStageData(elem);
if (trc_data->nCurves != 3)
return false;
/* First try to translate the curve to a parametric one. */
if (translate_curve_element_parametric(xform, trc_data, step))
return true;
/* Curve does not fit any of the parametric curves that we implement, so
* fallback to LUT. */
return translate_curve_element_LUT(xform, trc_data, step);
}
static bool
translate_matrix_element(struct weston_color_mapping *map, cmsStage *elem)
{
......@@ -533,9 +899,7 @@ translate_pipeline(struct cmlcms_color_transform *xform, const cmsPipeline *lut)
return true;
if (cmsStageType(elem) == cmsSigCurveSetElemType) {
if (!translate_curve_element(&xform->base.pre_curve,
xform->pre_curve,
cmlcms_fill_in_pre_curve, elem))
if (!translate_curve_element(xform, elem, PRE_CURVE))
return false;
elem = cmsStageNext(elem);
......@@ -555,9 +919,7 @@ translate_pipeline(struct cmlcms_color_transform *xform, const cmsPipeline *lut)
return true;
if (cmsStageType(elem) == cmsSigCurveSetElemType) {
if (!translate_curve_element(&xform->base.post_curve,
xform->post_curve,
cmlcms_fill_in_post_curve, elem))
if (!translate_curve_element(xform, elem, POST_CURVE))
return false;
elem = cmsStageNext(elem);
......
......@@ -182,6 +182,10 @@ curve_type_to_str(enum weston_color_curve_type curve_type)
return "identity";
case WESTON_COLOR_CURVE_TYPE_LUT_3x1D:
return "3x1D LUT";
case WESTON_COLOR_CURVE_TYPE_LINPOW:
return "linpow";
case WESTON_COLOR_CURVE_TYPE_POWLIN:
return "powlin";
}
return "???";
}
......
......@@ -52,6 +52,50 @@ enum weston_color_curve_type {
/** Three-channel, one-dimensional look-up table */
WESTON_COLOR_CURVE_TYPE_LUT_3x1D,
/** Transfer function named LINPOW
*
* y = (a * x + b) ^ g | x >= d
* y = c * x | 0 <= x < d
*
* We gave it the name LINPOW because the first operation with the input
* x is a linear one, and then the result is raised to g.
*
* As all parametric curves, this one should be represented using struct
* weston_color_curve_parametric. For each color channel RGB we may have
* different params, see weston_color_curve_parametric::params.
*
* For LINPOW, the params g, a, b, c, and d are respectively
* params[channel][0], ... , params[channel][4].
*
* The input for all color channels may be clamped to [0.0, 1.0]. In
* such case, weston_color_curve_parametric::clamped_input is true.
* If the input is not clamped and LINPOW needs to evaluate a negative
* input value, it uses mirroring (i.e. -f(-x)).
*/
WESTON_COLOR_CURVE_TYPE_LINPOW,
/** Transfer function named POWLIN
*
* y = (a * (x ^ g)) + b | x >= d
* y = c * x | 0 <= x < d
*
* We gave it the name POWLIN because the first operation with the input
* x is an exponential one, and then the result is multiplied by a.
*
* As all parametric curves, this one should be represented using struct
* weston_color_curve_parametric. For each color channel RGB we may have
* different params, see weston_color_curve_parametric::params.
*
* For POWLIN, the params g, a, b, c, and d are respectively
* params[channel][0], ... , params[channel][4].
*
* The input for all color channels may be clamped to [0.0, 1.0]. In
* such case, weston_color_curve_parametric::clamped_input is true.
* If the input is not clamped and POWLIN needs to evaluate a negative
* input value, it uses mirroring (i.e. -f(-x)).
*/
WESTON_COLOR_CURVE_TYPE_POWLIN,
};
/** LUT_3x1D parameters */
......@@ -80,6 +124,17 @@ struct weston_color_curve_lut_3x1d {
unsigned optimal_len;
};
/** Parametric color curve parameters */
struct weston_color_curve_parametric {
/* For each color channel we may have different curves. For each of
* them, we can have up to 10 params, depending on the curve type. The
* channels are in RGB order. */
float params[3][10];
/* The input of the curve should be clamped from 0.0 to 1.0? */
bool clamped_input;
};
/**
* A scalar function for color encoding and decoding
*
......@@ -99,6 +154,7 @@ struct weston_color_curve {
union {
/* identity: no parameters */
struct weston_color_curve_lut_3x1d lut_3x1d;
struct weston_color_curve_parametric parametric;
} u;
};
......
......@@ -46,6 +46,8 @@
/* enum gl_shader_color_curve */
#define SHADER_COLOR_CURVE_IDENTITY 0
#define SHADER_COLOR_CURVE_LUT_3x1D 1
#define SHADER_COLOR_CURVE_LINPOW 2
#define SHADER_COLOR_CURVE_POWLIN 3
/* enum gl_shader_color_mapping */
#define SHADER_COLOR_MAPPING_IDENTITY 0
......@@ -124,10 +126,16 @@ uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float view_alpha;
uniform vec4 unicolor;
uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d;
uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset;
uniform HIGHPRECISION float color_pre_curve_params[30];
uniform bool color_pre_curve_clamped_input;
uniform HIGHPRECISION sampler2D color_post_curve_lut_2d;
uniform HIGHPRECISION vec2 color_post_curve_lut_scale_offset;
uniform HIGHPRECISION float color_post_curve_params[30];
uniform bool color_post_curve_clamped_input;
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
uniform HIGHPRECISION sampler3D color_mapping_lut_3d;
......@@ -212,6 +220,120 @@ sample_color_pre_curve_lut_2d(float x, compile_const int row)
vec2(tx, (float(row) + 0.5) / 4.0)).x;
}
float
linpow(float x, float g, float a, float b, float c, float d)
{
/* See WESTON_COLOR_CURVE_TYPE_LINPOW for details about LINPOW. */
if (x >= d)
return pow((a * x) + b, g);
return c * x;
}
float
powlin(float x, float g, float a, float b, float c, float d)
{
/* See WESTON_COLOR_CURVE_TYPE_POWLIN for details about POWLIN. */
if (x >= d)
return a * pow(x, g) + b;
return c * x;
}
float
sample_color_pre_curve_linpow(float x, compile_const int color_channel)
{
float g, a, b, c, d;
/* For each color channel we have 10 parameters. The params are
* linearized in an array of size 30, in RGB order. */
g = color_pre_curve_params[0 + (color_channel * 10)];
a = color_pre_curve_params[1 + (color_channel * 10)];
b = color_pre_curve_params[2 + (color_channel * 10)];
c = color_pre_curve_params[3 + (color_channel * 10)];
d = color_pre_curve_params[4 + (color_channel * 10)];
if (color_pre_curve_clamped_input)
x = clamp(x, 0.0, 1.0);
/* We use mirroring for negative input values. */
if (x < 0.0)
return -linpow(-x, g, a, b, c, d);
return linpow(x, g, a, b, c, d);
}
float
sample_color_pre_curve_powlin(float x, compile_const int color_channel)
{
float g, a, b, c, d;
/* For each color channel we have 10 parameters. The params are
* linearized in an array of size 30, in RGB order. */
g = color_pre_curve_params[0 + (color_channel * 10)];
a = color_pre_curve_params[1 + (color_channel * 10)];
b = color_pre_curve_params[2 + (color_channel * 10)];
c = color_pre_curve_params[3 + (color_channel * 10)];
d = color_pre_curve_params[4 + (color_channel * 10)];
if (color_pre_curve_clamped_input)
x = clamp(x, 0.0, 1.0);
/* We use mirroring for negative input values. */
if (x < 0.0)
return -powlin(-x, g, a, b, c, d);
return powlin(x, g, a, b, c, d);
}
float
sample_color_post_curve_linpow(float x, compile_const int color_channel)
{
float g, a, b, c, d;
/* For each color channel we have 10 parameters. The params are
* linearized in an array of size 30, in RGB order. */
g = color_post_curve_params[0 + (color_channel * 10)];
a = color_post_curve_params[1 + (color_channel * 10)];
b = color_post_curve_params[2 + (color_channel * 10)];
c = color_post_curve_params[3 + (color_channel * 10)];
d = color_post_curve_params[4 + (color_channel * 10)];
if (color_post_curve_clamped_input)
x = clamp(x, 0.0, 1.0);
/* We use mirroring for negative input values. */
if (x < 0.0)
return -linpow(-x, g, a, b, c, d);
return linpow(x, g, a, b, c, d);
}
float
sample_color_post_curve_powlin(float x, compile_const int color_channel)
{
float g, a, b, c, d;
/* For each color channel we have 10 parameters. The params are
* linearized in an array of size 30, in RGB order. */
g = color_post_curve_params[0 + (color_channel * 10)];
a = color_post_curve_params[1 + (color_channel * 10)];
b = color_post_curve_params[2 + (color_channel * 10)];
c = color_post_curve_params[3 + (color_channel * 10)];
d = color_post_curve_params[4 + (color_channel * 10)];
if (color_post_curve_clamped_input)
x = clamp(x, 0.0, 1.0);
/* We use mirroring for negative input values. */
if (x < 0.0)
return -powlin(-x, g, a, b, c, d);
return powlin(x, g, a, b, c, d);
}
vec3
color_pre_curve(vec3 color)
{
......@@ -224,6 +346,16 @@ color_pre_curve(vec3 color)
ret.g = sample_color_pre_curve_lut_2d(color.g, 1);
ret.b = sample_color_pre_curve_lut_2d(color.b, 2);
return ret;
} else if (c_color_pre_curve == SHADER_COLOR_CURVE_LINPOW) {
ret.r = sample_color_pre_curve_linpow(color.r, 0);
ret.g = sample_color_pre_curve_linpow(color.g, 1);
ret.b = sample_color_pre_curve_linpow(color.b, 2);
return ret;
} else if (c_color_pre_curve == SHADER_COLOR_CURVE_POWLIN) {
ret.r = sample_color_pre_curve_powlin(color.r, 0);
ret.g = sample_color_pre_curve_powlin(color.g, 1);
ret.b = sample_color_pre_curve_powlin(color.b, 2);
return ret;
} else {
/* Never reached, bad c_color_pre_curve. */
return vec3(1.0, 0.3, 1.0);
......@@ -275,6 +407,16 @@ color_post_curve(vec3 color)
ret.g = sample_color_post_curve_lut_2d(color.g, 1);
ret.b = sample_color_post_curve_lut_2d(color.b, 2);
return ret;
} else if (c_color_post_curve == SHADER_COLOR_CURVE_LINPOW) {
ret.r = sample_color_post_curve_linpow(color.r, 0);
ret.g = sample_color_post_curve_linpow(color.g, 1);
ret.b = sample_color_post_curve_linpow(color.b, 2);
return ret;
} else if (c_color_post_curve == SHADER_COLOR_CURVE_POWLIN) {
ret.r = sample_color_post_curve_powlin(color.r, 0);
ret.g = sample_color_post_curve_powlin(color.g, 1);
ret.b = sample_color_post_curve_powlin(color.b, 2);
return ret;
} else {
/* Never reached, bad c_color_post_curve. */
return vec3(1.0, 0.3, 1.0);
......
......@@ -61,6 +61,8 @@ enum gl_shader_texture_variant {
enum gl_shader_color_curve {
SHADER_COLOR_CURVE_IDENTITY = 0,
SHADER_COLOR_CURVE_LUT_3x1D,
SHADER_COLOR_CURVE_LINPOW,
SHADER_COLOR_CURVE_POWLIN,
};
/* Keep the following in sync with fragment.glsl. */
......@@ -87,14 +89,14 @@ struct gl_shader_requirements
bool input_is_premult:1;
bool green_tint:1;
unsigned color_pre_curve:1; /* enum gl_shader_color_curve */
unsigned color_pre_curve:2; /* enum gl_shader_color_curve */
unsigned color_mapping:2; /* enum gl_shader_color_mapping */
unsigned color_post_curve:1; /* enum gl_shader_color_curve */
unsigned color_post_curve:2; /* enum gl_shader_color_curve */
/*
* The total size of all bitfields plus pad_bits_ must fill up exactly
* how many bytes the compiler allocates for them together.
*/
unsigned pad_bits_:21;
unsigned pad_bits_:19;
};
static_assert(sizeof(struct gl_shader_requirements) ==
4 /* total bitfield size in bytes */,
......@@ -113,17 +115,36 @@ struct gl_shader_config {
GLfloat unicolor[4];
GLint input_tex_filter; /* GL_NEAREST or GL_LINEAR */
GLuint input_tex[GL_SHADER_INPUT_TEX_MAX];
GLuint color_pre_curve_lut_tex;
GLfloat color_pre_curve_lut_scale_offset[2];
union {
struct {
GLuint tex;
GLfloat scale_offset[2];
} lut_3x1d;
struct {
GLfloat params[3][10];
GLboolean clamped_input;
} parametric;
} color_pre_curve;
union {
struct {
GLuint tex;
GLuint tex;
GLfloat scale_offset[2];
} lut3d;
GLfloat matrix[9];
} color_mapping;
GLuint color_post_curve_lut_tex;
GLfloat color_post_curve_lut_scale_offset[2];
union {
struct {
GLuint tex;
GLfloat scale_offset[2];
} lut_3x1d;
struct {
GLfloat params[3][10];
GLboolean clamped_input;
} parametric;
} color_post_curve;
};
struct gl_renderer {
......
......@@ -41,9 +41,17 @@
struct gl_renderer_color_curve {
enum gl_shader_color_curve type;
GLuint tex;
float scale;
float offset;
union {
struct {
GLuint tex;
float scale;
float offset;
} lut_3x1d;
struct {
GLfloat params[3][10];
GLboolean clamped_input;
} parametric;
} u;
};
struct gl_renderer_color_mapping {
......@@ -69,8 +77,15 @@ struct gl_renderer_color_transform {
static void
gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve)
{
if (gl_curve->tex)
glDeleteTextures(1, &gl_curve->tex);
switch (gl_curve->type) {
case SHADER_COLOR_CURVE_IDENTITY:
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
glDeleteTextures(1, &gl_curve->u.lut_3x1d.tex);
break;
};
}
static void
......@@ -132,6 +147,37 @@ gl_renderer_color_transform_get(struct weston_color_transform *xform)
destroy_listener);
}
static void
gl_color_curve_parametric(struct gl_renderer_color_curve *gl_curve,
const struct weston_color_curve *curve)
{
const struct weston_color_curve_parametric *parametric = &curve->u.parametric;
ARRAY_COPY(gl_curve->u.parametric.params, parametric->params);
gl_curve->u.parametric.clamped_input = parametric->clamped_input;
}
static bool
gl_color_curve_linpow(struct gl_renderer_color_curve *gl_curve,
const struct weston_color_curve *curve)
{
gl_curve->type = SHADER_COLOR_CURVE_LINPOW;
gl_color_curve_parametric(gl_curve, curve);
return true;
}
static bool
gl_color_curve_powlin(struct gl_renderer_color_curve *gl_curve,
const struct weston_color_curve *curve)
{
gl_curve->type = SHADER_COLOR_CURVE_POWLIN;
gl_color_curve_parametric(gl_curve, curve);
return true;
}
static bool
gl_color_curve_lut_3x1d(struct gl_renderer *gr,
struct gl_renderer_color_curve *gl_curve,
......@@ -173,9 +219,9 @@ gl_color_curve_lut_3x1d(struct gl_renderer *gr,
free(lut);
gl_curve->type = SHADER_COLOR_CURVE_LUT_3x1D;
gl_curve->tex = tex;
gl_curve->scale = (float)(lut_len - 1) / lut_len;
gl_curve->offset = 0.5f / lut_len;
gl_curve->u.lut_3x1d.tex = tex;
gl_curve->u.lut_3x1d.scale = (float)(lut_len - 1) / lut_len;
gl_curve->u.lut_3x1d.offset = 0.5f / lut_len;
return true;
}
......@@ -228,14 +274,8 @@ gl_renderer_color_transform_from(struct gl_renderer *gr,
{
static const struct gl_renderer_color_transform no_op_gl_xform = {
.pre_curve.type = SHADER_COLOR_CURVE_IDENTITY,
.pre_curve.tex = 0,
.pre_curve.scale = 0.0f,
.pre_curve.offset = 0.0f,
.mapping.type = SHADER_COLOR_MAPPING_IDENTITY,
.post_curve.type = SHADER_COLOR_CURVE_IDENTITY,
.post_curve.tex = 0,
.post_curve.scale = 0.0f,
.post_curve.offset = 0.0f,
};
struct gl_renderer_color_transform *gl_xform;
bool ok = false;
......@@ -264,12 +304,20 @@ gl_renderer_color_transform_from(struct gl_renderer *gr,
ok = gl_color_curve_lut_3x1d(gr, &gl_xform->pre_curve,
&xform->pre_curve, xform);
break;
case WESTON_COLOR_CURVE_TYPE_LINPOW:
ok = gl_color_curve_linpow(&gl_xform->pre_curve,
&xform->pre_curve);
break;
case WESTON_COLOR_CURVE_TYPE_POWLIN:
ok = gl_color_curve_powlin(&gl_xform->pre_curve,
&xform->pre_curve);
break;
}
if (!ok) {
gl_renderer_color_transform_destroy(gl_xform);
return NULL;
}
switch (xform->mapping.type) {
case WESTON_COLOR_MAPPING_TYPE_IDENTITY:
gl_xform->mapping = no_op_gl_xform.mapping;
......@@ -288,6 +336,7 @@ gl_renderer_color_transform_from(struct gl_renderer *gr,
gl_renderer_color_transform_destroy(gl_xform);
return NULL;
}
switch (xform->post_curve.type) {
case WESTON_COLOR_CURVE_TYPE_IDENTITY:
gl_xform->post_curve = no_op_gl_xform.post_curve;
......@@ -297,6 +346,14 @@ gl_renderer_color_transform_from(struct gl_renderer *gr,
ok = gl_color_curve_lut_3x1d(gr, &gl_xform->post_curve,
&xform->post_curve, xform);
break;
case WESTON_COLOR_CURVE_TYPE_LINPOW:
ok = gl_color_curve_linpow(&gl_xform->post_curve,
&xform->post_curve);
break;
case WESTON_COLOR_CURVE_TYPE_POWLIN:
ok = gl_color_curve_powlin(&gl_xform->post_curve,
&xform->post_curve);
break;
}
if (!ok) {
gl_renderer_color_transform_destroy(gl_xform);
......@@ -319,14 +376,42 @@ gl_shader_config_set_color_transform(struct gl_renderer *gr,
return false;
sconf->req.color_pre_curve = gl_xform->pre_curve.type;
sconf->color_pre_curve_lut_tex = gl_xform->pre_curve.tex;
sconf->color_pre_curve_lut_scale_offset[0] = gl_xform->pre_curve.scale;
sconf->color_pre_curve_lut_scale_offset[1] = gl_xform->pre_curve.offset;
switch (gl_xform->pre_curve.type) {
case SHADER_COLOR_CURVE_IDENTITY:
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
sconf->color_pre_curve.lut_3x1d.tex = gl_xform->pre_curve.u.lut_3x1d.tex;
sconf->color_pre_curve.lut_3x1d.scale_offset[0] = gl_xform->pre_curve.u.lut_3x1d.scale;
sconf->color_pre_curve.lut_3x1d.scale_offset[1] = gl_xform->pre_curve.u.lut_3x1d.offset;
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
memcpy(sconf->color_pre_curve.parametric.params,
gl_xform->pre_curve.u.parametric.params,
sizeof(sconf->color_pre_curve.parametric.params));
sconf->color_pre_curve.parametric.clamped_input =
gl_xform->pre_curve.u.parametric.clamped_input;
break;
}
sconf->req.color_post_curve = gl_xform->post_curve.type;
sconf->color_post_curve_lut_tex = gl_xform->post_curve.tex;
sconf->color_post_curve_lut_scale_offset[0] = gl_xform->post_curve.scale;
sconf->color_post_curve_lut_scale_offset[1] = gl_xform->post_curve.offset;
switch (gl_xform->post_curve.type) {
case SHADER_COLOR_CURVE_IDENTITY:
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
sconf->color_post_curve.lut_3x1d.tex = gl_xform->post_curve.u.lut_3x1d.tex;
sconf->color_post_curve.lut_3x1d.scale_offset[0] = gl_xform->post_curve.u.lut_3x1d.scale;
sconf->color_post_curve.lut_3x1d.scale_offset[1] = gl_xform->post_curve.u.lut_3x1d.offset;
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
memcpy(&sconf->color_post_curve.parametric.params,
&gl_xform->post_curve.u.parametric.params,
sizeof(sconf->color_post_curve.parametric.params));
sconf->color_post_curve.parametric.clamped_input =
gl_xform->post_curve.u.parametric.clamped_input;
break;
}
sconf->req.color_mapping = gl_xform->mapping.type;
switch (gl_xform->mapping.type) {
......
......@@ -52,6 +52,8 @@
#include "fragment-shader.h"
struct gl_shader {
struct wl_list link; /* gl_renderer::shader_list */
struct timespec last_used;
struct gl_shader_requirements key;
GLuint program;
GLuint vertex_shader, fragment_shader;
......@@ -60,8 +62,16 @@ struct gl_shader {
GLint tex_uniforms[3];
GLint view_alpha_uniform;
GLint color_uniform;
GLint color_pre_curve_lut_2d_uniform;
GLint color_pre_curve_lut_scale_offset_uniform;
union {
struct {
GLint tex_2d_uniform;
GLint scale_offset_uniform;
} lut_3x1d;
struct {
GLint params_uniform;
GLint clamped_input_uniform;
} parametric;
} color_pre_curve;
union {
struct {
GLint tex_uniform;
......@@ -69,10 +79,16 @@ struct gl_shader {
} lut3d;
GLint matrix_uniform;
} color_mapping;
GLint color_post_curve_lut_2d_uniform;
GLint color_post_curve_lut_scale_offset_uniform;
struct wl_list link; /* gl_renderer::shader_list */
struct timespec last_used;
union {
struct {
GLint tex_2d_uniform;
GLint scale_offset_uniform;
} lut_3x1d;
struct {
GLint params_uniform;
GLint clamped_input_uniform;
} parametric;
} color_post_curve;
};
static const char *
......@@ -115,6 +131,8 @@ gl_shader_color_curve_to_string(enum gl_shader_color_curve kind)
#define CASERET(x) case x: return #x;
CASERET(SHADER_COLOR_CURVE_IDENTITY)
CASERET(SHADER_COLOR_CURVE_LUT_3x1D)
CASERET(SHADER_COLOR_CURVE_LINPOW)
CASERET(SHADER_COLOR_CURVE_POWLIN)
#undef CASERET
}
......@@ -336,15 +354,42 @@ gl_shader_create(struct gl_renderer *gr,
} else {
shader->color_uniform = -1;
}
shader->color_pre_curve_lut_2d_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_2d");
shader->color_pre_curve_lut_scale_offset_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset");
shader->color_post_curve_lut_2d_uniform =
glGetUniformLocation(shader->program, "color_post_curve_lut_2d");
shader->color_post_curve_lut_scale_offset_uniform =
glGetUniformLocation(shader->program, "color_post_curve_lut_scale_offset");
switch(requirements->color_pre_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
shader->color_pre_curve.parametric.params_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_params");
shader->color_pre_curve.parametric.clamped_input_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_clamped_input");
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
shader->color_pre_curve.lut_3x1d.tex_2d_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_2d");
shader->color_pre_curve.lut_3x1d.scale_offset_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset");
break;
}
switch(requirements->color_post_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
shader->color_post_curve.parametric.params_uniform =
glGetUniformLocation(shader->program, "color_post_curve_params");
shader->color_post_curve.parametric.clamped_input_uniform =
glGetUniformLocation(shader->program, "color_post_curve_clamped_input");
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
shader->color_post_curve.lut_3x1d.tex_2d_uniform =
glGetUniformLocation(shader->program, "color_post_curve_lut_2d");
shader->color_post_curve.lut_3x1d.scale_offset_uniform =
glGetUniformLocation(shader->program, "color_post_curve_lut_scale_offset");
break;
}
switch(requirements->color_mapping) {
case SHADER_COLOR_MAPPING_3DLUT:
......@@ -574,6 +619,7 @@ gl_shader_load_config(struct gl_shader *shader,
{
GLint in_filter = sconf->input_tex_filter;
GLenum in_tgt;
GLsizei n_params;
int i;
glUniformMatrix4fv(shader->proj_uniform,
......@@ -606,18 +652,25 @@ gl_shader_load_config(struct gl_shader *shader,
i = GL_SHADER_INPUT_TEX_MAX;
switch (sconf->req.color_pre_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
assert(sconf->color_pre_curve_lut_tex == 0);
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
assert(sconf->color_pre_curve_lut_tex != 0);
assert(shader->color_pre_curve_lut_2d_uniform != -1);
assert(shader->color_pre_curve_lut_scale_offset_uniform != -1);
assert(sconf->color_pre_curve.lut_3x1d.tex != 0);
assert(shader->color_pre_curve.lut_3x1d.tex_2d_uniform != -1);
assert(shader->color_pre_curve.lut_3x1d.scale_offset_uniform != -1);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve_lut_tex);
glUniform1i(shader->color_pre_curve_lut_2d_uniform, i);
glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve.lut_3x1d.tex);
glUniform1i(shader->color_pre_curve.lut_3x1d.tex_2d_uniform, i);
i++;
glUniform2fv(shader->color_pre_curve_lut_scale_offset_uniform,
1, sconf->color_pre_curve_lut_scale_offset);
glUniform2fv(shader->color_pre_curve.lut_3x1d.scale_offset_uniform,
1, sconf->color_pre_curve.lut_3x1d.scale_offset);
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
n_params = sizeof(sconf->color_pre_curve.parametric.params) / sizeof(GLfloat);
glUniform1fv(shader->color_pre_curve.parametric.params_uniform, n_params,
&sconf->color_pre_curve.parametric.params[0][0]);
glUniform1i(shader->color_pre_curve.parametric.clamped_input_uniform,
sconf->color_pre_curve.parametric.clamped_input);
break;
}
......@@ -645,18 +698,25 @@ gl_shader_load_config(struct gl_shader *shader,
switch (sconf->req.color_post_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
assert(sconf->color_post_curve_lut_tex == 0);
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
assert(sconf->color_post_curve_lut_tex != 0);
assert(shader->color_post_curve_lut_2d_uniform != -1);
assert(shader->color_post_curve_lut_scale_offset_uniform != -1);
assert(sconf->color_post_curve.lut_3x1d.tex != 0);
assert(shader->color_post_curve.lut_3x1d.tex_2d_uniform != -1);
assert(shader->color_post_curve.lut_3x1d.scale_offset_uniform != -1);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, sconf->color_post_curve_lut_tex);
glUniform1i(shader->color_post_curve_lut_2d_uniform, i);
glBindTexture(GL_TEXTURE_2D, sconf->color_post_curve.lut_3x1d.tex);
glUniform1i(shader->color_post_curve.lut_3x1d.tex_2d_uniform, i);
i++;
glUniform2fv(shader->color_post_curve_lut_scale_offset_uniform,
1, sconf->color_post_curve_lut_scale_offset);
glUniform2fv(shader->color_post_curve.lut_3x1d.scale_offset_uniform,
1, sconf->color_post_curve.lut_3x1d.scale_offset);
break;
case SHADER_COLOR_CURVE_LINPOW:
case SHADER_COLOR_CURVE_POWLIN:
n_params = sizeof(sconf->color_post_curve.parametric.params) / sizeof(GLfloat);
glUniform1fv(shader->color_post_curve.parametric.params_uniform, n_params,
&sconf->color_post_curve.parametric.params[0][0]);
glUniform1i(shader->color_post_curve.parametric.clamped_input_uniform,
sconf->color_post_curve.parametric.clamped_input);
break;
}
}
......
......@@ -97,6 +97,9 @@ do { \
#define weston_assert_double_eq(compositor, a, b) \
weston_assert_(compositor, a, b, double, "%.10g", ==)
#define weston_assert_uint32_eq(compositor, a, b) \
weston_assert_(compositor, a, b, uint32_t, "%u", ==)
#define weston_assert_uint32_neq(compositor, a, b) \
weston_assert_(compositor, a, b, uint32_t, "%u", !=)
......@@ -109,6 +112,9 @@ do { \
#define weston_assert_uint32_lt(compositor, a, b) \
weston_assert_(compositor, a, b, uint32_t, "%u", <)
#define weston_assert_uint64_eq(compositor, a, b) \
weston_assert_(compositor, a, b, uint64_t, "%" PRIx64, ==)
#define weston_assert_str_eq(compositor, a, b) \
weston_assert_fn_(compositor, strcmp, a, b, const char *, "%s", ==)
......
......@@ -132,4 +132,8 @@ TEST(asserts)
abort_if_not(ret);
ret = weston_assert_bit_is_set(compositor, bitfield, 57);
abort_if_not(ret == false);
uint64_t max_uint64 = UINT64_MAX;
ret = weston_assert_uint64_eq(compositor, max_uint64, 0);
abort_if_not(ret == false);
}
......@@ -136,9 +136,9 @@ static const struct setup_args my_setup_args[] = {
/* name, ref img, pipeline, tolerance, dim, profile type, clut tolerance, vcgt_exponents */
{ { "sRGB->sRGB MAT" }, 0, &pipeline_sRGB, 0.0, 0, PTYPE_MATRIX_SHAPER },
{ { "sRGB->sRGB MAT VCGT" }, 3, &pipeline_sRGB, 0.8, 0, PTYPE_MATRIX_SHAPER, 0.0000, {1.1, 1.2, 1.3} },
{ { "sRGB->adobeRGB MAT" }, 1, &pipeline_adobeRGB, 1.4, 0, PTYPE_MATRIX_SHAPER },
{ { "sRGB->adobeRGB MAT" }, 1, &pipeline_adobeRGB, 1.6, 0, PTYPE_MATRIX_SHAPER },
{ { "sRGB->adobeRGB MAT VCGT" }, 4, &pipeline_adobeRGB, 1.0, 0, PTYPE_MATRIX_SHAPER, 0.0000, {1.1, 1.2, 1.3} },
{ { "sRGB->BT2020 MAT" }, 2, &pipeline_BT2020, 4.5, 0, PTYPE_MATRIX_SHAPER },
{ { "sRGB->BT2020 MAT" }, 2, &pipeline_BT2020, 1.1, 0, PTYPE_MATRIX_SHAPER },
{ { "sRGB->sRGB CLUT" }, 0, &pipeline_sRGB, 0.0, 17, PTYPE_CLUT, 0.0005 },
{ { "sRGB->sRGB CLUT VCGT" }, 3, &pipeline_sRGB, 0.9, 17, PTYPE_CLUT, 0.0005, {1.1, 1.2, 1.3} },
{ { "sRGB->adobeRGB CLUT" }, 1, &pipeline_adobeRGB, 1.8, 17, PTYPE_CLUT, 0.0065 },
......@@ -225,6 +225,17 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
struct compositor_setup setup;
char *file_name;
#if !HAVE_CMS_GET_TONE_CURVE_SEGMENT
/* When cmsGetToneCurveSegment() is at disposal, Weston is able to
* inspect the LittleCMS color curves and convert them to Weston's
* internal representation of color curves. In such case, we don't need
* to fallback to a more generic solution (usage of LUT's), which is
* less precise. Thanks to that, we are able to decrease the
* tolerance in this test. We already have cmsGetToneCurveSegment() in
* our CI, so simply skip this test when this is not available. */
return RESULT_SKIP;
#endif
cmsSetLogErrorHandler(test_lcms_error_logger);
compositor_setup_defaults(&setup);
......