semmixkolor i percepcja

Kolejność bloków

GIF pozwala na dość swobodne ułożenie bloków, ale nie na dowolne. Specyfikacja opisuje kolejność jak gramatykę: z czego składa się plik, a z czego jego części.

Gramatyka

plik GIF = nagłówek ekran [paleta globalna] dane* trailer dane = blok graficzny | blok specjalny blok graficzny = [sterowanie grafiką $F9] blok rysujący blok rysujący = obraz $2C [paleta lokalna] dane LZW | tekst graficzny $01 blok specjalny = rozszerzenie aplikacji $FF | komentarz $FE * dowolna liczba powtórzeń, także zero [] element opcjonalny

Zasady, które z tego wynikają

Przykładowy plik

nagłówek GIF89a ekran 4 × 2, paleta globalna 4 kolory paleta 12 bajtów blok specjalny komentarz "semmix.pl" blok graficzny $F9 + obraz $2C trailer 3B

Bajty są rozpisane we wstępie.

Animacja

Animowany GIF to po prostu wiele bloków graficznych po kolei, każdy z własnym $F9 z opóźnieniem:

GIF89a ekran paleta globalna $FF NETSCAPE2.0, pętla w nieskończoność $F9 opóźnienie 10, usuwanie 1 $2C klatka 1 (cały ekran) $F9 opóźnienie 10, usuwanie 1 $2C klatka 2 (tylko zmieniony prostokąt) $F9 opóźnienie 10, usuwanie 1 $2C klatka 3 3B

Szczegóły opóźnienia i usuwania klatek są na stronie sterowanie grafiką, a pętli na stronie rozszerzenie aplikacji.

Pętla dekodera

let pos = 13;                                    // po nagłówku i opisie ekranu
if (b[10] & 0x80) pos += 3 * (2 << (b[10] & 7));  // paleta globalna
let control = null;
while (pos < b.length) {
  const id = b[pos];
  if (id === 0x3B) break;                        // trailer
  if (id === 0x21 && b[pos + 1] === 0xF9) {      // sterowanie grafiką
    control = { flags: b[pos + 3], delay: b[pos + 4] | (b[pos + 5] << 8), transparent: b[pos + 6] };
    pos = skipExtension(b, pos);
  } else if (id === 0x21) {
    pos = skipExtension(b, pos);                 // inne rozszerzenia
  } else if (id === 0x2C) {
    const img = readImage(b, pos);               // zob. blok obrazu
    // ... dekompresja img.data, rysowanie z ustawieniami control
    control = null;
    pos = img.next;
  } else {
    throw new Error('nieznany blok ' + id.toString(16));
  }
}

Funkcje skipExtension i readImage są na stronach bloki rozszerzeń i blok obrazu.