PDA

Visualizza la versione completa : [C++] Bitmap e specificazione dimensione variabile in numero di byte


RooccoXXI
16-02-2011, 23:00
Ciao a tutti.

Sto provando a creare una classe Bitmap che mi permetta di caricare o di creare file .bmp.

Mi sono letto tutta la pagina di Wikipedia (http://en.wikipedia.org/wiki/BMP_file_format) e ho una vaga idea di come procedere.

Però ho un problema. In C++ come faccio a specificare la dimensione di una variabile come numero di bytes?
In pratica ho bisogno di creare vari campi di lunghezza prestabilita.

Ho proceduto in questo modo:

#ifndef DATATYPE_H
#define DATATYPE_H

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;

typedef struct BITMAPFILETYPE
{
unsigned char magic[2]; // Magic number (identify BMP file)
} bfType;

typedef struct BITMAPFILEHEADER
{
uint32_t bfSize; // Size of BMP file in byte
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bmpOffset; // Offset starting address
} BitmapFileHeader;

typedef struct BITMAPINFOHEADER
{
uint32_t biSize; // Size of this header
int32_t biWidth; // Bitmap whidt in pixels
int32_t biHeight; // Bitmap whidt in pixels
uint16_t biPlanes; // Number of color planes being used
uint16_t biBitCount; // Number of bits per pixel
uint32_t biCompression; // Compression method being used
uint32_t biSizeImage; // Image size
int32_t biXPixelPerMeter; // Horizontal resolution of the image
int32_t biYPixelPerMeter; // Vertical resolutions
uint32_t biClrUsed; // Number of colors in the color palette
uint32_t biClrImportant; // Number of important color used
} BitmapInfoHeader;

typedef struct PIXEL
{
uint8_t B; // Blue color
uint8_t G; // Green color
uint8_t R; // Red color
uint8_t reserved; // Reserved
} Pixel;

#endif

Però la lunghezza degli int o dei char dipende dalla macchina, quindi le variabili che ho creato non hanno una lunghezza fissa, ma sono dipendenti dalla macchina (32, 64, ... bit), giusto? Come faccio a renderle lunghezze fisse?!

MItaly
16-02-2011, 23:18
http://en.wikipedia.org/wiki/Stdint.h

RooccoXXI
16-02-2011, 23:26
Originariamente inviato da MItaly
http://en.wikipedia.org/wiki/Stdint.h

Avevo già visto questa libreria...
Ma non trovo la corrispondente in C++!

MItaly
16-02-2011, 23:44
Il problema è che <cstdint> esisterà nel prossimo standard C++ (il cosiddetto C++0x), anche se ormai tutti i compilatori recenti lo forniscono; VC++ se non erro lo supporta dalla versione 2010, ma anche per le precedenti trovi in rete degli stdint.h già pronti.

In ogni caso, praticamente ogni compilatore fornisce una qualche sintassi per usare i tipi a dimensioni fissate, guarda nella documentazione del tuo compilatore.

RooccoXXI
17-02-2011, 07:39
Originariamente inviato da MItaly
Il problema è che <cstdint> esisterà nel prossimo standard C++ (il cosiddetto C++0x), anche se ormai tutti i compilatori recenti lo forniscono; VC++ se non erro lo supporta dalla versione 2010, ma anche per le precedenti trovi in rete degli stdint.h già pronti.

In ogni caso, praticamente ogni compilatore fornisce una qualche sintassi per usare i tipi a dimensioni fissate, guarda nella documentazione del tuo compilatore.

Io utilizzo GCC sotto Xcode. Ho appena installato l'ultima versione di Xcode, ma ho notato che GCC non viene aggiornato (è fermo alla versione 4.2.1).
Comunque guarderò nella documentazione del mio compilatore. Grazie per le dritte!

KoutaOyamada
17-02-2011, 12:01
lascia tutto cosi e racchiudi le struct fra #pragma pack(1) e #pragma pack()
pochi giorni fa ho fatto una classe analoga che mi permette di lavorare con i file bmp, e ha funzionato semplicemente utilizzando uint 16/32 e #pragma pack

in ogni caso non so se lo sai ma in windows.h sono già fornite tutte le structs per i bitmap già pronte (una volta capita la struttura mi sono riscritto tutta la classe usando quelle giusto per essere sicuro):
http://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/aa930622.aspx
http://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx

non c'è bisogno di ri-diachiararle, basta includere Windows.h
Il piccolo programma di base che avevo fatto prima di scrivere la classe usando le struct di Windows.h: http://pastebin.com/PfMCbER3
gira sia su VC++ che su GCC senza problemi, l'unica cosa è che devi sostituire il fopen_s con il fopen normale se compili su gcc

MItaly
17-02-2011, 12:15
Comunque, per quanto riguarda i tipi a dimensione fissata, nulla che non si possa risolvere con un po' di abuso di metaprogrammazione template e qualche macro:


#ifndef EXACTWIDTH_HPP_INCLUDED
#define EXACTWIDTH_HPP_INCLUDED

#include <limits>

namespace ExactWidthInt
{

namespace Implementation // Implementazione
{

// Tipo di base usato per registrare i tipi di interi disponibili
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_ExactBitWidthInt
{
static const bool Defined=false;
};

#define EXACTWIDTH_BEGINTYPETABLE \
const unsigned int Imp_TableFirstLine=__LINE__;

#define EXACTWIDTH_ADDTYPE(type) \
template<> \
struct Imp_ExactBitWidthInt< \
sizeof(type)*std::numeric_limits<unsigned char>::digits,\
std::numeric_limits<type>::is_signed, \
__LINE__ > \
{ \
typedef type T; \
static const bool Defined=true; \
};

#define EXACTWIDTH_ENDTYPETABLE \
const unsigned int Imp_TableLastLine=__LINE__;

// Tabella dei tipi - si inizia con questa macro...
EXACTWIDTH_BEGINTYPETABLE
// Qui in mezzo si mettono tutti i tipi che si vogliono coinvolgere nella ricerca
// Di base metto solo i tipi standard
EXACTWIDTH_ADDTYPE(signed char)
EXACTWIDTH_ADDTYPE(unsigned char)
EXACTWIDTH_ADDTYPE(signed short int)
EXACTWIDTH_ADDTYPE(unsigned short int)
EXACTWIDTH_ADDTYPE(signed int)
EXACTWIDTH_ADDTYPE(unsigned int)
EXACTWIDTH_ADDTYPE(signed long)
EXACTWIDTH_ADDTYPE(unsigned long)
// ... e si finisce con questa
EXACTWIDTH_ENDTYPETABLE

#undef EXACTWIDTH_ADDTYPE
#undef EXACTWIDTH_BEGINTYPETABLE
#undef EXACTWIDTH_ENDTYPETABLE

// Ricerca ricorsiva del tipo che soddisfa le richieste

// Caso generale (mai richiamato, da solo la signature del template e l'argomento di default)
template<int Size, bool Signed, unsigned int LineNumber=Imp_TableFirstLine, bool Found=Imp_ExactBitWidthInt<Size, Signed, LineNumber>::Defined>
struct Imp_FindExactBitWidthInt;

// Tipo trovato: fornisce effettivamente il tipo richiesto come typedef
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_FindExactBitWidthInt<Size, Signed, LineNumber, true>
{
typedef typename Imp_ExactBitWidthInt<Size, Signed, LineNumber>::T T;
};

// Tipo non trovato: continua la ricerca, passando alla linea successiva
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_FindExactBitWidthInt<Size, Signed, LineNumber, false>
: Imp_FindExactBitWidthInt<Size, Signed, LineNumber+1>
{
};

// Caso base: tipo non trovato, tabella dei tipi finita; non definisce ::T, così
// si ottiene un errore di compilazione
template<int Size, bool Signed>
struct Imp_FindExactBitWidthInt<Size, Signed, Imp_TableLastLine, false>
{
};

// Tutto il giochino della ricerca anche per linea si rende necessario perché
// più tipi possono avere le medesime dimensioni; se così non fosse, basterebbe
// una tabella dei tipi senza numeri di riga e non sarebbe necessaria la ricerca
// ricorsiva

} // namespace Implementation

// Le due classi da usare per davvero:

// Fornisce come typedef ::T il tipo che corrisponde alla dimensione richiesta (in bit)
// con segno o meno a seconda del secondo parametro
template<int Size, bool Signed>
struct ExactBitWidthInt
{
typedef typename Implementation::Imp_FindExactBitWidthInt<Size, Signed>::T T;
};

// Fornisce come typedef ::T il tipo che corrisponde alla dimensione richiesta (in byte)
// con segno o meno a seconda del secondo parametro
template<int Size, bool Signed>
struct ExactByteWidthInt
{
typedef typename ExactBitWidthInt<Size*std::numeric_limits<unsigned char>::digits, Signed>::T T;
};

}

/*
Esempio:

typedef ExactWidthInt::ExactBitWidthInt<32, false>::T myuint32;
typedef ExactWidthInt::ExactByteWidthInt<2, true>::T my2bytesint;

*/
#endif

È venuto un pelo più complicato del previsto (per via del fatto che più tipi possono avere le stesse dimensioni), ma dovrebbe comunque funzionare bene. La cosa divertente è che se il tipo che corrisponda alle richieste (in termini di bit e di signed/unsigned) non è presente tra quelli della tabella si ha un errore di compilazione.

Per farti un esempio, per avere un intero a 32 bit senza segno basta fare


ExactWidthInt::ExactBitWidthInt<32, false>::T var;

Naturalmente come notazione è un po' pesante, per cui potresti voler fare una roba del tipo:


typedef ExactWidthInt::ExactBitWidthInt<32, false>::T UInt32;
typedef ExactWidthInt::ExactBitWidthInt<16, false>::T UInt16;
// ... eccetera

anche per evitare di rischiare l'istanziazione di quella caterva di template ogni volta che vuoi usare una variabile a dimensione fissata. :D

MItaly
17-02-2011, 14:30
... o comunque c'è sempre la boost integer library.
http://www.boost.org/doc/libs/release/libs/integer/index.html

RooccoXXI
17-02-2011, 21:13
Originariamente inviato da KoutaOyamada
lascia tutto cosi e racchiudi le struct fra #pragma pack(1) e #pragma pack()
pochi giorni fa ho fatto una classe analoga che mi permette di lavorare con i file bmp, e ha funzionato semplicemente utilizzando uint 16/32 e #pragma pack

in ogni caso non so se lo sai ma in windows.h sono già fornite tutte le structs per i bitmap già pronte (una volta capita la struttura mi sono riscritto tutta la classe usando quelle giusto per essere sicuro):
http://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/aa930622.aspx
http://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx

non c'è bisogno di ri-diachiararle, basta includere Windows.h
Il piccolo programma di base che avevo fatto prima di scrivere la classe usando le struct di Windows.h: http://pastebin.com/PfMCbER3
gira sia su VC++ che su GCC senza problemi, l'unica cosa è che devi sostituire il fopen_s con il fopen normale se compili su gcc

Lavoro con Mac OS X e occasionalmente anche con Linux, quindi windows.h è esclusa a priori. Poi comunque volevo implementare io la classe da zero! =)

