Sei sulla pagina 1di 19

CAPITOLO 4

Formati grafici

- 4.1 Introduzione
Fino a questo momento abbiamo parlato di come realizzare semplici demo che visualizzino sullo
schermo immagini calcolate dal nostro programma.
Per creare qualcosa di più complesso e allo stesso tempo gradevole, sorge il problema della
manipolazione delle immagini.
Esse di solito sono acquisite dal mondo esterno tramite dei speciali dispositivi chiamati scanner, che
prendono i dati sotto forma di luce e li convertono in dati elaborabili dal computer, oppure possono
essere più direttamente create mediante appositi programmi di disegno, fotoritocco, ambienti di
sviluppo tridimensionali, ecc…(ad esempio Paint Shop Pro, Corel Draw, 3D Studio e simili).
A questo punto dopo aver acquisito ed elaborato le informazioni, esse dovranno essere salvate su
memoria di massa come file, al fine di poter essere riutilizzate.
Le diverse strutturazioni di questi file sono dette formati grafici, e corrispondono ai veri e propri
tracciati record. Ogni immagine ha bisogno, per essere rappresentata, come detto nei capitoli
precedenti, di una palette, atta a contenere informazioni riguardo i colori utilizzati, e delle
informazioni che costituiscono l'immagine stessa. All'inizio di ogni file di rappresentazione
dell'immagine, c'è una testata (HEADER) che ne identifica il formato e descrive le caratteristiche
generali dell'immagine, specificando, se necessario, il tipo di elaborazione a cui è stata sottoposta.
Essa può essere compressa o meno, può andare da 1 a 32 bit di rappresentazione del pixel e
memorizzata nell’ordine originale dei bytes che la costituiscono o meno e con differenti risoluzioni.
E’ per questo che esistono molti standard di rappresentazione dell'immagine, ognuno adattato per
scopi diversi (minima occupazione di memoria di massa, con o senza perdita di dettagli, alta o bassa
risoluzione e velocità di elaborazione). Nel nostro trattato ne esporremmo dettagliatamente solo
alcuni e ne accenneremmo altri al fine di fornire al lettore una conoscenza generale dell’argomento
ed una certa destrezza nell’utilizzo dei formati più utilizzati nella programmazione di demo.

- 4.2 Il formato grafico RAW


Il metodo più semplice per archiviare informazioni grafiche su memoria di massa, consiste nella
creazione di files, atti a contenere rispettivamente, i dati relativi all’immagine ed alla palette, così
come dovrebbero essere contenuti in memoria video.
Il RAW (formato grezzo), non rappresenta uno standard vero e proprio, bensì, viene personalizzato
dagli stessi programmatori; nel nostro caso, lavorando ad una risoluzione di 320x200=64000 pixel a
256 colori, abbiamo pensato di definire due sottoformati. Il primo, come precedentemente detto, si
avvale di un file di 64.000 bytes contenente le informazioni relative all’immagine (con estensione
.RAW), ed un ulteriore file di supporto rappresentante la palette grafica (con dimensione 768 bytes
ed estensione .PAL). Il secondo consiste in una fusione dei due file sopra citati, in maniera di
compattare le informazioni e velocizzarne quindi l’accesso a livello hardware, ottenendo un file di
dimensioni 64.768 bytes ed estensione .RAW. Adottando questa tecnica, si riscontrano vantaggi a
livello temporale, poiché è sufficiente estrarre le informazioni dagli archivi e memorizzarle nel
buffer video, dopo aver opportunamente settato la palette, senza bisogno di ulteriori elaborazioni.
Di contro, con tale metodo, si provoca un’eccessiva occupazione della memoria di massa e delle
difficoltà nella portatilità in ambienti differenti, essendo un tipo di organizzazione non
standardizzato.
Il seguente listato RAW Saver, potrebbe essere molto utile se implementato come programma TSR
(Terminate and Stay Resident), ovvero un processo residente in memoria, attivabile mediante
pressione multipla di tasti (Es. Ctrl+S).

1
#include”vga.h”
#include<io.h>
#include<fcntl.h>
#include<sys\stat.h>
#include<mem.h>
#include<conio.h>
/***************************************************************************/
/* RAW SAVER, salva il contenuto della memoria video su un file passato */
/* come argomento dalla riga di comando */
/***************************************************************************/
int main(int argc, char *argv[]) {
PALETTE pal[256]; /* Array per memorizzazione palette grafica */
int fd; /* Descrittore del file (file handle) */

if(argc<2) { /* Input controllato dalla linea di comando */


printf(“\nSintassi : %s nomefile.raw”,argv[0]);
exit(-1);
}
Set_Vga_Mode(); /* Settaggio della modalità grafica */
/******************************************************/
/* ISTRUZIONI PER IL TRACCIAMENTO DI UN IMMAGINE */
/******************************************************/

/* Creazione file su disco e relativo controllo */


if((fd=open(argv[1],O_RDWR|O_CREAT|O_BINARY))==-1) {
printf(“\nErrore nella creazione del file %s”,argv[1]);
exit(-2);
}
Get_Palette(pal); /* Acquisizione palette grafica */
write(fd,pal,768); /* Memorizzazione su file della palette */
write(fd,VGAPTR,64000); /* Memorizzazione su file dei valori dei pixel */
close(fd); /* Chiusura file */
Set_Text_Mode(); /* Settaggio della modalità testuale */
}

/***************************************************************************/
/* RAW LOADER, visualizza un’immagine .RAW */
/***************************************************************************/
int main(int argc, char *argv[]) {
PALETTE pal[256]; /* Array per memorizzazione palette grafica */
int fd; /* Descrittore del file (file handle) */

if(argc<2) { /* Input controllato dalla linea di comando */


printf(“\nSintassi : %s nomefile.raw”,argv[0]);
exit(-1);
}
/* Apertura file su disco e relativo controllo */
if((fd=open(argv[1],O_RDWR|O_BINARY))==-1) {
printf(“\nErrore nella creazione del file %s”,argv[1]);
exit(-2);
}
Set_Vga_Mode() /* Settaggio della modalità grafica */
read(fd,pal,768); /* Lettura della palette dal file */
Set_Palette(pal); /* Acquisizione palette grafica */
read(fd,VGAPTR,64000); /* Visualizzazione immagine */
close(fd); /* Chiusura file */
getch(); /* Attende la pressione di un tasto */
Set_Text_Mode(); /* Settaggio della modalità testuale */
}

