Commit Graph
1909 Commits
Author SHA1 Message Date
Yuki-init e3a84b628d TIFF Options: compression, image pyramid and Save Transparency (#8827) (#10932)
Offer TIFF compression, image pyramid and transparency options on Save As and Save a Copy, with matching CLI and automation parameters.

Follow-up: preserve the unsaved state when a copy discards layers over the document's own file, and check the other export formats for the same issue.

Closes #8827.
2026-11-03 15:39:15 +01:00
Hartmut NörenbergandClaude Opus 5.5 2fff1f8688 fix(ui): Ctrl+Alt + wheel zooms the canvas like Alt + wheel (#10488) (#10517)
* fix(ui): Ctrl+Alt + wheel zooms the canvas like Alt + wheel (#10488)

Ctrl+Alt+wheel over the canvas did nothing useful because two conditions in
wheel_nav.rs excluded it from the Alt gesture:

    let latest_alt = wheel.last().map(|(_, _, m, _)| m.alt && !m.command && !m.ctrl);

and, in the raw-event accumulator, every branch was filtered with
`!m.command && !m.ctrl`. Ctrl+Alt+wheel therefore never set the tracked `alt`
and never contributed a notch to `raw`: with Preferences > General > Zoom with
Scroll Wheel ON it produced nothing at all (raw stayed zero, so the zoom read an
eased ~0), and with it OFF it fell through to classify's `_` arm and merely
scrolled vertically, because egui keeps vertical_scroll_modifier = ALT and does
not fold the delta onto x when both scroll patterns match. The combination is a
dead/incorrect gesture today, so no working behaviour is being replaced.

The new `alt_gesture` helper forgives `command` only when `ctrl` is set as well.
winit mirrors a held Ctrl on Windows (and Linux) into BOTH ctrl and command, so
a bare `!command` test can never match what a Windows reporter typed; macOS
Cmd+Alt (command set, ctrl not) is left out of the zoom gesture (egui 0.37 does not fold it sideways either: with Alt held both scroll patterns match, so its delta stays vertical). `latest_shift` stays strict, so
Ctrl+Alt+Shift+wheel zooms continuously instead of snapping to the ladder, and
the Shift-only and plain branches are untouched, so plain Ctrl+wheel still
scrolls sideways (#635). `legacy_pinch_to_zoom` is untouched and cannot
double-count a fractional Ctrl+Alt pinch step: it matches `modifiers.ctrl`
whatever else is held, but it *replaces* the wheel event with `Event::Zoom`, so
`wheel_events` never sees that step a second time.

Deliberately out of scope: the other half of the report, the centred-zoom /
overscroll complaint. The per-frame view clamp at canvas.rs:1737-1741 runs only
when Preferences > Tools > Overscroll is off (it is on by default), so with the
defaults nothing pulls the view centre back, and what the reporter is seeing is
more likely the deliberate wheel_anchor nearest-image-point clamp
(view_xform.rs:239, #3720) or Zoom Clicked Point to Center. That needs its own
reproduction and issue, and is noted as still open in the dev log.

Tests, fail-first: each_modifier_maps_like_photoshop gained Windows Ctrl+Alt
rows (preference off and on, instant and eased, a fractional pinch step counted
once, and a macOS Cmd+Alt guard row) while keeping its Ctrl-only sideways-scroll
rows; new full-app kittest test
input_tests::ctrl_alt_wheel_zooms_the_canvas_like_alt_wheel zooms five notches
around the pointer and holds the document point under it. Both failed before the
production change and pass after.

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

* test(ui): Ctrl + Alt + Shift + wheel is dead today, not a zoom (#10488)

CodeRabbit 4432102938 asked for an assertion on the Ctrl + Alt + Shift combination instead of
only a comment, and for the reason: the filter rejects the wheel event. Both are right. With ctrl
held, latest_shift (wheel_nav.rs:381) is false because it excludes ctrl and command, so the alt
branch of the filter (wheel_nav.rs:413-422) runs and its "!m.shift" drops the notch out of raw;
shift stays false, so the snap branch never takes it either.

The new row in each_modifier_maps_like_photoshop first asserted what my own comment claimed - a
continuous zoom, with Zoom with Scroll Wheel off and Animated Zoom off - and failed exactly that
way: "Ctrl + Alt + Shift + wheel zooms 10%, at once: [0.0 0.0] 1 snaps 0". Pan zero, zoom 1.0,
snaps 0, and the same numbers with Animated Zoom on, while plain Alt + Shift still snaps. The
gesture is dead, and only the ctrl half of it kills it.

So the three places that asserted a continuous zoom are corrected here instead of left to rot: the
module doc (wheel_nav.rs:8-11), the comment above the filter (wheel_nav.rs:407-413) and the parity
row (docs/ui-parity.md:193). They now state that the gesture is dead, point at the test that pins
it, and name the open question. No behaviour change: the repo has no measured Photoshop evidence
for this combination (the issue names only Ctrl + Alt), so the choice - (a) accept Shift in the alt
branch when Ctrl is held, making it zoom like Alt + wheel, or (b) treat it as exactly Alt + Shift
and snap - goes to a maintainer rather than being invented. Option (b) has a measured cost worth
knowing before anyone picks it: plain Ctrl + Shift + wheel pans sideways today ([40,0], zoom 1.0,
snaps 0, and no test covers it), and under (b) that becomes a no-op with Zoom with Scroll Wheel on.

Gates: cargo test -p photocraft-ui-egui --lib -- wheel = 102 passed, 0 failed (with the promised
assertion first: 101 passed, 1 failed); cargo clippy -p photocraft-ui-egui --all-targets
-- -D warnings clean; cargo fmt -p photocraft-ui-egui -- --check clean.

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

* test(ui): pin Ctrl + Alt + Shift + wheel with Zoom with Scroll Wheel on (#10488)

Review 4433950859: both Ctrl + Alt + Shift + wheel assertions ran with Zoom with
Scroll Wheel off, while the test comment, the wheel_nav.rs filter comment and the
ui-parity.md Wheel row stated the no-action result unqualified. Measured, not
copied: a temporary probe test ran one Ctrl + Alt + Shift notch (Line unit,
gesture_with, 60 frames summed) in all four preference combinations and printed
(pan, zoom, snaps) through an assert that was made to fail:

    zww=off anim=off ([0.0 0.0], 1.0, 0) | zww=off anim=on ([0.0 0.0], 1.0, 0)
    zww=on  anim=off ([0.0 40.0], 1.0, 0) | zww=on  anim=on ([0.0 40.0], 1.0, 0)

    test result: FAILED. 102 passed; 1 failed; 0 ignored; 0 measured; 4526 filtered out; finished in 2.63s

The suggestion's premise holds: the dead-gesture claim is true only with Zoom
with Scroll Wheel off. With the preference on, alt_gesture keeps the notch out of
the branch's raw, but `alt != zoom_with_wheel` is false so the zoom arms are
skipped, and egui's own smoothed scroll delta (which the raw filter never gates)
pans the notch through the ordinary scroll path: one whole notch, 40 points
vertical, no zoom, no snap, with Animated Zoom off AND on (it only spreads the
same 40 points over later frames).

Probe deleted; the qualified version landed in all three artefacts:
- wheel_nav/tests.rs: the two dead assertions now say "with Zoom with Scroll
  Wheel off", two new assertions pin the preference-on pan (near [0.0 40.0],
  zoom 1.0, snaps 0, both Animated Zoom modes), and the comment and the
  "not even a scroll" sentence carry the preference condition and the numbers.
- wheel_nav.rs classification comment: the dead-gesture sentence is qualified
  the same way.
- docs/ui-parity.md Wheel row: same qualification plus the measured numbers.

Final gates: cargo test -p photocraft-ui-egui --lib -- wheel ->
test result: ok. 102 passed; 0 failed (also re-run after cargo fmt re-wrapped
one over-long assert, same result); clippy -D warnings clean; fmt --check exit
0; cargo xtask layers: OK, 31 crates, no layering violations.

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

* fix(ui): keep Cmd+Ctrl+Alt + wheel off the Alt zoom path, pin AltGr (#10488)

alt_gesture now also requires !mac_cmd when forgiving command, so macOS
Cmd+Ctrl+Alt (command, ctrl, mac_cmd, alt) is not the zoom gesture. Tests pin
the macOS and Windows/Linux modifier shapes and that AltGr reaches egui as
neither ctrl nor alt.

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

* fix(ui): keep Ctrl+Alt+Shift + wheel as before, correct the AltGr note, pin the Alt key fallback (#10488)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:36:39 +01:00
FredApps 66e9325379 Merge remote-tracking branch 'upstream/main' into pr-5332 2026-11-03 11:16:12 +01:00
FredApps 5f70428dfc Merge storytold/main (d0bff086d) into PR #10897 branch
Resolve conflicts with #5331 (EXR streaming): the block-by-block EXR import now
reads from the same identity-verified handle as the rest of the open path, and
docs/scratch-disks.md keeps both the EXR streaming pointer and the RAM/scratch
admission notes.
2026-11-03 10:33:10 +01:00
FredApps 57443b6a30 Merge remote-tracking branch 'upstream/main' into pr-5332
# Conflicts:
#	crates/codecs/src/exr_stream.rs
#	crates/codecs/tests/it/exr_stream.rs
#	crates/io/src/exr_native.rs
#	crates/io/src/lib.rs
#	crates/io/tests/it/streaming_exr.rs
#	docs/roadmap.md
#	docs/scorecard.md
#	docs/scratch-disks.md
#	docs/streaming-io.md
#	scorecard/reliability.toml
2026-11-03 10:26:54 +01:00
FredApps 0ec30fcf7c Merge storytold/main into codex/gpu-viewport-streaming
Main moved the GPU canvas into canvas/gpu_canvas/* and gained its own
budget, LayerFit and background-redraw machinery. Conflicted files take
main's version; the viewport streaming is re-integrated on top of main's
budget in the following commit.
2026-11-03 09:59:21 +01:00
FredApps 3695186329 Consolidate RAM detection in raster::memory::system; scratch-aware PSD/TIFF admission
One RAM detector: the cgroup v1/v2, macOS reclaimable and Windows commit providers move from
photocraft-io into photocraft_raster::memory::system (L0), which the residency budget (#5330)
and photocraft_io::memory both read. Unreadable container limits still fail closed, swap is
never counted as RAM. sysinfo is unified on =0.40.1 (raster only).

While the scratch disk is spilling (on, not paused), import admission credits the tile pixels
of an estimate against spill::scratch_capacity() (free space above the reserve, within the
Scratch Size Limit); decode buffers, conversion copies and merged planes stay RAM-only. Spilling
off/paused and the web keep the RAM-only policy unchanged. PsdMemoryEstimate gains tile_bytes;
the flat TIFF streaming check credits its tile surface the same way; engine approveMemory's
fast pre-check uses the credited hard limit and the import re-checks exactly.
2026-11-03 09:48:22 +01:00
FredApps 7e29a9999c Wire EXR band streaming into main's path open
- import_file_for_open_with decodes single-part flat scanline EXR block by block into
  tiles; headers the band decoder refuses fall back to the whole-file decoder, so files
  main opened still open. Band-time failures and cancellation publish no document.
- The RAM tile budget and scratch-disk checks live in exr_native (the PSD stream module
  they came from is dropped); over-budget opens fail with IoError::OutOfMemory.
- The band decoder picks colour channels with exr_color_channels, like the byte decoder.
- Tests move into the per-crate it binaries and use import_file; an engine open-job test
  covers the OpenSource::Path route. The ui-egui sync open test is dropped: main's
  synchronous open reads bytes, and the io test covers the oversized header.
- docs/streaming-io.md, roadmap, scratch-disks and REL-212-1 describe EXR only.
2026-11-03 09:23:15 +01:00
FredApps fdab6c0a33 Merge remote-tracking branch 'upstream/main' into pr-5331 2026-11-03 09:06:31 +01:00
FredApps 25f21db893 Revert "Stream native PSD and EXR imports through bounded seekable bands"
This reverts commit 89ed4cb284 from the
#5332 branch so the GPU viewport streaming lands standalone. The import
work is rescoped separately in #5331 (EXR streaming only).
2026-11-03 09:03:38 +01:00
Khawar Zamman Wani 259b5590ae Merge origin/main (655295b2a2) into PR #10897 branch
Keep the PSD memory admission work and upstream's scratch storage
together. Conflicts resolved:
- Cargo.lock: both sysinfo versions are required. 0.40.1 serves
  photocraft-io's adaptive import budget; 0.37.2 serves
  photocraft-raster's scratch volume and memory supervision. Cargo
  metadata resolved the lock with no other changes.
- crates/engine/src/lib.rs: Session keeps pending_preflights and
  next_pending_preflight beside scratch_runtime and scratch_epoch.
  Session derives Default, so neither set needs a manual initializer.

Upstream: 655295b2a2
2026-11-03 12:54:59 +05:30
FredApps 8ce599da3a Merge remote-tracking branch 'upstream/main' into pr-5330 2026-11-03 07:12:38 +01:00
dotCooCoo fef15729aa Merge main into feat/pcraft-full-composite
Main now takes the manifest envelope from craft-pcraft-format, which has
no full_composite member. The save writes the member after the envelope
when it writes composite/full.png, and read_full_composite reads it back.
2026-11-02 17:02:14 -08:00
FredApps ed681787c3 Add a Scratch Size Limit preference and honour the web memory cap in the memory policy 2026-11-03 01:16:52 +01:00
FredApps fb684cd114 Merge remote-tracking branch 'upstream/main' into pr-5330 2026-11-03 00:02:18 +01:00
FredApps 9eddf1d8ca Pause and retry a failed or full scratch disk instead of turning spilling off
A scratch write or create failure, or a volume about to drop below max(1 GiB, 5%)
free space, now pauses spilling with exponential backoff (1 s doubling to 60 s)
instead of disabling it for the session. Tiles stay resident, set_budget keeps
the latest budget for the resume, preference changes retry at once, and
spill::epoch lets the engine re-apply History's byte limit while paused.
spill::set_size_cap adds an explicit size limit; free space comes from sysinfo's
disk feature.
2026-11-02 22:31:56 +01:00
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 947038d0db Unlink Unix scratch files right after opening
The spill manager is process-global and never dropped, so the Unix scratch file was only
removed by the next launch's reap. Unlink it immediately after opening and read/write
through the one handle with pread/pwrite (readers can no longer reopen it by path).
Windows keeps delete-on-close and per-thread readers. The abrupt-exit test now asserts
that no file remains on every platform; docs/scratch-disks.md updated.
2026-11-02 22:17:50 +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
Khawar Zamman Wani 5992650b36 fix: address PSD admission review and enable platform memory consent 2026-11-03 01:26:13 +05:30
David Mann 673bfb4d9c Merge remote-tracking branch 'origin/main' into integration-batch-12
# Conflicts:
#	docs/file-format-parity.md
2026-11-02 20:15:23 +01:00
FredApps f8e393fdb6 Merge pull request #6266 from danielmigueltejedor/fix/gnome-window-decorations
Enable font-free GNOME/Wayland controls for system title bars
2026-11-02 19:59:41 +01:00
9034223c4b TIFF: open YCbCr images as RGB (#10316) (#10319)
* TIFF: open YCbCr images as RGB (#10316)

Scanners often write JPEG-compressed YCbCr TIFFs with 2x2 chroma
subsampling. The tiff crate decodes and upsamples them but returns raw
YCbCr samples, which the decoder rejected with "colour type YCbCr(8)".

The new tiff/ycbcr.rs converts them to RGB after decoding: JPEG data
uses the JFIF full-range BT.601 conversion (as libtiff does), other
compressions honour ReferenceBlackWhite and YCbCrCoefficients with the
TIFF defaults as fallback. 8 and 16-bit. YCbCr with extra samples, or
in other sample formats, fails with a clear unsupported error.

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

* Contributors: add Yuki-init's display name

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

* TIFF YCbCr: test that YCbCrCoefficients set the luma weights

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

* TIFF: YCbCr tags with the wrong value count use the defaults

tag_f64s now returns None unless the tag holds exactly n values, so a
YCbCrCoefficients or ReferenceBlackWhite tag with extra values falls
back to the TIFF defaults instead of being truncated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn

* TIFF YCbCr: tests for tiled JPEG with ICC, big-endian 16-bit and planar

Covers the layouts the review asked for: a tiled 4:2:0 JPEG file with an
embedded profile (checked against an independent JPEG decoder), a
big-endian 16-bit file, and planar data in both byte orders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn

* TIFF YCbCr: cite the formula sources and pin the green-channel choice

Documents where the conversion comes from: TIFF 6.0 Section 21
(YCbCrCoefficients, pp. 90-91) and libtiff's tif_color.c
(TIFFYCbCrToRGBInit, TIFFYCbCrtoRGB). Red and blue follow the spec;
green follows libtiff and adds Y unscaled, which matches the spec only
when the luma weights sum to 1. The module doc records this as a
deliberate compatibility choice, and a new test pins it: with weights
0.25/0.25/0.25, mid gray stays (128, 128, 128).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn

* Codecs README: YCbCr TIFFs with three 8- or 16-bit samples decode to RGB

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn

* Format tiff_ycbcr tests with rustfmt

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

* test(tiff): compare grouped YCbCr decoding and update support notes

* style(tiff): align grouped decode call with test formatting

* style(tiff): apply repository rustfmt to grouped YCbCr regression

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-02 19:31:22 +01:00
FredApps 9236d27eb6 Merge current main into fix/gnome-window-decorations 2026-11-02 19:10:43 +01:00
David MannandClaude Opus 5.5 f79a169651 Integration: one Actions row and the batch's event total in the file format parity page
The doc merge left five Actions rows that disagreed (version 12 unsupported vs read; 120 vs
127 events). Found by CodeRabbit on #10776.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:42:36 +01:00
Khawar Zamman Wani 582a285b5d fix: enforce scoped PSD memory admission and complete retry integration 2026-11-02 19:01:15 +05:30
David MannandClaude Opus 5.5 8caea149c1 Integration: regenerate docs/atn.md for the batch (144 supported events, settings each PR keeps)
Supported table read from the batch's events.rs. The PRs' own sections (settings and command
options retained, Render defaults, steps kept disabled) are kept; the known-gaps tables that
#10702 and #10704 close are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:03:44 +01:00
Khawar Zamman Wani bcc060180f feat(io,engine,ui): adaptive PSD/PSB memory budget and interactive confirmation
- Distinguish available physical RAM from swap and commit headroom
- Adaptive soft and hard import thresholds with native fallback
- Interactive preflight confirmation modal before pixel decoding
- Headless and command parameters for explicit high-memory approval
- One-shot source-bound validation rejecting altered sources or degraded RAM
- Process-wide serialized decode gate with reentrancy support
- Documentation and synthetic regression test coverage
2026-11-02 17:11:03 +05:30
David Mann 79a2a4e15f Merge PR #10632: Actions: map all 47 Filter Gallery effects (#10592)
With our fix-up 023e2662d7 on top of the author's head. Conflicts resolved item by item (Rust), docs as unions.
2026-11-02 03:18:45 +01:00
David Mann 520a996b30 Merge PR #10630: Actions: map Photoshop's Split Channels, Merge Spot Channel, Trap and Match Color steps (#10609)
With our fix-up a299bf9f6c on top of the author's head. Conflicts resolved item by item (Rust), docs as unions.
2026-11-02 03:15:28 +01:00
David Mann 496d9f3623 Merge PR #10618: Map Stylize and Pixelate action events
With our fix-up 52cc86351b on top of the author's head. Conflicts resolved item by item (Rust), docs as unions; docs/atn.md regenerated at the end.
2026-11-02 03:10:21 +01:00
David Mann 5d6041c1cd Merge PR #10628: Map selection, clipboard and layer-operation ATN events
With our fix-up 0e41091687 on top of the author's head. Conflicts resolved item by item (Rust), docs as unions; docs/atn.md regenerated at the end.
2026-11-02 02:55:08 +01:00
David Mann 4f899196cb Merge PR #10702: Actions: preserve settings in mapped ATN events
With our fix-up 78cf0cf91f on top of the author's head. Conflicts resolved item by item (Rust), docs as unions; docs/atn.md regenerated at the end.
2026-11-02 02:55:02 +01:00
0e41091687 Integration: Group F steps recorded by the reference application map (#10628)
The Group F loader only mapped descriptors with an empty name and class
`null`, and nested shapes with an empty name. The reference application
names every step descriptor after the localized step (plus NUL), uses
the event code as its class and names nested shapes too, so every real
recorded Add To / Fade / Merge Layers ... step stayed a kept step and
never played (e.g. `addTo` with name "Add To\0", class `AddT` and a
nested `Rctn` named "\0"). The header is no longer checked; the shape
is matched by class only; key, order and enum checks stay strict.

IntW, Dstr and MrgL were registered with the string IDs
`interfaceWhite`, `distort` and none; the real ones are
`intersectWith`, `distribute` and `mergeLayers`, so steps written with
them stayed kept.

Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 02:50:33 +01:00
David Mann d570b77abb Merge PR #10753: Support recorded layer, document and layer comp references
With our fix-up ea4112a4ce on top of the author's head 686f08c884. docs/atn.md kept from the batch (regenerated at the end); development.md and parity doc rows as unions.
2026-11-02 02:40:55 +01:00
David Mann 48b0e221cd Merge PR #10627: Actions: map Photoshop's newer-feature steps (Oil Paint, Smart Sharpen, Focus Area, Sky, Remove Background) (#10610)
With our fix-up 6ab24508d8 on top of the author's head 8988b6bfd0. Conflicts resolved: import lists as unions (bare_from/bare_to taken from steps_other, #10631), event table, tests and parity doc rows as unions.
2026-11-02 02:16:45 +01:00
David Mann 5bc852379a Merge PR #10743: Read Photoshop Actions version 12 descriptors
With our fix-up 1f524c378b on top of the author's head 713aa92ba5. Resolved with main: the version check sits in main's parse_layout (it merged cleanly); doc comments combined; atn_contract no longer expects version 12 to be refused and pins that an empty version 12 set parses; one ATTRIBUTION row for all ATN fixtures; the fixture README states which files were encoded how; development.md no longer counts the fixtures.
2026-11-02 02:13:56 +01:00
6ab24508d8 Integration: .atn Oil Paint/Smart Sharpen clamp on import, Smart Sharpen maps the Gaussian form (#10627)
Out-of-range Oil Paint and Smart Sharpen settings kept the step as a
Photoshop step; the reference application clamps them when the step
plays, so the import now clamps (stylization 10.5 -> 10, Amnt 500.5 % ->
500, Rds 0.05 px -> 0.1). Export still refuses out-of-range params.

Oil Paint's light direction (LghD) had no range: LghD 500 mapped and
saved back as 500. It now clamps to the command's -180..180 on import
(500 -> 180) and export refuses {"angle": 181}.

Smart Sharpen mapped the legacy Lens Blur form, which the command's
Gaussian sharpen does not play, and wrote every step as legacy Lens
Blur. It now follows the PSD smart-filter mapping (photocraft-io
smart_map): only useLegacy false + blur GsnB maps (noiseReduction ->
reduceNoise); the reference default (useLegacy true, lensBlur), Motion
Blur and legacy steps stay Photoshop steps; export writes useLegacy
false, Amnt, Rds, noiseReduction, blur GsnB.

Co-authored-by: hectorviov <1501713+hectorviov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 02:13:01 +01:00
David Mann cafaabcf04 Merge PR #10631: Actions: map Photoshop's De-Interlace, NTSC, HSB/HSL, Custom, Average and matting steps (#10607)
With our fix-up 632a62b554 on top of the author's head 4ed3f5b591. Conflicts resolved as unions (module list, event table, tests, parity doc rows).
2026-11-02 02:03:24 +01:00
David Mann 2d332d3ba5 Merge PR #10629: Actions: map Photoshop's Distort filter steps (Pinch, Polar Coordinates, Spherize, Twirl, Wave, ZigZag) (#10605)
With our fix-up 363e0f5e89 on top of the author's head 244082fb2c. Conflicts with main resolved as unions (new tests next to main's; parity doc revision rows).
2026-11-02 02:02:03 +01:00
David Mann 5a99af60d7 Merge PR #10620: Map Render filter steps in Photoshop actions 2026-11-02 01:54:44 +01:00
aiongg fab871dadb Merge pull request #10535 from davidmann4/integration-batch-11
Integration batch 11: 9 ATN PRs on main
2026-11-01 19:11:02 -05:00
dotCooCoo 7a2d998ff7 Merge main into feat/pcraft-full-composite 2026-11-01 15:51:56 -08:00
Gabriel Heuser 24be8ea6e2 Add editable arrowheads to Line tool (#6408) (#9259)
* Add configurable arrowheads to line tool

* Add editable arrowheads to Line tool

* Fix arrow scaling validation and PSD import defaults

* Preserve arrow geometry under affine transforms

* Preserve scaled arrowhead dimensions and prevent non-finite geometry

* Localize arrowhead Start and End labels

* Fix arrow transform editability and short line handling

* Fix PSD arrow concavity serialization

* Fix arrow dimension fallback validation

* Refine arrowhead options UI to match Photoshop

* Add opt-in arrowhead defaults to Shape properties

* Fix tool reset syntax after upstream merge

* Validate arrowhead flags and clamp copied defaults

* Preserve editable headless lines under transforms

* Fix arrowhead UI imports and formatting after v0.17 merge

* Format arrowhead properties after v0.18 merge

* Fix Clippy lint errors in arrow geometry and tests

* Fix headless arrow geometry after nonuniform scaling

* Fix proportional arrow scaling in Properties panel

* Fix Rust formatting in Properties arrow scaling tests

* test: validate invalid arrowhead dimensions without mutation

* fix: preserve arrow compatibility with older pcraft versions
2026-11-01 18:46:05 -05:00
dotCooCoo e7829eb449 Merge main into feat/pcraft-full-composite 2026-11-01 15:30:48 -08:00
yoriswind 01b3d74c50 docs: mark recorded ATN reference forms supported 2026-11-01 13:23:53 -08:00
yoriswind 35850ca7db Merge original ATN reference documentation from #10603 2026-11-01 13:23:37 -08:00
yoriswind 21b6546907 Resolve recorded Action reference forms at playback 2026-11-01 12:55:02 -08:00
yoriswind 4c6ddb9cb7 Correct legacy action import expectations and document version support 2026-11-01 11:49:17 -08:00
yoriswind 7ca16e9eba Read Photoshop Actions version 12 descriptors 2026-11-01 11:45:32 -08:00