RooccoXXI
17-02-2011, 21:14
Originariamente inviato da MItaly
Comunque, per quanto riguarda i tipi a dimensione fissata, nulla che non si possa risolvere con un po' di abuso di metaprogrammazione template e qualche macro:


#ifndef EXACTWIDTH_HPP_INCLUDED
#define EXACTWIDTH_HPP_INCLUDED

#include <limits>

namespace ExactWidthInt
{

namespace Implementation // Implementazione
{

// Tipo di base usato per registrare i tipi di interi disponibili
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_ExactBitWidthInt
{
static const bool Defined=false;
};

#define EXACTWIDTH_BEGINTYPETABLE \
const unsigned int Imp_TableFirstLine=__LINE__;

#define EXACTWIDTH_ADDTYPE(type) \
template<> \
struct Imp_ExactBitWidthInt< \
sizeof(type)*std::numeric_limits<unsigned char>::digits,\
std::numeric_limits<type>::is_signed, \
__LINE__ > \
{ \
typedef type T; \
static const bool Defined=true; \
};

#define EXACTWIDTH_ENDTYPETABLE \
const unsigned int Imp_TableLastLine=__LINE__;

// Tabella dei tipi - si inizia con questa macro...
EXACTWIDTH_BEGINTYPETABLE
// Qui in mezzo si mettono tutti i tipi che si vogliono coinvolgere nella ricerca
// Di base metto solo i tipi standard
EXACTWIDTH_ADDTYPE(signed char)
EXACTWIDTH_ADDTYPE(unsigned char)
EXACTWIDTH_ADDTYPE(signed short int)
EXACTWIDTH_ADDTYPE(unsigned short int)
EXACTWIDTH_ADDTYPE(signed int)
EXACTWIDTH_ADDTYPE(unsigned int)
EXACTWIDTH_ADDTYPE(signed long)
EXACTWIDTH_ADDTYPE(unsigned long)
// ... e si finisce con questa
EXACTWIDTH_ENDTYPETABLE

#undef EXACTWIDTH_ADDTYPE
#undef EXACTWIDTH_BEGINTYPETABLE
#undef EXACTWIDTH_ENDTYPETABLE

// Ricerca ricorsiva del tipo che soddisfa le richieste

// Caso generale (mai richiamato, da solo la signature del template e l'argomento di default)
template<int Size, bool Signed, unsigned int LineNumber=Imp_TableFirstLine, bool Found=Imp_ExactBitWidthInt<Size, Signed, LineNumber>::Defined>
struct Imp_FindExactBitWidthInt;

// Tipo trovato: fornisce effettivamente il tipo richiesto come typedef
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_FindExactBitWidthInt<Size, Signed, LineNumber, true>
{
typedef typename Imp_ExactBitWidthInt<Size, Signed, LineNumber>::T T;
};

// Tipo non trovato: continua la ricerca, passando alla linea successiva
template<int Size, bool Signed, unsigned int LineNumber>
struct Imp_FindExactBitWidthInt<Size, Signed, LineNumber, false>
: Imp_FindExactBitWidthInt<Size, Signed, LineNumber+1>
{
};

// Caso base: tipo non trovato, tabella dei tipi finita; non definisce ::T, così
// si ottiene un errore di compilazione
template<int Size, bool Signed>
struct Imp_FindExactBitWidthInt<Size, Signed, Imp_TableLastLine, false>
{
};

// Tutto il giochino della ricerca anche per linea si rende necessario perché
// più tipi possono avere le medesime dimensioni; se così non fosse, basterebbe
// una tabella dei tipi senza numeri di riga e non sarebbe necessaria la ricerca
// ricorsiva

} // namespace Implementation

// Le due classi da usare per davvero:

// Fornisce come typedef ::T il tipo che corrisponde alla dimensione richiesta (in bit)
// con segno o meno a seconda del secondo parametro
template<int Size, bool Signed>
struct ExactBitWidthInt
{
typedef typename Implementation::Imp_FindExactBitWidthInt<Size, Signed>::T T;
};

// Fornisce come typedef ::T il tipo che corrisponde alla dimensione richiesta (in byte)
// con segno o meno a seconda del secondo parametro
template<int Size, bool Signed>
struct ExactByteWidthInt
{
typedef typename ExactBitWidthInt<Size*std::numeric_limits<unsigned char>::digits, Signed>::T T;
};

}

