mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
- 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.
87 lines
5.4 KiB
Markdown
87 lines
5.4 KiB
Markdown
# OpenRaster documents
|
||
|
||
PhotoCraft opens and saves OpenRaster (`.ora`), the layered format Krita, MyPaint, GIMP, Pinta
|
||
and others share. File › Open, recent files, drops, Save and Save As, the CLI (`convert`, `info`,
|
||
`batch`) and automation (`doc.open`, `doc.save`, MCP) all use the same reader and writer in
|
||
`photocraft-io`. The browser build accepts `.ora` too. A file is recognised by its `mimetype`
|
||
entry, so an OpenRaster file with another extension still opens as one.
|
||
|
||
## What is kept
|
||
|
||
| | Open | Save |
|
||
|---|---|---|
|
||
| Pixel layers (any size and position, also off-canvas) | yes | yes, cropped to their pixels |
|
||
| Groups, nested | yes | yes |
|
||
| Pass-through groups | `isolation="auto"` (before 0.0.6, only without a blend mode) | `isolation="auto"` |
|
||
| Names, visibility, opacity | yes | yes |
|
||
| Blend modes | the `svg:*` names, `svg:plus`, and Krita's `krita:*` names | `svg:*` where the spec has the mode, else `krita:*` (read back by Krita, Normal elsewhere) |
|
||
| Locks | `edit-locked`, `alpha-preserve` | the same |
|
||
| 8 and 16-bit | yes (the deepest layer sets the depth) | 8-bit documents as 8-bit PNGs, 16 and 32-bit as 16-bit PNGs |
|
||
| Resolution | `xres` | `xres` and `yres` |
|
||
| Merged image and thumbnail | not read (we render our own) | written (`mergedimage.png`, `Thumbnails/thumbnail.png`) |
|
||
|
||
Files declaring OpenRaster 0.0.6 or newer default to isolated groups; an explicit
|
||
`isolation="auto"` passes through regardless of the group's own composite operation. Older
|
||
files and files without a recognized version keep the legacy inference from the group's
|
||
blend mode. Pass-through group opacity still uses PhotoCraft's interpolation against the
|
||
backdrop, which can differ from 0.0.6's per-child opacity multiplication for overlapping children.
|
||
|
||
Saving reports what OpenRaster can't hold, layer by layer: pixel masks are applied to the
|
||
layer's pixels, fill opacity is folded into the pixels, text, shape, fill and smart object layers
|
||
are written as their rendered pixels, and adjustment layers, layer styles, vector masks,
|
||
clipping, alpha channels, metadata and a colour profile other than sRGB are left out. Xor and Paint.NET's
|
||
Color Burn and Color Dodge variants have no OpenRaster name and are saved as Normal. CMYK, Lab
|
||
and other modes are refused with a message to convert first; Grayscale saves as RGB.
|
||
|
||
An OpenRaster file is the document's own file only while it holds everything in it (#3806).
|
||
`photocraft_io::ora_losses` lists what a save would leave out, from the same checks the writer
|
||
warns with, without encoding anything. When that list isn't empty, File › Save doesn't overwrite
|
||
the `.ora`: Save As opens on a PSD of the same name, with a notice listing what OpenRaster would
|
||
drop. A save to an `.ora` path then writes a copy, as Photoshop does for formats that can't keep
|
||
everything: the document keeps its file and its unsaved changes. Automation follows the same rule
|
||
(`doc.save` and `app.save` without a path refuse, saying why).
|
||
|
||
OpenRaster elements other than `stack` and `layer` (MyPaint's text, for example), missing
|
||
layer images and unknown composite operations open with a warning instead of failing the file.
|
||
Krita's W3C Soft Light (`svg:soft-light`, `krita:soft_light_svg`) and its own Hard Mix open as
|
||
Photoshop's Soft Light and Hard Mix, which differ slightly, also with a warning.
|
||
|
||
## Limits and safety
|
||
|
||
The reader takes the archive with the bounded ZIP reader of `photocraft-format` (no ZIP64,
|
||
CRC-checked), parses `stack.xml` with `roxmltree` (16 MiB cap), and decodes each layer image
|
||
through `photocraft-codecs` with size limits. Canvases are at most 300,000 px a side, at most
|
||
8,192 layers and groups, nesting at most `MAX_GROUP_DEPTH` deep, and 1 GiB of decoded pixels in
|
||
total. Malformed archives, XML, numbers and images return errors; truncated and corrupted files
|
||
are covered by tests.
|
||
|
||
## Verification
|
||
|
||
Unit tests (`crates/io/src/ora/tests.rs`) round-trip layers, groups, pass-through, offsets,
|
||
opacity, visibility, locks and every blend mode at 8 and 16 bit, check masks, rendered layer
|
||
kinds, the archive layout and malformed input. Synthetic 8/16-bit pixel fixtures check modern
|
||
and legacy group-isolation defaults, explicit isolation and pass-through.
|
||
`crates/automation/tests/it/openraster.rs` saves,
|
||
reopens, edits and saves in place through the agent surface, and checks that a document with an
|
||
adjustment layer never overwrites its `.ora`; the CLI test converts both ways.
|
||
|
||
Real-file checks use Krita-authored oracles from
|
||
[photocraft-corpus](https://github.com/storytold/photocraft-corpus) (`krita/`, made by
|
||
`tools/krita-oracles/generate.py`). Each Krita file carries Krita's rendering of itself in
|
||
`mergedimage.png`:
|
||
|
||
```sh
|
||
KRITA_TEST_DIR=/path/to/photocraft-corpus/krita cargo test -p photocraft-io --test it -- krita_corpus:: --ignored --nocapture
|
||
```
|
||
|
||
2026-10-09, Krita 5.2.9: 32 of 35 `.ora` files render within 3/255 of Krita's merged image; the
|
||
other three differ for known reasons listed in `crates/io/tests/it/krita_corpus.rs` (Krita's
|
||
tie-break in Darker and Lighter Color, and Hard Mix at a channel sum of exactly 1). In the other
|
||
direction, Krita opened every PhotoCraft re-export of the first 34 of those files and rendered 33
|
||
of them identically to its original; the exception is Photoshop's Soft Light, which is written
|
||
as `svg:soft-light`. MyPaint and GIMP files have not been checked yet.
|
||
|
||
Format reference: the [OpenRaster specification](https://www.openraster.org/). The reader and
|
||
writer are written from the specification and from files Krita writes; no application code
|
||
was used.
|