Commit Graph
13 Commits
Author SHA1 Message Date
FredApps 44f13e5498 Scope scratch read failures to the document that holds the tile
One unreadable tile used to block editing, saving and exporting every open document
(spill::check_integrity was process-global). Add Tile/Surface/Document::scratch_health
(free while no tile has failed) and Document::for_each_surface (ops' history walk now
delegates to it). edit_with checks the document before and after, jobs the document they
captured, import the result, save/export the document written; check_since is now
generation-only so a failure or recovery during an operation refuses it once and a retry
works. Caches check their own sources and purge once per new failure instead of every
frame. Fault-injection tests use a duplicated handle (the Unix file has no name).
2026-11-02 22:23:42 +01:00
FredApps b0f3c7516b Merge current main into codex/scratch-storage
Resolve conflicts with main's prefs UI split, session/execute.rs move, GPU memory
budget (gpuMemoryMb, 0 = auto, kept from main) and effective_memory_usage_mb history
budget. Port the Memory Usage % and Scratch Disks preferences into prefs_ui, the edit
integrity guard into session/execute.rs, and adapt main's new pixel readers to the
guarded TileBytes API. Cargo.lock regenerated; scorecard regenerated.
2026-11-02 22:16:22 +01:00
David Mann 23d8f053cb Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/shape_split.rs
2026-10-24 03:12:55 +02:00
Dylan M. Taylor 6ca5bf359d security(compose): bound dense working sets and nested Blend If evaluation (#8949)
* security(compose): bound dense working sets and repeated group evaluation

Smart-filter mixing and stroke fitting work tile by tile instead of over the
union of sparse surfaces; render_layer admits its rect like render; hidden
layers no longer disable the proxy and uniform-area shortcuts; Blend If
groups nested deeper than two are judged by their result, so nesting costs
polynomial instead of exponential work; effect builds past an absolute byte
cap are skipped.

* CodeRabbit #8949: exact tile skips in filter_mask, allocated-tile scan in fit_stroke, contrasting backdrop in the nested-group test

- filter_mask mix/unmix: bare tiles (no tiles on either side) skip for
  non-alpha formats too when the two surfaces' default pixels are equal;
  differing defaults still get processed, so the clone's default pixel is
  never left wrong under a non-unit mask
- shape_split fit_stroke: scan the union of the three surfaces' allocated
  tiles (exact skip when all three defaults are transparent) instead of
  every tile of a 300k-canvas document; non-transparent defaults fall
  back to the full scan
- tests: the nested-groups assertion now uses a black backdrop so the
  asserted white can only come from rendering through the group chain

fixes CodeRabbit review comments (major/minor) on filter_mask.rs:27,29,
shape_split.rs:126, tests.rs:2354

* compose: skip a BTreeSet in fit_stroke — a sorted Vec compiles under the wasm toolchain (E0599 on ::drain there)
2026-10-23 15:33:01 -04:00
David MannandClaude Opus 5.5 95e0848332 Merge main into fix/4381-psd-frame-stroke
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:09:11 +02:00
David MannandClaude Opus 5.5 f8e6d9666a PSD: Frame layers keep their stroke (#4381)
A Photoshop Frame is a group whose record carries the frame outline as a
vector mask and its Properties > Stroke as a shape-style `vstk`. We dropped
that `vstk`, so frames lost their stroke. `Group::frame_stroke` now holds it:
read from and written back to PSD (byte-identical while unchanged), saved in
.pcraft, and drawn along the vector mask above the frame's content by the CPU
compositor and the GPU planner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:42:40 +02:00
David MannandClaude Opus 5.5 6bb45c8d5b Shapes: Density and Feather on shape layers (#5846)
A shape layer's path is its vector mask in Photoshop (Properties > Masks),
with its own vector-mask density and feather. PhotoCraft's ShapeLayer had
neither, so soft shapes could not be made, and stroked soft shapes from PSDs
lost both on import.

- doc: ShapeLayer gains `density` (0..1, default 1) and `feather` (px,
  default 0).
- vector: the shape renderer applies them to the fill as a soft vector mask
  (fill shows beyond the path at 1 - density; the feather is the vector
  mask's Gaussian), so the shape's own pixels, the GPU (which samples them)
  and the stroke/fill split all follow. The stroke stays crisp. The feather
  blur moves from compose::masks to vector::feather and both share it and the
  feathered area, so a shape and an equal vector mask render the same pixels.
- compose: soft shapes' bounds reach their fill, their layer effects follow
  the soft alpha, and the split cache key includes density and feather.
- engine: `layer.vectorMask.edit` on a shape layer sets density/feather in one
  history step (path/enabled/linked/invert are refused there); density is now
  validated (finite 0..100) instead of clamped. `layer.vectorMask.info`
  reports a shape's values.
- ui: Properties shows the Vector Mask Density and Feather sliders for a
  selected shape layer in both themes (live drag, one undo per gesture).
- .pcraft: manifest and convert carry both with serde defaults (older files
  load at 100 % and 0 px).
- PSD: export writes them as the vector density/feather mask parameters;
  import reads them into the shape (re-rendered, as the stored pixels lack the
  fill beyond the path). Soft unstroked solid/gradient shapes now import as
  shapes instead of fill layers with a vector mask; pattern-filled ones stay
  fill layers.

Photoshop's 10 px feather on a 100 px black square, row 100, x = 30..72:
ours is within 1 level of every measured value (8, 16 and 32 bits);
density 50 % shows 127/128 beyond the path and 0 inside.

Scorecard: TOOL-214-12 done.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 03:07:46 +02:00
vistaero 0456bc3f22 Add guarded scratch storage with owned memory supervision 2026-10-14 00:45:24 +02:00
FinnNkandClaude Opus 5.5 752f611264 perf(compose): the shape stroke split cache keeps to a byte budget and empties on Purge (#4530) (#4774)
shape_split caches a stroked shape's fill and stroke rendered over the
canvas, for shapes with clipped layers: up to 32 entries of two
canvas-sized surfaces each, with no byte cap (several GB at 24 MP) and
nothing in Edit › Purge › All to clear it.

It now evicts least recently used entries until a new one fits both
the 32-entry cap and a 256 MiB pixel budget (an entry larger than the
whole budget is used and not kept), and Purge › All clears it as
"shape stroke cache", reporting the bytes like the other caches.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:33:21 -04:00
Harrison O'Connor-Hoover 5355221f81 Preserve document depth when splitting vector shapes (#1495) 2026-10-09 09:01:39 +07:00
fb924dad16 Shapes: a hairline vector stroke no longer darkens the shape's edge pixels (#397)
* Shapes: a hairline vector stroke no longer darkens the shape's edge pixels

A stroked shape with layer effects (or clipped layers) is drawn as our
fill plus a vector stroke fitted to Photoshop's own pixels: where those
pixels reach past our fill, the stroke takes the alpha the fill doesn't
explain, `(pixels - fill) / (1 - fill)`. It kept our stroke's colour for
that alpha. When Photoshop's fill covers more of an edge pixel than ours
(shapes snapped to whole pixels with Align Edges, our path a fraction off
the grid), the fitted alpha is mostly Photoshop's fill, so a 0.25 px grey
border on a white card was painted as a solid grey line: the edge came out
at the stroke's full colour (122) instead of Photoshop's 220.

The fitted stroke now takes the colour that, drawn Normal over our fill,
gives back Photoshop's pixel (un-compositing the premultiplied colour),
and keeps our colour only where the fitted alpha is negligible. Where
Photoshop's pixels agree with our fill this is our stroke's colour, as
before.

Test: hairline_vector_stroke_keeps_photoshops_edge_pixel_colour (edge
pixel 0.478 before, 0.86 after). The compose, gpu, io and engine tests
and the psd/io/engine corpus tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Shapes: neutral wording in the hairline-stroke fix; test it at 8, 16 and 32 bits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: EthanSuquet <292885762+EthanSuquet@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Tanish Manku <tmanku.dev@gmail.com>
2026-10-07 13:50:09 -07:00
Brandon Thomas 8bac8926a3 PSD stroke effects: outlines of filled shapes, vector stroke above interior effects, joined subpaths (#233) (#233) 2026-10-05 11:48:28 -04:00
Brandon ThomasandClaude Opus 5.5 aa87012aab Analysis, notes, proof views, workspaces (+ in-progress work of other agents)
Image > Analysis (ruler, count, measurement scale/log/record, scale
marker), notes (PSD Anno round trip, Notes panel, import), Modifier Keys
panel, Proof Setup plates / Legacy Mac RGB / colour blindness (Vienot),
32-bit preview options, custom pixel aspect, extras options, New/Delete/
Lock Workspace. Parity 600/625, FLOOR 595.

Checkpoint: also contains compiling in-progress work from the parallel
panels, type styles, File menu, photo, filters, UI-parity and fidelity
agents, plus photocraft-cli serve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:49:07 -04:00