007-03  前へ←  ホームへ  →次へ  007-05

No.007 - 04  ビットマップの読込・保存


BMPファイルの読込・保存です。
最近めっきり見かけないRLE圧縮の読込・保存もやります。


ビットマップのファイル構造

ビットマップファイルのヘッダ構造部には実にいろいろな構造体が存在します。
しかもどれもこれも名前似すぎです。なるべくわかりやすく書こうと思いますが、至らぬ点はご了承ください。

ビットマップファイルは大きくわけて4つのブロックに分かれます。

1 BMPファイルヘッダ
2 BMPインフォヘッダ
3 RGB カラーテーブル
4 ピクセル(実データ)
 
順を追ってみていきます


1.BMPファイルヘッダ

BITMAPFILEHEADER構 造体です。
ここには、BMPのファイルとしての情報が入ってます
識別子「BM」、 ファイル全体のサイズ、 ピクセル(実データ)のファイル内でのオフセット位置の3点です。

識別子「BM」はシグネチャ(signature)とも呼ばれます。
シグネチャ(signature)は署名と訳されることもあるようですが、それでは語弊があるように思います。
signal(信号) やsignify(〜を示す)のように sign (サイン)から派生した言葉だと考えられ、「他ファイルと区別する」という意味で使ってるはず。音楽用語にsignatureで記号という意味もあるよう で命名者は多分そちらの意味で使ったんじゃないかな〜?なのでsignatureは識別記号、識別子という訳のほうがしっくりきます。


2.BMPインフォ

BITMAPINFO構造体です。
かなりわかりづらい構造体です(一見意味不明な仕様です)。

BMPインフォ{
       BITMAPINFOHEADER  bmiHeader;  // BMPインフォヘッダ
       RGBQUAD   bmiColors[1];              // RGBQUAD
}

というたった2つのメンバ変数を持つだけです。
必要な情報はBITMAPINFOHEADERに はいっていて、
カラーテーブルはRGBQUADから取得します


BITMAPINFOHEADER
ビットマップ・インフォ・ヘッダ構造体について説明します。
構造体サイズは40です。・・・(OS/2互換DIBならBITMAPCOREHEADERな ので12です)。
画像横幅、画像高さ メンバは周知のとおりです。
biPlanes はカラープレーンです。==1です。定義されたときから今までずっと1のままです。(Windows初期の名残)
biBitCount ビット深度です。現在は 1, 4, 8, 16, 24, 32 のどれかです。

biCompression は圧縮フラグです。ピクセルデータがどんな配列になってるかのフラグで、
BI_RGB、BI_RLE4、BI_RLE8、BI_BITFIELDSのいずれかの値です。
余程のコトが無い限りBI_RGBの値を使います。

BI_RGB なら圧縮なし、ピクセルデータはそのまま並んでます。
BI_RLE4 なら4bit RLE圧縮、BI_RLE8 なら8bit RLE圧縮、
BI_BITFIELDS なら16bit か 32bit で、この構造体の直後に保存されている「指定カラーマスク」を使用しますが、BI_BITFIELDSは事実上は存在しないと思われます。(指定カラーマスクの使えるアプリケーション見た事無いよ)
biCompression
とりうる値   とりうる値
1bit BI_RGB 16bit BI_RGB、BI_BITFIELDS
4bit BI_RGB、BI_RLE4 24bit BI_RGB
8bit BI_RGB、BI_RLE8 32bit BI_RGB、BI_BITFIELDS
カラーマスクについては下記参照。

biSizeImage は画像のバイト数(==行のバイト数*高さ)が入ってるはずですが、0が入ってること もあるようです。
biXPelsPerMeterbiYPelsPerMeter は水平解像度、垂直解像度を示していて、 1メートルあたりのピクセル数らしいのですがwindows内部では使いません。通常は0が入ってるようです。(ちなみにpelという言葉は picture element の略でIBMが使う言葉だそうです)

biClrUsedはカラーテーブルの配列数です。8bit以下のDIBでは非常に重要です。0が入ってるときは最大配列数 (1bit==2, 4bit==16, 8bit==256)になります。
biClrImportant は重要な色の数です。具体的には画像内で使用している色数です。0なら全色を使用していることを示します。


OS/2互換DIBの場合、
BITMAPINFO構造体BITMAPCOREINFO構造体
BITMAPINFOHEADER構造体BITMAPCOREHEADERに なります。
構造体サイズは異なりますが、構造体は互換なはずなので下記サンプルソースのままで読み込めるはず・・・です。

BITMAPINFOHEADER構造体は、BITMAPV4HEADER構造体やBITMAPV5HEADER構造体にもなりうるようです。
これらの構造体はWindowsが95,98〜と変遷するにつれて作られた、BITMAPINFOHEADER構造体と互換の構造体です。
カラーマスクやらガンマ値やら入ってますが、ビットマップではそこまでしなくてもいいんじゃないかと思います。

(実際ほんとにガンマ値等を扱うような画像なら、これだけでは満足な画像にならないからです。もっと高レベルな情報ヘッダが必要です。
そもそもガンマ補正とは電圧を均等に変化させても、人間の目には均等に見えないのでそれを補正するっていう技術なんですが
CRTも液晶も同じ補正値ってわけにはいかないので、
ガンマ補正を実装するにはそれ相応の開発環境が必要になってしまうのです。輝度測る機械などは必須。)



3.RGBカラーテーブル (パレット)

上述したようにBITMAPINFO構 造体のbmiColors[1]メンバで配列的にアクセスします。
配列数BITMAPINFOHEADERbiClrUsedメ ンバです。

BITMAPINFOの 構造体を見て大体の人はbmiColors[1] ってなんじゃいな?っておもうと思います。
配列1個だけなら配列にする必要ないじゃん?って思うはずです。

実はコレbmiColors[16]とか、bmiColors[255]とかやってカラーテーブルに配列的にアクセス するための仕込みです。
不正メモリアクセスになりそう?
確かにメモリがsizeof(BITMAPINFO)しかなければ不正メモリアクセスになりますが、
ここでポイント。
BITMAPINFOの メモリ確保(new でなくmalloc)をするときに
( sizeof(BITMAPINFO) + (色数 -1)*RGBQUAD )という メモリサイズにしてやるのです。

これでカラーテーブルにbmiColors[16]といった風に配列的にアクセスできます。
そういう意味なんですって。ぐふぅ。あほか。


4.ピクセル・データ
(実データ部分)

