Commit Graph
356 Commits
Author SHA1 Message Date
Khawar Zamman Wani 86ddfb72f7 Reuse large buffers so returning freed memory to the OS doesn't slow filters (#10479) (#10514)
* perf: reuse large buffers so a pinned glibc trim threshold costs no speed (#10479)

With MALLOC_TRIM_THRESHOLD_ set (#8743), glibc maps every allocation over
128 KiB fresh, so tiles, compositor bands and filter scratch were page-faulted
in on every call: viewport compose 3.6x slower, Gaussian Blur up to 1.8x,
.pcraft save 2.4x.

- photocraft_raster::scratch: a process-wide, capped pool of large Vec
  buffers (u8/u16/f32/[f32; 4]). A miss drops the oldest buffers worth its
  size down to a 256 MiB floor, so peak memory stays near glibc's defaults.
  The engine releases the pool after any command that closes a document,
  Headless::close does too, and an idle thread releases it after 2 s unused.
- Tiles, compose Buffer/masks/bands, algo Image and the blur paths, engine
  adjustment/filter scratch, GPU tile upload and io import buffers go through
  the pool.
- .pcraft save reuses one zstd FrameCompressor per worker (byte-identical).
- interactive_bench reports minor page faults per run.

36 MP, trim threshold set vs glibc defaults: viewport 5.6 vs 6.1 ms (was
22.7), Blur r4 445 vs 437 ms (was 852), Curves CPU proxy 35.0 vs 35.7 ms
(was 50), flatten 146 vs 147 ms (was 237), save .pcraft 251 vs 230 ms (was
545). RSS after closing a 5400x7200 document: 21 MB with trim set.

* fix: corpus tests build again; return remaining pooled buffers (#10479 review)

- Corpus-gated io tests and the Photoshop-oracle test moved `.px` out of a
  `Buffer`, which now implements `Drop` (E0509 with `--features corpus`):
  use `into_px()`.
- io: masks, the merged image, extra channels, smart-filter masks,
  smart-object pixels and patterns give their `interleave` buffers back to
  the scratch pool after writing them.
- compose: recycle the mask values of a pass-through group, `effect_shape`
  and both `split_parts` callers.
- Styled Free Transform preview copies each band out instead of moving it,
  so the band returns to the pool.
- interactive_bench: the fault count covers each whole run, untimed setup
  included; say so.

* fix: recycle the masks of adjustment-atop and shape layers (#10479 review)

* fix: recycle effect hiding masks (#10479 review)

* fix: recycle knockout, CMYK and GPU shape masks; release the pool when Split/Merge Channels close a document (#10479 review)

* fix: release the pool when a closed document's cancelled workers exit; idle-release switch for tests; don't pool PSD planes (#10479 review)

* fix: release the pool for workers cancelled before their document closes; take the pool lock for budget and idle-release changes (#10479 review)

* fix: idle release rechecks activity under the lock; try_take_exact releases the pool and retries before failing (#10479 review)

* test: cover a successful try_take_exact miss; name the failed-reservation test for what it checks (#10479 review)

* fix: pool the final GPU effect shape buffer and return it after use (#10479 review)

* docs: describe scratch pool lifecycle regression invariants

* test: verify polling releases cancelled document scratch buffers

* perf: recycle remaining transform selection and PSD scratch buffers

* fix: free PSD gather bands before allocating channel planes

* test: assert exact scratch take reuses pooled storage
2026-11-02 09:29:59 +01:00
David MannandClaude Opus 5.5 b9613c6caa Compose: Softer outer glows end before Size again, no tail past it
Reverts #8039 ("Compose: Extend Softer outer glows past Size (#7899)"),
which added a range-scaled fading tail beyond Size to Softer outer glows,
on the CPU compositor and the GPU one alike, and widened the effect
margin by it.

The reference application, measured: Outer Glow, Softer, Size 60,
Range 50 % (and 100 % alike), white, Normal, Opacity 100 %, Spread 0,
Noise 0, on an opaque white shape over black (240 x 40 document, shape
over the left 100 px, full height, sampled at row 20): the glow reads 4
at 50 px from the edge, 1 at 54, and 0 from 56 to 70 px. It reaches zero
just before Size; there is no tail beyond it.

The reverted code (the two-box tent) gives, in that setup, 8-bit levels
4, 1, 0, 0, 0 at 50/54/56/60/70 px for Range 50 % (unrounded 3.56,
1.20, 0.49, 0, 0) and 2, 1, 0, 0, 0 for Range 100 % (1.78, 0.60, 0.25,
0, 0); #8039's tail lifted 56-62 px above zero. So the revert stays as
is, CPU and GPU unchanged from before #8039.

Conflicts were formatting-only (later rustfmt of #8039's lines) in
compose effects.rs, effects/paint.rs and gpu fx/builders.rs; resolved to
the pre-#8039 code. The GlowSource/GlowTechnique imports a later commit
kept in gpu fx/tests.rs for #8039's test go with the test.

New test softer_outer_glow_fades_out_before_its_size renders that
document through the CPU compositor at Range 50 % and 100 % and checks
50 and 54 px within 2 levels and 56, 60 and 70 px at exactly 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:34:03 +01:00
FredApps 0f120bbda1 Merge current main, reconcile streamed PNG profiles and workflow inputs 2026-10-30 06:52:05 +01:00
FredApps c93c0bb36d Merge main into trimmed integration; reconcile blur, pattern and Liquify behavior 2026-10-29 23:47:53 +01:00
FredApps 29bf828ec2 Merge upstream main into reviewed performance integration
Preserve both UI parity status entries; retain all reviewed performance changes.
2026-10-29 23:35:19 +01:00
FredApps ea024174b2 Remove stale crop reset hook and apply CI formatting corrections 2026-10-29 18:59:43 +01:00
FredApps 255ecc81d0 Remove 21 requested PR contributions from the 108-PR integration
Retain 87 PRs. Reconcile overlapping integration fixes and update testing scope.
Removed: #7153 #7742 #5493 #9282 #3455 #9172 #3139 #3967 #4783 #5453 #8347 #1991 #3345 #4042 #4305 #4883 #4890 #5090 #6621 #5870 #5939.

Combined CI pending for this source; historical portable artifact is not applicable.
2026-10-29 18:49:17 +01:00
FredApps 53dbfe280c Integrate 16 reviewed performance and usability PRs
Includes #8472 #8488 #9461 #9793 #9809 #9813 #9814 #9822 #9826 #9847 #9850 #9852 #9921 #9924 #9961 #10080 at reviewed heads.

Preserves each PR head as a parent. Reconciles CI workflow inputs and benchmark jobs without dropping checks. Excludes #8536 #9825 #9969 #10129. Combined CI pending; no manual validation claimed.
2026-10-29 14:31:15 +01:00
FredApps 4f23f459ba Merge approved PhotoCraft PR #3464: fix(vector): target selected saved paths and own Pen gestures
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Reconcile captured Pen and Freeform targets with current resumed-subpath, Type Path, Path Operation and draft recovery behavior. Valid source-owned tab switches commit to captured original target; invalid snapshots cancel with a diagnostic; refused commits retain draft and suppress automatic retries. Preserve current Saved(index) API and hidden-work-path protections, register all new ownership/gesture tests and add original-target tab-switch/deleted-target cases. Includes verified compile/API corrections from prior checkpoints.
2026-10-29 06:33:02 +01:00
FredApps b63c6e00a6 Merge approved PhotoCraft PR #4711: Treat Dissolve as a per-pixel choice
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Current modular compositors already implement spatial Dissolve for adjustments and finished effects. Preserve equivalent implementations and newer Fill/HDR behavior, append approved missing CPU/GPU regression coverage.
2026-10-29 06:11:42 +01:00
FredApps 1c6acdf90f Merge approved PhotoCraft PR #4763: refactor(liquify): reuse native brush resizing and anchored HUD feedback
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Use shared zoom-corrected brush resize and HUD while preserving immediate press ownership and anchored outline when hover is absent. Keep original resize regressions. Fix duplicate identical Dissolve test registration and missing test-only UnicodeString import reported by 94-head check.
2026-10-29 06:11:12 +01:00
FredApps 2ab2348adf Merge approved PhotoCraft PR #4992: Preserve Dissolve coverage when compositing clipped layers
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Current CPU and GPU already implement binary clipped Dissolve at document coordinates. Preserve current equivalent implementation and stronger negative-origin/depth regression; retain original named regression and added GPU parity coverage.
2026-10-29 06:08:48 +01:00
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 78f9c137b3 Merge approved PhotoCraft PR #6621: Crop: right-click on the pending crop opens the crop menu (#6561)
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Extended current crop menu with Reset and shield toggles, preserving newer preset management, overlays, ownership checks and no-menu-during-gesture logic. Ported reset-frame no-op/undo semantics into current CropState/release and crop commit; retained original regression cases in split modules.
2026-10-29 05:29:44 +01:00
FredApps f369874ddb Merge approved PhotoCraft PR #8039: Compose: Extend Softer outer glows past Size (#7899)
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Added bounded Softer outer-glow tail in current CPU paint and GPU glow-builder modules, retaining original tent convolution and current program dispatch; registered exact tail/kernel regression and expanded render margin only for outer Softer glows.
2026-10-29 05:24:44 +01:00
FredApps 6c225b13c3 Merge approved PhotoCraft PR #5010: Layer Style: Photoshop's layout (three columns, OK/Cancel/New Style/Preview on the right, one-line rows) [depends on #4938]
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Port responsive three-column Layer Style with owned right-side actions, preserve saved defaults/Blend If and current Curves/modal routing, and keep all original layout/Cancel tests registered.
2026-10-29 04:36:06 +01:00
FredApps 31ce2208b8 Merge approved PhotoCraft PR #9589: fix(compose, gpu): an adjustment layer's This Layer range judges its result before Opacity
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Judge Blend If using the adjustment result before Opacity and masks in CPU/GPU, with the original regression in the current test module.
2026-10-29 03:55:55 +01:00
FredApps 29804c5221 Merge approved PhotoCraft PR #9512: fix(compose, gpu): adjustment layers apply Fill inside the special eight blend modes
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Combine Fill-aware adjustment blending with stable HDR coverage and add cross-feature CPU/GPU regression matrix.
2026-10-29 03:54:31 +01:00
FredApps 369abd15c9 Merge approved PhotoCraft PR #9595: fix(compose, gpu): a Pass Through group with Blend If renders isolated
Port unchanged PR hunks to their unique current module locations after upstream file splits.
2026-10-29 03:37:02 +01:00
FredApps 23a0d5b662 Merge approved PhotoCraft PR #9584: fix(compose, gpu): Blend If hard points match Photoshop at 16 bits and for Gray
Port unchanged PR hunks to their unique current module locations after upstream file splits.
2026-10-29 03:36:59 +01:00
FredApps 537f206dc3 Merge approved PhotoCraft PR #9509: fix: an edit at full coverage keeps its result over huge 32-bit values
Port unchanged PR hunks to their unique current module locations after upstream file splits.
2026-10-29 03:36:56 +01:00
FredApps 87bb366fbe Merge approved PhotoCraft PR #9493: fix(vector): a mask feather continues the mask past the canvas edge
Port unchanged PR hunks to their unique current module locations after upstream file splits.
2026-10-29 03:36:55 +01:00
FredApps cfc24243aa Merge approved PhotoCraft PR #8903: Box-filter minified patterns in the CPU and GPU samplers (#8831)
Port unchanged PR hunks to their unique current module locations after upstream file splits.
2026-10-29 03:36:41 +01:00
Federico Olivares 5a6270921c Merge main into the integration batch
Main now carries #9031 (Halftone) and #8923; this branch keeps only what it adds on top: the guard for a setting beside a Gallery stack's effect list, its test, and the PSD open warning for patterns that paint nothing.
2026-10-28 11:17:59 -03:00
Dylan M. Taylor f0ffcef216 Refactor compose for strict Clippy checks 2026-10-27 23:47:06 +00:00
aiongg 55effab1aa Merge #9689: CMYK: the test reference keeps a fully covered type pixel's ink exact (#9688) 2026-10-27 17:44:31 +00:00
aiongg b57b64207e Merge #9340: Tests: the effect-map deadlock watchdog waits 120 s for each render, not for all 101 2026-10-27 17:44:13 +00:00
aiongg 6e9fb30dbc Merge #6723: Color Lookup: PSD save keeps a .3dl/.look LUT file as loaded (#6544) 2026-10-27 17:44:05 +00:00
aiongg 01594f1f25 Merge #5890: PSD: Frame layers keep their stroke (#4381) 2026-10-27 17:44:01 +00:00
Federico Olivares e173a9be17 Merge main into the integration batch
Main reorganised the source tree (command modules under engine/src/cmd, tools and dialogs under ui-egui/src, large files split into modules, inline test modules moved to their own files). This branch's changes to the moved and split files are ported to their new locations.
2026-10-27 10:44:16 -03:00
David Mann 3e15ea9eb9 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/adjust.rs
#	crates/gpu/src/plan.rs
#	crates/io/src/adjust_map.rs
2026-10-27 14:19:34 +01:00
David Mann 2363c475c0 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/lib.rs
#	crates/gpu/src/plan.rs
2026-10-27 14:17:25 +01:00
Khawar Zamman Wani 77ddfaca64 perf(curves): sample graph directly and cache channel polylines 2026-10-27 13:59:41 +05:30
David MannandClaude Opus 5.5 c81e15cd24 Merge main into fix/5692-tsl-off-blend
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 03:07:51 +01:00
David MannandClaude Opus 5.5 90658663c7 Merge main into fix/cmyk-clip-vivid-light-group
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:41:36 +01:00
aiongg 273de6ace0 refactor(compose): split adjust.rs into adjust/ 2026-10-26 22:54:55 +00:00
aiongg 32588fb0ac refactor(compose): split effects.rs into effects/ 2026-10-26 22:54:55 +00:00
aiongg 9bb2423533 refactor(compose): split lib.rs into composite, content, ctx, extents, fx_cache, reduced, render 2026-10-26 22:54:55 +00:00
aiongg 7e52b713dd refactor(compose): move cmyk_tests.rs to cmyk/tests.rs 2026-10-26 22:30:14 +00:00
aiongg 6f35658164 refactor(compose): move masks.rs tests to masks/tests.rs 2026-10-26 22:30:13 +00:00
aiongg 120a3b5291 refactor(compose): move adjust.rs tone_tests to adjust/tone_tests.rs 2026-10-26 22:30:13 +00:00
aiongg 805d8b77b8 refactor(compose): move effects.rs tests to effects/tests.rs 2026-10-26 22:30:13 +00:00
David MannandClaude Opus 5.5 4531fabc75 CMYK: the test reference keeps a fully covered type pixel's ink exact (#9688)
The per-pixel reference mixed a type layer's fully covering pixel through
the type gamma round trip, which leaves white one float step short of no
ink (1 - 0.99999994). Vivid Light jumps exactly there (a source at full
brightness gives full brightness, anything below over no brightness gives
none), so a white glyph clipped onto a Vivid Light group over magenta
became full magenta in the reference, while the compositor (which keeps
covered pixels exact, like normal_gamma) gave no ink. The reference now
treats a fully covering source like the compositor and the identity it
is: the source's ink exactly.

Adds a named regression test with the shrunk document and a neighbour
test over Vivid/Linear/Pin Light, Hard Mix, Color Dodge/Burn, Normal,
Multiply and Screen with type or pixels clipped, plus the proptest seed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 15:45:02 +01:00
David Mann 7e4ceba5df Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/cms/src/lutfile.rs
2026-10-26 14:26:50 +01:00
Hector VillarrealandClaude Opus 5.5 75fa71a4f5 fix(compose, gpu): a Pass Through group with Blend If renders isolated (#9554)
A Pass Through group composites its children straight into the
backdrop, so an Invert inside it changed the layer beneath even when
the group had Blend If. Photoshop renders such a group isolated, as it
does below 100% Fill: the backdrop stays (64,128,192) under a group
holding an Invert with Red Underlying white at 110. The CPU and GPU
pass-through shortcuts are now skipped for a group with Blend If.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:20:48 -06:00
Hector VillarrealandClaude Opus 5.5 da15794507 fix(compose, gpu): an adjustment layer's This Layer range judges its result before Opacity (#9542)
Blend If on an adjustment layer judged This Layer by the composite after
Opacity and the mask had mixed the result into the backdrop. Photoshop
judges the adjustment's result itself: Invert at 40% over (64,128,192)
with Red This Layer black 150 keeps the effect, (115,128,140), as
Invert's red 191 passes 150. The CPU and the GPU now apply the
adjustment to the backdrop and judge that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:48:06 -06:00
Hector VillarrealandClaude Opus 5.5 fc0f7b1132 fix(compose, gpu): Blend If Gray uses Photoshop's weights and rounding (#9522)
Gray was Rec. 601 luma, unrounded, so mixed 8-bit colours near a hard
Gray point landed on the wrong side: (123,126,151) read 127.95 and was
hidden by a black point at 128. Photoshop's Gray, measured on pure
channels at three cutoffs, is (30R + 59G + 11B) / 100 rounded half down
on whole levels; it keeps both of the reporter's colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:20:50 -06:00
Federico Olivares 9f588ad085 Merge main into the integration batch 2026-10-25 20:42:06 -03:00
Dylan M. Taylor 3877bb1dbe chore: resolve SonarCloud maintainability issues (#9044)
* chore: resolve SonarCloud maintainability issues

* fix: format imports and fix effect_entry argument type

* fix: restore test closure and wildcard import that broke compilation

* refactor: split composite_to_surface to reduce cognitive complexity
2026-10-25 19:38:31 -04:00
Hector VillarrealandClaude Opus 5.5 d6a1f7cbc5 fix(compose, gpu): Blend If compares 16-bit values on Photoshop's 0..32768 steps (#9521)
A hard Blend If point at level c sits on Photoshop's 16-bit step
round(c * 32768 / 255), inclusive for black and white points (measured
at 1, 64, 128 and 200). blend_if_level snapped only within 1e-4 of a
whole level, so RGB16 32895 (level 128's step) read 127.996 and was
hidden by a black point at 128. A value on its nearest level's step is
now that level, on the CPU and the GPU; 8-bit levels are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:39:24 -06:00