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.
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.
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.
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.
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.
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.
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.
crop_ui.rs moved to tools/; the PR's builders replace main's Clippy extractions of the same code; #6723's Color Lookup save carried into the moved encoder.
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.
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>
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>
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>
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>
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>
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>
* Background redraw: key kept composites on the text gamma, trim them every frame (#8520)
Two gaps in the kept background redraws:
- Color Settings > "Blend Text Colors Using Gamma" is an application
setting that type layers composite with, but it wasn't part of the key.
After toggling it, going back to a kept state (an Alt-click solo and
again, undo/redo) uploaded type edges drawn with the old gamma. The key
now holds the gamma, and a redraw that ran while the gamma changed
(even if changed back, and also while another document was shown)
isn't kept. Every running redraw is checked every frame; two changes
within one frame aren't seen.
- Kept composites were trimmed only when the next deferred redraw
started. A state History dropped (or purged) kept its texels, and
lowering Memory Usage or a small edit that grew a kept document didn't
release them. Each frame now drops dead entries, and the cap is taken
again when Memory Usage or the revision of any kept document changes.
Tests (each fails without its part of the fix):
a_state_drawn_before_the_text_gamma_changed_is_composited_again,
a_redraw_across_a_text_gamma_change_is_not_kept,
a_redraw_across_a_text_gamma_change_and_back_is_not_kept,
a_hidden_documents_redraw_across_a_text_gamma_change_and_back_is_not_kept,
kept_composites_go_when_memory_usage_drops_between_redraws,
kept_composites_go_when_a_small_edit_grows_the_document_past_the_cap,
an_older_kept_composite_goes_when_its_document_grows_past_the_cap,
a_kept_state_history_purged_goes_at_the_next_frame.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* GPU compositor: test the layer page out-of-memory paths, keep GPU timing after one (#8521)
The PR's tests passed with its fix reverted: main's after-cell evict()
already meets every assert of the zero-budget test, so the out-of-memory
fallback had no test. A test hook (Compositor::fail_layer_allocs) now makes
the next n page textures fail as out of memory, and a parity test covers
the in-place retry, the submit-and-retry, the CPU fallback with a healthy
device, and the next refresh back on the GPU. Each of those fails when its
part of the fix is removed.
A retry that failed after its submit also switched GPU timing off for the
rest of the session: the submit marks the measurement flushed, and
cancelling a flushed measurement stops timing for good. The retry waits
for the GPU, so it now clears that mark (GpuTimer::drained);
an_out_of_memory_refresh_does_not_stop_gpu_timing fails without it.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Address CodeRabbit review: serialise the OOM timing test with GPU_LOCK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Return the pattern code to the state before the earlier 8000 x 8000 attempts. A pattern that fits the compositor's 256 MiB cap is expanded as before; a larger 8-bit or 16-bit one is held as straight RGBA8 or RGBA16, exactly its values at 4 or 8 bytes a pixel, so Define Pattern's 8000 x 8000 paints at 8 bits. Define Pattern refuses an area the compositor could not hold with its limit, patterns whose RGBA32F texture passes 256 MiB paint on the CPU, shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
The GPU path keeps its RGBA32F pattern textures and shader, and declines a pattern whose texture would pass 256 MiB so the CPU compositor, which holds an 8-bit pattern at 4 bytes a pixel, paints it. Add tests for the fallback, the pattern id staying the same when read in bands, and the paintable limits.
Define Pattern accepts 8-bit patterns up to 8000 x 8000: an 8-bit pattern is held at 4 bytes a pixel by the compositor and the GPU, read in bands, and what is paintable follows the working-copy bytes. Shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
* 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>