Obsah
1. Interní struktura formátu GIF
2. Nejmenší obrázek typu GIF – jeden neprůhledný pixel
3. Změna LZW kódu a barvové palety
4. Změna rozlišení logické obrazovky
5. Posun rámce v logické obrazovce
6. Obrázek typu GIF s transparentním pixelem
7. Testovací obrázky ke stažení
8. Obsah závěrečné části – animace, prokládání a LZW komprimace
1. Interní struktura formátu GIF
V první kapitole si řekneme, jakým způsobem jsou data v grafickém formátu GIF interně ukládána. Celý soubor (resp. v případě přenosu po datových sítích „pouhý“ tok bytů) je rozdělen do bloků, z nichž některé jsou povinné, některé se musí vyskytovat na určitém místě souboru, další se mohou opakovat apod. Povolené bloky, které se mohou v GIFu vyskytovat, jsou popsány v následující tabulce spolu s informací, zda se jedná o bloky povinné (tj. bloky, které se musí vyskytovat v každém validním souboru) a zda je možné bloky v rámci jednoho souboru opakovat (pro účely animací, slideshow, obejití maximálního počtu 256 barev apod.). Dále je u každého bloku uvedeno, zda se jeho definice vyskytovala už ve specifikaci GIF87a či až v novější specifikaci GIF89a.
Název bloku | Povinná položka? | Lze opakovat? | Verze |
---|---|---|---|
signatura GIFu | ano | ne | 87a |
verze souboru | ano | ne | 87a |
popis logické obrazovky | ano | ne | 87a |
globální barvová paleta | ne | ne | 87a |
rozšiřující grafický blok | ne | ano | 89a |
popis rámce | ano | ano | 87a |
lokální barvová paleta | ne | ano | 87a |
data rámce (pixely) | ano (pro rámec) | ano | 87a |
rozšiřující informace (textové, binární) | ne | ano | 89a |
ukončovací znak GIF souboru | ano | ne | 87a |
Při našem zkoumání interní struktury GIFu nejprve začneme s nejjednodušším prakticky použitelným souborem, který je možné vytvořit. Podrobný popis bude uveden v následující kapitole, zde si však řekněme, jaké bloky je nutné i v tom nejjednodušším obrázku použít a v jakém pořadí:
Pořadí | Název bloku |
---|---|
1 | signatura GIFu |
2 | verze souboru |
3 | popis logické obrazovky |
4 | globální barvová paleta |
5 | popis rámce |
6 | data rámce (pixely) |
7 | ukončovací znak GIF souboru |
Výše uvedeným způsobem jsou uloženy prakticky všechny statické GIFy, tj. GIFy neobsahující animaci ani optimalizaci s využitím více rámců (o více než 256 barvách ani nemluvě). V případě, že se ukládají animované GIFy, vypadá situace již poněkud složitěji, protože je nutné každý snímek animace reprezentovat pomocí minimálně jednoho rámce a mezi tyto rámce vložit pauzu pomocí rozšiřujícího grafického bloku. V případě, že se má animace opakovat, je nutné přidat i informaci o počtu opakování (popř. o nekonečné animaci), což se děje pomocí bloku aplikačního rozšíření (Application Extension Block, ten bude popsán v závěrečné části seriálu). Animace tvořená třemi snímky (rámci) bude mít následující strukturu:
Pořadí | Název bloku |
---|---|
1 | signatura GIFu |
2 | verze souboru |
3 | popis logické obrazovky |
4 | globální barvová paleta |
5 | aplikační rozšiřující blok se smyčkou |
6 | první rozšiřující grafický blok (nastavení zpoždění) |
7 | první popis rámce |
8 | první lokální barvová paleta (pouze pokud se liší od globální) |
9 | první data rámce (pixely) |
10 | druhý rozšiřující grafický blok (nastavení zpoždění) |
11 | druhý popis rámce |
12 | druhý lokální barvová paleta (pouze pokud se liší od předchozí) |
13 | druhý data rámce (pixely) |
14 | třetí rozšiřující grafický blok (nastavení zpoždění) |
15 | třetí popis rámce |
16 | třetí lokální barvová paleta (pouze pokud se liší od předchozí) |
17 | třetí data rámce (pixely) |
18 | ukončovací znak GIF souboru |
2. Nejmenší obrázek typu GIF – jeden neprůhledný pixel
Absolutně nejmenší obrázek typu GIF, který je možné zobrazit ve všech prohlížečích, obsahuje pouze globální barvovou paletu se dvěma položkami, jeden rámec o rozlišení 1×1 pixel a v něm uložený pouhopouhý jeden pixel. Délka souboru je rovna 35 bytům, což se sice na první pohled může zdát mnoho, ale „konkurenční“ minimální PNG má celých 65 bytů (délka těchto souborů je většinou menší, než jejich odkaz zapsaný v HTML stránce :-). Teoreticky je sice možné GIF ještě zmenšit, například vynecháním globální barvové palety či dokonce celého rámce, ale takto vytvořený GIF mnoho prohlížečů odmítne zpracovat. Dále se tedy budeme zabývat naším 35bytovým podiobrázkem, který má při zobrazení v hexadecimálním editoru (KHexEdit, bvi, vim s xxd, stále nepřekonaný Hiew, biew, Dos Navigator atd.) například tento tvar:
0000000: 47 49 46 38 39 61 01 00 01 00 80 00 00 ff 80 00
0000010: ff ff ff 2c 00 00 00 00 01 00 01 00 00 02 02 44
0000020: 01 00 3b
Obrázek 1: Podiobrázek o velikosti 1×1 pixel -najdete ho? (Root obrázky centruje)
Jak jsme si již ukázali v první kapitole, obsahuje tento soubor sedm bloků: signaturu GIF, verzi souboru, popis logické obrazovky, globální barvovou paletu, popis rámce, data rámce a ukončovací znak GIF souboru. Všechny zmiňované bloky jsou uloženy za sebou, takže si předchozí zápis můžeme blíže ujasnit a rozepsat v následující tabulce:
Offset (dec) | Byte či sekvence (hex) | Význam bytové sekvence |
---|---|---|
00 |
47 49 46 |
ASCII řetězec ‚GIF‘ – „magické“ číslo souboru (signatura GIF) |
03 |
38 39 61 |
ASCII řetězec ‚89a‘ – verze GIFu |
06 |
01 00 |
šířka logické obrazovky: jeden pixel |
08 |
01 00 |
výška logické obrazovky: jeden pixel |
10 |
80 |
bitové pole: povolení globální barvové palety |
11 |
00 |
index barvy pozadí |
12 |
00 |
poměr výšky a šířky pixelu: 1÷1 |
13 |
ff 80 00 |
první barva v paletě: oranžový odstín |
16 |
ff ff ff |
druhá barva v paletě: čistě bílá |
19 |
2c |
značka začátku rámce |
20 |
00 00 |
x-ová pozice levého okraje rámce: nultý sloupec |
22 |
00 00 |
y-ová pozice horního okraje rámce: nultý řádek |
24 |
01 00 |
šířka rámce: jeden pixel |
26 |
01 00 |
výška rámce: jeden pixel |
28 |
00 |
bitové pole: bez barvové palety, bez prokládání a animace |
29 |
02 |
počáteční velikost LZW kódu v bitech-1 |
30 |
02 |
velikost bloku zakódovaného pomocí LZW |
31 |
44 01 |
zakódovaná data rámce (jeden pixel s nulovým indexem) |
33 |
00 |
ukončovací znak bloku zakódovaného pomocí LWZ |
34 |
3b |
ukončovací znak GIF souboru: znak ‚;‘ |
Rozpoznání jednotlivých bloků a informací v nich uložených si můžeme popsat ještě podrobněji:
- Každý soubor typu GIF musí začínat jeho signaturou, což je sekvence bytů s hexadecimálními hodnotami 0×47, 0×49 a 0×46. Po převodu do ASCII můžeme tuto sekvenci číst jako řetězec ‚GIF‘.
- Ihned po signatuře GIFu musí následovat další trojice bytů, která udává verzi GIFu. V současné době (a pravděpodobně i v budoucnosti) je možné použít pouze dvě verze, první je GIF87a (sekvence bytů 0×38 0×37 0×61) a druhá GIF89a (sekvence bytů 0×38 0×39 0×61).
- Po signatuře je uložen popis logické obrazovky. Nejprve je na dvou bytech uvedena šířka obrazovky v pixelech, následuje výška obrazovky, samozřejmě taktéž v pixelech. Stejně jako u dalších vícebytových polí, i u hodnot šířky a výšky jsou byty uspořádány v pořadí nižší-vyšší (little endian). V našem obrázku, který má rozlišení nastaveno na 1×1 pixel, tedy bude šířka i výška nastavena na hexadecimální hodnotu 0×01 0×00.
- Posléze následuje bitové pole, ve kterém je uvedeno, zda je použita globální barvová paleta (sedmý bit), počet bitů na pixel-1 (bity 3–6), zda je barvová paleta setříděna (bit 2) a konečně délka barvové palety (bity 0 a 1). Skutečná délka palety se vypočte podle vzorce: 2hodnota+1. V našem obrázku je použita globální barvová paleta a její délka je rovna 2 (zapisujeme jako nulu, protože 20+1=2), tj. hodnota zapsaná do bitového pole je rovna 0×80.
- Další byte obsahuje index barvy, která je použita jako pozadí v případě, že by plocha rámců nevyplnila celý obrázek. My tuto hodnotu prozatím nevyužijeme, proto zde může být nula – 0×00.
- Následuje byte, ve kterém je uveden poměr mezi výškou a šířkou pixelu. Vztah pro výpočet je následující: Aspect Ratio=(Pixel Aspect Ratio+15)/64. Pokud je v tomto byte uložena nula (0×00), není poměr specifikovaný. U verze GIF87a není tento byte využit a podle specifikace zde musí být zapsána hodnota nula. Tímto bytem také končí blok s informacemi o logické obrazovce a následuje globální barvová paleta.
- Jelikož je délka globální barvové palety rovna nule, obsahuje paleta pouze dvě barvy, každou reprezentovanou trojicí bytů. První barva, tj. barva s indexem nula, má barvové složky RGB nastaveny na hodnoty 0×ff 0×80 0×00, tj. jedná se o oranžový odstín. Druhá barva má barvové složky nastaveny na maximální hodnoty (0×ff 0×ff 0×ff), tj. jedná se o čistě bílou barvu.
- Po globální barvové paletě ihned následuje hlavička rámce. Ta je v souboru rozpoznána značkou začátku rámce, tj. bytem s hodnotou 0×2c. Odpovídající ASCII znak je ‚,‘ (čárka), což je pro oddělovač vhodná mnemotechnická pomůcka.
- Dva byty za značkou udávají x-ovou pozici levého okraje rámce, další dva byty pak y-ovou pozici horního okraje rámce. Vzhledem k tomu, že rámec začíná v levém horním rohu obrázku, jsou zde uloženy nulové hodnoty (0×00 0×00 0×00 0×00).
- Hlavička rámce pokračuje dvěma byty se šířkou rámce a dalšími dvěma byty s jeho výškou. Obě hodnoty nastavíme na jedničku, protože rámce bude mít rozlišení 1×1 pixel (0×01 0×00 0×01 0×00).
- Následuje bitové pole, ve kterém je uvedeno, zda se v rámci bude používat lokální barvová paleta (sedmý bit), zda je rámec prokládán (šestý bit), zda jsou barvy v lokální barvové paletě setříděny (pátý bit) a konečně délka lokální barvové palety (bity 0–2). Vzhledem k tomu, že lokální barvovou paletu nepotřebujeme, je v tomto bitovém poli uložena hodnota 0×00. Pokud by byla lokální barvová paleta přítomna, začínala by ihned za tímto bytem.
- Po hlavičce rámce již začíná sekce s daty rámce (pixely). Jednotlivé pixely jsou kódovány pomocí LZW algoritmu. Dekodér potřebuje při své inicializaci znát počáteční velikost kódu ukládaného ho hashovací tabulky. Tato velikost odpovídá počtu bitů na pixel zvýšeného o jedničku. Pro náš (maximálně dvoubarevný) obrázek by tedy měla být velikost nastavená na dvojku, vzhledem k implementačním detailům LZW je však definitoricky nastavena na trojku, tj. kód má velikost tři bity. V souboru je tato velikost snížena o jedničku, tj. nalezneme zde hodnotu 0×02. Hashovací tabulka je inicializována do podoby ukázané níže.
- Následuje byte udávající velikost bloku zakódovaného pomocí LZW. Kód pro náš jediný pixel se kupodivu musí rozepsat na dva byty, proto je i zde uložena hodnota 0×02.
- Následují vlastní obrazová data komprimovaná algoritmem LZW. Tato data mají délku dva byty a hodnotu 0×44 0×01. Po rozpisu do binární podoby získáme bitový vzorek 01000100 00000001, který musíme správně dekódovat. Víme, že velikost kódu je nastavena na tři bity, takže bitový vzorek rozložíme do trojic: 100 000 101 ??? ??? ? (začíná se od bitu s nejnižší váhou, hranice bytů se prostě překračují). První trojice bitů tvoří Clear code, ten zde musí být vždy umístěn, aby se hashovací tabulka dekodéru správně inicializovala. Následuje trojice bitů 000, což je první index do barvové palety (oranžová barva). Poslední trojice bitů značí End of LZW code, čímž je dekodéru řečeno, že má tuto fázi zpracování rámce ukončit. Hodnoty dalších bitů ve druhém byte nás tedy nezajímají, jsou zde uloženy jako výplň (vhodné místo pro vodoznak :-).
- Náš jediný pixel je uložen, proto se celý blok ukončí takzvaným ukončovacím znakem, který má hodnotu 0×00.
- V obrázku je použitý pouze jeden rámec, je tedy na čase celý soubor uzavřít. K tomu slouží byte, jehož ASCII hodnota je rovna znaku ‚;‘ a hexadecimální kód je tedy 0×3b. Teoreticky by za tímto kódem mohla následovat další data, prohlížeč by je však měl ignorovat.
Bitový vzorek | Význam |
---|---|
000 | první index do barvové palety |
001 | druhý index do barvové palety |
100 | clear code (CC) – inicializace hashovací tabulky |
101 | end of LZW code (EC) |
3. Změna LZW kódu a barvové palety
Pro ilustraci si ještě uveďme, jak by se náš pidiobrázek změnil v případě, že by jediný uložený pixel měl hodnotu 1 a ne 0. Také se změnila globální barvová paleta tak, aby barva číslo jedna nebyla bílá, ale čistě zelená:
Offset (dec) | Byte či sekvence (hex) | Význam bytové sekvence |
---|---|---|
00 |
47 49 46 |
ASCII řetězec ‚GIF‘ – „magické“ číslo souboru (signatura) |
03 |
38 39 61 |
ASCII řetězec ‚89a‘ – verze GIFu |
06 |
01 00 |
šířka logické obrazovky: jeden pixel |
08 |
01 00 |
výška logické obrazovky: jeden pixel |
10 |
80 |
bitové pole: povolení globální barvové palety |
11 |
00 |
index barvy pozadí |
12 |
00 |
poměr výšky a šířky pixelu: 1÷1 |
13 |
ff 80 00 |
první barva v paletě: oranžová barva |
16 |
00 ff 00 |
druhá barva v paletě: čistě zelená |
19 |
2c |
značka začátku rámce |
20 |
00 00 |
x-ová pozice levého okraje rámce: nultý sloupec |
22 |
00 00 |
y-ová pozice horního okraje rámce: nultý řádek |
24 |
01 00 |
šířka rámce: jeden pixel |
26 |
01 00 |
výška rámce: jeden pixel |
28 |
00 |
bitové pole: bez barvové palety, bez prokládání a animace |
29 |
02 |
počáteční velikost LZW kódu v bitech |
30 |
02 |
velikost bloku zakódovaného pomocí LZW-1 |
31 |
4c 01 |
zakódovaná data rámce (jeden pixel) |
33 |
00 |
ukončovací znak bloku zakódovaného pomocí LWZ |
34 |
3b |
ukončovací znak GIF souboru: ASCII znak ‚;‘ |
LZW kód má v tomto obrázku hodnotu 0×4c 0×01, což je binárně 01001100 00000001 a po rozpisu na tříbitové složky dostáváme posloupnost 100 (Clear Code), 001 (barva číslo 1) a 101 (konec LZW kódu).
Obrázek 2: Podiobrázek o velikosti 1×1 pixel (pixel je čistě zelený)
4. Změna rozlišení logické obrazovky
Přišel čas ukázat si první trik, který je možné s grafickým formátem GIF provést. Tento trik spočívá v tom, že se zvětší rozlišení grafické obrazovky, ovšem původní velikost jediného rámce zůstane nastavena na 1×1 pixel. To povede k tomu, že prohlížeč (pokud ovšem dodržuje specifikaci) zobrazí obrázek o specifikované velikosti, který je celý vybarven barvou pozadí. Na tomto obrázku se v levém horním rohu „krčí“ rámec o velikosti 1×1 pixel, jehož pixel má nastavenou odlišnou barvu. Následuje kód obrázku, jehož rozlišení je rovno 400×300 pixelům, což se hexadecimálně do hlavičky zapíše jako 0×90 0×01 a 0×2c 0×01, protože 0×190=400 a 0×12c=300 (tyto údaje jsou zapsány od šestého bytu). Barvy v globální barvové paletě jsou nastaveny na ff ff 00 (oranžová=barva pozadí) a 00 00 00 (černá=barva pixelu v rámci).
0000000: 47 49 46 38 39 61 90 01 2c 01 80 00 00 ff ff 00
0000010: 00 00 00 2c 00 00 00 00 01 00 01 00 00 02 02 4c
0000020: 01 00 3b
Obrázek 3: Obrázek o velikosti 400×300 pixelů s rámcem umístěným v levém horním rohu
Některé webové prohlížeče u takto vytvořeného obrázku ignorují nastavenou barvu pozadí a místo toho nastaví buď svoji vlastní barvu (například bílou či šedou), nebo pozadí vůbec nepřekreslí, to znamená, že se chovají, jako kdyby bylo pozadí vyplněno průhlednými pixely. Tohoto chování se dá využít k tvorbě různých efektů, ovšem s tím rizikem, že se každý webový prohlížeč může chovat jinak.
5. Posun rámce v logické obrazovce
Předchozí příklad s uměle zvětšeným obrázkem je ještě možné upravit tak, že se rámec posune z levého horního rohu na jiné místo, například do středu obrázku. Ten se nachází na souřadnicích [200, 150], což je hexadecimálně 0×c8 0×00 a 0×96 0×00 (přesněji řečeno, střed je roven [199.5, 149.5], to však můžeme při dané „sudé“ velikosti obrázku ignorovat). Prohlížeč odpovídající specifikaci by měl zobrazit oranžový obrázek o rozlišení 400×300 pixelů, v jehož středu se bude nacházet pixel černé barvy. Některé webové prohližeče však barvu pozadí ignorují a místo ní zobrazí původní plochu či podkladovou texturu HTML stránky.
0000000: 47 49 46 38 39 61 90 01 2c 01 80 00 00 ff ff 00
0000010: 00 00 00 2c c8 00 96 00 01 00 01 00 00 02 02 4c
0000020: 01 00 3b
Obrázek 4: Obrázek o velikosti 400×300 pixelů s rámcem umístěným v jeho středu
6. Obrázek typu GIF s transparentním pixelem
Při designu webových stránek se mnohdy neobejdeme bez jednoho velmi užitečného pomocníka – obrázku typu GIF s jediným transparentním (neprůhledným) pixelem. Ten je možné využít buď pro rezervaci místa na HTML stránce (například při „roztahování“ buněk tabulek) nebo při vykreslování rastrových obrázků pomocí JavaScriptu. Obrázek s transparentním pixelem je možné, stejně jako každý jiný obrázek, pomocí atributů width a height zvětšit na libovolnou hodnotu. Obrázek s jediným transparentním pixelem má také tu výhodu, že ho zobrazí všechny prohlížeče, včetně Lynxu :-) Podívejme se nyní, jak bude příslušný GIF soubor vypadat.
V této chvíli si již nevystačíme s obrázkem o velikosti 35 bytů, ale musíme před první (a jediný) rámec vložit ještě takzvaný rozšiřující grafický blok (Graphic Control Extension – GCE), což vede ke zvětšení obrázku na stále rozumných 43 bytů. To znamená, že GCE má délku osmi bytů s následujícím formátem:
Offset (dec) | Byte či sekvence (hex) | Význam bytové sekvence |
---|---|---|
00 | 21 | značka začátku rozšiřujícího bloku |
01 | f9 | identifikace rozšiřujícího bloku jako GCE |
02 | 04 | délka GCE (vždy nastavena na hodnotu 4) |
03 | ?? | bitové pole s příznaky |
04 | ?? | čas zpoždění zobrazení následujícího rámce |
05 | ?? | vyšší byte hodnoty zpoždění (specifikováno v setinách sekundy) |
06 | ?? | index barvy, která bude považována za průhlednou (transparentní) |
07 | 00 | ukončení rozšiřujícího bloku |
Je vhodné se zmínit o tom, že mnoho programů při ukládání GIFů automaticky vytvoří i GCE a to i v případech, kdy GCE není zapotřebí. Proto byly výše uvedené jednopixelové obrázky upraveny ručně v hexadecimálním editoru. Zbývá vysvětlit roli jednotlivých bitů v bitovém poli (o zpoždění snímků se budeme bavit příště v textu o animacích). V nejnižším bitu je uvedeno, zda je nějaká barva nastavena jako průhledná. Pokud je tento bit nastavený na jedničku, přečte se ze šestého bytu GCE index barvy, který označuje ty pixely, které nemají být vykresleny, a tím pádem pod nimi bude prosvítat původní barva. Druhým bitem je specifikováno, zda se po zobrazení rámce bude čekat na vstup od uživatele. Významem tohoto bitu se budeme zabývat v dalším pokračování seriálu. Další tři bity specifikují způsob zobrazení rámce; opět se jimi budeme podrobněji zabývat příště. Vzhledem k tomu, že nám dostačuje povolit průhlednou (transparentní) barvu, bude v bitovém poli zapsána hodnota 01 a index průhledné barvy necháme na nule. To vede k této podobě GCE:
21 f9 04 01 00 00 00 00
Rámec obsahuje pouze jeden pixel s barvou číslo 0, která je nastavena jako průhledná. Celý rámec je zakódován do posloupnosti:
2c 00 00 00 00 01 00 01 00 00 02 02 44 01 00
Což vede k následující podobě celého průhledného pidiobrázku:
0000000: 47 49 46 38 39 61 01 00 01 00 80 00 00 ff ff ff
0000010: 00 00 00 21 f9 04 01 00 00 00 00 2c 00 00 00 00
0000020: 01 00 01 00 00 02 02 44 01 00 3b
Obrázek 5: Podiobrázek o velikosti 1×1 pixel s průhledným pixelem (pokud nic nevidíte, je to v pořádku)
7. Testovací obrázky ke stažení
Pokud jste se nemohli trefit na výše uvedené obrázky (a prohlížeč neumí zobrazit odkazy na ně ve vlastnostech stránky), můžete všech pět výše popsaných obrázků získat z následujících odkazů:
- podiobrázek o velikosti 1×1 pixel (pixel má oranžovou barvu)
- podiobrázek o velikosti 1×1 pixel (pixel je čistě zelený)
- obrázek o velikosti 400×300 pixelů s rámcem umístěným v levém horním rohu
- obrázek o velikosti 400×300 pixelů s rámcem umístěným v jeho středu
- podiobrázek o velikosti 1×1 pixel s průhledným pixelem
- maxiobrázek o velikosti 65535×65535 pixelů na pouhých 35 bytech!
8. Obsah závěrečné části – animace, prokládání a LZW komprimace
V poslední části tohoto seriálu si ukážeme, jakým způsobem se v GIFu pracuje s animacemi, jaký význam má prokládání obrázků a také si podrobněji popíšeme použitou LZW komprimaci.