Hola. Mirá, yo programo con Dev-C++, así que supongo que el compilador que uso es el de ANSI C++ estándar.
Casualmente tuve que hacer lectura de archivos BMP e hice una class para resolver la cuestión.
En primer lugar, necesitás la documentación de cómo es la estructura de un archivo BMP. Eso lo podés encontrar en la siguiente página:
http://atlc.sourceforge.net/bmp.html
Te cuento que yo solamente necesité hacer reconocimiento de archivos blanco y negro monocromo, o sea que solamente hice lectura bit a bit, y un bit 1 significa PIXEL BLANCO y un bit 0 significa PIXEL NEGRO.
Hay tres cosas a tener en cuenta:
1) Los BMPs son archivos con una estructura específica y estandarizada. Los primeros bytes (que son muchos) se usan para dar información general del formato de la imagen, resolución, tamaño, paleta de colores, etc.
En la segunda y última parte del BMP se coloca la lista de pixels. El modo en que deben interpretarse dichos pixels está especificado en la parte de formato.
2) Lo más común es que los pixels se especifiquen uno a uno. En tal caso, la imagen se descompone en líneas de un mismo ancho. El ancho de la línea se debe ''redondear'' a un DWORD, esto quiere decir que, si al terminarse la línea, los pixels usados en el BMP no son un múltiplo exacto de un DWORD (4 Bytes), entonces se rellena con 0's lo que falta para completar el DWORD.
3) También es lo más común que las líneas se almacenen en orden inverso, es decir, que la primer línea de la imagen figure al final del archivo. O sea que la imagen te queda patas para arriba.
Acá te paso mi programita, que es muy básico, y solamente sirve para leer BMPs monocromos.
Como asumí que mis archivos de entrada eran monocromos, no hice verificaciones al respecto, y solamente usé los primeros 26 bytes de la sección de formato del BMP. El resto lo ignoré olímpicamente, y salté directamente a la lectura de datos.
Para que las cosas estén mejor hechas, tendrás que agregarle la capacidad de leer archivos con paletas de colores, etc.
El formato en tales casos es muy preciso, así que no creo que tengas problemas, solamente debes tener en cuenta que la interpretación de 1 bit = 1 pixel, solamente es válida en el caso monocromo.
Otra cosa más que noté en la práctica, es que la información está guardada en paquetes de DWORDs, y resultó que los Bytes me quedaban ''al revés'' de lo que me parecía lógico, así que tuve que intercalar un método make_swap.
Espero que mi código te sirva de inspiración al menos, para notar qué tipos de inconvenientes hay que resolver para leer un BMP.
Con colores la cosa será más interesante, buena suerte.
Código:
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <cstring>
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////////
// CLASS BMPimage
//////////////////////////////////////////////////////////////////////////////
// La clase BMPimage sólo reconoce archivos BMP blanco y negro de 1 bit
#define Bits_in_a_BYTE 8
#define DWORD 4 // Cantidad de BYTES que ocupa un DWORD
#define Reading_Pack Bits_in_a_BYTE*DWORD
#define BYTE_base 0x100L // == 256: Base numérica que se obtiene al
// representar números con una base igual al
// total de números representables con 1 BYTE
// Potencias 0, 1, 2, 3, de BYTE_base
#define B0 1L
#define B1 B0*BYTE_base
#define B2 B1*BYTE_base
#define B3 B2*BYTE_base
#define MAX_PARAMS 0x001A
#define BMP_Identifier 0x0000L
#define BMP_FileSize 0x0002L
#define BMP_ResLaterUse 0x0006L
#define BMP_DataOffset 0x000aL
#define BMP_HeaderSize 0x000eL
#define BMP_PixelsWidth 0x0012L
#define BMP_PixelsHeight 0x0016L
#define MASK (0x1UL << (Reading_Pack - 1))
// Lo importante en la siguiente definición es que INTEGER sea un
// entero de 4 BYTES. Si en su sistema el tipo INT no tiene 4 BYTES,
// utilice otro, como LONG INT:
#define INTEGER unsigned int
class BMPimage {
public:
BMPimage(char *filename);
~BMPimage();
void PrintBitMap(const char On, const char Off) const;
protected:
char *fname;
ifstream f;
int width, height;
vector<bool> bitmap;
};
BMPimage::BMPimage(char *filename)
{
char parameters[MAX_PARAMS];
union {
char buffer[DWORD];
unsigned char byte[DWORD];
INTEGER bits;
void make_swap(void) {
unsigned char x;
x = byte[0]; byte[0] = byte[3]; byte[3] = x;
x = byte[1]; byte[1] = byte[2]; byte[2] = x;
}
unsigned long int count(void) {
return B3*byte[3]+B2*byte[2]+B1*byte[1]+B0*byte[0];
}
} u;
unsigned long begdata, enddata, width_inside_file;
fname = filename;
f.open(fname, ios::in | ios::binary);
f.read(parameters, sizeof(parameters));
strncpy(u.buffer,parameters+BMP_PixelsWidth,DWORD);
width = u.count();
strncpy(u.buffer,parameters+BMP_PixelsHeight,DWORD);
height = u.count();
strncpy(u.buffer,parameters+BMP_DataOffset,DWORD);
begdata = u.count();
strncpy(u.buffer,parameters+BMP_FileSize,DWORD);
enddata = u.count();
width_inside_file = (enddata - begdata) / height;
for(int i = 1; i <= height; i++)
{
// Las líneas en el archivo BMP están guardadas de abajo hacia arriba,
// luego conviene hacer una lectura hacia atrás, desde el fin del archivo
f.seekg(enddata - width_inside_file*i);
// Leer una línea
for(long int k = width; k>0; k -= Reading_Pack)
{
f.read(u.buffer, DWORD);
// Los 4 Bytes leídos deben reordenarse
// porque el formato BMP originalmente los coloca invertidos
u.make_swap();
for (int j=0; j<((k>=Reading_Pack)? Reading_Pack: k); j++)
bitmap.push_back((u.bits << j) & MASK);
}
}
f.close();
}
BMPimage::~BMPimage()
{
bitmap.clear();
fname = NULL;
width = 0;
height = 0;
}
void BMPimage::PrintBitMap(const char On, const char Off) const
{
unsigned long offset = 0;
for (int i = 0; i < height; cout << endl, i++)
for (int j = 0; j < width; j++, offset++)
cout << (bitmap[offset]? On: Off);
}
//////////////////////////////////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
cout << "MASK == " << hex << MASK << endl;
BMPimage dibujillo("./dibujo.bmp");
dibujillo.PrintBitMap(' ','#');
system("PAUSE");
return 0;
}
Te convendría experimentar este código con imágenes monocromas que tengan menos de 80 pixels de ancho, para que el resultado te salga bien en la pantalla de MS-DOS.
Una vez que entiendas cómo funciona la clase BMPimage, podrás usar el vector de pixels para tus propósitos.
Un problema que no pude resolver es el de forzar al compilador a darme un tipo entero de 4 BYTES, sin importar cuántos BYTES ocupen los enteros en cada implementación.
¿Hay alguna forma de asegurar un entero de 4 Bytes usando las directivas de compilación #if, #elif?
No pude hacerlo porque no me deja usar el operador sizeof() en las expresiones que pongo en #if.
Si eso se arregla, en vez de especificar en forma explícita que INTEGER es un INT, podría usar algo como esto (que el compilador no me permite no sé por qué):
Código:
#if (sizeof(int) == DWORD)
#define INTEGER unsigned int
#elif (sizeof(long int) == DWORD)
#define INTEGER unsigned long
#else
#error Compile Error: Necesito sizeof(INTEGER) == DWORD (== 4 BYTES)
#endif