Skip to content

Commit 71ba3f5

Browse files
authored
Merge pull request #21 from 343dev/improvement/gif2webp
Replace gif2webp with sharp
2 parents a739750 + f933671 commit 71ba3f5

10 files changed

+161
-296
lines changed

.optimiztrc.cjs

+11-35
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = {
1515
},
1616
// https://github.com/google/guetzli
1717
lossless: {
18-
quality: 90, // visual quality to aim for, expressed as a JPEG quality value
18+
quality: 90, // visual quality to aim for, expressed as a JPEG quality value; should be >= 84, otherwise the output will have noticeable artifacts
1919
memlimit: 6000, // memory limit in MB; guetzli will fail if unable to stay under the limit
2020
nomemlimit: false, // do not limit memory usage
2121
},
@@ -39,7 +39,7 @@ module.exports = {
3939
adaptiveFiltering: true,
4040
palette: false,
4141
quality: 100,
42-
effort: 7,
42+
effort: 10,
4343
colors: 256,
4444
dither: 1.0,
4545
},
@@ -88,67 +88,43 @@ module.exports = {
8888
// https://sharp.pixelplumbing.com/api-output#avif
8989
avif: {
9090
lossy: {
91-
quality: 50, // quality, integer 1-100
91+
quality: 64, // quality, integer 1-100
9292
lossless: false, // use lossless compression
9393
effort: 4, // CPU effort, between 0 (fastest) and 9 (slowest)
9494
chromaSubsampling: '4:4:4', // set to '4:2:0' to use chroma subsampling
9595
},
9696
lossless: {
97-
quality: 50,
97+
quality: 100,
9898
lossless: true,
99-
effort: 4,
99+
effort: 9,
100100
chromaSubsampling: '4:4:4',
101101
},
102102
},
103103

104104
// https://sharp.pixelplumbing.com/api-output#webp
105105
webp: {
106106
lossy: {
107-
quality: 85, // quality, integer 1-100
108-
alphaQuality: 100, // quality of alpha layer, integer 0-100
107+
quality: 82, // quality, integer 1-100
108+
alphaQuality: 82, // quality of alpha layer, integer 0-100
109109
lossless: false, // use lossless compression mode
110110
nearLossless: false, // use near_lossless compression mode
111111
smartSubsample: false, // use high quality chroma subsampling
112112
preset: 'default', // named preset for preprocessing/filtering, one of: default, photo, picture, drawing, icon, text
113113
effort: 4, // CPU effort, between 0 (fastest) and 6 (slowest)
114-
minSize: false, // prevent use of animation key frames to minimise file size (slow)
114+
minSize: true, // prevent use of animation key frames to minimise file size (slow)
115115
mixed: false, // allow mixture of lossy and lossless animation frames (slow)
116116
},
117117
lossless: {
118-
quality: 85,
118+
quality: 100,
119119
alphaQuality: 100,
120120
lossless: true,
121121
nearLossless: false,
122122
smartSubsample: false,
123-
effort: 4,
123+
preset: 'default',
124+
effort: 6,
124125
minSize: false,
125126
mixed: false,
126127
},
127128
},
128-
129-
// https://developers.google.com/speed/webp/docs/gif2webp
130-
webpGif: {
131-
lossy: {
132-
lossy: true, // encode image using lossy compression
133-
mixed: false, // for each frame in the image, pick lossy or lossless compression heuristically
134-
q: 75, // in case of lossy compression, a small factor produces a smaller file with lower quality; best quality is achieved by using a value of 100
135-
m: 6, // compression method (0=fast, 6=slowest)
136-
min_size: true, // minimize output size; can be combined with -q, -m, -lossy or -mixed options
137-
f: 0, // filter strength (0=off..100); for lossy encoding only
138-
metadata: 'xmp', // comma separated list of metadata to copy from the input to the output if present; valid values: all, none, icc, xmp
139-
loop_compatibility: false, // use compatibility mode for Chrome version prior to M62 (inclusive)
140-
mt: true, // use multi-threading if available
141-
},
142-
lossless: {
143-
lossy: false,
144-
mixed: false,
145-
q: 100,
146-
m: false,
147-
min_size: false,
148-
metadata: 'xmp',
149-
loop_compatibility: false,
150-
mt: true,
151-
},
152-
},
153129
},
154130
};

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 10.0.0 (2024-10-28)
4+
5+
Breaking Changes:
6+
7+
- Supported Node.js version updated to 18.18 or higher.
8+
- Replaced “gif2webp” with “sharp” for GIF-to-WebP conversions.
9+
- Removed the “webpGif” section from [.optimiztrc.cjs](.optimiztrc.cjs).
10+
- Fine-tuned params in [.optimiztrc.cjs](.optimiztrc.cjs).
11+
12+
313
## 9.1.1 (2024-10-22)
414

