ややプログラム紀行

博士2年のプログラムに関する日記

ビットマップの読み込み

画像を印刷する必要が出てきたんで、いろいろ頑張ってたんですけどなかなかうまくいかない・・・

調べてみたところ、そもそも画像の表示ができていないようでした

自分のPCでは、なぜかLoadImage関数が使えなくなっていたので、まあいい機会だと思ってDIBを使っていました

DIBとは、Device Independent Bitmap(デバイス独立ビットマップ)の略だそうで、色のデータや、パレット、何ビットの画像化などの情報を詰め込むことでどんな環境でも表示できるようにした画像らしいです

この対極にDDB(Deveice Dependent Bitmap)があるんですが、その違いははっきりとはわかってません

まあ自分の中での勝手なまとめだと、プログラム実行中に生データをいじれるのがDIB、いじれないのがDDBだと思ってます(´・ω・`)

で、今回は、ビットマップ画像をDIBで読み込もうというのが目的でした

ビットマップ画像は、多分数ある保存形式で一番簡単な仕組みになってるんじゃないでしょうか、それでも難しい

具体的な仕組みは、このサイトが一番わかりやすかったです

リンク踏んだ方が百倍速いですが軽く説明すると、ビットマップ画像は大まかに言って上から

・ファイルヘッダー

・インフォヘッダー(情報ヘッダー)

ピクセルのデータ

という仕組みになってます

正直、この3つの読み込み方はググりさえすればすぐできます、サイトを見つけるのに時間かかるだけです

fopen関数でビットマップ画像を開いたら、(バイト単位で読み込むのでifstreamよりこっちの方がやりやすい気がします)

BITMAPFILEHEADER BmpFileHdr;

fread(&BmpFileHdr, sizeof(BmpFileHdr), 1, h);

hっていうのはFILE型のポインターだと思ってください

最初からファイルヘッダー用の型が用意されてるのでこれだけで済みます

インフォヘッダーも同じく

BITMAPINFOHEADER BmpInfoHdr;

fread(&BmpInfoHdr, sizeof(BmpInfoHdr), 1, h);

です

ここら辺は様々なサイトで書かれてますし、僕が書く必要もあまりないと思います(というか上のソースもほかのサイトを参考にしたやつですつまりコピペ

しかし、このインフォヘッダーの「biCompression」というメンバ変数が非常に曲者です・・・

この変数のせいで画像がうまく表示されず、また、この記事を書こうと思った理由ですww

この変数は圧縮フラグに使われてるらしいですが、この変数がBI_BITFIELDSになっている場合が要注意です

上に書いたサイトでは

BI_BITFIELDSは事実上は存在しないと思われます。(指定カラーマスクの使えるアプリケーション見た事無いよ)

なんて書いてあるんですが、GIMPで出力した画像はこのBI_BITFIELDSが設定されていて、そのせいで画像が全く表示されず本当に困りました

なので、このフラグがセットされていた時の為のプログラムも書いておいた方がいいかと思います

インフォヘッダーの後ろには「パレット」というデータが続くんですが、もしBI_BITFIELDSがセットされていた場合はこのパレットと、インフォヘッダーの間に挿入されるようです(パレットが入っている画像を持っていないので未検証ですorz)

RGB各4バイトずつの12バイトの気がするんですが、実はこのカラーマスクデータ用の保存する型がないような気がします

多分パレットに使うRGBQUADという型に追加挿入、っていう感じなんじゃないかと・・・(誰か知ってる人いたら教えてくださいorz)

とりあえずカラーマスクデータの話はこんなところです

最終的に行きついたのが上に書いたサイトなので、それを読めば一通り理解できると思います(投げやり

ソースコードを載せておきますが、あってる保証は全くないです・・・(参考サイト

int PSize;//パレット領域のサイズ

int BitsSize;//ビット領域のサイズ

int IPSize;//インフォヘッダーとパレット領域を合わせたサイズ

BITMAPFILEHEADER BmpFileHdr;

BITMAPINFOHEADER BmpInfoHdr;

//ファイルを開く

char cBuffer[64]; // マルチバイト文字

WideCharToMultiByte(CP_ACP, NULL, fileName.c_str(), -1, cBuffer, sizeof(cBuffer), NULL, NULL);

FILE* h = fopen(cBuffer, "rb");

if(h == nullptr)

return DIB_FAILED;

//ファイルヘッダーを読み込む

fread(&BmpFileHdr, sizeof(BmpFileHdr), 1, h);

//インフォヘッダーを読み込む

fread(&BmpInfoHdr, sizeof(BmpInfoHdr), 1, h);

int used;

//16色と256色しかサポートしません(色数も決め打ちしてます)

switch (BmpInfoHdr.biBitCount)

{

case 8:

used=256;

break;

case 4:

used=16;

break;

default:

used = 0;

}

if(BmpInfoHdr.biCompression & BI_BITFIELDS)

used += 16;

//パレット領域のサイズ

PSize = used * sizeof(RGBQUAD);

//インフォヘッダーと、パレットのサイズ

IPSize = sizeof(BITMAPINFOHEADER) + PSize;

//画像のビットのサイズ

BitsSize = BmpFileHdr.bfSize - BmpFileHdr.bfOffBits;

//(ヘッダー+パレット)の領域を確保

pBmpInfo = (LPBITMAPINFO)malloc(IPSize);

memcpy(pBmpInfo, &BmpInfoHdr, sizeof(BITMAPINFOHEADER));//ヘッダをコピー

//パレットを読み込む

fread(((LPBYTE)pBmpInfo) + sizeof(BITMAPINFOHEADER), PSize, 1, h);

//CreateDIBSectionを使う

BMP = CreateDIBSection(nullptr, pBmpInfo, DIB_RGB_COLORS, (VOID**)&pBits, 0, 0);

//ビットの位置までシークして

fseek(h, BmpFileHdr.bfOffBits, 0);

//ビットを読み込む

fread(pBits, BitsSize, 1, h);

fclose(h);