Commit Graph
335 Commits
Author SHA1 Message Date
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
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
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
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
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
Hector VillarrealandClaude Opus 5.5 cd39cdc75c fix(compose, gpu): adjustment layers apply Fill inside the special eight blend modes (#9501)
Below 100% Fill, Photoshop blends Color Burn, Linear Burn, Color Dodge,
Linear Dodge, Vivid Light, Linear Light, Hard Mix and Difference with
the source pulled toward the mode's neutral colour, at Opacity. Pixel
layers already did (psblend::composite_fill); adjustment layers used
Fill as coverage on the CPU (blend_back) and the GPU (AdjMix), so a
40% Fill Color Dodge gave (90,179,217) instead of (80,160,240).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:24:50 -06:00
Hector VillarrealandClaude Opus 5.5 d78d7493d3 fix: an edit at full coverage keeps its result over huge 32-bit values (#9483)
Adjustment layers (compose blend_back), destructive edits
(mix_through_selection) and plug-in filters mixed their result back as
o + (r - o) * k. In f32 the difference had already rounded a small
result away against a huge backdrop, so a Color Lookup's 0.1 over 2^20
came out 0.125, and 0.5 over 1e8 came out 0. They now compute
o * (1 - k) + r * k, which is exactly r at full coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:03:01 -06:00
Hector VillarrealandClaude Opus 5.5 02f8d3a5e4 fix(vector): a mask feather continues the mask past the canvas edge (#9475)
The feather blur clamped every one of its box passes at the plane's
edge. Where the canvas cut a feathered mask off, each pass repeated the
already blurred values there, brightening the edge (12 levels at a
32 px feather) and making the result depend on the canvas size.
The blur now pads the plane with its edge values by the passes' reach,
blurs, and keeps the original area, which matches Photoshop within one
level both for a mask black at the edge and one white up to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:36:43 -06:00
dotCooCoo 0bb3ec2ae9 Tests: the effect-map deadlock watchdog waits 120 s for each render, not for all 101
effect_maps_with_nested_rayon_work_do_not_deadlock rendered its document
101 times and failed when the whole run took longer than 120 s. On a
machine busy with other builds the renders slow down far enough to pass
that limit while every render still finishes, and the test then fails
with "effect-map rendering deadlocked (#276)".

The render thread now sends a message after each render, and the test
fails when 120 s pass without one. A render that never finishes still
fails the test after 120 s.
2026-10-24 20:42:38 -07:00
David MannandClaude Opus 5.5 0b3cab2eba CMYK: Dissolve and post-adjustment rounding match the RGB compositor (#9033)
The CMYK proptests failed on main now and then (the_split_route_agrees_with_the_exact_path
after #8663). Three gaps, each found by the proptests at 5000 cases:

- cmyk.rs: a clipped Dissolve layer blended like Normal; it now shows each pixel fully or not
  at all by the compositor's noise, as the RGB compositor does since #3052.
- cmyk.rs: a Dissolve adjustment layer mixed by its opacity; opacity and mask are now the chance
  a pixel takes the adjustment, as on the RGB compositor (#3050).
- native.rs: the display/Flatten ink route didn't round to the document's levels after an
  adjustment layer, where the RGB compositor and cmyk.rs do; coverage in a pass-through group
  then differed by up to half a level.

The test reference model had the same two Dissolve gaps (ref_clip passed no position, so
Dissolve fell back to Normal), which is why random_documents_match_the_reference never caught
the first one. Three named regression tests cover the minimal cases.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 03:34:07 +02:00
David Mann 23d8f053cb Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/shape_split.rs
2026-10-24 03:12:55 +02:00
David Mann feb20ebc60 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/gpu/src/plan.rs
#	crates/io/src/adjust_map.rs
2026-10-24 02:59:10 +02:00
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
FinnNkandClaude Opus 5.5 89d127d259 fix(curves): 32-bit Curves continue past 0..1 like Photoshop (#8782)
* fix(curves): 32-bit Curves continue past 0..1 like Photoshop

In a 32-bit RGB document an edited Curves clipped every sample to the
curve's table, so HDR values above 1 (and negative ones) were flattened.
Photoshop extends the curve beyond its end points along its end segments:
the straight curve [[0,0],[255,128]] maps -0.5, 0.5 and 2 to
-0.250980407, 0.250980407 and 1.003921628 (measured in Photoshop 27.11 on
the issue). The CPU compositor, Image > Adjustments and the GPU kernel
now do the same, with the end slopes measured over 16 table steps.
8/16-bit documents and unedited curves are unchanged.

Fixes #7157

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

* fix(curves): extend 32-bit curves in RGB documents only

The curve's space is not the document's: a grayscale, CMYK or Lab
32-bit document with an RGB-space curve keeps the table's ends. The
document mode now reaches the decision on the CPU (apply_in, the
compositor) and the GPU (adjustment_program).

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:40:56 -04:00
HEOJUNFOandClaude Opus 5.5 44db720b22 Black & White: keep 32-bit superwhites (#8537) (#8748)
The grey was clamped to 0..1 at every depth, so a 32-bit (2, 2, 2) came
out as (1, 1, 1). In 32-bit documents it now clamps to 0..16, as
Photoshop 27.11 does; 8 and 16-bit still clip at 1. CPU and GPU both.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 17:40:00 -04:00
Gert Pellin - GPE 10a20743ea Soft Light: dark backdrops follow Photoshop's W3C curve (#8804) (#8817)
* 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.
2026-10-23 17:39:40 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
Dylan M. Taylor 6ca5bf359d security(compose): bound dense working sets and nested Blend If evaluation (#8949)
* security(compose): bound dense working sets and repeated group evaluation

Smart-filter mixing and stroke fitting work tile by tile instead of over the
union of sparse surfaces; render_layer admits its rect like render; hidden
layers no longer disable the proxy and uniform-area shortcuts; Blend If
groups nested deeper than two are judged by their result, so nesting costs
polynomial instead of exponential work; effect builds past an absolute byte
cap are skipped.

* CodeRabbit #8949: exact tile skips in filter_mask, allocated-tile scan in fit_stroke, contrasting backdrop in the nested-group test

- filter_mask mix/unmix: bare tiles (no tiles on either side) skip for
  non-alpha formats too when the two surfaces' default pixels are equal;
  differing defaults still get processed, so the clone's default pixel is
  never left wrong under a non-unit mask
- shape_split fit_stroke: scan the union of the three surfaces' allocated
  tiles (exact skip when all three defaults are transparent) instead of
  every tile of a 300k-canvas document; non-transparent defaults fall
  back to the full scan
- tests: the nested-groups assertion now uses a black backdrop so the
  asserted white can only come from rendering through the group chain

fixes CodeRabbit review comments (major/minor) on filter_mask.rs:27,29,
shape_split.rs:126, tests.rs:2354

* compose: skip a BTreeSet in fit_stroke — a sorted Vec compiles under the wasm toolchain (E0599 on ::drain there)
2026-10-23 15:33:01 -04:00