ピクセルデータはボトムアップ式になっています。
007-001でもやったように8bit以下のビット深度ならパレットの番号がはいっており、16bit以上ならRGB配列がはいっています。
上記biCompressionBI_RGBである場合(ほとんどコレ、99%以上)配列はただ、闇雲にだら〜と並べて置かれています。

またbiCompressionBI_RLE4BI_RLE8の値をとった場合(4bitと8bitの時ね)、配列はRLE圧縮されて格納されます。

RLE圧縮

<RLE圧縮するくらいならPNGで保存する方がいいよ>
とまあ、耳タコな事実をアピールしつつも、昨今のあまりにもRLEに対する扱いのひどさから今回の記述に至ったわけで。
筆者も正直「RLEは不毛だな〜」と思ってます。ええ思ってますとも。

RLE圧縮はランレングス(run-length)圧縮ともいい、ビットマップ内部の圧縮方法です。
ただ4bitと、8bitのビットマップにしか適用できず、実際あまり利用価値があるようには思えません。
またbiSizeImage0がはいってるとWindowsの標準ビューアで読み込めません(マジかよ)。

ランレングスはrun-lengthだから「ランレングス」じゃなくて「ランレンクス」じゃないの?と思うんですが、どの本も辞書もサイト も「グ」なんで詳細不明。とりあえず「ランレングス」で表記します。(アメリカの大学とか行かないとわからない?)


RLEの基本的な考え方はこうです。
同じものが続いていたらその繰り返し回数と繰り返す値を使う。
不規則な値が続いていたらその不規則数と、各値をそのまま置く。
これだけです。
圧縮の一番カンタンなやつです。
当然、同じ値が続いたピクセルがないと無圧縮時よりサイズが大きくなります。

まず8bitRLEから説明します(1バイト単位でわかりやすいから)。
RLEは1バイトずつ読み込んでその値により処理を分岐させていきます。
第1バイト 第2バイト 第3バイト 第4バイト 意味
00 00     行末
00 01     画像終端
00 02 dx dy (x+dx, y+dy) に移動
00 03〜0xFF     第3バイト以降に続く、[第2バイト]数分をそのまま使う
(不規則ピクセルの連続)
01〜0xFF (ピクセル値)     第2バイトの値のピクセルを、[第1バイト]数分繰り返す
(同値ピクセルの連続)

0x05  0x31
というバイトを読み込めば
0x31  0x31  0x31  0x31  0x31
というコードに変換されます

0x00  0x04  0x11  0x22  0x33  0x44
というバイトを読み込めば
0x11  0x22  0x33  0x44
というコードに変換されます

圧縮されているコードは常に2バイトの境界で揃えられます。
つまり圧縮されているコードは常に偶数バイトでなければならず、
もし奇数になってしまった場合0x00 という1バイトが末尾に足されています。

0x00  0x05  0x11  0x22  0x33  0x44  0x55  0x00
というバイトを読み込めば
0x11  0x22  0x33  0x44  0x55
というコードに変換されるということです。

また、0x00  0x02 といったバイトを読み込めば画像に未定義領域(何も書かない領域)が作られます。
(筆者、このコードの存在意味がわかりません。透明とも違うし)


次に4bitRLEを説明します
基本部分は同じなんですがバイトとピクセルが1対1になってないのでちょっと大変です。
第1バイトが0でないとき、第2バイトの値のピクセルの同値連続となりますが、
1バイトに2ピクセル分のデータが入ってるので、
例えば
0x04  0x31
なら
0x31  0x31
というコードに変換されるということです。

8bitのときの半分のピクセル数になります。
また繰り返し数が奇数のとき
例えば
0x05  0x31
なら
0x31  0x31  0x3?
というコードに変換されます。末尾4bitは不確定のまま次のピクセルへ進みます。


カラーマスク

説明だけ。実装しません。
フォトショップもWindows標準編集ソフトも保存不可能な形式を いったいだれが作ってんのか謎。
読込だけなら対応しているものもあるようです。
でもカラーマスクに対応させるのに下手なコーディングすると処理が遅くなります
(ピクセル反復子を拡張してもつらい、ていうかビット深度64bitがでるかもって言う時代に無理にやんなくてもいいでしょ?)。

カラーマスクは16bit , 32bit でbiCompression==BI_BITFIELDSときのみ発動です。
通常RGBは均等なビット数が割り振られています。
24bit, 32bit画像ならRGB各8ビットずつ、16bitも通常はRGB各5ビットずつです。
このビット数の割り振り方を変えて、使いたい色だけ特化させるってことです。

例えば16bit なら通常RGB 5-5-5となっているのを5-6-5にしたりとかするわけです。
このときカラーマスクは
bit
5-5-5 5-6-5
カラーマスク R 0x00007C00 0x0000F800
G 0x000003E0 0x000007E0
B 0x0000001F 0x0000001F
となります。

色を取得するときは5-6-5なら
R = (color & R_mask) >> 11;
G = (color & G_mask) >> 5;
B = (color & B_mask) >> 0;

これをつかうと理論上は色をどんなふうに割り振ってもいいことになります(0-0-16とか)。
たしかにNTとかでは超変則なカラーマスクもできるらしいんですが、
Win98で使える変則マスクは5-6-5だけです。win98、32bitは8-8-8しかだめです。
実用性は低いです、というか皆無です。




サンプル解説 
下記サンプルソースは6つのファイルから成っています

ピクセル反復子や、パレット(色テーブル)クラスを定義するヘッダの gt_img_parts.h と、
定数を定義している resource_04.h と、
画像クラスを定義している gt_img_04.h
リソーススクリプトの gt_img_04.rc
メインファイルの gt_img_04.cpp
そして実際にBMPファイルを読み書きするgt_img_bmp_io.cpp です。

gt_img_parts.hファイルは007-02のものと全く同じものを使います。ここには書きませんのでご注意ください。







サンプルで表示しているBMPとRLEです。→ gt_img_04_bmp_sample.zip

↑メニューからなんかファイルを開いてください ↑24ビット画像
↑8ビット画像 ↑4ビット画像



resource_04.h

// ビットマップ読込

#define IDM_MAKE_DIB_0      1000
#define IDM_MAKE_DIB_1      1001
#define IDM_MAKE_DIB_4      1004
#define IDM_MAKE_DIB_8      1008
#define IDM_MAKE_DIB_16     1016
#define IDM_MAKE_DIB_24     1024
#define IDM_MAKE_DIB_32     1032

