Obsah
1. Obecné informace o struktuře souborů PNG
2. Hlavička souboru typu PNG
3. Obecná struktura chunků
4. Způsob pojmenování chunků
5. Nejvýznamnější povinné chunky
6. Chunk typu IHDR
7. Chunk typu IEND
8. Nejvýznamnější nepovinné chunky
9. Obsah dalšího pokračování tohoto seriálu
1. Obecné informace o struktuře souborů PNG
Již v předchozích částech tohoto seriálu jsem se několikrát zmiňoval o tom, že mnozí programátoři považují PNG za jeden z nejlépe navržených binárních souborových formátů. Dnes se podíváme, kolik je na tomto tvrzení pravdy. Autoři PNG při jeho návrhu uvažovali o několika variantách interní struktury tohoto grafického formátu.
Jednou z uvažovaných variant byl čistě textový popis obrazových dat ve stylu formátů PBM, PGM a PPM (Portable BitMap, Portable GrayMap, Portable PixelMap), přičemž by celý soubor byl před svým uložením na disk zkomprimován programem gzip či bzip2. Nakonec se od tohoto návrhu, který byl v několika ohledech problematický (čitelnost, zabezpečení, přeskakování bloků dat apod.), upustilo a přešlo se na návrh čistě binárního formátu, což se z dlouhodobého hlediska ukázalo jako krok správným směrem.
Tvůrci PNG se také poučili z mnoha chyb a nedostatků do té doby používaných binárních formátů a navrhli konzistentní interní strukturu PNG, která je na jednu stranu velmi sofistikovaná a na druhou stranu přitom poměrně jednoduchá na implementaci (aplikace mají přesné informace o tom, která data musí načítat a která data mají menší význam). Celý binární soubor s uloženým obrázkem se skládá z hlavičky, která je neměnná, tj. neobsahuje ani číslo verze. Za hlavičkou se nachází libovolné množství takzvaných chunků, což jsou pojmenované bloky, z nichž každý je opatřen svou délkou, typem a kontrolním součtem CRC (Cyclic Redundancy Check/Cyclic Redundancy Code).
2. Hlavička souboru typu PNG
Nejprve si popíšeme hlavičku PNG. Ta byla vytvořena s ohledem na heterogenní prostředí Internetu. Při přenosu souborů v tomto heterogenním prostředí může docházet k nežádoucím modifikacím dat, které je nutné v případě binárního formátu detekovat a vadné soubory (resp. jejich části) nenačítat. Nežádoucí modifikace mohou být několika typů:
- Při přenosu obrázku přes internet se ztratí nejvyšší bit ve všech bajtech (bit se většinou vynuluje). Tato nežádoucí modifikace může nastat například při přenosu souboru pomocí protokolu FTP nebo při posílání souborů špatně nastaveným e-mailovým klientem (tento problém však mohou způsobit i některé e-mailové servery).
- Dojde k náhradě ASCII znaku CR (Cursor Return) za znak LF (Line Feed). Může se jednat o chybu při přenosu binárního souboru mezi různými platformami, nebo při editaci binárního souboru ve špatně nastaveném textovém editoru.
- Dojde k náhradě znaku LF za znak CR. Jedná se o obdobu předchozí chyby.
- Dojde k náhradě znaku CR či znaku LF za dvojici znaků CR+LF. Tato chyba může nastat při přenosu souboru pomocí protokolu FTP, přeposlání e-mailem či při editaci v textovém editoru.
- Dojde k náhradě dvojice znaků CR+LF za znak LF či za znak CR. Jedná se o obdobu předchozí chyby.
- Přenos dat je ukončen po přenesení ASCII znaku, který je na dané platformě považován za znak ukončující standardní vstup. Většinou se jedná o znak Ctrl+Z (DOS, Windows) nebo Ctrl+D (Unixy).
Hlavička grafického formátu PNG je z těchto důvodů vytvořena tak, aby byly všechny výše uvedené nežádoucí modifikace při načítání souboru detekovány a modifikované soubory odstraněny z dalšího zpracování. Hlavička PNG má délku pouhých osmi bytů, které mají vždy konstantní hodnoty (ve výpisu uvedené hexadecimálně):
89 50 4e 47 0d 0a 1a 0a
Význam jednotlivých bytů v hlavičce je následující:
Byte | Význam bytu |
---|---|
89 | jedná se o byte s nastaveným nejvyšším bitem (detekce sedmibitového přenosu) |
50 4e 47 | řetězec ‚PNG‘, spolu s prvním bytem jednoznačně detekuje typ souboru na platformách, které typ rozpoznávají z obsahu a ne z koncovky |
0d 0a | znaky CR+LF, detekce náhrady za jinou sekvenci: CR, LF či LF+CR |
1a | znak Ctrl+Z, pro příkaz type MS-DOSu |
0a | znak LF, detekce náhrady za CR+LF či LF+CR |
Vidíme, že v pouhých osmi bytech se tvůrcům PNG podařilo vytvořit sekvenci bytů, na kterých je možné otestovat, zda přenos s velkou pravděpodobností proběhl v pořádku. Hned první byte byl zvolen tak, aby byl větší než 0×7f, tj. aby měl nastavený nejvyšší bit na jedničku. To má dva významy: detekuje se defektní sedmibitový přenos a některé textové editory soubor správně detekují jako binární a ne ASCII. Dále následují tři znaky tvořící řetězec ‚PNG‘, což způsobuje, že hlavička souboru je částečně čitelná při zběžném pohledu i pro člověka. Následuje dvojice znaků CR+LF, na kterých je možné detekovat problémy při přenosu souboru mezi různými platformami. Následuje znak Ctrl+Z, který při zadání příkazu:
type obrazek.png
v prostředí MS-DOS/MS-Windows způsobí výpis následujícího textu:
ëPNG
tj. zobrazí se pouze čtyři znaky, za kterými je řádek i výpis ukončen. Nemůže tedy nastat situace, kdy se po zadání příkazu PNG obrazovka zaplní různými „paznaky“. Jediným vypsaným paznakem je ono ë (či jiný znak v závislosti na lokalizaci), které by mělo uživatele varovat, že se pokouší vypsat obsah binárního souboru. Poslední znak v hlavičce je LN, který opět slouží pro detekci, zda nedošlo k jeho náhradě jiným znakem či jinou sekvencí znaků, typicky CR, CR+LF či LF+CR. Ihned za hlavičkou totiž následuje sekvence tří nulových bytů, které by byly náhradou znaku CR za dva znaky posunuty, což opět způsobí detekovatelnou chybu při čtení.
Všimněte si, že nikde není uvedena verze PNG. To je zcela zbytečné, protože informace o tom, která data v obrázku jsou pro korektní zpracování (zobrazení) zapotřebí a která ne, je uvedena přímo u jednotlivých chunků.
Pokud je hlavička korektně načtena, může prohlížeč či jiná aplikace pracující s PNG pokračovat v načítání dalších dat. Před jejich popisem se však musíme seznámit s významem takzvaných chunků, což jsou pojmenované a pomocí kontrolního součtu zabezpečené bloky dat.
3. Obecná struktura chunků
Veškeré informace i metainformace o zpracovávaném obrázku jsou uloženy v blocích dat nazvaných chunky (přibližný překlad je blok). Každý chunk se skládá ze čtyř částí:
- První část má konstantní velikost čtyři byty a obsahuje celkovou délku datové části chunku. Teoreticky je tedy maximální délka datové části rovna 232-1 bytům; avšak pro snazší implementaci, například v těch programovacích jazycích, které nemají implementovaný beznaménkový datový typ (Java), je maximální hodnota délky rovna „pouze“ 231-1 bytů. V praxi jsou však délky chunků o několik řádů menší.
- Druhá část chunku má opět velikost čtyři byty. Obsahuje jméno (typ) chunku ve formátu čtyř ASCII znaků malé i velké anglické abecedy. Jedná se o ASCII kódy v rozsahu 65–90 a 97–122 decimálně. Jedná se o velmi dobře navržený způsob pojmenování, protože jméno chunku může být načteno a následně rozpoznáno pouhou jednou operací porovnání (32 bitových integerů) bez nutnosti implementace řetězcového porovnání a současně je typ chunku velmi dobře čitelný i pro člověka. Velikost znaků ve jménu (minusky/verzálky) dále určuje, zda je daný chunk pro zpracování obrázku volitelný či povinný – viz následující kapitoly.
- Třetí část chunku je tvořena vlastními daty. Tato část má v některých případech, například u chunku nazvaného IDAT, nulovou délku.
- Poslední čtvrtá část chunku má délku čtyři byty a obsahuje kontrolní součet (CRC) druhé a třetí části, tj. jména (typu) chunku a uložených dat. Vzhledem k tomu, že délka chunku není do kontrolního součtu zahrnuta, je možné CRC generovat přímo při zápisu dat bez nutnosti druhého průchodu v případě, že délka chunku není dopředu známá (například při komprimaci). Použitý polynom má tvar:
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
Všechny vícebytové položky v chuncích (včetně jejich délky) jsou uloženy v pořadí big endian, tj. byte s nejvyšší váhou je v souboru na prvním místě. Použití tohoto uspořádání bytů je pochopitelné, protože většina internetových formátů a protokolů pracuje se stejným uspořádáním, kterému se proto také někdy říká network byte order. Na platformách Apple Macintosh, Sun, Atari ST a Amiga se jedná o nativní formát, na platformě i86 je nutné provést převod pomocí knihovních maker.
4. Způsob pojmenování chunků
Zbývá vysvětlit, jakým způsobem jsou chunky v souboru pojmenovány. Již víme, že jméno (resp. typ) chunku je uloženo ve čtyřech bytech. Vždy se jedná o posloupnost čtyř ASCII znaků malé či velké abecedy, tj. z celkového množství 232 jmen je jich možné použít jen (2×26)5=380 204 032. To by mělo po nějaký čas postačovat :-).
Za povšimnutí stojí fakt, že u kódu ASCII je rozdíl mezi velkým a malým znakem dán pouze změnou jednoho bitu, konkrétně bitu číslo 5. Jinými slovy, znak malé abecedy (minuska) má ASCII kód o 32 vyšší než odpovídající znak velké abecedy (verzála). Vzhledem k tomu, že je jméno chunku tvořeno čtyřmi znaky, máme k dispozici celkem čtyři bity, kterými můžeme rozlišit různou důležitost chunků. Význam pátých bitů každého ze znaků názvu je následující:
- Pátý bit prvního znaku názvu: tento bit určuje, zda je daný chunk pro zpracování obrázku nezbytný. Příkladem chunku, který je nutné vždy zpracovat, je IHDR, mezi nepovinný chunk patří tEXt.
- Pátý bit druhého znaku názvu: tento bit určuje, zda je typ chunku veřejný (určený specifikací a známý více aplikacím) nebo privátní, tj. dávající smysl pouze jedné či několika málo aplikacím. Díky tomuto bitu nikdy nenastanou kolize mezi oficiálními a aplikačními chunky.
- Pátý bit třetího znaku názvu: je ve verzi PNG 1.0, PNG 1.1 i PNG 1.2 vždy vynulován, tj. třetí znak názvu chunku je vždy zapsán velkým písmenem.
- Pátý bit čtvrtého znaku názvu: tento bit je určen zejména pro grafické editory a konvertory obrázků. Jeho hodnota určuje, zda se při změně obrázku (editaci) má daný chunk uložit zpět do souboru, či zda se se změnou obrázku chunk stane nepoužitelným. Příkladem může být chunk obsahující histogram (hIST) – při změně obrázku dojde i ke změně histogramu, a proto není korektní provést kopii chunku ze zdrojového obrázku do obrázku cílového (něco jiného ovšem je vytvoření chunku stejného typu, ale s jiným obsahem).
5. Nejvýznamnější povinné chunky
Prakticky každý obrázek typu PNG obsahuje tři povinné chunky v následujícím pořadí:
Typ chunku | Název chunku |
---|---|
IHDR | hlavička obrázku |
IDAT | datová část – pixmapa |
IEND | ukončující značka |
U obrázků obsahujících barvovou paletu přibývá ještě jeden typ chunku (paletu) a formát je následující:
Typ chunku | Název chunku |
---|---|
IHDR | hlavička obrázku |
PLTE | barvová paleta |
IDAT | datová část – pixmapa |
IEND | ukončující značka |
V dalších dvou kapitolách si vysvětlíme formát chunků IHDR a IEND, popis dalších chunků (PLTE a IDAT) bude uveden příště.
6. Chunk typu IHDR
V chunku typu IHDR je uložena hlavička obrázku. Ta je odlišná od hlavičky celého souboru, kterou jsme si popsali ve druhé kapitole. Hlavička obrázku obsahuje základní metainformace o uloženém obrázku, zejména jeho rozlišení, bitovou hloubku, typ kódování barev a použitou filtraci. Vzhledem k tomu, že datová část hlavičky obrázku má vždy délku 13 bytů, začíná chunk posloupností 0×00 0×00 0×00 0×0d. Hlavička obrázku musí být uvedena ihned za hlavičkou PNG, při čtení je tedy nutné otestovat, zda se ihned po přečtení hlavičky PNG objeví tato posloupnost. Pokud tomu tak není, mohlo dojít k poškození souboru (viz předchozí kapitoly).
Datová část chunku IHDR obsahuje následující položky:
Offset | Počet byte | Význam |
---|---|---|
00 | 4 | šířka obrázku uvedená v pixelech |
04 | 4 | výška obrázku uvedená v pixelech |
08 | 1 | bitová hloubka pixelů v barvovém kanálu (povolené hodnoty jsou 1, 2, 4, 8 a 16) |
09 | 1 | typ kódování barev (viz další tabulka) |
0a | 1 | použitá metoda komprimace (musí zde být 0-deflate) |
0b | 1 | použitá metoda filtrace (musí zde být 0-adaptivní filtrace) |
0c | 1 | prokládání obrázku (0-bez prokládání, 1-prokládání) |
Metody komprimace i filtrace musí být v současnosti nastaveny na nulu. Pokud je obrázek prokládaný, je příslušný byte nastaven na 0×01, v opačném případě na 0×00. Zbývá nám vysvětlit vztah mezi bitovou hloubkou pixelů v barvovém kanálu a typem kódování barev. V současnosti je možné využít následující kombinace:
Typ kódování barev | Bitová hloubka | Popis |
---|---|---|
0 | 1,2,4,8,16 | Obrázek ve stupních šedi (popř. černobílá pérovka) |
2 | 8,16 | Plnobarevný (true color) obrázek typu RGB |
3 | 1,2,4,8 | Obrázek s barvovou paletou (v souboru musí existovat chunk PLTE) |
4 | 8,16 | Obrázek ve stupních šedi a průhledností (alfa kanálem) – nepříliš používaná kombinace |
6 | 8,16 | Obrázek, kde každý pixel obsahuje všechny čtyři hodnoty RGBA (tj. kromě tří barvových složek i alfa kanál) |
Bližší informace o jednotlivých typech obrázků budou uvedeny u popisu jejich binárního tvaru v příští části seriálu.
7. Chunk typu IEND
Chunk IEND by se měl nacházet na konci každého PNG souboru, aby byl načítací program informován, že může ukončit načítání a že se soubor přenesl celý. Tento chunk neobsahuje žádnou datovou část, proto je jeho bytová sekvence o délce dvanácti bytů vždy stejná (0×00 0×00 0×00 0×00 0×49 0×45 0×4e 0×44 0×ae 0×42 0×60 0×82):
Sekvence bytů | Význam |
---|---|
00 00 00 00 | Délka datové části chunku: 0 |
49 45 4e 44 | Sekvence ASCII znaků ‚IEND‘ |
ae 42 60 82 | CRC tohoto chunku |
8. Nejvýznamnější nepovinné chunky
V této kapitole si uvedeme soupis nejvýznamnějších nepovinných chunků, které se mohou v grafických souborech PNG vyskytnout. Podrobnější popis těchto chunků bude uveden v některém z následujících pokračování tohoto seriálu.
Typ chunku | Název chunku |
---|---|
gAMA | Gamma Value |
bKGD | Background Color |
tIME | Timestamp |
tEXt | zTXt Latin-1 Text Annotations |
iTXt | International Text Annotations |
hIST | Histogram |
sPLT | Suggested Palette |
sBIT | Significant Bits |
pHYs | Physical Pixel Dimensions |
sCAL | Physical Scale |
oFFs | Image Offset |
pCAL | Pixel Calibration |
fRAc | Fractal Parameters |
gIFg | gIFx GIF Conversion Info |
gIFt | GIF Plain Text |
9. Obsah dalšího pokračování tohoto seriálu
V následujícím pokračování tohoto seriálu si ukážeme a podrobně popíšeme binární strukturu jednoduchých obrázků typu PNG. Bude se jednat o obrázky s barvovou paletou, obrázky uložené ve stupních šedi a konečně o plnobarevné (truecolor) obrázky s i bez alfa kanálu (průhlednosti).