NanoJPEG: a compact JPEG decoder

(April 29, 2009)

If you followed my works, you know that I like compact, single-file implementations of decoders for various media formats, and where such a thing doesn’t exist, I tend to write or at least port one myself. Now I’d like to add the third format to that list: Baseline JPEG images.

There are already two decoders on the web that go by the name »Tiny JPEG Decoder«: One of them actually isn’t tiny at all, it’s nothing but a huge load of C++ bloat. Luc Saillard’s decoder at least deserves its name somehow – I use it for my demo engine currently. It’s far from perfect, though – the color conversion code is awful, for example. It may be reasonably fast, but it’s bloated (dedicated conversion routines for every common format) and it lacks a proper chroma upsampling filter, resulting in ugly artifacts.

Since I was writing a JPEG decoder for work anyway, I decided to write another one at home, too. My goals were compact code, reasonable quality (read: a proper upscaling filter is a must-have) and decent, but not necessarily good speed. I think I have achieved that. Here are the bullet points:

  • decodes baseline JPEG only, no progressive or lossless JPEG
  • supports 8-bit grayscale and YCbCr images, no 16 bit, CMYK or other color spaces
  • supports any power-of-two chroma subsampling ratio
  • supports restart markers
  • the four points above mean that it should be able to decode all digital camera JPEG files and many other JPEG files
  • below 900 lines of code (and that already includes over 200 lines of comments and empty lines!)
  • converts YCbCr to RGB
  • uses a bicubic chrominance upsampling filter (this is actually better than the mere bilinear filter of libjpeg!)
  • a little slower than libjpeg
  • memory requirements: ~512 KiB (static) + 1x the decoded image size for grayscale images or 2x the decoded image size for color images
  • very simple API
  • input: memory dumps of JPEG files
  • output: memory dumps of raw, uncompressed 8-bit grayscale or 24-bit RGB pixels
  • output format is compatible to the PGM/PPM file formats as well as OpenGL GL_LUMINANCE8/GL_RGB8 texture formats
  • not fault-tolerant – any bitstream error will stop the decoder immediately and return an error to the application
  • 100% pure C code
  • no warnings with GCC and Clang -pedantic and MSVC /W3
  • no UBSan warnings when compiled with -fwrapv
  • 32-bit integer arithmetic only
  • supposed to be endianness independent
  • 64-bit clean
  • not thread-safe
  • platform-independent
  • includes some provisions to build ultra-small Win32 executables
  • open source
  • single C file
  • batteries example program included

If you want to check it out, here is it:


The code compiles to less than 6 kB of x86 code; with a suitable main() function that doesn’t use any C runtime library calls, it’s easily possible to create a 8 KiB Win32 executable that does what the built-in example program does: Decode a JPEG file to PGM or PPM. (Well, almost. The Win32-only version lacks error messages :) This can be reduced further by using Crinkler, and voilĂ : Here’s a working JPEG decompression program for Windows in just 4072 bytes:


Ports to other programming languages

Other people have been busy porting NanoJPEG to other languages. Here is a list of all ports that have been contributed so far:

Finally, there’s my own port to (thread-safe) C and C++, called »µJPEG«, that adds a second bicubic chroma scaler for co-sited chroma samples (detected at runtime by examining the file’s Exif information), makes the scaler selectable at runtime and adds a »no decoding« mode where only the headers are examined to detect the image size.


2010-05-12: After a little delay of just (*cough*) two months, I updated nanojpeg.c to version 1.1. It fixes two compiler warnings with newer GCCs and, most importantly, a bug that caused NanoJPEG to reject valid files where the number of macroblocks is divisible by the restart interval – oops :)
Note that the new release is just an update of the C file, the example .exe file is still based on version 1.0.

2011-06-25: NanoJPEG now has an SVN repository:

2012-02-18: Folkert van Heusden found and fixed a bug which caused NanoJPEG to generate syntax errors for valid streams that used 0xFFFF-style padding. The fix is incorporated in the updated version 1.2.

122 Responses to »NanoJPEG: a compact JPEG decoder«

Post a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


By submitting the comment, you agree to the terms of the Privacy Policy.