Commit Graph
18 Commits
Author SHA1 Message Date
David Mann d089eaa81b Merge PR #7693: perf(compose): Reuse bounded scratch and tone tables 2026-10-20 22:39:29 +02:00
Rene Leonhardt a686a932c0 perf(compose): reuse bounded scratch and tone tables
Reuse tile scratch and tone tables across rendering work. Skip fully masked
adjustments and identity float curves. Previous-path tests preserve output
across sample depths, masks and clipping.
2026-10-20 15:52:44 +02:00
Akos Arinton dfeba9e974 test(compose): compare NaN as NaN in the vector span test, not by its bits 2026-10-19 18:11:13 +03:00
Akos Arinton 5418399dda perf(compose): blend pixel layers row by row, Normal in 8-pixel vector blocks
A pixel layer with nothing clipped to it is read from its tiles a row at a
time and blended straight onto the backdrop, instead of being copied into a
tile-sized buffer first (a megabyte per layer per 256² render tile). Normal
RGB rows go through a branch-free kernel over blocks of 8 pixels that the
compiler vectorizes; few-band renders also run their bands in parallel.

Bit for bit the same: the row kernel is tested against the scalar loop (NaN,
infinities, subnormals, signed zeros), the row path against the tile copy at
8/16/32-bit with every kind of mode, fill and mask, and the whole GPU parity
suite's CPU references (1620 renders) are unchanged.

P60 on the CPU compositor: 627 -> 530 ms (the render without effects
300 -> 199 ms).
2026-10-18 22:26:05 +03:00
Streetblock e156a9ed85 perf(compose): select Normal RGB kernel once per buffer (#3715) 2026-10-12 07:12:34 -04:00
MrCloudy2andClaude Opus 5.5 4760f75b4d Blend modes: Fill below 100% works inside the blend for Photoshop's special eight (#2552)
* Blend modes: Fill below 100% works inside the blend for Photoshop's special eight

In Photoshop, Fill and Opacity are the same for most blend modes, but not
for eight: Color Burn, Linear Burn, Color Dodge, Linear Dodge (Add),
Vivid Light, Linear Light, Hard Mix and Difference. PhotoCraft treated
Fill as Opacity for all of them, so a Difference layer at 50% Fill gave
(150,90,110) where Photoshop gives (150,60,10).

Measured on Photoshop 27.11 (scripted, 24 cases at Fill 25/50/75% with
two colour pairs): below 100% Fill, Photoshop pulls the layer's colour
toward the mode's neutral colour (black for Color Dodge, Linear Dodge,
Difference; white for Color Burn, Linear Burn; 50% gray for Vivid and
Linear Light) and blends that at full strength; Hard Mix becomes
clamp((b - f(1 - t)) / (1 - f)). Opacity still mixes the result in.
That reproduces 31 of 32 values within 1 level (Hard Mix at 75%: 3).

CPU (psblend::composite_fill, the final blend of plain pixel, shape and
type layers) and GPU (Kernel::Blend takes Fill in the spare uniform slot)
agree; Dissolve keeps Fill as coverage. Layers with effects, adjustment
layers and groups keep the old behaviour for now.

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

* GPU: composite_fill with a single blend_rgb call, so fs_blend stays small

The first version added a third inlined copy of blend_rgb (every blend
mode) to fs_blend: composite_g's two plus blend_rgb_fill's. Windows CI
(software WARP adapter) then timed out rendering the UI in
arbitrary_rotation_preview, which passes on main; a larger shader
compiles noticeably slower there.

