@napi-rs/image 1.0.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,31 +1,551 @@
1
1
  # `@napi-rs/image`
2
2
 
3
- Image processing library.
3
+ Transform and optimize images library.
4
+
5
+ See [Examples](../../example.mjs) for usage.
4
6
 
5
- ![CI](https://github.com/Brooooooklyn/image/workflows/CI/badge.svg)
6
7
  [![install size](https://packagephobia.com/badge?p=@napi-rs/image)](https://packagephobia.com/result?p=@napi-rs/image)
7
8
  [![Downloads](https://img.shields.io/npm/dm/@napi-rs/image.svg?sanitize=true)](https://npmcharts.com/compare/@napi-rs/image?minimal=true)
8
9
 
9
- ## Support matrix
10
+ ## Transformer:
11
+
12
+ This library support encode/decode these formats:
13
+
14
+ | Format | Input | Output |
15
+ | --------- | ----------------------------------------- | --------------------------------------- |
16
+ | RawPixels | RGBA 8 bits pixels | |
17
+ | JPEG | Baseline and progressive | Baseline JPEG |
18
+ | PNG | All supported color types | Same as decoding |
19
+ | BMP | ✅ | Rgb8, Rgba8, Gray8, GrayA8 |
20
+ | ICO | ✅ | ✅ |
21
+ | TIFF | Baseline(no fax support) + LZW + PackBits | Rgb8, Rgba8, Gray8 |
22
+ | WebP | No | ✅ |
23
+ | AVIF | No | ✅ |
24
+ | PNM | PBM, PGM, PPM, standard PAM | ✅ |
25
+ | DDS | DXT1, DXT3, DXT5 | No |
26
+ | TGA | ✅ | Rgb8, Rgba8, Bgr8, Bgra8, Gray8, GrayA8 |
27
+ | OpenEXR | Rgb32F, Rgba32F (no dwa compression) | Rgb32F, Rgba32F (no dwa compression) |
28
+ | farbfeld | ✅ | ✅ |
29
+
30
+ See [index.d.ts](./index.d.ts) for API reference.
31
+
32
+ ### New from Constructor
33
+
34
+ ```ts
35
+ import { Transformer } from '@napi-rs/image'
36
+
37
+ const transformer = new Transformer(input)
38
+ ```
39
+
40
+ ### New from RGBA RawPixels
41
+
42
+ ```ts
43
+ import { Transformer } from '@napi-rs/image'
44
+ import { decode } from 'blurhash'
45
+
46
+ // Uint8ClampedArray
47
+ const pixels = decode('LEHV6nWB2yk8pyo0adR*.7kCMdnj', 32, 32)
48
+ const transformer = Transformer.fromRgbaPixels(pixels, 32, 32)
49
+ ```
50
+
51
+ ### Metadata
52
+
53
+ ```ts
54
+ metadata(withExif?: boolean | undefined | null, signal?: AbortSignal | undefined | null): Promise<Metadata>
55
+
56
+ export interface Metadata {
57
+ width: number
58
+ height: number
59
+ exif?: Record<string, string> | undefined | null
60
+ orientation?: number | undefined | null
61
+ format: string
62
+ colorType: JsColorType
63
+ }
64
+
65
+ export const enum JsColorType {
66
+ /** Pixel is 8-bit luminance */
67
+ L8 = 0,
68
+ /** Pixel is 8-bit luminance with an alpha channel */
69
+ La8 = 1,
70
+ /** Pixel contains 8-bit R, G and B channels */
71
+ Rgb8 = 2,
72
+ /** Pixel is 8-bit RGB with an alpha channel */
73
+ Rgba8 = 3,
74
+ /** Pixel is 16-bit luminance */
75
+ L16 = 4,
76
+ /** Pixel is 16-bit luminance with an alpha channel */
77
+ La16 = 5,
78
+ /** Pixel is 16-bit RGB */
79
+ Rgb16 = 6,
80
+ /** Pixel is 16-bit RGBA */
81
+ Rgba16 = 7,
82
+ /** Pixel is 32-bit float RGB */
83
+ Rgb32F = 8,
84
+ /** Pixel is 32-bit float RGBA */
85
+ Rgba32F = 9
86
+ }
87
+ ```
88
+
89
+ **Example**:
90
+
91
+ ```ts
92
+ import { promises as fs } from 'fs'
93
+
94
+ import { Transformer } from '@napi-rs/image'
95
+
96
+ const WITH_EXIF_JPG = await fs.readFile('with-exif.jpg')
97
+
98
+ const decoder = new Transformer(WITH_EXIF_JPG)
99
+ const metadata = await decoder.metadata(true)
100
+ ```
101
+
102
+ The metadata will be
103
+
104
+ ```js
105
+ {
106
+ colorType: 2,
107
+ exif: {
108
+ Orientation: 'Unknown (5)',
109
+ 'Resolution Unit': 'in',
110
+ 'This image has an Exif SubIFD': '90',
111
+ 'X Resolution': '72 pixels per in',
112
+ 'Y Resolution': '72 pixels per in',
113
+ },
114
+ format: 'jpeg',
115
+ height: 450,
116
+ orientation: 5,
117
+ width: 600,
118
+ }
119
+ ```
120
+
121
+ ### Transform Image format
122
+
123
+ ```ts
124
+ import { promises as fs } from 'fs'
125
+
126
+ import { Transformer } from '@napi-rs/image'
127
+
128
+ const PNG = await fs.readFile('./un-optimized.png')
129
+
130
+ const webp = await new Transformer(PNG).webp()
131
+
132
+ await fs.writeFile('optimized.webp)
133
+ ```
134
+
135
+ #### webp
136
+
137
+ > The quality factor `quality_factor` ranges from 0 to 100 and controls the loss and quality during compression.
138
+ >
139
+ > The value 0 corresponds to low quality and small output sizes, whereas 100 is the highest quality and largest output size.
140
+ >
141
+ > https://developers.google.com/speed/webp/docs/api#simple_encoding_api
142
+ >
143
+ > Default is 90
144
+
145
+ ```ts
146
+ webp(qualityFactor: number, signal?: AbortSignal | undefined | null): Promise<Buffer>
147
+ webpSync(qualityFactor: number): Buffer
148
+ /// Encode lossless webp image
149
+ webpLossless(signal?: AbortSignal | undefined | null): Promise<Buffer>
150
+ webpLosslessSync(): Buffer
151
+ ```
152
+
153
+ #### AVIF
154
+
155
+ **Config**:
156
+
157
+ ```ts
158
+ export interface AvifConfig {
159
+ /** 0-100 scale 100 is lossless */
160
+ quality?: number | undefined | null
161
+ /** 0-100 scale */
162
+ alphaQuality?: number | undefined | null
163
+ /** rav1e preset 1 (slow) 10 (fast but crappy), default is 4 */
164
+ speed?: number | undefined | null
165
+ /** How many threads should be used (0 = match core count) */
166
+ threads?: number | undefined | null
167
+ /** set to '4:2:0' to use chroma subsampling, default '4:4:4' */
168
+ chromaSubsampling?: ChromaSubsampling | undefined | null
169
+ }
170
+
171
+ /**
172
+ * https://en.wikipedia.org/wiki/Chroma_subsampling#Types_of_sampling_and_subsampling
173
+ * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_concepts
174
+ */
175
+ export const enum ChromaSubsampling {
176
+ /**
177
+ * Each of the three Y'CbCr components has the same sample rate, thus there is no chroma subsampling. This scheme is sometimes used in high-end film scanners and cinematic post-production.
178
+ * Note that "4:4:4" may instead be wrongly referring to R'G'B' color space, which implicitly also does not have any chroma subsampling (except in JPEG R'G'B' can be subsampled).
179
+ * Formats such as HDCAM SR can record 4:4:4 R'G'B' over dual-link HD-SDI.
180
+ */
181
+ Yuv444 = 0,
182
+ /**
183
+ * The two chroma components are sampled at half the horizontal sample rate of luma: the horizontal chroma resolution is halved. This reduces the bandwidth of an uncompressed video signal by one-third.
184
+ * Many high-end digital video formats and interfaces use this scheme:
185
+ * - [AVC-Intra 100](https://en.wikipedia.org/wiki/AVC-Intra)
186
+ * - [Digital Betacam](https://en.wikipedia.org/wiki/Betacam#Digital_Betacam)
187
+ * - [Betacam SX](https://en.wikipedia.org/wiki/Betacam#Betacam_SX)
188
+ * - [DVCPRO50](https://en.wikipedia.org/wiki/DV#DVCPRO) and [DVCPRO HD](https://en.wikipedia.org/wiki/DV#DVCPRO_HD)
189
+ * - [Digital-S](https://en.wikipedia.org/wiki/Digital-S)
190
+ * - [CCIR 601](https://en.wikipedia.org/wiki/Rec._601) / [Serial Digital Interface](https://en.wikipedia.org/wiki/Serial_digital_interface) / [D1](https://en.wikipedia.org/wiki/D-1_(Sony))
191
+ * - [ProRes (HQ, 422, LT, and Proxy)](https://en.wikipedia.org/wiki/Apple_ProRes)
192
+ * - [XDCAM HD422](https://en.wikipedia.org/wiki/XDCAM)
193
+ * - [Canon MXF HD422](https://en.wikipedia.org/wiki/Canon_XF-300)
194
+ */
195
+ Yuv422 = 1,
196
+ /**
197
+ * n 4:2:0, the horizontal sampling is doubled compared to 4:1:1,
198
+ * but as the **Cb** and **Cr** channels are only sampled on each alternate line in this scheme, the vertical resolution is halved.
199
+ * The data rate is thus the same.
200
+ * This fits reasonably well with the PAL color encoding system, since this has only half the vertical chrominance resolution of [NTSC](https://en.wikipedia.org/wiki/NTSC).
201
+ * It would also fit extremely well with the [SECAM](https://en.wikipedia.org/wiki/SECAM) color encoding system,
202
+ * since like that format, 4:2:0 only stores and transmits one color channel per line (the other channel being recovered from the previous line).
203
+ * However, little equipment has actually been produced that outputs a SECAM analogue video signal.
204
+ * In general, SECAM territories either have to use a PAL-capable display or a [transcoder](https://en.wikipedia.org/wiki/Transcoding) to convert the PAL signal to SECAM for display.
205
+ */
206
+ Yuv420 = 2,
207
+ /**
208
+ * What if the chroma subsampling model is 4:0:0?
209
+ * That says to use every pixel of luma data, but that each row has 0 chroma samples applied to it. The resulting image, then, is comprised solely of the luminance data—a greyscale image.
210
+ */
211
+ Yuv400 = 3,
212
+ }
213
+ ```
214
+
215
+ ```ts
216
+ avif(options?: AvifConfig | undefined | null, signal?: AbortSignal | undefined | null): Promise<Buffer>
217
+ avifSync(options?: AvifConfig | undefined | null): Buffer
218
+ ```
219
+
220
+ #### PNG
221
+
222
+ **PngEncodeOptions**:
223
+
224
+ ```ts
225
+ export interface PngEncodeOptions {
226
+ /** Default is `CompressionType::Default` */
227
+ compressionType?: CompressionType | undefined | null
228
+ /** Default is `FilterType::NoFilter` */
229
+ filterType?: FilterType | undefined | null
230
+ }
231
+ export const enum CompressionType {
232
+ /** Default compression level */
233
+ Default = 0,
234
+ /** Fast, minimal compression */
235
+ Fast = 1,
236
+ /** High compression level */
237
+ Best = 2,
238
+ /** Huffman coding compression */
239
+ Huffman = 3,
240
+ /** Run-length encoding compression */
241
+ Rle = 4,
242
+ }
243
+ export const enum FilterType {
244
+ /**
245
+ * No processing done, best used for low bit depth greyscale or data with a
246
+ * low color count
247
+ */
248
+ NoFilter = 0,
249
+ /** Filters based on previous pixel in the same scanline */
250
+ Sub = 1,
251
+ /** Filters based on the scanline above */
252
+ Up = 2,
253
+ /** Filters based on the average of left and right neighbor pixels */
254
+ Avg = 3,
255
+ /** Algorithm that takes into account the left, upper left, and above pixels */
256
+ Paeth = 4,
257
+ /**
258
+ * Uses a heuristic to select one of the preceding filters for each
259
+ * scanline rather than one filter for the entire image
260
+ */
261
+ Adaptive = 5,
262
+ }
263
+ ```
264
+
265
+ ```ts
266
+ png(options?: PngEncodeOptions | undefined | null, signal?: AbortSignal | undefined | null): Promise<Buffer>
267
+ pngSync(options?: PngEncodeOptions | undefined | null): Buffer
268
+ ```
269
+
270
+ #### JPEG
271
+
272
+ ```ts
273
+ /** default `quality` is 90 */
274
+ jpeg(quality?: number | undefined | null, signal?: AbortSignal | undefined | null): Promise<Buffer>
275
+ /** default `quality` is 90 */
276
+ jpegSync(quality?: number | undefined | null): Buffer
277
+ ```
278
+
279
+ #### BMP
280
+
281
+ ```ts
282
+ bmp(signal?: AbortSignal | undefined | null): Promise<Buffer>
283
+ bmpSync(): Buffer
284
+ ```
285
+
286
+ #### ICO
287
+
288
+ ```ts
289
+ ico(signal?: AbortSignal | undefined | null): Promise<Buffer>
290
+ icoSync(): Buffer
291
+ ```
292
+
293
+ #### TIFF
294
+
295
+ ```ts
296
+ tiff(signal?: AbortSignal | undefined | null): Promise<Buffer>
297
+ tiffSync(): Buffer
298
+ ```
299
+
300
+ #### PNM
301
+
302
+ ```ts
303
+ pnm(signal?: AbortSignal | undefined | null): Promise<Buffer>
304
+ pnmSync(): Buffer
305
+ ```
306
+
307
+ #### TGA
308
+
309
+ ```ts
310
+ tga(signal?: AbortSignal | undefined | null): Promise<Buffer>
311
+ tgaSync(): Buffer
312
+ ```
313
+
314
+ #### Farbfeld
315
+
316
+ ```ts
317
+ farbfeld(signal?: AbortSignal | undefined | null): Promise<Buffer>
318
+ farbfeldSync(): Buffer
319
+ ```
320
+
321
+ ### Manipulate Image
322
+
323
+ #### `rotate`
324
+
325
+ > Rotate the image with exif orientation, if the input image contains no exif information, this API will have no effect.
326
+
327
+ ```ts
328
+ /**
329
+ * Rotate with exif orientation
330
+ * If the orientation param is not null,
331
+ * the new orientation value will override the exif orientation value
332
+ */
333
+ rotate(): this
334
+ ```
335
+
336
+ **Example**:
337
+
338
+ This image has orientation value `5` in exif:
339
+
340
+ <img src="../../with-exif.jpg" alt="with-exif.jpg" width="200" />
341
+
342
+ Without rotate:
343
+
344
+ ```ts
345
+ import { promises as fs } from 'fs'
346
+
347
+ import { Transformer } from '@napi-rs/image'
348
+
349
+ const WITH_EXIF_JPG = await fs.readFile('with-exif.jpg')
350
+
351
+ const imageOutputWithoutRotateWebp = await new Transformer(WITH_EXIF).resize(450 / 2).webp(75)
352
+
353
+ writeFileSync('output-exif.no-rotate.image.webp', imageOutputWithoutRotateWebp)
354
+ ```
355
+
356
+ <img src="../../output-exif.no-rotate.image.webp" alt="output-exif.no-rotate.image.webp" width="200" />
357
+
358
+ With rotate:
359
+
360
+ ```ts
361
+ import { promises as fs } from 'fs'
362
+
363
+ import { Transformer } from '@napi-rs/image'
364
+
365
+ const WITH_EXIF_JPG = await fs.readFile('with-exif.jpg')
366
+
367
+ const imageOutputWebp = await new Transformer(WITH_EXIF)
368
+ .rotate()
369
+ .resize(450 / 2)
370
+ .webp(75)
371
+
372
+ console.timeEnd('@napi-rs/image webp')
10
373
 
11
- | | node10 | node12 | node14 | node16 | node17 |
12
- | --------------------- | ------ | ------ | ------ | ------ | ------ |
13
- | Windows x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
14
- | Windows x32 | ✓ | ✓ | ✓ | ✓ | ✓ |
15
- | macOS x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
16
- | macOS arm64 (m chips) | ✓ | ✓ | ✓ | ✓ | ✓ |
17
- | Linux x64 gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
18
- | Linux x64 musl | ✓ | ✓ | ✓ | ✓ | ✓ |
19
- | Linux arm gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
20
- | Linux arm64 gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
21
- | Linux arm64 musl | ✓ | ✓ | ✓ | ✓ | ✓ |
22
- | Android arm64 | ✓ | ✓ | ✓ | ✓ | ✓ |
23
- | Android armv7 | ✓ | ✓ | ✓ | ✓ | ✓ |
24
- | FreeBSD x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
374
+ writeFileSync('output-exif.image.webp', imageOutputWebp)
375
+ ```
376
+
377
+ <img src="../../output-exif.image.webp" alt="output-exif.image.webp" width="200" />
378
+
379
+ #### `grayscale`
380
+
381
+ ```ts
382
+ /**
383
+ * Return a grayscale version of this image.
384
+ * Returns `Luma` images in most cases. However, for `f32` images,
385
+ * this will return a greyscale `Rgb/Rgba` image instead.
386
+ */
387
+ grayscale(): this
388
+ ```
389
+
390
+ #### `invert`
25
391
 
26
- ## Lossless compression
392
+ > Invert the colors of this image.
27
393
 
28
- ### `PNG`
394
+ ```ts
395
+ invert(): this
396
+ ```
397
+
398
+ #### `resize`
399
+
400
+ ```ts
401
+ /**
402
+ * Resize this image using the specified filter algorithm.
403
+ * The image is scaled to the maximum possible size that fits
404
+ * within the bounds specified by `width` and `height`.
405
+ */
406
+ resize(width: number, height?: number | undefined | null, filterType?: ResizeFilterType | undefined | null): this
407
+
408
+ export const enum ResizeFilterType {
409
+ /** Nearest Neighbor */
410
+ Nearest = 0,
411
+ /** Linear Filter */
412
+ Triangle = 1,
413
+ /** Cubic Filter */
414
+ CatmullRom = 2,
415
+ /** Gaussian Filter */
416
+ Gaussian = 3,
417
+ /** Lanczos with window 3 */
418
+ Lanczos3 = 4
419
+ }
420
+ ```
421
+
422
+ **ResizeFilterType**:
423
+
424
+ To test the different sampling filters on a real example, you can find two
425
+ examples called
426
+ [`scaledown`](https://github.com/image-rs/image/tree/master/examples/scaledown)
427
+ and
428
+ [`scaleup`](https://github.com/image-rs/image/tree/master/examples/scaleup)
429
+ in the `examples` directory of the crate source code.
430
+
431
+ Here is a 3.58 MiB
432
+ [test image](https://github.com/image-rs/image/blob/master/examples/scaledown/test.jpg)
433
+ that has been scaled down to 300x225 px:
434
+
435
+ <!-- NOTE: To test new test images locally, replace the GitHub path with `../../../docs/` -->
436
+ <div style="display: flex; flex-wrap: wrap; align-items: flex-start;">
437
+ <div style="margin: 0 8px 8px 0;">
438
+ <img src="https://raw.githubusercontent.com/image-rs/image/master/examples/scaledown/scaledown-test-near.png" title="Nearest"><br>
439
+ Nearest Neighbor
440
+ </div>
441
+ <div style="margin: 0 8px 8px 0;">
442
+ <img src="https://raw.githubusercontent.com/image-rs/image/master/examples/scaledown/scaledown-test-tri.png" title="Triangle"><br>
443
+ Linear: Triangle
444
+ </div>
445
+ <div style="margin: 0 8px 8px 0;">
446
+ <img src="https://raw.githubusercontent.com/image-rs/image/master/examples/scaledown/scaledown-test-cmr.png" title="CatmullRom"><br>
447
+ Cubic: Catmull-Rom
448
+ </div>
449
+ <div style="margin: 0 8px 8px 0;">
450
+ <img src="https://raw.githubusercontent.com/image-rs/image/master/examples/scaledown/scaledown-test-gauss.png" title="Gaussian"><br>
451
+ Gaussian
452
+ </div>
453
+ <div style="margin: 0 8px 8px 0;">
454
+ <img src="https://raw.githubusercontent.com/image-rs/image/master/examples/scaledown/scaledown-test-lcz2.png" title="Lanczos3"><br>
455
+ Lanczos with window 3
456
+ </div>
457
+ </div>
458
+
459
+ **Speed**
460
+
461
+ Time required to create each of the examples above, tested on an Intel
462
+ i7-4770 CPU with Rust 1.37 in release mode:
463
+
464
+ <table style="width: auto;">
465
+ <tr>
466
+ <th>Nearest</th>
467
+ <td>31 ms</td>
468
+ </tr>
469
+ <tr>
470
+ <th>Triangle</th>
471
+ <td>414 ms</td>
472
+ </tr>
473
+ <tr>
474
+ <th>CatmullRom</th>
475
+ <td>817 ms</td>
476
+ </tr>
477
+ <tr>
478
+ <th>Gaussian</th>
479
+ <td>1180 ms</td>
480
+ </tr>
481
+ <tr>
482
+ <th>Lanczos3</th>
483
+ <td>1170 ms</td>
484
+ </tr>
485
+ </table>
486
+
487
+ #### `blur`
488
+
489
+ > Performs a Gaussian blur on this image. <br/>
490
+ > sigma` is a measure of how much to blur by.
491
+
492
+ ```ts
493
+ blur(sigma: number): this
494
+ ```
495
+
496
+ #### `unsharpen`
497
+
498
+ > Performs an unsharpen mask on this image. <br/> `sigma` is the amount to blur the image by. <br/> `threshold` is a control of how much to sharpen.
499
+ >
500
+ > See <https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking>
501
+
502
+ ```ts
503
+ unsharpen(sigma: number, threshold: number): this
504
+ ```
505
+
506
+ #### `filter3x3`
507
+
508
+ Filters this image with the specified 3x3 kernel. Error will thrown if the kernel length is not `9`.
509
+
510
+ ```ts
511
+ filter3x3(kernel: Array<number>): this
512
+ ```
513
+
514
+ #### `adjustContrast`
515
+
516
+ > Adjust the contrast of this image.<br/> `contrast` is the amount to adjust the contrast by.<br/>
517
+ > Negative values decrease the contrast and positive values increase the contrast.
518
+
519
+ ```ts
520
+ adjustContrast(contrast: number): this
521
+ ```
522
+
523
+ #### `brighten`
524
+
525
+ > Brighten the pixels of this image.<br/> `value` is the amount to brighten each pixel by. <br/>
526
+ > Negative values decrease the brightness and positive values increase it.
527
+
528
+ ```ts
529
+ brighten(brightness: number): this
530
+ ```
531
+
532
+ #### `huerotate`
533
+
534
+ > Hue rotate the supplied image.<br/> `value` is the degrees to rotate each pixel by.
535
+ > 0 and 360 do nothing, the rest rotates by the given degree value.
536
+ > just like the css webkit filter hue-rotate(180)
537
+
538
+ ```ts
539
+ huerotate(hue: number): this
540
+ ```
541
+
542
+ ## Optimize PNG
543
+
544
+ ### Lossless compression
545
+
546
+ Lossless optimize PNG powered by [oxipng](https://github.com/shssoichiro/oxipng).
547
+
548
+ **PNGLosslessOptions**
29
549
 
30
550
  ```ts
31
551
  export interface PNGLosslessOptions {
@@ -72,10 +592,57 @@ export interface PNGLosslessOptions {
72
592
  /** Whether to use heuristics to pick the best filter and compression */
73
593
  useHeuristics?: boolean | undefined | null
74
594
  }
75
- export function losslessCompressPng(input: Buffer, options?: PNGLosslessOptions | undefined | null): Buffer
76
595
  ```
77
596
 
78
- ### `JPEG`
597
+ ```ts
598
+ export function losslessCompressPng(
599
+ input: Buffer,
600
+ options?: PNGLosslessOptions | undefined | null,
601
+ signal?: AbortSignal | undefined | null,
602
+ ): Promise<Buffer>
603
+ export function losslessCompressPngSync(input: Buffer, options?: PNGLosslessOptions | undefined | null): Buffer
604
+ ```
605
+
606
+ ### Lossy compression
607
+
608
+ Powered by [pngquant](https://github.com/ImageOptim/libimagequant), converts RGBA images to palette-based 8-bit indexed images, _including_ alpha component.
609
+
610
+ **PngQuantOptions**:
611
+
612
+ ```ts
613
+ export interface PngQuantOptions {
614
+ /** default is 70 */
615
+ minQuality?: number | undefined | null
616
+ /** default is 99 */
617
+ maxQuality?: number | undefined | null
618
+ /**
619
+ * 1- 10
620
+ * Faster speeds generate images of lower quality, but may be useful for real-time generation of images.
621
+ * default: 5
622
+ */
623
+ speed?: number | undefined | null
624
+ /**
625
+ * Number of least significant bits to ignore.
626
+ * Useful for generating palettes for VGA, 15-bit textures, or other retro platforms.
627
+ */
628
+ posterization?: number | undefined | null
629
+ }
630
+ ```
631
+
632
+ ```ts
633
+ export function pngQuantize(
634
+ input: Buffer,
635
+ options?: PngQuantOptions | undefined | null,
636
+ signal?: AbortSignal | undefined | null,
637
+ ): Promise<Buffer>
638
+ export function pngQuantizeSync(input: Buffer, options?: PngQuantOptions | undefined | null): Buffer
639
+ ```
640
+
641
+ ## Optimize JPEG
642
+
643
+ Lossy and Lossless JPEG compression powered by [mozjpeg](https://github.com/mozilla/mozjpeg).
644
+
645
+ **JpegCompressOptions**:
79
646
 
80
647
  ```ts
81
648
  export interface JpegCompressOptions {
@@ -87,5 +654,17 @@ export interface JpegCompressOptions {
87
654
  */
88
655
  optimizeScans?: boolean | undefined | null
89
656
  }
90
- export function compressJpeg(input: Buffer, options?: JpegCompressOptions | undefined | null): Buffer
91
657
  ```
658
+
659
+ ```ts
660
+ export function compressJpeg(
661
+ input: Buffer,
662
+ options?: JpegCompressOptions | undefined | null,
663
+ signal?: AbortSignal | undefined | null,
664
+ ): Promise<Buffer>
665
+ export function compressJpegSync(input: Buffer, options?: JpegCompressOptions | undefined | null): Buffer
666
+ ```
667
+
668
+ ## Credits
669
+
670
+ See [Credits](./credits.md)