Commit Graph
220 Commits
Author SHA1 Message Date
FredApps 8323688e1f Merge approved PhotoCraft PR #5453: Shapes: independent fill and stroke paints (solid, gradient, pattern), part 1
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrated independent exact shape paints and atomic pattern clipboard snapshots into current modules, preserving current soft-mask coverage, shape cache keys and gradient stop caps. Shared bounded COW pattern sampling now retains minification filtering; shapes/clipboards resolve lazy catalog entries to full pixels. Preserved CPU/GPU/depth/model regression matrices; also corrected missing glow-test enum imports found by the 80-head check.
2026-10-29 05:53:29 +01:00
FredApps c81347aaa8 Merge approved PhotoCraft PR #5493: Import Photoshop custom shape sets into the Shapes panel
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrated bounded atomic CSH set parsing, unique shape/group naming and native Shapes panel import with inline-data authorization guards; preserved duplicate-name, inner-hole, malformed-second-record and preferences-reload regressions. Combined independent catalog additions and retained current parity metadata.
2026-10-29 05:47:45 +01:00
FredApps bf33e45694 Merge approved PhotoCraft PR #9342: fix(io, engine): Embed Color Profile writes sRGB for an untagged document (#9325)
Preserve current module structure and port the approved source/test changes to unique current locations.
2026-10-29 03:51:21 +01:00
FredApps 7e061febf4 Merge approved PhotoCraft PR #9472: fix(io): import a Displace smart filter that names its map file without embedding it (#9162) 2026-10-29 03:29:54 +01:00
Brennan StehlingandClaude Opus 5.5 2119158a5e perf: a pixel change damages only the tiles that changed; command rects become checked hints (#9329)
step_damage narrows a layer whose only change is in its surfaces (pixels, mask, type,
smart-object and fill caches) to the bounding box of the tiles that differ: a merge walk of
the two tile maps, pointer compares only. A mask's tiles grow by its feather, all of them by
the layer's effects and the effects of the groups around it, never past the layer's whole
extents. A frame-spanning effect whose frame moved keeps the whole extents (#4053). Undo,
redo and History jumps get it for free: undoing a 60 px dab on a full-canvas layer redraws
its tiles, not the canvas, and so does a fill or filter on a small selection.

The painting commands' own damage rects go through one door, Session::hint_damage, which
intersects them with the edit's damage. Debug and test builds count a hint that misses a
changed tile in Session::damage_misses and log it; nothing panics. refresh_type_layers reports
step_damage instead of None.

Deleted: note_damage and layers_damage (identical to step_damage after layer.setProps and
layer.translate), note_inert_insert (step_damage already steps over inert layers, #1771),
and the engine's siblings_of they used.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 23:09:58 -07:00
David MannandClaude Opus 5.5 5ccb6fb723 Merge main
Conflicts with main's batch #9880:
- psd file.rs / layer.rs: #5287 (PSB by encoded size, RLE tables widened on
  promotion) against #6583's lazy reader. Main's new widening and length code
  read `.data`, which #6583 leaves empty while the bytes stay in the source;
  it now goes through `encoded()` / `encoded_len()` (also the auto-merged
  lines in `rle_widening`, `encoded_len` and `LayerRecord::write`), and the
  lazy reader sets `write_as_psb: false`. New test
  `a_lazy_psd_promotes_to_the_same_psb_as_an_eager_one`: a lazily read PSD
  writes the same PSB bytes as an eager one; it fails with a `.data` read.
- direct_select.rs / vector_ui.rs: #9321's Type Path and #6353's hidden
  Work Path both kept.
- io tests main.rs: both modules. scorecard.md regenerated (12 of 181);
  ui-parity.md and gaps.md follow it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 19:49:42 +01:00
David MannandClaude Opus 5.5 6fcbedd229 Merge origin/main into pr-4329-fixup
Main moved the engine commands into crates/engine/src/cmd/ and the
dialogs and panels into submodules. The PR's placement calls follow
them, and main's new insert sites (new_adjustment in adjust_params.rs,
Place in file_cmds/open.rs, shapes in vector_cmds/shape.rs) use
layer_place::insert_new like the PR's originals. The New Group dialog
moves to dialogs/new_group_ui.rs; the Layers panel flyout's New Group…
opens it.

Main's Session::precheck now also checks layer.new.group's and
layer.new.groupFromLayers' new params (name, color, mode, opacity,
position), so precheck_agrees_with_execute holds. Main had since added
"New Group…" to every catalog: the PR's duplicate rows are dropped.
Main's newer tests that made a layer with an open group active now
open the group last, as the PR did for the older ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 15:22:03 +01:00
jonathanwxh-cell f97eb2849a Merge upstream main and resolve custom shape import conflicts
Preserve CSH import behavior, inline-data authorization policy, localization and regression tests while retaining upstream module extraction.

