Skip to content
Snippets Groups Projects

Gamma correct blending with sRGB

Merged Pekka Paalanen requested to merge pq/weston:mr/color-arch into main
4 files
+ 139
3
Compare changes
  • Side-by-side
  • Inline
Files
4
  • 4345be90
    This adds shader support for using a three-channel one-dimensional
    look-up table for mapping input colors. This operation will be useful
    for applying EOTF or its inverse, in other words, gamma curves.
    
    Even though called three-channel and one-dimensional, it is actually
    implemented as a one-channel two-dimensional texture with four rows.
    Each row corresponds to a source color channel except the fourth one is
    unused. The reason for having the fourth row is to get texture
    coordinates in 1/8 steps instead of 1/6 steps. 1/6 may would not be
    exact in floating- or fixed-point arithmetic and might perhaps risk
    unintended results from bilinear texture filtering when we want linear
    filtering only in x but not in y texture coordinates. I may be paranoid.
    
    The LUT is applied on source colors after they have been converted to
    straight RGB. It cannot be applied with pre-multiplied alpha. A LUT can
    be used for both applying EOTF to go from source color space to blending
    color space, and EOTF^-1 to go from blending space to output
    (electrical) space. However, this type of LUT cannot do color space
    conversions.
    
    For now, this feature is hardcoded to off everywhere, to be enabled in
    following patches.
    
    Signed-off-by: default avatarPekka Paalanen <pekka.paalanen@collabora.com>
@@ -42,6 +42,10 @@
#define SHADER_VARIANT_SOLID 7
#define SHADER_VARIANT_EXTERNAL 8
/* enum gl_shader_source_lut */
#define SHADER_SOURCE_LUT_NONE 0
#define SHADER_SOURCE_LUT_3x1D 1
#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
#extension GL_OES_EGL_image_external : require
#endif
@@ -61,6 +65,13 @@ precision HIGHPRECISION float;
compile_const int c_variant = DEF_VARIANT;
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
compile_const bool c_green_tint = DEF_GREEN_TINT;
compile_const int c_source_lut = DEF_SOURCE_LUT;
bool
need_straight_alpha_pipeline()
{
return c_source_lut != SHADER_SOURCE_LUT_NONE;
}
vec4
yuva2rgba(vec4 yuva)
@@ -102,6 +113,8 @@ uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float alpha;
uniform vec4 unicolor;
uniform HIGHPRECISION sampler2D source_lut_2d;
uniform HIGHPRECISION vec2 source_lut_scale_offset;
vec4
sample_input_texture()
@@ -147,6 +160,69 @@ sample_input_texture()
return yuva2rgba(yuva);
}
/*
* Texture coordinates go from 0.0 to 1.0 corresponding to texture edges.
* When we do LUT look-ups with linear filtering, the correct range to sample
* from is not from edge to edge, but center of first texel to center of last
* texel. This follows because with LUTs, you have the exact end points given,
* you never extrapolate but only interpolate.
* The scale and offset are precomputed to achieve this mapping.
*/
float
lut_texcoord(float x, vec2 scale_offset)
{
return x * scale_offset.s + scale_offset.t;
}
/*
* Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has
* four rows so that the centers of texels have precise y-coordinates.
*/
float
sample_source_lut_2d(float x, compile_const int row)
{
float tx = lut_texcoord(x, source_lut_scale_offset);
return texture2D(source_lut_2d, vec2(tx, (float(row) + 0.5) / 4.0)).x;
}
vec3
source_lut_filter(vec3 color)
{
vec3 ret;
if (c_source_lut == SHADER_SOURCE_LUT_NONE) {
return color;
} else if (c_source_lut == SHADER_SOURCE_LUT_3x1D) {
ret.r = sample_source_lut_2d(color.r, 0);
ret.g = sample_source_lut_2d(color.g, 1);
ret.b = sample_source_lut_2d(color.b, 2);
return ret;
} else {
/* Never reached, bad c_source_lut. */
return vec3(1.0, 0.3, 1.0);
}
}
vec4
pipeline_straight(vec4 color, compile_const bool input_is_premult)
{
/* Ensure straight alpha */
if (input_is_premult) {
if (color.a == 0.0)
color.rgb = vec3(0, 0, 0);
else
color.rgb *= 1.0 / color.a;
}
/* View alpha (opacity) */
color.a *= alpha;
color.rgb = source_lut_filter(color.rgb);
return color;
}
vec4
pipeline_premult(vec4 color, compile_const bool input_is_premult)
{
@@ -164,12 +240,18 @@ pipeline_premult(vec4 color, compile_const bool input_is_premult)
void
main()
{
bool premult;
vec4 color;
/* Electrical (non-linear) RGBA values, may be premult or not */
color = sample_input_texture();
color = pipeline_premult(color, c_input_is_premult);
if (need_straight_alpha_pipeline()) {
color = pipeline_straight(color, c_input_is_premult);
color.rgb *= color.a;
} else {
color = pipeline_premult(color, c_input_is_premult);
}
/* color is guaranteed premult here */
Loading