diff --git a/gsk/gpu/gskgpubufferwriter.c b/gsk/gpu/gskgpubufferwriter.c index cc3eb39de9..184bbc1b2f 100644 --- a/gsk/gpu/gskgpubufferwriter.c +++ b/gsk/gpu/gskgpubufferwriter.c @@ -105,6 +105,19 @@ gsk_gpu_buffer_writer_append_vec4 (GskGpuBufferWriter *self, gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f)); } +void +gsk_gpu_buffer_writer_append_point (GskGpuBufferWriter *self, + const graphene_point_t *point, + const graphene_point_t *offset) +{ + float f[2]; + + f[0] = point->x + offset->x; + f[1] = point->y + offset->y; + + gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f)); +} + void gsk_gpu_buffer_writer_append_rect (GskGpuBufferWriter *self, const graphene_rect_t *rect, @@ -125,3 +138,13 @@ gsk_gpu_buffer_writer_append_rgba (GskGpuBufferWriter *self, gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f)); } + +void +gsk_gpu_buffer_writer_append_color_stops (GskGpuBufferWriter *self, + const GskColorStop *stops, + gsize n_stops) +{ + gsk_gpu_buffer_writer_append_uint (self, n_stops); + gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) stops, sizeof (GskColorStop) * n_stops); +} + diff --git a/gsk/gpu/gskgpubufferwriterprivate.h b/gsk/gpu/gskgpubufferwriterprivate.h index 5c915f43ce..7505ee554a 100644 --- a/gsk/gpu/gskgpubufferwriterprivate.h +++ b/gsk/gpu/gskgpubufferwriterprivate.h @@ -1,6 +1,7 @@ #pragma once #include "gskgputypesprivate.h" +#include "gskrendernode.h" #include @@ -44,10 +45,16 @@ void gsk_gpu_buffer_writer_append_matrix (GskGpuB const graphene_matrix_t *matrix); void gsk_gpu_buffer_writer_append_vec4 (GskGpuBufferWriter *self, const graphene_vec4_t *vec4); +void gsk_gpu_buffer_writer_append_point (GskGpuBufferWriter *self, + const graphene_point_t *point, + const graphene_point_t *offset); void gsk_gpu_buffer_writer_append_rect (GskGpuBufferWriter *self, const graphene_rect_t *rect, const graphene_point_t *offset); void gsk_gpu_buffer_writer_append_rgba (GskGpuBufferWriter *self, const GdkRGBA *rgba); +void gsk_gpu_buffer_writer_append_color_stops (GskGpuBufferWriter *self, + const GskColorStop *stops, + gsize n_stops); G_END_DECLS diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index 076894108e..eaa3cf6c19 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -766,6 +766,32 @@ gsk_gpu_node_processor_create_texture_pattern (GskGpuNodeProcessor *self, return TRUE; } +static gboolean +gsk_gpu_node_processor_create_linear_gradient_pattern (GskGpuNodeProcessor *self, + GskGpuBufferWriter *writer, + GskRenderNode *node, + GskGpuShaderImage *images, + gsize n_images, + gsize *out_n_images) +{ + if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE) + gsk_gpu_buffer_writer_append_uint (writer, GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT); + else + gsk_gpu_buffer_writer_append_uint (writer, GSK_GPU_PATTERN_LINEAR_GRADIENT); + + gsk_gpu_buffer_writer_append_point (writer, + gsk_linear_gradient_node_get_start (node), + &self->offset); + gsk_gpu_buffer_writer_append_point (writer, + gsk_linear_gradient_node_get_end (node), + &self->offset); + gsk_gpu_buffer_writer_append_color_stops (writer, + gsk_linear_gradient_node_get_color_stops (node, NULL), + gsk_linear_gradient_node_get_n_color_stops (node)); + + return TRUE; +} + static gboolean gsk_gpu_node_processor_create_glyph_pattern (GskGpuNodeProcessor *self, GskGpuBufferWriter *writer, @@ -944,13 +970,13 @@ static const struct }, [GSK_LINEAR_GRADIENT_NODE] = { 0, - NULL, - NULL, + gsk_gpu_node_processor_add_node_as_pattern, + gsk_gpu_node_processor_create_linear_gradient_pattern, }, [GSK_REPEATING_LINEAR_GRADIENT_NODE] = { 0, - NULL, - NULL, + gsk_gpu_node_processor_add_node_as_pattern, + gsk_gpu_node_processor_create_linear_gradient_pattern, }, [GSK_RADIAL_GRADIENT_NODE] = { 0, diff --git a/gsk/gpu/gskgputypesprivate.h b/gsk/gpu/gskgputypesprivate.h index 63f1b4a7c5..02533a22b2 100644 --- a/gsk/gpu/gskgputypesprivate.h +++ b/gsk/gpu/gskgputypesprivate.h @@ -34,5 +34,7 @@ typedef enum { GSK_GPU_PATTERN_TEXTURE, GSK_GPU_PATTERN_COLOR_MATRIX, GSK_GPU_PATTERN_GLYPHS, + GSK_GPU_PATTERN_LINEAR_GRADIENT, + GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT, } GskGpuPatternType; diff --git a/gsk/gpu/shaders/enums.glsl b/gsk/gpu/shaders/enums.glsl index 8623d1b15b..f0ac95104d 100644 --- a/gsk/gpu/shaders/enums.glsl +++ b/gsk/gpu/shaders/enums.glsl @@ -11,5 +11,7 @@ #define GSK_GPU_PATTERN_TEXTURE 3u #define GSK_GPU_PATTERN_COLOR_MATRIX 4u #define GSK_GPU_PATTERN_GLYPHS 5u +#define GSK_GPU_PATTERN_LINEAR_GRADIENT 6u +#define GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT 7u #endif diff --git a/gsk/gpu/shaders/gradient.glsl b/gsk/gpu/shaders/gradient.glsl new file mode 100644 index 0000000000..0fd2cb9649 --- /dev/null +++ b/gsk/gpu/shaders/gradient.glsl @@ -0,0 +1,147 @@ +#ifndef _GRADIENT_ +#define _GRADIENT_ + +#ifdef GSK_FRAGMENT_SHADER + +#include "common.glsl" + +struct Gradient +{ + int n_stops; + uint offset; +}; + +float +gradient_read_offset (Gradient self, + int i) +{ + return gsk_get_float (self.offset + uint(i) * 5u); +} + +vec4 +gradient_read_color (Gradient self, + int i) +{ + uint u = uint (clamp (i, 0, self.n_stops - 1)); + return vec4 (gsk_get_float (self.offset + u * 5u + 1u), + gsk_get_float (self.offset + u * 5u + 2u), + gsk_get_float (self.offset + u * 5u + 3u), + gsk_get_float (self.offset + u * 5u + 4u)); +} + +vec4 +gradient_get_color_for_range_unscaled (Gradient self, + float start, + float end) +{ + vec4 result = vec4 (0.0); + float offset; + int i; + + for (i = 0; i < self.n_stops; i++) + { + offset = gradient_read_offset (self, i); + if (offset >= start) + break; + } + if (i == self.n_stops) + offset = 1.0; + + float last_offset = i > 0 ? gradient_read_offset (self, i - 1) : 0.0; + vec4 last_color = gradient_read_color (self, i - 1); + vec4 color = gradient_read_color (self, i); + if (last_offset < start) + { + last_color = mix (last_color, color, (start - last_offset) / (offset - last_offset)); + last_offset = start; + } + if (end <= start) + return last_color; + + for (; i < self.n_stops; i++) + { + offset = gradient_read_offset (self, i); + color = gradient_read_color (self, i); + if (offset >= end) + break; + result += 0.5 * (color + last_color) * (offset - last_offset); + last_offset = offset; + last_color = color; + } + if (i == self.n_stops) + { + offset = 1.0; + color = gradient_read_color (self, i); + } + if (offset > end) + { + color = mix (last_color, color, (end - last_offset) / (offset - last_offset)); + offset = end; + } + result += 0.5 * (color + last_color) * (offset - last_offset); + + return result; +} + +vec4 +gradient_get_color_repeating (Gradient self, + float start, + float end) +{ + vec4 c; + + if (floor (end) > floor (start)) + { + float fract_end = fract(end); + float fract_start = fract(start); + float n = floor (end) - floor (start); + if (fract_end > fract_start + 0.01) + c = gradient_get_color_for_range_unscaled (self, fract_start, fract_end); + else if (fract_start > fract_end + 0.01) + c = - gradient_get_color_for_range_unscaled (self, fract_end, fract_start); + c += gradient_get_color_for_range_unscaled (self, 0.0, 1.0) * n; + } + else + { + start = fract (start); + end = fract (end); + c = gradient_get_color_for_range_unscaled (self, start, end); + } + c /= end - start; + + return color_premultiply (c); +} + +vec4 +gradient_get_color (Gradient self, + float start, + float end) +{ + vec4 c; + + start = clamp (start, 0.0, 1.0); + end = clamp (end, 0.0, 1.0); + c = gradient_get_color_for_range_unscaled (self, start, end); + if (end > start) + c /= end - start; + + return color_premultiply (c); +} + +uint +gradient_get_size (Gradient self) +{ + return uint (self.n_stops) * 5u + 1u; +} + +Gradient +gradient_new (uint offset) +{ + Gradient self = Gradient (gsk_get_int (offset), + offset + 1u); + + return self; +} + +#endif /* GSK_FRAGMENT_SHADER */ +#endif /* __GRADIENT__ */ diff --git a/gsk/gpu/shaders/meson.build b/gsk/gpu/shaders/meson.build index 4a7e0bd731..9289258e9d 100644 --- a/gsk/gpu/shaders/meson.build +++ b/gsk/gpu/shaders/meson.build @@ -5,6 +5,7 @@ gsk_private_gpu_include_shaders = files([ 'common-vulkan.glsl', 'ellipse.glsl', 'enums.glsl', + 'gradient.glsl', 'pattern.glsl', 'rect.glsl', 'roundedrect.glsl', diff --git a/gsk/gpu/shaders/pattern.glsl b/gsk/gpu/shaders/pattern.glsl index 5d293c08fa..5d72b20ec1 100644 --- a/gsk/gpu/shaders/pattern.glsl +++ b/gsk/gpu/shaders/pattern.glsl @@ -2,6 +2,7 @@ #define _PATTERN_ #include "common.glsl" +#include "gradient.glsl" #include "rect.glsl" #ifdef GSK_FRAGMENT_SHADER @@ -22,6 +23,12 @@ read_float (inout uint reader) return result; } +vec2 +read_vec2 (inout uint reader) +{ + return vec2 (read_float (reader), read_float (reader)); +} + vec4 read_vec4 (inout uint reader) { @@ -37,18 +44,27 @@ read_rect (inout uint reader) mat4 read_mat4 (inout uint reader) { - mat4 result = mat4 (gsk_get_float (reader + 0), gsk_get_float (reader + 1), - gsk_get_float (reader + 2), gsk_get_float (reader + 3), - gsk_get_float (reader + 4), gsk_get_float (reader + 5), - gsk_get_float (reader + 6), gsk_get_float (reader + 7), - gsk_get_float (reader + 8), gsk_get_float (reader + 9), - gsk_get_float (reader + 10), gsk_get_float (reader + 11), - gsk_get_float (reader + 12), gsk_get_float (reader + 13), - gsk_get_float (reader + 14), gsk_get_float (reader + 15)); - reader += 16; + mat4 result = mat4 (gsk_get_float (reader + 0u), gsk_get_float (reader + 1u), + gsk_get_float (reader + 2u), gsk_get_float (reader + 3u), + gsk_get_float (reader + 4u), gsk_get_float (reader + 5u), + gsk_get_float (reader + 6u), gsk_get_float (reader + 7u), + gsk_get_float (reader + 8u), gsk_get_float (reader + 9u), + gsk_get_float (reader + 10u), gsk_get_float (reader + 11u), + gsk_get_float (reader + 12u), gsk_get_float (reader + 13u), + gsk_get_float (reader + 14u), gsk_get_float (reader + 15u)); + reader += 16u; return result; } +Gradient +read_gradient (inout uint reader) +{ + Gradient gradient = gradient_new (reader); + reader += gradient_get_size (gradient); + + return gradient; +} + void opacity_pattern (inout uint reader, inout vec4 color, @@ -108,6 +124,26 @@ texture_pattern (inout uint reader, return texture (gsk_get_texture (tex_id), (pos - push.scale * tex_rect.xy) / (push.scale * tex_rect.zw)); } +vec4 +linear_gradient_pattern (inout uint reader, + vec2 pos, + bool repeating) +{ + vec2 start = read_vec2 (reader) * push.scale; + vec2 end = read_vec2 (reader) * push.scale; + Gradient gradient = read_gradient (reader); + + vec2 line = end - start; + float line_length = dot (line, line); + float offset = dot (pos - start, line) / line_length; + float d_offset = 0.5 * fwidth (offset); + + if (repeating) + return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); + else + return gradient_get_color (gradient, offset - d_offset, offset + d_offset); +} + vec4 color_pattern (inout uint reader) { @@ -145,6 +181,12 @@ pattern (uint reader, case GSK_GPU_PATTERN_OPACITY: opacity_pattern (reader, color, pos); break; + case GSK_GPU_PATTERN_LINEAR_GRADIENT: + color = linear_gradient_pattern (reader, pos, false); + break; + case GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT: + color = linear_gradient_pattern (reader, pos, true); + break; } } }