gsk: Add a cicp convert shader

This shader receives cicp parameters via uniforms, and converts
the texture data from or to the output colorstate. It computes
the matrix in the vertex shader, and then picks the eotf/oetf
according to the cicp parameters in the fragment shader.
This commit is contained in:
Matthias Clasen 2024-07-16 23:51:57 -04:00
parent 64f4967867
commit 226652edb0
6 changed files with 516 additions and 19 deletions

View File

@ -0,0 +1,119 @@
#include "config.h"
#include "gskgpuconvertcicpopprivate.h"
#include "gskgpuframeprivate.h"
#include "gskgpuprintprivate.h"
#include "gskrectprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include "gpu/shaders/gskgpuconvertcicpinstance.h"
typedef struct _GskGpuConvertCicpOp GskGpuConvertCicpOp;
struct _GskGpuConvertCicpOp
{
GskGpuShaderOp op;
};
#define VARIATION_OPACITY (1u << 0)
#define VARIATION_STRAIGHT_ALPHA (1u << 1)
#define VARIATION_REVERSE (1u << 2)
static void
gsk_gpu_convert_cicp_op_print_instance (GskGpuShaderOp *shader,
gpointer instance_,
GString *string)
{
GskGpuConvertcicpInstance *instance = (GskGpuConvertcicpInstance *) instance_;
gsk_gpu_print_rect (string, instance->rect);
gsk_gpu_print_image (string, shader->images[0]);
if (shader->variation & VARIATION_STRAIGHT_ALPHA)
gsk_gpu_print_string (string, "straight");
g_string_append_printf (string, "cicp %u/%u/%u/%u",
instance->color_primaries,
instance->transfer_function,
0, 1);
}
static const GskGpuShaderOpClass GSK_GPU_CONVERT_OP_CLASS = {
{
GSK_GPU_OP_SIZE (GskGpuConvertCicpOp),
GSK_GPU_STAGE_SHADER,
gsk_gpu_shader_op_finish,
gsk_gpu_shader_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_shader_op_vk_command,
#endif
gsk_gpu_shader_op_gl_command
},
"gskgpuconvertcicp",
gsk_gpu_convertcicp_n_textures,
sizeof (GskGpuConvertcicpInstance),
#ifdef GDK_RENDERING_VULKAN
&gsk_gpu_convertcicp_info,
#endif
gsk_gpu_convert_cicp_op_print_instance,
gsk_gpu_convertcicp_setup_attrib_locations,
gsk_gpu_convertcicp_setup_vao
};
void
gsk_gpu_convert_from_cicp_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
const GdkCicp *cicp,
GskGpuColorStates color_states,
float opacity,
gboolean straight_alpha,
const graphene_point_t *offset,
const GskGpuShaderImage *image)
{
GskGpuConvertcicpInstance *instance;
gsk_gpu_shader_op_alloc (frame,
&GSK_GPU_CONVERT_OP_CLASS,
color_states,
(opacity < 1.0 ? VARIATION_OPACITY : 0) |
(straight_alpha ? VARIATION_STRAIGHT_ALPHA : 0),
clip,
(GskGpuImage *[1]) { image->image },
(GskGpuSampler[1]) { image->sampler },
&instance);
gsk_gpu_rect_to_float (image->coverage, offset, instance->rect);
gsk_gpu_rect_to_float (image->bounds, offset, instance->tex_rect);
instance->opacity = opacity;
instance->color_primaries = cicp->color_primaries;
instance->transfer_function = cicp->transfer_function;
}
void
gsk_gpu_convert_to_cicp_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
const GdkCicp *cicp,
GskGpuColorStates color_states,
float opacity,
gboolean straight_alpha,
const graphene_point_t *offset,
const GskGpuShaderImage *image)
{
GskGpuConvertcicpInstance *instance;
gsk_gpu_shader_op_alloc (frame,
&GSK_GPU_CONVERT_OP_CLASS,
color_states,
(opacity < 1.0 ? VARIATION_OPACITY : 0) |
(straight_alpha ? VARIATION_STRAIGHT_ALPHA : 0) |
VARIATION_REVERSE,
clip,
(GskGpuImage *[1]) { image->image },
(GskGpuSampler[1]) { image->sampler },
&instance);
gsk_gpu_rect_to_float (image->coverage, offset, instance->rect);
gsk_gpu_rect_to_float (image->bounds, offset, instance->tex_rect);
instance->opacity = opacity;
instance->color_primaries = cicp->color_primaries;
instance->transfer_function = cicp->transfer_function;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "gskgpushaderopprivate.h"
#include <graphene.h>
G_BEGIN_DECLS
void gsk_gpu_convert_from_cicp_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
const GdkCicp *cicp,
GskGpuColorStates color_states,
float opacity,
gboolean straight_alpha,
const graphene_point_t *offset,
const GskGpuShaderImage *image);
void gsk_gpu_convert_to_cicp_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
const GdkCicp *cicp,
GskGpuColorStates color_states,
float opacity,
gboolean straight_alpha,
const graphene_point_t *offset,
const GskGpuShaderImage *image);
G_END_DECLS

