Il formato "FLIC"

di Alessandro Scotti


Il formato "FLIC" è stato inventato da Jim Kent per l'Autodesk Animator, ed in poco tempo è diventato praticamente uno standard per quanto riguarda le animazioni generate su PC. Nonostante oggi ci siano formati senz'altro più evoluti ed in grado di supportare anche una colonna sonora che accompagni l'animazione, come per esempio l'AVI di Windows o il MPEG, il formato FLIC è tutt'altro che superato.
C'è da dire che i FLIC non sono in diretta competizione con gli AVI, i MPEG o altri formati del genere, perché questi ultimi sono stati sviluppati per trattare immagini reali digitalizzate, provenienti per lo più da telecamera o videoregistratore, mentre i FLIC contengono di solito immagini rese direttamente su PC con programmi di rendering o raytracing, oppure disegnate a mano.
Il grande vantaggio dei FLIC nei confronti dei formati sopra citati è senz'altro la sua semplicità. Scrivere un player di FLIC, infatti, è tutt'altro che difficile, e magari può servire ad arricchire la presentazione dei propri programmi. Tra l'altro questa pratica è abbastanza diffusa anche in prodotti "commerciali", per esempio andando a guardare in mezzo ai "resource" di Ultima Underworld II si scopre che la bellissima introduzione è appunto un FLIC.

Vediamo allora quali sono le idee alla base dei FLIC. Un'animazione fluida e plausibile richiede che lo schermo sia aggiornato circa 25/30 volte al secondo (anche se si hanno risultati apprezzabili già con 12/15 frame per secondo). Su PC, purtroppo, gli accessi alla memoria video sono molto lenti, anche sulle SVGA più veloci siamo ancora molto lontani dai tempi di accesso delle normali RAM. Inoltre è la CPU che deve scrivere in memoria video, e nel fare questo ruba spesso tempo ad altri compiti. Paradossalmente, il modo migliore per usare una VGA è... usarla il meno possibile!
Dovendo fare i conti anche con la lunghezza del file che contiene l'animazione, non c'è quindi da stupirsi se nei FLIC un frame (un "fotogramma") non viene quasi mai memorizzato per intero: solo le parti differenti dal frame precedente vengono compresse e salvate nel file. Si ottiene così il duplice scopo di ridurre la lunghezza del file e aumentare la velocità di esecuzione, minimizzando gli accessi a video.

Un file di tipo FLIC è strutturato nel modo seguente:

Figura 1.1 - Struttura di un file FLIC

Header
			Prefix chunk
				Settings chunk
				Position chunk
			Frame 1 chunk
				Postage stamp
				Color data
				Pixel data
			Frame 2 chunk
				Color data
				Pixel data
			...
			Ring frame chunk
				Color data
				Pixel data

Come si vede, è una gerarchia di oggetti chiamati "chunk". Un chunk contiene la propria dimensione, il proprio tipo e i dati necessari per interpretarlo. In questo modo è possibile aggiungere nuovi chunk ed estendere il formato senza che programmi già esistenti ne risentano, perché questi ultimi semplicemente ignorano i chunk che non conoscono.

Andiamo ora ad analizzare come è organizzato l'header ed i vari tipi di chunk.

L'header


L'header principale si trova naturalmente all'inizio del file ed è lungo 128 byte. La sua struttura e' riportata in figura 1.2: dato che quest'ultima è differente per i .FLI ed i .FLC, la cosa migliore da fare nel caso si incontri un .FLI è modificarne l'header rendendolo compatibile con quello di un file .FLC.

Figura 1.2 - Header

Offset		Dim. (bytes)	Nome		Descrizione
  0 (00h)	4		size		lunghezza dell'intero file
  4 (04h)       	2               	type		tipo di file
  6 (06h)	2		frames		numero di frame presenti
  8 (08h)	2		width		larghezza dell'immagine in pixel
10 (0Ah)	2		height		altezza dell'immagine in pixel
12 (0Ch)	2		depth		numero di bit per pixel, vale sempre 8
14 (0Eh)	2		flags		flags, vale di solito 0003h
16 (10h)	4		speed		intervallo di tempo tra un frame e l'altro
20 (14h)	2		reserved
22 (16h)	4		created	data e ora di creazione del file
26 (1Ah)	4		creator		(*)
30 (1Eh)	4		updated	data e ora dell'ultima modifica al file
34 (22h)	4		updater	(*)
38 (26h)	2		aspectx	(**)
40 (28h)	2		aspecty	(**)
42 (2Ah)	38		reserved
80 (50h)	4		oframe1	offset del primo frame
84 (54h)	4		oframe2	offset del secondo frame
88 (58h)	40		reserved

(*) I campi "creator" e "updater" vengono riempiti da Animator Pro con il numero di serie del programma che ha creato il file e quello del programma che l'ha modificato per ultimo. Per i .FLC creati da altre applicazioni questo campo contiene 0.
(**) I campi "aspectx" e "aspecty" descrivono lo schermo su cui è stato creata l'animazione. Su quello schermo un rettangolo largo "aspectx" pixel e alto "aspecty" pixel appare quadrato.

