PNG File Documentation


Summary

A PNG (Portable Network Graphics) file is a lossless raster image that keeps full transparency through an alpha channel. It was introduced in 1996 as a patent-free replacement for GIF and is now a W3C Recommendation and the ISO/IEC 15948 standard. Every operating system and web browser opens a .png file natively, so the real questions are usually about transparency, file size, and converting to JPG or WebP.

Technical details

FeatureValue
Full namePortable Network Graphics
File extension.png
MIME typeimage/png
Format typeRaster image, chunk-based binary
CompressionLossless (DEFLATE / zlib)
DeveloperPNG Development Group (W3C / ISO standard)
Introduced1996 (v1.0)
StandardISO/IEC 15948:2004; W3C PNG Specification (3rd ed., 2022)
Open standardYes — royalty-free, patent-unencumbered
Colour depth1–8-bit indexed, 24-bit truecolour, up to 48-bit deep colour
Alpha channelYes — 8-bit or 16-bit per-pixel transparency
TransparencyFull alpha channel; palette transparency via tRNS
GrayscaleSupported (1–16-bit)
InterlacingAdam7 (7-pass, optional)
AnimationNo — the APNG extension adds animation, backward-compatible
Colour managementGamma (gAMA), sRGB, embedded ICC profile (iCCP)
MetadataText chunks tEXt, zTXt, iTXt
Byte orderBig-endian
Signature (hex)89 50 4E 47 0D 0A 1A 0A
Related extensions.apng, .webp, .gif, .jpg, .svg, .bmp
Specificationw3.org/TR/png-3/
File signature (magic bytes)
89 50 4E 47 0D 0A 1A 0A

Offset 0, 8 bytes. In ASCII this reads \x89 P N G \r \n \x1A \n. The non-ASCII first byte (0x89) together with the CR–LF and EOF bytes is a deliberate corruption check: it flags files damaged by 7-bit transfers or by software that rewrites line endings. The signature is always followed by the IHDR chunk, and every valid PNG ends with an IEND chunk.

What is a PNG file?

PNG stands for Portable Network Graphics, a lossless raster image format (a rectangular grid of pixels) that stores an image without discarding any data. It was designed in 1995 and published in 1996 by an informal group coordinated by Thomas Boutell, as a patent-free replacement for GIF after Unisys began enforcing its patent on the LZW compression that GIF used. PNG became a W3C Recommendation in 1996 and the international standard ISO/IEC 15948; the current text is the W3C PNG (Third Edition) specification.

Two properties define the format at the byte level. Its compression is lossless, using the DEFLATE algorithm (the same LZ77-plus-Huffman scheme as zlib and ZIP), so re-saving a PNG never degrades it the way re-saving a JPG does. And it carries a true alpha channel, so each pixel can be fully opaque, fully transparent, or any value in between. Everything else about the file — dimensions, colour model, gamma, transparency, animation — is expressed through a chunked binary container described below.

The 8-byte signature and the chunk container

Every PNG begins with a fixed 8-byte signature: 89 50 4E 47 0D 0A 1A 0A. Each byte does a job. The leading 0x89 has the high bit set, so a transfer that strips bit 7 (an old 7-bit channel) corrupts it detectably. Bytes 2–4 are the ASCII letters P N G. The 0D 0A pair is a DOS CR-LF; if some tool rewrites line endings, it changes and the file is flagged as damaged. The 1A byte is DOS end-of-file, which stops TYPE on old systems from dumping binary garbage. The trailing 0A is a lone LF that catches the reverse (CR-LF collapsed to LF) conversion.

After the signature comes a sequence of chunks. Every chunk has the same four-field layout, which lets a decoder validate, skip, or copy any chunk even if it does not understand its type:

+--------------------------------------------------------------+
| Length   (4 bytes, big-endian) = number of bytes in DATA     |
| Type     (4 bytes) = four ASCII letters, e.g. "IHDR"         |
| Data     (Length bytes) = the chunk payload                  |
| CRC-32   (4 bytes) = checksum over Type + Data (not Length)  |
+--------------------------------------------------------------+

