CPP149 BMP画像の保存
CPP149 Preservation of BMP Image
黒月解析研究所 (KULOTSUKI ANALYSIS INSTITUTION)
CPP149 BMP画像の保存

 私は2008年の12月にC言語の本格的な学習を始めた。C言語の萌芽のころから、少しは気にしていたものの、BASICからF-BASICに移ることにより、ウィンドウ化の変化をなんとか吸収しようとしていたのだが、そのF-BASICも、そのあとの発展形を生み出さずに消えてゆくことになった。黒月解析研究所の最初の研究テーマは、スポーツの運動力学における立体的なシミュレーションだったが、これをF-BASICで行っていた。やがて、メインの研究テーマは、アインシュタインの特殊相対性理論の謎へと移ってゆき、コンピュータのキーボードによって、ほとんどWORDだけを操作することになった。2008年の11月ころに「幽霊変換」という論文を生み出すことで、一応の区切りを付けることができ、ここまでのことを普及させるという、次の段階のことを棚上げした。次は、一般相対性理論への反論材料となる、天文画像などの解析を行うため、BMP画像を取り込んで、この画像データを配列に書き込み、あれこれと加工して、より見やすくなるようにするということが目標となった。BMP画像を取り込むという処理をBASICで行えるかどうか分からなかったことと、C言語のテキストの中に、このことが行えると説明してあるページがあったので、C言語に関して、ほとんど初心者の状態であったが、学習を始めた。これらは、2008年の12月から2009年の2月ころのこと。C言語のプログラムでBMP画像を読み込み、それについて加工し、再び描写するということは、まもなく、できるようになった。すぐに、BASIC時代に学んであった、データ解析のノウハウを盛り込んで、いろいろな画像の中に潜んでいる、未知の事実を明らかにするということに集中した。このとき、読み込む画像をダイアログボックスで指定するという技術までは至っておらず、ペイントソフトを利用して、画像の名前をso.bmpと変え、この名前だけを読み取るプログラムの、加工部分だけを大きく膨らませていたのである。このようなダイアログボックスを利用できるようになったのが、2009年の8月ごろだったかもしれない。おそらく新技術と考えられるゴブリンアイを生み出し、これを、画像解析ソフトの大きな幹へと移植した後も、「BMP画像の保存」という、他の画像ソフトでは何ということもない技術が、私には、手の届かないものであった。

BMP画像の保存」が成功したのは、ようやく10月になってからのことである。BMP画像を読みだして、より分かりやすく、見やすくなるように加工して、その解析結果を保存する。この流れを、ようやく完結することができた。このことが、かなり難しいことであったのは、もちろん、私の学習過程にも問題があるのだが、C言語の上級者のそばで学習することができないという、私のような状況にあるものにとって、唯一の手がかりである、学習のためのテキストというものが、このあたりの段階になると、急に乏しくなってくるからでもあった。そして、このように独学するものにとっては、わずかな知識の欠損が、大きな壁となってしまうのである。私のようなものにとっての問題の多くは、上級者たちが、やさしいことと思っているところに潜んでいる。「BMP画像の保存」についても、同じようなことが起こっていた。私は、上級者たちがかんたんに通って行った道のあちこちで、あまりに多すぎる道標の、どれを信じ、どれを選んでゆけばよいのかが、なかなか分からなかったのだ。

C言語で「BMP画像の保存」を行うときの処理の流れについて、まとめよう。その前に一言。私が開発環境としているのは、Borland C++ Compiler である。C言語の入門書に付録として付いてあるものを利用している。これに関する導入については、高田美樹氏の「C言語スタートブック」が詳しく説明している。これは、C言語の入門者に対するテキストとしては名著である。

もう一言述べておく必要がある。C言語のプログラムでは、MS-DOSの画面で結果を表示する、ごくごく基本的なものに対して、ウインドウズの画面を用いるものでは、いろいろなことが異なってくる。プログラムを書いてあるテキストをsample1.cとし、これを置いてあるディレクトリをc:\mysrcとすると、前者をコンパイルするときには、コマンドプロンプトのウィンドウで、「c:\mysrc>bcc32 sample1.c」とすればよいが、ウインドウズの画面を用いるものでは「c:\mysrc>bcc32 W sample1.c」となって、「-W」というオプションが必要となる。もちろん、同じプログラムテキストでは無理で、それなりの内容を書いておく必要がある。これらの事情を、私は、粂井康孝氏の「猫でもわかるゲームプログラミング」で学ぼうとしたが、これは失敗だったかもしれない。とはいえ、別の人のテキストで、もっと良いものも見つからなかった。粂井康孝氏のテキストの中では、どちらかというと、「猫でもわかるWindowsプログラミング」のほうを選ぶべきであったようだ。それでも、高田美樹氏のテキストと、粂井康孝氏のテキストの間には、大きなギャップがある。粂井康孝氏は、氏のホームページで、彼の著書を初心者用だと述べておられるが、初心者という言葉は、あまりに広義の意味をもっているようで、私が感じるところでは、「猫でもわかるゲームプログラミング」や「猫でもわかるWindowsプログラミング」は、中級者に対応するものだと思える。ここまでのことが理解できる「猫」は、おそらく存在しないだろう。上記のテキストなどを学んで、およそのことを理解し終えた段階であれば、これから述べることも、ある程度理解できるかもしれない。おそらく、これらの知識は、C言語の中級者レベルに対応していることだろう。

C言語のプログラムの文頭に置くコードとして、#include<  > というものがあるが、ここには、次のすべてではないが、どれかが必要となる。これは、私のプログラムにおいて、「BMP画像の保存」が成功したものから取り出してきたものである。不要なものもあるかもしれない。

 

#include <windows.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <math.h>

#include <LIMITS.H>

#include <windowsx.h>

 

次に、広義の変数として定義すべきものが、何かあるかもしれない。また、メイン関数やウインドウプロシージャ関数に関しての問題については、上記のテキストなどで学んでほしい。このあとは、BMP画像を保存する関数のことについてのみ述べる。

BMP画像のデータは、大きく分けると2つ、厳密に分けると3つの種類となっている。この3種類について、各1行ずつの、データ書き込みコードを準備して実行することができれば、BMP画像を保存することができる。

BMP画像のデータを大きく2つに分けるというとき、一つ目は画像データの特徴を記したヘッダのデータであり、二つ目が実際の画像データである。3つに分けるというときは、このヘッダのデータを、さらに、ファイルヘッダと情報ヘッダという2つに分ける。

これらのデータの並びを、実際に見ることができる。BMP画像を一つ用意し(1)Windows OSのコンピュータに入っているメモ帳へと、マウスでドラッグしてゆきドロップオンしてみると、メモ帳の画面に、文字化けのような、文字と記号が入り混じったものが描かれる(2)。他のBMP画像で繰り返すと、同じようなものが描かれる(3)が、これらを観察すると、文の初めのあたりが、なんとなく似ていることが分かるだろう。まずBMとあって、空白やら、数字や記号が散在して、さらに空白が続き、突然、密集した記号の群れが並び始める。このときの、密集した記号の群れのあたりから最後までが、実際の画像データであり、BMからこのあたりまでがヘッダのデータである。

もう少し厳密に調べるときは、何らかのバイナリ・エディタを使うことになる。私は、今回、ベクターで公開されているstir131(フリーソフト)をダウンロードして使ってみた。実行ファイル名はStirling.exeである。図4は、Stirlingを走らせ、読み込み画像ファイルとしてamagoi.bmpを指定しようとしているところである。そして、図5は、この操作の実行結果である。ADDRESSの行は、この画像ファイルが、このコンピュータのメモリーのどこに保存されているかということを記したもののようだ。次の行からが、amagoi.bmpのデータということになる。「BM」から、最初の「」の前までが、おそらくヘッダ部分である。「ヌ・」「ネ・」「ヒ・」「ハ・」などの部分は、amagoi.bmpの画像にある、周囲の肌色部分を表現しているのだろう。ヘッダに戻って、左の機械語表記のところを見ると、「00」が多くある。これは、数字の0を表わしている。「42」が「B」で、「4D」が「M」に対応している。機械語は16進数であるから、42, 43, 44, 45, 46, 47, 48, 49, 4A, 4B, 4C, 4Dと並ぶのだろう。これで「42」を「B」として数えてゆくと、「4D」で、めでたく「M」となる。「9A」と「BC」が漢字の「」となり、「36」が数字の「6」か(?)。しかし、このような探索をやっていても、目的のプログラムへと結びつかない。少し方針を変えよう。

BMP画像のデータが、大きく分けてヘッダデータと画像データに分かれることが分かった。それでは、これらの各データの、さらに詳しい内容を調べよう。この目的のため、私は、次のようなウェブページを見つけて、ここから知識を吸収した。

 

BMPファイルフォーマット」kondo@kk.iij4u.or.jp

http://www.kk.iij4u.or.jp/~kondo/bmp/

C言語による画像処理プログラミング」静岡大学工学部 安藤和敏

http://coconut.sys.eng.shizuoka.ac.jp/bmp/

 

これらの内容を繰りかえすのは控えようと思う。これらは、よくまとまっており、プログラムではないので、バグの心配はない。実際のプログラムにおいて、ファイルヘッダと情報ヘッダが、どのように取りあつかわれるのかということを知ってから、これらの詳しい知識を参照すると、これらの知識も、うまく利用できる。

ここでは、とりあえず、ファイルヘッダが14byteであり、Windowsでは情報ヘッダが40byteであることに触れておこう。合計して、ヘッダデータはWindows のとき、54byteとなる。なるほど、図5の「42」から始まって、4行目の、「C7」の前にある「00」までで、ちょうど54個である。

次は、「BMP画像の保存」という目的に沿った、C言語によるプログラムを探すことになる。私は、この目的に対して、次のようなウェブページを見つけることができた。

 

① ■ビットマップを保存する(RLE圧縮対応) ■ Sample NO.79

(このウェブページは不明)

② 碧色工房 画像ファイルの扱い方(7) BMP(DIB)形式 (3)

BMP形式出力関数

http://www.mm2d.net/c/c-13.shtml

③ キャプチャされた画像をBMP File に保存

   http://www13.plala.or.jp/kmaeda/winc/clipfile.htm

 

これらの3つのウェブページに掲載されていたプログラムについては、これらを一つずつ、私のプログラムの中に書き込み、引数やポインタの名称などを修正して、「通れ」と念じながらコンパイルしてみたものの、いずれもエラーの表示が現れて、うまくいかなかった。エラーの中には、原文によるミスプリもあったし、こちらの学習能力の低さに由来する誤解などもあったが、決定的に、意味不明となってしまうエラーが残ってしまい、これらの利用を一時保留していた。

結果的に、私は、あるとき、BMP画像を保存することに成功したのであるが、このとき、上記のサンプルプログラムを読み比べ、共通して書かれている要素だけを選別し、自分なりに、最小限度のものだけを残すことにより、エラーの数を絞りこんでゆくことができた。最後にはエラーが表示されなくなったので、これで「通った」と思ったものの、カレントディレクトリに現れた画像ファイルの中は空っぽのままであった。この、エラーなしのエラーという、パラドックスのような状態から抜け出すヒントが、②に書かれていることに気づいて、ようやく、画像データに満たされているBMPファイルを、新たに書き込むことができた。ただし、このときの画像は、もとの画像に対して、逆立ち状態であった。この原因はすぐに分かった。②の著者が気を利かせすぎていたのである。画像データは、ディスプレイに描いたときの、左下から右上に向かって並べられている。これを、ディスプレイの座標に合わせて、左上からのものに変えてしまうと、余計な御世話になってしまって、画像が逆立ちしてしまうのだ。Windows OSが、このときの並べ替えをやってくれるので、こちらは、そのままの並びにしておくべきなのである。

物語が突然ラストのところへと飛んでしまった。もう一度、BMP画像を保存する関数の、C言語による表記のところへと戻ろう。

BMP画像のデータは、ファイルヘッダと情報ヘッダと画像データの3つに分けられる。このときの2種のヘッダの内容を指定するには、次のようにプログラムに書く必要がある。

 

BITMAPFILEHEADER bf;

BITMAPINFOHEADER bi;

 

このときの大文字部分は変更できない。ときどきテキストにBMPFILEHEADERBMPINFOHEADERと書かれていることもあるが、私が使っているBorland C++ Compiler は、この短縮形を認識してはくれなかった。小文字のbfbiは、変えられるようだが、ここでは、これらの形で指定しておく。

このように指定すると、画像ファイルのヘッダのための、C言語において、あらかじめ定義されている、構造体を利用することができる。少し戻って、この構造体へと入力するための値を、先に指定しておく必要がある。上記の②が中心的なものになるので、それに準じて、次のように指定する。

 

int x;          // 画像のx軸方向の画素数

int  y;          // 画像のy軸方向の画素数

int  pad;        // 32bit境界のためのパディング

int  imgsize;    // 画像のサイズ

 

