Entry

プログラミングメモ - BITMAPFILEHEADER を自前で宣言する

2008年12月28日

みんな引っかかるとこだと思ったんですけど,BITMAPFILEHEADER を自前で定義しようとしてはまったからメモ。

BITMAPFILEHEADER っつのは,いわゆるビットマップファイルのヘッダセクションに書かれているデータです。 DIB 形式のファイルヘッダということで,実際はビットマップファイルに限った話ではないんですが。

ともかく,この構造体なんですけど,以下のような構造体宣言をそのまま使ってファイルから読もうとすると,ちゃんと値を取ることができません。

typedef struct tagBITMAPFILEHEADER {
    WORD   bfType;
    DWORD  bfSize;
    WORD   bfReserved1;
    WORD   bfReserved2;
    DWORD  bfOffBits;
} BITMAPFILEHEADER;

お気づきの人はもう分かると思うんですけど,WORD 値(unsigned short)の次に DWORD(unsigned long)値があるので,アラインメントの関係で,コンパイラが勝手にパディングを入れてしまうのでした。つまり,こういう構造体ができてしまう,と。

typedef struct tagBITMAPFILEHEADER {
    WORD   bfType;
    WORD   __PADDING__;  // これが入る
    DWORD  bfSize;
    WORD   bfReserved1;
    WORD   bfReserved2;
    DWORD  bfOffBits;
} BITMAPFILEHEADER;

MSVC の場合,デフォルトのアラインメントは 4 bytes ですから,bfType と bfSize の間に,2 bytes 分の空きができてしまうわけです。sizeof(BITMAPFILEHEADER) の値が,どうしても 16 bytes になってしまうもんで(本当は14 bytes),ちとはまってしまった。

こういう場合,パディングしないように(パックするように)コンパイラに命令する必要があります。volatile 修飾子をつけたらなんとかなるかと思ったんですけど,うちの環境ではどうにもなりませんでした(トホホ)。この点,MSVC の場合は,pshpack2.h を include して #pragma pack(2) を使います。アラインメントを戻す時は,poppack.h を include する。けど,#pragma ディレクティブは MSVC の場合だけで有効なので,あまり汎用性がありません。だったら自前で定義する必要ないじゃんってなことになりますもんね。

Windows 環境以外でも,ちゃんとパックするためには,それぞれのコンパイラでパックするように指示しないといけません。例えば,GCC を使うときは __attribute__((__packed__)) 修飾子を使います。

つことで,MSVC でも Cygwin gcc でもコンパイルできるように,BITMAPFILEHEADER を宣言する。こんな感じになるんでしょうか。

#ifdef _MSC_VER
#include <pshpack2.h>
#endif // _MSC_VER

#ifdef __GNUC__
struct __attribute__((__packed__)) BitmapFileHeader {
#else
struct BitmapFileHeader {
#endif
    unsigned short bfType;
    unsigned long bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned long bfOffBits;
};

#ifdef __GNUC__
struct __attribute__((__packed__)) BitmapInfoHeader {
#else
struct BitmapInfoHeader {
#endif
    unsigned long biSize;
    long biWidth;
    long biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned long biCompression;
    unsigned long biSizeImage;
    long biXPelsPerMeter;
    long biYPelsPerMeter;
    unsigned long biClrUsed;
    unsigned long biClrImportant;
};

#ifdef _MSC_VER
#include <poppack.h>
#endif // _MSC_VER

これでどっちでも読めるようになりました。おまけで BITMAPINFOHEADER もつけてみた。

ちとこの頃,ポータブルな座標クラスや矩形クラスを自前で揃えようと思っていて,趣味でゴニョゴニョやってるんですけど,なかなか実装が進みません。MFC のソースを読んでいると,いろいろと工夫があるもんで,一筋縄ではいかなそう。

個人的に,裏方のライブラリを作るのは嫌いじゃないんだけれども,ノウハウの塊を読むにつれて,「自分で作る意味あるのか?」と自問することもしばしばです。ま,年末の暇つぶしにゴニョゴニョなわけです。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN