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',