Projects/Build a PNG Encoder and Decoder

Build a PNG Encoder and Decoder

Start by recognizing eight magic bytes and end with a codec that decodes a real PNG to RGBA pixels and encodes pixels back into a valid PNG. Along the way you build DEFLATE inflate from scratch - a LSB-first bit reader, canonical Huffman tables, the length and distance alphabets, and an overlapping LZ77 copy - then the Paeth predictor and the rest of the filter set, and finally a basic compressor that produces files any PNG viewer will open.

52 lessonsLarge~20 min / lessonDEFLATEHuffman codingPNG filters
The project

What you'll build over the next 52 lessons

Over 52 lessons you build a working PNG codec from first principles, decoder first and then encoder. You begin with the container: the eight-byte signature, the length/type/data/CRC chunk structure, and the roles of IHDR, IDAT, PLTE, and IEND. You implement CRC32 from the reversed polynomial as a lookup table and validate every chunk. Then comes the centerpiece, a from-scratch DEFLATE inflate: the zlib wrapper and its Adler-32 trailer, a LSB-first bit reader, stored blocks, canonical Huffman code construction and decoding, the literal/length and distance alphabets with their extra-bit tables, LZ77 back-references into a 32K window including the tricky overlap case, and the dynamic code-length tables. You reverse the five PNG filters (None, Sub, Up, Average, and the Paeth predictor) and assemble raw bytes into RGBA pixels across grayscale, truecolor, palette, and alpha color types at bit depths 1 through 16.

With a complete decoder in hand you reverse the whole pipeline: serialize pixels to filtered scanlines, DEFLATE-compress them, wrap them in zlib with a correct Adler-32, and write signature plus IHDR, IDAT, and IEND chunks each carrying a correct CRC32, producing a valid PNG that other decoders and your own can read. The capstone decodes a real embedded PNG to pixels and proves your encoder round-trips it.

This is a teaching-grade codec built around the real PNG and DEFLATE specifications. The decoder is genuinely complete for the standard non-interlaced color types and bit depths; the encoder is deliberately basic, using stored and fixed-Huffman blocks with simple filtering rather than an optimizing compressor, so its files are valid but larger than a production encoder would produce. What you finish with is the honest core that libraries like libpng and zlib extend with interlacing, ancillary chunks, and optimal compression.

build-a-png-codec / lesson-01.md
Lesson 01 · The PNG container

The eight-byte signature

Every PNG file begins with the same eight magic bytes. Today you build the smallest useful thing a decoder can do - recognize that a byte stream even claims to be a PNG - so the codec has a front door from day one.

The goal

Report whether a byte slice begins with the exact PNG signature.

Start here - the target
TO DO
Scenario: Recognizing the PNG magic bytes
Giventhe byte sequence 137, 80, 78, 71, 13, 10, 26, 10 followed by any other bytes
Whenthe signature check runs on it
Thenit reports true
Anda sequence whose first byte is 138 instead of 137, or one shorter than eight bytes, reports false
Background

Every PNG file opens with the same eight-byte signature: 89 50 4E 47 0D 0A 1A 0A. It is carefully chosen. The high bit of the first byte (0x89) catches transmission over channels that strip bit 7; the ASCII PNG (50 4E 47) is human-readable in a hex dump; and the 0D 0A, 1A, 0D 0A bytes detect the kinds of newline mangling and file truncation that plagued early binary formats.

You will not decode anything yet. You are building the one gate every decoder needs first: does this stream even claim to be a PNG? Anything that does not begin with these exact bytes is rejected before a single chunk is read. Pin the edges now - the wrong first byte fails, and a stream shorter than eight bytes fails rather than reading past its end.

Make it work
// the fixed 8-byte PNG signature
var Signature = []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
func HasSignature(b []byte) bool {
// true only if b starts with all eight signature bytes
}
CheckpointDONE
Your codec recognizes a PNG by its signature and rejects anything else. Commit and stop here.
Scope & extensions

Where this project stops - and where to go next

A genuinely complete, robust decoder for standard non-interlaced PNGs across every color type and bit depth (a full from-scratch DEFLATE inflater, all five filters, and pixel assembly), paired with a deliberately minimal encoder that writes valid 8-bit truecolor-with-alpha files other decoders can read but does no LZ77 match-finding, and neither side handles interlacing or ancillary chunks like transparency, gamma, or text.

Extend it next
  • Add LZ77 match-finding to the encoder so output is not several times larger than necessary
  • Add per-scanline filter selection (Sub, Up, Average, Paeth) to the encoder instead of a single fixed filter
  • Support tRNS transparency on both decode and encode so palette and grayscale images can carry alpha
  • Support Adam7 interlacing, at least on the decode side, to read progressively-stored PNGs
  • Let the encoder emit grayscale, palette, and lower-bit-depth output rather than always 8-bit RGBA
  • Interpret ancillary chunks (gAMA, sRGB, tEXt) instead of skipping them
Recommended reading

Books & references that go deeper

  • The authoritative PNG specification: the signature, chunk layout, IHDR fields, filter algorithms with the exact Paeth predictor, and the color-type and bit-depth matrix. The primary reference for the whole project.

  • The DEFLATE spec: block types, the canonical Huffman construction algorithm, the literal/length and distance code tables, and the dynamic code-length encoding. Chapters three and four build directly from this.

  • RFC 1950 - ZLIB Compressed Data Format Specification · L. Peter Deutsch, Jean-Loup Gailly

    The zlib wrapper around DEFLATE: the CMF/FLG header bytes, the FCHECK constraint, and the Adler-32 trailer that PNG uses to frame its compressed image data.

  • A small, heavily commented reference inflate by the author of zlib. The clearest single file to read alongside chapter four when your Huffman decoding or LZ77 copy misbehaves.

  • The PNG File Format (write-up) · ImageMagick / Understanding formats

    A readable overview of the PNG container and how the pieces fit together - a gentler orientation to the chunk stream and filtering before diving into the formal specs.

  • A plain-language walkthrough of LZ77 plus Huffman as DEFLATE combines them - good intuition for why the length/distance alphabets and the sliding window look the way they do.