diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-03-09 22:29:22 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-03-10 08:44:32 -0500 |
commit | b9d21f1f1a68ad87e5801afed8c5e7405c5975c1 (patch) | |
tree | 5e4ad9feb6eebb66b01b7f866cbe0b84d4cf9335 | |
parent | 8baf29063cc41f555f3f45ce5c7f64ec43ed025e (diff) | |
download | gtk+-ngl-tweaks.tar.gz |
ngl: Add a shader for short linear gradientsngl-tweaks
Almost all our linear gradients have 2 stops.
Add a simple shader for that case.
-rw-r--r-- | gsk/meson.build | 1 | ||||
-rw-r--r-- | gsk/ngl/gsknglprograms.defs | 6 | ||||
-rw-r--r-- | gsk/ngl/gsknglrenderjob.c | 35 | ||||
-rw-r--r-- | gsk/resources/glsl/linear_gradient_2.glsl | 84 |
4 files changed, 125 insertions, 1 deletions
diff --git a/gsk/meson.build b/gsk/meson.build index dd1ac34ff9..1d579ab5ae 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -7,6 +7,7 @@ gsk_private_gl_shaders = [ 'resources/glsl/coloring.glsl', 'resources/glsl/color.glsl', 'resources/glsl/linear_gradient.glsl', + 'resources/glsl/linear_gradient_2.glsl', 'resources/glsl/radial_gradient.glsl', 'resources/glsl/conic_gradient.glsl', 'resources/glsl/color_matrix.glsl', diff --git a/gsk/ngl/gsknglprograms.defs b/gsk/ngl/gsknglprograms.defs index b0b797f0e9..cfd5580037 100644 --- a/gsk/ngl/gsknglprograms.defs +++ b/gsk/ngl/gsknglprograms.defs @@ -57,6 +57,12 @@ GSK_NGL_DEFINE_PROGRAM (linear_gradient, GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points) GSK_NGL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat)) +GSK_NGL_DEFINE_PROGRAM (linear_gradient_2, + "/org/gtk/libgsk/glsl/linear_gradient_2.glsl", + GSK_NGL_ADD_UNIFORM (1, LINEAR_GRADIENT_2_COLOR_STOPS, u_color_stops) + GSK_NGL_ADD_UNIFORM (2, LINEAR_GRADIENT_2_POINTS, u_points) + GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_2_REPEAT, u_repeat)) + GSK_NGL_DEFINE_PROGRAM (outset_shadow, "/org/gtk/libgsk/glsl/outset_shadow.glsl", GSK_NGL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color) diff --git a/gsk/ngl/gsknglrenderjob.c b/gsk/ngl/gsknglrenderjob.c index 794086c4e3..b34692b3cb 100644 --- a/gsk/ngl/gsknglrenderjob.c +++ b/gsk/ngl/gsknglrenderjob.c @@ -1494,6 +1494,37 @@ gsk_ngl_render_job_visit_linear_gradient_node (GskNglRenderJob *job, } static inline void +gsk_ngl_render_job_visit_linear_gradient_2_node (GskNglRenderJob *job, + const GskRenderNode *node) +{ + const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL); + const graphene_point_t *start = gsk_linear_gradient_node_get_start (node); + const graphene_point_t *end = gsk_linear_gradient_node_get_end (node); + int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node); + gboolean repeat = gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE; + float x1 = job->offset_x + start->x; + float x2 = job->offset_x + end->x; + float y1 = job->offset_y + start->y; + float y2 = job->offset_y + end->y; + + g_assert (n_color_stops == 2); + + gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, linear_gradient_2)); + gsk_ngl_program_set_uniform1fv (job->current_program, + UNIFORM_LINEAR_GRADIENT_2_COLOR_STOPS, 0, + n_color_stops * 5, + (const float *)stops); + gsk_ngl_program_set_uniform4f (job->current_program, + UNIFORM_LINEAR_GRADIENT_2_POINTS, 0, + x1, y1, x2 - x1, y2 - y1); + gsk_ngl_program_set_uniform1i (job->current_program, + UNIFORM_LINEAR_GRADIENT_2_REPEAT, 0, + repeat); + gsk_ngl_render_job_draw_rect (job, &node->bounds); + gsk_ngl_render_job_end_draw (job); +} + +static inline void gsk_ngl_render_job_visit_conic_gradient_node (GskNglRenderJob *job, const GskRenderNode *node) { @@ -3695,7 +3726,9 @@ gsk_ngl_render_job_visit_node (GskNglRenderJob *job, case GSK_LINEAR_GRADIENT_NODE: case GSK_REPEATING_LINEAR_GRADIENT_NODE: - if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS) + if (gsk_linear_gradient_node_get_n_color_stops (node) == 2) + gsk_ngl_render_job_visit_linear_gradient_2_node (job, node); + else if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS) gsk_ngl_render_job_visit_linear_gradient_node (job, node); else gsk_ngl_render_job_visit_as_fallback (job, node); diff --git a/gsk/resources/glsl/linear_gradient_2.glsl b/gsk/resources/glsl/linear_gradient_2.glsl new file mode 100644 index 0000000000..b4cc0fb7b2 --- /dev/null +++ b/gsk/resources/glsl/linear_gradient_2.glsl @@ -0,0 +1,84 @@ +// VERTEX_SHADER +uniform vec4 u_points; + +_NOPERSPECTIVE_ _OUT_ vec4 info; + +void main() { + gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); + + vec2 mv0 = u_modelview[0].xy; + vec2 mv1 = u_modelview[1].xy; + vec2 offset = aPosition - u_points.xy; + vec2 coord = vec2(dot(mv0, offset), + dot(mv1, offset)); + + // Original equation: + // VS | maxDist = length(end - start); + // VS | gradient = end - start; + // VS | gradientLength = length(gradient); + // FS | pos = frag_coord - start + // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient + // FS | offset = length(proj) / maxDist + + // Simplified formula derivation: + // 1. Notice that maxDist = gradientLength: + // offset = length(proj) / gradientLength + // 2. Let gnorm = gradient / gradientLength, then: + // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) = + // = dot(gnorm, pos) * gnorm + // 3. Since gnorm is unit length then: + // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos) + // 4. We can avoid the FS division by passing a scaled pos from the VS: + // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength) + // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL + vec2 gradient = vec2(dot(mv0, u_points.zw), + dot(mv1, u_points.zw)); + float rcp_gradient_length = inversesqrt(dot(gradient, gradient)); + + info = rcp_gradient_length * vec4(coord, gradient); +} + +// FRAGMENT_SHADER: +uniform float u_color_stops[2 * 5]; +uniform bool u_repeat; + +_NOPERSPECTIVE_ _IN_ vec4 info; + +float get_offset(int index) { + return u_color_stops[5 * index]; +} + +vec4 get_color(int index) { + int base = 5 * index + 1; + + return vec4(u_color_stops[base], + u_color_stops[base + 1], + u_color_stops[base + 2], + u_color_stops[base + 3]); +} + +void main() { + float offset = dot(info.xy, info.zw); + + if (u_repeat) { + offset = fract(offset); + } + + float offset0 = get_offset(0); + float offset1 = get_offset(1); + + if (offset < offset0) { + gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); + } + else if (offset < offset1) { + float f = (offset - offset0) / (offset1 - offset0); + vec4 curr_color = gsk_premultiply(get_color(0)); + vec4 next_color = gsk_premultiply(get_color(1)); + vec4 color = mix(curr_color, next_color, f); + gskSetOutputColor(color * u_alpha); + return; + } + else { + gskSetOutputColor(gsk_scaled_premultiply(get_color(1), u_alpha)); + } +} |