ここで、xyについては、保存しようとしている画像によって決められる値である。私の解析プログラムでは、×64の窓を使ったとき、横15画素×縦10画素のものを、それぞれ64倍にするので、960×640となる。このとき、x=960y=640である。これらの値については、広域変数でbmp_wbmp_hを定義しておき、そこへと入力してある。

画像データを取りこんで加工してから再現するとき、このときのxの値が4で割り切れるものでないと、うまく描けない。このことを防ぐための方法の一つとして、②の著者はpadという値を設定して、これを利用している。

 

pad = (x * 3 + 3) / 4 * 4 x * 3;

imgsize = (x * 3 + pad) * y;

 

私は、任意のサイズの画像を取り込んだとき、4で割って、余り数がでると、このときに削り取ってしまうので、私のプログラムの中では、padについての処理は不要となり、pad=0となるだろう。上記のpadの式は、少しおかしい。これだと、pad=3しか出てこない。うまく行った私のプログラムで確認してみると、次のように書いている。これが正解のようだ。x=100を代入するとpad=0となる。x=101ならpad=1で、x=102のときはpad=2で、x=103のときはpad=3となる。

 

pad = 4*(int)((x * 3 + 3) / 4 ) - x * 3; // 32bit境界条件によるパディング

 

上記のimgsizex3が掛けられているのは、1画素の色に対して、青と緑と赤の色値が、lpBMP[3*k ], lpBMP[3*k +1], lpBMP[3*k +2]と、連続した数字で並べられるようになっているからである。私のプログラムでは、これらのディスプレイ用の配列とは別に、処理用の配列を、青緑赤の色別に、isob[k], isog[k], isor[k]と設定しておいて、ここへと、lpBMP[  ]の値を入力したり、戻したりしている。

ともあれ、私のプログラムにおいては、xはすでに4で割り切れる値となっているので、pad=0 imgsize = (x * 3) * y である。

ようやく準備が済んだ。ヘッダの構造体の説明へと戻ろう。まず、ファイルヘッダの構造体の要素から。

 

bf.bfType       = *(WORD*)BM;  // ファイルタイプ

bf.bfSize        = imgsize + sizeof(BITMAPFILEHEADER)

                  + sizeof(BITMAPINFOHEADER) ; 

           // ファイルサイズ(byte)

bf.bfReserved1  = 0 ;  // 予約領域, 常に0

bf.bfReserved2  = 0 ;  // 予約領域, 常に0

bf.bfOffBits     = sizeof(BITMAPFILEHEADER)

                  + sizeof(BITMAPINFOHEADER) ;

    // ファイル先頭から画像データまでのオフセット(byte)

 

1行目の記述は②のもの。私は、この書式の意味がよく分かっていないのだが、これで、”BM”という文字が入力できるらしい。sizeof( )というコードは( )内のもののサイズを求めるもののようだ。0が代入されている2つの変数は、将来の使用のために残してあるもので、まだ使われていない。Windowsの場合、sizeof(BITMAPFILEHEADER)=14sizeof(BITMAPINFOHEADER)=40となっているので、bf.bfOffBits=54と書いておいてもよい。

次に、情報ヘッダの構造体の要素を見よう。

 

bi.biSize           = sizeof(BITMAPINFOHEADER) ; // 40

bi.biWidth         = x ;    // 画像の幅(pixel, 画素数)

bi.biHeight        = y ;    // 画像の高さ(pixel, 画素数)

bi.biPlanes        = 1 ;  // 常に1

bi.biBitCount      = 24 ;   // 1677万色(true color)ビットマップ

bi.biCompression   = 0 ;    // 圧縮形式 0は無圧縮

bi.biSizeImage     = imgsize ;   // 画像データ部のサイズ(byte)

bi.biXPelsPerMeter = 0 ;  

// 横方向解像度(以下の項目は0のときもあるらしい)

bi.biYPelsPerMeter = 0 ;    // 縦方向解像度

bi.biClrUsed       = 0 ;    // 格納されているパレット数

bi.biClrImportant  = 0 ;    // 重要なパレットのインデックス

 

この構造体で、よく使われるのはbi.biWidth bi.biHeightであり、それに伴って、bi.biSizeImageを決めておく必要がある。

③のプログラムでは、次のように定義した後で、bi.biWidth = x などの、あらためて指定すべき変数の値を入力している。私は、上記システムで成功したので、この書式が通用するかどうかについては、テストできていない。おそらくエラーではなかったと思う。

 

static BITMAPFILEHEADER  bf = {0x4d42, 0, 0, 0, 0x36};