515
- Replaced [imagemin/guetzli-bin](https://github.com/imagemin/guetzli-bin) with [343dev/guetzli](https://github.com/343dev/guetzli).

Dockerfile

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:18.17.0-bullseye-slim
1+
FROM node:18.18.0-bullseye-slim
22
LABEL maintainer="Andrey Warkentin (https://github.com/343dev)"
33

44
WORKDIR /app
@@ -8,13 +8,13 @@ COPY . .
88
ENV NODE_ENV="production"
99

1010
RUN apt update \
11-
&& apt install --yes --no-install-recommends build-essential libpng16-16 libjpeg62-turbo libjpeg62-turbo-dev libpng-dev pkg-config dh-autoreconf \
12-
&& npm ci \
13-
&& npm link \
14-
&& npm cache clean --force \
15-
&& apt purge --yes build-essential pkg-config libpng-dev libjpeg62-turbo-dev dh-autoreconf \
16-
&& apt autoremove --yes --purge \
17-
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man
11+
&& apt install --yes --no-install-recommends build-essential libpng16-16 libjpeg62-turbo libjpeg62-turbo-dev libpng-dev pkg-config dh-autoreconf \
12+
&& npm ci \
13+
&& npm link \
14+
&& npm cache clean --force \
15+
&& apt purge --yes build-essential pkg-config libpng-dev libjpeg62-turbo-dev dh-autoreconf \
16+
&& apt autoremove --yes --purge \
17+
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man
1818

1919
WORKDIR /src
2020

MIGRATION.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Migration
22

3+
## 9.1.1 → 10.0.0
4+
5+
Node.js version must be 18.18 or higher.
6+
7+
The “sharp” module now replaces “gif2webp” for converting GIFs to WebP. Consequently, the “webpGif” section in [.optimiztrc.cjs](.optimiztrc.cjs) has been replaced with a “webp” section. If you use a custom configuration file, please remove any “webpGif” section.
8+
9+
Default settings in Optimizt’s configuration have been updated. Note that in lossless mode, image processing times may increase slightly, but resulting file sizes are expected to be smaller compared to previous versions.
10+
11+
312
## 8.0.1 → 9.0.0
413

514
The main change in the new version is how Optimizt handles file processing. Before, the result was stored in memory until all files were processed. This could cause the app to crash if it ran out of memory, leading to a loss of optimization results. Now, each file is processed one by one, and the result is saved to disk immediately. Logging events also happen in real-time, instead of waiting until all files are done.

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ For SVG files, the settings in Lossy and Lossless modes are identical.
7272

7373
## Configuration
7474

75-
Image processing is done using [sharp](https://github.com/lovell/sharp) for [JPEG](https://sharp.pixelplumbing.com/api-output#jpeg), [PNG](https://sharp.pixelplumbing.com/api-output#png), [WebP](https://sharp.pixelplumbing.com/api-output#webp), and [AVIF](https://sharp.pixelplumbing.com/api-output#avif), while SVG is processed by [svgo](https://github.com/svg/svgo).
75+
Image processing is done using [sharp](https://github.com/lovell/sharp) for [JPEG](https://sharp.pixelplumbing.com/api-output#jpeg), [PNG](https://sharp.pixelplumbing.com/api-output#png), [WebP](https://sharp.pixelplumbing.com/api-output#webp), and [AVIF](https://sharp.pixelplumbing.com/api-output#avif).
7676

77-
For GIF, [gifsicle](https://github.com/kohler/gifsicle) is used, and for converting GIF to WebP — [gif2webp](https://developers.google.com/speed/webp/docs/gif2webp).
77+
SVG is processed by [svgo](https://github.com/svg/svgo), while for GIF, [gifsicle](https://github.com/kohler/gifsicle) is used.
7878

7979
> [!NOTE]
8080
> In Lossless mode for JPEG, we use [Guetzli](https://github.com/google/guetzli), which offers high level of compression with good visual quality. However, repeated optimization may degrade visual quality.

README.ru.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ find . -iname \*.jpg -exec optimizt {} +
7272

7373
## Конфигурация
7474

75-
Операции с [JPEG](https://sharp.pixelplumbing.com/api-output#jpeg), [PNG](https://sharp.pixelplumbing.com/api-output#png), [WebP](https://sharp.pixelplumbing.com/api-output#webp) и [AVIF](https://sharp.pixelplumbing.com/api-output#avif) производятся с помощью библиотеки [sharp](https://github.com/lovell/sharp), а SVG обрабатывается с помощью утилиты [svgo](https://github.com/svg/svgo).
75+
Операции с [JPEG](https://sharp.pixelplumbing.com/api-output#jpeg), [PNG](https://sharp.pixelplumbing.com/api-output#png), [WebP](https://sharp.pixelplumbing.com/api-output#webp) и [AVIF](https://sharp.pixelplumbing.com/api-output#avif) производятся с помощью библиотеки [sharp](https://github.com/lovell/sharp).
7676

77-
Для оптимизации GIF используется [gifsicle](https://github.com/kohler/gifsicle), а для конвертации GIF в WebP — [gif2webp](https://developers.google.com/speed/webp/docs/gif2webp).
77+
SVG обрабатывается с помощью утилиты [svgo](https://github.com/svg/svgo), а для оптимизации GIF используется [gifsicle](https://github.com/kohler/gifsicle).
7878

7979
> [!NOTE]
8080
> В режиме Lossless для оптимизации JPEG используется энкодер [Guetzli](https://github.com/google/guetzli), который позволяет получить высокий уровень компрессии и при этом сохранить хорошее визуальное качество изображения. Но, нужно иметь в виду, что при повторной оптимизации файла размер может уменьшаться за счёт деградации визуального качества изображения.

convert.js

+3-23
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import fs from 'node:fs';
22
import os from 'node:os';
33
import path from 'node:path';
44

5-
import execBuffer from 'exec-buffer';
6-
import gif2webp from 'gif2webp-bin';
75
import pLimit from 'p-limit';
86
import sharp from 'sharp';
97

@@ -20,7 +18,6 @@ import {
2018
logProgress,
2119
logProgressVerbose,
2220
} from './lib/log.js';
23-
import { optionsToArguments } from './lib/options-to-arguments.js';
2421
import { parseImageMetadata } from './lib/parse-image-metadata.js';
2522
import { programOptions } from './lib/program-options.js';
2623
import { showTotal } from './lib/show-total.js';
@@ -53,7 +50,6 @@ export async function convert({ filePaths, config }) {
5350

5451
const avifConfig = getConfig('avif');
5552
const webpConfig = getConfig('webp');
56-
const webpGifConfig = getConfig('webpGif');
5753

5854
const cpuCount = os.cpus().length;
5955
const tasksSimultaneousLimit = pLimit(cpuCount);
@@ -78,13 +74,11 @@ export async function convert({ filePaths, config }) {
7874
}
7975

8076
if (shouldConvertToWebp) {
81-
const isGif = path.extname(filePath.input).toLowerCase() === '.gif';
82-
8377
accumulator.push(
8478
tasksSimultaneousLimit(
8579
() => processFile({
8680
filePath,
87-
config: (isGif ? webpGifConfig : webpConfig) || {},
81+
config: webpConfig || {},
8882
progressBarContainer,
8983
progressBar,
9084
totalSize,
@@ -187,23 +181,9 @@ async function processWebp({ fileBuffer, config }) {
187181
const imageMetadata = await parseImageMetadata(fileBuffer);
188182
checkImageFormat(imageMetadata.format);
189183

190-
if (imageMetadata.format === 'gif') {
191-
return execBuffer({
192-
bin: gif2webp,
193-
args: [
194-
...optionsToArguments({
195-
options: config,
196-
prefix: '-',
197-
}),
198-
execBuffer.input,
199-
'-o',
200-
execBuffer.output,
201-
],
202-
input: fileBuffer,
203-
});
204-
}
184+
const isAnimated = imageMetadata.pages > 1;
205185

206-
return sharp(fileBuffer)
186+
return sharp(fileBuffer, { animated: isAnimated })
207187
.rotate() // Rotate image using information from EXIF Orientation tag
208188
.webp(config)
209189
.toBuffer();

0 commit comments

Comments
 (0)