View File

@ -24,6 +24,12 @@ color_unpremultiply (vec4 color)
return color.a > 0.0 ? color / vec4 (color.aaa, 1.0) : color;
}
float
luminance (vec3 color)
{
return dot (vec3 (0.2126, 0.7152, 0.0722), color);
}
vec4
alt_color_alpha (vec4 color,
float alpha)
@ -90,6 +96,20 @@ pq_oetf (float v)
return pow (((c1 + (c2 * pow (x, n))) / (1.0 + (c3 * pow (x, n)))), m);
}
/* Note that these matrices are transposed from the C version */
const mat3 srgb_from_rec2020 = mat3(
1.659944, -0.124350, -0.018466,
-0.588220, 1.132559, -0.102459,
-0.071724, -0.008210, 1.120924
);
const mat3 rec2020_from_srgb = mat3(
0.627610, 0.069029, 0.016649,
0.329815, 0.919817, 0.089510,
0.042574, 0.011154, 0.893842
);
vec3
apply_eotf (vec3 color,
uint cs)
@ -140,19 +160,6 @@ apply_oetf (vec3 color,
}
}
/* Note that these matrices are transposed from the C version */
const mat3 srgb_from_rec2020 = mat3(
1.659944, -0.124350, -0.018466,
-0.588220, 1.132559, -0.102459,
-0.071724, -0.008210, 1.120924
);
const mat3 rec2020_from_srgb = mat3(
0.627610, 0.069029, 0.016649,
0.329815, 0.919817, 0.089510,
0.042574, 0.011154, 0.893842
);
vec3
convert_linear (vec3 color,
uint from,
@ -226,10 +233,4 @@ output_color_from_alt (vec4 color)
OUTPUT_COLOR_SPACE, OUTPUT_PREMULTIPLIED);
}
float
luminance (vec3 color)
{
return dot (vec3 (0.2126, 0.7152, 0.0722), color);
}
#endif /* _COLOR_ */

View File