composite_fill now handles every case itself with one blend_rgb call
(Fill as coverage for normal modes, the special-eight blend, Lab Normal
mixing, the text gamma space) and is fs_blend's only composite path,
leaving it with fewer inlined copies than on main. Same results: the 35
GPU parity tests pass.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:16:20 -04:00
ericandhideoneric 5b051861bc fix(blend): keep 32-bit Hue/Saturation/Color/Luminosity above 1 (#1735)
- Hue, Saturation, Color and Luminosity no longer clip 32-bit documents to 0..1, so values above 1 survive in the CPU compositor (blend_rgb_hdr now uses blend_rgb_within at f32::MAX) and in the GPU shader (under the existing F_HDR flag). 8/16-bit results are unchanged apart from rounding at white.
- ClipColor returns the grey when the luminosity is outside the in-gamut range, instead of dividing by max − lum rounding noise. The old formula turned colours inside out (Luminosity of a grey of 2 over (0.5, 0.25, 0.125) gave (1.0, 2.32, 2.99)), and CPU and GPU disagreed (Hue over a grey of 2: CPU 1.0, GPU 2.82).
- Tests: 8/16-bit inputs match the ISO 32000 formula, HDR cases in compose, psblend::with_hdr nesting, and a CPU/GPU parity test across depths.

Co-authored-by: hideoneric <hideoneric@users.noreply.github.com>
2026-10-10 13:15:18 -04:00
Claude cc8d3c3f77 Merge main into #1462; in_blend_space restores LAB_MIX and HDR instead of clearing them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHgFEHEsefoQoBParH47JA
2026-10-09 11:49:32 +00:00
ericandhideoneric 60018dea69 fix(compose): don't clip Add and Divide at 1 in 32-bit documents (#1722)
- Linear Dodge (Add) and Divide keep values above 1 in 32-bit float documents, clipped only at f32::MAX. Integer depths still clip at 1.
- CPU: add psblend::HDR and generic::blend_rgb_hdr / blend_channel_hdr; render sets the flag per document depth.
- GPU: add the F_HDR pass flag, and compose.wgsl uses it for the same clip bound.
- Non-separable modes are unchanged.
- Tests: colour unit tests, compose depth tests, and the GPU parity test float_documents_blend_past_white.

Co-authored-by: hideoneric <hideoneric@users.noreply.github.com>
2026-10-09 18:00:39 +07:00
Claude Opus 5.5 f1433f4ab2 Compose: restore the Lab-mix flag instead of clearing it after a tile (#1112)
Rendering set the thread-local LAB_MIX to the document's value around each
tile, then set it to false. A nested rayon job (a gradient fill, say) can
let the same thread render another tile in the middle of an outer one;
clearing the flag there made the rest of the outer Lab tile mix in sRGB.
The issue's reproduction (a 4096 px Lab document with a gradient fill and
two 50 % fills, 60 renders, RAYON_NUM_THREADS=4) showed whole 256 px tiles
differing between identical renders.

psblend::with_lab_mix sets the flag, runs the closure, and restores the
previous value with a drop guard, as with_cmyk_space does for the CMYK
space. The three set/clear pairs in render_tiled_with use it.

The new test sets the flag, renders Lab and RGB documents on the single-
and multi-tile paths, and checks the flag is what it was. It failed before
the fix. The issue's reproduction, run twice each way on macOS with four
threads: before, 6 and 2 of 60 renders had one whole tile wrong (65536 px);
after, 0 of 60 both times.
2026-10-08 18:27:48 -04:00
Alex GorevskiandCopilot 0c40ec17c6 fix(color): match Photoshop blend corner values (#699)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e77cd34a-ce3e-4981-996f-4d37d49df90d
2026-10-07 16:00:27 -04:00
Brandon Thomas 1d9d92341f PSD effects fidelity: spread metric, emboss, gradient geometry, text gamma (113 -> 118 oracle passes) (#224) (#224) 2026-10-05 10:52:47 -04:00
Brandon ThomasandClaude Opus 5.5 27e047f19f Fix flaky canvas_geom undo test: isolate the text-gamma test in its own binary (#135)
blend_text_gamma_setting switched the compositor's process-wide text gamma
(psblend::set_text_gamma) to 1.0 and back while other engine unit tests ran
in parallel threads. turns_at_every_depth_and_mode flattens a document with
a type layer before a turn and again after edit.undo; when the toggle landed
between the two flattens, anti-aliased glyph edges blended under different
gammas and the bit-exact comparison failed (seen once on Windows CI).

Move the gamma test to crates/engine/tests/text_gamma.rs (its own process),
document the hazard on set_text_gamma, and make the undo assertion report
the turn, depth, mode, differing pixel count, max difference and first pixel.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:36:37 -04:00
Brandon ThomasandClaude Opus 5.5 f7eb6ea9e7 Format: adopt the craft-family rustfmt.toml and format the workspace
CI's `cargo fmt --all -- --check` has failed on every run because the
repo had no rustfmt config and was never formatted. Use the same settings
as EffectCraft/FilmCraft (max_width 160, use_small_heuristics Max), which
also keeps the churn small. Formatting only; no code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:31:17 -04:00
Brandon ThomasandClaude Opus 5.5 42266291d5 Fidelity, GPU fallbacks, interactive performance; print spool-file fix
PSD oracle 111 -> 113 (tent effect blurs, bevel height/shading, glow
range, spread rounding, text gamma, gradient span, Lab blending, colour
burn/dodge edges), channel restrictions (brst), bevel contour/texture,
chisel techniques. Vector masks, pass-through clips, artboards, pattern
fills and bevel textures render on the GPU (parity <=1/255). PNG export
7x, PSD save 4x, .pcraft save 3.6x faster; CPU effects refresh 11x;
deadlock in the effect-map blur fixed. interactive_bench example.

Print: unique spool file per call (concurrent prints of same-named
documents overwrote each other).

Workspace: 2,040 tests pass, clippy clean, layers + wasm OK, parity 600/625.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 01:26:34 -04:00
Brandon ThomasandClaude Opus 5.5 aa87012aab Analysis, notes, proof views, workspaces (+ in-progress work of other agents)
Image > Analysis (ruler, count, measurement scale/log/record, scale
marker), notes (PSD Anno round trip, Notes panel, import), Modifier Keys
panel, Proof Setup plates / Legacy Mac RGB / colour blindness (Vienot),
32-bit preview options, custom pixel aspect, extras options, New/Delete/
Lock Workspace. Parity 600/625, FLOOR 595.

Checkpoint: also contains compiling in-progress work from the parallel
panels, type styles, File menu, photo, filters, UI-parity and fidelity
agents, plus photocraft-cli serve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:49:07 -04:00
Brandon Thomas ae92ac20bf continued wip 2026-09-30 09:37:18 -04:00
Brandon Thomas 16941f7198 one-shot 2026-09-30 05:02:08 -04:00