With a naming template, Format Same still took the raw extension (IMG_0001_001.dng: unknown file format). HEIC, SVG, Cineon, PCX and XPM open but have no writer, so they save as PSD too; DDS has a writer and keeps its format.
DDS textures can now be saved, not just opened: uncompressed (BGRA, BGRX, L8, A8L8; lossless) or BC1/BC2/BC3 (DXT1/3/5) from a clean-room block encoder, with an optional box-filtered mip chain, in the legacy header. Save As and Save a Copy ask with DDS Options; scripts pass `ddsCompression`/`ddsMipmaps` (file commands, control `app.save`, RPC `doc.save`, MCP `doc_save`/`doc_export`), and the CLI takes `--dds-compression`/`--dds-mipmaps`. Targa and DDS answers travel together as `FormatChoices` through the Save As flow.
Review follow-ups:
- File › Save behind DDS or Targa Options records a plain Save with its answer, and replaying `file.save` applies it (#3531).
- The block encoder finds the principal axis from every channel, so a channel that varies on its own can't hide the others' variation.
- DDS joins the shared round-trip suite; the header is built before the output buffer is allocated.
- Enter on a focused Cancel in DDS, Targa or TIFF Options cancels instead of saving.
On wasm32 a near-1 GiB .abr arrives as base64 and is copied several times
before decoding; with a 1 GiB sample budget the import could pass 4 GiB and
abort the tab instead of failing cleanly. Pick the decoder budget by pointer
width (like MAX_DECODED_BYTES) and derive the engine's file cap from it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows the Print dialog takes the paper and its orientation from the
printer driver, and had no way to change the orientation short of the
driver's own Print Settings… dialog. Photoshop has Layout buttons for it.
A print laid out landscape on portrait driver settings (from a script,
the CLI or another app) stopped with "the driver paper or orientation
differs from the print layout".
- The dialog has an Orientation row (Portrait, Landscape) under Print
Settings…. It turns the driver settings through file.print.page and
takes the paper from the driver again, so the paper and preview turn.
- file.print.page takes "orientation": "portrait|landscape" and returns
the turned settings as printerSettings, with the page they give.
- print_windows turns the settings (the given ones or the printer's
defaults) to the layout's orientation when the driver has the layout's
paper the other way round; another paper is still refused.
Turning sets dmOrientation and its dmFields bit in the DEVMODE and keeps
every other byte, the driver's private settings included; the driver
validates the result as it does settings from Print Settings…. No new
UI strings: "Orientation", "Portrait" and "Landscape" are in every
catalog.
Tests: the turned settings (orientation set, all else kept, with and
without private driver bytes), when a paper counts as turned, an unknown
orientation refused before any printer is asked, the dialog's reading of
the driver paper and its error path, and, run by hand on Windows, a
real "Microsoft Print to PDF" driver turning for a landscape layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A turned crop rotated the whole canvas, then cropped to the frame. With
Delete Cropped Pixels on, every layer, mask and channel is now trimmed to
the frame's source (plus the interpolation kernel's reach) before the
turn, so the rotation costs the frame's size: a 512x442 frame turned 3°
on a 3859x2594 two-layer document goes from 541 ms to 42 ms.
Rotate Canvas also fills the Background with shared tiles instead of a
canvas-sized float buffer.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One value of the wrong type in prefs.json (say "recentFileCount": "ten")
made the whole file fail to deserialize, so every preference fell back
to its default for the session, and since the file was never rewritten
it happened again on every launch.
load_prefs_json now reads leniently when the file doesn't deserialize as
a whole: it starts from the defaults and lays the file's values over
them one at a time, descending into section objects and map entries,
keeping every value that still reads and leaving out only the ones that
don't. Colour settings get the same treatment instead of resetting as a
whole. It returns the left-out values (keys and original value); text
that isn't a JSON object still fails as before.
On launch the app shows a notice naming the reset paths and saves once:
the three-way merge base gets the unreadable values, so the merge
replaces (or removes) exactly those in the stored file and keeps
everything else, including keys this version doesn't know.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Load Actions applied each rename as its own pass over every action in the set, so chained
renames ("A" -> "A 2", "A 2" -> "A 2 2") retargeted a step twice, and with replace:false it
also rewrote steps of actions already in the set. Now one original->new map is applied once
to the newly loaded actions' Play and Conditional (then/else) steps and to function keys.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
image.revealAll ran image.crop, whose step is labelled "Crop". It now
runs it inside analysis_cmds::compound with the label "Reveal All".
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Save Actions wrote these four steps under the four-char codes BoxB, SrfB,
Fltn and BlnW, which Photoshop does not run: executeAction fails with "the
command is not currently available" (measured in Photoshop on a document
with a filled pixel layer; Fltn resolves to the flatness property, not the
flatten event). The names boxblur, surfaceBlur and blackAndWhite and the
code FltI run.
- Box Blur, Surface Blur and Black & White now export by name
(boxblur, surfaceBlur, blackAndWhite); Flatten Image exports as FltI.
The descriptors are unchanged.
- Import accepts the names, FltI and flattenImage, and still accepts the
old codes (a LEGACY_CODES table consulted only for four-char codes), so
actions PhotoCraft saved before keep loading.
- Tests: each step exports the new id and imports back to the same command
and params under the new id, the name and the legacy code; a set mixing
Photoshop-shaped and legacy steps loads and re-saves with the new ids;
near-miss ids (case, padding, empty, NULs) stay Photoshop steps; broken
descriptors don't panic.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- PHOTOCRAFT_GPU_ADAPTER now counts as a per-process launch option, so a
second launch with it set doesn't forward to the running window and drop it.
- Gate RenderState::available_adapters to native targets (it doesn't exist on
wasm, which broke the web build).
- Share the adapter name matcher (engine prefs) between startup and
Preferences, so a saved fragment like "nvidia" isn't flagged as missing when
it matches a listed adapter.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(engine): record Apply Layer Comp by the comp's name, not its document-local id (#7181)
* test(engine): a recorded Apply Layer Comp applies the comp of that name in another document (#7181)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Session::edit has reported a new layer's bounds as damage since #3847, so
Duplicate Layer, Duplicate Layers and Paste no longer need to keep the
document from before the edit and work the damage out again with
note_insert. Paste Into still does: it is three edits.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Migrate Presets deserialized the incoming tool presets all or nothing, so
one damaged entry dropped every good one, and it skipped the checks the
launch loader applies: blank or overlong names were imported and then lost
on restart, and a large file pushed the library past MAX_PRESETS. Read the
list with `presets::tools::load` and stop at MAX_PRESETS.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
transform_doc_marks moved guide positions but not their colour entries,
which are keyed by exact position: after a crop the coloured guide fell
back to the preference colour and a guide that landed on its old place
took the colour. Map the entries with the same expressions as the
guides (swap orientation on quarter turns, clear on arbitrary angles).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>