@ -0,0 +1,346 @@
#define GSK_N_TEXTURES 1
#include "common.glsl"
#define VARIATION_OPACITY (1u << 0)
#define VARIATION_STRAIGHT_ALPHA (1u << 1)
#define VARIATION_REVERSE (1u << 2)
#define HAS_VARIATION(var) ((GSK_VARIATION & var) == var)
PASS(0) vec2 _pos;
PASS_FLAT(1) Rect _rect;
PASS(2) vec2 _tex_coord;
PASS_FLAT(3) float _opacity;
PASS_FLAT(4) uint _transfer_function;
PASS_FLAT(5) mat3 _mat;
#ifdef GSK_VERTEX_SHADER
IN(0) vec4 in_rect;
IN(1) vec4 in_tex_rect;
IN(2) float in_opacity;
IN(3) uint in_color_primaries;
IN(4) uint in_transfer_function;
const mat3 identity = mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
const mat3 srgb_to_xyz = mat3(
0.4125288, 0.2127102, 0.0193373,
0.3581642, 0.7163284, 0.1193881,
0.1774037, 0.0709615, 0.9343260
);
const mat3 xyz_to_srgb = mat3(
3.2409699, -0.9692436, 0.0556301,
-1.5373832, 1.8759675, -0.2039770,
-0.4986108, 0.0415551, 1.0569715
);
const mat3 rec2020_to_xyz = mat3(
0.6369615, 0.2627016, 0.0000000,
0.1448079, 0.6788934, 0.0281098,
0.1663273, 0.0584050, 1.0449416
);
const mat3 xyz_to_rec2020 = mat3(
1.7166512, -0.6666844, 0.0176399,
-0.3556708, 1.6164812, -0.0427706,
-0.2533663, 0.0157685, 0.9421031
);
const mat3 pal_to_xyz = mat3(
0.4305538, 0.2220043, 0.0201822,
0.3415498, 0.7066548, 0.1295534,
0.1783523, 0.0713409, 0.9393222
);
const mat3 xyz_to_pal = mat3(
3.0633611, -0.9692436, 0.0678610,
-1.3933902, 1.8759675, -0.2287993,
-0.4758237, 0.0415551, 1.0690896
);
const mat3 ntsc_to_xyz = mat3(
0.3935209, 0.2123764, 0.0187391,
0.3652581, 0.7010599, 0.1119339,
0.1916769, 0.0865638, 0.9583847
);
const mat3 xyz_to_ntsc = mat3(
3.5060033, -1.0690476, 0.0563066,
-1.7397907, 1.9777789, -0.1969757,
-0.5440583, 0.0351714, 1.0499523
);
const mat3 p3_to_xyz = mat3(
0.4865709, 0.2289746, 0.0000000,
0.2656677, 0.6917385, 0.0451134,
0.1982173, 0.0792869, 1.0439444
);
const mat3 xyz_to_p3 = mat3(
2.4934969, -0.8294890, 0.0358458,
-0.9313836, 1.7626641, -0.0761724,
-0.4027108, 0.0236247, 0.9568845
);
mat3
cicp_to_xyz (uint cp)
{
switch (cp)
{
case 1: return srgb_to_xyz;
case 5: return pal_to_xyz;
case 6: return ntsc_to_xyz;
case 9: return rec2020_to_xyz;
case 10: return identity;
case 12: return p3_to_xyz;
default: return identity;
}
}
mat3
cicp_from_xyz (uint cp)
{
switch (cp)
{
case 1: return xyz_to_srgb;
case 5: return xyz_to_pal;
case 6: return xyz_to_ntsc;
case 9: return xyz_to_rec2020;
case 10: return identity;
case 12: return xyz_to_p3;
default: return identity;
}
}
void
run (out vec2 pos)
{
Rect r = rect_from_gsk (in_rect);
pos = rect_get_position (r);
_pos = pos;
_rect = r;
_tex_coord = rect_get_coord (rect_from_gsk (in_tex_rect), pos);
_opacity = in_opacity;
_transfer_function = in_transfer_function;
if (HAS_VARIATION (VARIATION_REVERSE))
{
if (OUTPUT_COLOR_SPACE == GDK_COLOR_STATE_ID_SRGB ||
OUTPUT_COLOR_SPACE == GDK_COLOR_STATE_ID_SRGB_LINEAR)
_mat = cicp_from_xyz (in_color_primaries) * srgb_to_xyz;
else
_mat = cicp_from_xyz (in_color_primaries) * rec2020_to_xyz;
}
else
{
if (OUTPUT_COLOR_SPACE == GDK_COLOR_STATE_ID_SRGB ||
OUTPUT_COLOR_SPACE == GDK_COLOR_STATE_ID_SRGB_LINEAR)
_mat = xyz_to_srgb * cicp_to_xyz (in_color_primaries);
else
_mat = xyz_to_rec2020 * cicp_to_xyz (in_color_primaries);
}
}
#endif
#ifdef GSK_FRAGMENT_SHADER
float
bt709_eotf (float v)
{
if (v < 0.081)
return v / 4.5;
else
return pow ((v + 0.099) / 1.099, 1.0/0.45);
}
float
bt709_oetf (float v)
{
if (v < 0.081)
return v * 4.5;
else
return 1.099 * pow (v, 0.45) - 0.099;
}
float
hlg_eotf (float v)
{
const float a = 0.17883277;
const float b = 0.28466892;
const float c = 0.55991073;
if (v <= 0.5)
return (v * v) / 3.0;
else
return exp (((v - c) / a) + b) / 12.0;
}
float
hlg_oetf (float v)
{
const float a = 0.17883277;
const float b = 0.28466892;
const float c = 0.55991073;
if (v <= 1.0 / 12.0)
return sqrt (3.0 * v);
else
return a * log (12.0 * v - b) + c;
}
vec3
apply_cicp_eotf (vec3 color,
uint transfer_function)
{
switch (transfer_function)
{
case 1:
case 6:
case 14:
case 15:
return vec3 (bt709_eotf (color.r),
bt709_eotf (color.g),
bt709_eotf (color.b));
case 8:
return color;
case 13:
return vec3 (srgb_eotf (color.r),
srgb_eotf (color.g),
srgb_eotf (color.b));
case 16:
return vec3 (pq_eotf (color.r),
pq_eotf (color.g),
pq_eotf (color.b));
case 18:
return vec3 (hlg_eotf (color.r),
hlg_eotf (color.g),
hlg_eotf (color.b));
default:
return vec3 (1.0, 0.2, 0.8);
}
}
vec3
apply_cicp_oetf (vec3 color,
uint transfer_function)
{
switch (transfer_function)
{
case 1:
case 6:
case 14:
case 15:
return vec3 (bt709_oetf (color.r),
bt709_oetf (color.g),
bt709_oetf (color.b));
case 8:
return color;
case 13:
return vec3 (srgb_oetf (color.r),
srgb_oetf (color.g),
srgb_oetf (color.b));
case 16:
return vec3 (pq_oetf (color.r),
pq_oetf (color.g),
pq_oetf (color.b));
case 18:
return vec3 (hlg_oetf (color.r),
hlg_oetf (color.g),
hlg_oetf (color.b));
default:
return vec3 (1.0, 0.2, 0.8);
}
}
vec4
convert_color_from_cicp (vec4 color,
bool from_premul,
uint to,
bool to_premul)
{
if (from_premul)
color = color_unpremultiply (color);
color.rgb = apply_cicp_eotf (color.rgb, _transfer_function);
color.rgb = _mat * color.rgb;
color.rgb = apply_oetf (color.rgb, to);
if (to_premul)
color = color_premultiply (color);
return color;
}
vec4
convert_color_to_cicp (vec4 color,
bool to_premul,
uint from,
bool from_premul)
{
if (from_premul)
color = color_unpremultiply (color);
color.rgb = apply_eotf (color.rgb, from);
color.rgb = _mat * color.rgb;
color.rgb = apply_cicp_oetf (color.rgb, _transfer_function);
if (to_premul)
color = color_premultiply (color);
return color;
}
void
run (out vec4 color,
out vec2 position)
{
vec4 pixel;
if (HAS_VARIATION (VARIATION_STRAIGHT_ALPHA))
pixel = gsk_texture_straight_alpha (GSK_TEXTURE0, _tex_coord);
else
pixel = texture (GSK_TEXTURE0, _tex_coord);
if (HAS_VARIATION (VARIATION_REVERSE))
pixel = convert_color_to_cicp (color,
ALT_PREMULTIPLIED,
OUTPUT_COLOR_SPACE, OUTPUT_PREMULTIPLIED);
else
pixel = convert_color_from_cicp (color,
ALT_PREMULTIPLIED,
OUTPUT_COLOR_SPACE, OUTPUT_PREMULTIPLIED);
float alpha = rect_coverage (_rect, _pos);
if (HAS_VARIATION (VARIATION_OPACITY))
alpha *= _opacity;
color = output_color_alpha (pixel, alpha);
position = _pos;
}
#endif

View File

@ -20,6 +20,7 @@ gsk_private_gpu_shaders = files([
'gskgpucolormatrix.glsl',
'gskgpuconicgradient.glsl',
'gskgpuconvert.glsl',
'gskgpuconvertcicp.glsl',
'gskgpucrossfade.glsl',
'gskgpulineargradient.glsl',
'gskgpumask.glsl',

View File

@ -88,6 +88,7 @@ gsk_private_sources = files([
'gpu/gskgpucolorop.c',
'gpu/gskgpuconicgradientop.c',
'gpu/gskgpuconvertop.c',
'gpu/gskgpuconvertcicpop.c',
'gpu/gskgpucrossfadeop.c',
'gpu/gskgpudownloadop.c',
'gpu/gskgpudevice.c',