The Length field is a 4-byte unsigned integer, so a single chunk holds at most 2^31−1 bytes. All multi-byte integers in PNG are big-endian (network byte order). The four letters of the Type code carry meaning in their capitalisation: bit 5 of each letter is a flag. An uppercase first letter marks the chunk critical (a decoder must understand it to render the image); lowercase marks it ancillary (safe to ignore). The second letter's case marks public vs private; the fourth marks whether an editor may safely copy the chunk after modifying the image. That is how new chunk types are added without breaking old readers. A complete file is laid out like this:

89 50 4E 47 0D 0A 1A 0A   signature (8 bytes)
IHDR   critical  image header (must be first)
gAMA / cHRM / sRGB / iCCP   colour info (optional, before PLTE/IDAT)
PLTE   critical* palette (required for colour type 3)
tRNS   ancillary transparency (palette alpha or colour key)
pHYs / bKGD / tEXt / zTXt / iTXt   metadata (optional)
IDAT   critical  compressed pixel data (one or more)
IEND   critical  end marker (must be last, zero-length data)

IHDR is always first and IEND always last. Pixel data may span several IDAT chunks; splitting lets an encoder stream output without buffering the whole compressed image, and the decoder concatenates their data before decompressing.

The IHDR chunk, field by field

The image header is a critical chunk whose data field is exactly 13 bytes, in this order:

Offset  Size  Field           Meaning
  0      4    Width           image width in pixels  (big-endian, must be > 0)
  4      4    Height          image height in pixels (big-endian, must be > 0)
  8      1    Bit depth       bits per sample: 1, 2, 4, 8 or 16
  9      1    Colour type     0, 2, 3, 4 or 6 (see table)
 10      1    Compression     always 0 (DEFLATE)
 11      1    Filter method   always 0 (adaptive, 5 filter types)
 12      1    Interlace       0 = none, 1 = Adam7

Width and Height are 4 bytes each, so the maximum declared dimension is 2^31−1 pixels. Bit depth is bits per sample, not per pixel: for an RGB pixel at depth 8, each of the red, green and blue samples is 8 bits, so the pixel is 24 bits. The colour type is a bit field: bit 0 (value 1) means "palette used", bit 1 (value 2) means "colour used", bit 2 (value 4) means "alpha used". That is why the valid values are 0, 2, 3, 4 and 6, and why 1, 5 and 7 do not exist. Compression method and filter method are both fixed at 0 in every conforming PNG; they exist so a future revision could add alternatives. Here is the annotated hex dump of a signature plus the IHDR of a 16×16, 8-bit truecolour-with-alpha, non-interlaced image:

89 50 4E 47 0D 0A 1A 0A   PNG signature
00 00 00 0D               Length = 13
49 48 44 52               Type   = "IHDR"
00 00 00 10               Width  = 16
00 00 00 10               Height = 16
08                        Bit depth   = 8
06                        Colour type = 6 (truecolour + alpha)
00                        Compression = 0 (DEFLATE)
00                        Filter      = 0 (adaptive)
00                        Interlace   = 0 (none)
90 77 53 DE               CRC-32 over "IHDR" + the 13 data bytes

Colour types and their allowed bit depths

Not every bit depth is legal with every colour type. The specification fixes the combinations exactly, which is why a decoder can reject an out-of-range pair without guessing:

Colour typeNameSamples/pixelAllowed bit depths
0Greyscale11, 2, 4, 8, 16
2Truecolour (RGB)38, 16
3Indexed (palette)1 (index)1, 2, 4, 8
4Greyscale + alpha28, 16
6Truecolour + alpha48, 16

Indexed colour (type 3) caps at 8 bits because a palette holds at most 256 entries. Truecolour and the two alpha types start at 8 bits because sub-byte RGB samples are not meaningful. At 16 bits per sample, a type-6 pixel is 64 bits, giving PNG its "deep colour" ceiling of 48-bit RGB plus a 16-bit alpha. Greyscale at depth 1 is a bilevel (black/white) image of 1 bit per pixel.

Scanline filters and the Paeth predictor

Before compression, PNG does not store raw pixels. Each scanline (one row) is prefixed by a single filter-type byte, then every byte of the row is replaced by the difference between it and a prediction from already-decoded neighbours. There are five filter types, and the filter byte can differ from row to row (that is what "adaptive" filtering means):