#define IDM_OPEN            2000
#define IDM_LOAD_DIB        2001
#define IDM_SAVE_DIB        2002
#define IDM_SAVE_DIB_RLE    2003



gt_img_04.rc

#include "resource_04.h"

//リソーススクリプト
GT_DIB_MENU MENU {
    POPUP "ファイル" {
        POPUP "作成" {
            MENUITEM "1 bit" , IDM_MAKE_DIB_1
            MENUITEM "4 bit" , IDM_MAKE_DIB_4
            MENUITEM "8 bit" , IDM_MAKE_DIB_8
            MENUITEM "16 bit", IDM_MAKE_DIB_16
            MENUITEM "24 bit", IDM_MAKE_DIB_24
            MENUITEM "32 bit", IDM_MAKE_DIB_32
        }
        MENUITEM "ひらく",      IDM_OPEN
        MENUITEM "保存",        IDM_SAVE_DIB
        MENUITEM "ランレングス圧縮で保存",  IDM_SAVE_DIB_RLE
    }
}



gt_img_04.h
// ビットマップ 読込

#include <stdio.h>
#include <windows.h>
#include "resource_04.h"
#include "gt_img_parts.h"

// ======== GT_IMG ==================================
class GT_IMG {
    public :
    GT_COL_TABLE m_col_table;
    unsigned long *m_col_mask;
    char *m_alpha;

    int m_width, m_height, m_byte_width, m_bit_count;
    BYTE *m_pbits;

    GT_IMG();
    virtual ~GT_IMG();

    // 作成と破棄
    virtual destroy();
    virtual create(int cx, int cy, int a_bit_count, int a_color_size=0);

    // ピクセル描画
    set_pixel(int x, int y, DWORD col);
    DWORD get_pixel(int x, int y);

    // アルファ値
    is_alpha(){ return (m_alpha!=NULL); }

    // カラー
    set_col_mask(UINT r, UINT g, UINT b);
    set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b);

    // 読込
    load_dib(char *filename);
    load_dib_rle4(HANDLE hfile);    // 4bit ランレングス圧縮
    load_dib_rle8(HANDLE hfile);    // 8bit ランレングス圧縮

    // 書込み
    save_dib(char *filename, int rle_compress_flag=0);
    save_dib_rle4(HANDLE hfile);    // 4bit ランレングス圧縮
    save_dib_rle8(HANDLE hfile);    // 8bit ランレングス圧縮
};


// コンストラクタ
GT_IMG::GT_IMG(){
    m_col_mask =NULL;
    m_alpha =NULL;
    m_width = m_height = m_byte_width = m_bit_count =0;
    m_pbits =NULL;
}
// デストラクタ
GT_IMG::~GT_IMG(){
    destroy();
}
// GIMG 破棄
GT_IMG::destroy(){
    m_width=0;
    m_height=0;
    m_byte_width=0;
    m_bit_count=0;
    if (m_pbits) delete[] m_pbits;
    m_pbits = NULL;
    if (m_col_mask) delete[] m_col_mask;
    m_col_mask = NULL;
    m_col_table.destroy();
    return 0;
};
// GIMG 作成
GT_IMG::create(int cx, int cy, int a_bit_count, int a_color_size){
    int i, palette_len, info_size;
    destroy();  // ファイルが開けたら既存データ破棄

    if (cx<=0 || cy<=0) return -1;
    if (a_bit_count!=1 && a_bit_count!=4 && a_bit_count!=8
        && a_bit_count!=16 && a_bit_count!=24 && a_bit_count!=32) return -1;

    if (a_bit_count<=8 && a_color_size==0)  palette_len=1<<a_bit_count; // 8bit以下でパレット色数指定無しなら
    else palette_len=a_color_size;

    // パレット・メモリ確保
    m_col_table.make(palette_len);

    /* バッファの1ラインの長さを計算 */
    m_byte_width=4*((cx*a_bit_count+31)/32);
    m_pbits = new BYTE[m_byte_width*cy];
    ZeroMemory(m_pbits, m_byte_width*cy);

    if (!m_pbits) return -1;
    m_width= cx;
    m_height=cy;
    m_bit_count=a_bit_count;

    // パレット自動配色
    // 適当な基本色
    RGBTRIPLE rgb[8]={
        {0,0,0},{255, 255, 255}, {0,0,255}, {255,0,0}, {0,255,0}, {255, 255,0}, {255,0,255},{0,255,255}
    };

    for(i=0;i<min(palette_len, 8);i++){
        m_col_table.add_col(rgb[i].rgbtRed, rgb[i].rgbtGreen, rgb[i].rgbtBlue);
    }

    return 0;
}
// パレットに色設定
inline GT_IMG::set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b){
    return m_col_table.set_pal_col(NULL, index, r, g, b);
}
// カラーマスク設定
GT_IMG::set_col_mask(UINT r, UINT g, UINT b){
    if (!m_col_mask) m_col_mask = new unsigned long[3];
    m_col_mask[0] = r;
    m_col_mask[1] = g;
    m_col_mask[2] = b;
}
// 1点ピクセル取得
DWORD GT_IMG::get_pixel(int x, int y){
    if (!m_pbits) return 0;
    if (x<0 || x>=m_width || y<0 || y>=m_height) return 0;
    BYTE *ppix;
    ppix=m_pbits+(m_height-y-1)*m_byte_width + (x*m_bit_count>>3);

    switch(m_bit_count){
    case 1: return 0x01 & (*ppix >> (7-(x&7)));
    case 4: return 0x0f & (*ppix >> ((x&1)?0:4));
    case 8: return *ppix ;
    case 16: return *(WORD*)ppix;
    case 24: return 0x00FFffFF & *(DWORD*)ppix;
    case 32: return *(DWORD*)ppix ;
    }
    return 0;
}
// 1点ピクセル書きこみ
GT_IMG::set_pixel(int x, int y, DWORD col){
    if (!m_pbits) return 0;
    if (x<0 || x>=m_width || y<0 || y>=m_height) return 0;
    BYTE *ppix;
    ppix=m_pbits+(m_height-y-1)*m_byte_width + (x*m_bit_count>>3);

    switch(m_bit_count){
    case 1: *ppix &= ~(1         <<(7-(x&7)));
            *ppix |= ((bool)col) <<(7-(x&7));
            break;
    case 4: *ppix &= 0x0f << ((x&1)?4:0);
            *ppix |= (0x0f & col)  << ((x&1)?0:4);
            break;
    case 8: *ppix = (BYTE)col;                          break;
    case 16: *(WORD*)ppix = (WORD)col;                  break;
    case 24: *(RGBTRIPLE*)ppix = *(RGBTRIPLE*)&col;     break;
    case 32: *(DWORD*)ppix = *(DWORD*)&col;             break;
    }
    return 0;
}


