Het artikel beschrijft het volledige technische proces van het decoderen van een PNG-bestand. De kernpunten zijn:
- Chunking: PNG-bestanden zijn opgebouwd uit 'chunks' (zoals IHDR voor metadata, IDAT voor beeldata en IEND als afsluiting). Elke chunk bevat een lengte, type, data en een CRC-check.
- Decompressie: De eigenlijke beeldata in de IDAT-chunks is gecomprimeerd met zlib. Nadat deze data is samengevoegd en gedecomprimeerd, moet deze nog worden verwerkt.
- Defiltering: Omdat PNG's filters gebruiken om data beter comprimeerbaar te maken, moet elke regel van de afbeelding worden 'gedefilterd' om de oorspronkelijke kleuren terug te krijgen. Gebruikte filters zijn onder andere Subtract, Up, Average en Paeth.
- Interlacing: Het artikel legt de Adam7-methode uit, waarbij een afbeelding in zeven opeenvolgende fasen wordt geladen, wat zorgt voor een progressieve weergave van laag naar hoog detail.
Elke stap van het decoderen van een PNG
Chunking
PNG-bestanden worden opgeslagen als een reeks 'chunks'. Elke chunk heeft een lengte van 4 bytes, een naam van 4 bytes, de eigenlijke data en een CRC (Cyclic Redundancy Check). De meeste chunks bevatten metadata over de afbeelding, maar de IDAT-chunk bevat de eigenlijke gecomprimeerde beeldata. De hoofdlettercombinatie van de naam van de chunk bepaalt hoe onverwachte chunks behandeld moeten worden:
| Hoofdletters | Kleine letters | Betekenis |
| Kritiek | Hulp (Ancillary) | Is deze chunk vereist om de afbeelding correct weer te geven? |
| Publiek | Privé | Is dit een private, applicatie-specifieke chunk? |
| Gereserveerd | Gereserveerd | - |
| Onveilig om te kopiëren | Veilig om te kopiëren | Moeten programma's die de PNG-data wijzigen deze chunk begrijpen? |
Hieronder volgen de chunks in de voorbeeldafbeelding:
Signature
Dit identificeert de afbeelding als een PNG. Het bevat newline-tekens en andere tekens die vaak corrupt raken, om er zeker van te zijn dat de binaire beeldata niet beschadigd is. 89 50 4E 47 0D 0A 1A 0A
Header (IHDR)
Deze bevat metadata over de afbeelding.
- Lengte:
00 00 00 0D = 13 bytes lang
- Type:
49 48 44 52 = IHDR
- Data:
00 00 01 00 00 00 01 00 08 02 00 00 00
- Breedte: 256
- Hoogte: 256
- Bitdiepte: 8
- Kleurruimte: Truecolor RGB
- Compressiemethode: 0
- Filtermethode: 0
- Interlacing: uitgeschakeld
- CRC:
D3 10 3F 31
Gamma (gAMA)
Deze bevat de gamma van de afbeelding, gebruikt om nauwkeuriger kleuren weer te geven.
- Lengte:
00 00 00 04 = 4 bytes lang
- Type:
67 41 4D 41 = gAMA
- Data:
00 00 B1 8F = 45455
- CRC:
0B FC 61 05
Informatie over kleurruimte (cHRM)
Deze bevat data over waar in de volledige CIE-kleurruimte de kleuren in de afbeelding zich bevinden, zodat monitoren die kleuren buiten de standaard sRGB-ruimte ondersteunen de afbeelding beter kunnen weergeven.
- Lengte:
00 00 00 20 = 32 bytes lang
- Type:
63 48 52 4D = cHRM
- Data:
00 00 7A 26 00 00 80 84 00 00 FA 00 00 00 80 E8 00 00 75 30 00 00 EA 60 00 00 3A 98 00 00 17 70
- CRC:
9C BA 51 3C
Fysieke afmetingen (pHYs)
Deze bevat de fysieke afmetingen van de afbeelding, zodat deze indien mogelijk in de juiste fysieke grootte kan worden weergegeven.
- Lengte:
00 00 00 09 = 9 bytes lang
- Type:
70 48 59 73 = pHYs
- Data:
00 00 0B 13 00 00 0B 13 01 = 2835 pixels = 1 meter
- CRC:
00 9A 9C 18
Laatste wijzigingsdatum (tIME)
Deze bevat het tijdstip waarop de afbeelding is gemaakt.
- Lengte:
00 00 00 07 = 7 bytes lang
- Type:
74 49 4D 45 = tIME
- Data:
07 E5 05 0A 0A 2F 2C = 2021-05-10, 10:47:44 (UTC)
- CRC:
00 53 9E DD
Achtergrondkleur (bKGD)
Deze bevat de achtergrondkleur van de afbeelding die wordt weergegeven terwijl de afbeelding wordt geladen.
- Lengte:
00 00 00 06 = 6 bytes lang
- Type:
62 4B 47 44 = bKGD
- Data:
00 FF 00 FF 00 FF = wit
- CRC:
A0 BD A7 93
Beeldata (IDAT)
Deze bevat de gecomprimeerde beeldata.
- Lengte:
00 00 F5 51 = 62801 bytes lang
- Type:
49 44 41 54 = IDAT
- Data: ... (de gecomprimeerde en gefilterde bytes van de afbeelding)
- CRC:
BF EB 1B 15
Tekst (tEXt)
Dit kan willekeurige getagde tekstuele data over de afbeelding opslaan.
- Lengte:
00 00 00 27 = 39 bytes lang
- Type:
74 45 58 74 = tEXt
- Data:
46 69 6C 65 00 2F 68 6F 6D 65 2F 73 6D 69 74 2F 50 69 63 74 75 72 65 73 2F 69 63 6F 6E 33 2F 33 64 2E 62 6C 65 6E 64
- key: "File", value: "/home/smit/Pictures/icon3/3d.blend"
- CRC:
88 DA 55 E7
Tekst (tEXt)
- Lengte:
00 00 00 13 = 19 bytes lang
- Type:
74 45 58 74 = tEXt
- Data:
52 65 6E 64 65 72 54 69 6D 65 00 30 30 3A 31 32 2E 31 35
- key: "RenderTime", value: "00:12.15"
- CRC:
5E 2F 7A B4
Trailer (IEND)
Deze lege chunk geeft het einde van het PNG-bestand aan.
- Lengte:
00 00 00 00 = 0 bytes lang
- Type:
49 45 4E 44 = IEND
- CRC:
AE 42 60 82
Het extraheren van de beeldata
We nemen alle IDAT-chunks en voegen deze samen. In deze afbeelding is er slechts één IDAT-chunk. Sommige afbeeldingen hebben meerdere IDAT-chunks; de inhoud hiervan wordt door de PNG-decoder samengevoegd. Dit is gedaan zodat streaming-encoders de totale datalengte niet vooraf hoeven te weten, aangezien de datalengte aan het begin van elke chunk staat. Meerdere IDAT-chunks zijn ook nodig voor het coderen van afbeeldingen waarbij de beeldata een lengte heeft die groter is dan de grootste mogelijke chunk-grootte (2554).
Wanneer we de gecomprimeerde data behandelen als ruwe beeldata, ziet het eruit als willekeurige ruis. Dit betekent dat het compressie-algoritme goed werk heeft geleverd. Gecomprimeerde data zou een hogere entropie moeten hebben dan de data met lagere entropie die het codeert. Bovendien is het minder dan een derde van de hoogte van de eigenlijke afbeelding: dat is een goede compressie!
Decompresseren
De belangrijkste chunk is de IDAT-chunk, die de eigenlijke beeldata bevat. Om de gefilterde beeldata te verkrijgen, voegen we de data in de IDAT-chunks samen en decompresseren we deze met zlib. Er is hier geen sprake van een afbeelding-specifiek compressiemechanisme: gewoon normale zlib-compressie.
Naast het feit dat de kleuren er helemaal verkeerd uitzien, lijkt de afbeelding ook horizontaal verschoven te zijn. Dit komt doordat elke regel van de afbeelding een filter heeft als eerste byte. Filters verminderen de grootte van de afbeelding niet direct, maar brengen deze in een vorm die beter comprimeerbaar is door zlib.
Defiltering
We nemen de gedecomprimeerde data en maken het filter op elke regel ongedaan. Dit levert ons de gedecodeerde afbeelding op, identiek aan het origineel. Hieronder staat de populariteit van elk filtertype:
- None: 0
- Subtract: 23
- Up: 77
- Average: 134
- Paeth: 22
Interlacing
PNG's kunnen optioneel 'interlaced' zijn, wat de afbeelding opsplitst in 7 verschillende afbeeldingen, waarvan elke afbeelding een niet-overlappend gebied van de totale afbeelding beslaat.
Elk van de 7 afbeeldingen wordt opeenvolgend geladen, waarbij steeds meer detail aan de afbeelding wordt toegevoegd terwijl deze wordt geladen. Deze reeks afbeeldingen wordt Adam7 genoemd.
De data voor elk van de 7 afbeeldingen worden na elkaar in het beeldbestand opgeslagen. Als we het voorbeeldbeeld nemen en interlacing inschakelen, zien we de ruwe ongecomprimeerde beeldata. De 7 passes die we uit die beeldata kunnen extraheren, zien eruit als gedownscalde versies van de afbeelding.
Omdat sommige van die sub-afbeeldingen rechthoeken zijn terwijl de eigenlijke afbeelding vierkant is, zullen er bij het laden van de afbeelding meer details horizontaal dan verticaal zichtbaar zijn. Dit is omdat horizontale details belangrijker zijn voor de menselijke visuele perceptie dan verticale details.
Bonus: bugs
Dit is wat er gebeurt wanneer er een bug zit in het 'Average'-filter, waardoor overflows onjuist worden behandeld (de manier waarop integer overflow in de filterwaarde-berekening is gespecificeerd, is namelijk iets anders dan bij de rest).
Elke stap van het decoderen van een PNG
Chunking
PNG-bestanden worden opgeslagen als een reeks 'chunks'. Elke chunk heeft een lengte van 4 bytes, een naam van 4 bytes, de eigenlijke data en een CRC (Cyclic Redundancy Check). De meeste chunks bevatten metadata over de afbeelding, maar de IDAT-chunk bevat de eigenlijke gecomprimeerde beeldata. De hoofdlettercombinatie van de naam van de chunk bepaalt hoe onverwachte chunks behandeld moeten worden:
| Hoofdletters | Kleine letters | Betekenis |
| Kritiek | Hulp (Ancillary) | Is deze chunk vereist om de afbeelding correct weer te geven? |
| Publiek | Privé | Is dit een private, applicatie-specifieke chunk? |
| Gereserveerd | Gereserveerd | - |
| Onveilig om te kopiëren | Veilig om te kopiëren | Moeten programma's die de PNG-data wijzigen deze chunk begrijpen? |
Hieronder volgen de chunks in de voorbeeldafbeelding:
Signature
Dit identificeert de afbeelding als een PNG. Het bevat newline-tekens en andere tekens die vaak corrupt raken, om er zeker van te zijn dat de binaire beeldata niet beschadigd is. 89 50 4E 47 0D 0A 1A 0A
Header (IHDR)
Deze bevat metadata over de afbeelding.
- Lengte:
00 00 00 0D = 13 bytes lang
- Type:
49 48 44 52 = IHDR
- Data:
00 00 01 00 00 00 01 00 08 02 00 00 00
- Breedte: 256
- Hoogte: 256
- Bitdiepte: 8
- Kleurruimte: Truecolor RGB
- Compressiemethode: 0
- Filtermethode: 0
- Interlacing: uitgeschakeld
- CRC:
D3 10 3F 31
Gamma (gAMA)
Deze bevat de gamma van de afbeelding, gebruikt om nauwkeuriger kleuren weer te geven.
- Lengte:
00 00 00 04 = 4 bytes lang
- Type:
67 41 4D 41 = gAMA
- Data:
00 00 B1 8F = 45455
- CRC:
0B FC 61 05
Informatie over kleurruimte (cHRM)
Deze bevat data over waar in de volledige CIE-kleurruimte de kleuren in de afbeelding zich bevinden, zodat monitoren die kleuren buiten de standaard sRGB-ruimte ondersteunen de afbeelding beter kunnen weergeven.
- Lengte:
00 00 00 20 = 32 bytes lang
- Type:
63 48 52 4D = cHRM
- Data:
00 00 7A 26 00 00 80 84 00 00 FA 00 00 00 80 E8 00 00 75 30 00 00 EA 60 00 00 3A 98 00 00 17 70
- CRC:
9C BA 51 3C
Fysieke afmetingen (pHYs)
Deze bevat de fysieke afmetingen van de afbeelding, zodat deze indien mogelijk in de juiste fysieke grootte kan worden weergegeven.
- Lengte:
00 00 00 09 = 9 bytes lang
- Type:
70 48 59 73 = pHYs
- Data:
00 00 0B 13 00 00 0B 13 01 = 2835 pixels = 1 meter
- CRC:
00 9A 9C 18
Laatste wijzigingsdatum (tIME)
Deze bevat het tijdstip waarop de afbeelding is gemaakt.
- Lengte:
00 00 00 07 = 7 bytes lang
- Type:
74 49 4D 45 = tIME
- Data:
07 E5 05 0A 0A 2F 2C = 2021-05-10, 10:47:44 (UTC)
- CRC:
00 53 9E DD
Achtergrondkleur (bKGD)
Deze bevat de achtergrondkleur van de afbeelding die wordt weergegeven terwijl de afbeelding wordt geladen.
- Lengte:
00 00 00 06 = 6 bytes lang
- Type:
62 4B 47 44 = bKGD
- Data:
00 FF 00 FF 00 FF = wit
- CRC:
A0 BD A7 93
Beeldata (IDAT)
Deze bevat de gecomprimeerde beeldata.
- Lengte:
00 00 F5 51 = 62801 bytes lang
- Type:
49 44 41 54 = IDAT
- Data: ... (de gecomprimeerde en gefilterde bytes van de afbeelding)
- CRC:
BF EB 1B 15
Tekst (tEXt)
Dit kan willekeurige getagde tekstuele data over de afbeelding opslaan.
- Lengte:
00 00 00 27 = 39 bytes lang
- Type:
74 45 58 74 = tEXt
- Data:
46 69 6C 65 00 2F 68 6F 6D 65 2F 73 6D 69 74 2F 50 69 63 74 75 72 65 73 2F 69 63 6F 6E 33 2F 33 64 2E 62 6C 65 6E 64
- key: "File", value: "/home/smit/Pictures/icon3/3d.blend"
- CRC:
88 DA 55 E7
Tekst (tEXt)
- Lengte:
00 00 00 13 = 19 bytes lang
- Type:
74 45 58 74 = tEXt
- Data:
52 65 6E 64 65 72 54 69 6D 65 00 30 30 3A 31 32 2E 31 35
- key: "RenderTime", value: "00:12.15"
- CRC:
5E 2F 7A B4
Trailer (IEND)
Deze lege chunk geeft het einde van het PNG-bestand aan.
- Lengte:
00 00 00 00 = 0 bytes lang
- Type:
49 45 4E 44 = IEND
- CRC:
AE 42 60 82
Het extraheren van de beeldata
We nemen alle IDAT-chunks en voegen deze samen. In deze afbeelding is er slechts één IDAT-chunk. Sommige afbeeldingen hebben meerdere IDAT-chunks; de inhoud hiervan wordt door de PNG-decoder samengevoegd. Dit is gedaan zodat streaming-encoders de totale datalengte niet vooraf hoeven te weten, aangezien de datalengte aan het begin van elke chunk staat. Meerdere IDAT-chunks zijn ook nodig voor het coderen van afbeeldingen waarbij de beeldata een lengte heeft die groter is dan de grootste mogelijke chunk-grootte (2554).
Wanneer we de gecomprimeerde data behandelen als ruwe beeldata, ziet het eruit als willekeurige ruis. Dit betekent dat het compressie-algoritme goed werk heeft geleverd. Gecomprimeerde data zou een hogere entropie moeten hebben dan de data met lagere entropie die het codeert. Bovendien is het minder dan een derde van de hoogte van de eigenlijke afbeelding: dat is een goede compressie!
Decompresseren
De belangrijkste chunk is de IDAT-chunk, die de eigenlijke beeldata bevat. Om de gefilterde beeldata te verkrijgen, voegen we de data in de IDAT-chunks samen en decompresseren we deze met zlib. Er is hier geen sprake van een afbeelding-specifiek compressiemechanisme: gewoon normale zlib-compressie.
Naast het feit dat de kleuren er helemaal verkeerd uitzien, lijkt de afbeelding ook horizontaal verschoven te zijn. Dit komt doordat elke regel van de afbeelding een filter heeft als eerste byte. Filters verminderen de grootte van de afbeelding niet direct, maar brengen deze in een vorm die beter comprimeerbaar is door zlib.
Defiltering
We nemen de gedecomprimeerde data en maken het filter op elke regel ongedaan. Dit levert ons de gedecodeerde afbeelding op, identiek aan het origineel. Hieronder staat de populariteit van elk filtertype:
- None: 0
- Subtract: 23
- Up: 77
- Average: 134
- Paeth: 22
Interlacing
PNG's kunnen optioneel 'interlaced' zijn, wat de afbeelding opsplitst in 7 verschillende afbeeldingen, waarvan elke afbeelding een niet-overlappend gebied van de totale afbeelding beslaat.
Elk van de 7 afbeeldingen wordt opeenvolgend geladen, waarbij steeds meer detail aan de afbeelding wordt toegevoegd terwijl deze wordt geladen. Deze reeks afbeeldingen wordt Adam7 genoemd.
De data voor elk van de 7 afbeeldingen worden na elkaar in het beeldbestand opgeslagen. Als we het voorbeeldbeeld nemen en interlacing inschakelen, zien we de ruwe ongecomprimeerde beeldata. De 7 passes die we uit die beeldata kunnen extraheren, zien eruit als gedownscalde versies van de afbeelding.
Omdat sommige van die sub-afbeeldingen rechthoeken zijn terwijl de eigenlijke afbeelding vierkant is, zullen er bij het laden van de afbeelding meer details horizontaal dan verticaal zichtbaar zijn. Dit is omdat horizontale details belangrijker zijn voor de menselijke visuele perceptie dan verticale details.
Bonus: bugs
Dit is wat er gebeurt wanneer er een bug zit in het 'Average'-filter, waardoor overflows onjuist worden behandeld (de manier waarop integer overflow in de filterwaarde-berekening is gespecificeerd, is namelijk iets anders dan bij de rest).