5773 Commits
Author SHA1 Message Date
David Mann a7b833caf6 Integration: stack #8529 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00
David Mann fff01d8918 Integration: stack #7335 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00
David Mann 6cb8673e8f Integration: stack #6658 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00
David Mann 9e7b35b716 Integration: stack #6055 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00
David Mann c6e628afad Integration: stack #5815 (its merge onto main, conflicts resolved) 2026-10-22 23:40:10 +02:00
David Mann 8748dcbf16 Integration: stack #5598 (its merge onto main, conflicts resolved) 2026-10-22 23:40:09 +02:00
David Mann ddf1bba02f Integration: stack #5485 (its merge onto main, conflicts resolved) 2026-10-22 23:40:09 +02:00
David Mann 7546c9d60d Integration: stack #4221 (its merge onto main, conflicts resolved) 2026-10-22 23:40:09 +02:00
David MannandClaude Opus 5.5 e0fed98f23 Merge PR #8529: perf(bench): run bench_filters2 from cargo xtask perf, four heavy filters now tracked (P64-P67)
docs/scorecard.md: conflict in generated file, kept ours (b7-base); regenerated later.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:39:17 +02:00
David MannandClaude Opus 5.5 6c7e920d5a Merge PR #7335: Gradient Overlay: add Method selector and retain editable source stops
- crates/ui-egui/src/layer_style.rs: values_of keeps the PR's method/smoothness keys and source-stop ends, and main's (#7072) unconditional From/To and geometry fields.
- crates/compose/src/cmyk_tests.rs (no textual conflict): add the PR's new `interpolation: None` field to main's CMYK test GradientOverlay literal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:39:14 +02:00
David MannandClaude Opus 5.5 6aa27edb06 Merge PR #6658: fix(ui): confirm opening damaged PSD/PSB documents
crates/io/src/lib.rs: kept main's ImportOptions; import_for_open/_with and import_stages now take &ImportOptions (plus the PR's damaged flag); import_with_options_and_interrupt delegates to import_for_open_with.
crates/engine/src/jobs.rs: open job decodes via import_for_open_with(&opts) so the damage flag reaches the held-open path; main's raw_defaults and docId kept.
crates/ui-egui/src/lib.rs: open_bytes_from uses main's import_options + raw_defaults and the PR's PSD damage check via import_for_open(&opts).
Also: moved crates/io/tests/psd_recovery_generated.rs to tests/it/ (mod added, support path fixed) and adapted PR tests to ImportOptions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:39:09 +02:00
David MannandClaude Opus 5.5 945a10dc7c Merge PR #6055: Color panel: Photoshop's panel-menu modes, the same in every theme (#6000)
Conflicts:
- crates/ui-egui/src/i18n/{cs,de,el,es,fr,hu,id,it,ja,ko,nl,pl,pt-br,ru,sr,tr,uk,vi,zh-hans,zh-hant}.tsv: kept every key of both sides (main's block, then the PR's Color panel block); no duplicate keys.
- crates/ui-egui/src/panels.rs: took the PR's removal of color_picker/hsb_sliders/rgb_sliders (moved to color_panel_ui.rs); kept main's color_field_height doc comment; main's #5842 slider recolour tests ported to color_panel_tests.rs for the new HSB/RGB slider modes; main's black-HSB test dropped as the PR's hsb_sliders_take_values_on_black covers it.
- crates/ui-egui/src/color_panel_ui.rs (no textual conflict, adapted): set_color now goes through apply_color::foreground_changed with the gesture (was type_tool::foreground_changed), so every mode (sliders, ramp, alerts, wheel) recolours selected type/shapes tool-gated in one undo step per gesture; panel() calls apply_color::track_focus.
- crates/ui-egui/src/color_panel_tests.rs (adapted): new every_slider_mode_recolours_the_selected_shape test.
- docs/gaps.md, docs/ui-parity.md: union of both sides' status lines, rows and revision history; the PR's done row replaces the old "missing" Color panel row.
- docs/scorecard.md: generated, took b7-base's version.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:39:04 +02:00
David MannandClaude Opus 5.5 cab3ef6191 Merge PR #5815: perf(doc): share the XMP packet, Duotone PSD data and unsupported adjustment bytes between snapshots
- crates/engine/src/tests.rs: kept both tests (main's restore_layer_selection test and the PR's history_states_share_large_document_payloads).
- crates/io/src/psd_import.rs: kept main's parsed Duotone inks (duotone_map) with the PR's Arc<[u8]> psd_raw; also main's unknown-adjustment-key import (#6575) now builds Arc<[u8]> raw.
- crates/io/tests/adjust_warnings.rs: moved to tests/it/adjust_warnings.rs on main; PR's .into() edits applied there, plus #6322's Color Lookup test (raw.as_slice().into()).
- docs/architecture.md: union; status line names both changes, both revision-history rows kept.
- Adapted main-new sites to the PR's types: psd_export.rs (Duotone write-back via as_deref/to_vec, Unsupported block compare by slice), inspect.rs and ui-egui unsupported_marker_tests.rs (raw .into()), io tests/it/psd_unknown_adjustment.rs (slice compare).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:56 +02:00
Gabriel Luís da SilvaandClaude Opus 5.5 0ec95fa266 View › Extras (Cmd+H) hides the Move tool's transform controls (#6699) (#6705)
As in Photoshop, the Move tool's Show Transform Controls box and handles are an Extra: hiding
Extras hides them, and their handles stop responding (no hover cursor, a press doesn't start Free
Transform). Cmd+H again brings them back. Show Transform Controls and the layer are unchanged.

