Resolve conflicts with #5331 (EXR streaming): the block-by-block EXR import now
reads from the same identity-verified handle as the rest of the open path, and
docs/scratch-disks.md keeps both the EXR streaming pointer and the RAM/scratch
admission notes.
Main moved the GPU canvas into canvas/gpu_canvas/* and gained its own
budget, LayerFit and background-redraw machinery. Conflicted files take
main's version; the viewport streaming is re-integrated on top of main's
budget in the following commit.
One RAM detector: the cgroup v1/v2, macOS reclaimable and Windows commit providers move from
photocraft-io into photocraft_raster::memory::system (L0), which the residency budget (#5330)
and photocraft_io::memory both read. Unreadable container limits still fail closed, swap is
never counted as RAM. sysinfo is unified on =0.40.1 (raster only).
While the scratch disk is spilling (on, not paused), import admission credits the tile pixels
of an estimate against spill::scratch_capacity() (free space above the reserve, within the
Scratch Size Limit); decode buffers, conversion copies and merged planes stay RAM-only. Spilling
off/paused and the web keep the RAM-only policy unchanged. PsdMemoryEstimate gains tile_bytes;
the flat TIFF streaming check credits its tile surface the same way; engine approveMemory's
fast pre-check uses the credited hard limit and the import re-checks exactly.
- import_file_for_open_with decodes single-part flat scanline EXR block by block into
tiles; headers the band decoder refuses fall back to the whole-file decoder, so files
main opened still open. Band-time failures and cancellation publish no document.
- The RAM tile budget and scratch-disk checks live in exr_native (the PSD stream module
they came from is dropped); over-budget opens fail with IoError::OutOfMemory.
- The band decoder picks colour channels with exr_color_channels, like the byte decoder.
- Tests move into the per-crate it binaries and use import_file; an engine open-job test
covers the OpenSource::Path route. The ui-egui sync open test is dropped: main's
synchronous open reads bytes, and the io test covers the oversized header.
- docs/streaming-io.md, roadmap, scratch-disks and REL-212-1 describe EXR only.
This reverts commit 89ed4cb284 from the
#5332 branch so the GPU viewport streaming lands standalone. The import
work is rescoped separately in #5331 (EXR streaming only).
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
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.
A scratch write or create failure, or a volume about to drop below max(1 GiB, 5%)
free space, now pauses spilling with exponential backoff (1 s doubling to 60 s)
instead of disabling it for the session. Tiles stay resident, set_budget keeps
the latest budget for the resume, preference changes retry at once, and
spill::epoch lets the engine re-apply History's byte limit while paused.
spill::set_size_cap adds an explicit size limit; free space comes from sysinfo's
disk feature.
One unreadable tile used to block editing, saving and exporting every open document
(spill::check_integrity was process-global). Add Tile/Surface/Document::scratch_health
(free while no tile has failed) and Document::for_each_surface (ops' history walk now
delegates to it). edit_with checks the document before and after, jobs the document they
captured, import the result, save/export the document written; check_since is now
generation-only so a failure or recovery during an operation refuses it once and a retry
works. Caches check their own sources and purge once per new failure instead of every
frame. Fault-injection tests use a duplicated handle (the Unix file has no name).
The spill manager is process-global and never dropped, so the Unix scratch file was only
removed by the next launch's reap. Unlink it immediately after opening and read/write
through the one handle with pread/pwrite (readers can no longer reopen it by path).
Windows keeps delete-on-close and per-thread readers. The abrupt-exit test now asserts
that no file remains on every platform; docs/scratch-disks.md updated.
Resolve conflicts with main's prefs UI split, session/execute.rs move, GPU memory
budget (gpuMemoryMb, 0 = auto, kept from main) and effective_memory_usage_mb history
budget. Port the Memory Usage % and Scratch Disks preferences into prefs_ui, the edit
integrity guard into session/execute.rs, and adapt main's new pixel readers to the
guarded TileBytes API. Cargo.lock regenerated; scorecard regenerated.
* TIFF: open YCbCr images as RGB (#10316)
Scanners often write JPEG-compressed YCbCr TIFFs with 2x2 chroma
subsampling. The tiff crate decodes and upsamples them but returns raw
YCbCr samples, which the decoder rejected with "colour type YCbCr(8)".
The new tiff/ycbcr.rs converts them to RGB after decoding: JPEG data
uses the JFIF full-range BT.601 conversion (as libtiff does), other
compressions honour ReferenceBlackWhite and YCbCrCoefficients with the
TIFF defaults as fallback. 8 and 16-bit. YCbCr with extra samples, or
in other sample formats, fails with a clear unsupported error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Contributors: add Yuki-init's display name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* TIFF YCbCr: test that YCbCrCoefficients set the luma weights
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* TIFF: YCbCr tags with the wrong value count use the defaults
tag_f64s now returns None unless the tag holds exactly n values, so a
YCbCrCoefficients or ReferenceBlackWhite tag with extra values falls
back to the TIFF defaults instead of being truncated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn
* TIFF YCbCr: tests for tiled JPEG with ICC, big-endian 16-bit and planar
Covers the layouts the review asked for: a tiled 4:2:0 JPEG file with an
embedded profile (checked against an independent JPEG decoder), a
big-endian 16-bit file, and planar data in both byte orders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn
* TIFF YCbCr: cite the formula sources and pin the green-channel choice
Documents where the conversion comes from: TIFF 6.0 Section 21
(YCbCrCoefficients, pp. 90-91) and libtiff's tif_color.c
(TIFFYCbCrToRGBInit, TIFFYCbCrtoRGB). Red and blue follow the spec;
green follows libtiff and adds Y unscaled, which matches the spec only
when the luma weights sum to 1. The module doc records this as a
deliberate compatibility choice, and a new test pins it: with weights
0.25/0.25/0.25, mid gray stays (128, 128, 128).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn
* Codecs README: YCbCr TIFFs with three 8- or 16-bit samples decode to RGB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrgj5pnShYtsSnrsZq5iRn
* Format tiff_ycbcr tests with rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(tiff): compare grouped YCbCr decoding and update support notes
* style(tiff): align grouped decode call with test formatting
* style(tiff): apply repository rustfmt to grouped YCbCr regression
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
The doc merge left five Actions rows that disagreed (version 12 unsupported vs read; 120 vs
127 events). Found by CodeRabbit on #10776.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Supported table read from the batch's events.rs. The PRs' own sections (settings and command
options retained, Render defaults, steps kept disabled) are kept; the known-gaps tables that
#10702 and #10704 close are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Distinguish available physical RAM from swap and commit headroom
- Adaptive soft and hard import thresholds with native fallback
- Interactive preflight confirmation modal before pixel decoding
- Headless and command parameters for explicit high-memory approval
- One-shot source-bound validation rejecting altered sources or degraded RAM
- Process-wide serialized decode gate with reentrancy support
- Documentation and synthetic regression test coverage
The Group F loader only mapped descriptors with an empty name and class
`null`, and nested shapes with an empty name. The reference application
names every step descriptor after the localized step (plus NUL), uses
the event code as its class and names nested shapes too, so every real
recorded Add To / Fade / Merge Layers ... step stayed a kept step and
never played (e.g. `addTo` with name "Add To\0", class `AddT` and a
nested `Rctn` named "\0"). The header is no longer checked; the shape
is matched by class only; key, order and enum checks stay strict.
IntW, Dstr and MrgL were registered with the string IDs
`interfaceWhite`, `distort` and none; the real ones are
`intersectWith`, `distribute` and `mergeLayers`, so steps written with
them stayed kept.
Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With our fix-up ea4112a4ce on top of the author's head 686f08c884. docs/atn.md kept from the batch (regenerated at the end); development.md and parity doc rows as unions.
With our fix-up 6ab24508d8 on top of the author's head 8988b6bfd0. Conflicts resolved: import lists as unions (bare_from/bare_to taken from steps_other, #10631), event table, tests and parity doc rows as unions.
With our fix-up 1f524c378b on top of the author's head 713aa92ba5. Resolved with main: the version check sits in main's parse_layout (it merged cleanly); doc comments combined; atn_contract no longer expects version 12 to be refused and pins that an empty version 12 set parses; one ATTRIBUTION row for all ATN fixtures; the fixture README states which files were encoded how; development.md no longer counts the fixtures.
Out-of-range Oil Paint and Smart Sharpen settings kept the step as a
Photoshop step; the reference application clamps them when the step
plays, so the import now clamps (stylization 10.5 -> 10, Amnt 500.5 % ->
500, Rds 0.05 px -> 0.1). Export still refuses out-of-range params.
Oil Paint's light direction (LghD) had no range: LghD 500 mapped and
saved back as 500. It now clamps to the command's -180..180 on import
(500 -> 180) and export refuses {"angle": 181}.
Smart Sharpen mapped the legacy Lens Blur form, which the command's
Gaussian sharpen does not play, and wrote every step as legacy Lens
Blur. It now follows the PSD smart-filter mapping (photocraft-io
smart_map): only useLegacy false + blur GsnB maps (noiseReduction ->
reduceNoise); the reference default (useLegacy true, lensBlur), Motion
Blur and legacy steps stay Photoshop steps; export writes useLegacy
false, Amnt, Rds, noiseReduction, blur GsnB.
Co-authored-by: hectorviov <1501713+hectorviov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With our fix-up 363e0f5e89 on top of the author's head 244082fb2c. Conflicts with main resolved as unions (new tests next to main's; parity doc revision rows).