apply_color_replacement and apply_mixer_stroke return a Result since #11070.
Two tests that landed on main after that PR's CI ran still dropped it, so
clippy's unused-must-use lint fails the gate on main.
* paint: refuse a sequential-dab stroke whose working copy is too large
apply_dab_stroke read the whole rectangle enclosing a stroke's dabs into
two dense f32 copies (and a third for the mix), sized by the area
between the dabs rather than what they paint. Two distant points asked
for gigabytes on a sparse document.
Check the three planes against a 4 GiB budget (1 GiB on 32-bit) before
reading, and paint nothing past it.
* address review: apply_dab_stroke returns Result so over-budget dab strokes fail with 'stroke too large'
* fix tests: apply_dab_stroke returns a Result
* fix tests: handle the Result of the dab-stroke functions
* Engine: check dense working buffers against a budget before expanding sparse content
A native document stores only the tiles it holds, so a few distant tiles (or a
huge empty canvas) make a small file whose content bounds span trillions of
pixels. Several commands read those bounds, or the whole canvas, into dense
f32 buffers without checking the size first, and ran the process out of memory.
- allocation::check_dense refuses an area whose dense buffers would pass
MAX_DENSE_BYTES (the Image Size allowance), before anything is allocated.
- Adjustments run through process_surface, Apply Layer Mask, channel sources
(Apply Image / Calculations / Load Selection), face detection and Multichannel
conversion (both directions) check their buffers first.
- Auto-Blend checks the final work area too, not only the layers' union.
- Liquify's selection blend works over the part of the content the selection
reaches, on a copy of the old pixels, and checks that area.
- Floating a selection visits only stored source or selection tiles when a
default is empty, and checks the full grid otherwise.
- Frame from Layers and the Frame tool fill their mask without a dense buffer
and check its area.
- Image Size checks every surface's resampled content bounds, off-canvas
pixels included, not only the canvas.
* cargo fmt
* address review: propagate budget error out of adjust_as_filter
* address review: budget merged CMYK channel planes, frame mask GRAY8 source, Duotone inks and the stored surface in Multichannel conversions
* address review: budget the Frame mask at its tile footprint and Auto-Blend stack pyramids
* address review: non-seamless stack blend skips the fusion pyramids in its budget; canvas refresh returns smart-object refresh errors
* address merge: take main's resample_doc signature in Bitmap; fmt
* address merge: Image Size over distant tiles is bounded by main's resample budget; drop the duplicate test
* address merge: keep brush_clear_background_tests test-only
* address CI: a smart object that cannot re-render keeps its resampled fallback (main's tested behavior); drop the refresh error propagation
* Actions: charge skipped steps to the playback budget, bound batch nesting, clip the set override
- actions.play charges every stored step of a call (run, disabled or before
`from`) to the playback tree, and refuses a call that no longer fits before
copying the action, so repeated calls to a large disabled child stop at the
shared limit.
- file.automate.batch and runDroplet refuse to nest more than MAX_BATCH_DEPTH
deep, checked before the droplet is read; nested batches charge the shared
step budget like batches inside played actions.
- actions.load clips the `set` override to the importer's name limit before
copying it into every action.
* address review: check the script step limit before charging skipped playback steps
* address review: nested batches keep the shared playback count, and nesting or playback limits fail the enclosing input
* address review: a limit stop inside a called action or nested batch fails the enclosing input
* address review: keep begin_playback's signature; the call that starts mid-action uses begin_playback_from
* address review: keep child results in limit failures, history after the script-limit check, trim a clipped set name, open-document batches carry the nesting state
* fix(engine): authorization for nested scratch sessions and preset calls, CSV and PostScript text escaping
* address review: Batch checks the authorization gate for each save point
* address merge: restore the #[test] attribute
* address merge: batch save points answer to the authorization gate in the rewritten batch
* address review: neutralize leading line feeds and full-width formula characters; test the preset command gate
* address review: check formula prefixes after leading spaces
* fix(codecs): checked sizes in encoders, EXR name lookup, FITS float limit, VP8 plane copies, EXIF thumbnail bytes
* address review: thumbnail range must be one SHORT/LONG tag and clear of IFD0
* address review: keep the TIFF header and Exif/GPS sub-IFDs out of the blanked thumbnail range
* address review: bound the Exif/GPS sub-IFD overlap scan with a total budget
* address review: keep the Interoperability IFD out of a blanked thumbnail range
* fix(format): limit zstd windows, set bundle permissions through the handle, refuse symlinked presets
* test(engine): create the symlink test's directory
* address review: preset writes never follow a symlink swapped in after the check
* address review: a zstd window past the limit is a limit error; replacing a link never reads its target's attributes
* test: replacing a link to a FIFO does not block
* fix(io): charge importers for the whole tiles surfaces keep
* test(io): size the affinity and SVG pixel budgets in whole tiles
* address review: charge retained tiles after prune, logical scratch in SVG peak check
* address review: charge only the tiles a raster layer keeps
* fix PsdImportBudget initializer after merging main
* address review: leave the multichannel tile budget to the PSD preflight
* address review: charge a shape cache for the tiles it keeps
* codecs: list each Cryptomatte layer once and index streams by name
layers() cloned the cached stream list for every cryptomatte/<key>/name
attribute that named a layer, so many keys naming one layer (in any
case or _/- spelling) retained keys x channels strings. Streams are now
indexed by normalized layer name in one pass over the channels, each
layer's list is moved into the first key that names it, and later keys
naming the same layer are skipped (decode resolves a name to its first
layer anyway).
decode() searched the layer's stream list for every decoded channel;
it now builds a stream-name -> slot map once.
* address review: compare cryptomatte layer names normalized in decode
* address review: prefer exact Cryptomatte layer name
* address review: sort cryptomatte keys so layer names are stable
* address review: prefer the key with a usable manifest when keys name one cryptomatte layer
* fix(algo): bound EXIF text decoding, warp scans, gallery windows, inpaint sizes, pyramid depth
* address review: gallery window budget no longer scales with the effect count; assert blended pixels in the level-clamp test
* address review: Filter Gallery window budget counts the working window and effect temporaries
* address review: empty Filter Gallery is not charged working buffers; document the warp scan skip
* address merge: multiband returns a Result; keep apply_tiled_with_impl under the complexity limit
* fix(ui-egui): bound the RAW re-read and treat symlinked names as taken in batch export
* address review: refuse an oversized RAW file from its length and reserve the read buffer fallibly
* address review: lower the RAW re-read ceiling to the decoder's 2 GiB allocation limit
* Save: a replacement in another group does not widen group access
* address review: set the group before copying extended attributes; skip the group test when the original's group is not assignable
* address review: copy the permissions (minus group bits) when the group lookup fails
* Camera Raw: read each source row once when building the preview proxy
The proxy box-averages k x k blocks, with k chosen from the longer side
of the domain. Blocks past the last row re-read that row once per
missing row, so a one-row domain (a sparse document a pixel high and a
million wide) read the same row k times: quadratic in its width, on the
UI thread.
Read every source row once and weight the last one by the block rows it
stands for; the averages are unchanged.
* address review: accumulate camera raw preview rows in f64
* text: charge the rasterizer's output tiles, not only its pixels
The renderer checked a layer's pixel count and working buffers, but its
output surface stores whole 256x256 tiles. An extreme transform of short
text gives a thin rectangle that crosses tens of thousands of tiles while
holding few pixels, so the tile storage far exceeded the budget.
Refuse a render whose output tiles exceed MAX_RASTER_BYTES.
* address review: charge text accumulator with output tiles
* address review: compute the accumulator size in u64
* Escape control characters in log lines and diagnostic output taken from files and plug-ins
* address review: escape the plug-in load error and the atn_dump path before printing
* Place: refuse a scale that makes the placed image exceed the render pixel limit
* address review: bound the rounded-up resampled size when placing at a scale
* address review: the oversized-scale error names the placing command
Main moved the catalogs out to the optional craft-i18n build input; built
without it, Lang::from_code("de") is None and the test's expect panicked.
It now skips like main's other translation tests (needs_catalogs).
Co-Authored-By: Hector Villarreal <hectorviov@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
live_path rounds a star's outer points and leaves its inner points
sharp corners, as the reference application does: a 5-point star at
Star Ratio 50 % with corner radius 10 has 15 anchors instead of 20.
Plain polygons keep rounding every vertex.
The on-canvas corner radius widget shows knobs at the outer points only
and takes its largest radius from them, so it previews the same path
as shape.create.
Keeps both sides: main's value_field_whole_alerting and new test modules
next to the PR's value_field_whole and count_options_bar_tests.
Co-Authored-By: Hector Villarreal <hectorviov@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>