mozjpeg
| Language | C (usable from C++) |
|---|---|
| License | IJG License, BSD-3-Clause and zlib (permissive, same as libjpeg-turbo) |
| Repository | github.com/mozilla/mozjpeg |
| Documentation | README-mozilla.txt (extensions), libjpeg.txt (API) |
| Depends on | nothing (libpng and zlib optional, for PNG input in cjpeg) |
| Read / write | yes / yes; its value is in encoding |
mozjpeg is a fork of libjpeg-turbo that tunes the encoder for file size. With the same quality setting it produces files 5-15% smaller than libjpeg-turbo with the same or better visual quality. It does this with:
- trellis quantization: picks quantized coefficients that cost fewer bits with little extra error;
- progressive scan optimization (the jpgcrush / jpegrescan technique): tries several ways of splitting coefficients into scans and keeps the smallest;
- quantization tables tuned for modern displays (table set 3 by default) and optional deringing of edges on white backgrounds.
The library is API and ABI compatible with libjpeg (the jpeglib.h API) and includes the TurboJPEG API of the libjpeg-turbo 2.x line (tjCompress2, not the newer tj3* functions). Encoding is several times slower than libjpeg-turbo; decoding is the same code. Use it for images you compress once and serve many times. Squoosh, ImageOptim and libvips (when built against it) use mozjpeg.
Installation#
mozjpeg installs the same library names as libjpeg-turbo, so distributions don’t package it as a system library:
# macOS (keg-only: not linked into /usr/local, so it doesn't replace jpeg-turbo)
brew install mozjpeg
# Windows / Linux (vcpkg)
vcpkg install mozjpeg
# from source (needs CMake and NASM)
git clone https://github.com/mozilla/mozjpeg.git
cd mozjpeg && mkdir build && cd build
cmake -G"Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/opt/mozjpeg ..
make && sudo make install
Link against /opt/mozjpeg/lib (or the Homebrew prefix) explicitly so you don’t pick up the system libjpeg-turbo.
Basic usage: compress an RGB buffer to JPEG#
With no extra calls, jpeg_set_defaults in mozjpeg selects the JCP_MAX_COMPRESSION profile: progressive, trellis quantization and optimized scans are on:
#include <stdio.h>
#include <jpeglib.h>
int save_jpeg(const char *path, const unsigned char *rgb, int width, int height, int quality) {
FILE *out = fopen(path, "wb");
if (!out) return 1;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr); // default handler prints and exits on error
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, out);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo); // mozjpeg profile: smallest output
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
while (cinfo.next_scanline < cinfo.image_height) {
JSAMPROW row = (JSAMPROW)&rgb[(size_t)cinfo.next_scanline * width * 3];
jpeg_write_scanlines(&cinfo, &row, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(out);
return 0;
}
To write into memory instead of a file, replace jpeg_stdio_dest with jpeg_mem_dest(&cinfo, &buffer, &size) and free(buffer) when done.
How to make a baseline JPEG with mozjpeg#
Some old decoders and hardware only accept baseline files. Keep trellis quantization but turn off progressive scans after jpeg_set_defaults:
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 80, TRUE);
cinfo.num_scans = 0; // no progressive scan script
cinfo.scan_info = NULL;
How to set mozjpeg options (trellis, quantization tables)#
Extension parameters are set with jpeg_c_set_bool_param and jpeg_c_set_int_param. The compression profile must be chosen before jpeg_set_defaults, which applies it:
// fast, libjpeg-turbo-like output (baseline, no trellis)
jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
jpeg_set_defaults(&cinfo);
// or keep the default profile and change single options
jpeg_set_defaults(&cinfo);
jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_QUANT, TRUE);
jpeg_c_set_bool_param(&cinfo, JBOOLEAN_OVERSHOOT_DERINGING, TRUE); // text/graphics on white
jpeg_c_set_int_param(&cinfo, JINT_BASE_QUANT_TBL_IDX, 3); // 0-8, 3 is the default
jpeg_set_quality(&cinfo, 80, TRUE); // after changing the table set
How to compress images with cjpeg and jpegtran#
mozjpeg’s cjpeg reads PNG (when built with libpng), BMP, PPM and Targa:
# encode at quality 80 (progressive, trellis)
cjpeg -quality 80 -outfile out.jpg in.png
# 4:4:4 sampling for images with fine colored detail
cjpeg -quality 85 -sample 1x1 -outfile out.jpg in.png
# losslessly re-optimize an existing JPEG and drop metadata
jpegtran -copy none -optimize -progressive -outfile out.jpg in.jpg
Tips#
- Quality numbers aren’t comparable between encoders: mozjpeg at 75 often looks like libjpeg-turbo at 80-85. Compare file sizes at the same visual quality (e.g. with a SSIM or Butteraugli tool), not the same number.
- mozjpeg’s own
cjpegdoesn’t copy Exif from a source JPEG. Re-encode, then copy metadata withexiftool -tagsFromFile src.jpg out.jpgif you need it. jpegtranfrom mozjpeg optimizes scans losslessly, so it’s useful even for JPEGs you can’t re-encode.