* Perspective Crop: the swap button swaps W and H (#7059)
The W/H swap button called crop_size::swap, which only acts in the Crop tool's W x H x Resolution mode; Perspective Crop never sets that mode, so the button did nothing there. The button now always swaps the fields; X keeps the guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Address review: the test clicks Perspective Crop's ⇄ button
The test now renders the real options bar with the Perspective Crop tool,
clicks ⇄ through the egui_kittest harness and checks that W and H swapped,
instead of calling `swap_fields` directly. It fails if the button goes back
to calling `swap`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Merges main (8f09d122d). Conflicts: the replay_save doc comment (main added
file.export.exportAs replay) and the status lines and history rows of
docs/file-format-parity.md and docs/gaps.md; both sides kept.
The docs called FILE-215-19 done and said only Save As and Save a Copy ask.
Photoshop's JPEG Options also has Matte, the progressive scan count
(Scans 3-5) and the size preview, none of which is offered, and Baseline
Optimized writes the same file as Baseline for RGB (4:2:0). The row is now
partial with those gaps named, and the docs say File > Save asks too.
docs/scorecard.md is regenerated.
Adds jpeg_options_ui::tests::the_answer_reaches_the_encoder. Every UI test
used a fake exporter, so dropping `opts.encode.jpeg_format` from
ExportSettings::io_options (the desktop and web save path) left them all
green; this test writes a real JPEG through io_options and fails without it.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 20 i18n catalogs: main and the PR both appended a block at the end;
both kept, main's first. docs/gaps.md: main's text with the PR's
TYPE-218-2 change applied (G8 Missing, Evidence 5 done / 4 partial /
0 missing, revision row), dated 2026-10-22. docs/scorecard.md
regenerated with cargo xtask scorecard.
The TYPE-218-2 note now also says PSD doesn't store the paragraph
direction (crates/text psd.rs maps only the vertical WritingDirection),
so a direction set with the new buttons reopens from a PSD as Auto.
docs/localization-parity.md: the Arabic row said paragraph direction has
no UI (TYPE-218-2); it now has the buttons, the RTL caret is still missing.
Co-authored-by: tareqziad1998 <83318513+tareqziad1998@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Zoom tool click or wheel zoom keeps the clicked spot under the pointer, so
the view centre moves off the image's middle. The window then grew to the
image's size around that off-centre view: a 400x300 image zoomed to 200% about
(50, 50) showed grey on the left and top and lost its right and bottom edges.
The size also left out the 12 pt scrollbars, which are drawn over the canvas
and hid the image's right and bottom edges.
The window now makes room for the scrollbars, and a window that grows around
the whole image centres it in the area the scrollbars leave free (any flip,
rotation, pixel aspect and display scale). A window the screen caps keeps the
zoomed spot where it is. Scrubby zoom and the Animated Zoom hold re-anchor the
view only when the zoom changes, so their release frame and the frames at the
zoom limit keep that centred view. A maximized or full-screen window keeps the
size the user gave it.
Co-authored-by: Nguyễn Minh Long <181250616+KLBMinhLong@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each arrow step in the open font menu applies its family, the options bar,
Character and Glyphs panels record it as the most recent font, and the next
frame hoisted it to row 0: with Recent Fonts on (default 10), Down then
alternated between two families and Up stayed on the first row (CodeRabbit's
open thread on #3559). The menu now keeps the recent group it opened with.
As in Photoshop, the recent families now head the menu in their own group
above a divider and keep their alphabetical place below it, instead of
moving out of it. The arrows start from the current family's alphabetical
copy (or the row the last arrow chose), and only that row is marked.
Tests: open_menu_arrows_walk_on_through_recent_fonts,
options_bar_menu_arrows_walk_on_while_recording_recent_fonts and
open_menu_on_a_recent_font_steps_through_the_alphabet fail without the
fix; recent_fonts_appear_first_in_the_menu pins the group, divider and
alphabetical copies.
Co-authored-by: Reynold Hu <88558425+reynold-hu@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The resolution preference reached File › Open, but the other ways a raw
becomes a document of its own still opened it at 72 ppi: File › Revert
(a raw opened at 240 dropped to 72), Batch / Image Processor / Lens
Correction batch (a raw saved as PSD came out at 72 while Open + Save
gave 240), Load Files into Stack, the headless MCP `doc.open`, and the
CLI (`convert` at 72 while `run`/`info`, which open headless, now take
the default 300). They now apply the same `raw_defaults` rule; Batch
passes the user's value into its scratch sessions.
Tests that fail without this change (72 vs 240/300):
revert_batch_and_stack_open_a_raw_at_the_raw_defaults_resolution
(engine, one assertion per path), doc_open_gives_a_raw_the_raw_defaults_resolution
(automation), convert_opens_a_raw_at_the_raw_defaults_resolution (CLI).
Co-authored-by: Korg1ch <57859048+Korg1ch@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 32-bit branch cut the slider to floor(|s| * 255) / 255, which only matches
Photoshop near 25 and 50 %: measured at every whole percent from -100 to +100
on a 32-bit document, Photoshop lowers by 1 - floor(|s| * 256) / 256 and raises
by 255 / (255 - floor(s * 254)) taken to 1/256 and capped at 32767/256 (201 of
201 exact). +100 gave 512x where Photoshop gives 128x, +90 9.8x for 9.44x.
It also kept the HSL saturation unclipped, which is inf at HSL lightness 1 and
0 and negative above 1: (2, 0, 0), red at +1 stop, turned NaN with any edit,
Hue +40 alone included, where Photoshop gives (2, 1.333, 0). The 32-bit branch
now scales the chroma around the lightness instead (same result in gamut),
on the CPU and in the shader, in whole numbers on both. Colorize clamps its
saturation before the 32-bit scaling, so a PSD's out-of-range value renders
the same on the GPU.
Tests (fail without this): thirty_two_bit_saturation_steps_match_photoshop,
thirty_two_bit_hue_saturation_keeps_bright_colours, the GPU parity
hdr_hue_saturation_bright_colours. Photoshop-oracle floor 188 -> 190 of 258,
measured after merging main.
Co-authored-by: Timothy Daly <100327160+timothydaly1@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR's test checks L at full opacity only, so it still passes when a and b are left unrounded or
the rounding moves before the opacity mix, or truncates. New test: Curves taking a 140 to 151.4 at
50% opacity in an 8-bit Lab document must show a 146 (145.7 unrounded, 145.5 rounded before the
mix, 145 truncated: all three fail).
Co-authored-by: timothydaly1 <100327160+timothydaly1@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Photoshop's Create Rectangle switches only the right-clicked field
(Width 35.28 mm, Height still 100 px), lists the six length units
without Percent, and opens in pixels again after OK. The PR shared one
unit between both fields, offered Percent, and saved the unit on every
OK, so after one use with the rulers in px the dialog stayed in px even
after the rulers changed to cm, though no unit was ever picked.
Now each field has its own widthUnit / heightUnit (set only by the
menu, never remembered), and the menu is rulers::unit_menu_of with
LENGTH_UNITS. The new test right-clicks the real field and clicks the
menu item through egui input.
Co-authored-by: Korg1ch <57859048+Korg1ch@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel scrolled with `scroll_to_rect(current, Some(Align::BOTTOM))` on every revision
change. In egui an explicit alignment always realigns, and the revision also changes when
nothing in the history does (selecting a layer, a channel eye). So clicking a visible older
state slid it to the bottom under the pointer, Undo hid the greyed redo states, a layer click
threw a user who had scrolled up back to the bottom, and selecting a snapshot scrolled it out
of view.
Now the panel follows the history position (document, past/redo counts, current name, and the
newest past state, which also changes at the state limit), skips it while a snapshot is
selected, and scrolls without alignment, so only a hidden current row moves into view. New
tests: a clicked visible state stays put, Undo keeps the redo state in view, a layer click keeps
the scroll, a selected snapshot stays in view, a new state at the limit is revealed; the first
three fail on the PR head, the others without the snapshot guard / the newest-state key.
Co-authored-by: nrq6993-a11y <342501791+nrq6993-a11y@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A set built from the bindings dropped action keys from the main shortcuts but copied them from
the alternates, and applying a set replaced the alternates wholesale, dropping an action's
alternate key. Both now treat alternates like the main shortcuts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Duotone PSD opens flattened from its merged image (its layer records are
not read back), but with Maximize Compatibility off a layered Duotone was
saved with the white placeholder, so reopening it showed white. Its merged
image is now always written, like a document without layer records.
Test: a_layered_duotone_keeps_its_image_without_maximize_compatibility.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clouds, Difference Clouds, Fibers and Lens Flare opened as editable smart
filters even when their descriptor held an extra or repeated setting, a
value of another type (a long centre, a unit seed), an RGBC colour with
extra keys or out of 0-255, or a Lens Flare enum of another type. A
re-save rebuilt the item without it. They now need exactly the keys and
value types Photoshop writes; anything else stays a psd.unsupportedFilter
and round-trips byte for byte, as main's newer filters do with only_keys.
Test: render_filters_with_settings_we_dont_model_stay_verbatim (30 cases).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts with main's smart-filter and layer-style work, both sides kept:
- smart_map: main's 27 more filters and this batch's Clouds, Difference
Clouds, Fibers and Lens Flare (45 known); the item colours are written for
Pointillize, Tiles and the three coloured render filters.
- layer_style: main's new Stroke default (1 px inside black) with this
batch's Overprint.
- psd_export: main's verbatim unknown adjustments with this batch's unpadded
Gradient Map check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mezzotint and Color Halftone use main's only_keys (now also rejecting a
repeated setting) and enum_from (the Mezzotint table becomes string
pairs) instead of the branch's own only_keys and typed_enum; the new
malformed cases use main's pixelate_item.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offset and Custom use main's only_keys (now also rejecting a repeated
setting, for every filter that uses it) and enum_from instead of the
branch's own only_keys and typed_enum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The blur tables are now (Photoshop value, ours) string pairs, so the typed
enum check goes through main's shared enum_from instead of a second reader.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dialog's Set dropdown replaced its bindings with the set's copy and
computed "(modified)" itself, so it still dropped the actions' F-keys and
marked a set modified when one was assigned. It now uses the engine's
ShortcutSet::apply (what useSet does), shortcut_sets::differs (what the
engine's `modified` uses) and ShortcutSet::from_bindings for Save and
New Set. Sets sent with `sets` (the dialog's OK) hold no action keys
either, and an action key a set saved earlier still holds doesn't count
as a change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- swatches.import with bytes only (no path or format): an .act whose first
colour is 0, 1, x or 0, 2, x still went to the .aco reader, which ignores
trailing bytes and returned a handful of junk swatches. At the two .act
lengths (768/772) an .aco header now counts only when its sections span
the whole file (new `aco::sections_len`); a Photoshop .aco of exactly
768 bytes is still an .aco. Test: act_that_starts_like_an_aco_is_still_an_act.
- The .atn corpus tests (psd and engine) skipped unreadable folders and
entries silently; they now fail with the path.
- act::write doc: the trailer is written when `count` or `transparent` is given.
Co-authored-by: Abdul karim mia <97598943+abdul-karim-mia@users.noreply.github.com>
Co-authored-by: Fabio Lissi <fabio.lissi@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Look in from each edge through strips that double in thickness, so a subtracted
or intersected subpath's empty margin costs a few renders instead of one per row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A hold left from an earlier press (the tool changed before its release)
is only reused by the press that started it (same origin and start time);
a new press starts a fresh one instead of zooming at once about the old
anchor or never becoming a hold.
- Only a press the canvas owns becomes a hold: not one on a dialog, a
scrollbar or the rotate compass over the canvas.
- Each canvas (tiled windows, secondary windows) keeps its own hold, so a
hold on one never zooms another.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
slice.list and slice.set report a stored slice with its own canvas-clipped
rectangle (what slice.set takes back) and its visible parts under "pieces";
a slice wholly covered by later ones is still listed, with number null.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeping the print size at a higher Output resolution could ask for more than
the 300,000 px document limit; the side was clamped, silently changing the
print size. Refuse it instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
toolBrushes.owner names the tool whose brush lastBrush holds. While the
session brush can't be remembered yet (a sampled tip too big to embed),
lastBrush keeps the previous owner's brush, so keep that owner saved too;
otherwise the next launch gave the old owner's brush to the tool selected
since. Exit flushes the brush before the tool memory, as a frame does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The uncancelled speedup is for layers with locked transparency only, the
numbers are local runs and the perf baseline is not re-measured yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BUG-207-5: only eight retouch tools are tested to follow the Channels panel target (Healing,
Spot Healing, Sponge and Remove are skipped by changes_gray). BUG-208-4: the Polygonal Lasso test
keeps the selection for Shift or Alt and commits a Shift polygon; no test commits an Alt one.
The notes this branch changes now cite file:line, as docs/development.md asks; ui-parity.md gets
a dated row for the marquee Shift+Alt test instead of rewriting the 2026-10-11 one.
Regenerated docs/scorecard.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
large_file_bench needs about 30 GB (its PSB, as its doc comment says), not 16 GB; the opt-in
tests need about 12 GB (2 GB PSD) and 25 GB (4.4 GB PSB), as their #[ignore] reasons say.
Regenerated docs/scorecard.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The coarse Content-Aware Move preview scaled the selection down in its own
8-bit format, so scattered 1/255 pixels averaged to 0 and the preview failed
with "the selection is empty" while the command moves them. Scale the
selection in 32-bit float instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A soft shape's transform frame, snap rectangle and Properties X/Y/W/H came from
the path plus the full stroke width on every side, so a centred or inside stroke
grew the frame when Density or Feather was set. The frame is now the pixels the
same shape renders at full Density without Feather (edge strips only, clipped to
the canvas), identical to the hard shape's for every stroke alignment.
feather_matches_photoshops_ramp now enforces the 1-level claim the scorecard
makes (measured max deviation 1 at 8, 16 and 32 bit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A higher Output resolution passed the resample budget (document bytes only)
but then allocated ~40 B/px of f32 composite, gray and output buffers at the
new size. Budget the whole working set before resampling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening: Smart Blur and Radial Blur enums must have their measured enum
type, Surface Blur's radius must be #Pxl and Smart Blur's radius and
threshold plain doubles; anything else stays verbatim instead of being
re-written in another shape.
Saving: a present value that is not a number or lies outside the range
the filter opens with (Surface Blur radius 0.5, a text Radial Blur
centre) is an error with a warning, not a filter that reopens as
unsupported or a centre silently taken as 0.5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A recognized no-options filterID (or Shadows/Highlights) with a present
but unreadable Fltr opened as editable and lost the Fltr on save; it now
stays an unsupported filter, written back unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve vector_ui.rs: keep main's shows_anchors and the PR's
selected_saved_path; the selected saved path now shows its anchors with the
same tools as the work path (Path Selection and the Pen tools).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- type.setStyle/edit/create: an unknown `baseline` is a BadParams error and no longer resets
superscript or subscript to Normal (#5807).
- Indexed/Bitmap PSD save warns whenever flattening loses something, also for a single hidden,
masked, non-Normal, effect, type, shape or smart object layer, not only for two or more layers
(#4816).
- brush.presets.importAbr: the apply step keeps only `replace` and `select`, not a second copy of
a base64 `data` string of up to ~1.3 GiB (#7180).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The PR stopped writing PhotoCraft's defaults for params a step leaves
out. Checked against Photoshop, that is fine for Fill opacity and mode,
Crop delete, Selective Color method, Channel Mixer monochrome, Paint
Bucket contiguous and Gradient Map reverse/dither, but Photoshop plays
other missing keys differently: Canvas Size anchors top left (PhotoCraft:
centre), Apply Image blends Normal (Multiply), Color Balance turns
preserve luminosity off (on), and a Paint Bucket step without a
tolerance doesn't play at all. Those, and the unverified Hue/Saturation,
Color Balance tone and Save for Web keys, are written again as before.
- `tools.setColors` exported only the foreground of a step that sets
both colours; such a step now doesn't export (the strict save refuses
it, as before the PR).
- `kind` on an imported `layer.setAdjustment` made Photoshop's "reset to
default" (`set` with an empty `T`) a no-op merge; it now joins only
params that edit something.
- `cargo xtask corpus --atn` (and `--all`) failed on Windows: the Bjango
archive has a macOS `Icon\r` file tar can't create there; it's skipped.
- `test-corpus --changed` now runs for `atn_cmds` changes, and the
`ALL` doc comment is back on `ALL`.
Tests: defaults_photoshop_plays_differently_are_written,
set_colors_exports_only_a_single_colour and
adjustment_layer_reset_still_resets fail without this commit; the psd
and engine .atn corpus tests still pass (16 files byte-identical; 513
kept Photoshop steps).
Co-authored-by: Fabio Lissi <3223836+FabioLissi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Color Table dialog: after Load…, picking a preset in the Table menu still
applied (OK) and saved (Save…) the loaded colors, as the hidden `colors`
field won over the menu (`image.mode.colorTable` applies `colors` over a
preset). The dialog now drops the loaded colors from the fields OK sends
once a preset is picked; Save… uses them only while the menu is on Custom.
- Swatches import from bytes (browser, drag and drop) sniffed the format by
content, and .act has no signature, so an .act whose first color is
00 01 xx or 00 02 xx went to the .aco parser and failed. A file named
.act now imports as .act; .aco and .ase are still detected by content.
- Removed "Export Swatches as Color Table…" from the Swatches menu: Photoshop
saves .act from Image > Mode > Color Table, not from the Swatches panel.
Co-authored-by: Abdul karim mia <97598943+abdul-karim-mia@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fill_to (Paint Bucket export): keep main's `source` alias and explicit
colour (`Usng` = `Clr ` plus `Clr `, #7199) together with the PR's
optional `Tlrn`/`AntA`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
apps/photocraft{,-web}: main added "cin" to OPEN_EXTS; kept it and the PR's
"act". apps/photocraft/src/services.rs: main gave File > Open one filter per
format (#6337) and checks with every_open_format_has_its_own_filter that the
filters cover OPEN_EXTS exactly, so "act" joins the Swatches filter.
i18n: both sides appended sections at the end of every catalog; kept both.
Co-authored-by: Abdul karim mia <97598943+abdul-karim-mia@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Older builds wrote every generated `grdm` 2 bytes short of four-byte
alignment (86 + 20n bytes), which Photoshop refuses. The PR pads new
blocks, but opening such a file and saving it again kept the old block:
the keep and Reverse/Dither retarget paths reuse any `grdm` that parses
to the same Gradient Map. Such a block is now regenerated, like the
legacy `phfl` repair. Only blocks that are byte for byte the old writer's
output are matched, so other writers' blocks (stops in CMYK/Lab/Gray)
are still kept verbatim.
Test: legacy_unaligned_gradient_map_is_repaired_on_resave (146 bytes
without this change).
Co-authored-by: Sir Starch <281476371+Sir-Starch@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A normal big-endian TySh that the typed reader rejects (an unknown text
descriptor version, a stub from another writer) opened as a text layer on
main, with the best-effort text and its TySh saved back verbatim. With the
PR it became a raster layer, and the PSD export strips TySh from rasters,
so open + save turned the type layer into pixels for Photoshop. Now only
foreign-order blocks and unsafe transforms become pixels; the old fallback
runs under the same transform gate (shared as
photocraft_text::psd::transform_is_safe). The warning no longer claims the
type data is kept.
Test: canonical_type_the_typed_reader_rejects_stays_type_and_saves_its_block
(descriptor version 17, ordinary matrix) fails without this change.
Co-authored-by: Sean Lavery <702713+TheSeanLavery@users.noreply.github.com>
Co-authored-by: Tanish Manku <13985398+tanish2k09@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/text/src/psd.rs: keep both appended test modules (transform_safety_tests
and main's nonfinite_tests). docs/file-format-parity.md: add the #4758 item to
main's current change line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lfx2_roundtrip_all_kinds compares by writing both sides again, so a reader
or writer that drops FrFX `overprint` on both sides still passes. The new
test checks the parsed flag for off and on and for a stroke without the key
(other writers); it fails with either the reader or the writer reverted.
The new "Overprint" checkbox label had no row in any of the 20 catalogs, so
every non-English UI showed it in English, unlike its translated siblings.
Co-authored-by: varanus-salvator <262257003+varanus-salvator@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/file-format-parity.md: main added three 2026-10-15 revision rows
(DDS save, PNG sTER, PSD guides) where the PR added its own; keep all four.
Co-authored-by: varanus-salvator <262257003+varanus-salvator@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lfx2_roundtrip_all_kinds compares re-serialized bytes, so dropping the
new `.with("antialiasGloss", ...)` line from write_one still passed every
test: both sides then lose the flag the same way. The new test writes a
Bevel with the Gloss Contour's Anti-aliased on and reads the flag back;
it fails with that line removed.
Co-authored-by: varanus-salvator <262257003+varanus-salvator@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A PANTONE (colour-book) ink is stored by its book key, so it read as
black: Photoshop's usual Black + PANTONE duotones showed, exported flat
and converted to RGB much darker than the grey plate before. Photoshop
writes every ink's Lab equivalent in resource 1066; it now gives the
ink its colour. Without it, the grey plate is shown, as before.
- Ink colours go through channel_map::decode_known_color (HSB inks were
black with a "colour book" warning; CMYK is colour-managed).
- Photoshop has only 8-bit Duotone and refuses to open a 16-bit Duotone
PSD: a 16-bit Duotone is written as Grayscale with the warning again.
- A curve on the 13-point grid without a 0 % or 100 % point was written
with -1 there, which Photoshop refuses to open: both ends are filled.
- A layered TIFF of an Indexed, Bitmap or Duotone document says it is
written in its pixel format (CodeRabbit's thread at tiff_layers.rs).
Co-authored-by: Gert Pellin <8284204+switch87@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#4935 is stacked on #4816. Merging our reviewed #4816 fix-up (baa854341b,
which already contains origin/main) brings both up to date; no conflicts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>