11ty / eleventy-img

Utility to perform build-time image transformations.
https://www.11ty.dev/docs/plugins/image/
436 stars 54 forks source link

Resizing Feature Request: output largest image without upscaling #184

Closed pavlo-tk closed 8 months ago

pavlo-tk commented 1 year ago

Hi folks,

I was working on resizing images with the plugin and was missing what seemed like an obvious feature. I'll explain my scenario below as well as the temporary solution I used to get around it.

eleventy-img version: 3.1.0

The config:

{
  widths: [640, 2560],
  formats: ['avif', 'webp', 'auto']
}

The input image is 2048 px wide.

The current behavior is that the plugin outputs only one version of the image: the 640 px one. And that's all. What I was expecting is to have two images generated: the 640 px and the 2048 px because we don't upscale.

If I were to use the config below:

{
  widths: [2560],
  formats: ['avif', 'webp', 'auto']
}

Then the plugin outputs the 2048 px version as expected.


So, the question is: if we have several widths set and the input image is smaller than the largest size from the widths array, can we output its largest version without upscaling (in this case it would mean outputting the original 2048 px)?


For the time being, I worked around this with the following code using Sharp directly and assembling the widths array for the plugin on the fly:

const sharpMetadata = await sharp(srcFile).metadata()
const widths = [640];

if (sharpMetadata.width > 2560) {
  widths.push(2560);
} else {
  widths.push(sharpMetadata.width);
}
zachleat commented 1 year ago

Just so we’re on the same page, does widths: [640, "auto"], accommodate this use case?

pavlo-tk commented 1 year ago

Unfortunately, no. The auto means "keep the original width".

So, using widths: [640, 'auto'] the plugin will work like this:

  1. If the input image is 2048px, it will generate 640px and 2048px versions.
  2. If the input image is 3000px, it will generate 640px and 3000px versions.

The idea behind my request/question is to tell the plugin the max width of the generated images, but don't upscale.

What I wanted to achieve with widths: [640, 2560] is below:

  1. If the input image is 2048px, it would generate 640px and 2048px versions (since 2048 < 2560 and we don't upscale).
  2. If the input image is 3000px, it would generate 640px and 2560px versions.

This behavior that I was expecting does seem logical, doesn't it?

JaredReisinger commented 1 year ago

I've encountered this same issue; I have a photo gallery with some already cropped images, and some original source (very large) images, and I'd like to specify just a couple of widths, including a "don't go any bigger than this" size. (Because I don't want the hosting/bandwidth of a 10MP+ image for those very large images.)

I'm tempted to submit a PR, since I know exactly where to make this change in Image.getValidWidths(). Roughly, if upscaling is not allowed, any specified widths larger than the original width are converted/capped to the original width in that first-line widths.map(), and then later deduped. (I don't love conflating the no-upscaling logic into that first widths.map() call, but on the other hand it also makes the later valid.filter() completely unnecessary, since given this suggested change, there will not be larger-than-original widths in the valid list in the no-upscaling case any more.)

But in any case, this isn't even marked as an enhancement or needs-votes, yet, so I'm not sure how you'd feel about the submission. (Maybe the votes are more for prioritizing your time, though, and outside contributions aren't bound by them?)