2
- 4.3 Il formato grafico PCX
Ci siamo resi conto, di come sia possibile immagazzinare un’immagine su file, e di come
effettuarne la relativa visualizzazione.
Le figure che utilizzeremo, molto spesso, oltre che essere utilizzate come sfondo o intermezzo tra
eventi delle nostre demo, rappresenteranno veri e propri contenitori di sprites , cioè le entità oggetto
di animazione del nostro video (personaggi dei videogames, oggetti in movimento, le scritte che
scorrono nei text-scroller, ecc..), dei quali ci occuperemo in dettaglio nei prossimi capitoli.
Non ci accontenteremo di utilizzare il formato grezzo (.RAW), amplieremo le nostre conoscenze
introducendo lo standard PCX, e le varie routines che svilupperemo in seguito, faranno riferimento
direttamente ad esso.

Tale scelta è giustificata dai seguenti motivi :

1) larghissima diffusione a livello internazionale, rappresenta un formato riconosciuto da tutti i


programmi di disegno elettronico e non, ed è ampliamente adottato dai demo’s groups;
2) unisce un accettabile livello di compressione, ad una semplice implementazione dell’algoritmo
di codifica/decodifica;
3) ci permette di affidare il lavoro del disegno ad un grafico specializzato, senza doverlo tradurre
in altri formati.

Tale standard della Zsoft supporta quasi tutti i tipi di immagini che esistono attualmente:
- 1 bit, colore monocromatico;
- 2 bit, 4 colori;
- 4 bit, 16 colori;
- 8 bit, 256 colori;
- 15 e 16 bit, 32K e 64K colori;
- 24 bit, immagini true-color.

Quello che andremo ad esaminare è il formato a 256 colori, cioè quello che siamo in grado di
visualizzare e manipolare.

Il formato grafico PCX è costituito sequenzialmente, da una testata di 128 bytes, dai dati
dell'immagine codificati con una compressione del tipo RLE (Run Lenght Encoding) ed infine dalle
informazioni relative alla palette grafica in formato RAW ovvero non compresse.

HEADER
L’header contiene le informazioni relative all’immagine, di carattere generico.
128 BYTES

RLE Qui vi sono i dati compressi con la codifica RLE.


n bytes

In coda al file è posta la palette, memorizzata RAW, non compressa, secondo la


Palette notazione RGB.
768 bytes

3
Segue il tracciato record della testata (o HEADER) del file PCX :

typedef struct {
char Manufacturer; /* identificatore tipo file PCX deve essere 10 */
char Version; /* versione del file 0, 2 o 5 */
char Encoding; /* tipo di compressione deve essere 1 (RLE) */
char BitsPerPixel; /* numero di bits per ogni pixel */
unsigned Xmin, Ymin, Xmax, Ymax; /* permettono di calcolare la dimensione
dell'immagine */
unsigned HDpi, VDpi; /* risoluzione orizzontale e verticale */
char ColorMap[48]; /* mappa dei colori per la scheda EGA */
char Reserved; /* riservato */
char NumberOfPlanes; /* numero di bit planes */
unsigned BytesPerLine; /* byte per ogni linea */
unsigned PaletteInfo; /* informazioni sulla palette */
unsigned HscreenSize, VscreenSize;/*dimensione orizzontale e verticale dello schermo*/
char Filler[54]; /* riempitore per sviluppi futuri */
} PCX_Header; /* Testata di un file PCX */

Passiamo ora a descrivere ogni campo in maniera più dettagliata :

Manufacturer : è un byte che serve per identificare il file PCX. Il suo valore deve essere
necessariamente uguale a 10 (decimale) altrimenti il file non è un formato PCX;

Version : è un byte che indica la versione del file. Esso può assumere i valori 0, 2, 5.
0 è la prima versione del file che può rappresentare solo immagini con colore a un
bit e quindi monocromatiche.
2 è la versione successiva e può rappresentare immagini a 4 bit -16 colori.
5 è l’ultima versione del file e supporta tutti i formati superiori o uguali a 8 bit;

Encoding : è un byte che specifica il tipo di codifica dell’immagine. Esso deve essere 1 (RLE);

BitsPerPixel : questo campo precisa con quanti bits è codificato un colore. Esso può assumere
valori 1,2,4,8,16,24;

Xmin, Ymin, Xmax, Ymax : questi campi identificano la dimensione dell’immagine in pixel
mediante semplici calcoli :
LARGHEZZA = Xmax – Xmin + 1;
ALTEZZA = Ymax – Ymin + 1;

HDpi, Vdpi : questi campi contengono i valori della risoluzione orizzontale e verticale
dell’immagine in punti per pollice;

ColorMap[48] : array destinato a contenere i dati relativi alla palette per la scheda EGA a 16
colori (3*16=48);

Reserved : campo riservato; dovrebbe essere settato a 0;

NumberOfPlanes : questo campo contiene il numero di piani di bit (bit planes) nell’immagine.
I bit planes sono i banchi di memoria video necessari per la rappresentazione
dell’immagine.
Nel caso che sia un’immagine a 8 bit, il numero di bit planes sarà uguale a 1, in
quanto con il metodo indicizzato del colore abbiamo bisogno di una sola

4
componente che identifica una sfumatura cromatica.
Nel caso che l’immagine sia true color a 24 bit ci saranno 3 bit planes poiché
utilizzando la tecnica di rappresentazione libera del colore, ognuno di essi,
dovrà contenere un tipo di componente RGB. Ad esempio un bit plane conterrà
solo la percentuale di rosso, un altro quella di verde ed infine il restante bit plane
quella di blu;

BytesPerLine : contiene il numero di bytes necessari per determinare una scanline dell’immagine.
E’ sempre un numero pari, ma non è uguale a Xmax - Xmin

PaletteInfo : contiene informazioni sulla palette. Se il relativo valore è 1 significa che la palette
è a colori o in bianco e nero; se contiene il valore 2 allora la palette è in scala di
grigi;

HscreenSize, VscreenSize : dimensione orizzontale e verticale dello schermo;

Filler[54] : vettore utilizzato per il riempimento dei restanti bytes, in modo tale che l’header
occupi 128 bytes; sarà utilizzato probabilmente per sviluppi futuri, dovrebbe essere
settato tutto a zero.

Passiamo ora ad esaminare la struttura dei dati dell’immagine :

Come precedentemente deto, il formato PCX si basa su un semplice algoritmo di compressione


