| Crates.io | waterui-graphics |
| lib.rs | waterui-graphics |
| version | 0.2.1 |
| created_at | 2025-12-14 08:05:02.720588+00 |
| updated_at | 2025-12-14 11:30:14.00426+00 |
| description | Graphic components for WaterUI |
| homepage | |
| repository | https://github.com/water-rs/waterui |
| max_upload_size | |
| id | 1983994 |
| size | 155,792 |
High-performance GPU rendering primitives for WaterUI applications.
waterui-graphics provides three distinct APIs for GPU-accelerated rendering, each targeting different levels of abstraction and use cases:
All three APIs render at display refresh rates (60-120fps+) and support HDR surfaces when available. The crate automatically handles surface format selection, preferring Rgba16Float for HDR displays and falling back to sRGB formats on SDR displays.
Add to your Cargo.toml:
[dependencies]
waterui-graphics = "0.1.0"
Or via the main waterui crate:
[dependencies]
waterui = "0.2"
The simplest way to draw vector graphics:
use waterui::graphics::Canvas;
use waterui::graphics::kurbo::{Circle, Line, Point, Rect};
use waterui::graphics::peniko::Color;
use waterui::prelude::*;
fn main() -> impl View {
vstack((
text("H₂O Molecule").size(24),
text("Simple 2D molecular visualization").size(14),
Canvas::new(|ctx| {
let size = ctx.size();
let center = ctx.center();
// Background
ctx.fill(
Rect::from_origin_size(Point::ZERO, size),
Color::new([0.08, 0.1, 0.14, 1.0]),
);
// Molecule geometry
let oxygen_radius = 40.0;
let hydrogen_radius = 22.0;
let bond_length = 90.0;
// Water bond angle ~104.5°
let angle = 104.5_f64.to_radians() / 2.0;
let hx1 = center.x - bond_length * angle.sin();
let hy1 = center.y + bond_length * angle.cos();
let hx2 = center.x + bond_length * angle.sin();
let hy2 = center.y + bond_length * angle.cos();
let oxygen = center;
let hydrogen1 = Point::new(hx1, hy1);
let hydrogen2 = Point::new(hx2, hy2);
// Bonds
ctx.stroke(
Line::new(oxygen, hydrogen1),
Color::new([0.9, 0.9, 0.9, 0.8]),
4.0,
);
ctx.stroke(
Line::new(oxygen, hydrogen2),
Color::new([0.9, 0.9, 0.9, 0.8]),
4.0,
);
// Atoms
// Oxygen (O)
ctx.fill(
Circle::new(oxygen, oxygen_radius),
Color::new([0.85, 0.2, 0.25, 1.0]),
);
// Hydrogens (H)
ctx.fill(
Circle::new(hydrogen1, hydrogen_radius),
Color::new([0.95, 0.95, 0.95, 1.0]),
);
ctx.fill(
Circle::new(hydrogen2, hydrogen_radius),
Color::new([0.95, 0.95, 0.95, 1.0]),
);
}),
text("Bond angle ≈ 104.5°").size(12),
))
.padding()
}
Load and render fragment shaders with automatic uniform management:
use waterui::graphics::shader;
use waterui::prelude::*;
fn main() -> impl View {
vstack((
text("Flame Animation").size(24),
text("GPU-rendered procedural fire").size(14),
// Just one line to load and render a shader!
shader!("starfield.wgsl").size(400.0, 500.0),
text("Rendered at 120fps").size(12),
))
.padding()
}
The shader automatically receives these uniforms:
struct Uniforms {
time: f32, // Elapsed time in seconds
resolution: vec2<f32>, // Surface size in pixels
_padding: f32,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@fragment
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let t = uniforms.time;
return vec4<f32>(uv.x, uv.y, sin(t), 1.0);
}
For advanced rendering pipelines, implement the GpuRenderer trait:
use waterui::graphics::{GpuContext, GpuFrame, GpuRenderer, GpuSurface, bytemuck, wgpu};
use waterui::prelude::*;
fn main() -> impl View {
vstack((
text("Cinematic HDR Flame (GpuSurface)").size(24),
text("HDR film buffer + bloom + ACES tonemap").size(14),
GpuSurface::new(FlameRenderer::default()).size(400.0, 500.0),
text("Rendered at 120fps").size(12),
))
.padding()
}
Canvas provides a callback-based API where you receive a DrawingContext each frame:
kurbo geometry primitivespeniko brushesThe DrawingContext provides helper methods:
ctx.size() // Canvas dimensions as kurbo::Size
ctx.center() // Center point
ctx.fill(shape, color) // Fill a shape with solid color
ctx.fill_brush(shape, brush) // Fill with gradient/pattern
ctx.stroke(shape, color, width) // Stroke a shape outline
ctx.push_clip(shape) // Begin clipping region
ctx.push_alpha(alpha, bounds) // Begin alpha layer
ctx.pop_layer() // End layer
ctx.scene() // Access underlying Vello scene
ShaderSurface automatically injects a uniform buffer accessible via @group(0) @binding(0):
time: f32 - Elapsed seconds since shader creation (for animations)resolution: vec2<f32> - Current surface size in pixelsuv: vec2<f32> - Normalized coordinates (0.0 to 1.0)The GpuRenderer trait defines three lifecycle methods:
pub trait GpuRenderer: Send + 'static {
fn setup(&mut self, ctx: &GpuContext);
fn render(&mut self, frame: &GpuFrame);
fn resize(&mut self, width: u32, height: u32) {} // Optional
}
setup() - Called once when GPU resources are ready; create pipelines, buffers, bind groupsresize() - Called when surface size changes (before render); recreate size-dependent resourcesrender() - Called each frame with GpuFrame containing device, queue, texture, and dimensionsAll three APIs automatically detect and utilize HDR surfaces:
// In setup()
if ctx.is_hdr() {
// Surface format is Rgba16Float or Rgba32Float
// Use extended color range (values > 1.0)
}
// In render()
if frame.is_hdr() {
// Render with HDR-specific parameters
}
HDR surfaces use Rgba16Float format when available, allowing color values beyond 1.0 for highlights and bloom effects.
use waterui::graphics::Canvas;
use waterui::graphics::kurbo::Circle;
use waterui::graphics::peniko::{Brush, Color, Gradient};
Canvas::new(|ctx| {
let gradient = Gradient::new_linear((0.0, 0.0), (ctx.width as f64, ctx.height as f64))
.with_stops([
(0.0, Color::new([1.0, 0.2, 0.2, 1.0])),
(1.0, Color::new([0.2, 0.2, 1.0, 1.0])),
]);
ctx.fill_brush(
Circle::new(ctx.center(), 100.0),
&Brush::Gradient(gradient),
);
})
use waterui::graphics::ShaderSurface;
ShaderSurface::new(r#"
@fragment
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let t = uniforms.time;
let color = vec3<f32>(uv.x, uv.y, sin(t));
return vec4<f32>(color, 1.0);
}
"#)
See /Users/lexoliu/Coding/waterui/examples/flame/src/lib.rs for a complete example implementing:
Canvas::new(draw_fn) - Create canvas with drawing callbackDrawingContext - Frame-by-frame drawing context with shape rendering methodskurbo (2D geometry), peniko (colors, brushes, gradients)ShaderSurface::new(wgsl_source) - Create surface from WGSL fragment shader stringshader!(path) - Macro to load shader from file at compile timetime, resolutionGpuSurface::new(renderer) - Create surface with custom GpuRendererGpuRenderer trait - Implement for custom GPU rendering logicGpuContext - GPU resources during setup (device, queue, surface format)GpuFrame - Frame data during render (device, queue, texture, view, dimensions)preferred_surface_format(caps) - Helper to select best surface format (HDR preferred)wgpu - Direct access to wgpu types for GpuRenderer implementationsbytemuck - Safe byte conversions for uniform bufferskurbo - 2D geometry (via vello::kurbo)peniko - Styling primitives (via vello::peniko)canvas - Enables Canvas API (depends on wgpu and vello)wgpu - Enables GpuSurface and ShaderSurface (no Canvas)All features are enabled by default. To use only lower-level GPU APIs without Vello:
[dependencies]
waterui-graphics = { version = "0.1.0", default-features = false, features = ["wgpu"] }
StretchAxis::Both).size(width, height) modifier to constrain dimensionsGraphics rendering requires a platform backend that supports wgpu:
Terminal UI backend (tui) does not support GPU rendering.