Repository navigation
feat(canvas): noise adjustment for film grain - #9933
joshistoast wants to merge 2 commits into
Conversation
Adds a Noise raster/group adjustment with Gaussian or Uniform distribution, monochromatic or color grain, amount, and a re-rollable seed. Noise is a deterministic function of position, so display, generation, export, merges and PSD share the same grain. Layer bakes and PSD now apply a layer's own adjustments before resampling, matching the display.
JPPhoto
left a comment
There was a problem hiding this comment.
Merge blockers
-
[P1 · Must fix] Merge Down drops Noise.
mergeLayerController.ts:139composites raw cache pixels; the reducer removes the layer adjustments. Impact / recovery: Grain disappears from either adjusted layer; undo is the recovery. Test: Add a Noise-specific pre/post Merge Down pixel comparison. Fix: Bake adjustments before compositing. If skipped: Merge Down violates the PR’s promise that merges preserve grain. -
[P1 · Must fix] Older builds can erase the whole adjustment stack.
canvasMigration.ts:125accepts Noise, while the writer still emits schema 3 or 4. An older client can drop the unknown stack on load, then autosave that loss. Impact / recovery: Permanent after the save. Test: Open and save a Noise document with an older schema reader. Fix: Mark Noise documents with a schema version older builds refuse. The PR documents this limitation, but it remains a data-loss path.
Other findings/issues
-
[P2 · Should fix] Rasterize changes the Noise pattern.
rasterizeLayerController.ts:86transforms raw parametric pixels, resets the transform, then keeps the adjustment. Noise runs on a new pixel grid. Impact / recovery: Grain changes after rasterizing a transformed text, shape or gradient layer; undo recovers it. Test: Compare pixels before and after rasterizing a transformed noisy layer. Fix: Apply Noise before baking the transform. -
[P2 · Should fix] Text noise can exceed its memory reservation.
rasterComposite.ts:247reserves from estimated text bounds, while rasterization uses browser-measured bounds. Wide text under heavy downscaling can allocate a larger scratch region than reserved. Impact / likelihood: Possible budget overrun or browser allocation failure with wide text and tight memory. Test: Use text whose measured bounds exceed the estimate. Fix: Reserve from actual layer bounds or a conservative text bound.
Summary
Adds a Noise adjustment for raster layers and groups, aimed at adding film grain.
Noise is a deterministic hash of seed, pixel position and channel. A layer's grain lives in layer-local pixels and moves, scales and rotates with the layer; a group's grain is anchored to document pixels.
applyAdjustmentsnow takes the buffer's origin, and every caller passes one, so partial redraws match full ones.To keep grain identical everywhere, a layer's own adjustments now run on layer-local pixels before resampling on every path: display, generation composite, composite export, merge/crop/copy bakes and PSD. Noise stacks no longer take the adjust-after-resampling shortcut in composites. Bakes and PSD do this for all stacks, so scaled or rotated layers with existing adjustments now bake exactly what the canvas shows. Bakes reuse the display's adjusted copy when it is current, which avoids extra memory. Thumbnails reuse it too.
QA Instructions
pnpm check:releaseininvokeai/frontend/webv2: passed. Covers lint, unit, browser, fixtures, architecture and browser performance gates, project-file journeys and accessibility.pnpm dev:mock):Review
Resolved:
Remaining limits:
Compatibility / Rollout
noisearm to the persisted adjustment schema. Existing documents load unchanged.Checklist
What's Newcopy (if doing a release after this PR)Need help on this PR? Tag
@codesmith-botwith what you need.