denominato Run Lenght Encoding (RLE), che sfrutta la costanza cromatica dei colori di
un’immagine.
Essendo quest’ultima considerata come un vettore di n elementi, si può immaginare di scandirla
sequenzialmente, dall’inizio fino alla fine, tenendo conto del numero di ripetizioni continue di un
certo colore del disegno preso in esame; contando quante volte il colore x è ripetuto
successivamente, e memorizzando nel file sia il numero k delle ripetizioni, sia il colore x.
Equivale, con un’analogia al linguaggio parlato, a pronunciare: “Ci sono 20 colori n° 1“, invece di
ripetere per 20 volte : “C’è il colore n° 1“.
Si avranno così una sequenza finita di coppie del tipo <<numero delle ripetizioni, colore>>,
rappresentante, ognuna di esse, una serie di pixel adiacenti aventi tutti lo stesso colore.

Tale codifica, si adatta molto, sia intuitivamente che su basi statistiche, all’applicazione su
immagini generate a mano, non digitalizzate tramite dispositivi di I/O come scanner, per il semplice
motivo che le prime presentano un’alta probabilità di contenere sequenze contigue di colori uguali
(si pensi ad una scritta di un certo colore su di uno sfondo di un altro colore), mentre le altre, ad
esempio una foto, presentano un elevato numero di sfumature di colore adiacenti.
Occorre quindi fare molta attenzione, ad applicare sconsideratamente tale metodologia di
compressione a qualsiasi tipo di modello poiché, in alcuni casi (specialmente dove sono poche le
serie di colori uguali ed adiacenti) è possibile anche aumentare le dimensioni del file compresso
rispetto a quelle originali (nel peggiore dei casi, si pensi ad una immagine in cui ogni pixel è
differente dall’altro: il file compresso avrebbe una dimensione raddoppiata rispetto a quella
originale).

Ritornando alle coppie elaborate dallo RLE, cioè <<numero delle ripetizioni, colore>>, occorre
specificare che la dimensione di ognuno dei due campi, è di 1 byte.
Da notare, a prima vista, che seppure un byte sia sufficiente per referenziare un colore (lavoriamo
nella modalità a 256 colori), il numero massimo delle ripetizioni descrivibili in un passaggio,
sembrano essere 256; inoltre, nel caso in cui un pixel generico dell’immagine si ripeta una volta

5
sola (cioè i pixel ad esso adiacenti siano diversi), vengono sprecati ben 2 bytes per memorizzare
l’informazione di uno.
Per ovviare ai suddetti problemi, la ZSoft, ha ben pensato di raggiungere un compromesso tra lo
spreco di memoria, determinato dall’ultimo problema sopra esposto, ed il numero massimo di
ripetizioni, modificando leggermente il concetto di RLE, secondo le due seguenti regole (da
applicare nella codifica e/o decodifica delle immagini in formato PCX) :

1) se i due bit più significativi del byte appena letto sono entrambi settati ad 1, allora i restanti 6 bit
rappresentano il numero di ripetizioni del byte successivo, che a sua volta rappresenta il colore
da stampare o memorizzare; in parole povere, se tale valore è maggiore od uguale a 192, allora
bisogna sottrarvi 192 per ottenere il numero delle ripetizioni del byte successivo;
2) se i due bit più significativi del byte appena letto non sono entrambi settati ad 1, allora tale
valore viene considerato come un colore di lunghezza unitaria. Ciò significa che tale valore è
inferiore a 192, allora rappresenta direttamente il colore da stampare o memorizzare.

Applicando tali regole, sorge il problema di rappresentare i colori unitari, di indice compreso tra
192 e 255 inclusi, poiché si potrebbero confondere con i contatori delle ripetizioni. Si è così deciso
di rappresentarli come delle “serie particolari di lunghezza 1”; in tal caso si riscontra una perdita nel
ratio di compressione, cioè quando vi è la presenza di solo pixel avente valore compreso in tale
intervallo i byte necessari per memorizzarlo non sono più uno ma due.

Al fine di chiarire le idee al lettore esordiamo con semplice esempio, facente riferimento ad una
sequenza di valori:

[IMMAGINE] …, 24, 24, 24, 24, 24, 5, 13, 13, 251, 13, 195, 195, 1, 1, 1, 1, 1, 1, …

ed ora la relativa codifica PCX:

[CODIFICA] …, 197, 24, 5, 194, 13, 193, 251, 13, 194, 195, 198, 1, …

Dall’esempio si nota che tale tecnica è abbastanza efficiente, e permette di risparmiare circa il 30%
rispetto alla dimensione originale del file (eccezioni escluse, ovviamente).

Passiamo ora ad analizzare l’ultimo blocco del file PCX :

In tale posizione è allocata la palette, che non è memorizzata nella sua forma originale (RGB), ma
ogni valore dell'intensità delle componenti cromatiche è contenuta nei sei bit più significativi
(MSB) di ogni elemento, ovvero shiftata di due posizioni verso sinistra.
All’atto del caricamento della palette, quindi, bisognerà shiftarla di due posizioni verso destra per
ottenerne la giusta combinazione di colori nella notazione RGB standard.

SCHEMATIZZAZIONE GRAFICA DELLA PALETTE PCX :

PALETTE PALETTE
PCX NORMALE