CodeFilterPrediction for byte x
0None0 (store the raw byte)
1Suba (byte to the left)
2Upb (byte directly above)
3Averagefloor((a + b) / 2)
4PaethPaethPredictor(a, b, c)

Here a is the corresponding byte in the pixel to the left, b the byte directly above, and c the byte above-left. The neighbour distance is measured in whole bytes-per-pixel (rounded up to 1 for sub-byte depths), so filtering never mixes unrelated channels. The Paeth predictor, named after Alan W. Paeth, computes an initial estimate p = a + b − c, then picks whichever of a, b or c is numerically closest to p. All filter arithmetic is done modulo 256 on unsigned bytes, so the inverse (unfiltering) reconstructs the exact original.

Filtering matters because it feeds DEFLATE data it can compress well. A smooth gradient has neighbouring bytes that are almost equal; after Sub or Up, most residuals collapse to zero or small values, producing long runs and a skewed byte distribution that LZ77 matching and Huffman coding both exploit. Raw pixel bytes rarely repeat; filtered residuals do. Encoders such as libpng try each filter per row and keep the one that minimises the sum of absolute residuals, which is a cheap proxy for "compresses best".

IDAT and the zlib/DEFLATE stream

The concatenated data of all IDAT chunks forms a single zlib stream (RFC 1950) wrapping a DEFLATE payload (RFC 1951). The stream opens with a 2-byte zlib header: a CMF byte (compression method 8 = DEFLATE, plus the window-size exponent in its high nibble) and an FLG byte carrying the compression-level hint and a check so that CMF×256+FLG is a multiple of 31. After the DEFLATE blocks comes a 4-byte Adler-32 checksum of the uncompressed filtered data.

What gets compressed is the full sequence of filtered scanlines: for each row, the one filter-type byte followed by the filtered sample bytes. So the decompressed size is height × (1 + bytes_per_scanline). DEFLATE itself is a series of blocks, each either stored (uncompressed), compressed with fixed Huffman codes, or compressed with dynamic Huffman codes built for that block, with LZ77 back-references reaching up to 32 KB into the already-decoded output. Because the whole thing is one continuous stream split arbitrarily across chunks, you cannot decompress one IDAT in isolation; a reader appends every IDAT data field in order, then inflates.

PLTE, tRNS and the two transparency models

The PLTE chunk holds the palette: 1 to 256 entries, each exactly 3 bytes (R, G, B), so its length is always a multiple of 3. It is mandatory for colour type 3 (indexed) and forbidden for the greyscale types; for truecolour it may appear only as a suggested quantisation palette. Palette indices in the pixel data must not point past the last entry.

Transparency comes in two forms. The alpha colour types (4 and 6) store opacity per pixel inside the image data itself. Everything else uses the ancillary tRNS chunk. For indexed images, tRNS is an array of 1-byte alpha values that lines up with the start of the palette, giving each palette entry its own opacity. For greyscale, tRNS holds a single 2-byte grey value that is treated as fully transparent (a colour key); for truecolour it holds three 2-byte values (one transparent RGB triple). This colour-key model is how PNG matches, and improves on, the single-colour transparency of GIF while still supporting full per-pixel alpha where needed.

Ancillary chunks: colour management and metadata

Ancillary chunks carry information a decoder may use but does not strictly need to render pixels. The colour-related ones must appear before the first IDAT: gAMA stores the image gamma as a 4-byte integer scaled by 100000; cHRM gives the CIE x,y chromaticities of the white point and the R/G/B primaries (eight 4-byte values); sRGB is a single byte declaring the image is in the sRGB colour space with a stated rendering intent; and iCCP embeds a full, DEFLATE-compressed ICC colour profile with a name. A decoder that sees iCCP or sRGB should prefer it over a bare gAMA.

Other ancillary chunks describe layout and text. pHYs records the intended pixels-per-unit in X and Y plus a unit byte (1 = metre), which is how a PNG carries a DPI. bKGD suggests a background colour for composing images that have transparency. Text metadata comes in three flavours: tEXt stores a keyword and Latin-1 value uncompressed; zTXt stores the same but DEFLATE-compressed; and iTXt stores UTF-8 text with an optional language tag and translated keyword, so titles, authors and software names survive in any script.

