Commit Graph
194 Commits
Author SHA1 Message Date
aiongg 0e80156397 Integration: track t16
Resolutions: CLI usage, flag lists and flag parsing carry both --tiff-compression (t15) and
--jpeg-format (t16); file.saveACopy, batch, imageProcessor and layersToFiles param specs carry
both tiffCompression and jpegFormat; file_cmds keeps tiff_compression_param and
jpeg_format_param; atn_cmds keeps flag_set/applies_at_canvas_bounds (t15) and UNHOLDABLE (t16),
edit.transform uses the pixel_target guard (t15) with layer_ref(p)? (t16); ui-egui open paths
build ImportOptions (t16) and apply raw_defaults (t15); docs headers/rows unioned; atn tests keep
both sides; tr.tsv "Keep Using CPU" kept once.
2026-10-22 15:38:13 +00:00
aiongg 754692c45c Integration: #7638 test's Bevel literal sets gloss_anti_alias, a field main added 2026-10-22 02:28:48 +00:00
aiongg d8865fc682 Merge PR #7638: Map the bevel's shading through its gloss contour, not its light (#7360)
crates/compose/src/effects.rs: kept main's par_units row loop in bevel_maps and ported the PR's change into it: k maps shade and the flat-face level through the gloss contour (lut_at), and the closure returns (hi, sh) without the apply_contour pair.
2026-10-22 02:27:06 +00:00
aiongg 2b3af0f815 Merge PR #3193: fix(compose): resolve Dissolve coverage across adjustment, effects, a…
Conflicts:
crates/compose/src/tests.rs: appended tests, kept both
crates/gpu/tests/parity.rs: appended tests, kept both
2026-10-22 02:23:06 +00:00
aiongg f32f882f3a Merge main into integration-10
# Conflicts:
#	apps/photocraft-web/src/web.rs
#	apps/photocraft/src/services.rs
#	crates/engine/src/file_cmds/tests.rs
#	crates/engine/src/float_cmds.rs
#	crates/engine/src/float_cmds/tests.rs
#	xtask/src/corpus.rs
#	xtask/src/corpus_pins.rs
#	xtask/src/main.rs
2026-10-21 21:56:26 +00:00
skylar taylor-barrickandGitHub 18e2d45420 Merge pull request #6318 from robertoffmoura/fix/floating-selection-move-lag
Fix lag when dragging a floating selection with the Move tool
2026-10-21 14:25:55 -06:00
aiongg d5bf6f0398 Merge PR #7846: Scale layer-effect noise by coverage so it follows the glow 2026-10-21 18:15:10 +00:00
aiongg e027e048fc Merge PR #5847: Shapes: Density and Feather on shape layers (#5846)
# Conflicts:
#	docs/scorecard.md
2026-10-21 15:45:38 +00:00
aiongg 77557d75f1 Merge PR #8229: Integration batch: 8 PRs on main
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/io/src/psd_export.rs
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
2026-10-21 15:45:07 +00:00
Roberto Moura 8352b26c1a Fix lag when dragging a floating selection with the Move tool
- Replace whole-region Vec<f32> read_region/write_region in CutParts::new
  with tile-parallel cut_piece + try_fill_surface/try_clear_surface,
  sharing interior Arc<Tile> allocations in O(1) and activating the
  solid_tile hole-fill fast path on Background and mask targets.
- Cache piece_bounds on CutParts so CutParts::place avoids scanning tile
  content bounds on every drag frame.
- Parallelize composite_over across top tiles with Rayon and share fully
  opaque top tiles by Arc<Tile> clone in O(1), preserving COW tile
  identity for GPU canvas uploads.
- Parallelize Surface::translated across target tiles with Rayon and
  prune cleared tiles in-place inside edit_tiles.
- Skip zero-offset floating preview generation on pointer-down in
  move_ui::floating_doc.
- Exclude SnapKind::Selection when moving selected pixels with the Move
  tool so small initial drags do not snap back to (0, 0).
