Skip to content

Commit 9601e61

Browse files
authored
Merge pull request #16 from Zoxc/blurred-rect
Add blurred rectangles
2 parents 74e6ea1 + 9d638a4 commit 9601e61

File tree

4 files changed

+101
-0
lines changed

4 files changed

+101
-0
lines changed

src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,39 @@ impl Vger {
480480
self.render(prim);
481481
}
482482

483+
/// Fills a blurred rectangle.
484+
pub fn fill_blurred_rect<Rect: Into<LocalRect>>(
485+
&mut self,
486+
rect: Rect,
487+
radius: f32,
488+
paint_index: PaintIndex,
489+
blur_radius: f32,
490+
) {
491+
let mut prim = Prim::default();
492+
prim.prim_type = PrimType::BlurredRect as u32;
493+
let r: LocalRect = rect.into();
494+
let min = r.min();
495+
let max = r.max();
496+
prim.cvs[0] = min.x;
497+
prim.cvs[1] = min.y;
498+
prim.cvs[2] = max.x;
499+
prim.cvs[3] = max.y;
500+
prim.cvs[4] = blur_radius;
501+
prim.radius = radius;
502+
prim.paint = paint_index.index as u32;
503+
prim.quad_bounds = [
504+
min.x - blur_radius * 3.0,
505+
min.y - blur_radius * 3.0,
506+
max.x + blur_radius * 3.0,
507+
max.y + blur_radius * 3.0,
508+
];
509+
prim.tex_bounds = prim.quad_bounds;
510+
prim.xform = self.add_xform() as u32;
511+
prim.scissor = self.add_scissor() as u32;
512+
513+
self.render(prim);
514+
}
515+
483516
/// Strokes a rectangle.
484517
pub fn stroke_rect(
485518
&mut self,

src/prim.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub enum PrimType {
3030

3131
/// Path fills.
3232
PathFill,
33+
34+
/// Rounded blurred rectangle.
35+
BlurredRect,
3336
}
3437

3538
#[derive(Copy, Clone, Default)]

src/shader.wgsl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const vgerGlyph = 8;
3030
/// Path fills.
3131
const vgerPathFill = 9;
3232

33+
/// Rounded blurred rectangle.
34+
const vgerBlurredRect = 10;
35+
3336
struct Prim {
3437

3538
/// Min and max coordinates of the quad we're rendering.
@@ -343,6 +346,10 @@ fn sdPrimBounds(prim: Prim) -> BBox {
343346
b = expand(b, cvs.cvs[i32(prim.start)+i]);
344347
}
345348
}
349+
case 10u: { // vgerBlurredRect
350+
b.min = prim.cv0;
351+
b.max = prim.cv1;
352+
}
346353
default: {}
347354
}
348355
return b;
@@ -463,6 +470,26 @@ fn sdPrim(prim: Prim, p: vec2<f32>, filterWidth: f32) -> f32 {
463470
d = d * s;
464471
break;
465472
}
473+
case 10u: { // vgerBlurredRect
474+
let blur_radius = prim.cv2.x;
475+
let center = 0.5*(prim.cv1 + prim.cv0);
476+
let half_size = 0.5*(prim.cv1 - prim.cv0);
477+
let point = p - center;
478+
479+
let low = point.y - half_size.y;
480+
let high = point.y + half_size.y;
481+
let start = clamp(-3.0 * blur_radius, low, high);
482+
let end = clamp(3.0 * blur_radius, low, high);
483+
484+
let step = (end - start) / 4.0;
485+
var y = start + step * 0.5;
486+
var value = 0.0;
487+
for (var i: i32 = 0; i < 4; i++) {
488+
value += roundedBoxShadowX(point.x, point.y - y, blur_radius, prim.radius, half_size) * gaussian(y, blur_radius) * step;
489+
y += step;
490+
}
491+
d = 1.0 - value * 4.0;
492+
}
466493
default: { }
467494
}
468495
return d;
@@ -619,6 +646,27 @@ fn toLinear(s: f32) -> f32
619646
return pow((s + 0.055)/1.055, 2.4);
620647
}
621648

649+
// This approximates the error function, needed for the gaussian integral
650+
fn erf(x: vec2<f32>) -> vec2<f32> {
651+
let s = sign(x);
652+
let a = abs(x);
653+
var y = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
654+
y *= y;
655+
return s - s / (y * y);
656+
}
657+
658+
fn gaussian(x: f32, sigma: f32) -> f32 {
659+
let pi: f32 = 3.141592653589793;
660+
return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
661+
}
662+
663+
fn roundedBoxShadowX(x: f32, y: f32, sigma: f32, corner: f32, halfSize: vec2<f32>) -> f32 {
664+
let delta = min(halfSize.y - corner - abs(y), 0.0);
665+
let curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
666+
let integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
667+
return integral.y - integral.x;
668+
}
669+
622670
@fragment
623671
fn fs_main(
624672
in: VertexOutput,

tests/tests.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ fn fill_circle_translate() {
8787
render_test(&mut vger, &device, &queue, "circle_translate.png", false);
8888
}
8989

90+
#[test]
91+
fn fill_blurred_rect() {
92+
let (device, queue) = setup();
93+
94+
let mut vger = Vger::new(
95+
device.clone(),
96+
queue.clone(),
97+
wgpu::TextureFormat::Rgba8UnormSrgb,
98+
);
99+
100+
vger.begin(512.0, 512.0, 1.0);
101+
let cyan = vger.color_paint(Color::CYAN);
102+
vger.fill_blurred_rect(euclid::rect(100.0, 100.0, 100.0, 100.0), 10.0, cyan, 10.0);
103+
104+
render_test(&mut vger, &device, &queue, "blurred_rect.png", false);
105+
}
106+
90107
#[test]
91108
fn fill_rect() {
92109
let (device, queue) = setup();

0 commit comments

Comments
 (0)