Rasterization & Dithering
Relevant source files
The following files were used as context for generating this wiki page:
The Rasterization & Dithering system in SwiftDitherKit is responsible for transforming high-level chart data into low-resolution, ordered-dithered images. Unlike standard vector-based SwiftUI charts, this system uses a custom software renderer that manipulates raw pixel buffers to achieve a retro aesthetic characterized by discrete cell patterns and texture variants.
The system is built around a "cell-based" approach where a high-resolution view is mapped to a smaller pixel grid. This grid is then processed using a Bayer threshold matrix to determine pixel occupancy and transparency, resulting in textures such as gradients, dots, and hatches.
Core Raster Architecture
The foundation of the rendering system is the DitherRaster structure and the DitherKernel. The raster maintains a 32-bit RGBA pixel buffer, while the kernel contains the logic for applying dithering algorithms.
DitherRaster Data Structure
The DitherRaster acts as a managed wrapper around a [UInt8] byte array. It handles alpha blending and the conversion of raw bytes into a CGImage for display in SwiftUI.
Pixel Processing Logic
The system uses a fixed cell size of 2.0 points for its grid. The DitherKernel.backingSize function calculates the required pixel dimensions based on the input view size, capping the maximum resolution to 520 columns and 200 rows to maintain performance and aesthetic consistency.
Sources: Sources/SwiftDitherKit/DitherRaster.swift:76-80, Tests/SwiftDitherKitTests/DitherRasterTests.swift:23-29
The Dithering Algorithm
Ordered dithering is achieved using a 4x4 Bayer matrix. This matrix provides threshold values that determine whether a pixel at a specific coordinate should be "lit" based on its intended density.
Bayer Threshold Matrix
The matrix is normalized to values between 0 and 1. The threshold(x:y:) function uses bitwise operations to wrap coordinates, ensuring the pattern repeats infinitely across the raster.
| Matrix Position | Normalized Value |
|---|---|
| (0,0) | 0.5 / 16 |
| (1,0) | 8.5 / 16 |
| (3,3) | 5.5 / 16 |
Sources: Sources/SwiftDitherKit/DitherRaster.swift:82-92, Tests/SwiftDitherKitTests/DitherRasterTests.swift:16-21
Texture Variants
The DitherVariant enumeration defines how the kernel fills shapes. Each variant modifies the threshold comparison logic:
- Solid: Fills the entire area without dithering.
- Gradient: Uses the standard Bayer threshold compared against a vertical density value.
- Dotted: Filters out pixels where the threshold is not met, creating a sparse pattern.
- Hatched: Uses a diagonal pattern by checking
(x + y) & 3.
Sources: Sources/SwiftDitherKit/DitherRaster.swift:115-136, Sources/SwiftDitherKit/DitherTypes.swift:39-44
Rendering Pipeline
The rendering process follows a structured path from SwiftUI input to a displayed raster image.
Component Roles
| Component | Responsibility |
|---|---|
DitherRenderer | Orchestrates the rendering of Cartesian, Pie, and Radar charts. |
DitherLinearScale | Maps domain values to the pixel grid coordinates. |
DitherGeometry | Calculates shapes (bands, bars, polygons) to be dithered. |
DitherPalette | Converts DitherColor into 8-bit DitherRGB triplets. |
Sources: Sources/SwiftDitherKit/DitherRenderers.swift:35-50, Sources/SwiftDitherKit/DitherTypes.swift:156-170
Sparkle System
A unique feature of the rasterization process is the DitherSparkleField. This system generates transient, animated pixels that appear on top of the dithered raster for Area and Line charts.
Sparkle Generation Logic
Sparkles are generated based on a seed derived from the series index and datum position. They are constrained to the area between the "top" and "floor" of a dithered series, ensuring they only appear within the rendered shapes.
let depth = Double((seed * 53 + 7) % 100) / 100
let y = Int((top[x] + depth * (floor[x] - top[x])).rounded())
Sources: Sources/SwiftDitherKit/DitherSparkles.swift:34-58
Sparkle Animation
The DitherSparkleMotion system calculates alpha values for center pixels and "flares" (adjacent pixels) using a sine-based twinkle function.
- Tick Interval: 0.1 seconds.
- Visibility Threshold: 0.55 alpha.
- Reduce Motion Support: If enabled, sparkles remain at a steady 0.85 twinkle value without flashing.
Sources: Sources/SwiftDitherKit/DitherSparkles.swift:101-138, Sources/SwiftDitherKit/DitherRasterView.swift:239-255
Image Composition & Post-Processing
Once the raster is generated, the DitherRasterLayers view handles the final visual presentation, including optional "Bloom" effects.
Bloom Styles
The system can apply a post-processing glow by layering a blurred, brightened version of the raster behind the main image.
| Bloom Case | Blur Radius | Brightness | Saturation | Opacity |
|---|---|---|---|---|
| Low | 3 | 0.08 | 1.4 | 0.70 |
| High | 5 | 0.14 | 1.5 | 0.78 |
| Aura | 15 | 0.35 | 3.0 | 0.10 |
Sources: Sources/SwiftDitherKit/DitherTypes.swift:172-187, Sources/SwiftDitherKit/DitherRasterView.swift:218-235
Summary
The Rasterization & Dithering system provides a high-performance software rendering pipeline for SwiftUI. By decoupling the domain logic (geometry and scaling) from the drawing logic (DitherKernel and Bayer matrix), the system achieves a consistent lo-fi aesthetic across various chart types while maintaining interactive features like selection and tooltips. The inclusion of a dedicated sparkle system and bloom post-processing further enhances the retro-digital visual style.