pub( super ) const VERT_SRC: &str = r#"
attribute vec2 a_pos;
varying vec2 v_uv;
uniform mat4 u_mvp;
void main()
{
v_uv = a_pos;
gl_Position = u_mvp * vec4(a_pos, 0.0, 1.0);
}
"#;
pub( super ) const RECT_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform vec4 u_color;
uniform vec2 u_size;
uniform vec4 u_radii;
uniform float u_stroke;
uniform float u_pad;
// Per-fragment corner radius lookup. `c` is the fragment position
// relative to the rect's centre. Inside the shader, p.y grows UPWARD:
// `ortho_rect` flips the v_uv axis so v_uv.y=0 maps to the bottom edge
// of the rect in screen space and v_uv.y=1 to the top. So with `c =
// p - size/2`:
// c.x < 0, c.y > 0 → top-left → r.x
// c.x > 0, c.y > 0 → top-right → r.y
// c.x > 0, c.y < 0 → bottom-right → r.z
// c.x < 0, c.y < 0 → bottom-left → r.w
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
float r_max = max(max(u_radii.x, u_radii.y), max(u_radii.z, u_radii.w));
if (r_max <= 0.0 && u_stroke <= 0.0 && u_pad <= 0.0)
{
float a = u_color.a;
gl_FragColor = vec4(u_color.rgb * a, a);
return;
}
vec2 p = v_uv * (u_size + 2.0 * vec2(u_pad)) - vec2(u_pad);
vec2 c = p - u_size * 0.5;
float r = corner_radius(c, u_radii);
r = min(r, min(u_size.x, u_size.y) * 0.5);
vec2 q = abs(c) - (u_size * 0.5 - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
// 2-px-wide AA band (half-width 1.0). A narrower (±0.5) band is
// only sampled when pixel centres happen to fall inside it — when
// the rasterizer grid aligns with the edge at subpixel ±0.5 the
// band is jumped over and the transition becomes a binary step
// (visible stair-stepping on curved edges). Doubling the band
// guarantees at least one partial-coverage sample per scanline
// regardless of alignment. The quad pad set by the caller is 1 px
// so this still fits inside the geometry at d ≤ 1.
float coverage;
if (u_stroke > 0.0)
{
float half_w = u_stroke * 0.5;
coverage = 1.0 - smoothstep(half_w - 1.0, half_w + 1.0, abs(d));
} else {
coverage = 1.0 - smoothstep(-1.0, 1.0, d);
}
float a = u_color.a * coverage;
gl_FragColor = vec4(u_color.rgb * a, a);
}
"##;
pub( super ) const TEX_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_sampler;
uniform float u_opacity;
void main()
{
gl_FragColor = texture2D(u_sampler, vec2(v_uv.x, 1.0 - v_uv.y)) * u_opacity;
}
"##;
pub( super ) const CLIP_COMPOSITE_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_layer;
uniform sampler2D u_mask;
uniform vec2 u_canvas;
uniform vec4 u_bbox;
void main()
{
// `v_uv.y` runs bottom-to-top in screen space (FBO origin is lower-left, as
// `ortho_rect` + the texture shader's flip establish), so the screen Y of
// this fragment is `bbox.y + (1 - v_uv.y) * bbox.h`.
vec2 sp = vec2( u_bbox.x + v_uv.x * u_bbox.z, u_bbox.y + ( 1.0 - v_uv.y ) * u_bbox.w );
vec2 luv = vec2( sp.x / u_canvas.x, 1.0 - sp.y / u_canvas.y );
vec4 col = texture2D( u_layer, luv );
float cov = texture2D( u_mask, vec2( v_uv.x, 1.0 - v_uv.y ) ).a;
gl_FragColor = col * cov;
}
"##;
pub( super ) const GLYPH_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_sampler;
uniform vec4 u_color;
uniform float u_opacity;
uniform vec2 u_uv_offset;
uniform vec2 u_uv_scale;
void main()
{
vec2 uv = vec2(v_uv.x, 1.0 - v_uv.y) * u_uv_scale + u_uv_offset;
float coverage = texture2D(u_sampler, uv).r;
float a = u_color.a * coverage * u_opacity;
gl_FragColor = vec4(u_color.rgb * a, a);
}
"##;
pub( super ) const GLYPH_BATCH_VERT_SRC: &str = r#"
attribute vec2 a_pos;
attribute vec2 a_uv;
varying vec2 v_uv;
void main()
{
v_uv = a_uv;
gl_Position = vec4(a_pos, 0.0, 1.0);
}
"#;
pub( super ) const GLYPH_BATCH_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_sampler;
uniform vec4 u_color;
uniform float u_opacity;
void main()
{
float coverage = texture2D(u_sampler, v_uv).r;
float a = u_color.a * coverage * u_opacity;
gl_FragColor = vec4(u_color.rgb * a, a);
}
"##;
pub( super ) const BLIT_VERT_SRC: &str = r#"
attribute vec2 a_pos;
varying vec2 v_uv;
void main()
{
v_uv = a_pos;
gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
}
"#;
pub( super ) const BLIT_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_sampler;
void main()
{
gl_FragColor = texture2D(u_sampler, v_uv);
}
"#;
pub( super ) const SUB_BLIT_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_sampler;
uniform float u_opacity;
uniform float u_fade_bottom_px;
uniform float u_height_px;
void main()
{
vec4 c = texture2D(u_sampler, v_uv);
float fade = 1.0;
if (u_fade_bottom_px > 0.0)
{
float y_from_bottom = v_uv.y * u_height_px;
fade = clamp(y_from_bottom / u_fade_bottom_px, 0.0, 1.0);
}
gl_FragColor = c * (u_opacity * fade);
}
"#;
pub( super ) const LINEAR_GRADIENT_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_lut;
uniform vec2 u_dir;
uniform vec2 u_size;
uniform float u_line_length;
uniform vec4 u_radii;
uniform float u_pad;
uniform float u_lut_domain_min;
uniform float u_lut_domain_span;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
vec2 p = v_uv * (u_size + 2.0 * vec2(u_pad)) - vec2(u_pad);
vec2 c = p - u_size * 0.5;
float r = corner_radius(c, u_radii);
r = min(r, min(u_size.x, u_size.y) * 0.5);
vec2 q = abs(c) - (u_size * 0.5 - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
// 2-px AA band — see RECT_FRAG_SRC for the grid-alignment rationale.
float coverage = 1.0 - smoothstep(-1.0, 1.0, d);
// Gradient evaluated in rect-local pixel space. `p` already accounts
// for `u_pad` (set by the caller to expand the quad for AA), so the
// gradient direction stays anchored to the original rect even when
// the quad spills outside.
float dist = dot(c, u_dir);
float t = 0.5 + dist / u_line_length;
float t_lut = (t - u_lut_domain_min) / u_lut_domain_span;
vec4 grad = texture2D(u_lut, vec2(clamp(t_lut, 0.0, 1.0), 0.5));
float a = grad.a * coverage;
gl_FragColor = vec4(grad.rgb * a, a);
}
"##;
pub( super ) const RADIAL_GRADIENT_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_lut;
uniform vec2 u_center;
uniform float u_radius_frac;
uniform vec2 u_size;
uniform vec4 u_radii;
uniform float u_pad;
uniform float u_lut_domain_min;
uniform float u_lut_domain_span;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
vec2 p = v_uv * (u_size + 2.0 * vec2(u_pad)) - vec2(u_pad);
vec2 c = p - u_size * 0.5;
float r = corner_radius(c, u_radii);
r = min(r, min(u_size.x, u_size.y) * 0.5);
vec2 q = abs(c) - (u_size * 0.5 - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
// 2-px AA band — see RECT_FRAG_SRC for the grid-alignment rationale.
float coverage = 1.0 - smoothstep(-1.0, 1.0, d);
// Centre/extent in box-relative fractions evaluated against `p`
// (which already accounts for `u_pad`), so the radial centre stays
// anchored to the original rect when the quad spills outside.
vec2 t_uv = p / u_size;
float t = distance(t_uv, u_center) / max(u_radius_frac, 1e-6);
float t_lut = (t - u_lut_domain_min) / u_lut_domain_span;
vec4 grad = texture2D(u_lut, vec2(clamp(t_lut, 0.0, 1.0), 0.5));
float a = grad.a * coverage;
gl_FragColor = vec4(grad.rgb * a, a);
}
"##;
pub( super ) const SHADOW_INSET_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform vec2 u_size;
uniform vec2 u_padding;
uniform vec4 u_radii;
uniform float u_spread;
uniform float u_sigma;
uniform vec2 u_offset;
uniform vec4 u_color;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
vec2 p = v_uv * (u_size + 2.0 * u_padding) - u_padding;
vec2 c = p - u_size * 0.5;
// Outer SDF: the shape itself, untransformed. Used to clip the inset
// to the silhouette. 2-px AA band — see RECT_FRAG_SRC.
vec2 half_outer = u_size * 0.5;
float r_outer = corner_radius(c, u_radii);
r_outer = min(r_outer, min(half_outer.x, half_outer.y));
vec2 q_outer = abs(c) - (half_outer - vec2(r_outer));
float d_outer = min(max(q_outer.x, q_outer.y), 0.0) + length(max(q_outer, 0.0)) - r_outer;
float outer_coverage = 1.0 - smoothstep(-1.0, 1.0, d_outer);
// Inner SDF: the shape shifted by offset and eroded by spread. Its
// distance drives the Gaussian falloff. The inner radii match the
// outer per-corner shape, eroded by `u_spread` (clamped at zero).
vec2 p_shifted = p - u_offset;
vec2 c_shifted = p_shifted - u_size * 0.5;
vec2 half_inner = half_outer - vec2(u_spread);
// Degenerate case: spread larger than half the shape erodes it to a
// point. Guard so `half_inner` never goes negative.
half_inner = max(half_inner, vec2(1e-3));
vec4 inner_radii = max(u_radii - vec4(u_spread), vec4(0.0));
float r_inner = corner_radius(c_shifted, inner_radii);
r_inner = min(r_inner, min(half_inner.x, half_inner.y));
vec2 q_inner = abs(c_shifted) - (half_inner - vec2(r_inner));
float d_inner = min(max(q_inner.x, q_inner.y), 0.0) + length(max(q_inner, 0.0)) - r_inner;
float intensity;
if (d_inner >= 0.0)
{
intensity = 1.0;
}
else
{
float s = max(u_sigma, 0.5);
intensity = exp(-(d_inner * d_inner) / (2.0 * s * s));
}
intensity *= outer_coverage;
float a = u_color.a * intensity;
gl_FragColor = vec4(u_color.rgb * a, a);
}
"##;
pub( super ) const SHADOW_OUTER_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform vec2 u_size;
uniform vec2 u_padding;
uniform vec4 u_radii;
uniform float u_spread;
uniform float u_sigma;
uniform vec4 u_color;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
vec2 p = v_uv * (u_size + 2.0 * u_padding) - u_padding;
vec2 c = p - u_size * 0.5;
vec2 half_sz = u_size * 0.5 + vec2(u_spread);
// Per-corner shadow radius — outer shape grown uniformly by spread.
vec4 r_base = max(u_radii + vec4(u_spread), vec4(0.0));
float r = corner_radius(c, r_base);
r = min(r, min(half_sz.x, half_sz.y));
vec2 q = abs(c) - (half_sz - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
float intensity;
if (d <= 0.0)
{
intensity = 1.0;
}
else
{
float s = max(u_sigma, 0.5);
intensity = exp(-(d * d) / (2.0 * s * s));
}
float a = u_color.a * intensity;
gl_FragColor = vec4(u_color.rgb * a, a);
}
"##;
pub( super ) const BACKDROP_BLUR_H_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_source;
uniform vec2 u_texel;
uniform vec2 u_canvas_size;
uniform float u_sigma;
void main()
{
const int RADIUS = 20;
vec2 uv = gl_FragCoord.xy / u_canvas_size;
vec4 total = vec4(0.0);
float total_w = 0.0;
for (int i = -RADIUS; i <= RADIUS; i++)
{
float fi = float(i);
float w = exp(-(fi * fi) / (2.0 * u_sigma * u_sigma));
total += texture2D(u_source, uv + fi * u_texel) * w;
total_w += w;
}
gl_FragColor = total / total_w;
}
"#;
pub( super ) const BACKDROP_COMPOSITE_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_source;
uniform vec2 u_canvas_size;
uniform vec2 u_texel;
uniform float u_sigma;
uniform vec2 u_size;
uniform vec2 u_padding;
uniform vec4 u_radii;
uniform vec4 u_tint;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
const int RADIUS = 20;
// Rounded-rect SDF clip. 2-px AA band — see RECT_FRAG_SRC.
vec2 p = v_uv * (u_size + 2.0 * u_padding) - u_padding;
vec2 c = p - u_size * 0.5;
vec2 half_sz = u_size * 0.5;
float r = corner_radius(c, u_radii);
r = min(r, min(half_sz.x, half_sz.y));
vec2 q = abs(c) - (half_sz - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
float coverage = 1.0 - smoothstep(-1.0, 1.0, d);
if (coverage <= 0.0) { discard; }
// Vertical Gaussian on the H-blurred source.
vec2 uv = gl_FragCoord.xy / u_canvas_size;
vec4 total = vec4(0.0);
float total_w = 0.0;
for (int i = -RADIUS; i <= RADIUS; i++)
{
float fi = float(i);
float w = exp(-(fi * fi) / (2.0 * u_sigma * u_sigma));
total += texture2D(u_source, uv + fi * u_texel) * w;
total_w += w;
}
vec4 blurred = total / total_w;
// Optional tint, "tint over blurred" in premul space.
vec3 tint_premul = u_tint.rgb * u_tint.a;
vec3 result_rgb = tint_premul + blurred.rgb * (1.0 - u_tint.a);
gl_FragColor = vec4(result_rgb * coverage, coverage);
}
"#;
pub( super ) const BACKDROP_FAST_BLUR_H_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_source;
uniform vec2 u_texel;
uniform vec2 u_canvas_size;
uniform float u_sigma;
void main()
{
const int RADIUS = 4;
vec2 uv = gl_FragCoord.xy / u_canvas_size;
vec4 total = vec4(0.0);
float total_w = 0.0;
for (int i = -RADIUS; i <= RADIUS; i++)
{
float fi = float(i);
float w = exp(-(fi * fi) / (2.0 * u_sigma * u_sigma));
total += texture2D(u_source, uv + fi * u_texel) * w;
total_w += w;
}
gl_FragColor = total / total_w;
}
"#;
pub( super ) const BACKDROP_FAST_COMPOSITE_FRAG_SRC: &str = r#"
precision mediump float;
varying vec2 v_uv;
uniform sampler2D u_source;
uniform vec2 u_canvas_size;
uniform vec2 u_texel;
uniform float u_sigma;
uniform vec2 u_size;
uniform vec2 u_padding;
uniform vec4 u_radii;
uniform vec4 u_tint;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
const int RADIUS = 4;
// Rounded-rect SDF clip — identical to the full-quality variant.
vec2 p = v_uv * (u_size + 2.0 * u_padding) - u_padding;
vec2 c = p - u_size * 0.5;
vec2 half_sz = u_size * 0.5;
float r = corner_radius(c, u_radii);
r = min(r, min(half_sz.x, half_sz.y));
vec2 q = abs(c) - (half_sz - vec2(r));
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
float coverage = 1.0 - smoothstep(-1.0, 1.0, d);
if (coverage <= 0.0) { discard; }
vec2 uv = gl_FragCoord.xy / u_canvas_size;
vec4 total = vec4(0.0);
float total_w = 0.0;
for (int i = -RADIUS; i <= RADIUS; i++)
{
float fi = float(i);
float w = exp(-(fi * fi) / (2.0 * u_sigma * u_sigma));
total += texture2D(u_source, uv + fi * u_texel) * w;
total_w += w;
}
vec4 blurred = total / total_w;
vec3 tint_premul = u_tint.rgb * u_tint.a;
vec3 result_rgb = tint_premul + blurred.rgb * (1.0 - u_tint.a);
gl_FragColor = vec4(result_rgb * coverage, coverage);
}
"#;
pub( super ) const SHADOW_INSET_OVERLAY_FRAG_SRC: &str = r##"
precision mediump float;
varying vec2 v_uv;
uniform vec2 u_size;
uniform vec2 u_padding;
uniform vec4 u_radii;
uniform float u_spread;
uniform float u_sigma;
uniform vec2 u_offset;
uniform vec4 u_color;
uniform sampler2D u_snapshot;
uniform vec2 u_canvas_size;
// Per-fragment corner radius lookup — see RECT_FRAG_SRC for the
// quadrant convention.
float corner_radius(vec2 c, vec4 r)
{
float top = (c.x < 0.0) ? r.x : r.y;
float bottom = (c.x < 0.0) ? r.w : r.z;
return (c.y > 0.0) ? top : bottom;
}
void main()
{
vec2 p = v_uv * (u_size + 2.0 * u_padding) - u_padding;
vec2 c = p - u_size * 0.5;
// Outer silhouette clip — same as SHADOW_INSET_FRAG_SRC, 2-px AA band.
vec2 half_outer = u_size * 0.5;
float r_outer = corner_radius(c, u_radii);
r_outer = min(r_outer, min(half_outer.x, half_outer.y));
vec2 q_outer = abs(c) - (half_outer - vec2(r_outer));
float d_outer = min(max(q_outer.x, q_outer.y), 0.0) + length(max(q_outer, 0.0)) - r_outer;
float outer_coverage = 1.0 - smoothstep(-1.0, 1.0, d_outer);
// Offset-shifted inner SDF → Gaussian intensity. Per-corner inner
// radii match the outer shape eroded by `u_spread`.
vec2 p_shifted = p - u_offset;
vec2 c_shifted = p_shifted - u_size * 0.5;
vec2 half_inner = half_outer - vec2(u_spread);
half_inner = max(half_inner, vec2(1e-3));
vec4 inner_radii = max(u_radii - vec4(u_spread), vec4(0.0));
float r_inner = corner_radius(c_shifted, inner_radii);
r_inner = min(r_inner, min(half_inner.x, half_inner.y));
vec2 q_inner = abs(c_shifted) - (half_inner - vec2(r_inner));
float d_inner = min(max(q_inner.x, q_inner.y), 0.0) + length(max(q_inner, 0.0)) - r_inner;
float intensity;
if (d_inner >= 0.0)
{
intensity = 1.0;
}
else
{
float s = max(u_sigma, 0.5);
intensity = exp(-(d_inner * d_inner) / (2.0 * s * s));
}
intensity *= outer_coverage;
float mask = u_color.a * intensity;
// Sample the snapshot at the fragment's FBO position. The snapshot
// holds premultiplied content; divide by alpha before applying the
// straight-alpha Overlay formula. Guard alpha=0 to avoid NaNs.
vec2 snap_uv = gl_FragCoord.xy / u_canvas_size;
vec4 snap = texture2D(u_snapshot, snap_uv);
vec3 base = snap.a > 0.0 ? snap.rgb / snap.a : vec3(0.0);
vec3 src = u_color.rgb;
// Per-channel Overlay. `step(0.5, base)` yields 0 where base < 0.5
// and 1 otherwise; `mix` picks multiply vs screen accordingly.
vec3 multiply = 2.0 * base * src;
vec3 screen = 1.0 - 2.0 * (1.0 - base) * (1.0 - src);
vec3 overlay = mix(multiply, screen, step(0.5, base));
// Output premul: `(overlay * mask, mask)`. Combined with the premul
// over blend this replaces the base by `overlay` wherever mask == 1
// and leaves it untouched where mask == 0.
gl_FragColor = vec4(overlay * mask, mask);
}
"##;