49 Commits
Author SHA1 Message Date
itsF4LCON 3bca0123b0 Type: the blink-phase test finds the caret by its strokes
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.
2026-10-17 18:12:29 +02:00
itsF4LCON 6d521c2d3e Merge remote-tracking branch 'upstream/main' into fix/6085-text-caret-contrast 2026-10-17 18:04:21 +02:00
itsF4LCON 85aa10b16d Merge remote-tracking branch 'upstream/main' into fix/6085-text-caret-contrast 2026-10-17 11:01:10 +02:00
F4LCON 75978a72f0 Targa: RLE packets end with their scanline (#6382) (#6386)
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.
2026-10-16 20:15:32 -06:00
F4LCON a05daee325 Filters: the live preview is never coarser than the screen (#6326) (#6351)
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.
2026-10-16 19:14:50 -06:00
itsF4LCON 1302e08791 Arrange: the shown tiles stay put when a visible document is activated (#6017)
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.
2026-10-16 12:04:34 +02:00
itsF4LCON 5b5fc9a80e Merge remote-tracking branch 'upstream/main' into fix/6017-arranged-tiles 2026-10-16 12:02:40 +02:00
itsF4LCON a6211253f8 Merge remote-tracking branch 'upstream/main' into fix/6085-text-caret-contrast 2026-10-16 11:37:08 +02:00
itsF4LCON 73e6671893 Merge remote-tracking branch 'upstream/main' into feat/6278-replace-color-result 2026-10-16 11:37:07 +02:00
itsF4LCON dedf89feae Type: move the caret test next to its helper, away from the file's end 2026-10-16 01:33:24 +02:00
itsF4LCON 67602017f8 Merge remote-tracking branch 'upstream/main' into feat/6278-replace-color-result
# Conflicts:
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-16 01:21:27 +02:00
itsF4LCON a76d3295ba Type: the text caret shows on a white background (part of #6085)
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.
2026-10-16 00:38:48 +02:00
itsF4LCON 0752cd8365 Merge remote-tracking branch 'upstream/main' into feat/6278-replace-color-result
# Conflicts:
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-16 00:27:03 +02:00
itsF4LCON c04da11bb3 Smart filters: rustfmt 2026-10-16 00:16:06 +02:00
itsF4LCON 8176b613ca Smart filters: the dialog's preview window shows the edited filter too (#6324)
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.
2026-10-16 00:15:50 +02:00
itsF4LCON 8369b4b55c File › Open: a file type filter per format (#6337)
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.
2026-10-16 00:04:50 +02:00
itsF4LCON f71eb28f84 Replace Color: a Result swatch whose Color Picker sets the sliders (#6278)
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.
2026-10-15 23:55:58 +02:00
itsF4LCON 0504331c51 Smart filters: an edited filter's dialog previews again (#6324)
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.
2026-10-15 23:46:54 +02:00
itsF4LCON 4258089716 Replace Color: the sample eyedropper is armed when the dialog opens (#6279)
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.
2026-10-15 23:38:01 +02:00
itsF4LCON d065c874e5 Merge remote-tracking branch 'upstream/main' into fix/5996-help-search-mnemonics 2026-10-15 12:58:26 +02:00
itsF4LCON 116a607697 Window › Arrange: tiles keep their documents, and every tile has rulers (#6017)
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.
2026-10-15 09:41:16 +02:00
itsF4LCON 510636b19c Type: Korean paragraph text wraps between words, not syllables (#5959)
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.
2026-10-15 09:34:09 +02:00
itsF4LCON 494308a038 Image Size: Indexed Color resamples with Nearest Neighbor only (#6019)
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.
2026-10-15 09:28:55 +02:00
itsF4LCON 5b39b2b3af Help › Search: typed letters go to the field, not to menu mnemonics (#5996)
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.
2026-10-15 09:18:19 +02:00
F4LCON ef8dc9ea30 Layer locks: locking the pixels clears the transparency lock, as in Photoshop (#4892) (#4898) 2026-10-13 12:47:10 -04:00
F4LCON 96c0241b26 Distort: a quad dragged past the horizon draws only its front side, in the preview and the result (#4863) (#4870) 2026-10-13 12:33:53 -04:00
F4LCONandpersi-man 62bf7391ce New Document: a custom background colour, from Custom… or the swatch beside the menu (#4789) (#4801)
* New Document: a custom background colour, from Custom… or the swatch beside the menu (#4789)

* New Document: test the swatch's colour for each Background Contents choice (#4789)

Co-authored-by: persi-man <69083413+persi-man@users.noreply.github.com>

---------

Co-authored-by: persi-man <69083413+persi-man@users.noreply.github.com>
2026-10-13 12:24:16 -04:00
F4LCON 1168a9dd73 Pen: a new path joins the selected path instead of replacing the Work Path (#4721) (#4730) 2026-10-13 12:03:43 -04:00
F4LCON 5adba14cd6 Navigator: one zoom row as in Photoshop, the percentage, Zoom Out, the slider and Zoom In (#4840) (#4854) 2026-10-13 12:03:30 -04:00
F4LCON 3d54e8cd68 Alerts: Enter is OK, but not the Enter that raised the alert (#4872) (#4880) 2026-10-13 12:02:12 -04:00
F4LCON e50b25f45c Shapes: test that the options bar Fill swatch recolours the shape just drawn (#3997, #3999) (#4510)
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.
2026-10-13 12:02:07 -04:00
F4LCONandBrandon Thomas f4e3dc63b6 Properties: Align and Distribute with the Background in a multiple selection (#4608) (#4637)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 02:45:42 -04:00
f2c52a92c9 Pen: a close-path pointer over the first anchor (#4596) (#4612)
* 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>
2026-10-13 02:44:57 -04:00
F4LCONandBrandon Thomas ad5b11a120 Layers panel menus: highlight the item a press-drag is over (#4601) (#4607)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 01:14:49 -04:00
F4LCONandBrandon Thomas 8c135457b4 Brushes: a New Brush Preset dialog for every way of making a preset (#4490) (#4563)
* 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>
2026-10-13 00:58:50 -04:00
F4LCONandBrandon Thomas 99fbd8e96b Move tool: the curved rotate arrow just outside its transform controls (#4658) (#4687)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-13 00:12:42 -04:00
F4LCONandBrandon Thomas 321b7da12b Load Files into Stack: Photoshop's dialog with a Source Files list and the align option (#4671, #4696) (#4705)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 23:54:15 -04:00
F4LCONandBrandon Thomas 25dd3b4794 Save for Web: no matte by default, as Photoshop's scripted export (#4115) (#4578)
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-12 23:52:16 -04:00
F4LCON 05e98e7ade Smart Filters: select a filter, drag or ⌥-drag it onto another smart object (#4271) (#4371) 2026-10-12 22:35:54 -04:00
F4LCON b4aea45e19 Actions: record File › Save a Copy and play it back as a copy (#4373) (#4390) 2026-10-12 22:24:18 -04:00
F4LCON 1f76d839e3 SVG: a group clipped by one path opens as editable shapes with a vector mask (#4266) (#4425) 2026-10-12 22:24:11 -04:00
F4LCON 1bc7d7baad Puppet Warp: ⌘Z / ⌘⇧Z undo and redo pin edits (#4440) (#4479) 2026-10-12 21:47:45 -04:00
F4LCON 6c2934ba79 Recent files: a saved document is listed, as an opened one is (#4400) (#4411) 2026-10-12 21:47:32 -04:00
F4LCON 851916e0d7 Save As: offer ICO, GIF and BMP in the file type list (#4357) (#4405) 2026-10-12 21:47:27 -04:00
F4LCON f5d64a66bd Color panel: the spectrum field in every theme (#4367) (#4399) 2026-10-12 21:47:18 -04:00
F4LCON 973e7776ea History panel: snapshots (camera button, snapshot rows) (#3545)
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.
2026-10-12 00:35:56 -04:00
b14037083d Brush and Pencil: hold ~ to erase, and Clear mode (#3439) (#3465)
* 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>
2026-10-12 00:02:42 -04:00
F4LCON b773a39efa Home screen: remove individual Recent entries (#2731)
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.
2026-10-10 16:49:34 -04:00
F4LCONandBrandon Thomas ae03e7cd66 OpenEXR/HDR export: linearize grayscale documents too (#2386) (#2448)
* 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>
2026-10-10 13:13:40 -04:00