// ======== GT_DIB ==================================
class GT_DIB : public GT_IMG{
    public :
    HDC m_hdc;
    HBITMAP m_hbmp;

    GT_DIB();
    ~GT_DIB();

    // DC 適用
    apply_dc(HDC hdc);
    apply_dc(HWND hwnd);

    // 作成と破棄
    create(int cx, int cy, int a_bit_count, int a_color_size=0);
    virtual destroy();

    // カラー
    apply_palette_all();
    set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b);

};
// コンストラクタ
GT_DIB::GT_DIB(){
    m_hdc = NULL;
    m_hbmp = NULL;
}
// デストラクタ
GT_DIB::~GT_DIB(){
    destroy();
}
// HDCパレット設定(全て)
GT_DIB::apply_palette_all(){
    return m_col_table.apply_palette_all(m_hdc);
}
// パレットに色設定
inline GT_DIB::set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b){
    return m_col_table.set_pal_col(m_hdc, index, r, g, b);
}

// DC有効化
GT_DIB::apply_dc(HDC hdc){
    if (m_hdc) { DeleteDC(m_hdc);   m_hdc=NULL; }
    m_hdc = CreateCompatibleDC(hdc);
    SelectObject(m_hdc, m_hbmp);
    return 0;
}
// DC有効化(HWND hwnd) 
GT_DIB::apply_dc(HWND hwnd){
    if (m_hdc) { DeleteDC(m_hdc);   m_hdc=NULL; }
    HDC hwnd_hdc = GetDC(hwnd);
    m_hdc = CreateCompatibleDC(hwnd_hdc);
    SelectObject(m_hdc, m_hbmp);
    ReleaseDC(hwnd, hwnd_hdc);
    return 0;
}

// 破棄
GT_DIB::destroy(){
    if (m_hdc)      { DeleteDC(m_hdc);  m_hdc=NULL; }
    if (m_hbmp)     { DeleteObject(m_hbmp); m_hbmp=NULL; }
//  if (m_pbits) delete[] m_pbits;
    m_pbits = NULL;
    m_width=0;
    m_height=0;
    m_byte_width=0;
    m_bit_count=0;
    if (m_col_mask) delete[] m_col_mask;
    m_col_mask = NULL;
    m_col_table.destroy();
    return 0;
}
// 作成
GT_DIB::create(int cx, int cy, int a_bit_count, int a_color_size){
    BITMAPINFO *bmp_info;
    int i, palette_entry, info_size;
    destroy();  // 作成済みメモリを破棄する

    if (cx<=0 || cy<=0) return -1;
    if (a_bit_count!=1 && a_bit_count!=4 && a_bit_count!=8
        && a_bit_count!=16 && a_bit_count!=24 && a_bit_count!=32) return -1;

    palette_entry=1;
    if      (a_color_size!=0) palette_entry=a_color_size;
    else if (a_bit_count<=8)  palette_entry=1<<a_bit_count; // 8bit以下でパレット色数指定無しなら

    info_size=sizeof(BITMAPINFOHEADER) + (palette_entry-1)*sizeof(RGBQUAD)+1;
    bmp_info = (BITMAPINFO*)malloc(info_size);
    if (!bmp_info) return -1;
    ZeroMemory(bmp_info, info_size);

    // 適当な基本色
    RGBTRIPLE rgb[8]={
        {0,0,0},{255, 255, 255}, {0,0,255}, {255,0,0}, {0,255,0}, {255, 255,0}, {255,0,255},{0,255,255}
    };

    m_col_table.make(palette_entry);
    for(i=0;i<min(palette_entry, 8);i++){
        bmp_info->bmiColors[i].rgbBlue  = rgb[i].rgbtBlue;
        bmp_info->bmiColors[i].rgbGreen = rgb[i].rgbtGreen;
        bmp_info->bmiColors[i].rgbRed   = rgb[i].rgbtRed;
        bmp_info->bmiColors[i].rgbReserved = 0;
        m_col_table.add_col(rgb[i].rgbtRed, rgb[i].rgbtGreen, rgb[i].rgbtBlue);
    }

    /* BITMAPINFO構造体設定 */
    bmp_info->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmp_info->bmiHeader.biWidth=cx;
    bmp_info->bmiHeader.biHeight=cy;
    bmp_info->bmiHeader.biPlanes=1;
    bmp_info->bmiHeader.biBitCount=a_bit_count;
    bmp_info->bmiHeader.biCompression=BI_RGB;
    bmp_info->bmiHeader.biSizeImage=0;
    bmp_info->bmiHeader.biXPelsPerMeter=0;
    bmp_info->bmiHeader.biYPelsPerMeter=0;
    bmp_info->bmiHeader.biClrUsed=palette_entry;
    bmp_info->bmiHeader.biClrImportant=0;

    m_hbmp = CreateDIBSection(NULL, bmp_info, DIB_RGB_COLORS, (VOID**)&m_pbits, NULL, 0);
    if (!m_hbmp) {
        free(bmp_info);
        return -1;
    }
    m_width= cx;
    m_height=cy;
    m_bit_count=a_bit_count;

    /* バッファの1ラインの長さを計算 */
    m_byte_width=4*((m_width*m_bit_count+31)/32);
    ZeroMemory(m_pbits, m_byte_width*m_height);

    free(bmp_info);
    return 0;
}



gt_img_bmp_io.cpp

#include "gt_img_04.h"

