A high-performance library implementing 2D signed distance functions (SDFs) with multiple rendering modes and SIMD optimizations. Implemented with Nim but compatible with C/C++. Drop a note if you'd like a C API.
These SDFs are targeted and tune for quickly making drop shadows for GUIs. These can be used with graphical renderers to speed up expensive drop and inset shadows.
- Fast SDF Implementation: Optimized implementations of common 2D shapes
- Multiple Rendering Modes: Support for various anti-aliasing and effect techniques
- SIMD Acceleration: Leverages SIMD (SSE2/NEON) instructions for maximum CPU performance
- Pixie Integration: Seamless integration with the Pixie graphics library
- Flexible API: Easy-to-use interface for rendering SDFs to images
Creating good looking drop and inset shadows is traditionally done using Gaussian Blur. This requires a 2D convolution which is slow even if done in X and then Y directions.
It's also a bit wasteful since we know the vector shape for most basic GUI elements. Why can't we use that to reduce the computational overhead?
That's where SDFs come in. They allow efficiently getting the nearest distance to a shape for any point and whether it's inside or outside the shape. We can then take this value (a linear gradient) and apply a 1D gaussian function to it.
From this we get a good looking, if not perfect, drop shadow! There's a slight difference it seems between the SDF + Gaussian 1D function vs a 2D true Gaussian blur on corners.
- Rounded Rectangle: Fully configurable rounded rectangles with independent corner radii
- Chamfer Box: Rectangles with chamfered (cut) corners
- Circle: Perfect circles with configurable radius
- Box: Simple rectangles/boxes with sharp corners
- Ellipse: Elliptical shapes with configurable semi-axes
- Quadratic Bézier Curve: Smooth curves defined by three control points
- Arc: Circular arc segments with configurable aperture and thickness
- Parallelogram: Four-sided shapes with parallel opposite sides and configurable skew
- Pie: Pie slice/sector shapes with configurable aperture and radius
- Ring: Ring/annular segments with configurable aperture, radius, and thickness
- Clip: Sharp edges without anti-aliasing
- Clip AA: Sharp edges with anti-aliasing
- Annular: Creates ring/annular shapes
- Annular AA: Anti-aliased ring shapes
- Feather: Standard anti-aliased edges with customizable factor
- Feather Inverse: Inverted feather anti-aliasing
- Feather Gaussian: Gaussian-based anti-aliasing for smooth edges
- Drop Shadow: Gaussian-based drop shadow effects
- Inset Shadow: Inner shadow effects
- Inset Shadow Annular: Annular inner shadow effects
Pixie Shadow | 456 ms | 476 ms | 1.0x |
Clip | 5 ms | 20 ms | 4.0x |
Clip AA | 6 ms | 30 ms | 5.0x |
Annular | 5 ms | 22 ms | 4.4x |
Annular AA | 6 ms | 33 ms | 5.5x |
Feather | 6 ms | 23 ms | 3.8x |
Feather Inverse | 6 ms | 26 ms | 4.3x |
Feather Gaussian | 7 ms | 24 ms | 3.4x |
Drop Shadow | 7 ms | 24 ms | 3.4x |
Inset Shadow | 8 ms | 24 ms | 3.0x |
Inset Shadow Annular | 7 ms | 24 ms | 3.4x |
Performance measured on rounded rectangles (300x300 image). SIMD provides 3-5x performance improvement. SDF functions are 15-65x faster than traditional Pixie rendering with shadows.
Shape-specific Performance:
- Simple shapes (Circle, Box): Fastest rendering, full SIMD optimization
- Medium complexity (Rounded Box, Chamfer Box, Arc, Pie, Ring): Good SIMD optimization
- Complex shapes (Ellipse, Bézier, Parallelogram): Partial SIMD optimization with scalar fallbacks for complex math
Here are examples of the different rendering modes applied to rounded rectangles:
Add to your .nimble file:
Or install directly:
Calculate the signed distance from a point to a rounded rectangle.
- p: Point to test
- b: Box half-extents (width/2, height/2)
- r: Corner radii as Vec4 (x=top-right, y=bottom-right, z=bottom-left, w=top-left)
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a chamfered rectangle.
- p: Point to test
- b: Box half-extents (width/2, height/2)
- chamfer: Chamfer amount
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a circle.
- p: Point to test
- r: Circle radius
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a box/rectangle.
- p: Point to test
- b: Box half-extents (width/2, height/2)
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to an ellipse.
- p: Point to test
- ab: Ellipse semi-axes (width/2, height/2)
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a quadratic Bézier curve.
- p: Point to test
- A, B, C: Control points of the Bézier curve
- Returns: Distance to the curve (always positive for curves)
Calculate the signed distance from a point to an arc.
- p: Point to test
- sc: Sin/cos of the arc's aperture (sc.x = sin, sc.y = cos)
- ra: Inner radius
- rb: Thickness (outer radius difference)
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a parallelogram.
- p: Point to test
- wi: Width
- he: Height
- sk: Skew
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a pie slice.
- p: Point to test
- c: Sin/cos of the pie's aperture (c.x = sin, c.y = cos)
- r: Radius
- Returns: Signed distance (negative inside, positive outside)
Calculate the signed distance from a point to a ring.
- p: Point to test
- n: Sin/cos of the ring's aperture (n.x = sin, n.y = cos)
- r: Radius
- th: Thickness
- Returns: Signed distance (negative inside, positive outside)
Generic function to render shapes to an image using SDF.
- image: Target image to render to
- center: Center position of the shape
- wh: Width and height of the shape (ignored for some shapes like circles, arcs, etc.)
- params: Shape parameters (see Shape Parameters section)
- pos: Color for inside the shape
- neg: Color for outside the shape
- factor: Anti-aliasing factor (default: 4.0)
- spread: Spread amount for shadow effects (default: 0.0)
- mode: Rendering mode (see SDFMode enum)
SDFY is designed to work with multiple image types through a flexible generic interface. You can use:
- Pixie Image: The standard pixie.Image type from the Pixie graphics library
- SdfImage: The included SdfImage type for lightweight image operations
- Custom Image Types: Any type that implements the required interface
For an image type to work with SDFY's drawSdfShape function, it must provide:
- SdfImage: Lightweight, minimal dependencies, included with SDFY
- Pixie Image: Full-featured graphics library with extensive drawing capabilities, file I/O, and more
Both types implement the same interface and can be used interchangeably with SDFY functions.
This library is based on the excellent work by Íñigo Quílez on 2D distance functions.
Licensed under the Apache License 2.0. See LICENSE file for details.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.