static BITMAPINFOHEADER  bi = {0x28, 0, 0, 1, 24, 0, 0, 0, 0, 0, 0 };

 

さて、ヘッダの変数を指定したら、さっそく、これらの値をファイルに書き込むことにしよう。②ではfwrite( )というものを使っているが、①と③がWriteFile( )というものを使っているので、多数決ではないが、③のプログラムをベースとしていたため、私は、WriteFile( )の書式で成功した。このとき、次のような準備も必要となる。hFile というハンドルで保存のためのファイルをCreateFile(  )で作っておく。dwBytesは、ここまでのところで、特に何も指定しないようだ。WriteFile( )の解説が「猫でもわかるWindowsプログラミング」のp258にある。WriteFile(a, b, c, d, e)とすると、aはファイルハンドル、bはバッファ、cは書き込むバイト数、dは書き込んだバイト数、eはオーバーラップ構造体とある。なるほど、dwBytesには、あまり重要な意味はないようだ。「猫でもわかるWindowsプログラミング」ではfwrite( )の検索項目がなく、「猫でもわかるC言語プログラム」のほうにはある。このfwrite( )は、ウィンドウオプション「-W」が必要なプログラムでは、あまり使われていないようだ。しかし、使えるか使えないかをテストしたわけではないから、使えないと決まったわけではない。

 

char szFile[MAX_PATH];   // ファイル入力の関数でも使うので広義変数として定義

HANDLE    hFile;      // 以下、BMP画像保存の関数での定義

DWORD     dwBytes;    // これらの定義は関数の始めのところで行う

hFile = CreateFile(szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,

 FILE_ATTRIBUTE_NORMAL, NULL);

WriteFile(hFile, &bf, sizeof(BITMAPFILEHEADER), &dwBytes, NULL);

WriteFile(hFile, &bi, sizeof(BITMAPINFOHEADER), &dwBytes, NULL);

 

BMP画像の保存」に必須の3要素に関して、2つをクリアーした。あとは画像データの本体そのものである。②の著者は、この目的のため、あるテクニックを使っている。最終的に私のプログラムがうまくいったのは、ここのところのテクニックの意味が分かったことによる。②の著者は、まず、関数の最初の定義部分で、「画像データとその先頭ポインタ」を定義している。それから、上記のヘッダ出力が終わったころで、「出力バッファ確保」という処理を行っている。ここでは、一行に等号(=)が二度現れており、このような書式をC言語で見たのは初めてだったので、強く印象に残った。この行は誤りではなく、きちんと通っている。ここで著者がやろうとしていたことを真に理解できたのは、このプログラムにおけるチャレンジを保留した後2ヶ月たってからのことだった。

 

unsigned char    *buf,  *buf_top ;  // 画像データとその先頭ポインタ

buf_top = buf = (unsigned char*)malloc(imgsize) ;  //出力バッファ確保

 

一つのポインタに対して、二つの記号を与えるのは、片方だけをどんどん変えていきながら、このポインタへと入力した値の全体については、変化させなかったほうの記号で処理するためなのである。②の著者のプログラムを参考にしつつ、①と③の表現も考慮して、私のプログラムで、うまくいったものを、次に記そう。私のプログラムでは、広域変数として、bufを他の用途のために定義しているので、混乱をさけるため、bufのところをg_imgに置き換えてある。この記号は③のプログラムからの遺伝である。

 

  int       i, j, so ;          // 関数の初めのところでの定義

  // ----------- データ整列 --------------- //

    for (i=0; i<y; i++)

    {

        for (j=0; j<x; j++)

        {

            so = i * x + j;

            *(g_img++) = isob[so];

            *(g_img++) = isog[so];

            *(g_img++) = isor[so];

        }

        for (j=0; j<pad; j++)   // 私のプログラムでは不要となる

        {

            *(g_img++) = 0;

        }

    }

    // ----------- 画像データ出力 ----------- //

    if ((WriteFile(hFile, (BYTE*)g_img_top, imgsize, &dwBytes, NULL))==FALSE)

    {

        MessageBox(NULL, "ピクセルデータの書き込みに失敗しました",

"PutBmp", MB_OK); //MBOKの間にアンダーライン有り

        CloseHandle(hFile);

        return;

    }

 

これで説明は終わったことになるが、話を前後させているところもあり、実際にプログラムを組むときには雛型があったほうがよいだろう。私のプログラムにおいて、BMP画像を保存する関数のところを書いておこう。AからBまでの部分は、保存のためのディレクトリと保存名を指定するダイアログボックスのためのものである。ここで定義されていない変数(おそらく、bmp_wbmp_h)は、広域変数として定義してある。

 

