- Replace whole-region Vec<f32> read_region/write_region in CutParts::new
with tile-parallel cut_piece + try_fill_surface/try_clear_surface,
sharing interior Arc<Tile> allocations in O(1) and activating the
solid_tile hole-fill fast path on Background and mask targets.
- Cache piece_bounds on CutParts so CutParts::place avoids scanning tile
content bounds on every drag frame.
- Parallelize composite_over across top tiles with Rayon and share fully
opaque top tiles by Arc<Tile> clone in O(1), preserving COW tile
identity for GPU canvas uploads.
- Parallelize Surface::translated across target tiles with Rayon and
prune cleared tiles in-place inside edit_tiles.
- Skip zero-offset floating preview generation on pointer-down in
move_ui::floating_doc.
- Exclude SnapKind::Selection when moving selected pixels with the Move
tool so small initial drags do not snap back to (0, 0).
- Keep navigator_plain active during Move/Brush/Patch drags on a GPU
canvas so navigator_gpu is not evicted into a synchronous full-document
CPU thumbnail composite on the first drag frame.
- Parallelize row normalization in compose::render_reduced_in_bands and
add a direct read_rgba_into fast path for unmasked Normal layers over
an empty backdrop.
Loading a layer's transparency as the selection and dragging it with the Move
tool left a ghost of a·(1−a). Photoshop lifts the selection's share of a
pixel's coverage, up to all of it, and leaves the rest, so its own
transparency lifts it whole. Opaque pixels split as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Step Backward in the History panel puts a floating selection back like Edit > Undo; Step
Forward and Edit > Redo put it back and step instead of dropping it first (which recorded a
Move and wiped the redo states); with nothing to step to the piece keeps floating.
- Memory figures and the byte budget count the states Clear History keeps for its undo.
- Undoing Clear History keeps a saved document clean.
- New Snapshot... in the panel menu runs history.newSnapshot.
- Tests: Clear on one document leaves the others alone, and each fix above.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Clicking a History panel state ran Edit > Undo / Redo once per state. Each
step bumped the document revision, so the canvas could not tell what the
jump changed and recomposited the whole document (re-uploading every
changed layer on the GPU path).
history.selectState {state} returns the document to a state (numbered as
the panel lists them, redo states included) in one command: the history
moves by pointer swaps, the document becomes current once, with one
revision and the damage rect between start and target, and the targeted
layers restored as Undo restores them. The panel runs it once per click;
an open Free Transform refuses it (panel guard kept, and the transform
intercept greys and refuses it for every caller). A floating piece goes
back as with Undo instead of being dropped as a new step.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Photoshop's Background is the bottom layer of a file without transparency: a pixel layer that is
transparency- and position-locked, whatever the file's language calls it ("Hintergrund", "背景",
"Arrière-plan"). We matched the name "Background" in compose (deep / shallow knockout), the
engine (cut/clear, merge visible, transforms, warps, distort, erasers, styles, masks, crops,
straighten, smart-object unpack) and the UI (Properties, layer row, tab title), so a localized
file's deep knockout revealed transparency instead of the Background.
- `Document::background` / `background_mut` / `is_background` and `Layer::has_background_traits`
in the doc model replace every name check; `extra_cmds::background_to_layer` is the one
"Background → Layer 0" conversion.
- PSD import marks the bottom record position-locked when it is transparency-protected and has
no transparency channel, with or without an `lspf` block. Affinity import and HDR Toning now
lock the Background's position like every other place that creates one.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Make large pixel operations allocation-fallible
* Warp: drop the fixed 400 MP cap; fallible reservations already cover large transforms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Adapt #2530 to main: fallible steep-Distort warp, Result from mask paste in float moves; regenerate scorecard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Move tool on a locked layer: Photoshop's message
Dragging with the Move tool (or Cmd with another tool) on a layer it can't
move showed nothing: the Background or a position-locked layer with no
selection, or a locked layer whose selected pixels can't float. Photoshop
puts up "Could not use the move tool because the layer is locked." when
the drag starts (a plain click says nothing); move_lock.rs does the same
with the Error dialog, translated in every catalogue. The float lock rule
moves to float_cmds::locked_for_float so the UI can ask before the press
goes anywhere.
Also in Dutch and Ukrainian. The Cmd-with-a-painting-tool case lands with
the Cmd-as-Move change, whichever merges second.
* Fix merge: close the lock-message test
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Move tool moves the selected pixels when there is a selection (#578)
With a selection, a Move-tool drag (from anywhere) or arrow-key nudge now
floats the active layer's selected pixels (select.float) instead of moving
the whole layer: the hole is transparent, or the background colour on the
Background layer; Alt copies the pixels instead of duplicating the layer;
Shift constrains the drag to 45 degrees; Enter, Deselect or any other
command drops the piece as one "Move" history step. Layers whose pixels
can't float (type, shape, smart object, group, position-locked, hidden, or
several selected layers) and Auto-Select without a selection behave as
before. select.float gains "copy"; a click that moves nothing no longer
leaves a floating piece behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pipette cursor test: Windows draws the precise crosshair itself
input_tests::eyedropper_and_alt_sampling_show_a_pipette (#1028) expected
the OS crosshair with Other Cursors = Precise, but on Windows the canvas
draws its own crosshair and hides the OS cursor (visible_crosshair), so
the test failed on every Windows run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHgFEHEsefoQoBParH47JA
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Selection drags: ⌘⌥-drag floats a copy; ⌘-drag with the Polygonal Lasso and Magic Wand
- `select.float {"copy": true}` floats a duplicate of the selected pixels
and leaves the originals in the layer; ⌘⌥-drag inside the selection uses
it (⌘-drag still cuts).
- ⌘-drag inside the selection now also floats it with the Polygonal Lasso
and the Magic Wand (not while a polygon is being drawn); a plain press
there still places a point / samples, so only ⌘ moves the selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ / ⌘⌥ with a selection always cut / copy and drag it, never combine selections
With a selection tool and an active selection, a ⌘ or ⌘⌥ press anywhere (not
only inside the ants) floats the selected pixels (cut, or copy with ⌥) and
drags them; ⌥ no longer turns it into a subtract. The cursor shows no
selection-mode badge while ⌘ is held over a selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ with a selection tool moves the layer outside the selection; ⌘⌥ duplicates it
With a selection tool, ⌘ acts as the Move tool for the drag: inside the
selection it cuts the selected pixels (⌘⌥ copies them), and outside the
selection, or without one, it moves the whole layer; ⌘⌥ duplicates the
layer first and moves the copy. ⌘ never combines selections, so the cursor
shows no add / subtract badge while it is held.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ outside the selection moves the layer, not the selected pixels
Only a ⌘ press inside the selection floats its pixels; outside it the layer
moves (`command_moves_layer`). Drops the duplicate badge check.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘⌥-drag on a floating piece drops it and drags a copy
A floating piece (from a ⌘ cut-drag) dragged again with ⌘⌥ is dropped where
it is and a copy of it floats with the pointer, as ⌘⌥ does on any selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Jason Short <3344856+jason4short@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Engine (scripts, the control channel and agents can drive it):
- `select.float {"dx","dy"}`: cuts the selected pixels of the active layer
into a floating piece the first time, then moves it by whole pixels.
The document is unchanged while it floats (`DocState::floating`);
`float_cmds::displayed` gives the document with the piece at its offset.
- `select.drop`: drops the piece into its layer and moves the selection
with it, one history step ("Move Selected Pixels").
- Any other command drops a floating piece first (a hook at the top of
dispatch, so it lands where it was shown); Undo puts it back instead.
- The layer is split once, only inside the selection, and the piece is
cropped to it: placing it costs the piece, not the layer (6000×4000
layer, 500×400 piece: 18 ms to cut, under 5 ms per move).
Canvas (Marquee / Lasso):
- A drag inside the ants moves the outline (`select.transformSelection`);
a click inside without moving deselects; ⇧ / ⌥ drags still draw.
- ⌘-drag floats the selected pixels and shows them at the pointer live;
plain drags on the piece move it again; a press elsewhere drops it.
- A move cursor over the ants; the ants follow the dragged / floating piece.
Co-authored-by: Jason Short <3344856+jason4short@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>