mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-29 12:27:38 -06:00
Review of f116c32 was right that the guard did not establish the bound it
advertised. reject_bounds ran on the value stroke_coverage() returned, and that
function calls dense_coverage() internally, so the dense Vec was already
allocated by the time it was checked. reject_huge_stroke, the only check that
ran first, estimated from the raw points and half the tip, which brush dynamics
exceed: Scattering offsets a dab centre by its amount times the tip size.
Split rendering from dense extraction. stroke_coverage_within renders the stroke
into the renderer's tiles, which hold only what the dabs touch, reads the extent
once every dab is placed and refuses before allocating the width x height
buffer. retouch_cmds::coverage wraps it at MAX_STROKE_PIXELS and every stroke
command goes through it, so the bound now holds whatever the dynamics do.
reject_bounds is removed; it could not do the job it was named for.
spot_heal_surface returned a bare Rect and so had nowhere to refuse; it returns
Result<Rect> now. reject_huge_stroke stays as the cheap early-out and pads by
the scatter reach and the dual-brush radius so it no longer falls short.
The allowance is unchanged, so no stroke that worked before starts failing.
Tests: coverage_within takes the budget as a parameter, and a scattering brush
proves the rendered extent exceeds what the points alone suggest, is refused one
pixel under it and fits at it. Every command's refusal now asserts the pixels
and the history step count are untouched, and the ordinary stroke uses a valid
fixture and must succeed rather than merely avoid the size error.