- Keep navigator_plain active during Move/Brush/Patch drags on a GPU
  canvas so navigator_gpu is not evicted into a synchronous full-document
  CPU thumbnail composite on the first drag frame.
- Parallelize row normalization in compose::render_reduced_in_bands and
  add a direct read_rgba_into fast path for unmasked Normal layers over
  an empty backdrop.
2026-10-21 13:21:10 +00:00
David MannandClaude Opus 5.5 4bc06b906b Address CodeRabbit review: bound the soft shape frame scan
Look in from each edge through strips that double in thickness, so a subtracted
or intersected subpath's empty margin costs a few renders instead of one per row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:15:11 +02:00
David MannandClaude Opus 5.5 e5f4f8e260 Address CodeRabbit review: soft shape frame from the hard render, feather claim
A soft shape's transform frame, snap rectangle and Properties X/Y/W/H came from
the path plus the full stroke width on every side, so a centred or inside stroke
grew the frame when Density or Feather was set. The frame is now the pixels the
same shape renders at full Density without Feather (edge strips only, clipped to
the canvas), identical to the hard shape's for every stroke alignment.

feather_matches_photoshops_ramp now enforces the 1-level claim the scorecard
makes (measured max deviation 1 at 8, 16 and 32 bit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:14:46 +02:00
David Mann 86811ddeee Merge PR #6002: Stroke: keep Overprint (overprint)
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/ui-egui/src/layer_style.rs
#	docs/file-format-parity.md
2026-10-21 11:36:58 +02:00
David Mann 5bf2898f4e Merge remote-tracking branch 'origin/main' into feat/tool-214-12-shape-feather
# Conflicts:
#	docs/scorecard.md
2026-10-21 11:36:04 +02:00
David Mann a492716367 Merge PR #5990: Bevel & Emboss: keep the Gloss Contour's Anti-aliased (antialiasGloss) 2026-10-21 11:29:05 +02:00
aiongg fc371711e6 Merge PR #5847: Shapes: Density and Feather on shape layers (#5846)
# Conflicts:
#	docs/scorecard.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-21 05:04:03 +00:00
aiongg 9b889e1a58 Merge PR #6640: Layers: toggling a clipped adjustment damages only its base (#6535) 2026-10-21 01:06:16 +00:00
aiongg 68f8be3967 Merge PR #5791: PSD Multichannel: alpha channels are stored as-is, not as ink (#5790) 2026-10-21 01:06:14 +00:00
aionggandGitHub b097800e78 Merge pull request #8095 from aiongg/integration-7
Integration batch: 9 PRs on main
2026-10-20 20:44:51 -04:00
David MannandClaude Opus 5.5 627bb8f1bf Integration: merge main, resolve conflicts (#6002)
docs/file-format-parity.md: both sides added revision-history rows; keep both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:11:01 +02:00
0073b395d4 Integration: merge main, resolve conflicts (#5990)
docs/file-format-parity.md: main added three 2026-10-15 revision rows
(DDS save, PNG sTER, PSD guides) where the PR added its own; keep all four.

Co-authored-by: varanus-salvator <262257003+varanus-salvator@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:50:13 +02:00
David Mann d089eaa81b Merge PR #7693: perf(compose): Reuse bounded scratch and tone tables 2026-10-20 22:39:29 +02:00
David Mann 651c32b9eb Merge PR #7566: perf(compose, gpu): many-layer composites, P60/P61 on the GPU 111 -> 28 ms 2026-10-20 22:39:26 +02:00
aiongg ea7553f031 Merge PR #5111: Large documents: a gigapixel New Document draws without freezing (#4017) 2026-10-20 18:39:28 +00:00
David Mann 0b39361076 Merge main into feat/tool-214-12-shape-feather 2026-10-20 17:12: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
Nigel Ihza Firdausy f0aae59938 Merge upstream main and retain CPU/GPU Dissolve parity coverage 2026-10-20 19:44:22 +07:00
Hector Villarreal b517b6af02 Merge remote-tracking branch 'upstream/main' into fix/7360-type-bevel-fill 2026-10-19 16:23:08 -06:00
NetterSilverandClaude Haiku 5.5 8591ca1904 Scale layer-effect noise by coverage so it follows the glow
Fixes #7833

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:44:44 +00:00
aiongg acc853b517 Merge upstream main into integration-5
docs/file-format-parity.md: keeps both 2026-10-15 rows, main's #6063 PNG sTER row first, then #5976's PSD guides row
2026-10-19 17:20:29 +00:00
5c1b9425d1 Gradient Overlay: repaint the whole layer when a stroke moves its frame (#4053) (#5340)
A Gradient Overlay (and gradient strokes, linked patterns, linked bevel
textures) is laid out over the layer's bounds. Painting grows those
bounds, but the stroke commands reported only the stroke as damage, and
the canvas's committed-stroke path repainted only what the live stroke
had not shown. The canvas therefore kept the old gradient around earlier
strokes: one gradient per stroke instead of one over the whole layer.

Session dispatch now grows a command's reported damage by
photocraft_compose::relayout_damage(before, after): the whole composite
of any visible layer whose effects span its frame, when that frame moved.
The canvas adds the same area to a committed live stroke. Strokes inside
the existing bounds and frame-independent effects keep tight damage.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 13:04:52 -04:00
bc4f2a783d Scripts: Flatten All Layer Effects bakes with the document's Global Light and patterns (#5719) (#5720)
`bake` rendered each layer through `render_layer`, which has no document context (default Global
Light, no patterns, the render rect as canvas). It now uses the new doc-aware `render_layer_in`.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 13:04:46 -04: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
David Mann c2f3e23a40 Merge PR #5477: perf: chisel bevel size changes, CPU passes on threads and GPU fields share their sharp edges (results unchanged) 2026-10-19 16:51:22 +02:00
David MannandClaude Opus 5.5 de2cb1f0c0 Compose: a clipped adjustment inside a pass-through clip base damages the whole group (#6535)
A pass-through group that is the base of a clipping group renders isolated while it holds a
visible adjustment (`clip_base_adjusts`), so showing, hiding, adding or removing a clipped
adjustment inside it can switch the group's other children (a Multiply layer beside the base)
between isolated and pass-through rendering. `change_bounds_in` now takes the list holding the
parent group and reports that group's change bounds in this case; `layers_damage`, `step_damage`
and `one_sided_bounds` pass it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:14:06 +02:00
Akos Arinton 80f896210a perf(compose): write band tiles as they finish, not after the band
Parallel bands each collected their finished tiles until the band's last
one was done, so with work stealing many bands could hold most of their
tiles at once, close to a second copy of the output. Each tile is now put
into its band behind a lock as soon as it is rendered; only the tiles in
flight are held, as before the bands ran in parallel.
2026-10-19 13:34:32 +03:00
David MannandClaude Opus 5.5 8534f0c935 Compose: a clipped adjustment with no base inside a pass-through group reaches everything (#6535)
The compositor draws a clipped adjustment that has no unclipped layer
beneath it as an ordinary adjustment, but change_bounds counted it as
contained in a pass-through group. Hiding the layer clipped to such a
group (which ends its isolation) or the group itself then recomposited
only the group's pixels and left the rest of the canvas stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:03:54 +02:00
Hector VillarrealandClaude Opus 5.5 a42e831698 Map the bevel's shading through its gloss contour, not its light (#7360)
The gloss contour was applied to the highlight and shadow amounts after
the lighting split, so a contour that starts high (Rolling Slope -
Descending) turned every unlit pixel into a full highlight: a bevelled
glyph's flat face painted (255, 40, 42) over its (151, 22, 23) fill. It
now maps the shading before the split, with the flat face's mapped
shading as the neutral, on the CPU and the GPU. Linear is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:01:11 -06:00
Ni-zavandVikram Bhaskaran a221622ad4 fix(compose): cover clipped Dissolve adjustments
Port the clipped-adjustment coverage path from #4711 and verify depth, alpha, masks, and document-coordinate GPU parity.

Co-authored-by: Vikram Bhaskaran <vikram.bhaskaran@outlook.com>
2026-10-19 07:18:54 +07:00
Nigel Ihza FirdausyandNi-zav 9050e86dac test(compose): preserve clipped Dissolve coverage across fill, boundaries, and regions 2026-10-19 07:18:54 +07:00
Nigel Ihza FirdausyandNi-zav 157711d1be style(compose): apply rustfmt layout from CI for Dissolve regression 2026-10-19 07:18:54 +07:00
Nigel Ihza FirdausyandNi-zav e0d3d940c6 fix(compose): resolve Dissolve coverage across adjustment, effects, and clipping paths (#3050-#3052) 2026-10-19 07:18:53 +07: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
David Mann 0739ff48c5 Merge remote-tracking branch 'origin/main' into fix/4017-huge-new-document
# Conflicts:
#	crates/ui-egui/src/gpu_canvas.rs
#	docs/scorecard.md
#	perf/budgets.toml
2026-10-17 01:07:37 +02:00
David MannandClaude Opus 5.5 b9d646baad Merge main into fix/multichannel-alpha
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 22:11:01 +02:00
David MannandClaude Opus 5.5 145acbda8b Layers: toggling a clipped adjustment damages only its base (#6535)
`photocraft_compose::change_bounds` returned "anywhere" for every
adjustment layer before looking at `clipped`, although a clipped
adjustment only changes its clip base's pixels. Hiding or showing one
(or undoing that) therefore recomposited the whole canvas, which on a
heavy document fails the GPU memory fit check and falls back to a full
CPU redraw taking seconds to minutes.

`change_bounds` sees one layer and cannot find the base, so a new
`change_bounds_in(layers, k, canvas)` looks at the sibling list: a
clipped adjustment's change bounds are its base's change bounds. It
stays `None` (anywhere) when no unclipped base lies beneath it (the
compositor then draws it as an ordinary adjustment), when the base is
an adjustment (layers clipped to it composite atop the adjusted
backdrop), and, through `change_bounds`, when the base is a
pass-through group holding an adjustment (visible clipped layers make
that group isolated, so they decide whether its adjustment reaches the
layers beneath) or draws outside its bounds. The engine's
`layers_damage`, `step_damage` and `one_sided_bounds` use it.

Tests: a compose unit test that the clipped adjustment's bounds equal
the base's, that every pixel the toggle changes lies inside them, and
the cases that stay unbounded; an engine test that hide, undo, redo,
show and `layer.setProps` visible report the base's rect (top level and
inside a group), and that recompositing only that rect over the
previous canvas equals a full recomposite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 15:04:09 +02:00
David Mann ce19e018b9 Merge remote-tracking branch 'origin/main' into feat/tool-214-12-shape-feather
# Conflicts:
#	docs/ui-parity.md
2026-10-15 23:52:29 +02:00
David MannandClaude Opus 5.5 24a77a18c0 Shapes: a soft shape's transform frame follows its path (#5846)
A shape below 100 % Density renders over the whole canvas, and a Feather
grows its pixels, so Free Transform, the Move tool's transform controls,
snapping and Properties > Transform framed the canvas or the blur. As in
Photoshop the frame now follows the path and stroke whatever the mask
Density and Feather: photocraft_compose::hard_shape_bounds (shape_bounds
without the soft reach) feeds whole_layer_bounds and the Properties
Transform fields. Rendering keeps the soft bounds; other layers and hard
shapes are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 17:08:05 +02:00
David MannandClaude Opus 5.5 442987528f Merge main into fix/4017-huge-new-document
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 16:52:49 +02:00
skylar taylor-barrickandGitHub 4e7a17cd5d fix(compose): serialize effect identity cache misses
Keep effect-identity memo lookup, pure hashing and insertion atomic across concurrent render callers. Preserve existing identities and add a contention regression with eight synchronized callers.
2026-10-15 08:18:21 -06:00