arrow_keys_reset_caret_blink_phase (#5512) looked for a 1.5-wide line, the old caret. The caret is now CARET_STROKES (a 3 pt white halo and a 1 pt black line), so the test matches those.
The image crate's Targa encoder let run-length packets run on from the end of
one row into the next. TGA 2.0 says a packet must not cross a scanline, and
readers that decode a row at a time misread such files: Pillow fails on them
with "buffer overrun", and texture tools and game engines reject them.
Targa is now written by our own encoder (codecs::tga), as Photoshop writes
it: RLE true colour or grayscale, every packet within its row, rows stored
bottom-up (the origin readers that ignore the descriptor assume), the alpha
bits in the descriptor and the TGA 2.0 footer. Reading stays with the image
crate.
On a document over about 5 MP with Low Resolution Previews on, a filter
dialog's live preview ran on a reduced proxy at every zoom, so at 100 % the
canvas showed the image in k-pixel blocks and hid exactly what Unsharp Mask,
High Pass and Smart Blur do.
ensure_filter_preview now caps its factor with proxy::view_factor(k, zoom),
the same rule adjustment previews got in #5764, so from 100 % the preview is
full resolution and stays reduced when zoomed out. The preview already runs on
a cancellable background worker, so the full-resolution path does not block the
UI thread.
Also in this change:
- canvas.rs and filter_preview_ui.rs share one canvas::filter_preview_factor,
so the in-dialog preview window cannot size textures from an uncapped factor
and rebuild them when the first result arrives.
- The cap is gated on cfg!(target_arch = "wasm32"), so the web build keeps its
proxy rather than running a full-resolution filter inline on the UI thread.
- The end-to-end test moved into mod tests and covers Low Resolution Previews
off; a new factor test covers 25/50/75/100/400 %, NaN and zero zoom.
With more documents than tiles, the run of shown documents was worked out from the active
index alone, so after activating document 2 in Two-up, a click into the visible document 1
moved the tiles back to [0, 1]. The run is now kept from frame to frame and slides only when
a hidden document becomes active.
In the Photoshop themes the caret was a 1.5 pt white line with a 0.75 pt
black hairline inside, so on a white background only a faint grey line
showed. It is now a solid 1 pt black line inside a 3 pt white halo, readable
on any background, in every theme.
The filter dialog's own preview window matched the canvas preview by the
filter's command, but an edited smart filter previews as
layer.smartFilter.setParams, so the window kept the unfiltered image.
smart_ui::preview_command names the command the preview runs.
After All Formats the Open dialog listed only PhotoCraft, OpenRaster, Krita,
SVG and Paint.NET. Every format it reads now has its own filter (Photoshop,
Affinity, HEIF, PNG, JPEG, TIFF, Portable Bitmap, OpenEXR, Radiance HDR,
camera raw, …), and together they cover All Formats exactly.
Photoshop's Replace Color shows the replacement colour under the Hue,
Saturation and Lightness sliders; picking one in its Color Picker sets the
sliders so the sampled colour becomes it. tone::replace_color_result gives
the swatch (the sampled colour after the shift) and tone::replace_color_shift
the slider values for a picked colour.
Re-opening a smart filter dropped the dialog's Preview, since the live
preview ran the filter command and would have stacked a second copy. The
preview now runs layer.smartFilter.setParams on the proxy, as OK does, at
full size and reduced afterwards (the smart object renders again from its
source, which the proxy doesn't scale), so it shows the filter replaced in
its stack.
Photoshop's first click on the image after opening Replace Color samples the
colour. The dialog now opens (and resets) with its canvas eyedropper active;
the pipette button still turns it off and on.
With documents tiled side by side, a click into the other one moved it
to the first tile, swapping the two, and only the active tile showed
rulers. Each document now keeps its tile (with more documents than
tiles, the shown run slides just enough to include the active one), and
inactive tiles and floating windows draw their own document's rulers.
Guides and the zero point are still dragged in the active view.
Unicode's line-breaking rules allow a break between any two Hangul
syllables, so Korean box text split words (설 / 정이나). Korean is set
with breaks at the spaces between words, as Photoshop does: runs of
Hangul now lay out with WordBreak::KeepAll. Chinese and Japanese keep
breaking between characters.
An Indexed Color document holds only its colour table's colours, and a
save quantises to them. Interpolating methods showed blended colours on
screen that the saved file then lost. image.imageSize now resamples an
Indexed document with Nearest Neighbor whatever method it is given, and
the dialog offers only Automatic and Nearest Neighbor for one.
In an open menu a plain letter runs the row with that mnemonic. Typing
into Help › Search did too: "S" ran System Info and "A" opened the
ArtCraft website, so only "De" of "Deselect" could be typed. Mnemonics
now wait while a text field in the menu has the keyboard.
In v0.7.0 the options bar's Fill swatch only previewed the foreground
colour, so a drawn shape's fill could not be changed from it while Stroke
could. Main fixed that in #3019 and #3370; this test covers the reported
steps (draw a rectangle, pick a colour from the Fill swatch): the fill
changes, the stroke does not, and it is one undo step.
* Pen: a close-path pointer over the first anchor (#4596)
* Canvas: the close-path pointer arm before the pen tools' own cursors
The pen tools' icon cursors (#4650) matched first and made this arm unreachable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Brush presets: remember what the New Brush Preset dialog captured (#4490)
* Brushes: a New Brush Preset dialog for every way of making a preset (#4490)
* Brushes: the New Brush Preset strings in the Serbian catalog (#4490)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Part of #2966. The History panel's camera button takes a named snapshot
of the document ("Snapshot N", not a history state). Snapshots are
listed under the opening state with a thumbnail and scroll with the
states; clicking one returns the document to it as one new, undoable
state named after it. A double-click or right-click › Rename… renames
it in place, and right-click › Delete removes it. Snapshots live for the
session only, as in Photoshop.
For agents: history.newSnapshot {name?, from?: fullDocument |
mergedLayers | currentLayer}, history.selectSnapshot, renameSnapshot and
deleteSnapshot, addressed by ids that are never reused, and doc.inspect
reports snapshots.
* Brush and Pencil: hold ~ to erase
Fixes#3439. Holding ~ (the backtick key) with the Brush or Pencil makes
the strokes started while it is held erase, with that tool's own brush,
as in Photoshop; releasing it paints again and the tool never changes.
The key is a Tools › Temporary binding (tools.temporary.erase), so it
can be rebound or removed in Keyboard Shortcuts.
* Brush and Pencil: Clear painting mode, used by ~
Photoshop's Clear joins the Brush's and Pencil's Mode menus, after
Dissolve: chosen there, every stroke erases with that tool's brush until
another mode is picked. Holding ~ now puts the stroke in Clear mode too,
and the Mode menu shows Clear while the key is held. History names the
tool rather than the Eraser. Agents get it as "mode": "clear" on
paint.stroke and paint.pencil.
* Keep the Smoothing doc comment on smoothing_field
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Fixes#2691. The hovered row of the Home screen's Recent list shows a ×
that takes the file off the list without opening it, and right-click
offers Remove from Recent and Clear Recent Files. The change is stored in
the preferences, so it survives a restart, and the file comes back when it
is opened again. `file.removeRecent {"path"}` does the same for agents.
Rows carry their file name as their accessible label.
* OpenEXR/HDR export: linearize grayscale documents too
Fixes#2386. The EXR/HDR branch of export_flat converted RGB images to
linear sRGB but left gray ones as stored, so an sGray #808080 was written
as 0.502 instead of 0.216. Gray images are now converted from their
profile (sGray when untagged) to linear gray. An untagged grayscale
OpenEXR opens tagged linear gray (an EXR Y channel is linear luminance),
and the open policy keeps that profile without a mismatch prompt, as it
already does for linear sRGB.
* contributors: add itsF4LCON
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>