diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index 1726553c8..d99b7c77a 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -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]; diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index 318cd31e5..45e6c3b75 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -464,11 +464,339 @@ merge_curvesets(cmsPipeline **lut, cmsContext context_id) return modified; } +static bool +linpow_from_type_1(struct weston_compositor *compositor, + struct weston_color_curve *curve, + const float type_1_params[3][10], bool clamped_input) +{ + struct weston_color_curve_parametric *parametric = &curve->u.parametric; + unsigned int i; + + curve->type = WESTON_COLOR_CURVE_TYPE_LINPOW; + + 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, @@ -521,6 +849,12 @@ translate_curve_element(struct cmlcms_color_transform *xform, 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); } diff --git a/libweston/color.c b/libweston/color.c index df4033336..883a73260 100644 --- a/libweston/color.c +++ b/libweston/color.c @@ -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 "???"; } diff --git a/libweston/color.h b/libweston/color.h index e0a40f5ec..854f84fd1 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -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; }; diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 1ea95b2c1..b19d04274 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -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); diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 9d409c7a6..80a75bdc2 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -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 */, @@ -119,6 +121,10 @@ struct gl_shader_config { GLuint tex; GLfloat scale_offset[2]; } lut_3x1d; + struct { + GLfloat params[3][10]; + GLboolean clamped_input; + } parametric; } color_pre_curve; union { @@ -134,6 +140,10 @@ struct gl_shader_config { GLuint tex; GLfloat scale_offset[2]; } lut_3x1d; + struct { + GLfloat params[3][10]; + GLboolean clamped_input; + } parametric; } color_post_curve; }; diff --git a/libweston/renderer-gl/gl-shader-config-color-transformation.c b/libweston/renderer-gl/gl-shader-config-color-transformation.c index 49224d3d1..d550cefd5 100644 --- a/libweston/renderer-gl/gl-shader-config-color-transformation.c +++ b/libweston/renderer-gl/gl-shader-config-color-transformation.c @@ -47,6 +47,10 @@ struct gl_renderer_color_curve { float scale; float offset; } lut_3x1d; + struct { + GLfloat params[3][10]; + GLboolean clamped_input; + } parametric; } u; }; @@ -75,6 +79,8 @@ gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve) { 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); @@ -141,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, @@ -267,6 +304,14 @@ 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); @@ -301,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); @@ -331,6 +384,14 @@ gl_shader_config_set_color_transform(struct gl_renderer *gr, 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; @@ -342,6 +403,14 @@ gl_shader_config_set_color_transform(struct gl_renderer *gr, 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; diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 999814d2c..1f4b8a833 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -67,6 +67,10 @@ struct gl_shader { GLint tex_2d_uniform; GLint scale_offset_uniform; } lut_3x1d; + struct { + GLint params_uniform; + GLint clamped_input_uniform; + } parametric; } color_pre_curve; union { struct { @@ -80,6 +84,10 @@ struct gl_shader { GLint tex_2d_uniform; GLint scale_offset_uniform; } lut_3x1d; + struct { + GLint params_uniform; + GLint clamped_input_uniform; + } parametric; } color_post_curve; }; @@ -123,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 } @@ -348,6 +358,13 @@ gl_shader_create(struct gl_renderer *gr, 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"); @@ -359,6 +376,13 @@ gl_shader_create(struct gl_renderer *gr, 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"); @@ -595,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, @@ -639,6 +664,14 @@ gl_shader_load_config(struct gl_shader *shader, 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; } switch (sconf->req.color_mapping) { @@ -677,6 +710,14 @@ gl_shader_load_config(struct gl_shader *shader, 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; } } diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 700da1418..38bdc354e 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -136,7 +136,7 @@ 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->sRGB CLUT" }, 0, &pipeline_sRGB, 0.0, 17, PTYPE_CLUT, 0.0005 },