Repository navigation
How does sharp infer the height if the width is provided? #4353
Description
Question about an existing feature
What are you trying to achieve?
I am writing a webpack loader to optimize images, some of that work includes resizing. For dev purposes I need to know up-front what will be the image dimensions. As the user can specify the width only I need to infer the height. After looking at sharp source code I found these lines:
Lines 176 to 184 in 7c631c0
| } else if (baton->width > 0) { | |
| // Fixed width | |
| xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width); | |
| if (baton->canvas == Canvas::IGNORE_ASPECT) { | |
| targetResizeHeight = baton->height = inputHeight; | |
| } else { | |
| // Auto height | |
| yfactor = xfactor; | |
| targetResizeHeight = baton->height = static_cast<int>(round(static_cast<double>(inputHeight) / yfactor)); |
I thought that this must be it so I reimplemented this logic in my loader:
// https://github.com/lovell/sharp/blob/7c631c0787915416e20a567a039516e99c81c42d/src/pipeline.cc#L176-L184
function inferHeight(currentWidth, currentHeight, newWidth) {
const xFactor = currentWidth / newWidth;
const newHeightNotRounded = currentHeight / xFactor;
const newHeight = Math.round(newHeightNotRounded);
return newHeight;
}Unfortunately there must be more to it. For the target width of 496 and current height of 2731 and current width of 4096 this code inferred 331. While sharp resized the image to 330. It's not the rounding semantics and it's not the numerical precision, I check these already.
So the question stands, how does sharp auto-scale the images?
Best,
Wiktor
Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this question
I've created a smal example with the image that behaves in this way (other images work with the inference code). Running index.js in this repo, will run sharp and this inference function: The link to a reproduction repo
Activity
lovell commentedon Mar 24, 2025
Did you see the
fastShrinkOnLoadoption of resize?watjurk commentedon Mar 24, 2025
No I did not, I will take a look. But I don't understand how that would help me.
lovell commentedon Mar 26, 2025
BooleantrueWhen you use
fastShrinkOnLoadwith JPEG input, some of the resizing might be carried out via mozjpeg/libjpeg-turbo DCT scaling feature, which rounds down. If you want predictable calculations, setfastShinkOnLoadtofalseto always use sharp/libvips for all of the resizing, which rounds to nearest.watjurk commentedon Mar 28, 2025
I set
fastShrinkOnLoadtofalsein the reproduction repo. Unfortunately the result is the same. The height inferred by JS is331, while sharp resizes to330.kleisauke commentedon Mar 28, 2025
Here's what's happening:
So, the image is initially shrunk by a factor of 4 during the shrink-on-load stage:
(this rounding happens because JPEG always strictly rounds down)
The resized height (682) is then used in the next calculation:
This would result in a thumbnail of 496x330 pixels.
I'm not sure if there's an easy fix for this. I suppose it could also confuse users who expect these commands to produce the same output dimensions.
strarsis commentedon Apr 20, 2025
When in the JPEG format the height is always rounded down (floored), would stretching the image by using an explicit height and with
mode: 'fill'then degrade the image quality of the resulting, displayed JPEG image (if even so slightly)?