Fixes #6699

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:38:53 -04:00
David MannandClaude Opus 5.5 ced9aa6947 Merge PR #5598: Plug-ins: wire Enable Generator (Image Assets needs it)
docs/scorecard.md: conflict in generated file, kept ours (b7-base); regenerated later.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:50 +02:00
David MannandClaude Opus 5.5 f56147655d Merge PR #5485: Add opt-out startup release checks and manual update status
Conflicts resolved:
- crates/ui-egui/src/i18n/hu.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/i18n/ru.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/i18n/sr.tsv: kept base's added sections, then the PR's update-check keys
- crates/ui-egui/src/i18n/tr.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/menus.rs: kept base's saved-workspace entries, then the PR's Help › Check for Updates reordering
- docs/gaps.md: base status line with the PR's G17 clause prepended; PR revision row above base's rows
- docs/target-app-parity.md: base status line (updated 2026-10-22) with the PR's clause prepended; PR revision row above base's rows
- docs/scorecard.md: generated, took b7-base's version

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:46 +02:00
David Mann e7c53231a5 Merge PR #8547: perf(gpu): layer pages that just miss the budget borrow the effect-map share, P60/P61 10× faster on integrated GPUs 2026-10-22 23:38:40 +02:00
David MannandClaude Opus 5.5 08f951b909 Merge PR #4221: Options bar: the same tool options in every theme (#4178)
Conflicts:
- crates/ui-egui/src/panels.rs: took b7-base's file (keeps align_bar in the Move bar and all other main changes) and re-applied the PR's change: dropped the six `if t.pro` guards (Marquee, Lasso, Paint Bucket, Perspective Crop, Crop, Move) and deleted the eight non-Pro hint/fallback arms (incl. the Studio Move arm, now covered by the Pro arm which already has Auto-Select, Show Transform Controls and align_bar); rustfmt reindent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:40 +02:00
David Mann 4d6e2b08c5 Merge PR #8465: perf(camera-raw): exposure with all scopes under its 10 ms budget (P36), same output 2026-10-22 23:38:39 +02:00
aiongg c36e46c387 Merge pull request #8680 from aiongg/automation-workspace-checks
Automation: tighten workspace checks for export, linked files and dialogs
2026-10-22 17:18:24 -04:00
Finn NewickandClaude Opus 5.5 e165f57cde Merge origin/main into perf/doc-shared-blobs
Adapt main's new code to the shared field types: Duotone's parsed inks
(#7958 era import, psd_raw stays Arc-shared), the merged PSD
adjustment-key check and Duotone export in psd_export, the unsupported
adjustment import, and tests that build these fields (now in the single
crates/io/tests/it binary).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:06:47 +01:00
skylar taylor-barrick d89828968d fix(actions): clean up paused playbacks and protect their tokens
Restore suspended recording when the Displace map picker is canceled or fails. Reject control requests that replace a dialog continuation token. Cover failure and successful continuation paths with regression tests.

PR #8680
2026-10-22 14:32:29 -06:00
5c772cf540 Integration: refuse empty artboard assets (#8254)
Exporting artboard assets at the full board wrote a blank, fully
transparent board-size PNG for an empty artboard, or one whose contents
all sit outside the board. main refused those with "the layer is empty",
and Photoshop's Generator refuses a zero-bounds pixmap before it pads to
the board. The board is now used only when its composite has a visible
pixel; otherwise the asset fails with "the layer is empty".

Test: image_assets_refuse_empty_artboards (an empty transparent board and
a board whose only layer is outside it) expects no files and two "the
layer is empty" errors; without the check both are written as blank PNGs.

Co-authored-by: IbrahimOezhan <89545837+IbrahimOezhan@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:05:01 +02:00
8ee9917e5d Integration: move eyedropper_ring tests into tests/it (#7021)
Main now builds each crate's integration tests as one binary (crates/ui-egui/tests/it). The PR's
tests/eyedropper_ring.rs moves to tests/it/eyedropper_ring.rs behind `mod eyedropper_ring;`,
and both wgpu tests take photocraft_testkit::gpu_lock() like their neighbours.
ring_reads_over_a_flat_area_of_its_own_colour still fails with main's canvas.rs and passes with the PR's.

Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:03:30 +02:00
David MannandClaude Opus 5.5 ad68fa36e0 Integration: merge main (#7021)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:03:07 +02:00
dc180cd891 Integration: write a shift tool switch change made before the first save (#5878)
When the file on disk is already version 2 (another instance saved the
migrated "on"), the save base took this instance's current value, so
turning the switch off here before the first save matched the base and
was never written; the file kept "on". The base now holds the migrated
value (on), so only a change made here since loading is written.

Test: turning_the_shift_tool_switch_off_is_saved_over_another_instances_migration
(fails with the previous base: true kept instead of false).

Co-authored-by: Higgs32584 <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:57:43 +02:00
4efebbe2bf Integration: lock a Shift selection drag to multiples of 45° (#6254)
Photoshop constrains a Shift-mid-drag selection move to multiples of 45°
("Move a selection border"), and the Move tool's drag of selected pixels
already uses TRANSFORM_DIRECTIONS (8). selection_drag_to used
MOVE_DIRECTIONS (4), so a near-diagonal Shift drag (delta 13,11) snapped
to the horizontal axis (13,0) instead of 45° (12,12).

Test: shift_constrains_a_selection_drag_to_an_axis gains a diagonal case
(fails with 4 directions); the floating-drag test moves to (34,22) so it
stays an axis case with 8 directions.

Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:56:36 +02:00
d921fedcca Integration: export dialog-recorded Apply Image channels, refuse numeric selectors (#8180)
The Apply Image dialog sends Channels-panel names in sourceChannel ("RGB" by
default, "Red", "Alpha 1"), but the export matched only lowercase keys, so a
dialog-recorded Apply Image failed strict .atn export. Channel names now map
case-insensitively (RGB/CMYK/Lab to the composite), an alpha channel is written
as a Chnl name reference and read back on import, and a numeric layer or channel
selector is refused instead of becoming the merged composite.

Tests: apply_image_dialog_channel_names_survive_a_strict_export (the dialog's
exact params) and apply_image_numeric_source_selectors_are_refused; both fail
without the fix.

Co-authored-by: VishnuByrraju <112673081+VishnuByrraju@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:51:51 +02:00
06530cd1a1 Integration: skip CPU canvas pixel tests only without an adapter (#6966)
The two pixel/checker tests wrapped all of harness() in catch_unwind, so a
failing fixture command or a panic in the first 12 frames was reported as
"no offscreen graphics adapter" and the test passed without checking
anything (CodeRabbit thread on line 107). harness() now runs the fixture
commands first, catches only the wgpu renderer creation (the one step that
panics without an adapter) and builds and steps the harness outside it.

fixture_failures_are_not_skipped_as_a_missing_adapter feeds a 0x0 document:
with the old catch-all it was swallowed and the test fails; now it panics.

Co-authored-by: hogawish <60434858+hogawish@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:45:09 +02:00
3175a3c4cb Integration: keep failed compositor pipelines in the map (#8118)
The parallel build skipped a non-map pipeline whose creation raised an
error, so with new_with_format (errors logged, compositor still returned)
its key was missing from kit.pipelines and the first draw panicked on the
`self.kit.pipelines[&(kernel, acc)]` index. As before the PR, a failed
non-map pipeline now keeps its invalid handle (draws report GPU errors);
only a failed effect-map pipeline is skipped (all maps go to the CPU).

Test: a_compositor_with_failed_pipelines_draws_without_panicking builds
new_with_format(Rgb9e5Ufloat) on a device with an uncaptured-error
handler and renders: it panicked at the index without the fix. The PR's
test moves into tests/it (one test binary) and takes GPU_LOCK.

Co-authored-by: 00xJS <56056764+00xJS@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:42:40 +02:00
2e29033b50 Integration: no Outside stroke share under opaque pixels on the GPU too (#5937)
The PR gives an Outside stroke no share beneath fully covered pixels on the
CPU, but no test failed without it: under a Normal layer the opaque pixel hides
the stroke anyway. It shows with Blend Interior Effects as Group on a Multiply
layer: main multiplied the red interior with the blue stroke beneath it (black).
New test outside_stroke_under_multiply_layer_leaves_interior_to_backdrop
(raster and filled shape) fails on main's effects.rs, passes with the PR.

The GPU compositor mirrored the old math (outline_share 1 at full coverage, and
1 under an opaque pixel layer); compose.wgsl now matches the CPU. Blend Interior
Effects as Group itself renders on the CPU, so on the GPU this is consistency;
a parity case pins non-Normal layers with an Outside stroke.

Co-authored-by: seotarek <30214297+seotarek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:41:01 +02:00
David Mann aa87afc886 Integration: merge main (#8180) 2026-10-22 21:39:51 +02:00
1bfae77c16 Integration: test stringID blend modes through .atn import (#8502)
The PR's test only called blend_label/blend_ps. This adds one through the
import entry point: a Set Lyr step with Md enm("BlnM","softLight") and a
Make layer step with Usng Md "luminosity", written with atn::write and
imported, must give layer.setProps blend "Soft Light" and layer.new.layer
mode "Luminosity" with no unsupported steps. Without the PR's table rows
the Set step is unsupported (and the Make falls back to Normal).

Co-authored-by: justinjohn0306 <34035011+justinjohn0306@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:39:28 +02:00
Brandon ThomasandClaude Opus 5.5 ce2a1e337a Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.13.0
2026-10-22 15:39:21 -04:00
6bb15d2278 Integration: keep another instance's shift tool switch choice (#5878)
Two instances migrate the same version 1 file (useShiftKeyForToolSwitch
false -> true), so both save bases hold false. When B turns the switch off
and saves version 2, A's next unrelated save wrote its migrated true over
that choice. write_prefs now drops the migration delta from the base when
the file on disk is already version 2 or later.

Test: another_instances_shift_tool_switch_choice_beats_this_ones_migration
(fails without the fix: true written over false).

Co-authored-by: Higgs32584 <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:39:04 +02:00
95c3af68c3 Integration: prove doc.replaceSmartObject replaces and undoes (#8150)
The rpc test only checked the returned layer id, so a no-op that echoed
{"layer": id} passed, its undo check passed on doc.place's own step, and
the bad-layer case asserted on an empty reason. It now checks the smart
object's file name and rendered centre pixel (42 -> 200 after the
replace, back to 42 on undo, 200 on redo), that every refusal changes
nothing, and that layer 999999 fails with "no such layer".

The bridge row in docs/control-protocol.md claimed doc_replace_smart_object
maps onto layer.smartObjects.replaceContents; the desktop refuses that
command (ambient path), like doc_place's file.placeEmbedded. The row now
says so and points to the headless / workspace server method.

Co-authored-by: VishnuByrraju <112673081+VishnuByrraju@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:36:42 +02:00
David Mann b7eabd4990 Integration: merge main (#6254) 2026-10-22 21:36:35 +02:00
ebosido444-ai f8f2df8c95 fix(algo): keep the oil-paint rounding exact just below a half
`round_i32` added the half in `f32`, where a value within one ulp of a
half-integer can round across it: `f32::from_bits(0x3eff_ffff)`, the largest
`f32` below `0.5`, plus `0.5` rounds up to `1.0`, so the helper returned `1`
where `f32::round` returns `0` and the LIC could select a different tangent or
bristle-noise coordinate than the build this PR measures against. Adding the
half in `f64` is exact for every window coordinate, so the helper matches
`f32::round` on that boundary again and the pixels are main's.

`rounding_matches_the_standard_library` now covers both half-integer
boundaries and the two `f32` steps either side of every integer a tile
coordinate can reach; it fails on the `f32` form at `0.49999997`.
2026-10-22 22:33:35 +03:00
249eae743f Integration: make Oil Paint's round_i32 exact for every f32 (#8556)
round_i32 added 0.5 in f32, which rounds up at the binade crossing:
0.49999997 (0.5 - 2^-25) + 0.5 becomes 1.0, so round_i32 gave 1 (and -1 for
the negative) where f32::round gives 0. The sum is now taken in f64, where
it is exact for every f32 the i32 cast does not saturate; still no roundf call.

rounding_matches_the_standard_library now covers +-0.49999997, the ties and
8388607.5, and a new test compares every f32 bit pattern in [-4096, 4096]
against f32::round (0.6 s). Both fail on the previous head. The code comment
and docs/oil-paint-performance.md no longer claim the f32 sum is exact.

Co-authored-by: ebosido444-ai <316167330+ebosido444-ai@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:31:24 +02:00
fd8a2856d4 Integration: line the layout's ink box up with the TySh ink box (#6094)
refine_legacy_point_origin moved the layout's logical box (line x0,
baseline - ascent) onto the TySh boundingBox, but Photoshop writes
boundingBox as the tight ink box (Arial 120 pt "..." has bounds top
-102.98, boundingBox top -11). Legacy point text whose ink starts below
the ascent line ("...", "_", lowercase) opened up to ~116 pt too low.

Match like with like: use the layout's unrounded text-space ink bounds
(new render::ink_bounds). The PR's tests built boundingBox from the
logical box, so they now write bounds = layout box and boundingBox =
ink box and pin the exact anchor; legacy_origin_lines_up_ink_with_the_
ink_bounding_box covers "Name", "..." and "_" and fails without this.

Co-authored-by: bogusyogi <102761952+bogusyogi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:29:29 +02:00
David Mann 1e3323f64c Integration: merge main (#6966) 2026-10-22 21:28:41 +02:00
David Mann 87905d448f Integration: merge main (#8118) 2026-10-22 21:25:47 +02:00
David Mann f9efad49d6 Integration: merge main (#5937) 2026-10-22 21:16:38 +02:00
4e80150200 Integration: export artboard assets at the full board, no preference (#8254)
Photoshop's Generator always exports an artboard named like an asset at the
board's bounds (generator-assets renderer: canvasBounds = layer.artboard),
with no setting. The PR put that behind a new Export preference that
defaulted to the trimmed, non-Photoshop output. Image Assets now always tries
artboard_document first and falls back to layer_document; the preference,
its dialog label and its 20 translations are gone (scorecard prefs back to
main's 21 of 176).

Test: image_assets_keep_full_artboards writes a transparent 40x30 artboard
with an 8x6 logo through file.generate.imageAssets and expects board.png
at 40x30 (the trimmed path gives 8x6) and logo.png still trimmed to 8x6.

Co-authored-by: IbrahimOezhan <89545837+IbrahimOezhan@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:16:12 +02:00
cc498e537e Integration: keep the save base complete after the prefs migration (#5878)
The save base after loading kept only the keys the file had, so a key an
older file lacks was written as this instance's default over another
instance's change (two instances on one v1 file: B sets the theme, A saves
anything, the theme reverts). A version 3 file was also written back as
version 2. The base is now what was loaded, with only the version and
tools.useShiftKeyForToolSwitch put back as the file had them (a newer
version is left alone), the way repair_invalid patches keys.

Tests: an_old_sparse_file_keeps_another_instances_change,
a_newer_files_version_is_kept_on_save (both fail with the previous base).

Co-authored-by: Higgs32584 <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:15:53 +02:00
David MannandClaude Opus 5.5 018d3605b1 Integration: merge main (#6094)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:13:36 +02:00
77caa32765 Integration: keep a hidden layer's eye box empty (#8496)
The PR widened the eye hit area to the whole column (#8467) but also painted
a 12x12 sunken well in a hidden layer's eye box. The maintainer decided on
#1662 that a hidden layer's eye box stays empty, as in Photoshop (and the
hidden fx-row eye still does), so the well goes; the full-column hit area and
its test stay. New test a_hidden_layers_eye_box_paints_nothing checks, in all
five themes, that nothing is drawn inside a hidden layer's eye column while a
visible layer's eye is; it fails with the well. Also drops the trailing blank
line at EOF of layer_row_ui/tests.rs (cargo fmt).

Co-authored-by: kobaltgit <159009675+kobaltgit@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:10:34 +02:00
f40366f1a8 Integration: keep only the segment of a lossless-JPEG tile, test the sum through decode (#6041)
A compression-7 frame may declare up to four times its segment's samples
(ljpeg::decode is capped at 4x the tile), and read_plane returned the whole
frame, so each retained tile held up to 4x what retained_segment_bytes
charges it: tiles declared 256x256 holding 512x512 frames pass a 1 MiB
budget while keeping 2 MiB. The lossless-JPEG arm moves into
lossless_jpeg(), which now truncates to the segment's rows (all the scatter
reads) and releases the spare capacity.

The PR's test only called the helper; deleting the call in read_plane kept
it green. decode_charges_the_retained_tiles_together drives decode on an
18x18 CFA in four padded 16x16 LJ92 tiles: 1500 B -> LimitExceeded, 2048 B
-> Ok. lossless_jpeg_keeps_only_the_segment pins the truncation. The doc
comment now says the output plane is alive alongside the sum.

Co-authored-by: Muhammad Saad <188063027+saad-mughal435@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:09:26 +02:00
David Mann 34146281ea Integration: merge main (#8254) 2026-10-22 20:57:14 +02:00
edea110601 Integration: refuse Paste Into a Lock-All group before pasting (#6352)
With the mask commands now honouring Lock All, Paste Into / Paste Outside with a
child of a Lock-All group active committed edit.paste and then failed on the
refused layer.layerMask.revealAll, leaving an unmasked pasted layer behind an
Err. paste_into now checks the destination group's locks first and refuses
cleanly. Test: mask_lock_all::paste_into_a_lock_all_group_refuses_before_pasting
(layer count and history unchanged). Also moves mask_lock_all.rs into main's
one-binary tests/it layout.

Co-authored-by: osaidahmed <170216456+osaidahmed@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:52:59 +02:00