Skip to content

3bl3gamer/wasm-mozjpeg

Repository files navigation

WebAssembly MozJPEG encoder.

Inspired by cyrilwanner/wasm-codecs, saschazar21/webassembly/mozjpeg and the thirst for adventures.

Examples

Features

  • 321 KiB before gzip (wasm + js), 34 KiB after;
  • TypeScript definitions;
  • node and browser support;
  • different input color spaces;
  • grayscale, RGB, YCbCr, CMYK and YCCK output color spaces (not all in->out transformations supported, see mozjpeg/libjpeg.txt);
  • disabled arithmetic coding (smaller bundle);
  • sequential (aka "bump") memory allocator (smaller bundle, +30% memory usage);
  • no build-in worker.

Installation

npm install https://github.com/3bl3gamer/wasm-mozjpeg

Usage

Basic

import { loadWebModule, compressSimpleRGBA } from 'wasm-mozjpeg'

loadWebModule().then(mozJpeg => {
    const quality = 75
    const iData = someCanvas.getContext('2d').getImageData(0, 0, 320, 240)

    const jpegChunks = compressSimpleRGBA(mozJpeg, iData.width, iData.height, quality, iData.data)
    const blob = new Blob(jpegChunks, { type: 'image/jpeg' })

    const img = new Image()
    img.src = URL.createObjectURL(blob)
    document.body.appendChild(img)
})

Advanced

import { loadWebModule, initCompressSimple, writeRowsSimple,
    JCS_EXT_RGBA, JCS_YCbCr } from 'wasm-mozjpeg'

loadWebModule().then(mozJpeg => {
    const iData = someCanvas.getContext('2d').getImageData(0, 0, 320, 240)

    const channels = 4 //R, G, B and A
    let { rowBufLocation, imgChunks } =
        initCompressSimple(mozJpeg, iData.width, iData.height, JCS_EXT_RGBA, channels)

    mozJpeg.cinfo_set_out_color_space(JCS_YCbCr)
    mozJpeg.cinfo_set_quant_table(3)
    mozJpeg.cinfo_set_quality(95, -1)
    mozJpeg.cinfo_set_optimize_coding(true)
    mozJpeg.cinfo_set_chroma_subsample(2, 2)
    mozJpeg.cinfo_set_smoothing_factor(0)
    mozJpeg.cinfo_disable_progression()
    mozJpeg.cinfo_set_trellis(10, true, true, true)

    mozJpeg.start_compress()
    writeRowsSimple(mozJpeg, rowBufLocation, iData.data, iData.height, iData.width * channels)
    mozJpeg.finish_compress()
    const blob = new Blob(imgChunks, { type: 'image/jpeg' })

    const img = new Image()
    img.src = URL.createObjectURL(blob)
    document.body.appendChild(img)
})

Color spaces

Different in/out color spaces may be used (see features). Just ensure MozJPEG can handle required color transformation.

For example, if input color data is already in YCbCr, it may be encoded directly:

// buffer should have length = width * wheight * 3
const { widht, height, buffer } = getYCbCrPixels()

const channels = 3
let { rowBufLocation, imgChunks } =
    initCompressSimple(mozJpeg, width, height, JCS_YCbCr, channels)

mozJpeg.cinfo_set_out_color_space(JCS_YCbCr)
//...
mozJpeg.start_compress()
writeRowsSimple(mozJpeg, rowBufLocation, buffer, height, width * channels)
mozJpeg.finish_compress()
//...

More info in index.d.ts, mozjpeg/libjpeg.txt and mozjpeg/README-mozilla.txt.

Bulding

./build_wasm.sh — rebuilds mozjpeg.wasm, generates const.js, requires Emscripten.

./generate_types.sh — generates .d.ts files by JSDoc annotations.