From a301f18ebf8e0bf921157d1c5ddbb45cffcb1050 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 2 Nov 2023 08:09:13 +0100 Subject: [PATCH] gpu: Track position fwidth explicitly This is relevant went encountering repeat nodes, where the repeat cutoff will make the fwidth of the position go wild otherwise. Gradients require more work now, because we need to compute offsets twice - once for the pixel, once for the offst. --- gsk/gpu/shaders/pattern.glsl | 105 +++++++++++++++++++++++------------ gsk/gpu/shaders/rect.glsl | 12 +++- 2 files changed, 80 insertions(+), 37 deletions(-) diff --git a/gsk/gpu/shaders/pattern.glsl b/gsk/gpu/shaders/pattern.glsl index 7c2523db6d..2a2f3e9599 100644 --- a/gsk/gpu/shaders/pattern.glsl +++ b/gsk/gpu/shaders/pattern.glsl @@ -24,6 +24,32 @@ stack_pop (void) return stack[stack_size]; } +struct Position +{ + /* pos.xy is the actual position + pos.zw is the fwidth() of it + */ + vec4 pos; +}; + +vec2 +position (Position pos) +{ + return pos.pos.xy; +} + +vec2 +position_fwidth (Position pos) +{ + return pos.pos.zw; +} + +Position +position_new (vec2 pos) +{ + return Position (vec4 (pos, fwidth (pos))); +} + uint read_uint (inout uint reader) { @@ -85,18 +111,17 @@ read_gradient (inout uint reader) void clip_pattern (inout uint reader, inout vec4 color, - vec2 pos) + Position pos) { Rect clip = read_rect (reader); - float alpha = rect_coverage (clip, pos); + float alpha = rect_coverage (clip, position (pos), abs (position_fwidth (pos))); color *= alpha; } void opacity_pattern (inout uint reader, - inout vec4 color, - vec2 pos) + inout vec4 color) { float opacity = read_float (reader); @@ -105,8 +130,7 @@ opacity_pattern (inout uint reader, void color_matrix_pattern (inout uint reader, - inout vec4 color, - vec2 pos) + inout vec4 color) { mat4 matrix = read_mat4 (reader); vec4 offset = read_vec4 (reader); @@ -120,24 +144,24 @@ color_matrix_pattern (inout uint reader, } void -repeat_push_pattern (inout uint reader, - inout vec2 pos) +repeat_push_pattern (inout uint reader, + inout Position pos) { - stack_push (vec4 (pos, 0.0, 0.0)); + stack_push (pos.pos); Rect bounds = read_rect (reader); vec2 size = rect_size (bounds); - pos = mod (pos - bounds.bounds.xy, size); + pos.pos.xy = mod (pos.pos.xy - bounds.bounds.xy, size); /* make sure we have a positive result */ - pos = mix (pos, pos + size, lessThan (pos, vec2 (0.0))); - pos += bounds.bounds.xy; + pos.pos.xy = mix (pos.pos.xy, pos.pos.xy + size, lessThan (pos.pos.xy, vec2 (0.0))); + pos.pos.xy += bounds.bounds.xy; } void -position_pop_pattern (inout uint reader, - inout vec2 pos) +position_pop_pattern (inout uint reader, + inout Position pos) { - pos = stack_pop ().xy; + pos = Position (stack_pop ()); } void @@ -188,22 +212,24 @@ mask_inverted_luminance_pattern (inout uint reader, vec4 glyphs_pattern (inout uint reader, - vec2 pos) + Position pos) { float opacity = 0.0; vec4 color = color_premultiply (read_vec4 (reader)); uint num_glyphs = read_uint (reader); uint i; + vec2 p = position (pos); + vec2 dFdp = abs (position_fwidth (pos)); for (i = 0u; i < num_glyphs; i++) { uint tex_id = read_uint (reader); Rect glyph_bounds = read_rect (reader); vec4 tex_rect = read_vec4 (reader); - float coverage = rect_coverage (glyph_bounds, pos); + float coverage = rect_coverage (glyph_bounds, p, dFdp); if (coverage > 0.0) - opacity += coverage * gsk_texture (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)).a; + opacity += coverage * gsk_texture (tex_id, (p - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)).a; } return color * opacity; @@ -211,27 +237,27 @@ glyphs_pattern (inout uint reader, vec4 texture_pattern (inout uint reader, - vec2 pos) + Position pos) { uint tex_id = read_uint (reader); vec4 tex_rect = read_vec4 (reader); - return gsk_texture (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); + return gsk_texture (tex_id, (position (pos) - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); } vec4 straight_alpha_pattern (inout uint reader, - vec2 pos) + Position pos) { uint tex_id = read_uint (reader); vec4 tex_rect = read_vec4 (reader); - return gsk_texture_straight_alpha (tex_id, (pos - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); + return gsk_texture_straight_alpha (tex_id, (position (pos) - GSK_GLOBAL_SCALE * tex_rect.xy) / (GSK_GLOBAL_SCALE * tex_rect.zw)); } vec4 linear_gradient_pattern (inout uint reader, - vec2 pos, + Position pos, bool repeating) { vec2 start = read_vec2 (reader) * GSK_GLOBAL_SCALE; @@ -240,8 +266,9 @@ linear_gradient_pattern (inout uint 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); + float offset = dot (position (pos) - start, line) / line_length; + float other_offset = dot (position (pos) + position_fwidth (pos) - start, line) / line_length; + float d_offset = 0.5 * abs (offset - other_offset); if (repeating) return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); @@ -251,7 +278,7 @@ linear_gradient_pattern (inout uint reader, vec4 radial_gradient_pattern (inout uint reader, - vec2 pos, + Position pos, bool repeating) { vec2 center = read_vec2 (reader) * GSK_GLOBAL_SCALE; @@ -260,9 +287,11 @@ radial_gradient_pattern (inout uint reader, float end = read_float (reader); Gradient gradient = read_gradient (reader); - float offset = length ((pos - center) / radius); + float offset = length ((position (pos) - center) / radius); + float other_offset = length ((position (pos) + position_fwidth (pos) - center) / radius); offset = (offset - start) / (end - start); - float d_offset = 0.5 * fwidth (offset); + other_offset = (other_offset - start) / (end - start); + float d_offset = abs (0.5 * (offset - other_offset)); if (repeating) return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); @@ -272,19 +301,24 @@ radial_gradient_pattern (inout uint reader, vec4 conic_gradient_pattern (inout uint reader, - vec2 pos) + Position pos) { vec2 center = read_vec2 (reader); float angle = read_float (reader); Gradient gradient = read_gradient (reader); /* scaling modifies angles, so be sure to use right coordinate system */ - pos = pos / GSK_GLOBAL_SCALE - center; - float offset = atan (pos.y, pos.x); + vec2 dpos = position (pos) / GSK_GLOBAL_SCALE - center; + vec2 dpos2 = (position (pos) + position_fwidth (pos)) / GSK_GLOBAL_SCALE - center; + float offset = atan (dpos.y, dpos.x); + float offset2 = atan (dpos2.y, dpos2.x); offset = degrees (offset + angle) / 360.0; + offset2 = degrees (offset2 + angle) / 360.0; float overflow = fract (offset + 0.5); + float overflow2 = fract (offset2 + 0.5); offset = fract (offset); - float d_offset = max (0.00001, 0.5 * min (fwidth (offset), fwidth (overflow))); + offset2 = fract (offset2); + float d_offset = max (0.00001, 0.5 * min (abs (offset - offset2), abs (overflow - overflow2))); return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset); } @@ -299,9 +333,10 @@ color_pattern (inout uint reader) vec4 pattern (uint reader, - vec2 pos) + vec2 pos_) { vec4 color = vec4 (1.0, 0.0, 0.8, 1.0); /* pink */ + Position pos = position_new (pos_); for(;;) { @@ -324,10 +359,10 @@ pattern (uint reader, color = glyphs_pattern (reader, pos); break; case GSK_GPU_PATTERN_COLOR_MATRIX: - color_matrix_pattern (reader, color, pos); + color_matrix_pattern (reader, color); break; case GSK_GPU_PATTERN_OPACITY: - opacity_pattern (reader, color, pos); + opacity_pattern (reader, color); break; case GSK_GPU_PATTERN_LINEAR_GRADIENT: color = linear_gradient_pattern (reader, pos, false); diff --git a/gsk/gpu/shaders/rect.glsl b/gsk/gpu/shaders/rect.glsl index 2193ea630d..70f3418474 100644 --- a/gsk/gpu/shaders/rect.glsl +++ b/gsk/gpu/shaders/rect.glsl @@ -78,15 +78,23 @@ rect_get_coord (Rect r, vec2 pt) #ifdef GSK_FRAGMENT_SHADER float -rect_coverage (Rect r, vec2 p) +rect_coverage (Rect r, + vec2 p, + vec2 dFdp) { - vec2 dFdp = abs(fwidth (p)); Rect prect = Rect(vec4(p - 0.5 * dFdp, p + 0.5 * dFdp)); Rect coverect = rect_intersect (r, prect); vec2 coverage = rect_size(coverect) / dFdp; return coverage.x * coverage.y; } +float +rect_coverage (Rect r, vec2 p) +{ + return rect_coverage (r, p, abs (fwidth (p))); +} + + #endif #endif