Documentation

Format Guides

Use the support table to check input and output formats. Compare output size and visual quality on your own images: a quality value of 75 is specific to an encoder, not a shared quality scale across formats or libraries.

WebP

  • Lossy: webp(qualityFactor) accepts 0–100 and defaults to 90. Try 75–80 as a starting point, then inspect the output.
  • Lossless: webpLossless() preserves decoded pixels. Useful for graphics and screenshots; compare its size with PNG on your inputs.
ts
await new Transformer(png).webp(75)
await new Transformer(png).webpLossless()

Both WebP decoding and encoding are supported. The API works with still images and does not expose animation controls.

AVIF

AVIF supports both input and output. Encoding uses libavif with the AOM codec. It can produce compact files, but the quality/size/time trade-off depends on the input and settings.

ts
import { Transformer, ChromaSubsampling } from '@napi-rs/image'

const avif = await new Transformer(png).avif({
  quality: 75,
  alphaQuality: 90,
  chromaSubsampling: ChromaSubsampling.Yuv420,
  speed: 5,
})
  • quality: 0–100, default 80. Quality 100 alone does not guarantee a pixel-identical RGB/RGBA round trip: color conversion, chroma subsampling and alpha quality also matter.
  • alphaQuality: 0–100, default 90; controlled independently of color quality.
  • chromaSubsampling: Yuv444 by default. Yuv420 reduces chroma resolution, which can reduce size but may affect colored edges and text. Compare it visually before choosing it.
  • speed: 1–10, with 5 used by the 1.15.0 implementation. Higher values favor encoding speed over compression efficiency. Set it explicitly for reproducible comparisons.
  • threads: defaults to the CPU count; 0 requests automatic selection by the codec. Tune this together with the number of images processed concurrently.

The published decoder produces 8-bit pixels, and RGB/RGBA encoding converts higher-bit-depth inputs to 8-bit. Do not use this path when you need to preserve 10/16-bit color precision. In the browser, use the async Worker integration; it supports threaded WASM and is not a main-thread script.

PNG

  • losslessCompressPng: optimize existing PNG bytes with oxipng. strip: true removes non-critical chunks, including metadata.
  • pngQuantize: lossy palette quantization, including alpha. Tune minQuality / maxQuality and inspect the result. Smaller output is not guaranteed for every input.
  • Transformer.png(): encode a transformed image to PNG with PngEncodeOptions.
ts
import { losslessCompressPng, pngQuantize } from '@napi-rs/image'

await losslessCompressPng(png, { strip: true })
await pngQuantize(png, { maxQuality: 75 })

JPEG

  • Transformer.jpeg(quality): encode a decoded image to JPEG; default quality 90.
  • compressJpeg(bytes): lossless coefficient optimization of an existing JPEG via MozJPEG; default quality 100. This preserves the compressed image's coefficients, not its original file bytes or all metadata.
  • compressJpeg(bytes, { quality: 75 }): decode and re-encode a JPEG with the image crate's JPEG encoder. This is a lossy path; optimizeScans applies to the default MozJPEG coefficient-optimization path.
ts
await new Transformer(input).jpeg(82)
await compressJpeg(jpegBytes)
await compressJpeg(jpegBytes, { quality: 75 })

JPEG XL

JPEG XL input (.jxl, as a bare codestream or in the JPEG XL container) is decoded on every platform, including browser/WASM builds, by the bundled pure-Rust jxl-rs decoder. There is no JPEG XL encoder, so there is no .jxl() output method: decode JPEG XL and encode to another format. metadata().format is 'jxl'.

ts
import { Transformer } from '@napi-rs/image'

const webp = await new Transformer(jxlBytes).resize(800).webp(80)
Behavior Result
Orientation Applied while decoding, so pixels are already upright. metadata().orientation is undefined and .rotate() without an angle changes nothing
Color Converted to sRGB when the file uses another color space (Display P3, Rec.2100, an embedded ICC profile); the ICC profile is not kept
HDR PQ and HLG input is tone mapped to SDR sRGB with libjxl's own method: Rec. 2408 from the intensity target down to 250 nits for PQ, and the HLG OOTF down to 80 nits. Colors outside sRGB are desaturated towards gray instead of clipped. 203 nits comes out at about 74% of sRGB white. The curve is smooth and neutral on gray, and integer and float files give the same pixels. HDR that comes with an embedded ICC profile is converted by that profile
Bit depth Up to 8 bits per sample decodes to 8-bit; 9–16 bit and floating-point input decode to 16-bit
Grayscale Stays grayscale, unless its color space needs conversion; then it decodes to RGB. Gray PQ and HLG input stays grayscale
Alpha Straight (unpremultiplied); premultiplied files are converted
Animation Only the first frame is decoded
Metadata EXIF and XMP boxes are not read: metadata(true).exif is undefined
Limits CMYK files are rejected. Files whose decoded pixels would need more than 512 MiB are rejected before decoding, like other formats. The decoder also rejects images of about 134 megapixels or more (about 100 megapixels with alpha)

JPEG files recompressed with cjxl decode to pixels; the original JPEG bytes are not reconstructed.

HEIC and HEIF

HEVC-in-HEIF input (.heic / .heif) and HEIC output are available only on macOS and Windows, using OS codecs. The package does not bundle a HEVC codec. Linux, Android, FreeBSD and browser/WASM builds reject HEIC operations.

ts
import { Transformer } from '@napi-rs/image'

const jpeg = await new Transformer(heicBytes).rotate().jpeg(80)
const heic = await new Transformer(pngBytes).heic({ quality: 80 })
Behavior macOS (ImageIO) Windows (WIC)
Codec requirement OS ImageIO HEIC support HEIF Image Extensions and HEVC Video Extensions available to WIC
Decode 8-bit → RGBA8; 10-bit → RGBA16 Always RGBA8, including 10-bit sources
Encode bit depth 8 or 10; 16-bit input defaults to 10 8 only; bitDepth: 10 rejects
Encode alpha OS encoder behavior Flattened to opaque
Quality Default 80; 90–100 share a 0.9 ceiling; no lossless mode Default 80; maps 0–100 to the OS encoder's 0–1 scale

Do not assume the Windows extensions exist on server or CI hosts; missing codecs cause an error. HEIC input is normalized to sRGB without retaining an ICC profile. HDR gain maps are not composited. Use .rotate() for orientation tags where the decoder has not already applied them.

Choosing an output

Need Start with
Photo delivery Compare WebP and AVIF at your target size and visual quality
Exact decoded graphics Compare PNG optimization and lossless WebP
Smaller PNG with acceptable palette loss pngQuantize
Keep JPEG output compressJpeg for existing JPEGs; Transformer.jpeg after transforms
HEIC output on a supported OS Check the installed codec and bit-depth limits above

Use the playground to compare common formats, and the API Reference for options. Browser display support and this library's codec support are separate considerations.