Local verification: formatting and independent static review passed. Parser/engine/automation/UI runtime, broader Clippy, WASM and visual gates remain incomplete locally because of resource limits. Hosted CI must establish remaining gates; this commit does not claim the PR is ready to merge.
2026-10-27 19:47:58 +08:00
dotCooCoo e670397fe0 Keep uncached printer listings responsive while the spooler is busy
Return an empty or unsupported result when the spooler lock is held and no matching cached printer list exists. Preserve the source-specific cache and cover empty and mismatched caches with regression tests.

Merge current main and move the Print dialog changes into its extracted modules.
2026-10-26 23:30:52 -07:00
aiongg 065c2c2beb test: keep split modules visible to the source scans
The i18n coverage scan and prefs_usage stopped reading a file at its first `#[cfg(test)] mod`. A split parent declares `#[cfg(test)] mod tests;` before the rest of its code, so the scans skipped that code, and on main an inline test module in the middle of panels.rs hid the code after it. Both now drop only the body of an inline test module.

The prefs audits in scorecard and prefs_usage count a read when the section and the field appear in the same file. Both now also treat a split module (`foo.rs` with `foo/`) as one file, and prefs_usage leaves out `engine/src/prefs/` as scorecard already does, since that directory now holds the rest of the prefs model.

On main and on this branch: the i18n coverage report is identical (2,142 source keys; the old scan found 2,049 on main), `cargo xtask scorecard --check` is up to date, and prefs_usage passes.
2026-10-26 22:54:56 +00:00
jonathanwxh-cell 9e81ec3576 fix(shapes): store CSH display names and report the group used
Localization keys such as $$$/Presets/Shapes/Tiger=Tiger were kept raw, so the Shapes panel showed the key. File → Open now reports the group the importer stored, including a suffix when that name was already taken.
2026-10-26 09:50:13 +00:00
jonathanwxh-cell 059940c993 Merge upstream main into the CSH shape import branch
Keep the custom-shape translations and the CSH import notes with main's newer format and gap status.
2026-10-26 09:50:13 +00:00
Hasan-ZayniandClaude Opus 5.5 0f48dfacc8 fix(io): import a Displace smart filter that names its map file without embedding it (#9162)
Photoshop can save a Displace smart filter with only DspF, a path to the
map file, and no embedded DspD map. Import required the embedded map, so
such a filter became psd.unsupportedFilter and Replace Contents skipped it.
It now imports as filter.distort.displace with the path as mapPath (read on
re-render through the document path policy) and writes back unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:09:08 +03:00
4a17c64139 Integration: a straight Content-Aware crop fills over hidden pixels (#8179)
Photoshop keeps the pixels an earlier Delete Cropped Pixels off crop hid
only under a turned frame. A straight crop pulled back out over them fills
the whole area past the old canvas from the canvas, with Delete Cropped
Pixels off or on (checked: 64x48 green with a red left strip, crop to x=16
keeping pixels, then a Content-Aware crop to x=-24 w=72: all green). The
PR took the snapshot for straight crops too, so the hidden red came back.
The straight path is main's again; the turned path keeps the snapshot.

extending_crop_keeps_the_hidden_pixels becomes
extending_crop_fills_over_the_hidden_pixels (transparent and white
Background, Delete off and on); it fails with the straight snapshot back.

Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 14:35:33 +01:00
David Mann 14946918cd Integration: merge main (#8179) 2026-10-25 14:33:03 +01:00
Hector VillarrealandClaude Opus 5.5 6f1164e034 fix(io, engine): Embed Color Profile writes sRGB for an untagged document (#9325)
An untagged document (a new one in the default working space, or a file
opened without a profile) is treated as sRGB (sGray for grayscale), but
Export As, Save As and Save for Web wrote no profile for it, so viewers
had to guess. Photoshop embeds sRGB IEC61966-2.1 for both a new and a
"Don't Color Manage" document when the profile is embedded.

A flat export to a format that carries profiles now tags an untagged
image with the profile assumed for its mode; the codec still leaves it
out when embedding is off, and formats without profiles are unchanged.
Save for Web does the same when Embed Color Profile is on.

Tests that relied on untagged exports carrying no profile (an EXIF
ColorSpace round trip, the Export As proxy-estimate premise, an Affinity
preview fixture, a WebP lossy check) now say so explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:40:40 -06:00
aiongg 360150dcd4 Merge pull request #7281 from wanikhawar/claude/blur-tool-lag-684245
perf(retouch): releasing a Blur/Smudge/Dodge stroke takes its live pixels
2026-10-24 11:13:32 -04:00
jonathnwxh 050ac592d4 Merge main preserving CSH, ASL and contour imports and translations 2026-10-24 15:57:08 +08:00
David Mann 2b0deddec2 Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/ui-egui/src/panels.rs
2026-10-24 00:51:37 +02:00
Omar SaidahmedandBrandon Thomas afd5127dc8 perf: skip redundant smart object resamples and per-pixel reads (#6406)
* skip redundant smart object resamples and per-pixel reads

* resample smart objects only when their re-render fails

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:36:54 -04:00
Perchenko AntonandBrandon Thomas f3941972df fix(actions): preserve scalar Selective Color settings in ATN export (#7681)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 18:34:54 -04:00
David Mann 9c5a7b907a Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/ui-egui/src/i18n/tr.tsv
#	docs/ui-parity.md
2026-10-24 00:34:11 +02:00
5750ed40f9 Fix pasted shape group bounds and effects across documents (#8992)
* fix(clipboard): render copied shapes in destination canvas space

When a copied shape group is placed beyond its source canvas, rebuild its vector caches against the destination canvas and pixel format. Previously a centered paste into a larger document could clip the caches to an empty source-sized frame, leaving no transform bounds and incorrect effects until a point edit refreshed each shape.

Restore missing shape caches and refresh source-clipped caches when canvas dimensions change. Cover Paste and Copy to Document, nested group contents, 8/16-bit destinations, rendered cache equality, transform bounds and movement Undo.

* fix(clipboard): measure shape placement before destination clipping

Restore missing source-space shape caches before measuring copy bounds, then adapt and translate for the destination. Re-render empty caches when shapes move back onto the canvas instead of shifting an empty raster.

Add focused integration coverage for 32 combinations of partial and full destination clipping, grouped shapes, missing caches, 8/16-bit output, centered and point placement, and Undo/Redo. Clippy and WASM checks pass.

---------

Co-authored-by: kotulsky <7805437+kotulsky@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-23 17:54:26 -04:00
Zuujis 4bda8d7388 test(panic_hunt): give the panic-counting self-test its own deadline (#8473)
contained_panics_count_and_graceful_errors_do_not used the 4 s hang
budget meant for fuzzed commands. With RUST_BACKTRACE=1 the panic hook
prints a backtrace, and on loaded Windows runners the first symbol
lookup took long enough (19.6 s) that run_watched returned None instead
of Some(false). The self-test now waits up to 120 s; the fuzz keeps 4 s.
2026-10-23 17:40:32 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
aiongg 18f4b5348c Tests: spread three format-by-geometry matrices over the days, and build one precheck state per case
The tilt-shift reference tests and tiled_clear_and_fill_match_one_pass_over_the_area cross pixel
formats or channel counts with independent geometry and fill settings. Each run takes a quarter of
the combinations through Sweep, a different quarter each UTC day.

precheck_agrees_with_execute built all five document states for every call and kept one; it now
builds only the state the call needs.
2026-10-23 08:30:48 +00:00
David Mann 24051914fd Merge PR #6352: fix(engine): honor lock all on layer mask commands 2026-10-22 23:40:15 +02:00
David Mann 6e4d89d13d Merge PR #8473: test(panic_hunt): give the panic-counting self-test its own deadline 2026-10-22 23:40:12 +02:00
edea110601 Integration: refuse Paste Into a Lock-All group before pasting (#6352)
With the mask commands now honouring Lock All, Paste Into / Paste Outside with a
child of a Lock-All group active committed edit.paste and then failed on the
refused layer.layerMask.revealAll, leaving an unmasked pasted layer behind an
Err. paste_into now checks the destination group's locks first and refuses
cleanly. Test: mask_lock_all::paste_into_a_lock_all_group_refuses_before_pasting
(layer count and history unchanged). Also moves mask_lock_all.rs into main's
one-binary tests/it layout.

Co-authored-by: osaidahmed <170216456+osaidahmed@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:52:59 +02:00
dotCooCoo 9e8657984e Merge main into fix/print-listing-serialized 2026-10-22 11:41:25 -07:00
David Mann 9095272487 Integration: merge main (#6352) 2026-10-22 20:38:21 +02:00
aiongg ffa400c754 Keep tests correct and docs current with one test binary per crate
- 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.
2026-10-22 16:55:34 +00:00
aiongg a6428d3dfb Build each crate's integration tests as one binary
Every tests/*.rs file is its own executable that links the whole crate
graph. Move each crate's test files into tests/it/ as modules of one
tests/it/main.rs; helper modules under tests/common move with them.

These keep their own binary (own process):
- engine panic_hunt: a process-wide panic hook counts every panic.
- engine text_gamma: changes the process-wide text gamma.
- engine smart_filter_stages, mask_cache_purge: assert on global caches.
- codecs pnm_peak_memory: reads the process's peak RSS.
- photocraft mac_menu_appkit: harness = false.
2026-10-22 16:55:34 +00:00
aiongg f618cdea29 Integration: #8371 the .atn corpus round trip merges the per-property Sets that Save now writes 2026-10-22 15:39:49 +00:00
aiongg 2496742eaa Integration: merge main
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.
2026-10-22 15:39:28 +00:00
aiongg f72c3c2187 Integration: track t08
# Conflicts:
#	crates/ui-egui/src/camera_raw_ui.rs
#	crates/ui-egui/src/jobs_ui.rs
#	crates/ui-egui/src/widgets.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-22 15:38:23 +00:00
aiongg 446b74a8de Integration: track t07
# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	crates/ui-egui/src/layer_shortcut_tests.rs
2026-10-22 15:38:21 +00:00
aiongg 9c7cc91f82 Integration: track t05
# Conflicts:
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/shortcuts.rs
#	docs/ui-parity.md
2026-10-22 15:38:18 +00:00
aiongg 1f0177f23c Integration: track t04
# Conflicts:
#	ATTRIBUTION.md
#	contributors/people.toml
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/gaps.md
2026-10-22 15:38:17 +00:00
aiongg 4d1b922ddf Integration: track t02
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/file_cmds/tests.rs
#	crates/engine/src/print_cmds/tests.rs
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-22 15:38:14 +00:00
aiongg 9172ad7525 Merge PR #7215: Corrective Camera Raw - fix(import/camera-raw): preserve document identity across asynchronous opens and re-develop 2026-10-22 15:37:37 +00:00
jonathanwxh-cell 99dbe727ae Merge main preserving ACT and CSH preset imports
Resolve catalog and parity-document conflicts without dropping either side.
Preserve ACT swatch routing alongside CSH shapes and add an ACT extension regression.
2026-10-22 17:22:50 +08:00
zuujiscom afa4cc7964 test(panic_hunt): give the panic-counting self-test its own deadline
contained_panics_count_and_graceful_errors_do_not used the 4 s hang
budget meant for fuzzed commands. With RUST_BACKTRACE=1 the panic hook
prints a backtrace, and on loaded Windows runners the first symbol
lookup took long enough (19.6 s) that run_watched returned None instead
of Some(false). The self-test now waits up to 120 s; the fuzz keeps 4 s.
2026-10-22 00:53:45 -05:00
skylar taylor-barrick f4cff2780c test(retouch): name live dab replay regressions by behavior 2026-10-22 08:16:51 +05:30
skylar taylor-barrick 0635061b86 fix(retouch): preserve Smudge pickup through live stroke release 2026-10-22 08:16:51 +05:30
aiongg 02c9e32d1d Merge PR #8088: fix(type): resolve exact PostScript names in type.create 2026-10-22 02:26:06 +00:00
aiongg ff2ba37278 Merge PR #8353: Scale shape strokes when resizing for Export As
crates/ui-egui/src/export_dialog.rs: tests appended by both sides; kept both whole.
2026-10-22 02:23:59 +00:00
aiongg d8acd0579a Integration: #7976 bound the ';' search in xml_unescape to 13 bytes
A long run of '&' without a nearby ';' scanned to the end of the string for every '&' (quadratic). Adds a test with a 200k-ampersand description.
2026-10-22 02:23:17 +00:00
aiongg 9a9aab3bb3 Merge PR #7976: Decode numeric character references in File Info XMP text 2026-10-22 02:23:01 +00:00
aiongg 2d74af4c8e Merge PR #7721: fix(actions): refuse lossy fractional Crystallize ATN export
crates/engine/src/atn_cmds.rs: kept main's fields_from/fields_to doc comments and range-checked Kind::Long arm; one ExactLong definition (main's); Ripple Amnt stays ExactLong and Crystallize ClSz becomes ExactLong.
2026-10-22 02:22:59 +00:00