Vediamo in dettaglio alcuni dei campi più importanti:

Type

Specifica il formato del file: AF11h per i FLI e AF12h per gli FLC. Nel caso dei FLI, i campi dell'header dall'offset 20 (14h) in poi non hanno significato.

Frames

Il numero di frame presenti. In effetti il file contiene un ulteriore frame, detto ring frame, che collega l'ultimo frame con il secondo. Il ring frame è necessario perché spesso il primo frame è molto lungo e lento da visualizzare, dato che contiene l'intera palette di colori e le informazioni necessarie a costruire la prima schermata per intero. Dopo l'ultimo frame, quindi, invece di ritornare al primo si legge il ring frame e si salta poi al secondo frame.

Speed

E' l'intervallo di tempo che deve trascorrere tra un frame e l'altro, cioè la velocità di riproduzione. Nei FLI questa velocità viene espressa, chissà perché, in 70-esimi di secondo, mentre negli FLC è in millisecondi.

OFrame1 e OFrame2

Contengono gli offset rispettivamente del primo e del secondo frame, relativi all'inizio del file (solo per gli FLC). L'offset del primo frame è necessario perché dopo l'header si possono trovare dei dati aggiuntivi (il prefix chunk) inseriti dal programma che ha creato l'animazione, mentre l'offset del secondo frame, da usare insieme al ring frame, può essere calcolato a partire dall'offset del primo.

I frame


Un frame chunk contiene tutte le informazioni necessarie a visualizzare un singolo "fotogramma" dell'animazione. La sua struttura è molto semplice:

Figura 1.3 - Frame chunk

Offset		Dim. (bytes)	Nome		Descrizione
  0 (00h)	4		length		lunghezza del frame
  4 (04h)	2		type		tipo di frame, sempre F1FAh
  6 (06h)	2		chunks		numero di chunks contenuti nel frame
  8 (08h)	8		reserved	
16 (10h)	length-16	...		dati 

In effetti la struttura descritta sopra non è altro che un "contenitore" per i dati veri e propri, che vengono memorizzati dentro ulteriori blocchi che chiameremo semplicemente chunk. Un frame può contenere uno o più chunk, ed ad ogni chunk è associata una particolare azione, come pulire lo schermo, aggiornare la palette e così via.

I chunk


Un chunk è composto dal breve header descritto in figura 1.4, immediatamente seguito dai dati ad esso necessari.

Figura 1.4 - Chunk

Offset		Dim. (bytes)	Nome		Descrizione
  0 (00h)	4		length		lunghezza del chunk
  4 (04h)	2		type		tipo di chunk
  6 (06h)	length-6	...		dati

Ci sono diversi tipi di chunk, e se il programma che sta visualizzando un FLIC incontra un chunk di tipo sconosciuto non deve interrompere l'esecuzione, ma "saltare" tutto il chunk, di cui conosce la lunghezza, e passare al prossimo.
Ed ecco finalmente l'elenco dei chunk che si possono incontrare.

Type 4 (COLOR_256: palette a 256 livelli)

I dati sono organizzati in blocchi (packet). La prima word dopo l'header contiene il numero di packet nel chunk. Un packet ha la seguente struttura:

  	byte	indice del colore da aggiornare, relativo all'indice corrente 
	byte	numero di colori da aggiornare
	...	3 byte di informazione (R,G,B) per ogni colore da aggiornare

All'inizio l'indice del colore vale 0. Per ogni packet presente si aggiorna innanzitutto l'indice sommandogli il primo byte del packet. Il numero di colori si trova nel secondo byte: il valore 0 indica che ci sono 256 colori. Per ogni colore infine si hanno tre byte che forniscono il valore delle componenti: rosso, verde e blu rispettivamente. Ogni componente può andare da 0 a 255, quindi bisogna dividere il valore per 4 prima di poterlo inserire nella palette della VGA, che dispone di soli 6 bit per componente.

Type 7 (DELTA_FLC)

E' il tipo di chunk usato nei file FLC per descrivere le differenze rispetto al frame precedente, compresse con il metodo RLE (word-oriented).
La prima word dopo l'header contiene il numero di linee da aggiornare. Ogni linea inizia con una word (control word) il cui significato dipende dal valore dei due bit più significativi, come indicato in figura 1.5.

Figura 1.5 - DELTA_FLC control word

Bit 15 Bit 14 Descrizione
0 0 La word contiene il numero di packet presenti nella linea ed è subito seguita dal primo packet. Il numero di packet può essere 0
1 0 Il byte meno significativo contiene il colore dell'ultimo pixel della linea nelle immagini con larghezza dispari. Questa word è sempre seguita da una word che contiene il numero di packet.
1 1 Questa word indica il numero di linee da saltare, pari al suo valore assoluto, ed è seguita da un'altra control word.

Ogni packet è composto da un byte, che indica il numero di pixel da saltare nella linea corrente, e da un byte che contiene il tipo e la dimensione del packet (packet type). Ci sono tre possibilità:
1) packet type = 0
il packet non contiene ulteriori informazioni (si usa per saltare più pixel di quanti ne permette un solo byte);
2) packet type > 0
il valore di packet type indica il numero di word che devono essere copiate dal packet nell'immagine;
3) packet type < 0
il packet contiene una word che deve essere replicata nell'immagine un
numero di volte pari al valore assoluto di packet type.

