Streaming mask support: avoid f32 path and full materialization for pixel-wise masks
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 2
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Masks in zenresize (via with_mask()) currently force the f32 pipeline path. Since all mask types (RoundedRect, LinearGradient, RadialGradient) compute values per-pixel per-row, there's no fundamental reason they can't work in the i16 paths too.
Additionally, zenfilters' Masked filter wrapper operates on full OklabPlanes buffers, but for row-local inner filters (exposure, contrast, saturation, tone curves — anything that doesn't need spatial neighborhood), the save→apply→mask→blend cycle could stream row-by-row:
- Save original row
- Apply filter to row (row-local filters only)
- Compute mask values for that row (all mask types are
y-addressable) - Blend:
result = original * (1 - mask) + filtered * mask - Emit row
What this enables
- i16 mask path in zenresize: per-pixel lerp in i16 space (
(a * (256 - m) + b * m + 128) >> 8for u8 masks). Avoids f32 conversion overhead for the common case of rounded corners + solid background → JPEG. - Streaming masked filters in zenfilters/zenpipe: color grading + gradient mask could stream without materializing the full image. Only non-row-local inner filters (blur, warp, sharpen with large radius) would still need full materialization.
- Composable with zenresize's existing streaming pad/crop/orient: mask application slots in after resize, before orient — same position it's in now, just without forcing f32.
Non-goals
- Masks that depend on image content (future content-aware masks) are inherently non-streaming.
- Non-row-local inner filters (blur, warp) still need full materialization regardless of mask streaming.
Relevant code
zenresize/src/streaming.rs—with_mask()forces f32 pathzenblend/src/mask.rs—MaskSourcetrait,MaskSpans::for_row(y, width)zenfilters/src/masked.rs—Maskedwrapper, full-image save→apply→blendzenfilters/src/filter.rs—Filter::is_neighborhood()/neighborhood_radius()— already distinguishes row-local from spatial filters
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with zenresize/src/streaming.rs to trace how with_mask() selects the f32 path, then read zenblend/src/mask.rs and MaskSpans::for_row(y, width). Review zenfilters/src/masked.rs and zenfilters/src/filter.rs to distinguish row-local from neighborhood filters. Done means i16 mask processing and row-by-row masked filtering work without full materialization where the stated constraints allow it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100