Keep a side computed for a size with one side 0 at 1 or more (closes #283)

The side was rounded down, so for a thin source it came out at 0, and
libvips made the size square: an 8192x1 source at 100x0 came out at
100x100, at 1300x0 with fit=cover libvips asked for about 39 GB, and an
8192x2 source at 100x0 answered 500. fit=contain and fit=inside work
both sides out again from the smaller scale and keep each at 1 or more
as well, as a 1x8192 source at 0x100 answered 500 there.

Model: opus-5-5
This commit is contained in:
2026-10-16 02:05:57 +00:00
parent 472d3354bd
commit a7bf8d040d
2 changed files with 26 additions and 14 deletions
+14 -9
View File
@@ -311,8 +311,10 @@ func (p *ImageProcessor) Process(
// targetDimensions calculates the output dimensions for a requested size,
// scaling proportionally when only one dimension is given and keeping the
// original dimensions when both are zero. A side scaled proportionally can
// come out above MaxOutputDimension, which is ErrOutputTooLarge.
// original dimensions when both are zero. A side scaled proportionally is
// rounded down but kept at 1 or more, as for a thin image it would round
// down to 0, a side of no pixels. It can come out above MaxOutputDimension,
// which is ErrOutputTooLarge.
func targetDimensions(size Size, origWidth, origHeight int) (int, int, error) {
width, height := size.Width, size.Height
@@ -322,10 +324,10 @@ func targetDimensions(size Size, origWidth, origHeight int) (int, int, error) {
return origWidth, origHeight, nil
case width == 0:
// Only height specified: calculate width proportionally
width = origWidth * height / origHeight
width = max(1, origWidth*height/origHeight)
case height == 0:
// Only width specified: calculate height proportionally
height = origHeight * width / origWidth
height = max(1, origHeight*width/origWidth)
}
if width > MaxOutputDimension || height > MaxOutputDimension {
@@ -479,13 +481,16 @@ func (p *ImageProcessor) resize(
return cropCentreAndResize(img, width, height)
case FitContain:
// Resize to fit within dimensions, maintaining aspect ratio
// Resize to fit within dimensions, maintaining aspect ratio. Scaled
// by the smaller scale, the short side of a thin image can round
// down to 0, which libvips refuses as a width, so each side is at
// least 1.
imgW, imgH := img.Width(), img.Height()
scaleW := float64(width) / float64(imgW)
scaleH := float64(height) / float64(imgH)
scale := min(scaleW, scaleH)
newW := int(float64(imgW) * scale)
newH := int(float64(imgH) * scale)
newW := max(1, int(float64(imgW)*scale))
newH := max(1, int(float64(imgH)*scale))
return img.Thumbnail(newW, newH, vips.InterestingNone)
@@ -503,8 +508,8 @@ func (p *ImageProcessor) resize(
scaleW := float64(width) / float64(imgW)
scaleH := float64(height) / float64(imgH)
scale := min(scaleW, scaleH)
newW := int(float64(imgW) * scale)
newH := int(float64(imgH) * scale)
newW := max(1, int(float64(imgW)*scale))
newH := max(1, int(float64(imgH)*scale))
return img.Thumbnail(newW, newH, vips.InterestingNone)