Skip to content

Commit

Permalink
remove progressive jpeg transformation
Browse files Browse the repository at this point in the history
  • Loading branch information
georges-gomes committed Apr 19, 2024
1 parent 7f92f3d commit f54a321
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ <h1>Above the fold optimizations</h1>

<p>You want images above the fold to arrive as soon as possible.</p>

<h2>Non-progressive JPEG => Progressive JPEG</h2>
<h2>Non-progressive JPEG</h2>
<img src="./redpanda_500x335.jpg" alt="Red panada in the bush" />

<h2>Transparent PNG => WebP</h2>
<h2>Transparent PNG</h2>
<img src="./music_500x447.png" alt="Girl listening to music" />

<h2>Non-transparent PNG => Progressive JPEG</h2>
<h2>Non-transparent PNG</h2>
<img src="./html.to.design.png" alt="html.to.design banner" />

<h2>JPEG in picture tag => no sources required</h2>
<h2>JPEG above the fold</h2>
<picture>
<img src="./redpanda_500x335.jpg" alt="Red panada in the bush" />
<img src="./water.jpg" alt="Beach" />
</picture>

<div>
Expand All @@ -37,7 +37,7 @@ <h2>JPEG in picture tag => no sources required</h2>

<h2>JPEG below the fold</h2>
<picture>
<img src="./water_below.jpg" alt="Beach" />
<img src="./water.jpg" alt="Beach" />
</picture>
</body>
</html>
14 changes: 1 addition & 13 deletions src/compressors/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@ export type Image = {

export type ImageOutputOptions = {
resize?: sharp.ResizeOptions;
toFormat?:
| 'webp'
| 'avif'
| 'png'
| 'jpeg'
| 'jpeg+progressive'
| 'unchanged';
toFormat?: 'webp' | 'avif' | 'png' | 'jpeg' | 'unchanged';
};

function createWebpOptions(opt: WebpOptions | undefined): sharp.WebpOptions {
Expand Down Expand Up @@ -119,12 +113,6 @@ export async function compressImage(
sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options } || {});
outputFormat = 'jpg';
break;
case 'jpeg+progressive':
sharpFile = sharpFile.jpeg(
{ ...config.image.jpeg.options, progressive: true } || {}
);
outputFormat = 'jpg';
break;
case 'png':
sharpFile = sharpFile.png({ ...config.image.png.options } || {});
outputFormat = 'png';
Expand Down
156 changes: 66 additions & 90 deletions src/optimize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,21 +433,9 @@ async function processImage(

const isInPicture: boolean = img.parent()?.is('picture');

let srcToFormat: 'webp' | 'avif' | 'jpeg' | 'jpeg+progressive' | 'unchanged' =
'unchanged';
if (isAboveTheFold) {
// Above the fold, nothing beats progressive JPEG for LCP
// But jpeg doesn't support transparency
if ((await originalImage.getImageMeta())?.isOpaque) {
srcToFormat = 'jpeg+progressive';
} else {
srcToFormat =
(await originalImage.getMime()) === 'image/avif' ||
(await originalImage.getMime()) === 'image/webp'
? 'unchanged'
: 'webp';
}
} else if (isInPicture) {
let srcToFormat: 'webp' | 'avif' | 'jpeg' | 'unchanged' = 'unchanged';

if (isInPicture) {
// srcToFormat should not change if in picture
} else {
// Let's go to avif -> avif , webp -> webp, else * -> webp
Expand All @@ -465,11 +453,7 @@ async function processImage(
});
}

if (
newImage?.data &&
(newImage.data.length < (await originalImage.getLen()) ||
srcToFormat === 'jpeg+progressive') // Progressive override all even if worse
) {
if (newImage?.data && newImage.data.length < (await originalImage.getLen())) {
const newFilename = path.join(
path.dirname(originalImage.filePathAbsolute),
path.basename(
Expand Down Expand Up @@ -627,84 +611,76 @@ async function processImage(
if (isInPicture) {
const picture = img.parent();

if (isAboveTheFold && srcToFormat === 'jpeg+progressive') {
// We were previously removing <source> elements here
// but they may be used to express art direction
// + WebP or AVIF may be a lot smaller than JPEG progressive...
// So for the moment we do nothing else
// just add the JPEG progressive
} else {
// List of possible sources to generate
//
const sourcesToGenerate: {
mime: ImageMimeType;
format: 'avif' | 'webp';
}[] = [];

// Only try to generate better images
// TODO generate more compatible formats (avif => webp/jpeg/png)
//
const mime = await originalImage.getMime();
switch (mime) {
case 'image/png':
case 'image/jpeg':
// List of possible sources to generate
//
const sourcesToGenerate: {
mime: ImageMimeType;
format: 'avif' | 'webp';
}[] = [];

// Only try to generate better images
// TODO generate more compatible formats (avif => webp/jpeg/png)
//
const mime = await originalImage.getMime();
switch (mime) {
case 'image/png':
case 'image/jpeg':
sourcesToGenerate.push({
mime: 'image/webp',
format: 'webp',
});
case 'image/webp':
// Only add AVIF source for lossy images
// AVIF don't perform well on lossless images
// PNG and WebP will perform better
// https://www.reddit.com/r/jpegxl/comments/l9ta2u/how_does_lossless_jpegxl_compared_to_png/
// https://twitter.com/jonsneyers/status/1346389917816008704?s=19
const isLossless =
mime === 'image/png' ||
(mime === 'image/webp' &&
(await originalImage.getImageMeta())?.isLossless);
if (!isLossless) {
sourcesToGenerate.push({
mime: 'image/webp',
format: 'webp',
mime: 'image/avif',
format: 'avif',
});
case 'image/webp':
// Only add AVIF source for lossy images
// AVIF don't perform well on lossless images
// PNG and WebP will perform better
// https://www.reddit.com/r/jpegxl/comments/l9ta2u/how_does_lossless_jpegxl_compared_to_png/
// https://twitter.com/jonsneyers/status/1346389917816008704?s=19
const isLossless =
mime === 'image/png' ||
(mime === 'image/webp' &&
(await originalImage.getImageMeta())?.isLossless);
if (!isLossless) {
sourcesToGenerate.push({
mime: 'image/avif',
format: 'avif',
});
}
case 'image/avif':
// Nothing better
break;
}

const sizes = img.attr('sizes');

for (const s of sourcesToGenerate.reverse()) {
const sourceWithThisMimeType = picture.children(
`source[type="${s.mime}"]`
);
if (sourceWithThisMimeType.length > 0) {
// Ignore the creation of sources that already exist
continue;
}
case 'image/avif':
// Nothing better
break;
}

const srcset = await generateSrcSet(
htmlfile,
originalImage,
undefined,
undefined,
{
toFormat: s.format,
}
);
const sizes = img.attr('sizes');

if (!srcset) {
// No sourceset generated
// Image is too small or can't be compressed better
continue;
for (const s of sourcesToGenerate.reverse()) {
const sourceWithThisMimeType = picture.children(
`source[type="${s.mime}"]`
);
if (sourceWithThisMimeType.length > 0) {
// Ignore the creation of sources that already exist
continue;
}

const srcset = await generateSrcSet(
htmlfile,
originalImage,
undefined,
undefined,
{
toFormat: s.format,
}
);

const source = `<source ${
sizes ? `sizes="${sizes}"` : ''
} srcset="${srcset}" type="${s.mime}">`;
img.before(source); // Append before this way existing sources are always top priority
if (!srcset) {
// No sourceset generated
// Image is too small or can't be compressed better
continue;
}

const source = `<source ${
sizes ? `sizes="${sizes}"` : ''
} srcset="${srcset}" type="${s.mime}">`;
img.before(source); // Append before this way existing sources are always top priority
}
}
}
Expand Down

0 comments on commit f54a321

Please sign in to comment.