The per-chunk CRC-32

Every chunk ends with a 4-byte CRC-32 computed over the chunk's type code and its data field, but not its length. PNG uses the standard IEEE 802.3 polynomial (0xEDB88320 in reflected form), the same one used by zlib, gzip and ZIP. Because the type bytes are included, a corrupted type code is caught too, not just corrupted data. This is a per-chunk integrity check layered on top of the stream-level Adler-32 inside zlib: the CRC guards the container, the Adler-32 guards the decompressed pixels. A reader that hits a bad CRC on a critical chunk should refuse the image; on an ancillary chunk it may warn and continue. That two-level checking is why a truncated or bit-flipped PNG usually fails loudly rather than rendering a subtly wrong image.

Adam7 seven-pass interlacing

If the IHDR interlace byte is 1, pixel data is reordered by the Adam7 scheme, named after Adam M. Costello. Instead of top-to-bottom rows, the image is divided into 8×8 tiles and transmitted in seven passes that fill in a coarse-to-fine grid, so a low-resolution preview of the whole image appears after the first pass and sharpens as later passes arrive. Each pass is filtered and compressed as if it were its own smaller image, with its own per-scanline filter bytes; the passes select these pixels within every 8×8 block:

Pass:  1 6 4 6 2 6 4 6
       7 7 7 7 7 7 7 7
       5 6 5 6 5 6 5 6
       7 7 7 7 7 7 7 7
       3 6 4 6 3 6 4 6
       7 7 7 7 7 7 7 7
       5 6 5 6 5 6 5 6
       7 7 7 7 7 7 7 7

Pass 1 sends 1 pixel per 8×8 block (the top-left), pass 7 sends every pixel of the alternate rows. Adam7 usually makes the file slightly larger and compresses a little worse, because splitting the image into seven small sub-images breaks up the runs DEFLATE feeds on, so most encoders leave it off. It is a progressive-display convenience, not a quality setting.

The APNG animation extension

A base PNG holds one image. The APNG extension (an unofficial addition first shipped by Mozilla) adds animation through three ancillary chunks. acTL (animation control) appears before the first IDAT and states the number of frames and the loop count. Each frame is introduced by an fcTL (frame control) chunk giving that frame's width, height, x/y offset, a delay expressed as a numerator/denominator fraction of a second, and dispose/blend operations that say how to clear or composite the frame. Frame pixels after the first live in fdAT (frame data) chunks, which are byte-for-byte like IDAT except for a leading 4-byte sequence number that also appears in each fcTL to enforce ordering.

The design is backward compatible: because acTL, fcTL and fdAT are all ancillary, a decoder that does not know APNG ignores them and shows the ordinary IDAT as a single still frame. APNG keeps PNG's full 24-bit colour and 8-bit alpha, which is why it is preferred over GIF for animated stickers and emoji where GIF's 256-colour palette and 1-bit transparency look crude. For a different modern take on animation and alpha, see WebP; for resolution-independent vector graphics rather than a pixel grid, see SVG.

Frequently asked questions

Why does PNG put a CRC-32 on every chunk?

So corruption is caught at the container level, independently of the compressed stream. The CRC covers each chunk's type and data using the IEEE polynomial, so a bit flip in a chunk header, a truncated transfer, or a mangled type code all fail the check before the pixels are ever decoded. The zlib Adler-32 inside IDAT adds a second check on the decompressed data.

Why are colour type values 0, 2, 3, 4 and 6 but never 1, 5 or 7?

The colour type is a 3-bit field: bit 0 = palette, bit 1 = colour, bit 2 = alpha. Only five combinations are meaningful. A palette without colour (value 1) is nonsense, and a palette combined with alpha (values 5 and 7) is disallowed because palette transparency is carried by the tRNS chunk instead.

Why filter scanlines before DEFLATE instead of compressing pixels directly?

DEFLATE compresses repetition and skewed byte distributions. Raw pixel bytes rarely repeat, but the difference between a byte and its predicted neighbour (Sub, Up, Average or Paeth) is usually near zero, producing long runs of small values that LZ77 matching and Huffman coding shrink far more than the originals.

References