Every encoder piece is built; today they combine into one Encode that writes a complete, valid PNG file - signature, IHDR, a compressed IDAT, and IEND - that your decoder and any other reads.
Encode an image to a full PNG byte stream and confirm it decodes back to the same pixels.
This is the encoder’s payoff. Encode chains everything: pixels become filtered scanlines, those are compressed into a deflate block and wrapped in zlib, and the result is written as an IDAT chunk between a leading IHDR and a trailing IEND, all behind the signature. Because every stage is the exact inverse of a decoder stage - filter mirrors unfilter, deflate mirrors inflate, chunk-write mirrors chunk-read - the output is a real PNG. The proof is the round trip: encode the image, decode those bytes, and get the same pixels.
And it is not just your decoder that reads it - the CRCs, the zlib envelope, and the chunk structure are all standard, so any PNG viewer opens the file too. That interoperability is the whole point of building to the spec rather than a private format. You now have a complete codec: a full-featured decoder and a valid, if basic, encoder. The capstone brings them together on a real embedded image and proves the loop closes.
func Encode(im *Image) []byte {scan := toScanlines(im) // filtered scanlinesidat := zlibWrap(fixedBlock(scan), scan) // compress + zlib envelopeout := append([]byte{}, Signature...)out = append(out, writeIHDR(im.Width, im.Height, 8, 6)...)out = append(out, writeChunk("IDAT", idat)...)return append(out, writeChunk("IEND", nil)...)}