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.
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.
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.
Report whether a byte slice begins with the exact PNG signature.
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.
// the fixed 8-byte PNG signaturevar 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}
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.
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.
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.
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.