// DIB 読込
GT_IMG::load_dib(char *filename){
    BITMAPFILEHEADER bmp_file_header;
    BITMAPINFO *bmp_info;
    DWORD res_byte;
    int i, bmp_info_size;
    destroy();  // 破棄

    // ファイルオープン
    HANDLE hfile = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hfile == INVALID_HANDLE_VALUE) return -1;

    // 識別子'BM'
    ReadFile(hfile, &bmp_file_header, sizeof(BITMAPFILEHEADER), &res_byte, NULL);
    if (bmp_file_header.bfType != 0x4D42)   {
        CloseHandle(hfile);
        return -1;
    }

    // ヘッダタイプ判定・読込
    bmp_info_size = bmp_file_header.bfOffBits - res_byte;
    bmp_info = (BITMAPINFO *) malloc (bmp_info_size);
    ReadFile(hfile, bmp_info, bmp_info_size, &res_byte, NULL);

    // 生成
    BITMAPINFOHEADER *p_bmih=&bmp_info->bmiHeader;
    int col_len = bmp_info->bmiHeader.biClrUsed;
    if (col_len==0 && p_bmih->biBitCount<=8) col_len = 1 << p_bmih->biBitCount;
    if (create(p_bmih->biWidth, p_bmih->biHeight, p_bmih->biBitCount, p_bmih->biClrUsed)<0) {
        CloseHandle(hfile);
        free(bmp_info);
        return -1;
    }

    // カラーテーブル
    RGBQUAD *prgb=bmp_info->bmiColors;
    for(i=0;i<col_len;i++) set_pal_col(i, prgb[i].rgbRed, prgb[i].rgbGreen, prgb[i].rgbBlue);
    if (bmp_info->bmiHeader.biClrImportant==0) bmp_info->bmiHeader.biClrImportant = col_len;
    m_col_table.m_using_cols_len = bmp_info->bmiHeader.biClrImportant;

    int res=0;
    // 圧縮判定・データ読込
    switch(p_bmih->biCompression){
    case BI_BITFIELDS :
        if ((m_bit_count==16 || m_bit_count==32) && bmp_info_size>=sizeof(BITMAPV4HEADER)) {
            BITMAPV4HEADER *p_bmp_v4=(BITMAPV4HEADER*)p_bmih;
            set_col_mask(p_bmp_v4->bV4RedMask, p_bmp_v4->bV4GreenMask, p_bmp_v4->bV4BlueMask);
        }
        break;
    case BI_RLE4 :  // 4bit ランレングス圧縮
        res = load_dib_rle4(hfile);
        break;
    case BI_RLE8 :  // 8bit ランレングス圧縮
        res = load_dib_rle8(hfile);
        break;
    case BI_RGB :
        ReadFile(hfile, m_pbits, bmp_file_header.bfSize - bmp_file_header.bfOffBits, &res_byte, NULL);
        if ((bmp_file_header.bfSize - bmp_file_header.bfOffBits)!=res_byte) res=-1;
        break;
    default :
        res=-1;
    }

    CloseHandle(hfile);

    free(bmp_info);
    return res;
}
// DIB 読込 4bit ランレングス圧縮
GT_IMG::load_dib_rle4(HANDLE hfile){
    DWORD res_byte;
    PIXEL_ITR<4,0> pix;
    int i, ix, iy, loop_flag=1;
    BYTE code_1, code_2, code_3, code_4;
    pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);
    pix.m_pbits = m_pbits;  // 先頭ビット合わせ == pix.set_pos(0, m_height-1);

    ix=0; iy=m_height-1;
    while(loop_flag){
        ReadFile(hfile, &code_1, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
        ReadFile(hfile, &code_2, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
        switch(code_1){
        case 0:
            switch(code_2){
            case 0: // 行末
                ix=0; iy--;
                if (iy<0) { loop_flag=0; break; }
                pix.set_pos(ix, iy);
                break;
            case 1: // イメージの末尾
                loop_flag = 0;
                break;
            case 2: // 座標移動
                ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                ReadFile(hfile, &code_4, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                ix+=code_3; iy-=code_4;
                pix.set_pos(ix, iy);
                break;
            default:    // 後ろに続くn ピクセルをそのまま使う
                for(i=0;i<code_2;i++){
                    if ((i%2)==0) {
                        ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                    }
                    code_4 = (i&1)? code_3 : code_3>>4;
                    pix.set_col(0x0f & code_4);
                    pix++; ix++;
                }
                i=(code_2/2)%2;
                i+=code_2%2;
                if (i%2) {  // 2バイト境界判定
                    ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                }
                break;
            }
            break;
        default:    // code_1==01〜ff
            for(i=0;i<code_1;i++){
                code_4 = (i&1)? code_2 : code_2>>4;
                pix.set_col(0x0f & code_4);
                pix++; ix++;
            }
            break;
        }
    }
    return 0;
}
// DIB 読込 8bit ランレングス圧縮
GT_IMG::load_dib_rle8(HANDLE hfile){
    DWORD res_byte;
    int i, ix, iy, loop_flag=1;

    // 反復子セット
    PIXEL_ITR<8,0> pix;
    pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);
    pix.m_pbits = m_pbits;  // 先頭ビット合わせ == pix.set_pos(0, m_height-1);

    BYTE code_1, code_2, code_3, code_4;
    ix=0; iy=m_height-1;;
    while(loop_flag){
        ReadFile(hfile, &code_1, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
        ReadFile(hfile, &code_2, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
        switch(code_1){
        case 0:
            switch(code_2){
            case 0: // 行末
                ix=0; iy--;
                if (iy<0) { loop_flag=0; break; }
                pix.set_pos(ix, iy);
                break;
            case 1: // 画像の終端
                loop_flag = 0;
                break;
            case 2: // 座標移動
                ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                ReadFile(hfile, &code_4, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                ix+=code_3; iy-=code_4;
                pix.set_pos(ix, iy);
                break;
            default:    // 後ろに続くn ピクセルをそのまま使う
                for(i=0;i<code_2;i++){
                    ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;

                    *pix.m_pbits=code_3;
                    pix++; ix++;
                }
                if (code_2%2==1) {  // 2バイト境界判定
                    ReadFile(hfile, &code_3, 1, &res_byte, NULL);   if (res_byte<=0) return -1;
                }
                break;
            }
            break;
        default:    // code_1==01〜ff
            for(i=0;i<code_1;i++){
                pix.set_col(code_2);
                pix++; ix++;
            }
            break;
        }
    }
    return 0;
}

// DIB 保存 4bit ランレングス圧縮(コード書込み)
save_dib_rle4_put_pix(int hold_len, HANDLE hfile, PIXEL_ITR<4,0> &write_pix, int check_mode){
    // 書込み
    int i, total_write_size=0;
    enum { NO_MODE, SAME_MODE };
    DWORD res_byte;
    BYTE write_code[4];
    switch(check_mode){
    case NO_MODE :
        if (hold_len<=2){ // 単純ピクセル書込み
            write_code[0] = 2;      // 第1バイト
            for(i=0;i<(hold_len/2);i++){
                write_code[1] = write_pix.get_index() << 4; // 第2バイト(hi)
                write_pix++;
                write_code[1] |= write_pix.get_index(); // 第2バイト(low)
                write_pix++;
                if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
                total_write_size += 2;
            }
            write_code[0] = 1;      // 第1バイト
            if (hold_len%2){
                write_code[1] = write_pix.get_index() << 4; // 第2バイト(hi)
                write_pix++;
                if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
                total_write_size += 2;
            }
        }
        else { // 不規則ピクセルモード(3ピクセル以上)
            write_code[0] = 0;              // 第1バイト
            write_code[1] = (BYTE)hold_len; // 第2バイト
            WriteFile(hfile, write_code, 2, &res_byte, NULL);
            total_write_size += 2;
            // 第3バイト以降
            for(i=0;i<(hold_len/2);i++){
                write_code[0] = write_pix.get_index() << 4; // 第2バイト(hi)
                write_pix++;
                write_code[0] |= write_pix.get_index(); // 第2バイト(low)
                write_pix++;
                if (hfile) WriteFile(hfile, write_code, 1, &res_byte, NULL);
                total_write_size += 1;
            }
            if (hold_len%2){
                write_code[0] = write_pix.get_index() << 4; // 第2バイト(hi)
                write_pix++;
                if (hfile) WriteFile(hfile, write_code, 1, &res_byte, NULL);
                total_write_size += 1;
            }

            write_code[0] = 0;              // 第1バイト
            i=(hold_len/2)%2;
            i+=hold_len%2;
            if (i%2) {
                if (hfile) WriteFile(hfile, write_code, 1, &res_byte, NULL);    // 2バイト境界判定
                total_write_size += 1;
            }
        }
        break;
    case SAME_MODE : // 同値ピクセルモード書込み
        write_code[0] = (BYTE)hold_len; // 第1バイト
        write_code[1] = write_pix.get_index() << 4; // 第2バイト(hi)
        write_pix++;
        write_code[1] |= write_pix.get_index(); // 第2バイト(low)
        write_pix++;
        if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
        total_write_size += 2;
        write_pix += (hold_len-2);
        break;
    }
    return total_write_size;
}
// DIB 保存 4bit ランレングス圧縮
GT_IMG::save_dib_rle4(HANDLE hfile){
    enum { NO_MODE, SAME_MODE };
    BYTE c=0, stack_pix[5], stack_len=0, write_code[4];
    int i, ix, iy, check_mode, hold_len=0, total_write_size=0;
    check_mode = NO_MODE;
    DWORD res_byte;

    // 反復子セット
    PIXEL_ITR<4,0> write_pix, check_pix;
    write_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);    // 書込み用反復子
    check_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);    // チェック用反復子

    // 圧縮メイン
    for(iy=m_height-1;iy>=0;iy--){
        write_pix.set_pos(0, iy);       // 書込み用反復子
        check_pix.set_pos(0, iy);       // チェック用反復子

        hold_len = stack_len = 0;   // 保留ピクセル数をゼロにする
        check_mode = NO_MODE;   // スタックも保留ピクセル数もゼロなので "不定ピクセルモード"

        for(ix=0;ix<(m_width);ix++){
            // 第1第2ピクセルゲット
            stack_pix[stack_len] = check_pix.get_index();
            check_pix++;        // 反復子インクリメント
            stack_len++;        // スタックに積んでるピクセル数
            hold_len++; // 保留ピクセル数

            // 255オーバー
            if (hold_len>=255) {
                total_write_size += save_dib_rle4_put_pix(hold_len, hfile, write_pix, check_mode); // 保留ピクセル全部 書込み
                hold_len = stack_len = 0;   // 全部書き込んだので保留ピクセル数をゼロにする
                check_mode = NO_MODE;   // スタックも保留ピクセル数もゼロなので "不定ピクセルモード"
                continue;
            }
            if (stack_len<3) continue;

            // ピクセル判定
            switch(check_mode){
            case NO_MODE :  // 不定ピクセルモード
                if (stack_pix[0] == stack_pix[2]) {
                    // 不定ピクセルモード終了判定
                    total_write_size += save_dib_rle4_put_pix(hold_len-3, hfile, write_pix, check_mode); // 決まった分だけ書込み
                    hold_len = 3;   // stack_pix[0] 〜 stack_pix[2] は保留ピクセルへ
                    check_mode = SAME_MODE; // モード切替
                    stack_len = 2;  // 判定が済んだのでスタックに積んでるピクセル数を1個だけにもどす
                    break;
                }
                stack_pix[0] = stack_pix[1];    // 次回のstack_pix[0] != stack_pix[1] 比較のため
                stack_pix[1] = stack_pix[2];
                stack_len = 2;  // 判定が済んだのでスタックに積んでるピクセル数を1個だけにもどす
                break;
            case SAME_MODE :    // 同値ピクセルモード
                if (
                    (((hold_len%2)==0) && (stack_pix[0] != stack_pix[2]))
                ||  ((hold_len%2) && (stack_pix[1] != stack_pix[2]))
                    ) {
                    // 同値ピクセルモード終了判定
                    total_write_size += save_dib_rle4_put_pix(hold_len-1, hfile, write_pix, check_mode); // 決まった分だけ書込み
                    stack_pix[0] = stack_pix[2];    // stack_pix[2] は保留
                    hold_len = 1;
                    check_mode = NO_MODE;   // モード切替
                    stack_len = 1;
                }
                else stack_len = 2; // 判定が済んだのでスタックに積んでるピクセル数を1個だけにもどす
                break;
            }
        }

        // 行末コード
        total_write_size += save_dib_rle4_put_pix(hold_len, hfile, write_pix, check_mode); // 保留ピクセル全部 書込み
        write_code[0] = write_code[1] = 0;  // 第1バイト 第2バイト
        if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);    // 行末コード(00 00)
        total_write_size += 2;
    }

    // 画像の終端
    write_code[0] = 0;  // 第1バイト
    write_code[1] = 1;  // 第2バイト
    if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
    return total_write_size;
}
// DIB 保存 8bit ランレングス圧縮(コード書込み)
save_dib_rle8_put_pix(int hold_len, HANDLE hfile, PIXEL_ITR<8,0> &write_pix, int check_mode){
    // 書込み
    int i;
    enum { NO_MODE, SAME_MODE };
    DWORD res_byte;
    BYTE write_code[4];
    int total_write_size=0;

    switch(check_mode){
    case NO_MODE :
        if (hold_len<=2){ // 単純ピクセル書込み
            for(i=0;i<hold_len;i++){
                write_code[0] = 1;      // 第1バイト
                write_code[1] = *write_pix.m_pbits; // 第2バイト
                if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
                total_write_size += 2;
                write_pix++;
            }
        }
        else { // 不規則ピクセルモード(3ピクセル以上)
            write_code[0] = 0;                      // 第1バイト
            write_code[1] = (BYTE)hold_len; // 第2バイト
            if (hfile) {
                WriteFile(hfile, write_code, 2, &res_byte, NULL);
                WriteFile(hfile, write_pix.m_pbits, hold_len, &res_byte, NULL); // 第3バイト以降
                if (hold_len%2==1) WriteFile(hfile, write_code, 1, &res_byte, NULL);    // 2バイト境界判定
            }
            total_write_size += 2 + hold_len + hold_len%2;
            write_pix += hold_len;
        }
        break;
    case SAME_MODE : // 同値ピクセルモード書込み
        write_code[0] = (BYTE)hold_len; // 第1バイト
        write_code[1] = *write_pix.m_pbits; //stack_pix[0];         // 第2バイト
        if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
        write_pix += hold_len;
        total_write_size += 2;
        break;
    }
    return total_write_size;
}
// DIB 保存 8bit ランレングス圧縮
GT_IMG::save_dib_rle8(HANDLE hfile){
    enum { NO_MODE, SAME_MODE };
    BYTE c=0, stack_pix[5], stack_len=0, write_code[4];
    int i, ix, iy, check_mode, hold_len=0;
    check_mode = NO_MODE;
    DWORD res_byte;
    int total_write_size=0;

    // 反復子セット
    PIXEL_ITR<8,0> write_pix, check_pix;
    write_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);    // 書込み用反復子
    check_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);    // チェック用反復子

    // 圧縮メイン
    for(iy=m_height-1;iy>=0;iy--){
        write_pix.set_pos(0, iy);       // 書込み用反復子
        check_pix.set_pos(0, iy);       // チェック用反復子

        hold_len = stack_len = 0;   // 全部書き込んだので保留ピクセル数をゼロにする
        check_mode = NO_MODE;   // スタックも保留ピクセル数もゼロなので "不定ピクセルモード"

        for(ix=0;ix<m_width;ix++){
            // 第1第2ピクセルゲット
            stack_pix[stack_len] = *check_pix.m_pbits;
            check_pix++;        // 反復子インクリメント
            stack_len++;        // スタックに積んでるピクセル数
            hold_len++; // 保留ピクセル数

            // 255オーバー
            if (hold_len>=255) {
                total_write_size += save_dib_rle8_put_pix(hold_len, hfile, write_pix, check_mode); // 保留ピクセル全部 書込み
                hold_len = stack_len = 0;   // 全部書き込んだので保留ピクセル数をゼロにする
                check_mode = NO_MODE;   // スタックも保留ピクセル数もゼロなので "不定ピクセルモード"
                continue;
            }
            if (stack_len<2) continue;

            // ピクセル判定
            switch(check_mode){
            case NO_MODE :  // 不定ピクセルモード
                if (stack_pix[0] == stack_pix[1]) {
                    // 不定ピクセルモード終了判定
                    total_write_size += save_dib_rle8_put_pix(hold_len-2, hfile, write_pix, check_mode); // 決まった分だけ書込み
                    hold_len = 2;   // stack_pix[0] stack_pix[1] は保留
                    check_mode = SAME_MODE; // モード切替
                }
                stack_pix[0] = stack_pix[1];    // 次回のstack_pix[0] != stack_pix[1] 比較のため
                break;
            case SAME_MODE :    // 同値ピクセルモード
                if (stack_pix[0] != stack_pix[1]) {
                    // 同値ピクセルモード終了判定
                    total_write_size += save_dib_rle8_put_pix(hold_len-1, hfile, write_pix, check_mode); // 決まった分だけ書込み
                    stack_pix[0] = stack_pix[1];    // stack_pix[1] は保留
                    hold_len = 1;
                    check_mode = NO_MODE;   // モード切替
                }
                break;
            }
            stack_len = 1;  // 判定が済んだのでスタックに積んでるピクセル数を1個だけにもどす
        }

        // 行末コード
        total_write_size += save_dib_rle8_put_pix(hold_len, hfile, write_pix, check_mode); // 保留ピクセル全部 書込み
        write_code[0] = write_code[1] = 0;  // 第1バイト 第2バイト
        if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);    // 行末コード(00 00)
        total_write_size += 2;
    }

    // 画像の終端
    write_code[0] = 0;  // 第1バイト
    write_code[1] = 1;  // 第2バイト
    if (hfile) WriteFile(hfile, write_code, 2, &res_byte, NULL);
    total_write_size += 2;

    return total_write_size;
}

