From 2c1bd399d25e41fa6987df5f8c731b034568e0cc Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 3 Dec 2020 06:02:18 +0100 Subject: glrenderer: Implement a shader for conic gradients --- gsk/gl/gskglrenderer.c | 62 +++++++++++++++++++++++++++++- gsk/gl/gskglrenderops.c | 24 ++++++++++++ gsk/gl/gskglrenderopsprivate.h | 15 +++++++- gsk/gl/opbuffer.c | 1 + gsk/gl/opbuffer.h | 9 +++++ gsk/meson.build | 1 + gsk/resources/glsl/conic_gradient.glsl | 69 ++++++++++++++++++++++++++++++++++ 7 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 gsk/resources/glsl/conic_gradient.glsl diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 57b0767772..6e77194eb4 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -1483,6 +1483,35 @@ render_radial_gradient_node (GskGLRenderer *self, } } +static inline void +render_conic_gradient_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + const int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node); + + if (n_color_stops < GL_MAX_GRADIENT_STOPS) + { + const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL); + const graphene_point_t *center = gsk_conic_gradient_node_get_center (node); + const float rotation = gsk_conic_gradient_node_get_rotation (node); + + ops_set_program (builder, &self->programs->conic_gradient_program); + ops_set_conic_gradient (builder, + n_color_stops, + stops, + builder->dx + center->x, + builder->dy + center->y, + rotation); + + load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder); + } + else + { + render_fallback_node (self, node, builder); + } +} + static inline gboolean rounded_inner_rect_contains_rect (const GskRoundedRect *rounded, const graphene_rect_t *rect) @@ -3016,6 +3045,23 @@ apply_radial_gradient_op (const Program *program, glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]); } +static inline void +apply_conic_gradient_op (const Program *program, + const OpConicGradient *op) +{ + OP_PRINT (" -> Conic gradient"); + if (op->n_color_stops.send) + glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value); + + if (op->color_stops.send) + glUniform1fv (program->conic_gradient.color_stops_location, + op->n_color_stops.value * 5, + (float *)op->color_stops.value); + + glUniform1f (program->conic_gradient.rotation_location, op->rotation); + glUniform2f (program->conic_gradient.center_location, op->center[0], op->center[1]); +} + static inline void apply_border_op (const Program *program, const OpBorder *op) @@ -3250,6 +3296,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, { "/org/gtk/libgsk/glsl/inset_shadow.glsl", "inset shadow" }, { "/org/gtk/libgsk/glsl/linear_gradient.glsl", "linear gradient" }, { "/org/gtk/libgsk/glsl/radial_gradient.glsl", "radial gradient" }, + { "/org/gtk/libgsk/glsl/conic_gradient.glsl", "conic gradient" }, { "/org/gtk/libgsk/glsl/outset_shadow.glsl", "outset shadow" }, { "/org/gtk/libgsk/glsl/repeat.glsl", "repeat" }, { "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" }, @@ -3312,6 +3359,12 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end); INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius); + /* conic gradient */ + INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops); + INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops); + INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, center); + INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, rotation); + /* blur */ INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius); INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size); @@ -3663,6 +3716,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, render_radial_gradient_node (self, node, builder); break; + case GSK_CONIC_GRADIENT_NODE: + render_conic_gradient_node (self, node, builder); + break; + case GSK_CLIP_NODE: render_clip_node (self, node, builder); break; @@ -3724,7 +3781,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_REPEATING_RADIAL_GRADIENT_NODE: - case GSK_CONIC_GRADIENT_NODE: case GSK_CAIRO_NODE: default: { @@ -4027,6 +4083,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self) apply_radial_gradient_op (program, ptr); break; + case OP_CHANGE_CONIC_GRADIENT: + apply_conic_gradient_op (program, ptr); + break; + case OP_CHANGE_BLUR: apply_blur_op (program, ptr); break; diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c index 63273db93d..7351cf8746 100644 --- a/gsk/gl/gskglrenderops.c +++ b/gsk/gl/gskglrenderops.c @@ -1038,3 +1038,27 @@ ops_set_radial_gradient (RenderOpBuilder *self, op->start = start; op->end = end; } + +void +ops_set_conic_gradient (RenderOpBuilder *self, + guint n_color_stops, + const GskColorStop *color_stops, + float center_x, + float center_y, + float rotation) +{ + const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops); + OpConicGradient *op; + + /* TODO: State tracking? */ + + op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT); + op->n_color_stops.value = real_n_color_stops; + op->n_color_stops.send = true; + op->color_stops.value = color_stops; + op->color_stops.send = true; + op->center[0] = center_x; + op->center[1] = center_y; + op->rotation = rotation; +} + diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h index 7119ae0680..3dd3cb3411 100644 --- a/gsk/gl/gskglrenderopsprivate.h +++ b/gsk/gl/gskglrenderopsprivate.h @@ -13,7 +13,7 @@ #include "opbuffer.h" #define GL_N_VERTICES 6 -#define GL_N_PROGRAMS 14 +#define GL_N_PROGRAMS 15 #define GL_MAX_GRADIENT_STOPS 6 typedef struct @@ -129,6 +129,12 @@ struct _Program int end_location; int radius_location; } radial_gradient; + struct { + int num_color_stops_location; + int color_stops_location; + int center_location; + int rotation_location; + } conic_gradient; struct { int blur_radius_location; int blur_size_location; @@ -193,6 +199,7 @@ typedef struct { Program inset_shadow_program; Program linear_gradient_program; Program radial_gradient_program; + Program conic_gradient_program; Program outset_shadow_program; Program repeat_program; Program unblurred_outset_shadow_program; @@ -327,6 +334,12 @@ void ops_set_radial_gradient (RenderOpBuilder *self, float end, float hradius, float vradius); +void ops_set_conic_gradient (RenderOpBuilder *self, + guint n_color_stops, + const GskColorStop *color_stops, + float center_x, + float center_y, + float rotation); GskQuadVertex * ops_draw (RenderOpBuilder *builder, const GskQuadVertex vertex_data[GL_N_VERTICES]); diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c index 8a7f8c9b1f..806b8f7ca4 100644 --- a/gsk/gl/opbuffer.c +++ b/gsk/gl/opbuffer.c @@ -33,6 +33,7 @@ static guint op_sizes[OP_LAST] = { sizeof (OpBlend), sizeof (OpGLShader), sizeof (OpExtraTexture), + sizeof (OpConicGradient), }; void diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h index a2236dd7a1..08f48b7c5f 100644 --- a/gsk/gl/opbuffer.h +++ b/gsk/gl/opbuffer.h @@ -41,6 +41,7 @@ typedef enum OP_CHANGE_BLEND = 27, OP_CHANGE_GL_SHADER_ARGS = 28, OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29, + OP_CHANGE_CONIC_GRADIENT = 30, OP_LAST } OpKind; @@ -156,6 +157,14 @@ typedef struct float center[2]; } OpRadialGradient; +typedef struct +{ + ColorStopUniformValue color_stops; + IntUniformValue n_color_stops; + float center[2]; + float rotation; +} OpConicGradient; + typedef struct { const graphene_matrix_t *matrix; diff --git a/gsk/meson.build b/gsk/meson.build index 4ea6091a94..01c5e58fcc 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -8,6 +8,7 @@ gsk_private_gl_shaders = [ 'resources/glsl/color.glsl', 'resources/glsl/linear_gradient.glsl', 'resources/glsl/radial_gradient.glsl', + 'resources/glsl/conic_gradient.glsl', 'resources/glsl/color_matrix.glsl', 'resources/glsl/blur.glsl', 'resources/glsl/inset_shadow.glsl', diff --git a/gsk/resources/glsl/conic_gradient.glsl b/gsk/resources/glsl/conic_gradient.glsl new file mode 100644 index 0000000000..e9dee73e07 --- /dev/null +++ b/gsk/resources/glsl/conic_gradient.glsl @@ -0,0 +1,69 @@ +// VERTEX_SHADER +uniform vec2 u_center; +uniform float u_rotation; +uniform float u_color_stops[6 * 5]; +uniform int u_num_color_stops; + +const float PI = 3.1415926535897932384626433832795; + +_OUT_ vec2 center; +_OUT_ float rotation; +_OUT_ vec4 color_stops[6]; +_OUT_ float color_offsets[6]; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + // The -90 is because conics point to the top by default + rotation = mod (u_rotation - 90, 360.0); + if (rotation < 0) + rotation += 360.0; + rotation = PI / 180.0 * rotation; + + center = (u_modelview * vec4(u_center, 0, 1)).xy; + + for (int i = 0; i < u_num_color_stops; i ++) { + color_offsets[i] = u_color_stops[(i * 5) + 0]; + color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1], + u_color_stops[(i * 5) + 2], + u_color_stops[(i * 5) + 3], + u_color_stops[(i * 5) + 4])); + } +} + +// FRAGMENT_SHADER: +#ifdef GSK_LEGACY +uniform int u_num_color_stops; +#else +uniform highp int u_num_color_stops; // Why? Because it works like this. +#endif + +const float PI = 3.1415926535897932384626433832795; + +_IN_ vec2 center; +_IN_ float rotation; +_IN_ vec4 color_stops[6]; +_IN_ float color_offsets[6]; + +void main() { + // Position relative to center + vec2 pos = gsk_get_frag_coord() - center; + + // direction of point in range [-PI, PI] + float angle = atan (pos.y, pos.x); + // rotate, it's now [-2 * PI, PI] + angle -= rotation; + // fract() does the modulo here, so now we have progress + // into the current conic + float offset = fract (angle / 2 / PI + 2); + + vec4 color = color_stops[0]; + for (int i = 1; i < u_num_color_stops; i ++) { + if (offset >= color_offsets[i - 1]) { + float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]); + color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0)); + } + } + + gskSetOutputColor(color * u_alpha); +} -- cgit v1.2.1