Canvas Rendering Context
Relevant source files
The following files were used as context for generating this wiki page:
The Canvas Rendering Context in the guiltty project refers to the system responsible for managing pixel-level 2D graphics, compositing shapes and sprites, and facilitating the transmission of this state to terminal backends. At its core is the Canvas struct, which maintains a row-major RGBA8 pixel buffer that serves as the primary render target for all drawing operations. Sources: crates/guiltty-core/src/lib.rs:1-8, README.md:1-5
This system is designed to be backend-agnostic, providing a high-level API for drawing text, geometric shapes, and movable sprites. The rendered result is eventually consumed by a Backend implementation, such as the KittyBackend, which translates the raw pixel data into terminal-specific escape sequences, specifically leveraging the Kitty graphics protocol. Sources: crates/guiltty-core/src/lib.rs:88-99, crates/guiltty-kitty/src/lib.rs:1-5
Core Data Structures
The rendering context relies on several primitive structures to define spatial coordinates, dimensions, and color data.
| Structure | Description | Fields |
|---|---|---|
Color | RGBA8 color representation (4 bytes per pixel). | r, g, b, a (u8) |
Point | A pixel-addressable coordinate (origin top-left). | x, y (i32) |
Rect | A pixel-addressable rectangle. | x, y (i32), width, height (u32) |
Canvas | The main render target and pixel buffer. | id (u64), pixels (Vec) |
Sources: crates/guiltty-core/src/lib.rs:6-75
Canvas Architecture
The Canvas is the central authority for pixel manipulation. Every Canvas instance is assigned a unique id via a global atomic counter to prevent footprint restoration errors when working with multiple canvases. Sources: crates/guiltty-core/src/lib.rs:102-114
The diagram shows the internal structure of the Canvas and its relationship with the Color primitive. Sources: crates/guiltty-core/src/lib.rs:105-130
Drawing Logic and Algorithms
The context implements several standard rasterization algorithms to manipulate the pixel buffer.
Geometric Shapes
The draw_shape method dispatches to specific rasterizers based on the Shape variant:
- Lines: Uses Bresenham's line algorithm combined with Liang-Barsky clipping to ensure only visible segments are processed. Sources:
crates/guiltty-core/src/lib.rs:408-444 - Ellipses/Circles: Uses a scan-range clipped inclusion test performed in
f64to avoid integer overflow on large radii. Sources:crates/guiltty-core/src/lib.rs:475-502 - Triangles: Implements a barycentric sign test within the triangle's bounding box. Sources:
crates/guiltty-core/src/lib.rs:509-524 - Paths: Supports open segments (stroke) or closed polygons. Closed paths utilize a scanline polygon fill with the even-odd rule. Sources:
crates/guiltty-core/src/lib.rs:531-591,tasks/plan.md:T2
Text Rendering
Text is rendered using a built-in 3x5 bitmap font. The draw_text function handles glyph alignment, scaling, and canvas clipping. Sources: crates/guiltty-core/src/lib.rs:172-225
Text rendering flow demonstrating glyph lookup and scaling logic. Sources: crates/guiltty-core/src/lib.rs:184-219
Sprite Management and Compositing
Sprites represent movable bitmap content. The system uses a save-under/restore-under mechanism to allow movement without leaving trails on the canvas. Sources: crates/guiltty-core/src/lib.rs:723-745
Sprite Lifecycle
- Capture: Before drawing, the canvas captures the pixels currently in the sprite's new footprint.
- Restore: If the sprite was previously drawn, its
last_drawfootprint is restored to the canvas. - Compositing: The sprite's bitmap is drawn; pixels with
alpha == 0are skipped to preserve the background.
Sources:crates/guiltty-core/src/lib.rs:756-797
The sequence diagram illustrates the background preservation logic during sprite movement. Sources: crates/guiltty-core/src/lib.rs:756-785
Backend Integration
The Backend trait defines the interface for presenting the canvas to a terminal. The KittyBackend is the primary implementation. Sources: crates/guiltty-core/src/lib.rs:94-99
Kitty Graphics Protocol Encoding
The KittyBackend::present function performs the following steps:
- Converts the
Canvaspixels to raw RGBA8 bytes. - Base64 encodes the data.
- Chunks the payload into 4096-byte segments (per protocol limits).
- Transmits APC (Application Program Command) escape sequences to the terminal.
Sources:crates/guiltty-kitty/src/lib.rs:69-114
// Example of the Kitty protocol escape sequence generation
write!(
self.writer,
"\x1b_Ga=T,f=32,i={},p={},q=2,C=1,s={},v={},m={};",
self.image_id,
self.image_id,
canvas.width(),
canvas.height(),
more
).map_err(to_backend_err)?;
Sources: crates/guiltty-kitty/src/lib.rs:93-102
Error Handling
The context uses a recoverable error model via the Error enum, avoiding panics for external failures like image loading or terminal writes. Sources: crates/guiltty-core/src/lib.rs:79-86
| Error Variant | Cause |
|---|---|
Backend(String) | Failures during terminal communication or write operations. |
ImageLoad(String) | Failures in Bitmap::from_file (missing files, corrupt data). |
Sources: crates/guiltty-core/src/lib.rs:81-85