// DIBセーブ
GT_IMG::save_dib(char *filename, int rle_compress_flag){
    if (!m_pbits) return -1;
    BITMAPFILEHEADER bmp_file_header;
    BITMAPINFOHEADER bmp_info_header;
    DWORD res_bytes;
    int i, total_size, pixel_array_size, col_size;

    total_size=40;      // 構造体サイズ==40==ds.dsBmih.biSize
    if (m_col_mask) total_size+= 3*sizeof(DWORD);   // 固有カラーマスク (ds.dsBmih.biCompression==BI_BITFIELDS)

    // パレットサイズ
    total_size+=(m_col_table.m_cols_len*sizeof(RGBQUAD));

    // ピクセル配列サイズ
    pixel_array_size=m_byte_width*m_height; // 通常サイズ
    if (rle_compress_flag) {    // RLE 圧縮 ヘッダ・セット
        switch(m_bit_count){
        case 4 : pixel_array_size=save_dib_rle4(NULL); break;
        case 8 : pixel_array_size=save_dib_rle8(NULL); break;
        }
    }
    total_size+=pixel_array_size;

    bmp_file_header.bfType = *(WORD*) "BM";
    bmp_file_header.bfSize = sizeof(BITMAPFILEHEADER) + total_size;
    bmp_file_header.bfReserved1=0;
    bmp_file_header.bfReserved2=0;
    bmp_file_header.bfOffBits = bmp_file_header.bfSize - pixel_array_size;

    bmp_info_header.biSize=40;                  // 構造体サイズ==40
    bmp_info_header.biWidth=m_width;            // 画像横幅
    bmp_info_header.biHeight=m_height;          // 画像高さ
    bmp_info_header.biPlanes=1;                 // ==1(Windows初期の名残)
    bmp_info_header.biBitCount=m_bit_count;     // カラービット数(1, 4, 8, 16, 24, 32)
    bmp_info_header.biCompression=BI_RGB;       // 圧縮コード(BI_RGB, BI_RLE4, BI_RLE8, BI_BITFIELDSのいずれか)
    bmp_info_header.biSizeImage=pixel_array_size;   // イメージのバイト数
    bmp_info_header.biXPelsPerMeter=0;          // 水平解像度
    bmp_info_header.biYPelsPerMeter=0;          // 垂直解像度
    bmp_info_header.biClrUsed=m_col_table.m_cols_len;   // カラーテーブルの数
    bmp_info_header.biClrImportant=m_col_table.m_using_cols_len; // 重要な色の数

    HANDLE hfile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hfile == INVALID_HANDLE_VALUE) return 0;

    if (rle_compress_flag) {    // RLE 圧縮 ヘッダ・セット
        switch(m_bit_count){
        case 4 : bmp_info_header.biCompression=BI_RLE4; break;
        case 8 : bmp_info_header.biCompression=BI_RLE8; break;
        }
    }

    WriteFile(hfile, &bmp_file_header, sizeof (BITMAPFILEHEADER), &res_bytes, NULL);
    WriteFile(hfile, &bmp_info_header, sizeof (BITMAPINFOHEADER), &res_bytes, NULL);

    if (bmp_info_header.biCompression==BI_BITFIELDS)
        WriteFile(hfile, m_col_mask, 3*sizeof(DWORD), &res_bytes, NULL);

    col_size = m_col_table.m_cols_len;
    RGBQUAD rgb;
    rgb.rgbReserved=0;
    if (col_size>=0) {
        for(i=0;i<col_size;i++){
            rgb.rgbRed   = m_col_table.m_cols[i].m_r;
            rgb.rgbGreen = m_col_table.m_cols[i].m_g;
            rgb.rgbBlue  = m_col_table.m_cols[i].m_b;
            WriteFile(hfile, &rgb, sizeof(RGBQUAD), &res_bytes, NULL);
        }
    }

    // ピクセル・ビット書き込み
    switch(bmp_info_header.biCompression){
    case BI_RLE4 : save_dib_rle4(hfile); break; // 4bit RLE 圧縮
    case BI_RLE8 : save_dib_rle8(hfile); break; // 8bit RLE 圧縮
    default : WriteFile(hfile, m_pbits, pixel_array_size, &res_bytes, NULL);
    }
    CloseHandle(hfile);
    return 0;
}