void PutBmp(HWND hWnd) 

{

    OPENFILENAME ofn;   

    HANDLE    hFile;

    DWORD     dwBytes;

    BITMAP    bm;  // これを欠くと保存できなくなる(テスト済)

    BITMAPFILEHEADER  bf;

    BITMAPINFOHEADER  bi;

    unsigned char       *g_img, *g_img_top;

    int       i, j, so, x, y, pad;

    int       imgsize, imgfilesize;

    char szFile[MAX_PATH];

    char szFileTitle[MAX_PATH];

 

    memset(&ofn, 0, sizeof(OPENFILENAME));   //_____ A

    ofn.lStructSize = sizeof(OPENFILENAME);

    ofn.hwndOwner = hWnd;

    ofn.lpstrFilter = "ビットマップファイル

(*.bmp)\0*.bmp\0すべてのファイル(*.*)\0*.*\0\0"; 

    ofn.lpstrFile = szFile;

    ofn.lpstrFileTitle = szFileTitle;

    ofn.nFilterIndex = 1;

    ofn.nMaxFile = MAX_PATH;

    ofn.nMaxFileTitle = MAX_PATH;

    ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;

    ofn.lpstrDefExt = "bmp" ; //"bmp" 符号付き値と符号なし値の比較(警告箇所)

    ofn.lpstrTitle ="名前をつけて保存する";

 

    if (GetSaveFileName(&ofn) == 0)

    {

        //MessageBox(NULL, "失敗しました", "PutBmp", MB_OK); // 未使用

        CloseHandle(hFile);

        return;

    }                             //____ B

 

    hFile = CreateFile(szFile, GENERIC_WRITE, 0, 0,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile == NULL)  return;

    // ---------- ヘッダ作成 -------------- //

    x = bmp_w;  // bmp_w は広域変数で、この値は外部で指定されている

    y = bmp_h;   // bmp_h は広域変数で、この値は外部で指定されている

    pad = 4*(int)((x * 3 + 3) / 4 ) - x * 3;   // 32bit境界条件によるパディング

    imgsize = (x * 3 + pad ) * y;             // 出力される場増データサイズ

    imgfilesize = imgsize + sizeof(BITMAPFILEHEADER)

 + sizeof(BITMAPINFOHEADER);

    bf.bfType = *(WORD*)"BM";

    bf.bfSize = imgfilesize;  

    bf.bfReserved1 = 0;

    bf.bfReserved2 = 0;

    bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    bi.biSize = sizeof(BITMAPINFOHEADER);

    bi.biWidth = x;

    bi.biHeight = y;

    bi.biPlanes =1;

    bi.biBitCount = 24;   // 24色ビットマップ画像に対応

    bi.biCompression =0;

    bi.biSizeImage = imgsize;

    bi.biXPelsPerMeter = 0;

    bi.biYPelsPerMeter = 0;

    bi.biClrUsed       = 0;

    bi.biClrImportant  = 0;

    // ----------- 出力バッファ確保 --------- //   

    g_img_top = g_img = (unsigned char*)malloc(imgfilesize);

    // ----------- ヘッダ出力 --------------- //

    WriteFile(hFile, &bf, sizeof(BITMAPFILEHEADER), &dwBytes, NULL);

    WriteFile(hFile, &bi, sizeof(BITMAPINFOHEADER), &dwBytes, NULL);

    // ----------- データ整列 --------------- //

    for (i=0; i<y; i++)

    {

        for (j=0; j<x; j++)

        {

            so = i * x + j;

            *(g_img++) = isob[so];  // 青色の値の書き込み

            *(g_img++) = isog[so];  // 緑色の値の書き込み

            *(g_img++) = isor[so];   // 赤色の値の書き込み

        }

        for (j=0; j<pad; j++)

        {

            *(g_img++) = 0;

        }

    }

       // ----------- 画像データ出力 ----------- //

    if ((WriteFile(hFile, (BYTE*)g_img_top, imgsize, &dwBytes, NULL))== FALSE)

    {

        MessageBox(NULL, "ピクセルデータの書き込みに失敗しました",

"PutBmp", MB_OK); //MBOKの間にアンダーライン有り

        CloseHandle(hFile);

        return;

    }

}

 

(2009.010.11 Written by Kinohito KULOTSUKI)