Commit Graph
379 Commits
Author SHA1 Message Date
e4e2607c85 Preview spot inks over the RGB/CMYK composite as an overprint (#10276)
* PSD: a flat file's named extra channels are not its transparency

A PSD without layer records had its first extra channel read as the merged
transparency, so the first spot or alpha channel of a flattened document
vanished and its ink punched a transparent hole in the image (psd-tools
cmyk-alpha-spot.psd, cmyk-spot.psd). When the alpha channel names (1045, or
1006 when 1045 is missing) name every extra channel and the first is not
"Transparency", there is none. The import also reads channel names from
1006 when 1045 is missing, and a spot whose DisplayInfo colour space is
unknown takes its ink from the Alternate Spot Colors (1067).

* channel_map: read 1067 records with as_chunks

* Preview spot inks over the RGB/CMYK composite as an overprint

Visible spot channels of RGB and CMYK documents print over the canvas
composite with the Multichannel ink formula, in Channels panel order,
instead of a tint; alpha channels and Quick Mask still tint over them.
Spot channels open visible, and the PSD thumbnail (1036) prints them.
flatten, export and the merged image stay free of spot inks. Ink
colours go through the document's CMYK profile, and into an RGB
document's own profile.

Refs #10249

* channel_view: test solidity over a gray picture

* Thumbnail: matte only inked pixels; test the merged planes directly

Review feedback on #10276: pixels without spot ink keep their
transparency in the thumbnail buffer, and the thumbnail test checks the
saved merged process planes instead of a re-imported flatten.

* Thumbnail spot inks: sample big boxes on a 4x4 grid

Review feedback on #10276: reducing each spot channel no longer scans the
whole document; a thumbnail pixel averages at most 4x4 samples, so a huge
document costs about h x 4 row reads per spot.

* Spot overlay: composite a proxy for the first draw of big documents

The overprint needs the composite under the inks. Rendering the full-resolution
composite to sample every factor-th pixel cost ~1 s for a 24 MP CMYK document.
A proxy document picks the same pixels (nearest-neighbour downsample) for about
1/factor² of the work. Documents with layer effects, vector masks or feathered
masks keep the full-resolution path.

* Proxy: downsample rows in parallel; the overlay proxy leaves out unused channels

The spot overlay's proxy shrank the spot channels, selection and Quick Mask that an
RGB or CMYK composite never reads: about two thirds of its cost. downsample now
reads its rows in parallel, 64 at a time.

* Spot overlay proxy: keep the full-resolution composite under uncached pattern fills

A pattern fill renders its tiles at their own size on a proxy, so the proxy would
sample other pattern pixels. Unless the fill's matching cache is shrunk with the
proxy, use the full-resolution composite.

* Spot overlay proxy: hidden layers don't decide the composite path

Hidden layers and the contents of hidden groups are not composited, so a hidden
feathered mask or pattern fill no longer forces the full-resolution composite
(the same rule as proxy_faithful).

* Update crates/compose/src/multichannel.rs

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>

* Spot overlay: only inked pixels replace the canvas; inks through the display transform

Where every visible spot is 0 the overlay stays transparent, so the normal
canvas (display LUT, proofing, 32-bit preview, full resolution) shows; an
empty spot renders no composite. Printed pixels go through the canvas
display transform (sRGB encoding of linear composites, monitor profile,
Proof Colors), keyed into the texture. Ink colours come from one InkSpace
per render: the document's profiles are parsed once, not per spot per
damage rect.

* test(compose): cover hidden spot preview and document approximation

* fix(ui-egui): preserve solo spot channel display when process colours are hidden

Clicking a spot channel row in the Channels panel targets the channel alone
and hides all process colour channels (`vis == 0`). Overprint inks were
previously extracted from `overlays` before checking `vis == 0`, leaving
`overlays` empty and causing the solo channel view to fall back to an active
layer mask (or return `None` if the layer had no mask).

Keep the spot channel in `overlays` so `vis == 0` can select it as the solo
grayscale picture, separating overprint inks only when process colours are
visible (`vis > 0`).

Adds a regression test targeting an existing spot channel in RGB and CMYK on
an unmasked layer and editing it.

* channel_view: document the spot overprint preview as an approximation

* test(ui-egui): solo spot channel wins over an unrelated layer mask

* channel_view: sRGB spot inks, lazy display transform, inked-area composite

Review fixes for the spot overprint preview:

- RGB spot colours are sRGB, like the CMYK, Lab and gray ones: InkSpace now
  converts them into an RGB document's own profile too. In a linear (EXR/HDR),
  Adobe RGB or ProPhoto document an RGB ink showed the wrong colour (brightened
  in a linear one once the canvas sRGB-encoded it).
- The display transform is looked up only when the view is redrawn and inks
  are shown, not every frame before the cache check; display_transform_for
  borrows the proof state (proof_ref) instead of cloning a default CMYK profile.
- Visible spot channels without ink, with nothing else shown, draw no texture:
  render returns None and ensure remembers it, instead of uploading an
  all-transparent document-sized texture. A damage re-render that finds nothing
  clears its rect, so erased ink never lingers.
- Printing a few inked pixels composites only their bounding box when that is
  cheaper than the whole (proxy) composite, and a cached composite is borrowed
  instead of copied.

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

---------

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:34:12 +01:00
5c157e8400 Build the GPU compositor off the UI thread (#9969)
* Prepare GPU compositor in background

* Add compositor startup performance scenarios

* Generate scorecard in scratch CI

* Fix pipeline build type inference

* Address compositor startup review and document measurements

* Run startup performance checks on scratch CI

* Remove scratch performance job

* Count failed GPU attempts in frame timing

* Make shader fingerprint test portable

* Address review: run first-document scenario as a guarded group; share COMPOSITOR_BUILDING constant

* Handle compositor startup benchmark failures

* Fix frame timing after upstream merge

* Keep GPU startup failures on CPU fallback

* Record the failed GPU attempt of a deferred redraw, and bound --reps

A redraw deferred after a failed GPU composite carries that attempt's time to its end, where it is
recorded once beside the CPU time. Perf::record clears the attempt so a direct CPU refresh doesn't
inherit the last one's. perf_scenarios caps --reps.

* Refresh cached canvases after compositor build

* Expose canvas cache key to GPU completion handler

* ci: drop the GPU compositor probe job

AGENTS.md: feature and fix PRs don't add or change CI, and app workflows
only call the shared craft-common-actions workflows. The probe example
stays as a manual tool (cargo run -p photocraft-gpu --example
compositor_build_probe).

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

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:49:12 +01:00
6326117c53 Web build: big operations that won't fit fail with an error instead of trapping, and a trap says so (#10129)
* Raster: ask the allocator whether a big operation fits; PSD save reserves its whole output

New photocraft_raster::headroom: ensure(need) tries to reserve the need plus a margin and frees it
at once (web only; native always passes), below(bytes) tells whether less than that can be
allocated, and simulate_available fakes the answer for tests and diagnostics.

PsdFile::to_bytes reserved only the pixel data, so embedded smart-object data and other tagged
blocks grew the output by doubling, which on the web is one unrecoverable huge realloc. It now
reserves the exact encoded length.

* Big operations that won't fit in memory fail with an error instead of trapping

Place, Image Size, a smart-object re-render and Save now ask the allocator whether their estimated
need can be allocated before they start (photocraft_engine::memory_gate, estimates per operation,
photocraft_io::estimate_export_bytes for saves). A refusal changes nothing and says what the
operation needed and what to do (save, Edit > Purge > All, close documents).

A standing notice warns when less than 512 MiB can be allocated, with hysteresis, so the user can
save before anything traps. Notices can now be sticky, which keeps them past the auto-hide delay.

* Web build: a trap says so instead of freezing the page, and ?free-mb simulates low memory

index.html holds a hidden, static 'PhotoCraft stopped' message with a Reload link. web.rs reveals
it when the module traps (the window error event, or an unhandled rejection from a spawned
future), tells running out of memory from an internal error by the panic message a chained panic
hook kept, and skips the Leave site prompt once stopped. ?free-mb=N makes the memory gates and the
low-memory warning see only N MiB, for checking them in a browser.

* Docs: web build memory handling in the gaps list and the parity assessment

* Engine: use is_multiple_of in the megabyte formatter

* Cargo.lock: the web shell depends on photocraft-raster

* Web boot page: carry the stopped message too, as the test that keeps the two pages equal requires

* Save check counts one copy of the document, and runs once per save

The encoded file's buffer is reserved fallibly, so a save it doesn't fit
in fails with an error instead of trapping; counting it as well refused
saves that would have fitted. On the background-save path the check ran
in write_document and again in start_save, which other callers reach
directly; it now runs only in start_save there.

* Review fixes: warning check allocates nothing, PSD merged image counted, automation saves checked

The low-memory warning's periodic check reads the size of linear memory
instead of reserving a block: wasm memory never shrinks, so a trial every
few seconds raised the high-water mark by up to 768 MiB for nothing. The
operation checks keep their trial reservation.

PSD and PSB saves always build a canvas-sized merged image (a placeholder
without maximum compatibility) and hold it raw and encoded; the save
estimate now counts it. The synchronous automation save checks memory
before encoding, as File > Save does, and its test asserts the memory
error.

The trap listeners go in before the app is built, so a trap while
building it shows the stopped message too, and a memory access out of
bounds reads as unexpected rather than as running out of memory.

* Cover layered PSD preflight limitation

* Fix layered PSD test iteration

* Docs: layered PSD preflight limitation in the gaps list

* Web OOM gate: gate direct exports, truthful advice for unbounded PSD sources (#10024)

* Web OOM gate: estimate layered PSDs per layer instead of refusing every real PSD

The PSD check admitted a file only when its color-mode data, image
resources and layer section were all empty. Every Photoshop file has
image resources (at least the resolution), so on the web nearly every
real PSD was refused for Place, PSD smart objects could not render,
and Image Size was refused for documents holding one, whatever the
free memory (u64::MAX never fits).

The color-mode data and image resources hold no pixels, so they are
now only skipped. A layered file reads its layer count from the layer
info (signed, absolute value) and adds a canvas-sized surface and mask
per layer to the flat estimate. Files that are not 8-bit RGB or
grayscale, keep their layers in a Layr tagged block, or whose sections
can't be read stay unbounded and refused, as before.

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

* Web stopped page: match trap messages ignoring case

Safari reports the allocation-failure abort as "Unreachable code
should not be executed" and Firefox says "out of memory", which the
case-sensitive matching missed: the trap was not recognised, or was
shown as an unexpected stop instead of running out of memory.

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

* Export estimate: count the merged image OpenRaster writes

The ORA writer keeps the layers but also flattens the document for
mergedimage.png, a 16 B/px composite of the canvas, which the
estimate left out.

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

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:04:59 +01:00
David Mann a4a10166df Merge remote-tracking branch 'origin/main' into integration-batch-17
# Conflicts:
#	docs/gaps.md
2026-11-05 14:33:32 +01:00
Khawar Zamman WaniandFredApps a38e544faf Make Select and Mask refinement and previews tile-sparse (#10732)
* Make Select and Mask refinement and previews tile-sparse

* Preserve finite mask extent when copying sparse pixels

* Keep solid preview coverage shared during conversion

* Preserve alpha sharing and document sparse refinement limits

* Refine mixed tiles in bounded parallel batches

* Skip guide sampling for constant refinement halos

* Balance sparse refinement batches using measured queue size

* Preserve default alpha and cancel sparse refinement cooperatively

* Address review follow-ups: cancellation after completed flush, stroke assertions, and docstrings

* Fix merge: bind tile bytes to avoid temporary borrow in matting

---------

Co-authored-by: FredApps <fredapps@users.noreply.github.com>
2026-11-05 14:22:03 +01:00
David Mann 4e0d487568 Merge remote-tracking branch 'origin/main' into integration-batch-17
# Conflicts:
#	docs/gaps.md
2026-11-05 13:21:45 +01:00
Khawar Zamman Wani 0350b5de16 Select sky beneath an obstructed top border (#9693)
* Select sky beneath an obstructed top border

* sky: enforce adjacent-pixel RGB constraint in obstructed fallback seeds
2026-11-05 13:16:37 +01:00
David Mann f162c19e81 Merge remote-tracking branch 'origin/main' into integration-batch-17
# Conflicts:
#	crates/ui-egui/src/control/dispatch.rs
#	crates/ui-egui/src/control/params.rs
2026-11-05 12:44:02 +01:00
Khawar Zamman Wani e3ecd73569 feat(frame): rework the Frame tool's selection, handles and placement (#8920)
Thanks @wanikhawar — merging the Frame Tool rework.

Summary: Frame Tool selection/handles/placement now distinguish frame vs image (border / click / double-click and Layers thumbnails), with independent transforms, shape variants, Properties/options-bar stroke and fit commands, Convert to Frame, CLI/MCP `layer.frame.*` commands, native `.pcraft` persistence, and PSD export via editable groups + `pcFr` metadata (not Photoshop's native frame type). Tests cover selection, transforms, fitting, locks/hostile geometry, and native/PSD round-trips.
2026-11-05 12:39:02 +01:00
David MannandClaude Opus 5.5 16000b01ef Address CodeRabbit review: batch 17 findings
Engine: droplet dependency capture skips disabled steps; a nested Batch
that was cancelled or could not write its error log fails the caller;
runDroplet forwards overrideOpen; actions.rename follows named Batch
steps; Batch steps inside a script count against MAX_SCRIPT_STEPS;
output claims compare destination identity; Save and Close never
replaces a read-only source. ATN: Md must be a BlnM enumeration,
adjustment-layer names must round-trip, Calculations exports a missing
blending as Multiply, Apply Image refuses unnamed-layer transparency.
Filters: Shape Blur records its resolved geometry, reserves its kernel
memory first, and reports a preparation failure as an error, not a
cancel; Crop keeps an artboard preset when the size is unchanged and
fits the board on a turned crop.

Codecs/cms/algo: zero-axis FITS image HDUs are empty; a GIF walk that
breaks off before the trailer fails closed; two-value .3dl shaper rows
are applied; fragmented custom kernels are refused and their work polls
cancellation.

UI: Conditional branches play through panel playback; paused actions
refuse to resume after being replaced; Playback Options OK validates
its fields; transform double-click ignores a hidden pivot; path boxes
snap to a stationary selection; modal blocking uses the effective tool;
a picker-consumed release clears snap lines and ends a Move drag; the
Crop preset control shows Selection and checks only it; Crop box undo
restores frame_from_selection; Bosnian fill labels.

Tests: timing asserts moved to an ignored test, plus the review's
coverage nitpicks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:37:38 +01:00
David Mann cf65810dda Merge remote-tracking branch 'origin/main' into integration-batch-17
# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/gaps.md
2026-11-05 09:03:28 +01:00
d7d138b1e5 Art History Brush: Opacity on the options bar (#10263) (#10278)
* Art History Brush: Opacity on the options bar (#10263)

The Art History Brush's own brush opacity is shown in its options bar
(0-100%) and sent to paint.artHistoryBrush. It is kept per tool, saved
across launches, set by the 1-0 keys and restored to 100% by Reset Tool,
as the Clone Stamp's is. Tests cover the UI wiring, per-tool persistence,
Reset Tool and stroke output at 8/16/32-bit.

* Update crates/ui-egui/src/panels/options_bar.rs

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>

* Art History Brush starts at 100% opacity on first use

A tool picked for the first time takes the outgoing brush; the Art
History Brush now takes it at full opacity, as the issue's default asks,
so a 30% Brush no longer makes the first Art History stroke 30%. Other
tools keep inheriting opacity.

* Art History Brush: document Opacity in the command contract and carry it in tool presets

---------

Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 07:15:57 +01:00
Khawar Zamman WaniandFredApps ad59d9962c Type: add OpenType Stylistic Sets submenu (Set 1–20) (#10304)
* Type: add OpenType Stylistic Sets submenu (Set 1–20)

Type > OpenType > Stylistic Sets toggles ss01–ss20 through new
type.openType.stylisticSets.setN commands on the usual formatting scope
(selected characters while editing, else the selected type layers).
The toggle direction and the menu checkmark follow the style at the
selection start. Updates TYPE-218-5 and G8.

Closes #10285

* Stylistic Sets: round-trip test through a full PSD; scorecard parity count

* Stylistic Sets: test PSD round trip on an opened file; record the new-document PSD gap

Documents made in PhotoCraft get no Txt2 block on PSD save, and the set
mask lives only there, so their sets are not saved to PSD yet. The engine
test keeps the .pcraft round trip, an io test covers the PSD round trip
of a file with type objects, and TYPE-218-5 and G8 record the gap.

* Stylistic Sets: share one tag table between the engine and the menu

The engine exports STYLISTIC_SET_TAGS; the Set 1–20 commands index it and
the menu checkmarks read it, so the 20 tags are listed once.

* Stylistic Sets: check malformed menu ids through the resolver, not the menu list

* Scorecard: TYPE-218-5 notes PSD save of stylistic sets in documents made here (#10308)

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 06:16:54 +01:00
e580cca07a Port #5212: document Playback Options
Co-Authored-By: Zer0C00L01 <12090083+Zer0C00L01@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 06:13:13 +01:00
362a307449 Port #8659: show Stop messages and continue called actions after a pause
A Stop step with its dialog toggle on now shows a Message window with
the recorded text and Continue (when the step allows it) and Stop,
keeping the steps already applied; with the toggle off it ends playback
silently, and Insert Stop turns the toggle on, as measured in the
reference application. Allow Continue is read from the paused playback,
not from the dialog's fields. Agents and headless playback keep the
engine's Stop semantics.

A pause inside a called action now carries its callers (at most 16):
confirming finishes the called action and then each caller after its
call, replayed inside the callers' playback frames so recursion checks
and the single history state still hold. A silent stop ends the callers
too. Fixes #8386.

Co-Authored-By: bommerts <44821497+bommerts@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 04:56:41 +01:00
Tareq HalabyandFredApps 2472b4a46b PSD: keep stylistic sets in Txt2 for PhotoCraft-native documents (#10313)
* PSD: keep stylistic sets in Txt2 for PhotoCraft-native documents.

needs_txt2 now covers Txt2-only OpenType flags (ss01–ss20 and non-default TXT2_FEATURES). Fresh documents get a framed point-text object with full style runs so Set 1/5 round-trip on save/reopen (#10308).

* Txt2: seed fill colour and font list on PhotoCraft-native blocks.

Fresh style runs get a /53 paint dict, and an empty font list gets a CoolTypeFont entry so index 0 resolves (CodeRabbit on #10313).

* Txt2: tag SimplePaint and ensure numbered /0 on legacy blocks.

CodeRabbit: write /99 /SimplePaint on fresh /53 dicts, and create the numbered resource dictionary before pad_frames when a legacy block lacks it.

* Txt2: treat a missing stylistic-set mask as 0 when comparing styles

A style run without key 85 used to agree with any ssNN mask, so enabling
ss01 could leave the file's runs in place on save. Align with import's
default of 0. Also restore the G8 gaps note after rebasing onto main.

* style(io): rustfmt psd_kerning stylistic-sets test

* Txt2: keep Photoshop /0 stable when seeding fonts and frames

Only seed a CoolTypeFont list and inject /0/8 when the block is
PhotoCraft-native or already had those keys / needs new frames, so
unedited Photoshop Txt2 saves stay byte-identical (#2374).

* style(text): rustfmt oversized-template assert

* PSD: keep the Txt2 scaffolding to PhotoCraft-native blocks

A file's own Txt2 block (any Photoshop era) is written as before: no
numbered /0 or /0/8 frame list is injected, a layer without a template
object gets its stub, a run without /53 isn't given one, and no
minimal style base is used. Only a block PhotoCraft creates itself
(prev.is_none()) gets fresh point-text objects, minimal runs, a seeded
font list and a frame list.

Fresh objects now write one /5 paragraph run per paragraph (UTF-16
lengths, checked conversion), as edited objects are refitted.

Tests: Photoshop blocks with an empty or missing /0 get no native
scaffolding; the oversized-template test is back to main's assertion;
fresh paragraph runs; the PSD round trip checks ss01 and ss05 on every
run.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-04 13:28:06 +01:00
David Mann 3d36330d94 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/scorecard.md
2026-11-03 18:08:29 +01:00
d21c403e5f Type: add OpenType Numerals submenu (figure styles) (#11051)
* Type: add OpenType Numerals submenu (figure styles)

Type › OpenType › Numerals sets lnum/onum/pnum/tnum as a radio style
(Default through Tabular Oldstyle). Checkmarks follow the selection
start; TYPE-218-5 scorecard/gaps updated; parity floor 640.

* refactor(engine): one style-at-character lookup for the OpenType menus

feature_on_at and numerals_style_at each walked the runs the same way;
they now share style_at. NumeralsStyle::from_id and numerals_style_of
read the existing tables instead of repeating them.

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

* fix(text): persist OpenType numeral tags through PSD EngineData

Map lnum/pnum/tnum as EngineData bools and the width×height combos as
FigureStyle (Krita/Illustrator), so Numerals choices survive PSD round-trip.

* docs(gaps): note Numerals PSD figure-tag round-trip

* revert(text): keep PSD EngineData to the keys Photoshop writes

Backs out the PSD half of 18610324 and its gaps note (58813de3); the
engine reformat in that commit stays.

- `LiningFigures`, `ProportionalFigures` and `TabularFigures` are not
  Photoshop EngineData keys (the commit says Photoshop only has
  `OldStyle`), so writing them puts keys of our own into users' PSDs.
- The `/FigureStyle` code table (1-4) has no oracle: no Photoshop-authored
  file in photocraft-corpus, and neither ag-psd nor psd-tools reads it.

AGENTS.md rule 3 asks for public specs or observation. PSD persistence
of the numeral styles can come back with a Photoshop-authored oracle.
The Numerals menu still round-trips through .pcraft.

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

* test(text): lock PSD EngineData to Photoshop OldStyle for numerals

Document AGENTS.md clean-room: only onum survives TySh round-trip; lnum/pnum/tnum stay .pcraft-only until a Photoshop-authored corpus oracle.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:55:35 +01:00
David Mann 559d3fe0f4 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/engine/src/cmd/file_cmds.rs
#	crates/ui-egui/src/app/files.rs
#	crates/ui-egui/src/dialogs/tiff_options_ui.rs
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-11-03 17:11:53 +01:00
Yuki-init e3a84b628d TIFF Options: compression, image pyramid and Save Transparency (#8827) (#10932)
Offer TIFF compression, image pyramid and transparency options on Save As and Save a Copy, with matching CLI and automation parameters.

Follow-up: preserve the unsaved state when a copy discards layers over the document's own file, and check the other export formats for the same issue.

Closes #8827.
2026-11-03 15:39:15 +01:00
David Mann 4f6a2c50f0 Merge #4579: the author's latest commits
# Conflicts:
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-03 01:05:27 +01:00
Fabio Lissi a2c4ac0b0c Match Batch Esc flow and show read-only input preview 2026-11-02 17:36:55 -06:00
David Mann 2915849b07 Merge #4514: Record actions in a document-independent form (#219) [depends on #4496]
# Conflicts:
#	crates/engine/src/cmd/type_spell_cmds.rs
#	crates/engine/src/jobs.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-03 00:36:40 +01:00
Fabio Lissi 499f7ab609 Merge upstream main into action recording 2026-11-02 14:44:32 -06:00
Fabio Lissi 2537b927e0 Merge upstream main into batch automation 2026-11-02 14:38:41 -06:00
Fabio Lissi 00f75988b9 Match measured Photoshop Batch naming and error logs 2026-11-02 11:30:58 -06:00
Fabio Lissi 50360ac4ef Record portable nudges, document switches, and type positions 2026-11-02 11:30:37 -06:00
Fabio Lissi 4eba6bd2e9 Merge current main and complete Batch automation options 2026-11-01 23:04:19 -06:00
Fabio Lissi 21c0444559 Merge current main and preserve portable action recording 2026-11-01 23:01:23 -06:00
David Mann 496d9f3623 Merge PR #10618: Map Stylize and Pixelate action events
With our fix-up 52cc86351b on top of the author's head. Conflicts resolved item by item (Rust), docs as unions; docs/atn.md regenerated at the end.
2026-11-02 03:10:21 +01:00
David Mann 5d6041c1cd Merge PR #10628: Map selection, clipboard and layer-operation ATN events
With our fix-up 0e41091687 on top of the author's head. Conflicts resolved item by item (Rust), docs as unions; docs/atn.md regenerated at the end.
2026-11-02 02:55:08 +01:00
yoriswind 33ee38b431 Merge main and preserve the Render gap update 2026-11-01 10:22:26 -08:00
yoriswind 7e0bf9ab6c Merge main and retain both gap log updates 2026-11-01 10:04:57 -08:00
aiongg 713197635d Merge origin/main into prefs/gpu-adapter-typed-read 2026-11-01 13:16:28 +00:00
yoriswind 0a605880dd Separate disabled ATN steps from supported Render counts 2026-10-31 21:11:28 -07:00
yoriswind d55830de94 Preserve noncanonical Group F descriptor headers 2026-10-31 20:57:09 -07:00
yoriswind 815f5bdad0 Preserve measured Trace levels through playback and save 2026-10-31 20:41:15 -07:00
yoriswind ec44d1711f Map ATN selection and layer-operation events 2026-10-31 19:59:52 -07:00
yoriswind 34ba5d0122 Preserve duplicate Render descriptor keys and correct ATN docs 2026-10-31 19:56:58 -07:00
yoriswind 53098879c0 Correct ATN mapping table and current parity notes 2026-10-31 19:42:20 -07:00
David Mann c5ad3ed53b Merge PR #9274: Support editing selected color channel sets
# Conflicts:
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-30 14:28:46 +01:00
David Mann b2db24400e Merge main at b1cfff9d3 into integration batch 10
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
2026-10-30 14:28:02 +01:00
David Mann f5ae6d5276 Merge main into pr-9274-fixup
# Conflicts:
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs/adjust_editors/curves.rs
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-30 13:16:39 +01:00
FredApps 0f120bbda1 Merge current main, reconcile streamed PNG profiles and workflow inputs 2026-10-30 06:52:05 +01:00
FredApps 29bf828ec2 Merge upstream main into reviewed performance integration
Preserve both UI parity status entries; retain all reviewed performance changes.
2026-10-29 23:35:19 +01:00
FredApps 63637c4d80 Merge current main into trimmed integration, preserving exclusions and interpolation behavior 2026-10-29 21:31:18 +01:00
David Mann a3f8ef4816 Merge PR #8536: TIFF: images over 2^30 px open, decoded a group of rows at a time into the layer (#8169)
# Conflicts:
#	docs/file-format-parity.md
2026-10-29 21:06:41 +01:00
David Mann 355831f845 Merge PR #7621: Play .atn steps that switch to another open document
# Conflicts:
#	docs/gaps.md
2026-10-29 20:04:40 +01:00
David MannandClaude Opus 5.5 fefd90d576 Merge remote-tracking branch 'fork/integration-batch-10' into HEAD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:16:12 +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