Rpcx Rpcx>>2
Gpcx Gpcx>>2
Bpcx Bpcx>>2
6
A questo punto non rimane che implementare la codifica in linguaggio C della routine di apertura
del formato grafico PCX ed i relativi controlli delle caratteristiche principali :
/***************************************************************/
/* Questa funzione ritorna la grandezza in bytes della memoria */
/* necessaria per contenere l'immagine PCX */
/* INPUT : Nome del file .pcx */
/* Puntatore ad una variabile di tipo PCX_Header */
/* OUTPUT : Valore di ritorno=grandezza dell'immagine in bytes */
/* Nella variabile puntata da pcx_header, ritorna i */
/* dati che identificano il file PCX */
/* In caso di errore o di formato non supportato ritorna NULL */
/***************************************************************/
long Get_Pcx_Size(char *filename,PCX_Header *pcx_header)
{
int pcxfile; /* File Handle */
int pcx_xsize,pcx_ysize; /* Dimensioni orizzontali e verticali dell’immagine */

/* Apertura, controllo e lettura dell’header del file PCX */


if ((pcxfile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(pcxfile,pcx_header,sizeof(PCX_Header));

if (pcx_header->Manufacturer != 10) return(NULL); /* Controllo se il file è un PCX */


if (pcx_header->Encoding != 1) return(NULL); /* Controllo se la codifica è RLE */
if (pcx_header->NumberOfPlanes > 1) return(NULL); /* Controllo dei bit planes */
if (pcx_header->BitsPerPixel != 8) return(NULL); /* Controllo immagine ad 8 bit */

pcx_xsize = (pcx_header->Xmax - pcx_header->Xmin) + 1; /* Calcolo dimensioni reali */


pcx_ysize = (pcx_header->Ymax - pcx_header->Ymin) + 1;
return((long)pcx_xsize * pcx_ysize); /* Ritorna la dimensione in bytes dei dati */
}

/**************************************************************************/
/* Questa funzione carica in memoria l'immagine PCX e la relativa palette */
/* INPUT : Nome del file .pcx */
/* Puntatore all'area di memoria destinata a contenere l'immagine */
/* Puntatore all'area di memoria destinata a contenere la palette */
/* OUTPUT: Dati dell'immagine decodificati (in formato RAW) */
/* La palette dell’immagine in formato RGB standard */
/* In caso di errore o di formato non supportato ritorna NULL */
/**************************************************************************/
int Load_Pcx (char *filename,BYTE huge *image,PALETTE *pal)
{ /********************************/
#define get(data,pos) \ /* Questa macro permette la */
data=buffer[pos]; \ /* lettura di un dato ignorando */
pos++; \ /* la sua collocazione su file, */
if(pos>=byteletti) { \ /* come se fosse su un array, */
byteletti=read(pcxfile,buffer,size_of_buffer); \ /* velocizzandone gli accessi. */
pos=0; \ /********************************/
}

BYTE data, num_reps; /* Variabili per la lettura delle coppie di valori RLE */
long int count = 0; /* Contatore dati letti dal file (numero rip. + dati) */
BYTE *buffer; /* Area di memoria per accelerare gli accessi sul file */
int pcxfile,i; /* File Handle e contatore generico */
unsigned int byteletti, /* Bytes letti ad ogni accesso al file (max 65000) */
buffer_count, /* Contatore interno al buffer su memoria RAM */
size_of_buffer; /* Dimensioni del buffer in bytes */
long int free_memory; /* Memoria RAM libera nel sistema */
int pcx_xsize,pcx_ysize;/* Dimensioni orizz. e vert. in pixel dell’immagine*/
long int pcx_size; /* Grandezza effettiva in bytes dell’immagine */
PCX_Header pcx_header; /* Testata del file PCX */

/* Apertura, controllo e lettura dell’header del file PCX */


if ((pcxfile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(pcxfile,&pcx_header,sizeof(PCX_Header));

/* Controllo delle caratteristiche del formato grafico */

7
if (pcx_header.Manufacturer != 10) return(NULL);
if (pcx_header.Encoding != 1) return(NULL);
if (pcx_header.NumberOfPlanes > 1) return(NULL);
if (pcx_header.BitsPerPixel != 8) return(NULL);

/* Calcolo delle dimensioni */


pcx_xsize = (pcx_header.Xmax - pcx_header.Xmin) + 1;
pcx_ysize = (pcx_header.Ymax - pcx_header.Ymin) + 1;
pcx_size = (long)pcx_xsize * pcx_ysize;

free_memory=(long int)coreleft(); /* Chiamata di sistema : ritorna la memoria libera */

if(free_memory>65000) size_of_buffer=65000; /* Riserva al massimo blocchi di */


else size_of_buffer=free_memory; /* 65.000 bytes alla volta */

/* Allocazione dinamica della memoria RAM di sistema */


if((buffer=(BYTE *)malloc(size_of_buffer))==NULL) return(NULL);

byteletti=read(pcxfile,buffer,size_of_buffer); /* Prima lettura fuori ciclo */


buffer_count=0; /* Reset del contatore del buffer precedentemente allocato */
do {
get(data,buffer_count); /* Lettura di un dato dal buffer */
if((data & 0xc0) == 0xc0) { /* Ciclo di decodifica RLE */
num_reps = data & 0x3f; /* Si tratta di un contatore di ripetizioni */
get(data,buffer_count); /* allora legge il successivo colore e lo assegna */
setmem(image+count,num_reps,data); /* alle rispettive locazioni di memoria */
count+=num_reps; /* Incrementa il contatore */
}
else { /* Si tratta di un dato puro, quindi lo pone in memoria */
image[count]=data;
count++; /* ed incrementa il contatore */
}
}
while(count<pcx_size); /* Termina con la lettura dell’ultimo byte */
free(buffer); /* Restituisce al S.O. la memoria allocata */
lseek(pcxfile,-768L,SEEK_END); /* Posizionamento sulla palette nel file PCX */
read(pcxfile,pal,768); /* e successiva lettura */
for(i=0;i<256;i++){ /* Conversione in notazione RGB standard */
pal[i].r>>=2;
pal[i].g>>=2;
pal[i].b>>=2;
}
close(pcxfile); /* Chiusura file */
return(1); /* Valore di ritorno O.K. */
}

void main(int argc, char *argv[]){

BYTE *picture; /* Si presuppone che l’immagine sia in formato VGA std. */


PALETTE palette[256];

if(argc!=2){ printf("\nSintassi : %s nomefile.pcx",argv[0]); exit(-1);}

if((picture=(BYTE *)malloc((size_t)64000))==NULL){
printf(“\nErrore nell’allocazione della memoria”);
exit(-1);
}
if (Load_Pcx (argv[1], picture, palette) == NULL){
printf ("\nImpossibile trovare il file %s", argv[1]);
exit (-1);
}
Set_Vga_Mode(); /* Inizializza la modalità grafica */
Set_Palette(palette); /* Settaggio della palette grafica */
memcpy(VGAPTR,picture,64000); /* Visualizzazione immagine a video */
free(picture); /* Rilascio memoria allocata dinamicamente */
getch(); /* Attende la pressione di un tasto */
Set_Text_Mode(); /* Setta la modalità testuale */
}

8
- 4.4 Il formato grafico BMP
In questo paragrafo descriveremo le caratteristiche principali di tale formato grafico ampiamente
utilizzato negli ambienti e sistemi operativi WINDOWS e simili e dai programmi applicativi ad essi
integrati, ai quali deve la sua fama, a tal punto da essere considerato il più comune standard di
archiviazione delle immagini.
Bisogna precisare che BMP è l’acronimo di BitMaP, ovvero mappa o campo di bit in analogia
all’organizzazione della memoria video.
I files di questo tipo, si distinguono in due categorie: quelli compressi e quelli non compressi.
Ci occuperemo solamente di questi ultimi, che per semplicità, e motivati dal fatto di non fare uso di
questo formato nelle nostre demo, si presteranno in maniera più immediata alla comprensione del
loro meccanismo di memorizzazione.

La struttura di un file BMP è riassumibile nel seguente modo :

HEADER

PALETTE
Esempio riferito
ad un'immagine
di grandezza
320 x 200 pixel
DATI IMMAGINE
...
...
...

.............................................................................. 639

0 1 2 3 ............................................. 318 319

Analizziamo in dettaglio l’header del file bitmap :

la testata è distinguibile in due tracciati record chiamati BMPHEAD e BITMAPINFOHEADER.


Il BMPHEAD contiene le informazioni su tipo e dimensione del file e l’offset dal quale inizia
l’immagine, mentre il BITMAPINFOHEADER contiene altre informazioni descrittive, come
altezza e larghezza in pixel dell’immagine, il numero di bitplanes, il numero di bit per pixel, lo stato
di compressione, il numero di colori usati ed altre informazioni meno importanti.

Segue la descrizione completa dei tracciati record dell’header sopra descritto :

/**********************************/
/* Testata di un file BitMap .bmp */
/**********************************/
typedef struct{
char type[2]; /* Identificatore tipo file deve essere "BM" */
long size; /* Dimensioni del file in bytes */
int reserved; /* Riservato = 0 */
int reserved1; /* Riservato = 0 */
long offbits; /* Offset in bytes all'inizio dei dati */
} BMPHEAD;

9
/*************************************/
/* Informazioni nel file BitMap .bmp */
/*************************************/
typedef struct {
long size; /* Bytes necessari per contenere un BITMAPINFOHEADER */
long width; /* larghezza dell'immagine in pixel */
long height; /* altezza dell'immagine in pixel */
int planes; /* numero di bit planes */
int bitcount; /* bits per ogni pixel */
long compression; /* tipo di compressione */
long sizeimage; /* dimensione dell'immagine */
long xpelspermeter; /* risoluzione orizzontale */
long ypelspermeter; /* risoluzione verticale */
long clrused; /* numero di colori usati */
long clrimportant; /* colori importanti */
} BITMAPINFOHEADER;

/******************************************/
/* Testata completa di un file bitmap .bmp */
/******************************************/
typedef struct {
BMPHEAD head;
BITMAPINFOHEADER info;
} BMP_Header;

Passiamo ora ad analizzare il formato della palette :

è registrata nel file come un vettore di strutture, composte da quattro bytes, di lunghezza variabile, a
seconda del numero di colori complessivamente utilizzati :
I primi tre bytes per le componenti RGB, memorizzate non secondo l’ordine convenzionale, bensì
nella notazione BGR e, come per il formato PCX, ognuna di esse risulta shiftata di due posizioni
verso sinistra.
Infine, il quarto byte, è riservato per future modifiche.

Segue la struttura della palette in formato BMP :

/******************************************************/
/* Formato del colore utilizzato nei file bitmap .bmp */
/******************************************************/
typedef struct{
BYTE b; /* intensità blu */
BYTE g; /* intensità verde */
BYTE r; /* intensità rosso */
BYTE reserved; /* byte riservato */
} BMP_RGB;

Terminiamo la descrizione del formato, spiegando, come si nota dalla precedente figura, che i dati
dell’immagine sono memorizzati come una successione di linee orizzontali della stessa lunghezza, a
partire dall’angolo in basso a sinistra dell’immagine fino a quello in alto a sinistra.

Occorre quindi riorganizzare questi dati nel formato RAW direttamente visualizzabile.

Per completezza, occorre specificare che per riportare l’immagine in formato RAW è sufficiente
scambiare tra di loro le linee simmetriche rispetto ad un ipotetico asse orizzontale passante per il
centro dell’immagine.

Chiariremo ulteriormente tale concetto, con una schematizzazione grafica che segue :
10
IMMAGINE BMP
10 63 18 98 100130 47 43 78 10 24 230 69 23
23 67 4 0 56 56 21 90 128 64 33 19 19 40

ASSE DI
SIMMETRIA
88 239108 56 190130 12 12 70 121224 0 1 23
13 17 169 8 104 10 7 243 66 19 34 34 23 41

CONVERSIONE BMP RAW


13 17 169 8 104 10 7 243 66 19 34 34 23 41
88 239108 56 190130 12 12 70 121224 0 1 23

ASSE DI
SIMMETRIA
23 67 4 0 56 56 21 90 128 64 33 19 19 40
10 63 18 98 100130 47 43 78 10 24 230 69 23

Il seguente listato rappresenta un caricatore di immagini in formato BMP :

/***************************************************************/
/* Questa funzione ritorna la grandezza in bytes della memoria */
/* necessaria per contenere l'immagine BMP */
/* INPUT : Nome del file .bmp */
/* Puntatore ad una variabile di tipo BMP_Header */
/* OUTPUT : Grandezza dell'immagine */
/* Nella variabile puntata da bmp_header, ritorna i */
/* dati che identificano il file BMP */
/* In caso di errore o di formato non supportato ritorna NULL */
/***************************************************************/
long Get_Bmp_Size(char *filename,BMP_Header *bmp_header)
{
int bmpfile; /* File handle */

/* Apertura file BMP */


if ((bmpfile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(bmpfile,bmp_header,sizeof(BMP_Header));

/* Controllo caratteristiche file BMP */


if (bmp_header->head.type[0]!='B'||
bmp_header->head.type[1]!='M') return(NULL);
if (bmp_header->info.planes > 1) return(NULL);
if (bmp_header->info.bitcount != 8) return(NULL);
if (bmp_header->info.compression != 0) return(NULL);

return(bmp_header->info.sizeimage);
}

11
/******************************************************************************/
/* Questa funzione carica in memoria l'immagine BMP e la sua palette */
/* INPUT : Nome del file .bmp */
/* Puntatore all'area di memoria destinata a contenere l'immagine */
/* Puntatore all'area di memoria destinata a contenere la palette */
/* OUTPUT: Dati dell'immagine decodificati (in formato RAW) */
/* La palette dell’immagine in formato RGB standard */
/* In caso di errore o di formato non supportato ritorna NULL */
/******************************************************************************/

int Load_Bmp (char *filename,BYTE huge *image,PALETTE *pal)


{
long int count = 0; /* Contatore dati letti dal file */
BYTE *buffer; /* Area di memoria per accelerare gli accessi sul file */
int bmpfile; /* File handle */
unsigned int byteletti; /* Numero di bytes acquisiti ad ogni lettura dal file */
BMP_Header bmp_header; /* Testata del file BMP */
BMP_RGB palette[256]; /* Palette dell’immagine in formato BMP */
int bmp_xsize,bmp_ysize; /* Dimensioni in pixel dell’immagine */
long int i; /* Contatore generico */

/* Apertura file BMP */


if ((bmpfile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(bmpfile,&bmp_header,sizeof(BMP_Header));

/* Controllo caratteristiche file BMP */


if (bmp_header.head.type[0]!='B'||
bmp_header.head.type[1]!='M') return(NULL);
if (bmp_header.info.planes > 1) return(NULL);
if (bmp_header.info.bitcount != 8) return(NULL);
if (bmp_header.info.compression != 0) return(NULL);

bmp_xsize=bmp_header.info.width; /* Calcolo delle dimensioni dell’immagine */


bmp_ysize=bmp_header.info.height;

read(bmpfile,palette,sizeof(BMP_RGB)*bmp_header.info.clrused); /* Lettura palette */


for(i=0;i<bmp_header.info.clrused;i++){ /* Conversione della palette */
pal[i].r=palette[i].r>>2;
pal[i].g=palette[i].g>>2;
pal[i].b=palette[i].b>>2;;
}

count=0;
while(!eof(bmpfile)) { /* Lettura dei dati dell’immagine */
byteletti=read(bmpfile,image+count,65000);
image+=byteletti;
}

if((buffer=(BYTE *)malloc(bmp_xsize))==NULL) return(NULL);

for(i=0;i<bmp_ysize/2;i++) { /* Conversione dati in formato RAW */


memcpy(buffer,image+i*bmp_xsize,bmp_xsize);
memcpy(image+i*bmp_xsize,image+(bmp_ysize-1-i)*bmp_xsize,bmp_xsize);
memcpy(image+(bmp_ysize-1-i)*bmp_xsize,buffer,bmp_xsize);
}

free(buffer); /* Restituzione della memoria allocata dinamicamente al S.O. */


close(bmpfile); /* Chiusura file */
return(1); /* Valore di ritorno O.K. */
}

La metodologia d’uso delle funzioni è uguale a quella usata per i file PCX.

12
- 4.5 Il formato grafico TGA
Tratteremo infine il formato grafico TGA (che sta per TarGA), che per via della sua semplice
organizzazione ha avuto un ampio utilizzo.
Si tratta di uno standard che supporta anche la compressione delle immagini, basata sull’algoritmo
RLE (Run Length Encoding), anche se ci limiteremo all’utilizzo della sua variante non compressa,
al fine di focalizzare l’attenzione sulla struttura dati.
Tale organizzazione è simile allo standard BMP (BitMaP), dettagliatamente descritto nel precedente
paragrafo; infatti è costituito da una testata posta all’inizio del file, alla quale segue la palette dei
colori, ed infine i dati relativi all’immagine vera e propria.

Riportiamo la schematizzazione grafica di tale organizzazione :

HEADER

PALETTE

Esempio riferito
ad un'immagine
di grandezza
320 x 200 pixel
DATI IMMAGINE
...
...
...

.............................................................................. 639

0 1 2 3 ............................................. 318 319

Segue la definizione dell’header del file TGA :


/*********************************/
/* Testata di un file Targa .tga */
/*********************************/
typedef struct {
char primi[2]; /* non documentato */
char type; /* 1 o 2 = non compressi, 9 o 10 = RLE coded */
char array[9]; /* non documentato */
int width; /* larghezza in pixel dell'immagine */
int height; /* altezza in pixel dell'immagine */
char BitsPerPixel; /* bits per ogni pixel */
char xxx; /* non documentato */
} TGA_Header;

La palette è organizzata, come nei BMP, nell’ordine non convenzionale BGR, ed ogni componente
è shiftata di due posizioni verso sinistra; segue la definizione del relativo tracciato record :
/*****************************************************/
/* Formato del colore utilizzato nei file Targa .tga */
/*****************************************************/
typedef struct{
BYTE b; /* intensità blu */
BYTE g; /* intensità verde */
BYTE r; /* intensità rosso */
} TGA_RGB;

13
Per quanto riguarda la modalità di memorizzazione dei restanti dati, è come se l’immagine fosse
ruotata di 180° rispetto al suo centro, proprio come accade nei BMP, e già ampiamente discusso nel
paragrafo precedente.

Passiamo quindi alle routines di decodifica :

/*****************************************************************/
/* Questa funzione ritorna la grandezza in bytes della memoria */
/* necessaria per contenere l'immagine tga */
/* INPUT : Nome del file .tga */
/* Puntatore ad una variabile di tipo TGA_Header */
/* OUTPUT : Grandezza dell'immagine */
/* Nella variabile puntata da tga_header, ritorna i */
/* dati che identificano il file tga */
/* In caso di errore o di formato non supportato ritorna NULL */
/*****************************************************************/
long Get_Tga_Size(char *filename,TGA_Header *tga_header)
{
int tgafile; /* File handle */

/* Apertura del file TGA */


if ((tgafile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(tgafile,tga_header,sizeof(TGA_Header));

/* Controllo caratteristiche file TGA */


if (tga_header->type!=1) return(NULL);
if (tga_header->BitsPerPixel != 8) return(NULL);

return((long)tga_header->width*tga_header->height); /* Valore di ritorno */


}

/*****************************************************************************/
/* Questa funzione carica in memoria l'immagine TGA e la sua palette */
/* INPUT : Nome del file .tga */
/* Puntatore all'area di memoria destinata a contenere l'immagine */
/* Puntatore all'area di memoria destinata a contenere la palette */
/* OUTPUT : Dati dell'immagine decodificati (in formato RAW) */
/* La palette dell’immagine in formato RGB standard */
/* In caso di errore o di formato non supportato ritorna NULL */
/*****************************************************************************/
int Load_Tga (char *filename,BYTE huge *image,PALETTE *pal)
{
long int count = 0; /* Contatore dati letti dal file */
BYTE *buffer; /* Area di memoria per accelerare gli accessi sul file */
int tgafile; /* File handle */
unsigned int byteletti; /* Numero di bytes acquisiti ad ogni lettura dal file */
TGA_Header tga_header; /* Testata del file TGA */
TGA_RGB palette[256]; /* Palette dell’immagine in formato TGA */
int tga_xsize,tga_ysize; /* Dimensioni in pixel dell’immagine */
long int i; /* Contatore generico */

/* Apertura del file TGA */


if ((tgafile=open(filename,O_RDONLY|O_BINARY)) == -1) return(NULL);
else read(tgafile,&tga_header,sizeof(TGA_Header));

/* Controllo caratteristiche file TGA */


if (tga_header.type!=1) return(NULL);
if (tga_header.BitsPerPixel != 8) return(NULL);

tga_xsize=tga_header.width; /* Calcolo delle dimensioni dell’immagine */


tga_ysize=tga_header.height;

read(tgafile,palette,sizeof(TGA_RGB)*256); /* Lettura palette */


for(i=0;i<256;i++){ /* e successiva conversione */
pal[i].r=palette[i].r>>2;
pal[i].g=palette[i].g>>2;
pal[i].b=palette[i].b>>2;;
}

14
count=0;
while(!eof(tgafile)) { /* Lettura dei dati dell’immagine */
byteletti=read(tgafile,image+count,65000);
image+=byteletti;
}
if((buffer=(BYTE *)malloc(tga_xsize))==NULL) return(NULL);

for(i=0;i<tga_ysize/2;i++) { /* Conversione dati in formato RAW */


memcpy(buffer,image+i*tga_xsize,tga_xsize);
memcpy(image+i*tga_xsize,image+(tga_ysize-1-i)*tga_xsize,tga_xsize);
memcpy(image+(tga_ysize-1-i)*tga_xsize,buffer,tga_xsize);
}
free(buffer); /* Restituzione della memoria allocata dinamicamente al S.O. */
close(tgafile); /* Chiusura del file */
return(1); /* Valore di ritorno O.K. */
}

- 4.6 Ulteriori formati grafici

Tutti gli standard precedentemente descritti, rappresentano un compromesso tra semplice


comprensione e velocità dell’algoritmo di decodifica, ed i demo programmers prediligono tra di essi
il PCX, il quale permette di ottenere notevoli risparmi di memoria, ottenuti mediante una agevole e
rapida metodologia di compressione.
Molto spesso, nell’ambito del trattamento di immagini più sofisticate, come in alta risoluzione o in
true-color (24 bit), o nelle trasmissioni telematiche di immagini e nell’elaborazione di filmati, le
organizzazioni descritte perdono la loro efficacia, si ricorre quindi ad algoritmi più sofisticati come,
per citarne alcuni tra i più famosi, il GIF (Graphic Interchange Format), standard per eccellenza
delle immagini telematiche, largamente diffuso nei siti Internet, oppure il JPEG, finalizzato ad una
notevole compressione, adottando tecniche di approssimazione dell’immagine con perdite più o
meno evidenti di dettaglio.
Ultimamente nell’ambito dei filmati digitali, ha preso piede la codifica MPEG, implementata
addirittura a livello hardware nelle schede video di ultima generazione, oppure emulata, per motivi
di compatibilità tramite software.
Persino nel mondo delle animazioni digitali esiste una moltitudine di modelli, basti citare il .FLI di
3DStudio dell’Autodesk, il .AVI, di Video for Windows della Microsoft, il .ANM, .DL, ecc.,
utilizzati in applicativi meno noti.
Molto spesso viene utilizzata una tecnica di compressione temporale, basata sulla caratteristica di
alcuni filmati che non variano in maniera significativa col passare del tempo, come un oggetto in
movimento sovrapposto ad uno sfondo fisso (ne è un esempio eclatante il cronista di un
telegiornale, che durante la trasmissione gesticolerà, muoverà la testa, sposterà qualche foglio sulla
sua scrivania, senza andare a modificare nessun elemento dello studio in cui si trova).
Si considera quindi un singolo fotogramma dell’immagine, che viene nominato Key-Frame
(fotogramma chiave), scelto in maniera tale da risultare molto significativo nel senso che le
variazioni temporali su di esso devono risultare minime (come la posizione frontale del cronista);
ciò che viene archiviato, rappresenta, quindi, esclusivamente le modifiche da apportare a tale
immagine di riferimento, ottenendo un notevole risparmio di memoria ed un ingente incremento
della fluidità di animazione.
Solamente se si verificano durante la digitalizzazione, notevoli differenze rispetto al Key-Frame,
quest’ultimo viene sostituito con l’ultimo fotogramma ripreso, e si procede come precedentemente
detto fino al termine del filmato.
Una ulteriore tecnica, consiste nel modificare la chiave dopo prefissati intervalli di tempo,
ottenendo, a differenza dell’altro metodo, una più grezza approssimazione.
Concludiamo l’argomento accennando l’esistenza della compressione frattale, l’ultima generazione
tra i meccanismi di codifica, basata essenzialmente sulla geometria frattale, la quale permette di
ottenere rapporti di compressione (ratio) di addirittura 100 a 1, con una trascurabile perdita di
dettaglio.
15
- 4.7 Ottimizzazione dell’accesso alle immagini utilizzate nelle demo, sicurezza
e protezione di esse

Ora che siamo in grado di visualizzare un’immagine codificata con lo standard PCX (N.B. tale
discorso è estendibile a qualsiasi formato grafico), desideriamo far sì che il nostro prodotto finale,
cioè la nostra demo, non possa essere manipolata da qualsiasi utente.
Molto probabilmente, vi sarà capitato di visionare dei dimostrativi, costituiti da un unico file
eseguibile e totalmente privi di file grafici di supporto; è proprio questa tecnica, della quale
scopriremo il meccanismo, che permetterà ai nostri programmi di ottenere un elevato livello di
sicurezza e protezione dei dati, nonché di eliminare quei lassi di tempo, in cui questi ultimi vengono
caricati durante l’esecuzione del nostro dimostrativo.
Per implementare una demo di questo tipo, bisogna ricorrere al collegamento dei dati esterni con il
file eseguibile, al momento del linkaggio; ciò è reso possibile, convertendo il nostro file grafico in
un header file (con estensione .h), che verrà incluso nel sorgente mediante la direttiva al
preprocessore C : #include“nomefile.h”.
Dovremo quindi costruire una utility in grado di, preso in input il nome del file grafico PCX, fornire
in output un file con estensione .h, contenente due array: uno destinato a contenere i dati
dell’immagine, che nel caso di una risoluzione 320 x 200 a 256 colori avrà dimensione 64000
bytes, un altro di 768 elementi, equivalente alla palette dei colori.

I passi da seguire nell’implementazione dell’algoritmo sono i seguenti :

1 – INPUT FILE.PCX E FILE.H (dalla linea di comando);


2 – DECODIFICA FILE PCX IN MEMORIA RAM;
3 – CODIFICA DEI DATI DA FORMATO BINARIO A CODICE ASCII;
4 – SALVATAGGIO SU HEADER FILE DELL’ELABORAZIONE CON RELATIVA
FORMATTAZIONE DEL TESTO.

Prima di passare alla codifica, occorre precisare che agendo con questa tecnica, le dimensioni del
file eseguibile vengono ingigantite, ciò nonostante viene spesso utilizzata per motivi estetici,
poiché permette di raccogliere in un unico file codice, grafica, testo, musica e qualsiasi altro dato.

Utilizzando il Turbo C della Borland, occorre impostare il modello di memoria a HUGE, in


maniera tale da avere a disposizione 1 Mb per il codice e dati statici > 64Kb, e l’allineamento della
memoria a BYTE, altrimenti si hanno dei problemi nella lettura da file mediante delle strutture
(palette).

16
Segue la codifica in linguaggio C dell’utility denominata PCX_TO_H :

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<alloc.h>
#include"vga.h" /* Contiene le routines per l’utilizzo della scheda video */
#include"formati.h" /* Contiene le routines per la manipolazione dei formati grafici */

void main (int argc, char *argv[])


{
char *file_pcx, *file_h,
nome_img[] = "image", /* Nomi di default degli array */
nome_pal[] = "pal",
*img_initial_string,
*pal_initial_string,
ending_string[] = "\n};\n", /* Caratteri d’appoggio */
cifra[4],
r[3],g[3],b[3],
cr[] = "\n",
parentesi_ap[] = "{",
parentesi_ch[] = "}",
virgola[] = ", ",
line_count = 0;

BYTE huge *image;


PALETTE pal[256];
PCX_Header head;

FILE *out;
unsigned long int index,
size_image; /* Dimensioni dell’immagine in bytes */

/* Controllo dell'input dalla linea di comando */


if (argc!=3){
printf ("\nUso: %s [file.pcx] [file.h]\n",argv[0]);
exit (1);
}

file_pcx = argv[1];
file_h = argv[2];

/* Allocazione della memoria necessaria per contenere l'immagine PCX */


size_image=Get_Pcx_Size(file_pcx,&head);
if((image=(BYTE huge *)farmalloc(size_image))==NULL){
printf("Memoria insufficiente…");
exit(-1);
}
/* Caricamento in memoria dell'immagine PCX */
if((Load_Pcx(file_pcx, image, pal)) == NULL){
printf ("\nImpossibile trovare il file %s", file_pcx);
exit (-1);
}
/* Creazione dell'header file */
if((out=fopen (file_h, "wb"))==NULL){
printf("\nImpossibile creare l'header file %s",file_h);
exit(-1);
}

/* Inizio elaborazione immagine */


printf ("\nInizio elaborazione dei dati del file %s...", file_pcx);

/* Scriviamo nome e tipo e definiamo le dimensioni */


fprintf (out,“#define SIZE_IMAGE %lu\n“,size_image);
img_initial_string = (char *) malloc (30);
strcpy (img_initial_string, "char ");
strcat (img_initial_string, nome_img);
strcat (img_initial_string, " [] = {\n");
fwrite (img_initial_string, strlen (img_initial_string), 1, out);

17
for (index=0; index<size_image; index++)
{
/* Conversione binario ASCII */
itoa (image[index], cifra, 10);

/* Scriviamo nel file */


fwrite (cifra, strlen (cifra), 1, out);

/* Se siamo in fondo non si deve scrivere la virgola */


if (index < (size_image - 1) ) fwrite(virgola, strlen(virgola), 1, out);

/* Controlla la lunghezza della riga ed al termine di essa inserisce \n */


line_count += strlen (cifra);
if (line_count > 75)
{
fwrite (cr, strlen (cr), 1, out);
line_count = 0;
}
}
fwrite (ending_string, strlen (ending_string), 1, out);
printf ("\nElaborazione terminata.");

/* Inizio elaborazione palette */


printf ("\nInizio elaborazione della palette del file %s...", file_pcx);

/*** Scriviamo nome e tipo ***/


pal_initial_string = (char *) malloc (52);
strcpy (pal_initial_string, "\nPALETTE ");
strcat (pal_initial_string, nome_pal);
strcat (pal_initial_string, " [] = {\n");
fwrite (pal_initial_string, strlen (pal_initial_string), 1, out);

line_count = 0;
for (index=0; index<256; index++)
{
/* Lettura e conversione di una terna RGB */
itoa (pal[index].r, r, 10);
itoa (pal[index].g, g, 10);
itoa (pal[index].b, b, 10);

/* Scrittura di una terna RGB in modalit… testuale */


fwrite (parentesi_ap, strlen(parentesi_ap), 1, out);
fwrite (r, strlen(r), 1, out);
fwrite (virgola, strlen(virgola), 1, out);
fwrite (g, strlen(g), 1, out);
fwrite (virgola, strlen(virgola), 1, out);
fwrite (b, strlen(b), 1, out);
fwrite (parentesi_ch, strlen(parentesi_ch), 1, out);

/* In fondo al file la virgola non deve essere messa */


if (index < 255) fwrite (virgola, strlen(virgola), 1, out);

/* Controlla la lunghezza della riga ed al termine di essa inserisce \n */


line_count += strlen (r) + strlen (g) + strlen (b);
if (line_count > 30)
{
fwrite (cr, strlen (cr), 1, out);
line_count = 0;
}
}

/* Parentesi finale */
fwrite (ending_string, strlen (ending_string), 1, out);
printf ("Elaborazione palette ultimata.");

fclose (out);
}

18
Segue un esempio di utilizzo di tale utility :

/***************************************************************************************/
/* Questo programma visualizza un’immagine 320x200 a 256 colori precedentemente */
/* elaborata con l’utility PCX_TO_H. */
/***************************************************************************************/

#include<conio.h> /* Include la funzione getch() */


#include”vga.h” /* La nostra libreria per la gestione della VGA */
#include”immagine.h” /* L’immagine e la palette elaborati con la precedente utility */

void main(void){
Set_Vga_Mode(); /* Inizializzazione modalità grafica */
Set_Palette(pal); /* Modifica palette dei colori */
memcpy(VGAPTR,image,SIZE_IMAGE); /* Per ora si suppone che l’immagine sia 320x200 */
/* in seguito implementeremo routines anche per */
/* immagini con diverse dimensioni. */
getch(); /* Attende la pressione di un tasto */
Set_Text_Mode(); /* Ritorno alla modalità testuale */
}

19