Le immagini bitmap, introdotte nel 1990 con Windows 3.0, organizzano le informazioni all'interno di più strutture dati. Il file inizia con la struttura dati BITMAPFILEHEADER, di 14 byte. All'interno di questa prendono posto alcune importanti informazioni come la dimensione in byte del file, lo scostamento del primo pixel dell'immagine a partire dall'inizio del file e una stringa in ascii con il valore BM (valore decimale 19778, esadecimale 4D42). La successiva struttura dati, BITMAPINFOHEADER, di 40 byte, raccoglie invece altre informazioni, come ad esempio: le dimensioni in pixel dell'immagine, il numero di byte usato per ogni pixel, la risoluzione orizzontale e verticale etc... Qui trovate una descrizione dettagliata delle informazioni che è possibile rintracciare negli header di un file bmp. L'ultima struttura dati è infine la mappa dei pixel: ad ogni pixel corrisponde un colore sotto forma di codice (o indice se l'immagine usa una tavolozza di colori). Ogni codice specifica le componenti cromatiche dei colori primari (rosso, verde e blu) miscelati per ottenere il colore assegnato al pixel!
Possiamo leggere e scrivere queste informazioni (sia gli header che la mappa dei pixel) aprendo il file in modalità binaria e leggendo con fread n byte alla volta, a seconda della struttura dati. E' possibile assegnare i byte letti con fread a delle variabili (header1 e header2) e usare queste informazioni all'interno di un programma. Nel codice che propongo, dopo aver letto le informazioni (utili tra l'altro ad allocare in memoria un array di n char, uno per ogni pixel) eseguo prima con fseek il riposizionamento del puntatore all'inizio della mappa dei pixel (usando lo scostamento rimediato con la variabile header1), successivamente eseguo la lettura dei pixel. Il programma termina con la stampa della mappa dei pixel ma può essere esteso ulteriormente per l'editing dell'immagine (in tal caso si accederà al pixel con fwrite).
Possiamo leggere e scrivere queste informazioni (sia gli header che la mappa dei pixel) aprendo il file in modalità binaria e leggendo con fread n byte alla volta, a seconda della struttura dati. E' possibile assegnare i byte letti con fread a delle variabili (header1 e header2) e usare queste informazioni all'interno di un programma. Nel codice che propongo, dopo aver letto le informazioni (utili tra l'altro ad allocare in memoria un array di n char, uno per ogni pixel) eseguo prima con fseek il riposizionamento del puntatore all'inizio della mappa dei pixel (usando lo scostamento rimediato con la variabile header1), successivamente eseguo la lettura dei pixel. Il programma termina con la stampa della mappa dei pixel ma può essere esteso ulteriormente per l'editing dell'immagine (in tal caso si accederà al pixel con fwrite).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(2) // usa 2 byte per impachettare la struttura, che è di 14 byte (non è un multiplo del byte)!
/* BITMAPFILEHEADER (14 byte) */
typedef struct {
unsigned short int bfType;
unsigned int bfSize;
unsigned short int bfReserved1,bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
#pragma pack()
/* BITMAPINFOHEADER (40 byte) */
typedef struct {
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short int biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
/* struttura per un pixel */
typedef struct {
unsigned char blue;
unsigned char green;
unsigned char red;
} PIXEL;
/* codice (binari e esadecimali) relativi ai file bmp */
#define DECBMPCODE 19778
#define HEXBMPCODE 0x4D42
int main (int argc, char*argv[]) {
FILE *input_file;
BITMAPFILEHEADER header1;
BITMAPINFOHEADER header2;
PIXEL pixel, *image;
int i,j,n_pixel=0;
if (argc==1) {
printf("Specifica il file da usare!\n");
return -1;
}
else {
if ((input_file=fopen(argv[1],"rb+"))!=NULL) {
fread(&header1,sizeof(header1),1,input_file);
if (header1.bfType==DECBMPCODE) {
printf("Header del file %s:\n",argv[1]);
printf(" - Tipo di file: %x\n",header1.bfType);
printf(" - Dimensione del file: %u Kb (%u byte)\n",header1.bfSize/1024,header1.bfSize);
printf(" - Offset al primo bit nella mappa: %u\n",header1.bfOffBits);
fread(&header2,sizeof(header2),1,input_file);
printf("Header delle informazioni:\n");
printf(" - Dimensione del blocco informazione: %u byte\n",header2.biSize);
printf(" - Larghezza dell'immagine: %u pixel\n",header2.biWidth);
printf(" - Altezza dell'immagine: %u pixel\n",header2.biHeight);
/* Calcolo del padding */
int pitch = header2.biWidth * 3;
if (pitch % 4 != 0) pitch += 4 - (pitch % 4);
int padding = pitch - (header2.biWidth * 3);
printf(" - Pitch: %u\n", pitch);
printf(" - Padding: %u\n", padding);
fseek(input_file,header1.bfOffBits,SEEK_SET);
image=(PIXEL*)malloc((header2.biWidth*header2.biHeight)*sizeof(PIXEL));
printf("\nComponenti dei pixel: #pixel(RGB)\n");
for (i=0;i<header2.biHeight;i++) {
for (j=0;j<header2.biWidth;j++) {
fread(&pixel, sizeof(PIXEL),1,input_file);
printf("%2d(%u,%u,%u) ",n_pixel,pixel.red,pixel.green,pixel.blue);
image[n_pixel]=pixel;
n_pixel++;
}
fseek(input_file, padding, SEEK_CUR);
printf("\n");
}
free(image);
}
else {
printf("Il file %s non è un file bmp!\n",argv[1]);
return -1;
}
}
else {
printf("Non è stato possibile aprire il file %s!\n",argv[1]);
return -1;
}
}
return 0;
}
E' possibile verificare il programma con una delle seguenti immagini:


