Commit Graph
169 Commits
Author SHA1 Message Date
Triwarr a3778b4476 PDF: address editable-import review findings
- Colours: AlphaColor components are straight, not premultiplied; use
  them directly instead of dividing by alpha (the opacity test now
  checks RGB, not just alpha).
- Text: key families by font content key, not PostScript name, so two
  faces with one name cannot share a family. Keep text layers at the
  path index where their glyphs were painted instead of appending them
  after all paths.
- Correctness: reject bow-tie clip paths, start a new subpath after
  close, fall back on unparseable/unsupported glyph mappings, truncated
  content, failed font registration, and shape-cache budget exhaustion.
- Cancellation: check between text-cache renders as well as between
  objects.
- Include Bezier control points in the cache-size estimate; cache the
  content hash per Hayro font identity.
- Docs: ImportOptions/CLI help now describe editable text and the
  embedded-font caveat. Tests: text cache is not blank, paint order,
  bow-tie clip, line after close, cancellation between objects.
2026-11-08 06:14:24 +01:00
Triwarr 80a254050b PDF: editable import step 1 - paths as shape layers
Add an experimental, opt-in PDF path importer on craft-pdf's interpret
Device. Fills and strokes become ShapeLayers in document pixels with
their real colours, widths and dash patterns; text, images, clipping
paths, transparency groups, patterns, soft masks and annotations keep
the raster page with a warning, so a page is never half imported.

- ImportOptions::pdf_editable (off by default) and --pdf-editable.
- pdf-editable Cargo feature in photocraft-io; the CLI enables it.
- Shape caches are rendered within a 256 MiB page budget.
- Cancellation is forwarded through a 5 ms watcher; hostile pages are
  caught and fall back instead of panicking.

Tests cover fill/stroke colours and dashes, composite parity with the
raster page, text fallback and cancellation.
2026-11-08 05:02:23 +01:00
Triwarr 2d16a609ea Merge remote-tracking branch 'origin/main' into pdf
# Conflicts:
#	docs/scorecard.md
2026-11-08 03:48:47 +01:00
50b877645f Merge origin/main into feat/cli-metadata-option (#9975)
Conflicts with main's --no-maximize-compatibility (#8064) in the CLI
export args, output mapping and dev-dependencies, and in the
file-format parity status line and revision history: both sides kept.

Co-Authored-By: ljkx <ljkx@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:26:47 +01:00
Triwarr d297dd3a2d PDF: validate --pdf-dpi, band the raster conversion, complete the notices
- --pdf-dpi now rejects zero, negative, NaN and infinite values at parse time (unit-tested) instead of silently falling back to 300 DPI.
- The RGBA8 raster is converted and written in 256-row bands with a cancellation check per band, so the f32 buffer no longer duplicates a whole page; the pixel-memory estimate charges the retained raster instead of that buffer.
- docs/file-format-parity.md: the 2026-11-07 revision row is inside the revision table.
- NOTICE now reproduces the Hayro MIT notice, the Foxit/PDFium BSD-3-Clause text, Adobe's CMaps BSD-3-Clause text and the CC0 statement for the ICC profile; package.ps1 ships NOTICE and ATTRIBUTION.md in both installers.
2026-11-07 20:12:46 +01:00
Triwarr 5cb7c5a9ab Merge remote-tracking branch 'origin/main' into pdf
# Conflicts:
#	docs/scorecard.md
2026-11-07 19:27:23 +01:00
Cristi RaduandClaude Opus 5.5 0c449729d7 Say Bitmap, Indexed Color and Duotone keep the merged image (#8064)
The PSD writer always writes the real merged image for those modes (Bitmap and
Indexed are saved without layers; Duotone is exempt), so maximizeCompatibility
false does not give them the placeholder. The MCP params, control protocol and
CLI help now say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 21:07:10 +02:00
Cristi RaduandClaude Opus 5.5 a1bb9f003f Saves take maximizeCompatibility for a PSD/PSB's merged image (#8064)
Photoshop's Maximize Compatibility checkbox, for one save: `maximizeCompatibility`
(true or false) on Save, Save As, Save a Copy, `app.save`, serve `doc.save`, MCP
`doc_save`/`doc_export`, Batch, Image Processor and Layers to Files decides the
merged image over Preferences > File Handling > Maximize PSD and PSB File
Compatibility; unset, the preference decides. Anything but a boolean is refused
before anything is written. The CLI takes `--no-maximize-compatibility`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 21:07:10 +02:00
Triwarr 244a6287ab PDF: import options (page range, DPI, editable text/vector) and DLL packaging
- ImportOptions gains pdf_pages (1-based inclusive), pdf_text and pdf_vectors;
  the importer maps PDF text objects to editable text layers and vector paths to
  shape layers through the effective DPI.
- photocraft-cli convert gains --pdf-dpi and --pdf-pages.
- Preferences gain pdf_dpi/pdf_pages, wired through jobs::import_options.
- apps/photocraft/build.rs copies PDFIUM_DLL beside the exe; the MSI installs it.
2026-11-06 19:15:09 +01:00
David Mann 37f9d3b7d4 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/codecs/src/codecs/png.rs
2026-11-05 17:18:28 +01:00
Khawar Zamman Wani f54d605e2c Add recipe-driven checks for PSD exports (#10307)
* Add recipe-driven PSD export checks

* Exercise PSD recipe runner with the headless CLI

* Make PSD oracle tests isolate temporary paths

* Keep inspect output unchanged; PSD write runner as an xtask library

- info --detail adds text runs, anti-aliasing and full layer-effect settings;
  the control channel and MCP inspect output is unchanged.
- The CLI test uses xtask's new library target instead of #[path] includes.
- The runner records revision and dirty state as unknown outside a git checkout.

* psd-write-oracle: name the layer in each check, document versioning, add recipe tests

* Clean up PSD oracle test fixtures on failure with tempfile::TempDir and add docstrings
2026-11-05 17:11:56 +01:00
David MannandClaude Opus 5.5 42e92cb512 Address review: Batch stops, rename targets, Shape Blur memory, sampler releases
- A direct Batch in a script or a CLI command list stops it when it is cancelled,
  cannot write its error log, or ran out of script steps; per-file errors alone
  still continue (CodeRabbit, outside-diff findings on automate_cmds.rs and
  photocraft-cli document.rs).
- Recorded Save steps count against the script's step budget, and a Batch whose
  files failed with the budget spent carries scriptLimitError, so Browse reports
  the failure instead of ok: true (yoriswind).
- actions.rename rewrites a named Batch, nested Play or Conditional call only if
  it resolved to the renamed action before the rename; a Batch with explicit
  steps is left alone (yoriswind: two same-named actions in different sets).
- Shape Blur's run admission counts each worker's f64 prefix sums and, for a
  custom shape, the kernel mask kept for the run plus its worst-case coverage
  runs (yoriswind).
- Every sampler that takes the primary release (Color Picker, Curves/Levels and
  Properties eyedroppers, Curves Cmd-click, Color Range) ends a transform handle
  drag and a Move drag begun before it, not only the Color Picker (#11210 review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:20:01 +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
Khawar Zamman WaniandFredApps 13b481f892 Expose optional GPU composition and export in photocraft-cli (#10912)
* Add headless GPU backend foundation for composition and flat export (issue #8062 PR 1)

* Fix headless GPU readback bounds, API compliance, and error preservation (#8062)

* Fix GPU readback bounds, context thread spawn error, and test parity (#8062)

* Fix BandRenderer Send bound, PNG reborrows, and clippy lints (#8062)

* Fix effect shadow test construction and remove dead code wrappers (#8062)

* Use short reborrows for Headless renderer without lifetime escaping (#8062)

* Fix clippy collapsible_if and ensure multi-band geometry in flat export tests (#8062)

* Fix automation Rect import, add renderer diagnostics, and harden flat export retries (#8062)

* Expose optional GPU rendering in photocraft-cli (#8062)

* Fix GPU feature test APIs and native target gates

* Fix CLI locked metadata, bounded benchmark params, and protocol diagnostics (#8062)

* Verify unsupported GPU effects before testing band seams

* Fix CLI renderer borrows and GPU-enabled test compilation

* Use compositor buffer ownership API after upstream memory accounting change

* Fix GPU CLI lint failures and validate run environment policy

* Update export renderer test helper for owned compositor buffers

* Fix needless benchmark layer name borrow

* Fix headless GPU backend review findings (#10842)

- release a closed document's GPU caches through a BandRenderer hook
- bound the monitored readback wait and report a stalled GPU as device loss
- reject 32-bit float documents before band readback so exports fall back to CPU
- keep previews at 8-bit RGBA and report the palette/Duotone renderer bypass
- retain renderer diagnostics when encoding fails
- strengthen renderer, save-path and GPU diagnostics tests

* Use valid batch arguments and inspect method in GPU CLI tests

* Add GPU close-hook and 8-bit preview regression tests (#10842)

* Fix GPU CLI review findings (#10912)

- guard the benchmark's modulo divisors for one-pixel dimensions
- count a cold export as GPU only when the renderer context was used
- report every batch file's export warnings, deduplicating only GPU diagnostics
- drain renderer diagnostics before propagating convert and run save errors
- cover renderer use in the GPU precedence, bridge and serve tests

* fix(io): correct renderer binding mutability

* test(cli): verify narrow benchmark inputs and GPU diagnostics

* Fix remaining headless GPU review findings (#10842)

- release a document's GPU caches when file.close, file.closeAll or
  file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
  requested renderer
- keep fallback warnings in their export instead of leaking them into the
  next one
- requeue renderer diagnostics when a renderer-aware save's final write
  fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
  depth list

* Fix remaining headless GPU review findings (#10842)

- release a document's GPU caches when file.close, file.closeAll or
  file.closeOthers removes it through a command
- honor cancellation while band readback waits, polling in 100 ms slices
- fault the device after a readback timeout so later exports skip it
- report single-layer, CMYK ink-mixing, ORA and PDF CPU paths to a
  requested renderer
- keep fallback warnings in their export instead of leaking them into the
  next one
- requeue renderer diagnostics when a renderer-aware save's final write
  fails
- write MCP/serve diagnostics without panicking on a broken stderr
- document the actual colour-space contract and drop F32 from the GPU
  depth list

* Fix remaining GPU CLI review findings (#10912)

- reject non-UTF-8 PHOTOCRAFT_GPU values instead of silently using the CPU
- release a batch item's document caches before taking the renderer to the
  next item, and report fallbacks once
- count warm benchmark runs that fell back to CPU in the summary
- write serve diagnostics without panicking on a broken stderr
- assert that the batch test's --gpu policy reaches the renderer

* Use question mark for cancellable readback errors

* Use question mark for cancellable readback errors

* Keep GPU export diagnostics with their attempt and stop bands on cancel

Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.

Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.

Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.

Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.

* Keep GPU export diagnostics with their attempt and stop bands on cancel

Encode native .pcraft bytes before draining renderer diagnostics, and
requeue export warnings when a workspace write fails, so callers still
report the adapter or fallback reason with the error.

Move a failed GPU attempt's queued diagnostics into its CPU retry result
instead of leaving them for a later successful export to report.

Stop GPU band readback at the next band after cancellation, and check the
interrupt before each CpuRenderer sink delivery.

Band readback uploads half-precision textures, so reject U16 alongside
F32: their samples cannot survive the upload. Tighten the 8-bit CPU/GPU
parity checks to 1/255.

* Report partial warm fallbacks and CPU fallback on stderr

The averaged GPU Warm row now says how many warm runs fell back to the
CPU and were excluded from the average, through the existing helper.

The single-layer convert diagnostic test checks that the CPU fallback is
reported on stderr, in GPU-enabled and CPU-only builds alike.

* Keep headless export warnings when a workspace write fails (#10842)

A capability-scoped save encodes the export, then writes it; when the write
fails, the export warnings (folded together with renderer diagnostics) were
pushed back through `BandRenderer::push_diagnostic`, whose default is a no-op.
A custom renderer that overrides only `drain_diagnostics` therefore lost the
adapter or fallback reason that belonged with the error.

Keep them in `Headless` instead and return them from the next
`drain_diagnostics`, alongside the renderer's own queue. The regression uses a
renderer without a `push_diagnostic` override and checks the warnings survive
the failed write and drain exactly once.

Also name the actual source of the depth limit in the headless GPU matrix:
layer content is uploaded as half-precision textures, not band readback.

* Stop CPU bands and CPU retries on cancellation (#10912)

The CPU retry after a retryable GPU error composes through
`FlatSource::bands`, whose shared `emit` sink did not poll the export
interrupt: a cancel was only observed after the whole composite finished.
Check it once per band, as `CpuRenderer` does, for both RGB and CMYK ink
bands. The flat and streamed-PNG retry branches also check before starting
the full CPU composite again, so a cancelled export returns
`IoError::Cancelled` instead of re-rendering the document.

Tests: band-sink cancellation for RGB and CMYK inks, a renderer call counter
proving a second export reaches the renderer, and a pending `wait_slices`
poll observing cancellation set mid-wait.

* Merge origin/main into GPU foundation (#10842)

* Keep GPU-path diagnostics on every failure path, honour cancel in native copy

- save: export warnings of a failed local write stay in Headless pending diagnostics
- render: preview fallback warnings are returned, drained, and added to the doc.render reply
- io: renderer-neutral fallback wording; native single-layer copy checks cancellation per band
- gpu: gate wait_slices tests off wasm32 (#10842)

* Address CodeRabbit review: keep fallback reasons on failed retries, report native-copy and quick-export CPU use, guard diagnostics drain (#10912)

* Accept render diagnostics from the doc.render reply in the serve GPU test (#10912)

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 05:39:59 +01:00
yoriswind 940304b389 Compare CLI Batch output identity across native path separators 2026-11-04 11:39:13 -08:00
yoriswind 1457ea39ba Preserve CLI run continuation after direct Batch file errors 2026-11-04 11:15:56 -08:00
ljkxandClaude Opus 5.5 79b8073a8d Merge origin/main into feat/cli-metadata-option
Resolve docs/file-format-parity.md: keep main's new revision-history rows and this PR's row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:38:24 +01:00
ljkxandClaude Opus 5.5 516c70073e Merge origin/main into feat/cli-metadata-option
Resolve apps/photocraft-cli/src/args.rs: keep both ValueError variants (Metadata and main's OnOff).
Resolve docs/file-format-parity.md: keep main's entries and re-add this PR's status entry, table notes and history row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:03:15 +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 MannandClaude Opus 5.5 a0418a2e29 Address CodeRabbit review: Batch failures, late Esc, CLI format, bridge result
- A failed input stays in `errors` when `jobs.cancel` or Esc lands while it runs, instead of
  showing up as skipped without its reason.
- Esc after the last input is refused: the worker closes the interrupt window once no
  checkpoint is left, so `request_batch_interrupt` no longer answers true for a question
  that never comes.
- `photocraft-cli batch` without `--format` saves a file PhotoCraft only opens as PSD, the
  format `--naming`'s {ext} already plans, instead of asking the encoder for the input's
  import-only extension.
- `--recursive` help says the output is flat.
- The app bridge keeps a cancelled Batch's partial result in the error it returns to MCP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:50:52 +01:00
David Mann 7329c700bb Merge remote-tracking branch 'origin/main' into integration-batch-13
# Conflicts:
#	apps/photocraft-cli/src/args.rs
#	crates/engine/src/jobs.rs
#	crates/engine/src/jobs/tests.rs
2026-11-03 13:52:30 +01:00
ljkx a112beda7e CLI --metadata test: the export keeps the input's own ICC profile
Review nitpick (#9466): the test only checked that the default export had some ICC profile and that `none` matched it, so a regression writing sRGB instead of the input's Display P3 would still pass. It now compares the default export's profile with the input's bytes.
2026-11-03 11:45:00 +01:00
ljkx a99e8696fc CLI --metadata: qualify the docs per format, test text and ICC (#9466)
Review follow-up on #9975. The help text and the CLI book page said the
XMP and free-form text are kept in every flat export and the ICC profile,
EXIF and resolution "either way". Not every format holds them: JPEG and
WebP have no place for free-form text, TIFF writes only its named text
tags, and an Indexed Color document's palette PNG carries no XMP, text,
ICC or EXIF at all, only its resolution. The texts (and the parity-doc
revision row) now say the XMP and text are kept where the format holds
them and that the option leaves everything else as it is without it.

The test fixture gains a free-form text entry and a Display P3 profile
(from photocraft-cms, now a dev-dependency; no profile file committed),
so it checks the text is gone with `none` and kept by a default PNG, and
that the default export keeps a real ICC profile that `none` matches,
instead of comparing None with None.
2026-11-03 11:44:55 +01:00
ljkx b5770e4283 CLI: --metadata none|all for convert, run --out and batch (#9466)
Since #733 a flat export from the CLI keeps the document's whole XMP
packet and free-form text, as Save As does; only Export As starts at
Metadata: None. Batch exports of Photoshop masters therefore carry the
full DocumentAncestors list (and the text of every type layer), which
can be many times larger than the image, with no way to leave it out.

`--metadata` is a new export option shared by `convert`, `run --out`
and `batch`. `none` sets ExportOptions::xmp to XmpEmbed::None, the same
path Export As's Metadata: None and Quick Export use, so the XMP packet
and free-form text are dropped while the ICC profile, EXIF and
resolution are written as before; PSD, PSB and .pcraft keep the XMP
either way. `all` is the default, so existing scripts write the same
files. Any other value is a usage error (exit 2) naming the flag,
before anything is read or written.

Tested through the binary for PNG, WebP, JPEG and TIFF from all three
subcommands (packet present by default and with `all`, absent with
`none`; pixels, ICC and resolution unchanged), layered saves, and bad
values. Documented in the CLI book page and file-format parity doc.
2026-11-03 11:44:39 +01:00
Khawar Zamman Wani 259b5590ae Merge origin/main (655295b2a2) into PR #10897 branch
Keep the PSD memory admission work and upstream's scratch storage
together. Conflicts resolved:
- Cargo.lock: both sysinfo versions are required. 0.40.1 serves
  photocraft-io's adaptive import budget; 0.37.2 serves
  photocraft-raster's scratch volume and memory supervision. Cargo
  metadata resolved the lock with no other changes.
- crates/engine/src/lib.rs: Session keeps pending_preflights and
  next_pending_preflight beside scratch_runtime and scratch_epoch.
  Session derives Default, so neither set needs a manual initializer.

Upstream: 655295b2a2
2026-11-03 12:54:59 +05:30
David MannandClaude Opus 5.5 3ce04b52f4 Address CodeRabbit review: Batch writes, recording and dialog edge cases
- CLI batch: recursive inputs keep the engine's tree order for {serial};
  --format picks the encoder whatever extension --naming writes; the
  summary prints before --error-log is written.
- Batch: a jobs.cancel during a write keeps that file in `files`;
  Opened Files refuses a default-named result that replaces another open
  document's file; a recorded Save leaves a document changed when Batch
  writes nothing for it.
- Recording: folded Move-tool nudges add whole-pixel steps; a null
  Check Spelling `action` is recorded as Check Spelling.
- Linked files: a document without links lists none missing; a look
  whose links changed while it ran is dropped.
- Save for Web: a null or empty destination opens the picker; a rename
  while the picker is open writes the picked file.
- Actions: a Check Spelling step fails instead of replacing a dialog
  opened by hand.
- Docs: desktop Batch cancel flow, Check Spelling step shape, filter row
  history count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 03:02:19 +01:00
dotCooCoo fef15729aa Merge main into feat/pcraft-full-composite
Main now takes the manifest envelope from craft-pcraft-format, which has
no full_composite member. The save writes the member after the envelope
when it writes composite/full.png, and read_full_composite reads it back.
2026-11-02 17:02:14 -08: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 180cbe651a Merge current main into Batch branch 2026-11-02 10:18:27 -06:00
Khawar Zamman Wani e6a0b9ad20 fix: validate memory approvals before I/O and remove prompt panic 2026-11-02 19:28:00 +05:30
Khawar Zamman Wani 582a285b5d fix: enforce scoped PSD memory admission and complete retry integration 2026-11-02 19:01:15 +05:30
David Mann 4c95a2d4bd Merge main 5d9c57e60d into integration batch 12 2026-11-02 13:54:27 +01:00
Khawar Zamman Wani 5d9c57e60d Linux: return memory of closed documents to the system (#8743) (#10515)
* perf: reuse large buffers so a pinned glibc trim threshold costs no speed (#10479)

With MALLOC_TRIM_THRESHOLD_ set (#8743), glibc maps every allocation over
128 KiB fresh, so tiles, compositor bands and filter scratch were page-faulted
in on every call: viewport compose 3.6x slower, Gaussian Blur up to 1.8x,
.pcraft save 2.4x.

- photocraft_raster::scratch: a process-wide, capped pool of large Vec
  buffers (u8/u16/f32/[f32; 4]). A miss drops the oldest buffers worth its
  size down to a 256 MiB floor, so peak memory stays near glibc's defaults.
  The engine releases the pool after any command that closes a document,
  Headless::close does too, and an idle thread releases it after 2 s unused.
- Tiles, compose Buffer/masks/bands, algo Image and the blur paths, engine
  adjustment/filter scratch, GPU tile upload and io import buffers go through
  the pool.
- .pcraft save reuses one zstd FrameCompressor per worker (byte-identical).
- interactive_bench reports minor page faults per run.

36 MP, trim threshold set vs glibc defaults: viewport 5.6 vs 6.1 ms (was
22.7), Blur r4 445 vs 437 ms (was 852), Curves CPU proxy 35.0 vs 35.7 ms
(was 50), flatten 146 vs 147 ms (was 237), save .pcraft 251 vs 230 ms (was
545). RSS after closing a 5400x7200 document: 21 MB with trim set.

* fix(linux): restart once with glibc's trim threshold set so closed documents return memory (#8743)

With glibc's default malloc, the memory of a closed document stays with the
process: freeing a large buffer raises glibc's dynamic mmap threshold, so
later tile buffers land in heap pages glibc keeps. A session that opened and
closed a 5400x7200 document held ~450 MiB afterwards.

photocraft_engine::malloc_tuning::apply(), called first in both binaries'
main, re-executes the binary once with MALLOC_TRIM_THRESHOLD_=131072 on Linux
with glibc: same PID, arguments, argv[0] and environment, in safe Rust
(CommandExt::exec). A malloc variable or glibc.malloc.* tunable the user or a
package already set is left alone, so setting MALLOC_TRIM_THRESHOLD_ opts
out. No-op on macOS, Windows, musl and wasm.

Depends on the scratch-buffer pool (#10479), without which the pinned
threshold slows filters, previews, flatten and .pcraft save by 26-209%.

* fix: corpus tests build again; return remaining pooled buffers (#10479 review)

- Corpus-gated io tests and the Photoshop-oracle test moved `.px` out of a
  `Buffer`, which now implements `Drop` (E0509 with `--features corpus`):
  use `into_px()`.
- io: masks, the merged image, extra channels, smart-filter masks,
  smart-object pixels and patterns give their `interleave` buffers back to
  the scratch pool after writing them.
- compose: recycle the mask values of a pass-through group, `effect_shape`
  and both `split_parts` callers.
- Styled Free Transform preview copies each band out instead of moving it,
  so the band returns to the pool.
- interactive_bench: the fault count covers each whole run, untimed setup
  included; say so.

* fix: corpus tests build again; return remaining pooled buffers (#10479 review)

- Corpus-gated io tests and the Photoshop-oracle test moved `.px` out of a
  `Buffer`, which now implements `Drop` (E0509 with `--features corpus`):
  use `into_px()`.
- io: masks, the merged image, extra channels, smart-filter masks,
  smart-object pixels and patterns give their `interleave` buffers back to
  the scratch pool after writing them.
- compose: recycle the mask values of a pass-through group, `effect_shape`
  and both `split_parts` callers.
- Styled Free Transform preview copies each band out instead of moving it,
  so the band returns to the pool.
- interactive_bench: the fault count covers each whole run, untimed setup
  included; say so.

* fix: recycle the masks of adjustment-atop and shape layers (#10479 review)

* fix: recycle the masks of adjustment-atop and shape layers (#10479 review)

* fix: recycle effect hiding masks (#10479 review)

* fix: recycle effect hiding masks (#10479 review)

* fix: recycle knockout, CMYK and GPU shape masks; release the pool when Split/Merge Channels close a document (#10479 review)

* fix: recycle knockout, CMYK and GPU shape masks; release the pool when Split/Merge Channels close a document (#10479 review)

* fix: release the pool when a closed document's cancelled workers exit; idle-release switch for tests; don't pool PSD planes (#10479 review)

* fix: release the pool when a closed document's cancelled workers exit; idle-release switch for tests; don't pool PSD planes (#10479 review)

* fix: release the pool for workers cancelled before their document closes; take the pool lock for budget and idle-release changes (#10479 review)

* fix: release the pool for workers cancelled before their document closes; take the pool lock for budget and idle-release changes (#10479 review)

* fix: idle release rechecks activity under the lock; try_take_exact releases the pool and retries before failing (#10479 review)

* fix: idle release rechecks activity under the lock; try_take_exact releases the pool and retries before failing (#10479 review)

* test: cover a successful try_take_exact miss; name the failed-reservation test for what it checks (#10479 review)

* test: cover a successful try_take_exact miss; name the failed-reservation test for what it checks (#10479 review)

* fix: pool the final GPU effect shape buffer and return it after use (#10479 review)

* fix: pool the final GPU effect shape buffer and return it after use (#10479 review)

* docs: describe scratch pool lifecycle regression invariants

* test: verify polling releases cancelled document scratch buffers

* perf: recycle remaining transform selection and PSD scratch buffers

* fix: free PSD gather bands before allocating channel planes

* test: assert exact scratch take reuses pooled storage
2026-11-02 09:30:46 +01:00
Fabio Lissi dc28eb5083 Stream batch directory scans and report cancellations accurately 2026-11-02 00:25:43 -06:00
Fabio Lissi 85cb54025c Guard Batch outputs and preserve partial results 2026-11-01 23:56:10 -06:00
Fabio Lissi 4eba6bd2e9 Merge current main and complete Batch automation options 2026-11-01 23:04:19 -06:00
1f524c378b Integration: track the v12 .atn fixtures and sniff v12 in the CLI (#10743)
The three version 12 fixtures were tracked inside crates/psd/fuzz/corpus,
which crates/psd/fuzz/.gitignore ignores and cargo-fuzz rewrites (cmin) or
deletes; the psd and engine test binaries include_bytes! them, so cleaning
the corpus broke the build. They move to crates/psd/tests/fixtures/atn with
a README (sizes, purpose, sha256; the seeds note folded in) and an
ATTRIBUTION.md row; the fuzzer takes the folder as an extra corpus dir.

photocraft-cli recognised an extensionless Actions file only by version
word 16, so a version 12 set without .atn (classic Mac OS Photoshop 5)
went down the JSON path and failed with a UTF-8 error. It now accepts 12
and 16, the versions photocraft-psd reads. Input: v12-commands.atn copied
to "Legacy Actions" and passed to load_actions_file.

Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 02:09:41 +01:00
dotCooCoo f121865ffa Docs: a .pcraft save can leave the full-size composite out
The doc.save row, docs/development.md, the MCP doc_save description, the
file.saveACopy params text, the --pcraft-full-composite help and the
option's doc comments said the option writes composite/full.png. A save
leaves it out when the canvas is past 2^26 pixels, when its pixels cannot
be allocated or encoded, or when a ZIP bundle would pass its limits. They
now say so and point to the manifest's full_composite member, which names
the entry when it was written.
2026-10-30 08:20:23 -07:00
dotCooCoo 49d12c57bf Merge main into feat/pcraft-full-composite
# 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/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
2026-10-30 06:26:41 -07:00
Federico Olivares 796ef557cc Merge main into the integration batch
Main's noop_step now takes the step's params (an Arrange that cannot move the layer is a no-op too); it is public for the CLI's run and batch, which pass them.
2026-10-29 14:19:54 -03:00
dotCooCoo b527b5062a Save the flattened document at full size into .pcraft files as an option
A .pcraft bundle holds the flattened document only as thumb.png (at most
256 px) and composite/preview.png (at most 1024 px). A new option also
writes composite/full.png: the flattened document at its canvas size,
an 8-bit RGBA PNG made like the two previews, with the ICC profile of
its pixels embedded. The option is off by default, and a save without
it writes the same bytes as before.

- Preferences > File Handling > Save Full-Size Composite in .pcraft
  Files sets it for the app's Save, Save As and Save a Copy, in the
  desktop and web builds.
- The engine's file.saveACopy and its batch and export commands take
  "pcraftFullComposite" (unset, the preference), the automation
  doc.save and MCP doc_save take
  "pcraftFullComposite": true, and photocraft-cli convert, run and
  batch take --pcraft-full-composite.

The manifest names the image in a new optional member, full_composite;
the format version stays 1, and bundles without it open as before. A
canvas over 2^30 pixels, or one whose pixels cannot be allocated, is
saved without it, and so is a ZIP bundle that it would take past 4 GiB
or 65,535 entries.
2026-10-27 17:51:43 -07:00
Federico Olivares e173a9be17 Merge main into the integration batch
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.
2026-10-27 10:44:16 -03:00
skylar taylor-barrick d641b96a52 Merge pull request #9518 from storytold/codex/egui-037-photo-followup-20261025
Preserve locale-aware CJK fonts and migration regressions after #9363
2026-10-26 13:16:03 -06:00
Federico Olivares 8aaa03e2db Merge main into the integration batch 2026-10-26 11:52:45 -03:00
skylar taylor-barrick bf6e25af9a Use relocated nextest binaries in process integration tests 2026-10-26 01:20:00 +01:00
David Mann bb39b4a8dd Revert "Merge PR #1199: Add optional local models for subject matting and object selection"
This reverts commit 5f2a7b9903, reversing
changes made to 9ef4c0a487.
2026-10-25 15:19:27 +01:00
David MannandClaude Opus 5.5 5f2a7b9903 Merge PR #1199: Add optional local models for subject matting and object selection
Merged with our fix-up d5139d177 (pr-1199-fixup), which merged main as of 8f09d122d.
Conflicts with main since then and with batch 8:
- photocraft-cli: main split the CLI into clap modules (#7071); the model wiring
  (local_headless, configure_cli_models, batch's shared backend, droplet) is re-applied there.
- Cargo.lock regenerated: crates/ml pins ureq =3.4.2, so the app's ureq goes 3.5.0 -> 3.4.2.
- cutout_cmds: main's subject area (#8235) kept for the classical path beside the model path.
- file_cmds batch: main's per-input closure takes `s` later, so the backend and
  integrations prefs are read before the closure instead of inside it.
- prefs_ui: model-choice filter with #4573's pref_choice_label; Integrations stays visible
  (#4097) with the model choices only where models run; the translation-coverage test main
  deleted (c3aa3f9e5) stays deleted; old section_fields test calls take PreferenceFieldOptions.
- Semantic clashes git did not flag: #4097's Integrations test literals in
  apps/photocraft take ..Default::default() for the new model fields; main's and #4573's
  section_fields test calls pass PreferenceFieldOptions.
- engine lib.rs, prefs.rs, smartselect tests, snapshot example, jobs_ui_tests, people.toml,
  docs and i18n catalogs merged as unions.


Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 14:23:25 +01:00
David MannandClaude Opus 5.5 8c15e55cbf Integration: merge main, resolve conflicts (#3567)
panels.rs: the PR's snapshot rows next to main's History Brush source
and scroll-to-current; ui-parity.md: main's status line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 13:36:40 +01:00