///|
let visual_mode_fill : Double = 0.0
///|
let visual_mode_stroke : Double = 1.0
///|
let visual_mode_shadow : Double = 2.0
///|
let visual_mode_path : Double = 3.0
///|
let visual_vertex_stride : UInt64 = 88UL
///|
let advanced_vertex_stride : UInt64 = 176UL
///|
fn visual_shader_wgsl() -> String {
let shader =
#|struct VSOut {
#| @builtin(position) pos: vec4,
#| @location(0) local: vec2,
#| @location(1) size_radius_mode_stroke: vec4,
#| @location(2) blur_start: vec4,
#| @location(3) end: vec2,
#| @location(4) color0: vec4,
#| @location(5) color1: vec4,
#|};
#|
#|@vertex
#|fn vs_main(
#| @location(0) pos: vec2,
#| @location(1) local: vec2,
#| @location(2) size_radius_mode_stroke: vec4,
#| @location(3) blur_start: vec4,
#| @location(4) end: vec2,
#| @location(5) color0: vec4,
#| @location(6) color1: vec4,
#|) -> VSOut {
#| var out: VSOut;
#| out.pos = vec4(pos, 0.0, 1.0);
#| out.local = local;
#| out.size_radius_mode_stroke = size_radius_mode_stroke;
#| out.blur_start = blur_start;
#| out.end = end;
#| out.color0 = color0;
#| out.color1 = color1;
#| return out;
#|}
#|
#|fn rounded_box_sdf(p: vec2, half_size: vec2, radius: f32) -> f32 {
#| let r = min(radius, min(half_size.x, half_size.y));
#| let q = abs(p) - (half_size - vec2(r, r));
#| return length(max(q, vec2(0.0, 0.0))) + min(max(q.x, q.y), 0.0) - r;
#|}
#|
#|fn linear_gradient_color(local: vec2, start: vec2, end: vec2, c0: vec4, c1: vec4) -> vec4 {
#| let axis = end - start;
#| let denom = max(dot(axis, axis), 0.0001);
#| let t = clamp(dot(local - start, axis) / denom, 0.0, 1.0);
#| return mix(c0, c1, t);
#|}
#|
#|fn radial_gradient_color(local: vec2, center: vec2, radius: f32, c0: vec4, c1: vec4) -> vec4 {
#| let t = clamp(distance(local, center) / max(radius, 0.0001), 0.0, 1.0);
#| return mix(c0, c1, t);
#|}
#|
#|fn brush_color(local: vec2, start: vec2, end: vec2, c0: vec4, c1: vec4, kind: f32) -> vec4 {
#| if kind > 0.5 {
#| return radial_gradient_color(local, start, end.x, c0, c1);
#| }
#| return linear_gradient_color(local, start, end, c0, c1);
#|}
#|
#|@fragment
#|fn fs_main(in: VSOut) -> @location(0) vec4 {
#| let width = in.size_radius_mode_stroke.x;
#| let height = in.size_radius_mode_stroke.y;
#| let radius = in.size_radius_mode_stroke.z;
#| let mode = in.size_radius_mode_stroke.w;
#| let stroke_width = in.blur_start.x;
#| let blur_radius = in.blur_start.y;
#| let start = in.blur_start.zw;
#| let end = in.end;
#| if mode > 2.5 {
#| return brush_color(in.local, start, end, in.color0, in.color1, blur_radius);
#| }
#| let center = vec2(width * 0.5, height * 0.5);
#| let half_size = center;
#| let dist = rounded_box_sdf(in.local - center, half_size, radius);
#| var alpha = 1.0 - smoothstep(-0.5, 0.5, dist);
#| if mode > 1.5 {
#| let spread = max(blur_radius, 1.0);
#| alpha = 1.0 - smoothstep(-spread, spread, dist);
#| alpha = alpha * alpha * in.color0.a;
#| return vec4(in.color0.rgb, alpha);
#| }
#| if mode > 0.5 {
#| let inner_radius = max(radius - stroke_width, 0.0);
#| let inner_half = max(half_size - vec2(stroke_width, stroke_width), vec2(0.0, 0.0));
#| let inner_dist = rounded_box_sdf(in.local - center, inner_half, inner_radius);
#| let inner_alpha = 1.0 - smoothstep(-0.5, 0.5, inner_dist);
#| alpha = max(alpha - inner_alpha, 0.0);
#| }
#| let color = brush_color(in.local, start, end, in.color0, in.color1, blur_radius);
#| return vec4(color.rgb, color.a * alpha);
#|}
#|
shader
}
///|
fn advanced_shader_wgsl() -> String {
let shader =
#|struct VSOut {
#| @builtin(position) pos: vec4,
#| @location(0) uv: vec2,
#| @location(1) meta0: vec4,
#| @location(2) meta1: vec4,
#| @location(3) color0: vec4,
#| @location(4) color1: vec4,
#| @location(5) matrix0: vec4,
#| @location(6) matrix1: vec4,
#| @location(7) matrix2: vec4,
#| @location(8) matrix3: vec4,
#| @location(9) matrix4: vec4,
#| @location(10) rect_mask: vec4,
#|};
#|
#|@vertex
#|fn vs_main(
#| @location(0) pos: vec2,
#| @location(1) uv: vec2,
#| @location(2) meta0: vec4,
#| @location(3) meta1: vec4,
#| @location(4) color0: vec4,
#| @location(5) color1: vec4,
#| @location(6) matrix0: vec4,
#| @location(7) matrix1: vec4,
#| @location(8) matrix2: vec4,
#| @location(9) matrix3: vec4,
#| @location(10) matrix4: vec4,
#| @location(11) rect_mask: vec4,
#|) -> VSOut {
#| var out: VSOut;
#| out.pos = vec4(pos, 0.0, 1.0);
#| out.uv = uv;
#| out.meta0 = meta0;
#| out.meta1 = meta1;
#| out.color0 = color0;
#| out.color1 = color1;
#| out.matrix0 = matrix0;
#| out.matrix1 = matrix1;
#| out.matrix2 = matrix2;
#| out.matrix3 = matrix3;
#| out.matrix4 = matrix4;
#| out.rect_mask = rect_mask;
#| return out;
#|}
#|
#|@group(0) @binding(0) var layer_sampler : sampler;
#|@group(0) @binding(1) var layer_texture : texture_2d;
#|@group(0) @binding(2) var backdrop_texture : texture_2d;
#|
#|fn checker(local: vec2, amount: f32, c0: vec4, c1: vec4) -> vec4 {
#| let scale = max(amount, 8.0);
#| let cell = floor(local * scale);
#| let alt = (i32(cell.x) + i32(cell.y)) & 1;
#| if alt == 0 {
#| return c0;
#| }
#| return c1;
#|}
#|
#|fn vignette(local: vec2, amount: f32, c0: vec4) -> vec4 {
#| let centered = local * 2.0 - vec2(1.0, 1.0);
#| let fade = 1.0 - smoothstep(0.25, max(amount, 0.8), length(centered));
#| return vec4(c0.rgb * fade, c0.a);
#|}
#|
#|fn blur_sample(uv: vec2, amount: f32) -> vec4 {
#| let dims = vec2(textureDimensions(layer_texture));
#| let step = vec2(1.0, 1.0) / max(dims, vec2(1.0, 1.0));
#| let radius = max(amount, 1.0);
#| var color = vec4(0.0, 0.0, 0.0, 0.0);
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2(-1.0, -1.0)) * 0.0625;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2( 0.0, -1.0)) * 0.125;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2( 1.0, -1.0)) * 0.0625;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2(-1.0, 0.0)) * 0.125;
#| color += textureSample(layer_texture, layer_sampler, uv) * 0.25;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2( 1.0, 0.0)) * 0.125;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2(-1.0, 1.0)) * 0.0625;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2( 0.0, 1.0)) * 0.125;
#| color += textureSample(layer_texture, layer_sampler, uv + step * radius * vec2( 1.0, 1.0)) * 0.0625;
#| return color;
#|}
#|
#|fn rounded_box_sdf_advanced(p: vec2, half_size: vec2, radius: f32) -> f32 {
#| let r = min(radius, min(half_size.x, half_size.y));
#| let q = abs(p) - (half_size - vec2(r, r));
#| return length(max(q, vec2(0.0, 0.0))) + min(max(q.x, q.y), 0.0) - r;
#|}
#|
#|fn apply_filter(color: vec4, kind: i32, amount: f32, m0: vec4, m1: vec4, m2: vec4, m3: vec4, m4: vec4) -> vec4 {
#| if kind == 2 {
#| let luma = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
#| return vec4(mix(vec3(luma, luma, luma), color.rgb, amount), color.a);
#| }
#| if kind == 3 {
#| return vec4(color.rgb * amount, color.a);
#| }
#| if kind == 4 {
#| return vec4((color.rgb - vec3(0.5, 0.5, 0.5)) * amount + vec3(0.5, 0.5, 0.5), color.a);
#| }
#| if kind == 5 {
#| let v = vec4(color.rgb, color.a);
#| return vec4(
#| dot(v, m0) + m4.x,
#| dot(v, m1) + m4.y,
#| dot(v, m2) + m4.z,
#| dot(v, m3) + m4.w,
#| );
#| }
#| return color;
#|}
#|
#|fn overlay_channel(base: f32, source: f32) -> f32 {
#| if base <= 0.5 {
#| return 2.0 * base * source;
#| }
#| return 1.0 - 2.0 * (1.0 - base) * (1.0 - source);
#|}
#|
#|fn overlay_rgb(base: vec3, source: vec3) -> vec3 {
#| return vec3(
#| overlay_channel(base.r, source.r),
#| overlay_channel(base.g, source.g),
#| overlay_channel(base.b, source.b),
#| );
#|}
#|
#|@fragment
#|fn fs_main(in: VSOut) -> @location(0) vec4 {
#| let opacity = in.meta0.x;
#| let shader_kind = i32(in.meta0.z + 0.5);
#| let effect_amount = in.meta0.w;
#| let filter_kind = i32(in.meta1.z + 0.5);
#| let filter_amount = in.meta1.w;
#| var sample = textureSample(layer_texture, layer_sampler, in.uv);
#| if shader_kind == 1 {
#| sample = checker(in.meta1.xy, effect_amount, in.color0, in.color1);
#| } else if shader_kind == 2 {
#| sample = in.color0;
#| } else if shader_kind == 3 {
#| sample = mix(in.color0, in.color1, clamp(in.meta1.x, 0.0, 1.0));
#| } else if shader_kind == 4 {
#| sample = vignette(in.meta1.xy, effect_amount, in.color0);
#| } else if shader_kind == 5 {
#| sample = in.color0;
#| }
#| if filter_kind == 1 {
#| sample = blur_sample(in.uv, filter_amount);
#| }
#| sample = apply_filter(sample, filter_kind, filter_amount, in.matrix0, in.matrix1, in.matrix2, in.matrix3, in.matrix4);
#| var mask_alpha = 1.0;
#| if in.rect_mask.w > 0.5 {
#| let mask_size = in.rect_mask.xy;
#| let local = in.meta1.xy * mask_size;
#| let dist = rounded_box_sdf_advanced(local - mask_size * 0.5, mask_size * 0.5, in.rect_mask.z);
#| mask_alpha = 1.0 - smoothstep(-0.5, 0.5, dist);
#| }
#| let blend_mode = i32(in.meta0.y + 0.5);
#| let source_alpha = clamp(sample.a * opacity * mask_alpha, 0.0, 1.0);
#| var source_rgb = sample.rgb;
#| if shader_kind == 0 && sample.a > 0.0001 {
#| source_rgb = clamp(sample.rgb / sample.a, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0));
#| }
#| if blend_mode == 1 {
#| return vec4(source_rgb * source_alpha + vec3(1.0 - source_alpha), source_alpha);
#| }
#| if blend_mode == 2 {
#| return vec4(source_rgb * source_alpha, source_alpha);
#| }
#| if blend_mode == 3 {
#| let backdrop = textureSample(backdrop_texture, layer_sampler, in.uv);
#| let blended = overlay_rgb(backdrop.rgb, source_rgb);
#| let out_rgb = mix(backdrop.rgb, blended, source_alpha);
#| let out_alpha = source_alpha + backdrop.a * (1.0 - source_alpha);
#| return vec4(out_rgb, out_alpha);
#| }
#| if blend_mode == 4 {
#| return vec4(source_rgb * source_alpha + vec3(1.0 - source_alpha), source_alpha);
#| }
#| if blend_mode == 5 {
#| return vec4(source_rgb * source_alpha, source_alpha);
#| }
#| return vec4(source_rgb, source_alpha);
#|}
#|
shader
}