memory-stats reads /proc/self/smaps by default on Linux: every sample formats and parses one
block per mapping while holding the mmap lock. The benches' RssSampler polls it every 2 ms, so
on a profile of bevel_bench --size 4000x3000 the rss-sampler thread was ~22% of all samples
(smaps_account 14.7%, smaps_pte_entry 6.3%), contending with the measured threads.
Enable the crate's always_use_statm feature: one short read of the kernel's RSS counters, the
same number VmRSS reports. The measured code is unchanged; only the harness overhead goes.
parley's default overflow wrap only breaks at UAX #14 opportunities, so
a word longer than the paragraph box ran out past its edge and resizing
the box changed nothing. Paragraph text now sets OverflowWrap::BreakWord,
so such a word wraps at the box edge as Photoshop does; a space or hyphen
still wins over a mid-word break, a box that fits the word lays out as
before, and point text is untouched.
* fix(linux): avoid duplicate AppImage desktop launcher when managed by AM / AppMan (#6392)
* test(linux): cover AM / AppMan launcher detection in AppRun smoke test (#6392)
The local Laplacian filter behind Shadows and Highlights builds a whole
pyramid per reference value (up to 96). Three costs dominated it:
- blur_step called mirror() (an integer remainder) for every tap of every
pixel; the source rows are now found once per row and columns mirror only
within two pixels of an edge.
- up() blurred a zero-stuffed plane, three samples in four zero; it now adds
only the nonzero taps, in the same order.
- the small pyramid levels were split into one-row rayon tasks, costing more
in scheduling than the rows; planes under 32K samples run on one thread.
The output is bit for bit the same: a new test checks blur_step and up
against the plain versions on odd, even, tiny and parallel-sized planes.
With View > Snap on, the canvas snapped the Type tool's press (a point
gesture) before the tool's handle hit test, with the edited text's own
glyph bounds and centre lines among the targets. The snapped press could
leave the 6 pt handle grab area, so the press placed the caret or
committed the edit instead of resizing the box.
- The Type tools pick the box handle or glyph at the raw press, as Free
Transform already does.
- While editing, only a press on a box handle starts snapping, and the
resize drag snaps to guides and other layers, not to the text being
resized; caret drags are no longer snapped.
Canvas-level test drags all eight handles (at the handle and inside its
grab area) at 25-200 % zoom, 1x and 2x HiDPI, plain/scaled/rotated layer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
called_actions only followed actions.play steps, so a droplet whose action
used actions.conditional saved neither branch and failed with "no action
named ..." when run standalone. It now also queues the then/else targets.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Free Transform and Warp moved a fill layer's linked masks (and so the frame its
gradient is laid out in) but kept the cached render, which the compositor draws
as-is while the Fill is unchanged. Drop the cache in apply_transform and
warp_layer, as transform_layer_geometry and Perspective Crop already do.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Photoshop's 16-bit PSBs can mark the merged transparency only by naming it
"Transparency" in the alpha channel names (positive Lr16 layer count, no Mt16).
The importer read it as a user alpha channel, and the next save wrote a second
one. merged_has_alpha now also accepts that name when the layers sit in a
global Lr16/Lr32 block and the names cover every extra channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tx::filter_effects converted only the first u64-length section of FEid/FXid and copied the rest as stored, so a second smart object's section kept the source byte order and FilterEffects::parse lost the whole block.
It now mirrors FilterEffects::parse: one section per smart object, each with its 4-byte padding, until only trailing zero padding remains.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
plugin.run wrote into raster and smart object layers without checking
locks. It now refuses layers with locked pixels or Lock All like the
built-in filters, and with Lock Transparency it keeps the layer's alpha
(as built-in filters do, via filters::keep_alpha).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
renameGroup in the shared preset group editor renamed without checking
that the new name was free, so two groups could share a name and a later
replace import of that name removed both. It now refuses like the
Swatches sibling; renaming a group to its own name stays fine.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
map_fill multiplied the offset by the full linear part of the canvas transform, so Image Size
(and Perspective Crop) scaled it: 200% moved a 25% offset to 50%. Each axis of the linear part
is now normalised, so scale drops out while flips and 90° turns still mirror and turn it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With the layer mask targeted, apply_image took the layer branch and fetched the
layer's pixel surface with the target dropped, so the mask stayed unchanged and the
pixels were overwritten. A mask target now goes through the single-plane path and
is written back over the canvas, keeping the mask's default outside it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
actions.rename only rewrote the "action" param of actions.play steps, so an
actions.conditional step whose "then"/"else" named the renamed action failed
with "no action named ..." on the next play. The rename loop now also rewrites
matching then/else branches, in saved actions and the pending recording.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Auto-Align warped a moving layer's mask with the colour warp (warp_placed), which
returns a grey + alpha surface defaulting to 0, so a reveal-all mask hid everything
outside its painted area. Masks now go through warp_gray (planar placements) or
warp_mesh_gray (cylindrical/spherical), keeping one channel and the mask's default.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
color_from_desc had no HSBC arm, so HSB descriptor colours fell through to the
RGB fallback and came back None: solid fills vanished, strokes and effects went
black. Convert HSBC (hue in degrees, saturation/brightness 0..100) with the
existing channel_map::hsb_to_rgb, now pub(crate).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fit_image read width/height with f64_param and clamped to 1, so a 0 or
negative side (dialogs send 0 for an empty field) became a 1 px box and
shrank the document to 1x1. It now uses size_param like the rest of
file_cmds.rs, so such a side falls back to the document's own size.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The byte-order converter read Idnt/indx reference items as a bare u32, but
they are class + u32 (as descriptor.rs reads them since #4434), so a block
holding such a reference desynced and was dropped (or left unconverted).
Read the class first; regression test round-trips such an lfx2 block.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>