* 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>
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).
Filter Gallery steps (GEfc with the effect in GEfk, or the effect's own
event) map every effect to filter.gallery.<effect>, not only Spatter:
each key in recorded order, out-of-range values clamped and missing
keys given Photoshop's defaults, enumerations, Invert and Neon Glow's
colour mapped both ways. The table is generated from the event
reference's Group A; every integer range and default matches the
effects'. Glass maps the Frosted and Canvas textures the reference
gives codes for. A setting the event cannot hold refuses the step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
De-Interlace (Dntr), NTSC Colors (NTSC), HSB/HSL (HsbP), Custom (Cstm),
Average (Avrg) and Remove Black / White Matte (RmvB/RmvW) load as their
commands, play, and save back with the same keys. Missing keys take
Photoshop's defaults; a Custom matrix that is not 5 x 5 keeps the
Photoshop step. Replace Color and the direct Gradient Map (GrMp) are
still kept verbatim.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split Channels (SplC), Merge Spot Channel (MSpt), Trap (Trap) and Match
Color without a source (matchColor, noReference) load as channel.split,
channel.mergeSpot, image.trap and image.adjustments.matchColor and save
back with the keys Photoshop records. The split's recorded documentIDs
belong to its run and are neither replayed nor written; Trap's width is
clamped to 32767 px; Match Color takes Photoshop's defaults. Steps the
events cannot carry (a split keeping its original, a named spot
channel, a Match Color source or option) are refused on export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pinch, Polar Coordinates, Spherize, Twirl, Wave and ZigZag steps load
as filter.distort.*, play, and save back with every key in recorded
order. Out-of-range values are clamped as Photoshop clamps them and
missing keys take Photoshop's defaults (Wave's undefined areas are
Repeat Edge Pixels). A Wave scaled below 100 % stays a Photoshop step,
as the command has no scale. Shear is still kept verbatim: its curve
points need a step recorded through its dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Oil Paint (oilPaint), Smart Sharpen (smartSharpen), Focus Area
(focusMask), Select Sky (selectSky) and Remove Background
(removeBackground), recorded by string ID, load as their commands and
save back with the keys and defaults Photoshop records. Oil Paint's and
Select Sky's Photoshop defaults are written out where they differ from
the commands'. Smart Sharpen maps the legacy Lens Blur form with no
noise reduction; other forms stay Photoshop steps. Settings the events
cannot carry refuse the step on export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/atn.md lists the 117 events PhotoCraft maps today and, for 101 events it does
not map yet, every key the step carries in recorded order, with type, unit, factory
default, exact range and accepted enum values, measured in the reference application.
An implementation needs no further checks there. Linked from file-format-parity.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrate bounded Sony SR2 metadata decoding and ILCE-6000 image area; preserve newer bit-depth-aware camera profile application and explicit file crop precedence. Keep current parity counts and original metadata/crop tests.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrated bounded atomic CSH set parsing, unique shape/group naming and native Shapes panel import with inline-data authorization guards; preserved duplicate-name, inner-hole, malformed-second-record and preferences-reload regressions. Combined independent catalog additions and retained current parity metadata.
The PR read and wrote Halftone Pattern as `Sz ` and `Ptrn` (enum `HlfT`:
`Dt `/`Crc `/`Ln `). The reference application's Filter Gallery uses `HlSz`
for the size and a `ScrT` enum (`ScrD` dot, `ScrC` circle, `ScrL` line), so a
Halftone Pattern saved there still opened as an opaque filter, and one we saved
lost its size and pattern there without a warning. The tests built their fixture
with the same keys as the code, so they passed either way.
Now the keys match, a default Halftone Pattern (HlSz 1, Cntr 5, ScrD) opens as
{size 1, contrast 5, dot} (fails before this commit), and descriptors with the
old keys stay verbatim. The docs no longer claim reopen checks in the reference
application.
Co-authored-by: denstu473 <63150090+denstu473@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged with our fix-up branch pr-8885-fixup (8f6863f53) on the author's head 996b55eb4.
Conflicts resolved:
- crates/io/src/tiff_layers.rs: #9148's tiff_source (an empty LMsk gets Photoshop's 14 bytes)
gives way to #8885's tiff_image_source, which writes the same bytes for an empty or short
LMsk; #9148's test stays.
- crates/io/src/flat.rs: #8776's DDS alpha step and #8885's TIFF channel stamp both run.
- crates/io/Cargo.toml, Cargo.lock: #8402's zune dev-deps and #8885's tiff dev-dep.
- crates/io/tests/it/tiff_layers.rs, docs: unions; docs/scorecard.md kept as is, regenerated at
the batch gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>