Image

Image processing library.

This library support encode/decode these formats:

FormatInputOutput
RawPixelsRGBA 8 bits pixels
JPEGBaseline and progressiveBaseline JPEG
PNGAll supported color typesSame as decoding
BMPRgb8, Rgba8, Gray8, GrayA8
ICO
TIFFBaseline(no fax support) + LZW + PackBitsRgb8, Rgba8, Gray8
WebP
AVIF
PNMPBM, PGM, PPM, standard PAM
DDSDXT1, DXT3, DXT5No
TGARgb8, Rgba8, Bgr8, Bgra8, Gray8, GrayA8
OpenEXRRgb32F, Rgba32F (no dwa compression)Rgb32F, Rgba32F (no dwa compression)
farbfeld
SVG

See index.d.ts (opens in a new tab) for API reference.

CI

Support matrix

node10node12node14node16node18
Windows x64
Windows x32
macOS x64
macOS arm64 (m chips)
Linux x64 gnu
Linux x64 musl
Linux arm gnu
Linux arm64 gnu
Linux arm64 musl
Android arm64
Android armv7
FreeBSD x64

Performance

System info

OS: macOS 12.3.1 21E258 arm64
Host: MacBookPro18,2
Kernel: 21.4.0
Shell: zsh 5.8
CPU: Apple M1 Max
GPU: Apple M1 Max
Memory: 9539MiB / 65536MiB
node bench/bench.mjs

@napi-rs/image 202 ops/s
sharp 169 ops/s
In webp suite, fastest is @napi-rs/image
@napi-rs/image 26 ops/s
sharp 24 ops/s
In avif suite, fastest is @napi-rs/image
UV_THREADPOOL_SIZE=10 node bench/bench.mjs

@napi-rs/image 431 ops/s
sharp 238 ops/s
In webp suite, fastest is @napi-rs/image
@napi-rs/image 36 ops/s
sharp 32 ops/s
In avif suite, fastest is @napi-rs/image

@napi-rs/image

See Full documentation for @napi-rs/image

Example

You can clone this repo and run the following command to taste the example below:

  • yarn install
  • node example.mjs

The anime girl


CC-BY-SA 3.0 (opens in a new tab) by Niabot (opens in a new tab)

Optimization
Raw
Optimized
Raw SizeOptimized Size
losslessCompressPng()
Lossless
un-optimized.pngoptimized-lossless.png888K736K
pngQuantize({ maxQuality: 75 })
Lossy
un-optimized.pngoptimized-lossy.png888K248K
compressJpeg()
Lossless
un-optimized.jpgoptimized-lossless.jpg192K184K
compressJpeg(75)
Lossy
un-optimized.jpgoptimized-lossy.jpg192K104K
new Transformer(PNG).webpLossless()
Lossless
un-optimized.pngoptimized-lossless.webp888K588K
new Transformer(PNG).webp(75)
Lossy
un-optimized.pngoptimized-lossy-png.webp888K64K
Transformer(PNG).avif({ quality: 100 })
Lossless
un-optimized.pngoptimized-lossless-png.avif888K536K
new Transformer(PNG).avif({ quality: 75, chromaSubsampling: ChromaSubsampling.Yuv420 })
Lossy
un-optimized.pngoptimized-lossy-png.avif888K64K
example.mjs
import { readFileSync, writeFileSync } from 'fs'
 
import {
  losslessCompressPng,
  compressJpeg,
  pngQuantize,
  Transformer,
  ResizeFilterType,
  ChromaSubsampling,
} from '@napi-rs/image'
import chalk from 'chalk'
 
const PNG = readFileSync('./un-optimized.png')
const JPEG = readFileSync('./un-optimized.jpg')
// https://github.com/ianare/exif-samples/blob/master/jpg/orientation/portrait_5.jpg
const WITH_EXIF = readFileSync('./with-exif.jpg')
 
writeFileSync('optimized-lossless.png', await losslessCompressPng(PNG))
 
console.info(chalk.green('Lossless compression png done'))
 
writeFileSync(
  'optimized-lossy.png',
  await pngQuantize(PNG, {
    maxQuality: 75,
  }),
)
 
console.info(chalk.green('Lossy compression png done'))
 
writeFileSync('optimized-lossless.jpg', await compressJpeg(readFileSync('./un-optimized.jpg')))
 
console.info(chalk.green('Lossless compression jpeg done'))
 
writeFileSync('optimized-lossy.jpg', await compressJpeg(readFileSync('./un-optimized.jpg'), { quality: 75 }))
 
console.info(chalk.green('Lossy compression jpeg done'))
 
writeFileSync('optimized-lossless.webp', await new Transformer(PNG).webpLossless())
 
console.info(chalk.green('Lossless encoding webp from PNG done'))
 
writeFileSync('optimized-lossy-png.webp', await new Transformer(PNG).webp(75))
 
console.info(chalk.green('Encoding webp from PNG done'))
 
writeFileSync('optimized-lossless-png.avif', await new Transformer(PNG).avif({ quality: 100 }))
 
console.info(chalk.green('Lossless encoding avif from PNG done'))
 
writeFileSync(
  'optimized-lossy-png.avif',
  await new Transformer(PNG).avif({ quality: 75, chromaSubsampling: ChromaSubsampling.Yuv420 }),
)
 
console.info(chalk.green('Lossy encoding avif from PNG done'))
 
writeFileSync(
  'output-exif.webp',
  await new Transformer(WITH_EXIF)
    .rotate()
    .resize(450 / 2, null, ResizeFilterType.Lanczos3)
    .webp(75),
)
 
console.info(chalk.green('Encoding webp from JPEG with EXIF done'))