From 226652edb014d5f03c31ba4c701df67010790ce5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 16 Jul 2024 23:51:57 -0400 Subject: [PATCH] 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. --- gsk/gpu/gskgpuconvertcicpop.c | 119 +++++++++ gsk/gpu/gskgpuconvertcicpopprivate.h | 29 +++ gsk/gpu/shaders/color.glsl | 39 +-- gsk/gpu/shaders/gskgpuconvertcicp.glsl | 346 +++++++++++++++++++++++++ gsk/gpu/shaders/meson.build | 1 + gsk/meson.build | 1 + 6 files changed, 516 insertions(+), 19 deletions(-) create mode 100644 gsk/gpu/gskgpuconvertcicpop.c create mode 100644 gsk/gpu/gskgpuconvertcicpopprivate.h create mode 100644 gsk/gpu/shaders/gskgpuconvertcicp.glsl diff --git a/gsk/gpu/gskgpuconvertcicpop.c b/gsk/gpu/gskgpuconvertcicpop.c new file mode 100644 index 0000000000..b206b06fac --- /dev/null +++ b/gsk/gpu/gskgpuconvertcicpop.c @@ -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; +} diff --git a/gsk/gpu/gskgpuconvertcicpopprivate.h b/gsk/gpu/gskgpuconvertcicpopprivate.h new file mode 100644 index 0000000000..a5a11dfc9f --- /dev/null +++ b/gsk/gpu/gskgpuconvertcicpopprivate.h @@ -0,0 +1,29 @@ +#pragma once + +#include "gskgpushaderopprivate.h" + +#include + +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 diff --git a/gsk/gpu/shaders/color.glsl b/gsk/gpu/shaders/color.glsl index 0d2991395c..dc92a7a68b 100644 --- a/gsk/gpu/shaders/color.glsl +++ b/gsk/gpu/shaders/color.glsl @@ -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_ */ diff --git a/gsk/gpu/shaders/gskgpuconvertcicp.glsl b/gsk/gpu/shaders/gskgpuconvertcicp.glsl new file mode 100644 index 0000000000..430421ca07 --- /dev/null +++ b/gsk/gpu/shaders/gskgpuconvertcicp.glsl @@ -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 diff --git a/gsk/gpu/shaders/meson.build b/gsk/gpu/shaders/meson.build index e58bf9ab60..3d2f05cf68 100644 --- a/gsk/gpu/shaders/meson.build +++ b/gsk/gpu/shaders/meson.build @@ -20,6 +20,7 @@ gsk_private_gpu_shaders = files([ 'gskgpucolormatrix.glsl', 'gskgpuconicgradient.glsl', 'gskgpuconvert.glsl', + 'gskgpuconvertcicp.glsl', 'gskgpucrossfade.glsl', 'gskgpulineargradient.glsl', 'gskgpumask.glsl', diff --git a/gsk/meson.build b/gsk/meson.build index 6cc45edc9d..087ebcfe37 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -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',