Repository navigation
movy v0.2.0 - Alpha Blending
True Transparency - movy v0.2.0
In v0.2.0, movy adds a rendering capability that was long overdue: true alpha blending. This release implements Porter-Duff alpha compositing, enabling semi-transparent surfaces, smooth fade effects, and layered visual compositions with real transparency. Alongside this core feature, the entire documentation structure has been overhauled to provide comprehensive learning resources for developers new to movy.
What's New
Porter-Duff Alpha Compositing
The rendering engine now supports full alpha blending using the Porter-Duff "over" operator, enabling true semi-transparent effects. This is a fundamental upgrade to how movy handles transparency.
Previous behavior:
- Binary transparency only (shadow_map != 0 meant fully opaque)
- No support for semi-transparent surfaces
- Fade effects required workarounds / RenderEffect engine
New capability:
- True alpha blending with 0-255 opacity levels
- Semi-transparent overlays (glass effects, shadows, UI elements)
- Smooth fade-in/fade-out effects
- Layered compositions with variable transparency
New Rendering Functions
Two new compositing functions added to RenderEngine:
1. renderWithAlphaToBg() - RECOMMENDED
Optimized alpha blending for opaque backgrounds (the most common use case):
pub fn renderWithAlphaToBg(
surfaces_in: []const *movy.core.RenderSurface,
out_surface: *movy.core.RenderSurface,
) voidWhen to use:
- Standard rendering with transparency effects
- Fading sprites (fade-in, fade-out)
- Glass-like overlays
- Shadows and lighting effects
- Any time your background is opaque (α=255)
Performance: Optimized for the common case where background alpha is always 255.
2. renderWithAlpha() - General Compositing
Full Porter-Duff compositing with variable background alpha:
pub fn renderWithAlpha(
surfaces_in: []const *movy.core.RenderSurface,
out_surface: *movy.core.RenderSurface,
) voidWhen to use:
- Pre-compositing surfaces before final render
- Working with semi-transparent backgrounds
- Complex multi-pass rendering pipelines
Performance: Slightly slower due to full Porter-Duff formula with variable background alpha.
3. screen.renderAlpha() - Convenience Method
High-level rendering function added to Screen:
pub fn renderAlpha(self: *Screen) voidWhat it does:
- Calls
RenderEngine.renderWithAlphaToBg()internally - Composites all added surfaces with alpha blending
- Uses the screen's output surface as background
When to use:
- Standard application rendering with transparency
- Simplifies the common workflow of adding surfaces and rendering with alpha
- Recommended for most use cases requiring transparency
Example:
try screen.renderInit();
try screen.addRenderSurface(allocator, sprite_surface);
screen.renderAlpha(); // Composite with alpha blending
try screen.output();Technical Implementation
Alpha Mode: Straight (non-premultiplied) alpha
- RGB values are independent of alpha
- Matches PNG file format directly
shadow_mapvalues: 0 = transparent, 255 = opaque
Math: Integer-only operations with type widening
- No floating-point arithmetic
- Type progression: u8 → u16 → u32
Optimizations:
- Fast paths for α=0 (fully transparent) and α=255 (fully opaque)
- Inline blending functions for performance
- Minimal branching in hot loops
Formula (Porter-Duff "over" operator):
For renderWithAlphaToBg() (background opaque):
C_out = (C_fg × α_fg + C_bg × (255 - α_fg)) / 255
For renderWithAlpha() (variable background):
α_out = α_fg + α_bg × (255 - α_fg) / 255
C_out = (C_fg × α_fg + C_bg × α_bg × (255 - α_fg) / 255) / α_out
Code Example
const movy = @import("movy");
// Create semi-transparent red sprite
var sprite = try movy.RenderSurface.init(allocator, 20, 20, red);
for (sprite.shadow_map) |*alpha| {
alpha.* = 128; // 50% opacity
}
// Blend onto opaque background
var surfaces = [_]*movy.RenderSurface{sprite};
movy.render.RenderEngine.renderWithAlphaToBg(&surfaces, output);
// Result: Dark red (128, 0, 0) from 50% blendSee examples/alpha_blending.zig for a complete working example.
Documentation
The documentation has been completely restructured:
1. Guides (doc/)
In-depth documentation on core concepts:
-
- Creating surfaces with
init()andcreateFromPng() - Understanding color_map, shadow_map, char_map
- Text rendering with
putStrXY()andputUtf8XY() - Critical detail: Text must be on even y-coordinates
- Integration with Screen
- Creating surfaces with
-
- All rendering functions explained
- When to use each render mode
- Alpha blending mathematics
- Performance considerations
- Decision trees for choosing functions
2. Examples (examples/)
Four focused code examples demonstrating specific features:
| Example | Demonstrates | Run Command |
|---|---|---|
basic_surface.zig |
Surface creation, text rendering, Unicode | zig build run-basic_surface |
alpha_blending.zig |
50% transparent sprite with Porter-Duff | zig build run-alpha_blending |
layered_scene.zig |
Multi-layer composition with z-ordering | zig build run-layered_scene |
png_loader.zig |
Loading PNG files with alpha channels | zig build run-png_loader |
Each example is commented, and runnable.
3. Demos (demos/)
Complete working programs showing real-world usage:
- stars.zig - Animated starfield with 60 FPS control
- simple_game.zig - Game starter template
- mouse_demo.zig - Interactive input with UI Manager
- win_demo.zig - Multiple windows with themes
- mplayer.zig - Video playback (requires
-Dvideo=true)
See demos/README.md for detailed descriptions.
Legacy Examples
Previous examples moved to examples/legacy/ to maintain compatibility:
- sprite_frame_animation, keyboard, mouse, keyboard_mouse
- sprite_fade, sprite_fade_chain, sprite_fade_chain_pipeline
- render_effect_chain, index_animator_print_idx
Run with: zig build run-legacy-{name}
Testing Infrastructure
New testing system for render engine validation:
Test Coverage
7 comprehensive test blocks in RenderEngine.zig:
- Binary transparency - Legacy render() mode
- Alpha compositing (general) - renderWithAlpha() validation
- Alpha compositing (to background) - renderWithAlphaToBg() optimization
- Edge cases - Zero alpha, full alpha, empty inputs
- Z-ordering - Correct layering with alpha blending
- Porter-Duff formula - Mathematical correctness
- Performance characteristics - Fast path verification
Running Tests
zig build testTests verify:
- Correct color blending at various opacity levels
- Fast path optimizations (α=0, α=255)
- Edge case handling
- Z-index ordering with transparency
- Mathematical accuracy of Porter-Duff formula
Build System & PNG Loading
Video Support Parameter
Build system now accepts -Dvideo=true for FFmpeg integration:
zig build # Core movy only
zig build -Dvideo=true # Include movy_video (requires FFmpeg/SDL2)PNG Alpha Channel Loading
RenderSurface.createFromPng() now correctly loads alpha channels:
- Reads RGBA32 PNG data via lodepng
- Automatically populates
shadow_mapwith alpha values (0-255) - Enables direct use of PNG transparency in movy
Before:
- Alpha channel was ignored or incorrectly loaded
After:
- Alpha values directly mapped to shadow_map
- PNG transparency "just works"
Performance Considerations
Rendering Mode Comparison
| Mode | Use Case | Performance | Alpha Support |
|---|---|---|---|
render() |
Legacy binary transparency | Fastest | Binary (0 or 255) |
renderWithAlphaToBg() |
Standard alpha blending | Fast | Full (0-255) |
renderWithAlpha() |
Complex compositing | Moderate | Full (0-255) |
When to Use Each Function
Use render() when:
- You don't need semi-transparency
- Performance is critical
- Working with opaque sprites only
Use renderWithAlphaToBg() when:
- You need fade effects
- Working with semi-transparent sprites
- Background is opaque (most common case)
Use renderWithAlpha() when:
- Pre-compositing surfaces
- Background itself is semi-transparent
- Building complex multi-pass pipelines
Performance Notes
- Alpha blending adds minimal overhead for opaque pixels (α=255 fast path)
- Fully transparent pixels (α=0) are skipped entirely
- Integer math ensures predictable performance (no floating-point overhead)
- Type widening to u32 prevents overflow without branches
Example: Adding Fade Effect
Before (binary transparency):
screen.render(); // Uses RenderEngine.render() internallyAfter (with fade effect):
// Set opacity on your sprites
sprite.shadow_map[i] = fade_alpha; // 0-255
// Use alpha-aware rendering
var surfaces = [_]*movy.RenderSurface{sprite};
movy.render.RenderEngine.renderWithAlphaToBg(&surfaces, screen.output_surface);API Changes
New Public Functions
RenderEngine (src/render/RenderEngine.zig):
renderWithAlphaToBg()- Alpha blending for opaque backgroundsrenderWithAlpha()- Full Porter-Duff compositing
New Private Helper Functions
Inline blending functions (internal use only):
blendChannelGeneral()- General Porter-Duff channel blendblendPixelGeneral()- RGB pixel blend with variable background alphablendChannelToBg()- Optimized blend to opaque backgroundblendPixelToBg()- Optimized RGB blend to opaque background