Type 11 (COLOR_64: palette a 64 livelli)

E' identico al tipo 4 (COLOR_256), tranne per il fatto che i valori per le componenti R, G, B vanno da 0 a 63 (esattamente come se li aspetta una VGA).

Type 12 (DELTA_FLI)

Contiene le differenze rispetto al frame precedente, compresse con il metodo RLE. La prima word dopo l'header indica la posizione della prima linea da aggiornare, contata a partire dall'alto, mentre la seconda word contiene il numero di linee da aggiornare. Ogni linea inizia con un byte che indica il numero di packet presenti, ed ogni packet inizia con due byte: il primo indica il numero di pixel da saltare, relativo alla posizione corrente, il secondo contiene il tipo e la dimensione del packet (packet type). A seconda del segno di questo byte si hanno diverse possibilità:
1) packet type = 0
il packet non contiene ulteriori informazioni (si usa per saltare più pixel di quanti ne permette un solo byte);
2) packet type > 0
il packet contiene un certo numero di pixel (byte) da copiare nell'immagine, il numero di byte da copiare è dato proprio dal valore di packet type;
3) packet type < 0
il packet contiene un solo byte che deve essere copiato nell'immagine un numero di volte pari al valore assoluto di packet type.

Type 13 (BLACK)

Non ci sono dati, tutto il frame viene riempito con il colore 0.

Type 15 (BYTE_RUN)

Contiene tutte le informazioni necessarie a visualizzare un intero frame, compresse con il metodo RLE. Di solito questo chunk si trova nel primo frame, ed è molto simile al tipo DELTA_FLI. Ognuna delle height linee che compongono l'immagine è composta da un byte che indica il numero di packet presenti seguito dai packet stessi. Per immagini molto larghe il numero di packet può superare 255, quindi è meglio ignorare questa informazione e basarsi sul numero di pixel decompressi: dopo aver visualizzato width pixel la linea è finita e si può passare a quella seguente.
Ogni packet è preceduto da un byte che ne indica il tipo (packet type). Questo campo è identico a quello visto nel chunk 12 (DELTA_FLI), tranne per il segno, che è invertito. Quindi si hanno i tre casi seguenti:
1) packet type = 0
il packet non contiene ulteriori informazioni (non dovrebbero esserci packet di questo tipo);
2) packet type > 0
il packet contiene un solo byte che deve essere copiato nell'immagine un numero di volte pari al valore di packet type;
3) packet type > 0
il packet contiene un certo numero di pixel (byte) da copiare nell'immagine, il numero di byte da copiare è dato dal valore assoluto di packet type.

Type 16 (COPY)

Questo tipo di chunk contiene un intero frame in forma non compressa, e viene usato quando non è possibile comprimere il frame. Per ottenere la lunghezza del blocco di dati basta moltiplicare la larghezza dell'immagine per l'altezza.

Type 18 (PSTAMP)

Contiene una immagine miniaturizzata del primo frame e può essere tranquillamente ignorato. Su questo tipo di chunk non ho trovato informazioni "ufficiali". Da quello che ho potuto vedere l'header è seguito da tre word: la prima indica l'altezza dell'immagine, la seconda la larghezza e la terza il numero di sotto-chunk presenti. In pratica nei (pochi) file a mia disposizione ho trovato sempre una immagine di 100x63 con un solo sotto-chunk di tipo 15 (BYTE_RUN). Questo chunk inizia immediatamente dopo le tre word, ha il suo header standard con lunghezza e tipo, e funziona esattamente come descritto in precedenza, tranne per la differente dimensione dell'immagine. Purtroppo ho solo FLC di dimensioni 320x200, comunque dato che 100:63 è circa uguale a 320:200 si può supporre che il PSTAMP venga costruito fissando a 100 la larghezza e calcolando l'altezza in modo tale da lasciare inalterato l'aspect ratio.

Conclusioni


Come abbiamo visto i FLIC sono strutturati in modo ragionevolmente semplice, e la loro struttura permette di espandere facilmente il formato per tenerlo al passo con i tempi, basta aggiungere nuovi tipi di chunk.
Per fornire un esempio concreto di come leggere ed interpretare un FLIC, allegati a questo articolo ci sono diversi file: FLIP.EXE è un piccolo player di FLIC che funziona con una normale VGA in modalità 320x200 a 256 colori, e FLIP.PAS, scritto in Turbo Pascal e Assembler, è il sorgente di FLIP.EXE. Infine CFLIC.ZIP contiene sorgenti ed eseguibile per un player di FLIC scritto in C proprio da Jim Kent.


Nota: questo articolo è stato pubblicato su SBDI (Sound Blaster Digest Italia, una rivista elettronica) ma non ricordo quando. Il file porta la data di Marzo 1994.

Copyright (c) 2005 Alessandro Scotti. All rights reserved.

 Home :: Computer Programming

Site Map