Skip to content

fix(modifiers): skip an inserted element that lands wholly off the canvas - #135

Merged
olivervogel merged 1 commit into
Intervention:developfrom
nlemoine:fix/insert-modifier-off-canvas
Oct 12, 2026
Merged

olivervogel merged 1 commit into
Intervention:developfrom
nlemoine:fix/insert-modifier-off-canvas

Conversation

@nlemoine

Copy link
Copy Markdown
Contributor

InsertModifier throws Jcupitt\Vips\Exception (embed: bad dimensions) when the inserted image does not overlap the canvas at all, for example when the offset pushes it past an edge. GD and Imagick clip it away and return the image unchanged.

Base 40x30, inserted image 10x5:

position       x     y    gd          vips (develop)   vips (branch)
top-left       40    0    unchanged   exception        unchanged
top-left       39    0    1 column    1 column         1 column
top-left       0     30   unchanged   exception        unchanged
top-left       -10   0    unchanged   exception        unchanged
top-left       0     -5   unchanged   exception        unchanged
bottom-right   50    0    unchanged   exception        unchanged

Same with transparency below 1. As soon as one row or column of the inserted image is on the canvas, it works.

placeElement() embeds the element in a canvas of the frame size before composite2(), and libvips refuses an embed() with no overlap. Decoded sRGB and greyscale images get an alpha band, so they all go through this path. The insert() path without alpha already handles this case.

The modifier now returns the image as is when nothing overlaps. The check runs before Core::ensureInMemory(), so the element is not loaded into memory for nothing. Tests cover the four edges, alignment with offsets, an animated image (frame height, not the whole strip) and the single row/column cases.

…nvas

InsertModifier embeds an element with an alpha band into a frame-sized
canvas before compositing. When the position leaves no part of the
element on the frame, libvips refuses the embed with "embed: bad
dimensions", so insert() threw where GD and Imagick clip the element
away and return the image unchanged. The decoders add an alpha band to
sRGB and greyscale sources, so a JPEG element takes that path too.

Return the image as it is when the element does not overlap it. The
check runs before the element is materialised, so the pixels of an
element that would not be drawn are never read.
@olivervogel
olivervogel merged commit 0e703d1 into Intervention:develop Oct 12, 2026
8 checks passed
@olivervogel

Copy link
Copy Markdown
Member

Thanks, I made a small adjustment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants