207 Commits
Author SHA1 Message Date
Tanish Manku 4313eb2dbe Merge pull request #9319 from DuBearte/feat/8351-gradient-map-source
feat: Gradient Map keeps Photoshop's stops, midpoints and smoothness (#8351)
2026-11-11 00:03:04 -07:00
Tanish Manku 822ebbe252 Merge pull request #8732 from dotCooCoo/fix/gpu-timing-test-holds-on-every-gpu
Tests: the batched GPU timing test checks one span per command buffer
2026-11-11 00:00:28 -07:00
Tanish Manku 254000acaa Merge pull request #7020 from fjamesprice/perf/camera-raw-gpu-texture-dehaze
perf(camera-raw): develop Dehaze, Clarity and Texture on the GPU in the preview (#6916)
2026-11-10 23:58:06 -07:00
Tanish Manku 1d6cd334ba Merge pull request #6951 from fjamesprice/perf/camera-raw-gpu-local-tone
perf(camera-raw): develop Shadows and Highlights on the GPU in the preview (#6916)
2026-11-10 23:57:54 -07:00
Tanish Manku aaf11d9dd1 Merge pull request #6735 from vangel76/fix/6513-stroke-knockout
Compose: Normal inside/center stroke replaces the layer beneath it (#6513)
2026-11-10 23:56:46 -07:00
Tanish Manku 952be70074 Merge pull request #8734 from ebosido444-ai/fix/fxc-blend-channel
fix(gpu): return once at the end of blend_channel so FXC can build the compositor
2026-11-10 23:55:35 -07:00
fjamespriceandClaude Opus 5.5 fa5ca22757 test(camera-raw): validate the Dehaze, Clarity and Texture shader without a GPU
The adapter-free shader test now validates camera_raw_filters.wgsl as
the pass compiles it, appended to camera_raw_tone.wgsl, so a runner
without a GPU still catches a filters shader that wouldn't compile.
Appending an undefined identifier to camera_raw_filters.wgsl fails it.

Part of #6916.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014nuVUg57HqXLARdhymFiTc
2026-11-09 02:42:52 -05:00
fjamespriceandClaude Opus 5.5 b02d952e76 perf(camera-raw): the GPU Dehaze opens the dark channel like the CPU (#9273)
main's #9316 opens Dehaze's dark channel (a maximum filter of the same
radius after the minimum) and scales the 0.95 weight by the ratio of the
two means, so a sky beside a dark object clears instead of keeping a
bright halo. The GPU pass built the transmission from the minimum alone,
so its preview kept the halo that OK no longer applied.

The pass now does the same: fs_min gains a maximum mode for the opening,
fs_air sums the dark channel and its opening per tile and then turns the
sums into the weight, and the packing pass reads the weight from that
texel instead of a constant.

The CPU mean is now summed in f64. Its f32 running sum over the copy's
few hundred thousand cells drifted by about 1e-5, which the low
transmission beside haze multiplied past the GPU tests' 1/4096 (2.7e-4 in
clarity_texture_and_dehaze_with_shadows_highlights_and_per_pixel_stages);
with f64 the GPU matches to 1.6e-5 on the halo scene.

New test dehaze_opens_the_dark_channel_like_the_cpu develops #9316's
scene (a clear sky and a dark tower) both ways at three strengths. Dehaze
+40 on the 858x572 proxy: 5.1-7.7 ms on the GPU vs 17.7 ms on the CPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:42:52 -05:00
fjamespriceandClaude Opus 5.5 6025c504cf perf(camera-raw): develop Dehaze, Clarity and Texture on the GPU in the preview (#6916)
Step 3 of #6916. Dehaze, Clarity and Texture run as fragment passes
(box, minimum and guided filters) around step 2's Shadows and
Highlights, so every setting but Detail develops on the GPU with one
readback.

- algo: camera_raw::spatial resolves Dehaze, Clarity and Texture per
  frame; develop builds them the same way (130 output hashes unchanged,
  same speed). PerPixel gains the three stages, per_pixel() refuses only
  Detail, and Whites/Blacks are measured after any spatial stage.
- gpu: camera_raw_filters.wgsl; the tone frame's pyramid is made on
  demand; plans record four segments around the main shader's passes.
  Dehaze's light uses two exact selections (cutoff value, then cutoff
  index among its ties). Its <=768 px copy comes from the CPU
  (Dehaze::cells, develop's own code), cached per source and white
  balance: on WARP the GPU's copy split CPU ties at the cutoff and moved
  the light by 5.7e-3.
- GPU vs CPU at most 7.6e-5 (Vulkan, DX12, WARP), tolerance 1/4096.
  858x572: Dehaze +40 6.0 ms vs 29 ms, Clarity +50 3.5 vs 16 ms,
  Texture +50 3.4 vs 10 ms, everything 19 ms vs 650-690 ms (Vulkan).

Part of #6916.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:42:52 -05:00
fjamespriceandClaude Opus 5.5 f0f7b13c3d test(camera-raw): validate the Shadows and Highlights shader without a GPU
The Camera Raw GPU tests skip on runners with no adapter, and the one
test that runs there validated only the per-pixel shader. It now
parses and validates camera_raw_tone.wgsl with naga too, so a runner
without a GPU still catches a shader that wouldn't compile. Appending
an undefined identifier to camera_raw_tone.wgsl fails it.

Part of #6916.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014nuVUg57HqXLARdhymFiTc
2026-11-09 02:41:55 -05:00
fjamespriceandClaude Opus 5.5 e13e8e8afd fix(camera-raw): Shadows and Highlights on a 2 px proxy develop on the CPU
per_pixel sent Shadows and Highlights to the GPU on frames 2 pixels on
the short side, whose pyramid has one level. The pass refused them, and
CameraRawGpu::develop took that as a GPU failure, so the dialog stayed
on the CPU for every later preview. A layer about 450 times wider than
tall has such a proxy. per_pixel now returns None for these frames, so
the CPU develops them and the GPU stays on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:41:55 -05:00
fjamespriceandClaude Opus 5.5 a60090fff5 perf(camera-raw): develop Shadows and Highlights on the GPU in the preview (#6916)
Step 2 of #6916: the local Laplacian filter behind Shadows and Highlights
(Paris, Hasinoff, Kautz 2011, sampled as in Aubry et al. 2014, as
local_tone.rs does) as wgpu fragment passes, chained in front of step 1's
per-pixel pass with no readback in between.

- algo: local_tone exposes its constants and resolves the sliders into
  LocalTone / TonePass (the remapping is a method); the CPU filter runs
  those passes. PerPixel gains local_tone and measure_whites_blacks;
  whites_blacks_model() exposes the Whites and Blacks tables;
  quantile_rank() is the one rank rule. CPU output unchanged bit for bit
  (86 hashes, 8-bit and float, 171x113 to 2100x1300) and as fast.
- gpu: camera_raw_tone.rs/.wgsl. Pyramid levels are R32Float textures; the
  remapped pyramids of up to 96 references sit in one Rgba32Float atlas per
  level, four per tile. The filter's quantiles, and Whites and Blacks'
  points after it, are exact order statistics on the GPU (16 thresholds per
  round, 8 rounds); a pass renders the Whites and Blacks rows into the
  table texture. Textures, passes and bind groups are planned once per
  frame size. Pipelines build with the rest on the existing thread.
- ui-egui: the proxy worker sends Shadows and Highlights frames to the GPU;
  Dehaze, Texture, Clarity and Detail still use the CPU for the frame.

GPU vs CPU at 8, 16 and 32 bits: at most 9.9e-6 (RTX 5060 Ti, Vulkan and
DX12), 1.03e-5 (WARP); tests keep 1/4096. 858x572 proxy, release: Shadows
+50 8.7 ms (Vulkan) / 10.6 ms (DX12) vs 147-300 ms on the CPU.

Part of #6916.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 02:41:55 -05:00
DuBearteandClaude Opus 5.5 72df150914 feat: Gradient Map keeps Photoshop's stops, midpoints and smoothness (#8351)
A Gradient Map read from a PSD keeps Photoshop's own gradient (its
stops, midpoints, Smoothness and interpolation method) next to the
dense stops baked from it for rendering. A save writes that gradient
back, so Photoshop opens the same editable gradient; the editor shows
and edits Photoshop's stops and notes the Smoothness and method, and the
params carry it as "source". Plain stops, a preset or "source": null
replace it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 00:18:57 -05:00
DuBearteandClaude Opus 5.5 f190d1ba28 feat(compose): Stroke Emboss bevels the stroked outline (#8639)
Stroke Emboss was drawn as an Inner Bevel of the layer, shading the
layer's own edge. It now bevels the outline of the layer with its top
Stroke effect and shades only the stroke, as Photoshop does; without a
stroke it shows nothing. The GPU plan hands a layer with Stroke Emboss
to the CPU compositor, which builds the stroke's band.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 23:39:34 -05:00
Khawar Zamman WaniandFredApps 1089932ae9 Add a headless GPU backend for composition and flat export (#10842)
* Add headless GPU backend foundation for composition and flat export (issue #8062 PR 1)

* Fix headless GPU readback bounds, API compliance, and error preservation (#8062)

* Fix GPU readback bounds, context thread spawn error, and test parity (#8062)

* Fix BandRenderer Send bound, PNG reborrows, and clippy lints (#8062)

* Fix effect shadow test construction and remove dead code wrappers (#8062)

* Use short reborrows for Headless renderer without lifetime escaping (#8062)

* Fix clippy collapsible_if and ensure multi-band geometry in flat export tests (#8062)

* Fix automation Rect import, add renderer diagnostics, and harden flat export retries (#8062)

* Fix GPU feature test APIs and native target gates

* Verify unsupported GPU effects before testing band seams

* Use compositor buffer ownership API after upstream memory accounting change

* Update export renderer test helper for owned compositor buffers

* Fix headless GPU backend review findings (#10842)

- release a closed document's GPU caches through a BandRenderer hook
- bound the monitored readback wait and report a stalled GPU as device loss
- reject 32-bit float documents before band readback so exports fall back to CPU
- keep previews at 8-bit RGBA and report the palette/Duotone renderer bypass
- retain renderer diagnostics when encoding fails
- strengthen renderer, save-path and GPU diagnostics tests

* Add GPU close-hook and 8-bit preview regression tests (#10842)

* fix(io): correct renderer binding mutability

* Fix remaining headless GPU review findings (#10842)

- release a document's GPU caches when file.close, file.closeAll or
  file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
  requested renderer
- keep fallback warnings in their export instead of leaking them into the
  next one
- requeue renderer diagnostics when a renderer-aware save's final write
  fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
  depth list

* Use question mark for cancellable readback errors

* Keep GPU export diagnostics with their attempt and stop bands on cancel

Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.

Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.

Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.

Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.

* Keep headless export warnings when a workspace write fails (#10842)

A capability-scoped save encodes the export, then writes it; when the write
fails, the export warnings (folded together with renderer diagnostics) were
pushed back through `BandRenderer::push_diagnostic`, whose default is a no-op.
A custom renderer that overrides only `drain_diagnostics` therefore lost the
adapter or fallback reason that belonged with the error.

Keep them in `Headless` instead and return them from the next
`drain_diagnostics`, alongside the renderer's own queue. The regression uses a
renderer without a `push_diagnostic` override and checks the warnings survive
the failed write and drain exactly once.

Also name the actual source of the depth limit in the headless GPU matrix:
layer content is uploaded as half-precision textures, not band readback.

* Stop CPU bands and CPU retries on cancellation (#10912)

The CPU retry after a retryable GPU error composes through
`FlatSource::bands`, whose shared `emit` sink did not poll the export
interrupt: a cancel was only observed after the whole composite finished.
Check it once per band, as `CpuRenderer` does, for both RGB and CMYK ink
bands. The flat and streamed-PNG retry branches also check before starting
the full CPU composite again, so a cancelled export returns
`IoError::Cancelled` instead of re-rendering the document.

Tests: band-sink cancellation for RGB and CMYK inks, a renderer call counter
proving a second export reaches the renderer, and a pending `wait_slices`
poll observing cancellation set mid-wait.

* Merge origin/main into GPU foundation (#10842)

* Keep GPU-path diagnostics on every failure path, honour cancel in native copy

- save: export warnings of a failed local write stay in Headless pending diagnostics
- render: preview fallback warnings are returned, drained, and added to the doc.render reply
- io: renderer-neutral fallback wording; native single-layer copy checks cancellation per band
- gpu: gate wait_slices tests off wasm32 (#10842)

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 04:53:25 +01:00
David MannandClaude Opus 5.5 f05bb0ea9b Fix-up for #9979, #9182 and #10837: review findings on the batch
- #9979: ui.dialog.set can no longer rewrite an open field picker's __colorPicker
  marker (its OK went to the Crop shield instead of the field), and Preferences Apply
  waits for an open colour picker as OK does, so it never saves a previewed colour.
  Both have regression tests that fail without the fix.
- #9182: the stress test's PC_BUILD_ROUNDS / PC_BUILDS_PER_DEVICE fall back to the
  default for 0, which ran no builds and passed.
- #10837: the ROADMAP entry no longer says a Latin hint rules windows-1251 out; the
  hint settles a word-initial rare letter and breaks exact ties.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 13:22:51 +01:00
dotCooCoo f83f585d0a Merge main into fix/gpu-timing-test-holds-on-every-gpu 2026-11-02 14:40:32 -08:00
Finn NewickandClaude Opus 5.5 6a39ccd52e fix(gpu): build compositor pipelines without sharing FXC shader blobs across threads (#9173)
wgpu-hal's D3D12 backend caches each compiled shader and hands every pipeline a clone of the
cached ID3DBlob, calling AddRef under the cache lock and Release outside it
(gfx-rs/wgpu#10547). Compositor::build creates its pipelines in parallel, all from one vertex
entry point and each effect-map kernel twice from one fragment shader, so on D3D12 + FXC the
builds raced on shared blobs and dropping the device could abort (0xc0000409 on CI's WARP runs).

The build stays parallel, but no shader is shared between threads: the vertex stage moves to a
generated module with one entry point per build job; an effect-map kernel builds both formats
in one job; and concurrent Compositor::build calls take turns behind a process-wide lock.

Fixes #9173.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 08:51:02 +00:00
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
FredApps 722a9bd5cf Merge current main and remove app-local integration workflow additions 2026-10-30 12:22:42 +01:00
FredApps a4ab4873e8 Merge current main, retain full-sweep and new benchmark inputs 2026-10-30 03:05:00 +01:00
FredApps 4c000a2683 style: match rustfmt for GPU timing closure 2026-10-30 01:54:14 +01:00
FredApps db6e27ac02 Partition GPU theme cases and profile remaining parity bottlenecks 2026-10-30 01:42:18 +01:00
FredApps c8c8fc7821 Reduce repeated GPU setup and UI work; partition remaining regression matrices 2026-10-30 00:39:19 +01:00
FredApps c93c0bb36d Merge main into trimmed integration; reconcile blur, pattern and Liquify behavior 2026-10-29 23:47:53 +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 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 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 d98e419542 Merge approved PhotoCraft PR #5176: Play CopyCat, AJ and Line and Color action sets; keep patterns across restarts; Shift Edge scale
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported mappings and smart Save into current modules, preserving bounded authorized playback; migrated legacy PAT by committing validated per-pattern assets before atomic authoritative index; preserved same-ID replacements and builtin edits; added temporary-store interruption, corruption, duplicate ID/name and restart regression coverage. Current all-target compilation passed before final ownership guard refinement; focused tests pending.
2026-10-29 05:15:28 +01:00
FredApps e0bca6baf6 Use explicit float mask format in combined Fill parity regression 2026-10-29 05:02:27 +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 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
FredApps e4e75d0e2b Merge approved PhotoCraft PR #9110: test: GPU tests honour WGPU_BACKEND, so they run on Windows with an Intel GPU (#4) 2026-10-29 03:29:47 +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
vangel76andClaude Sonnet 5.5 5cccd2fcf7 Merge upstream/main into fix/6513-stroke-knockout
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 20:05:32 +01: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 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
Federico Olivares 8aaa03e2db Merge main into the integration batch 2026-10-26 11:52:45 -03: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
bf3ff9ce60 Integration: Camera Raw GPU tests fail on a shader that doesn't build (#6923)
The tests' setup returned None, without a message, when the device
request or the pipeline build failed, so every test passed early: with
a type error appended to camera_raw.wgsl all 13 tests still passed on a
machine with a GPU. A pipeline that doesn't build on a supported adapter
now fails the tests, a missing device skips with a message (and fails
with WGPU_BACKEND set, as in parity.rs), and a naga validation of the
shader fails on runners without a GPU too.

Found by CodeRabbit on #9386.

Co-authored-by: p0rps <134095787+fjamesprice@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 21:56:01 +01:00
Hector VillarrealandClaude Opus 5.5 6c55561128 GPU: a clipped adjustment takes Fill as coverage, as on the CPU
CodeRabbit on #9512 (inline 4371923258): the planner's clipped path
(atop_any) took the special eight's separate Fill too, while compose's
clipped branch (composite_atop) uses opacity × Fill as coverage, so a
clipped adjustment at partial Fill rendered differently on the GPU (53
levels off at 30% Fill). The clipped path now uses combined coverage.

Test: special_eight_fill_on_adjustment_layers_matches_cpu (adds a clipped
adjustment for each mode and Fill)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:48:03 -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