From 2883f4b7a25030f60ee440c5ff856997cea1bc06 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 8 Jun 2023 21:42:18 +0200 Subject: [PATCH] vulkan: Antialiasing for linear gradients Shaders are complicated now... --- gsk/vulkan/resources/linear.frag | 132 +++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 34 deletions(-) diff --git a/gsk/vulkan/resources/linear.frag b/gsk/vulkan/resources/linear.frag index d23543085f..9cc0c8c83a 100644 --- a/gsk/vulkan/resources/linear.frag +++ b/gsk/vulkan/resources/linear.frag @@ -4,11 +4,6 @@ #include "clip.frag.glsl" #include "rect.frag.glsl" -struct ColorStop { - float offset; - vec4 color; -}; - layout(location = 0) in vec2 inPos; layout(location = 1) in flat Rect inRect; layout(location = 2) in float inGradientPos; @@ -18,47 +13,116 @@ layout(location = 5) in flat int inStopCount; layout(location = 0) out vec4 color; -ColorStop -get_stop(int i) +float +get_offset (int i) { - ColorStop result; + return get_float (inStopOffset + i * 5); +} - result.offset = get_float(inStopOffset + i * 5); - result.color = vec4(get_float(inStopOffset + i * 5 + 1), - get_float(inStopOffset + i * 5 + 2), - get_float(inStopOffset + i * 5 + 3), - get_float(inStopOffset + i * 5 + 4)); +vec4 +get_color (int i) +{ + i = clamp (i, 0, inStopCount - 1); + return color = vec4 (get_float (inStopOffset + i * 5 + 1), + get_float (inStopOffset + i * 5 + 2), + get_float (inStopOffset + i * 5 + 3), + get_float (inStopOffset + i * 5 + 4)); +} + +vec4 +get_color_for_range_unscaled (float start, + float end) +{ + vec4 result = vec4 (0); + float offset; + int i; + + for (i = 0; i < inStopCount; i++) + { + offset = get_offset (i); + if (offset >= start) + break; + } + if (i == inStopCount) + offset = 1; + + float last_offset = i > 0 ? get_offset (i - 1) : 0; + vec4 last_color = get_color (i - 1); + vec4 color = get_color (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 < inStopCount; i++) + { + offset = get_offset (i); + color = get_color (i); + if (offset >= end) + break; + result += 0.5 * (color + last_color) * (offset - last_offset); + last_offset = offset; + last_color = color; + } + if (i == inStopCount) + { + offset = 1; + color = get_color (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 +get_color_for_range (float start, + float end) +{ + return get_color_for_range_unscaled (start, end) / (end - start); +} + void main() { - float pos; + vec4 c; + float pos_start, pos_end; + float dPos = 0.5 * abs (fwidth (inGradientPos)); if (inRepeating != 0) - pos = fract (inGradientPos); - else - pos = clamp (inGradientPos, 0, 1); - - ColorStop stop = get_stop (0); - float last_offset = stop.offset; - color = stop.color; - for (int i = 1; i < inStopCount; i++) { - stop = get_stop(i); - if (stop.offset < pos) - color = stop.color; + pos_start = inGradientPos - dPos; + pos_end = inGradientPos + dPos; + if (floor (pos_end) > floor (pos_start)) + { + float fract_end = fract(pos_end); + float fract_start = fract(pos_start); + float n = floor (pos_end) - floor (pos_start); + if (fract_end > fract_start + 0.01) + c = get_color_for_range_unscaled (fract_start, fract_end); + else if (fract_start > fract_end + 0.01) + c = -get_color_for_range_unscaled (fract_end, fract_start); + c += get_color_for_range_unscaled (0.0, 1.0) * n; + c /= pos_end - pos_start; + } else - color = mix (color, stop.color, clamp((pos - last_offset) / (stop.offset - last_offset), 0, 1)); - last_offset = stop.offset; - if (last_offset >= pos) - break; + { + c = get_color_for_range (fract (pos_start), fract (pos_end)); + } + } + else + { + pos_start = clamp (inGradientPos - dPos, 0, 1); + pos_end = clamp (inGradientPos + dPos, 0, 1); + c = get_color_for_range (pos_start, pos_end); } - - if (last_offset < pos) - color = mix (color, stop.color, clamp((pos - last_offset) / (1 - last_offset), 0, 1)); - float alpha = color.a * rect_coverage (inRect, inPos); - color = clip_scaled (inPos, vec4(color.rgb, 1) * alpha); + float alpha = c.a * rect_coverage (inRect, inPos); + color = clip_scaled (inPos, vec4(c.rgb, 1) * alpha); }