semmixkolor i percepcja

Blok obrazu ($2C)

Właściwy obraz to trzy części: 10-bajtowy deskryptor, opcjonalna paleta lokalna i skompresowane piksele. W pliku może być wiele bloków obrazu: tak powstają animacje i obrazy składane z kilku kawałków.

Deskryptor obrazu

BajtyPolePrzykład
0separator 2C (",")2C
1-2położenie lewej krawędzi na ekranie logicznym00 00 = 0
3-4położenie górnej krawędzi00 00 = 0
5-6szerokość obrazu04 00 = 4
7-8wysokość obrazu02 00 = 2
9flagi00

Obraz nie musi wypełniać całego ekranu logicznego. Animacje często zapisują w kolejnych klatkach tylko prostokąt, który się zmienił, z odpowiednim położeniem. To znacznie zmniejsza plik.

Bajt flag

bit 7 jest paleta lokalna bit 6 obraz z przeplotem bit 5 paleta lokalna posortowana bity 4-3 zarezerwowane (0) bity 2-0 N: paleta lokalna ma 2^(N+1) kolorów przykład: 00 = 00000000 bez palety lokalnej (używa globalnej), bez przeplotu

Przy ustawionym bicie 6 wiersze idą w kolejności czterech przebiegów, opisanej w artykule o przeplocie.

Paleta lokalna

Jeśli bit 7 jest ustawiony, zaraz za deskryptorem jest paleta lokalna: 3 · 2^(N+1) bajtów, zbudowana jak globalna. Obowiązuje tylko dla tego obrazu. Dzięki temu każda klatka animacji może mieć własne 256 kolorów, a cały plik więcej niż 256.

Dane obrazu

02 minimalna długość kodu LZW 05 04 12 30 13 05 podblok: 5 bajtów danych 00 terminator

Minimalna długość kodu to liczba bitów potrzebna na numer koloru (2 dla palet 2- i 4-kolorowych, 3 dla 8 kolorów, aż do 8 dla 256). Kody LZW zaczynają się od jednego bitu więcej (zob. LZW w GIF).

Dalej idą podbloki danych, po najwyżej 255 bajtów, i terminator 00 (zob. ogólna budowa). Przed dekompresją podbloki skleja się w jeden ciąg bajtów. Granice podbloków nie mają nic wspólnego z granicami kodów: kod może zaczynać się w jednym podbloku i kończyć w następnym.

Po rozkodowaniu dane to po prostu indeksy kolorów, wiersz po wierszu, od lewej do prawej:

04 12 30 13 05 → 0 0 1 1 0 3 3 1 → wiersz 0: 0 0 1 1 wiersz 1: 0 3 3 1
function readImage(b, pos) {                       // pos wskazuje bajt 2C
  const u16 = (i) => b[i] | (b[i + 1] << 8);
  const desc = { left: u16(pos + 1), top: u16(pos + 3), w: u16(pos + 5), h: u16(pos + 7) };
  const flags = b[pos + 9];
  pos += 10;
  if (flags & 0x80) pos += 3 * (2 << (flags & 7));   // pomiń paletę lokalną
  const minCodeSize = b[pos++];
  const data = [];
  for (let n = b[pos++]; n !== 0; n = b[pos++]) {    // sklej podbloki
    data.push(...b.subarray(pos, pos + n));
    pos += n;
  }
  return { ...desc, interlaced: (flags & 0x40) !== 0, minCodeSize, data, next: pos };
}