Commit Graph

386 Commits

Author SHA1 Message Date
skylar taylor-barrick 6d8cbac7dc Merge pull request #2945 from diegochagas/feat/xcf-read
Open GIMP XCF files: layers, groups, masks, blend modes, channels and selection
2026-10-21 14:35:32 -06:00
Diego Chagas ca18689304 Open GIMP XCF files: layers, groups, masks, modes, channels and selection (#1076)
PhotoCraft reads GIMP's .xcf (and .xcf.gz) files, read only, through a new
reader in photocraft-io written from the public XCF specification: versions
0 to 26, uncompressed, RLE and zlib tiles, 8/16/32-bit integer and
16/32/64-bit float precisions (linear precisions get a linear profile),
pixel layers at any position, groups by item path with pass-through,
layer masks applied or disabled, opacity, visibility, locks, colour tags,
the 2.10 and legacy blend modes mapped to Photoshop's (modes without an
equivalent open as Normal with a note), the ICC profile, resolution,
guides, the comment, extra channels and the saved selection; indexed
images open as RGB. Text, vector and link layers open as their pixels;
layer effects, paths and floating selections are reported.

Fixtures are written by GIMP 3.2 itself (scripts/xcf_fixtures.py), each
with GIMP's merged rendering as a PNG. In gamma-encoded blend and
composite spaces our composite matches GIMP within 0.5/255; GIMP's
linear-light default, its Clip to backdrop and its clamp-after-alpha
for unbounded modes are documented and measured in docs/xcf.md.
2026-10-21 16:57:22 -03:00
aiongg b1e408d28f Merge the smart-filter track (#7066 #6398 #6397 #6297 #6299 #6301 #6303 #6315 #6305 #6317)
# Conflicts:
#	docs/file-format-parity.md
2026-10-21 05:04:04 +00:00
aiongg fc371711e6 Merge PR #5847: Shapes: Density and Feather on shape layers (#5846)
# Conflicts:
#	docs/scorecard.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-21 05:04:03 +00:00
aiongg 47488dff8e Integration: recount KNOWN smart filters (41), for #6317 2026-10-21 01:21:06 +00:00
aiongg bfce71cddd Integration: name #6317's test item pixelate_item (measured_item is taken), for #6317 2026-10-21 01:18:45 +00:00
aiongg af3dff34a6 Merge PR #6317: PSD: Mezzotint and Color Halftone smart filters open and save like Photoshop (#6316)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:18:35 +00:00
aiongg 3fee618fd6 Integration: recount KNOWN smart filters (39), for #6305 2026-10-21 01:18:16 +00:00
aiongg bc248fbcec Integration: name #6305's range check long_in_range and its test item measured_named_item (int_in and measured_item are taken), for #6305 2026-10-21 01:18:07 +00:00
aiongg 2c87bd630b Merge PR #6305: PSD: Offset and Custom smart filters open and save like Photoshop (#6304)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:17:09 +00:00
aiongg c8979b69e8 Integration: recount KNOWN smart filters (37), for #6315 2026-10-21 01:16:51 +00:00
aiongg 5a8a2be4d5 Integration: Wind, Extrude and Tiles use the shared enum_from, enum_out, int_in and pi_in, for #6315 2026-10-21 01:16:41 +00:00
aiongg 2c0d232083 Merge PR #6315: PSD: Wind, Extrude and Tiles smart filters open and save like Photoshop (#6314)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:16:28 +00:00
aiongg 0dcf8e8e0f Integration: recount KNOWN smart filters (34), for #6303 2026-10-21 01:15:44 +00:00
aiongg 99c94f1435 Integration: drop the duplicate int_in and name the enum reader enum_from (enum_param is #6299's writer), for #6303 2026-10-21 01:12:32 +00:00
aiongg 7c92cb988c Merge PR #6303: PSD: Diffuse, Trace Contour, Crystallize and Pointillize smart filters open and save like Photoshop (#6302)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:11:06 +00:00
aiongg 47b282cf50 Integration: recount KNOWN smart filters (30), for #6301 2026-10-21 01:10:15 +00:00
aiongg 73a52e1aa4 Integration: drop the duplicate int_in and ps_item (kept from #6299), for #6301 2026-10-21 01:10:08 +00:00
aiongg 9fbb648f8a Merge PR #6301: PSD: Smart Sharpen and Dust & Scratches smart filters open and save like Photoshop (#6300)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:09:51 +00:00
aiongg 58dbb8f0af Integration: recount KNOWN smart filters (28), for #6299 2026-10-21 01:09:20 +00:00
aiongg 95256cc86a Merge PR #6299: PSD: Surface Blur, Smart Blur and Radial Blur smart filters open and save like Photoshop (#6298)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:09:08 +00:00
aiongg c7ece37b15 Merge PR #6297: PSD: parameterless smart filters (Blur, Sharpen, Find Edges, Solarize…) open and save like Photoshop (#6296)
# Conflicts:
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:08:09 +00:00
aiongg 3a3796af05 Merge PR #6397: PSD: Perspective Warp smart filters open and save like Photoshop (#6268)
# Conflicts:
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-21 01:07:59 +00:00
aiongg 61ef780550 Merge PR #6398: PSD: Hue/Saturation smart filters open and save like Photoshop (#6267) 2026-10-21 01:07:14 +00:00
aiongg d1b602212a Merge PR #7066: PSD: Hue/Saturation export writes each triplet in its own slot (#7065) 2026-10-21 01:07:14 +00:00
aiongg ac9b33e8b9 Merge PR #5887: Effects: Gradient Overlay transparency follows Smoothness, midpoints and method (#1954) 2026-10-21 01:06:16 +00:00
aiongg 68f8be3967 Merge PR #5791: PSD Multichannel: alpha channels are stored as-is, not as ink (#5790) 2026-10-21 01:06:14 +00:00
aiongg f1784d1497 Merge PR #6722: PSD: open an adjustment layer of an unknown type as an unsupported adjustment (#6575) 2026-10-21 01:06:13 +00:00
aiongg ea5aa58fb6 Merge PR #6775: PSD: read colours stored as HSB (#6774) 2026-10-21 01:06:13 +00:00
aiongg 5d1f3ec479 Merge PR #5109: Tests: generated large-file fixtures and a P20 number (#5107)
# Conflicts:
#	docs/scorecard.md
2026-10-21 01:06:10 +00:00
aiongg b097800e78 Merge pull request #8095 from aiongg/integration-7
Integration batch: 9 PRs on main
2026-10-20 20:44:51 -04:00
aiongg f5c2f2091e Merge PR #5857: Batch: Format "Same" saves camera raws as PSD instead of failing (#5799) 2026-10-20 22:07:10 +00:00
David Mann bc5e4d4c8d Merge PR #7948: PSB save: 8-byte-length global blocks use the 8B64 signature, so Photoshop opens the file (#7937)
Integration: #4816's fix-up made `block_signature(sig, version)` crate-visible for the
layerless Indexed/Bitmap writer (`multichannel_map::layerless_file`); #7948 adds the block
key so PSB long-length blocks get `8B64`. Kept #7948's function as `pub(crate)` and pass
the key from the layerless writer too, so an Indexed or Bitmap PSB gets the same fix.
2026-10-20 23:02:48 +02:00
David Mann 6db0473b43 Merge PR #7180: Brushes: accept .abr files up to 1 GiB 2026-10-20 22:39:24 +02:00
David Mann 34c37aa260 Merge PR #6913: PSD: read split ("quilt") warps of placed layers, so Replace Contents keeps their curvature (#4398) 2026-10-20 22:39:22 +02:00
David Mann f498375d52 Merge PR #6272: PSD: render constant RGB noise gradient fills 2026-10-20 22:39:17 +02:00
David Mann e4692cd71c Merge PR #4816: PSD: save Indexed Color, Bitmap and Duotone documents in their own mode 2026-10-20 22:39:10 +02:00
David Mann baa854341b Integration: Indexed PSD keeps transparency, ICC and channels (#4816)
Saving an Indexed Color document in its own PSD mode lost data the RGB
file used to keep (the five open CodeRabbit findings, all reproduced):

- an opaque pixel whose colour matched the transparent entry (opaque
  black next to a black transparent entry, common in PNG-8 files) was
  written as that entry, so it turned transparent; a table whose only
  entry is the transparent one now keeps the RGB file;
- PhotoCraft reopened the file with resource 1047 on the table only, so
  transparent pixels came back opaque (Photoshop stores transparent
  pixels of an Indexed file as the 1047 entry, checked on a GIF it
  opened and saved as PSD);
- the ICC profile was dropped (Photoshop writes 1039 for Indexed); only
  an RGB profile is written, as it describes the table;
- alpha channels and the Quick Mask were dropped without a warning; the
  importer doesn't read extra channels of these modes back, so such
  documents keep the RGB / Grayscale file;
- without a transparent entry, empty areas became the nearest colour to
  transparent black instead of white;
- a PSB-sourced 8B64 global block stayed 8B64 in a PSD (Multichannel
  too), which Photoshop refuses.

Tests: one per point in psd_structure.rs; each fails without this commit.

Co-authored-by: Gert Pellin <8284204+switch87@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:14:08 +02:00
aiongg c247a34bb6 Merge PR #7892: Indexed GIF opens in Indexed Color with its colour table, as in Photoshop (#7886) 2026-10-20 18:39:28 +00:00
aiongg c8cf9eaf0d Merge PR #7180: Brushes: accept .abr files up to 1 GiB 2026-10-20 18:39:28 +00:00
aiongg b82540845f Merge PR #7773: Give a new type layer a full Txt2 object and its own frame 2026-10-20 18:39:28 +00:00
NetterSilver e86e6910ae fix(psd): bound the base style run a save copies per run
The base run comes from the file and is cloned once per character run, so
it now has to fit the template node budget, and each font is looked up
once per name. Also names the reopened-layer tuple in psd_kerning for
clippy::type_complexity.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 20:07:39 +02:00
David Mann f65cea0ea0 Integration: keep split warps past the quilt limit in place on PSD save (#6913)
The reader takes a `quiltWarp` of at most 64 patches along a side, but the
writer wrote any split warp and framed `Trnf` by its control box. A Warp
Grid of 64 plus one more split (65 patches) was refused on reopen while its
corners still framed the warped box, so the layer came back stretched and
shifted ([2,0,0,2,120,210] -> [2.4,0,0,2.4,100,200]). Such a warp is now
written as no warp, with a message, framed by the source rect, as before
#6913, and a stale imported `quiltWarp` is dropped.

Co-authored-by: Thang Nguyen <13392060+thangnm93@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:53:57 +02:00
NetterSilver 20c9103148 fix(psd): write complete Txt2 style runs from the layer, number only file indices as kept
Photoshop reads the character style from Txt2 and quit on the bare run
(`/0 0 /11 mode`) that new objects got. New and edited objects now get one
style run per character run, built on one of the object's own runs with
every key kept, and font, size, faux styles, leading, scales, tracking,
fill colour, OpenType keys and auto-kern mode set from the layer. A font
the block's font list lacks is appended to it. Edited objects therefore no
longer keep the old text's features by position.

build_tysh no longer writes TextIndex 0 for a layer without one: the
export took it as a kept number, so a Type tool layer landed on object 0
(a deleted layer's, or the one of a layer above it). set_text_index puts a
missing TextIndex right before EngineData, where Photoshop writes it.

pad_frames pads to at most MAX_TEXT_INDEX + 1 frames.

edited_text_keeps_the_files_txt2_object (#7220) now expects style runs
that follow the layer's runs and size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 19:48:40 +02:00
David Mann d449d25d62 Merge main into feat/file-216-2-large-fixtures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:25:10 +02:00
David Mann 8254fbbd3f Merge main into feat/file-215-2-noparam
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:24:32 +02:00
David Mann 106600284f Merge main into feat/file-215-2-blur
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:23:52 +02:00
David Mann f83bbd0a14 Merge main into feat/file-215-2-sharpen-noise
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:23:20 +02:00
David Mann 6ff19e55c0 Merge main into feat/file-215-2-stylize-pixelate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:22:51 +02:00
David Mann 83aec38ba6 Merge main into feat/file-215-2-other
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:22:08 +02:00