gt_img_04.cpp
// ビットマップ 読込

#include <stdio.h>
#include <windows.h>
#include "resource_04.h"
#include "gt_img_04.h"

GT_DIB gt_dib_1;
static OPENFILENAME ofn = {0};
static char filename[MAX_PATH];

// メニュー
on_command(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
    static CHOOSECOLOR cc = {0};
    static DWORD col_costom[8] = {0};
    switch(LOWORD(wp)) {
    case IDM_MAKE_DIB_0:
    case IDM_MAKE_DIB_1:
    case IDM_MAKE_DIB_4:
    case IDM_MAKE_DIB_8:
    case IDM_MAKE_DIB_16:
    case IDM_MAKE_DIB_24:
    case IDM_MAKE_DIB_32:
        gt_dib_1.create(201, 151, LOWORD(wp)-IDM_MAKE_DIB_0, 0);
        gt_dib_1.apply_dc(hwnd);
        InvalidateRect(hwnd, NULL, 0);
        break;
    case IDM_OPEN:
        ofn.Flags = OFN_FILEMUSTEXIST;
        if (!GetOpenFileName(&ofn)) break;
        gt_dib_1.load_dib(filename);
        gt_dib_1.apply_dc(hwnd);
        gt_dib_1.apply_palette_all();
        InvalidateRect(hwnd, NULL, 1);
        break;
    case IDM_SAVE_DIB:
        ofn.Flags = 0;
        if (!GetSaveFileName(&ofn)) break;
        gt_dib_1.save_dib(filename);
        break;
    case IDM_SAVE_DIB_RLE:
        ofn.Flags = 0;
        if (!GetSaveFileName(&ofn)) break;
        gt_dib_1.save_dib(filename, 1);
        break;
    }
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
    POINT po;
    static char s[50];
    RECT rc;

    switch (msg) {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (gt_dib_1.m_hdc){
            BitBlt( hdc, 0, 0, gt_dib_1.m_width, gt_dib_1.m_height, gt_dib_1.m_hdc, 0, 0, SRCCOPY);
        }
        sprintf(s, "  ヨコ %d X タテ %d  %dビット",
                gt_dib_1.m_width, gt_dib_1.m_height, gt_dib_1.m_bit_count);
        GetClientRect(hwnd , &rc);
        rc.top=gt_dib_1.m_height;
        DrawText(hdc, s, -1, &rc, DT_WORDBREAK );
        EndPaint(hwnd, &ps);
        break;
    case WM_COMMAND:
        on_command(hwnd, msg, wp, lp);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_CREATE:
        ofn.lStructSize = sizeof (OPENFILENAME);
        ofn.hwndOwner = hwnd;
        ofn.lpstrFilter =   
                    TEXT("bmp files {*.bmp}\0*.bmp\0")
                    TEXT("All files {*.*}\0*.*\0\0");
        ofn.lpstrCustomFilter = NULL;
        ofn.nMaxCustFilter = 0;
        ofn.nFilterIndex = 0;
        ofn.lpstrFile = filename;
        ofn.nMaxFile = MAX_PATH;
        ofn.Flags = OFN_FILEMUSTEXIST;
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,    PSTR lpCmdLine, int nCmdShow) {
    HWND hwnd;
    MSG msg;
    WNDCLASS winc;

    winc.style      = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc    = WndProc;
    winc.cbClsExtra = winc.cbWndExtra   = 0;
    winc.hInstance      = hInstance;
    winc.hIcon      = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor        = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName   = TEXT("GT_DIB_MENU");
    winc.lpszClassName  = TEXT("GT_IMG");

    if (!RegisterClass(&winc)) return 1;

    hwnd = CreateWindow(
            TEXT("GT_IMG"), TEXT("ビットマップ 読込・保存"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
            CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, 
            NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) return 1;

    while (GetMessage(&msg, NULL, 0, 0 )) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}


 007-03  前へ←  ホームへ  →次へ  007-05