/*
Esempio:

typedef ExactWidthInt::ExactBitWidthInt<32, false>::T myuint32;
typedef ExactWidthInt::ExactByteWidthInt<2, true>::T my2bytesint;

*/
#endif

È venuto un pelo più complicato del previsto (per via del fatto che più tipi possono avere le stesse dimensioni), ma dovrebbe comunque funzionare bene. La cosa divertente è che se il tipo che corrisponda alle richieste (in termini di bit e di signed/unsigned) non è presente tra quelli della tabella si ha un errore di compilazione.

Per farti un esempio, per avere un intero a 32 bit senza segno basta fare


ExactWidthInt::ExactBitWidthInt<32, false>::T var;

Naturalmente come notazione è un po' pesante, per cui potresti voler fare una roba del tipo:


typedef ExactWidthInt::ExactBitWidthInt<32, false>::T UInt32;
typedef ExactWidthInt::ExactBitWidthInt<16, false>::T UInt16;
// ... eccetera

anche per evitare di rischiare l'istanziazione di quella caterva di template ogni volta che vuoi usare una variabile a dimensione fissata. :D

Non ho capito una mazza del tuo codice!xD. Mi sa che è a un livello troppo avanzato per me!xD.
Proverò ad appoggiarmi alla libreria che mi hai indicato!=).

Loading