Skip to content

Commit

Permalink
Merge pull request #16 from Zoxc/blurred-rect
Browse files Browse the repository at this point in the history
Add blurred rectangles
  • Loading branch information
wtholliday committed Jan 5, 2024
2 parents 74e6ea1 + 9d638a4 commit 9601e61
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,39 @@ impl Vger {
self.render(prim);
}

/// Fills a blurred rectangle.
pub fn fill_blurred_rect<Rect: Into<LocalRect>>(
&mut self,
rect: Rect,
radius: f32,
paint_index: PaintIndex,
blur_radius: f32,
) {
let mut prim = Prim::default();
prim.prim_type = PrimType::BlurredRect as u32;
let r: LocalRect = rect.into();
let min = r.min();
let max = r.max();
prim.cvs[0] = min.x;
prim.cvs[1] = min.y;
prim.cvs[2] = max.x;
prim.cvs[3] = max.y;
prim.cvs[4] = blur_radius;
prim.radius = radius;
prim.paint = paint_index.index as u32;
prim.quad_bounds = [
min.x - blur_radius * 3.0,
min.y - blur_radius * 3.0,
max.x + blur_radius * 3.0,
max.y + blur_radius * 3.0,
];
prim.tex_bounds = prim.quad_bounds;
prim.xform = self.add_xform() as u32;
prim.scissor = self.add_scissor() as u32;

self.render(prim);
}

/// Strokes a rectangle.
pub fn stroke_rect(
&mut self,
Expand Down
3 changes: 3 additions & 0 deletions src/prim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub enum PrimType {

/// Path fills.
PathFill,

/// Rounded blurred rectangle.
BlurredRect,
}

#[derive(Copy, Clone, Default)]
Expand Down
48 changes: 48 additions & 0 deletions src/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const vgerGlyph = 8;
/// Path fills.
const vgerPathFill = 9;

/// Rounded blurred rectangle.
const vgerBlurredRect = 10;

struct Prim {

/// Min and max coordinates of the quad we're rendering.
Expand Down Expand Up @@ -343,6 +346,10 @@ fn sdPrimBounds(prim: Prim) -> BBox {
b = expand(b, cvs.cvs[i32(prim.start)+i]);
}
}
case 10u: { // vgerBlurredRect
b.min = prim.cv0;
b.max = prim.cv1;
}
default: {}
}
return b;
Expand Down Expand Up @@ -463,6 +470,26 @@ fn sdPrim(prim: Prim, p: vec2<f32>, filterWidth: f32) -> f32 {
d = d * s;
break;
}
case 10u: { // vgerBlurredRect
let blur_radius = prim.cv2.x;
let center = 0.5*(prim.cv1 + prim.cv0);
let half_size = 0.5*(prim.cv1 - prim.cv0);
let point = p - center;

let low = point.y - half_size.y;
let high = point.y + half_size.y;
let start = clamp(-3.0 * blur_radius, low, high);
let end = clamp(3.0 * blur_radius, low, high);

let step = (end - start) / 4.0;
var y = start + step * 0.5;
var value = 0.0;
for (var i: i32 = 0; i < 4; i++) {
value += roundedBoxShadowX(point.x, point.y - y, blur_radius, prim.radius, half_size) * gaussian(y, blur_radius) * step;
y += step;
}
d = 1.0 - value * 4.0;
}
default: { }
}
return d;
Expand Down Expand Up @@ -619,6 +646,27 @@ fn toLinear(s: f32) -> f32
return pow((s + 0.055)/1.055, 2.4);
}

// This approximates the error function, needed for the gaussian integral
fn erf(x: vec2<f32>) -> vec2<f32> {
let s = sign(x);
let a = abs(x);
var y = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
y *= y;
return s - s / (y * y);
}

fn gaussian(x: f32, sigma: f32) -> f32 {
let pi: f32 = 3.141592653589793;
return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
}

fn roundedBoxShadowX(x: f32, y: f32, sigma: f32, corner: f32, halfSize: vec2<f32>) -> f32 {
let delta = min(halfSize.y - corner - abs(y), 0.0);
let curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
let integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
return integral.y - integral.x;
}

@fragment
fn fs_main(
in: VertexOutput,
Expand Down
17 changes: 17 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ fn fill_circle_translate() {
render_test(&mut vger, &device, &queue, "circle_translate.png", false);
}

#[test]
fn fill_blurred_rect() {
let (device, queue) = setup();

let mut vger = Vger::new(
device.clone(),
queue.clone(),
wgpu::TextureFormat::Rgba8UnormSrgb,
);

vger.begin(512.0, 512.0, 1.0);
let cyan = vger.color_paint(Color::CYAN);
vger.fill_blurred_rect(euclid::rect(100.0, 100.0, 100.0, 100.0), 10.0, cyan, 10.0);

render_test(&mut vger, &device, &queue, "blurred_rect.png", false);
}

#[test]
fn fill_rect() {
let (device, queue) = setup();
Expand Down

0 comments on commit 9601e61

Please sign in to comment.