- gpu: the test modules share one GPU_LOCK, and timing and vector_mask take it too, so one process drives one wgpu device at a time. - ui-egui: the wgpu tests in pattern_preview_canvas, adjust_preview_gpu, marquee_autoscroll and layout_perf take gpu_lock(). - affinity compound_limits: the child process selects its test by the module path (compound_limits::<name>); with --exact, the bare name matches no test and the child passes without running the check. - xtask scorecard reads crates/io/tests/it/corpus.rs. - Docs and comments name the new test paths and the --test it filters.
6.3 KiB
GIMP XCF documents
PhotoCraft opens GIMP's native .xcf files (and .xcf.gz) with their layers, read only. File ›
Open, recent files, drops, the CLI (convert, info, batch) and automation (doc.open, MCP)
use the same reader in photocraft-io (crates/io/src/xcf.rs). A file is recognised by its
gimp xcf signature, so an XCF with another extension still opens as one. Save suggests
.pcraft, which keeps everything PhotoCraft holds; exporting as .xcf fails with a message.
What is kept
| Open | |
|---|---|
| Pixel layers (any size and position, also off-canvas), opaque or with alpha | yes |
| Groups, nested; a group in Pass through mode | yes |
| Names, visibility, opacity, offsets | yes |
| Layer masks | yes, applied or disabled as in GIMP |
| Blend modes | the 2.10 set and the legacy set, mapped to Photoshop's (table below) |
| Locks (alpha, content, position), colour tags | yes |
| Precision | 8 and 16-bit integer as such; 32-bit integer as 16-bit; 16/32/64-bit float as 32-bit float |
| Linear-light precisions | the document gets a linear sRGB (or linear gray) profile |
Colour profile (icc-profile parasite), resolution, guides, comment |
yes |
| Grayscale, RGB | as such; indexed images open as RGB |
| Extra channels, the saved selection | yes (alpha channels, the selection) |
| Text, vector and link layers | as their pixels, with a note |
| Compression | none, RLE, zlib |
| XCF versions | 0 to 26 (GIMP 1.x to 3.2); newer files open with a note, unknown parts skipped |
Not kept, each with a note on open: non-destructive layer effects (GIMP 3's layer filters; the layer opens unfiltered), paths, floating selections, item sets, sample points. Layer effects are not representable without GEGL's operations; paths could follow later.
Blend modes
| GIMP | PhotoCraft |
|---|---|
| Normal, Dissolve, Multiply, Screen, Overlay, Difference, Darken only, Lighten only, Divide, Hard light, Vivid light, Pin light, Linear light, Hard mix, Exclusion, Linear burn, Subtract | the same |
| Addition | Linear Dodge (Add) |
| Dodge, Burn | Color Dodge, Color Burn |
| Pass through (groups) | Pass Through |
| Soft light (both the 2.10 and the legacy one) | Soft Light (GIMP's formula differs a little; noted) |
| HSV Hue / Saturation / Value, HSL Color, LCH Hue / Chroma / Color / Lightness, Luminance | Hue, Saturation, Luminosity, Color (Photoshop's HSL-based modes; noted) |
| Luma darken only, Luma lighten only | Darker Color, Lighter Color (noted) |
| Behind, Grain extract, Grain merge, Color erase, Erase, Merge, Split | Normal, with a note |
How the composite differs from GIMP
Three of GIMP's defaults have no Photoshop counterpart, so a file opened in PhotoCraft can render a little differently from GIMP; the first two are noted on open when they apply:
-
Linear light. Since 2.10 GIMP blends most modes and composites every layer (alpha blending too) in linear light by default. PhotoCraft, like Photoshop, composes in the document's own encoding. Semi-transparent edges, masks and most blend modes therefore render slightly differently: in the test files, a Multiply layer at 60% differs by up to 38/255 in one channel, anti-aliased text edges by up to 73/255 at the edge pixels. A layer whose blend and composite spaces GIMP set to RGB (perceptual) matches PhotoCraft within 2.5/255 (the
-perceptualtest files). -
Clip to backdrop. GIMP composites every mode but Normal with Clip to backdrop: the layer shows nothing where the layers beneath it are transparent. PhotoCraft shows it there. The difference only appears over transparent areas of the stack.
-
Clamping after alpha. For modes whose result can leave 0..1 (Addition, Subtract, Divide, Dodge, Burn, Linear light), GIMP applies the pixel's alpha and the layer's opacity first and clamps at the end; Photoshop clamps the blend result first. Fully opaque pixels at 100% match exactly; translucent ones differ where the raw result overshoots.
Modes without an equivalent open as Normal, and the HSV/LCH/luma modes use Photoshop's HSL-based formulas, so those layers render differently; the note names each layer.
Limits and safety
Written from the public XCF specification (https://developer.gimp.org/core/standards/xcf/) and
files GIMP 3.2 writes; no GIMP code. Every read is bounds-checked and returns an error on a
malformed file, never a panic: canvases at most 300,000 px a side and 1 GiB of decoded pixels,
at most 8,192 layers and 1,024 channels, nesting at most 64 deep, property payloads at most
64 MiB, strings 1 MiB, gzipped files inflating to at most 1 GiB. Tile data is decoded into the
exact size the drawable declares; a run that crosses a tile or a zlib stream that ends early is an
error. The property lengths the specification warns about (PROP_COLORMAP, PROP_USER_UNIT) are
computed from the payload instead of trusted. Truncated files are covered by tests at every cut.
Verification
Unit tests (crates/io/src/xcf/tests.rs) build XCF files from the specification with a small
writer and check every compression, precisions, pointer widths, groups by item path, masks,
channels, the colour map, parasites, limits and malformed input.
Real files come from GIMP 3.2.6 itself: gimp/ of photocraft-corpus
(generator tools/gimp-xcf/xcf_fixtures.py, run inside GIMP), fetched into corpus/gimp/ by
cargo xtask corpus --gimp (28 files, 251 KB): procedural layers the tests regenerate sample for
sample, and for each file GIMP's own merged rendering as a PNG. crates/io/tests/it/xcf.rs (feature
corpus, run by cargo xtask test-corpus -p io) checks the layer data against the formulas and our
composite against GIMP's:
| File | Composite vs GIMP |
|---|---|
layers-8bit-perceptual, mask-perceptual, text-perceptual |
within 2.5/255 everywhere |
gray16, indexed, channel |
within 2/255 |
groups-perceptual |
within 2.5/255 except where Clip to backdrop applies (15% of pixels) |
legacy-modes-perceptual |
Addition, Subtract, Divide, Dodge, Burn, Hard light, Exclusion, Linear light within 2.5/255 on opaque pixels (translucent ones differ by the clamping order above); the approximated modes reported |
| the default (linear-light) files | the known gap, measured: up to 38/255 (Multiply), 59/255 (mask edge), 73/255 (text edge) |