* Levels, Curves and the other adjustments: ⌥ plus the shortcut opens the dialog at its last settings (#8900)
* Image › Adjustments: ⌥ while choosing an item opens it at its last settings (#8900)
* Tests: the ⌥-click test picks the Image menu's Adjustments row, not the panel tab (#8900)
* Image › Adjustments: an item opened with ⌥ lands in Edit › Search's recents too (#8900)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(blend): Soft Light uses the W3C curve over dark backdrops (#8804)
Photoshop's Soft Light moves the backdrop towards D(cb) in the upper half,
and D(cb) is the cubic ((16·cb − 12)·cb + 4)·cb below a quarter, not
sqrt(cb). With sqrt the Soft Light cell of psd-tools rgb-blend-modes.psd
was up to 20 levels too light over the dark rainbow; with the cubic it is
within 4 of Photoshop's merged image.
Same change in compose.wgsl so the GPU compositor stays at CPU parity.
Krita's own soft_light (the sqrt curve) now opens as the approximation and
svg:soft-light / krita:soft_light_svg as the exact match, without warning.
Fixes#8804
* compose: the see-through Soft Light test expects the W3C curve over a dark backdrop (#8804)
The #6232 test computed Photoshop's Soft Light of yellow as sqrt(b) for red and
green. Its backdrop #04170F is below a quarter in both channels, where the W3C
curve (Photoshop's, measured in #8804) uses ((16b - 12)b + 4)b instead.
* Crop default mode: moving the image under the box snaps (#8580)
In the default (non-Classic) crop mode a drag inside the box moves the
image under it, but snapping was switched off for that drag, so content
couldn't be centred in a larger box without leaving for the Move tool.
The drag now snaps like a Classic Mode frame move: the frame moves by the
image's travel the other way over the document, its edges and centre snap
to the document, layers, guides and grid (smart guides when those are
empty), and the pointer is corrected in the press camera, as crop_ui reads
the drag. A turned box still doesn't snap.
Fixes#8580
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Crop image move: smart guides snap when no Snap To target is in reach (#8622 review)
The document's bounds keep the regular targets non-empty, so the smart
guides were never tried: with View › Snap To › Layers off, the box never
snapped to a layer while the image moved under it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Khawar Zamman Wani <wanikhawar@Khawars-MacBook-Air.local>
Delete the coverage tests, the per-language parity checks and the tests
of one language's exact wording. Catalog integrity, placeholder and
plural-form checks stay, as do the engine tests (locale resolution,
plural rules, fallback, language scoping, drawing in a language); those
compare against tr() or the plural rule rather than catalog wording.
Docs describe the report instead of a failing test.
swept_blocks_match_the_pixel_search skips the cases where the colour mode reaches extrude only
through a channel count and ink setting an earlier mode already covered. It and
damaged_layer_data_opens_flattened run a quarter of their cases per run, a different quarter each
UTC day (photocraft-testkit's Sweep). PHOTOCRAFT_FULL_SWEEP=1 runs every case, and
PHOTOCRAFT_SWEEP_PART=<n> replays the part a failing run printed.
cargo xtask test and the ci test step run cargo nextest over libs, bins and tests when nextest is
installed (cargo test otherwise, with a notice), then cargo test --doc. The wasm check is one
cargo check with --keep-going over the wasm-safe crates, plus one call per optional web feature.
Clippy runs with --keep-going.
select.refineEdge takes `edits`: the Select and Mask workspace's Quick
Selection, Brush, Lasso, Object Selection, Select Subject, Clear Selection
and Invert, applied in order to the starting selection (or layer mask)
before the edge is refined. Cancel discards them, OK is one history step,
and agents drive them over MCP and the control channel.
- engine/select_mask_edits.rs: parse (bounded, every bad field an Err)
and a pure apply/apply_one the workspace preview can share
- algo matting::brush_region: soft round brush coverage (hardness)
- ATN export reports steps with edits instead of dropping them
- "Edits" label in the 21 catalogs
Part of #7069 and #8148 (the workspace UI follows in a second PR).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in Photoshop, the Move tool's Show Transform Controls box and handles are an Extra: hiding
Extras hides them, and their handles stop responding (no hover cursor, a press doesn't start Free
Transform). Cmd+H again brings them back. Show Transform Controls and the layer are unchanged.
Fixes#6699
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- gpu: the test modules share one GPU_LOCK, and timing and vector_mask
take it too, so one process drives one wgpu device at a time.
- ui-egui: the wgpu tests in pattern_preview_canvas, adjust_preview_gpu,
marquee_autoscroll and layout_perf take gpu_lock().
- affinity compound_limits: the child process selects its test by the
module path (compound_limits::<name>); with --exact, the bare name
matches no test and the child passes without running the check.
- xtask scorecard reads crates/io/tests/it/corpus.rs.
- Docs and comments name the new test paths and the --test it filters.
Merges main (8f09d122d). Conflicts: the replay_save doc comment (main added
file.export.exportAs replay) and the status lines and history rows of
docs/file-format-parity.md and docs/gaps.md; both sides kept.
The docs called FILE-215-19 done and said only Save As and Save a Copy ask.
Photoshop's JPEG Options also has Matte, the progressive scan count
(Scans 3-5) and the size preview, none of which is offered, and Baseline
Optimized writes the same file as Baseline for RGB (4:2:0). The row is now
partial with those gaps named, and the docs say File > Save asks too.
docs/scorecard.md is regenerated.
Adds jpeg_options_ui::tests::the_answer_reaches_the_encoder. Every UI test
used a fake exporter, so dropping `opts.encode.jpeg_format` from
ExportSettings::io_options (the desktop and web save path) left them all
green; this test writes a real JPEG through io_options and fails without it.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 20 i18n catalogs: main and the PR both appended a block at the end;
both kept, main's first. docs/gaps.md: main's text with the PR's
TYPE-218-2 change applied (G8 Missing, Evidence 5 done / 4 partial /
0 missing, revision row), dated 2026-10-22. docs/scorecard.md
regenerated with cargo xtask scorecard.
The TYPE-218-2 note now also says PSD doesn't store the paragraph
direction (crates/text psd.rs maps only the vertical WritingDirection),
so a direction set with the new buttons reopens from a PSD as Auto.
docs/localization-parity.md: the Arabic row said paragraph direction has
no UI (TYPE-218-2); it now has the buttons, the RTL caret is still missing.
Co-authored-by: tareqziad1998 <83318513+tareqziad1998@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 32-bit branch cut the slider to floor(|s| * 255) / 255, which only matches
Photoshop near 25 and 50 %: measured at every whole percent from -100 to +100
on a 32-bit document, Photoshop lowers by 1 - floor(|s| * 256) / 256 and raises
by 255 / (255 - floor(s * 254)) taken to 1/256 and capped at 32767/256 (201 of
201 exact). +100 gave 512x where Photoshop gives 128x, +90 9.8x for 9.44x.
It also kept the HSL saturation unclipped, which is inf at HSL lightness 1 and
0 and negative above 1: (2, 0, 0), red at +1 stop, turned NaN with any edit,
Hue +40 alone included, where Photoshop gives (2, 1.333, 0). The 32-bit branch
now scales the chroma around the lightness instead (same result in gamut),
on the CPU and in the shader, in whole numbers on both. Colorize clamps its
saturation before the 32-bit scaling, so a PSD's out-of-range value renders
the same on the GPU.
Tests (fail without this): thirty_two_bit_saturation_steps_match_photoshop,
thirty_two_bit_hue_saturation_keeps_bright_colours, the GPU parity
hdr_hue_saturation_bright_colours. Photoshop-oracle floor 188 -> 190 of 258,
measured after merging main.
Co-authored-by: Timothy Daly <100327160+timothydaly1@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merges main through #3283 (Smart Filters: distinct editors, shared
disclosure and stable alpha).
filter_dialog.rs: main's split of the preview into is_smart_edit,
smart_preview_factor and scale_preview_params is kept. The plug-in branch
from #7524 (plugin_cmds::scale_for_preview scales only declared pixel
lengths) moved into scale_preview_params, so it applies to a plug-in run
on its own and to a plug-in recorded as a smart filter.
smart_ui.rs open_editor: main's smart_filter_dialog::target flow and the
shared adjust/filter dialog opening are kept; the Liquify re-open with
recorded strokes (r4) stays ahead of it. The old per-adjustment
edit_filter call is dropped with main's rewrite.
Resolutions: CLI usage, flag lists and flag parsing carry both --tiff-compression (t15) and
--jpeg-format (t16); file.saveACopy, batch, imageProcessor and layersToFiles param specs carry
both tiffCompression and jpegFormat; file_cmds keeps tiff_compression_param and
jpeg_format_param; atn_cmds keeps flag_set/applies_at_canvas_bounds (t15) and UNHOLDABLE (t16),
edit.transform uses the pixel_target guard (t15) with layer_ref(p)? (t16); ui-egui open paths
build ImportOptions (t16) and apply raw_defaults (t15); docs headers/rows unioned; atn tests keep
both sides; tr.tsv "Keep Using CPU" kept once.
crates/io/tests/corpus.rs: kept main's pass_floor 188 (the PR's 187 would lower it; not re-measured here)
docs/file-format-parity.md: kept main's header and PSD/PSB rows; added the PR's change-log row without its floor figure
docs/gaps.md: kept main's header and evidence line; added the PR's change-log row without its floor figure
docs/scorecard.md: ours
crates/io/tests/corpus.rs: kept main's pass_floor 188 (TAKE-FIX: drop the PR's 186 hunk)
docs/file-format-parity.md: kept main's header and PSD/PSB rows; added the PR's change-log row without its floor figure
docs/gaps.md: kept main's header and evidence line; added the PR's change-log row without its floor figure
docs/scorecard.md: ours
docs/gaps.md: header and change-log row once each (the PR's row on top).
docs/ui-parity.md: header and change-log row once each; the Selection row keeps main's #6549 sentence and adds the PR's #6548 sentence.
The Hand tool's held Zoom goes through `zoom_tool::drag`, so with Scrubby
Zoom off a ⌘-drag draws a zoom rectangle rather than scrubbing. Say so in
the parity row, and test that case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/format/src/store.rs: the Windows tiles/blobs directory flush goes before main's manifest write (main publishes the manifest before removing stale previews), and that write takes the PR's fs argument; the PR's second manifest write is dropped.