Skip to content

Commit 8ed2ce8

Browse files
committed
Adds minimumThreshold option for diffs between widths when original size is smaller than width requested
#190 (comment)
1 parent 4faf1ef commit 8ed2ce8

File tree

2 files changed

+81
-7
lines changed

2 files changed

+81
-7
lines changed

img.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ const {default: PQueue} = require("p-queue");
77
const getImageSize = require("image-size");
88
const sharp = require("sharp");
99
const brotliSize = require("brotli-size");
10-
const debug = require("debug")("EleventyImg");
10+
const {RemoteAssetCache, queue} = require("@11ty/eleventy-fetch");
1111

1212
const svgHook = require("./format-hooks/svg");
13-
14-
const {RemoteAssetCache, queue} = require("@11ty/eleventy-fetch");
1513
const MemoryCache = require("./memory-cache");
1614

15+
const debug = require("debug")("EleventyImg");
16+
1717
const globalOptions = {
1818
widths: [null],
1919
formats: ["webp", "jpeg"], // "png", "svg", "avif"
@@ -68,6 +68,13 @@ const globalOptions = {
6868
// Advanced
6969
useCacheValidityInHash: true,
7070

71+
// When the original width is smaller than the desired output width, this is the minimum size difference
72+
// between the next smallest image width that will generate one extra width in the output.
73+
// e.g. when using `widths: [400, 800]`, the source image would need to be at least (400 * 1.2 =) 500px wide
74+
// to generate two outputs (400px, 500px). If the source image is less than 500px, only one output will
75+
// be generated (400px).
76+
// Read more at https://github.com/11ty/eleventy-img/issues/184 and https://github.com/11ty/eleventy-img/pull/190
77+
minimumThreshold: 1.25,
7178
};
7279

7380
const MIME_TYPES = {
@@ -184,7 +191,7 @@ class Image {
184191
return this._contents;
185192
}
186193

187-
static getValidWidths(originalWidth, widths = [], allowUpscale = false) {
194+
static getValidWidths(originalWidth, widths = [], allowUpscale = false, minimumThreshold = 1) {
188195
// replace any falsy values with the original width
189196
let valid = widths.map(width => !width || width === 'auto' ? originalWidth : width);
190197

@@ -195,7 +202,19 @@ class Image {
195202
// This ensures that if a larger width has been requested, we're at least providing the closest
196203
// non-upscaled image that we can.
197204
if (!allowUpscale) {
198-
valid = valid.map(width => width > originalWidth ? originalWidth : width);
205+
let lastWidthWasBigEnough = true; // first one is always valid
206+
valid = valid.sort((a, b) => a - b).map(width => {
207+
if(width > originalWidth) {
208+
if(lastWidthWasBigEnough) {
209+
return originalWidth;
210+
}
211+
return -1;
212+
}
213+
214+
lastWidthWasBigEnough = originalWidth > Math.floor(width * minimumThreshold);
215+
216+
return width;
217+
}).filter(width => width > 0);
199218
}
200219

201220
// Remove duplicates (e.g., if null happens to coincide with an explicit width
@@ -489,7 +508,7 @@ class Image {
489508
continue;
490509
}
491510
} else { // not outputting SVG (might still be SVG input though!)
492-
let widths = Image.getValidWidths(metadata.width, this.options.widths, metadata.format === "svg" && this.options.svgAllowUpscale);
511+
let widths = Image.getValidWidths(metadata.width, this.options.widths, metadata.format === "svg" && this.options.svgAllowUpscale, this.options.minimumThreshold);
493512
for(let width of widths) {
494513
// Warning: if this is a guess via statsByDimensionsSync and that guess is wrong
495514
// The aspect ratio will be wrong and any height/widths returned will be wrong!
@@ -599,7 +618,7 @@ class Image {
599618
.then(info => {
600619
stat.size = info.size;
601620
return stat;
602-
})
621+
})
603622
);
604623
} else {
605624
outputFilePromises.push(sharpInstance.toBuffer({ resolveWithObject: true }).then(({ data, info }) => {

test/test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,61 @@ test("Try to use a width larger than original (with a null in there)", async t =
219219
t.is(stats.jpeg[0].width, 1280);
220220
});
221221

222+
test("Minimum width threshold (valid)", async t => {
223+
// original is 1280
224+
let stats = await eleventyImage("./test/bio-2017.jpg", {
225+
widths: [400, 1300],
226+
formats: ["jpeg"],
227+
outputDir: "./test/img/",
228+
dryRun: true,
229+
});
230+
t.is(stats.jpeg.length, 2);
231+
t.is(stats.jpeg[0].outputPath, path.join("test/img/KkPMmHd3hP-400.jpeg"));
232+
t.is(stats.jpeg[0].width, 400);
233+
t.is(stats.jpeg[1].outputPath, path.join("test/img/KkPMmHd3hP-1280.jpeg"));
234+
t.is(stats.jpeg[1].width, 1280);
235+
});
236+
237+
238+
test("Minimum width threshold (one gets rejected)", async t => {
239+
// original is 1280
240+
let stats = await eleventyImage("./test/bio-2017.jpg", {
241+
widths: [1200, 1300],
242+
formats: ["jpeg"],
243+
outputDir: "./test/img/",
244+
dryRun: true,
245+
});
246+
t.is(stats.jpeg.length, 1);
247+
t.is(stats.jpeg[0].outputPath, path.join("test/img/KkPMmHd3hP-1200.jpeg"));
248+
t.is(stats.jpeg[0].width, 1200);
249+
});
250+
251+
test("Minimum width threshold (one gets rejected, higher max)", async t => {
252+
// original is 1280
253+
let stats = await eleventyImage("./test/bio-2017.jpg", {
254+
widths: [1200, 1500],
255+
formats: ["jpeg"],
256+
outputDir: "./test/img/",
257+
dryRun: true,
258+
});
259+
t.is(stats.jpeg.length, 1);
260+
t.is(stats.jpeg[0].outputPath, path.join("test/img/KkPMmHd3hP-1200.jpeg"));
261+
t.is(stats.jpeg[0].width, 1200);
262+
});
263+
264+
test("Minimum width threshold (one gets rejected, lower min)", async t => {
265+
// original is 1280
266+
let stats = await eleventyImage("./test/bio-2017.jpg", {
267+
widths: [1100, 1500],
268+
formats: ["jpeg"],
269+
outputDir: "./test/img/",
270+
dryRun: true,
271+
});
272+
t.is(stats.jpeg.length, 1);
273+
t.is(stats.jpeg[0].outputPath, path.join("test/img/KkPMmHd3hP-1100.jpeg"));
274+
t.is(stats.jpeg[0].width, 1100);
275+
});
276+
222277
test("Just falsy width", async t => {
223278
let stats = await eleventyImage("./test/bio-2017.jpg", {
224279
widths: [null],

0 commit comments

Comments
 (0)