007-06  前へ←  ホームへ  →次へ  007-08

No.007 - 07  直線描画


Bresenham(ブレゼンハム)の直線描画を行います。
Jack Bresenham さんはアメリカのWinthrop(ウィンスロップ)大学のコンピュータ科学の教授だそうです。
(ちなみにBresenhamアルゴリズムをgoogleで検索するとかなりコード間違えてるサイトが上位にくるので注意してね。 2005.5)


直線描画
直線描画において、一般的な数学の公式を使うと、傾きなどを求めるときにどうしても誤差がでてしまいます。
また誤差がでないように全長からの比率で位置計算を行うと、割り算を使う都合上、非常に遅いルーチンになります。

なのでブレゼンハム方式で直線描画を行います。
ブレゼンハム方式は全てが整数演算で、割り算もないので非常に正確高速です。


Bresenham(ブレゼンハム)の考え方
たぶんBresenhamは最初にこんなことを考えたはず。

最も一般的な数学の公式。
  …数学の公式。

数学の実数ではこれもう満点です。



でも
デジタル描画では完全な整数演算なので、こんなことしなくても

ずつ増やして、
その分小数分増やしていけばいいじゃん。
ってことです。XY逆もまたしかり。

x が1増えると y は  y2-y1  だけ増えます
x2-x1

hh = y2-y1
ww = x2-x1
とすれば直線描画のルーチンは
それぞれ整数値での
座標がわかればOK
草案1
// ww >= hh のとき
double e=0;
while(1){
       x++;
       e += hh/ww;
       if (e>=1){
              e -= 1;
              y++;
        }
}
となるはず。
でも e += hh/ww;小数演算だから誤差でまくり。

xが1増えたら
e がhh/ww分増える
またxが1増えたら
e がhh/ww分増える
eが満タン(=1)
になったら
y を1個増やす


ここでBresenhamは考えた。
「割り算無くしゃあ誤差でねえじゃん」・・・・・(←語弊あり)
そうだ、e += hh/ww;両辺にww 掛けてやればいいんだ!!。

草案2
// ww >= hh のとき
double e=0;
while(1){
       x++;
       e += hh;
       if (e>=ww){
              e -= ww;
              y++;
        }
}
これで整数演算のみとなり、誤差なし、演算も高速になりました。
xが1増えたら
e がhh分増える
eが満タン(==ww)に
なったら
y を1個
増やす

…要は0.1 ずつ足して1になったかどうか調べるんじゃなくて、1ずつ足していって10になったかどうか調べるやりかたと同じ。


四捨五入
上記の草案2は四捨五入を考えてません。
単純に0.4999…まで切り捨て、0.5で切り上げればいいだけなので、
草案2の e の初期値を 0.5 (=ww/2) にしてやればいいだけです。e=0e=ww/2; です。

でも ww/2 は端数があるとき誤差がでます(でても1ですが)。
なので上例のように割り算なくします。
四捨五入
// ww >= hh のとき
double e=ww;
while(1){
       x++;
       e += 2*hh;
       if (e>=2*ww){
              e -= 2*ww;
              y++;
        }
}
e の初期値を ww にしてww, hh の値を2倍にしました。四捨五入ができました。





直線描画の範囲補正
なぜかどこのサイトもやってない範囲補正やります。
通常描画のx, y の初期値はx1, y1です。
x = x1;
y = y1;
ですが x1, y1がマイナス値だったり画像幅以上の値だったりすることもあるはずなのでその初期値を補正してやります。
当然数学の公式なんぞで補正すれば誤差がでます。端っこ1ピクセル分オーバーしたり届かなかったり。(余算が必要なのです)


上記の四捨五入のサンプルで、初期値を

x = x1 + start;

としてやると e は x が1進めば e += 2*hh、2進めば e += 4*hh、
なので

e = start * 2hh + ww;        // ww は四捨五入の分

そして y は e が (2ww)増えれば y += 1、(4ww)増えれば y += 2、
なので

y = y1 + e/2ww;

e = e%2ww;

と補正されていきます。
これで y が範囲内に収まればいいのですが右図のような場合、
この値はA点であり、条件を満たしません。
なので B点の座標を出します。

<図-2>
A点、B点の位置をドット的に求めたい

y = y1 + B_y;       // ここではB_y は-y1


上記ルーチン上、
start*2hh+ww>=B_y*2ww;
を満たせばいいので、計算して

start>=(B_y*2ww-ww)/2hh;
から

start = (B_y*2ww-ww)/2hh;
if ((B_y*2ww-ww)%2hh)) start++;
というコードができます。


(参考1)

傾きは同じなので
hh/ww==h1/w1
また w1/ww==h1/hh も成立


範囲補正(x座標)
// ww >= hh のとき
if (x1<0) start = -x1;
if (x1>=img_width) start = x1 - img_width +1;
はい、x座標の補正はカンタンですね。
(図-2中のA点になりました)

さらにここで得られた初期値はy座標も満たすのかチェックします。
範囲補正つづき(y座標)
// ww >= hh のとき
tmp = B_y + (start * 2hh + ww)/2ww;
if (tmp<0 || tmp>=img_height){
        if (tmp<0) { B_y = -y1; }
        else if (tmp>=img_height) { B_y = y1-img_height+1; }

        start = (B_y*2ww-ww)/2hh;
        if ((B_y*2ww-ww)%2hh) start++;
}
x座標で補正した初期値でのy値を
一度 tmp 変数に入れてチェックします。
範囲外だったら B_y に y の初期値を入れ x座標の初期値を上述の式を使って求めます。


以上をまとめるとこういうコードができあがります。
まとめ
// ww >= hh のとき
double e=ww;
while(1){
       x++;
       e += 2*hh;
       if (e>=2*ww){
              e -= 2*ww;
              y++;
        }
}

if (x1<0) start = -x1;
if (x1>=img_width) start = x1 - img_width +1;

tmp = B_y + (start * 2hh + ww)/2ww;
if (tmp<0 || tmp>=img_height){
        if (tmp<0) { B_y = -y1; }
        else if (tmp>=img_height) { B_y = y1-img_height+1; }

        start = (B_y*2ww-ww)/2hh;
        if ((B_y*2ww-ww)%2hh) start++;
}


これと同様に、終了点x2,y2から求め、
全長から逆算してやります。

上記中の start 変数を tmp_end に置き換えて最後に
end = ww - tmp_end ;
とします。

(参考2)

x2, y2 が範囲外のとき





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

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

gt_img_parts.hファイルは007-02のものと全く同じものを使います。ここには書きませんのでご注意ください。
gt_img_bmp_io.cppファイルは007-04のモノの中身は同じものを使います。ここには書きませんのでご注意ください。
#include "gt_img_04.h"  を   #include "gt_img_07.h"   に変えてください)




ドラッグで線を引くことができます。
このように画像の範囲外を引いても…
きちんと描画前に範囲補正して
描画します
色々な直線が引けます






resource_07.h

// 直線描画

#include "resource_07.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_COL
}



gt_img_07.rc

// 直線描画

#include "resource_07.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_COL
}


gt_img_07.h

// 直線描画

#include <stdio.h>
#include <windows.h>
#include "resource_07.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);

    // 直線描画
    draw_line(int sx, int sy, int ex, int ey, BYTE r, BYTE g, BYTE b, int rate);
    template <int d_bit> void draw_line_pixput(int sx, int sy, int ex, int ey, BYTE r, BYTE g, BYTE b, int rate);
    adjust_lining_limit(int *p_start, int *p_end, int sx, int sy, int ex, int ey, int *p_cx, int *p_cy);

    // アルファ値
    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::draw_line(int sx, int sy, int ex, int ey, BYTE r, BYTE g, BYTE b, int rate){
    if (!m_pbits) return -1;
    if (sx==ex && sy==ey) return -1;

    // 描画
    switch(m_bit_count){
    case 1 : draw_line_pixput<1> (sx, sy, ex, ey, r, g, b, rate);    break;
    case 4 : draw_line_pixput<4> (sx, sy, ex, ey, r, g, b, rate);    break;
    case 8 : draw_line_pixput<8> (sx, sy, ex, ey, r, g, b, rate);    break;
    case 16: draw_line_pixput<16>(sx, sy, ex, ey, r, g, b, rate);    break;
    case 24: draw_line_pixput<24>(sx, sy, ex, ey, r, g, b, rate);    break;
    case 32: draw_line_pixput<32>(sx, sy, ex, ey, r, g, b, rate);    break;
    }
    return 0;
}

// 直線描画のインライン・テンプレート(RGB版)
template <int d_bit> void 
GT_IMG::draw_line_pixput(int sx, int sy, int ex, int ey, BYTE r, BYTE g, BYTE b, int rate){
    int cx, cy, pos_x, pos_y, i, lim, start, end;
    PIXEL_ITR<d_bit,0> d_pix;
    if (rate<0) rate=0;
    if (rate>=255) rate=255;
    d_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);
    int col_index;
    double e;
    int *p_co1, *p_co2, ddis1, ddis2, dn1, dn2;
    if (adjust_lining_limit(&start, &end, sx, sy, ex, ey, &cx, &cy)<0) return;

    int base_axis = (abs(cx)>abs(cy))? 'x' : 'y';
    p_co1 = (base_axis=='x')? &pos_x : &pos_y;
    p_co2 = (base_axis=='x')? &pos_y : &pos_x;
    ddis1 = (base_axis=='x')? 2*cx : 2*cy;
    ddis2 = (base_axis=='x')? 2*cy : 2*cx;
    dn1 = dn2 = 1;
    if (ddis1<0) { dn1=-1; ddis1 = -ddis1; }
    if (ddis2<0) { dn2=-1; ddis2 = -ddis2; }
    pos_x = sx;
    pos_y = sy;
    *p_co1 += dn1* start;
    *p_co2 += dn2* (start*ddis2+ddis1/2) /ddis1;
    e = (ddis1/2 + start*ddis2)%ddis1;

    if (d_bit<8){   // (パレット版)
        col_index = m_col_table.get_col_index<d_bit>(r*rate/255, g*rate/255, b*rate/255);
        for(i=start;i<=end;i++){
            d_pix.set_pos(pos_x, pos_y);
            d_pix.set_col(col_index);
            (*p_co1)+=dn1;
            e += ddis2;
            if (e>=ddis1){
                e -= ddis1;
                (*p_co2)+=dn2;
            }
        }
    }
    else {  // (RGB版)
        for(i=start;i<=end;i++){
            d_pix.set_pos(pos_x, pos_y);
            d_pix.set_col(r, g, b, rate);
            (*p_co1)+=dn1;
            e += ddis2;
            if (e>=ddis1){
                e -= ddis1;
                (*p_co2)+=dn2;
            }
        }
    }
    return ;
};

// 直線描画 補正
GT_IMG::adjust_lining_limit(int *p_start, int *p_end, int sx, int sy, int ex, int ey, int *p_cx, int *p_cy){
    int img_lim, img_lim_other, base_lim, other_lim;
    int *p_res, base_co, other_co, tmp, cx, cy;
    cx=*p_cx=ex-sx;
    cy=*p_cy=ey-sy;
    int base_axis = (abs(cx)>abs(cy))? 'x' : 'y';
    *p_start = *p_end = 0;

    if (!cx && !cy) return -1;
    // x,yの長いほうの座標軸をベースにする
    if (base_axis=='x'){    // x 座標ベース
        img_lim = m_width;
        img_lim_other = m_height;
        base_lim = cx;
        other_lim = cy;
    }
    else{   // y 座標ベース
        img_lim = m_height;
        img_lim_other = m_width;
        base_lim = cy;
        other_lim = cx;
    }

    for(int i=0;i<2;i++){
        if (i==0) {
            p_res = p_start;
            base_co  = (base_axis=='x')? sx : sy;
            other_co = (base_axis=='x')? sy : sx;
        }
        else if (i==1) {
            p_res = p_end;
            base_co  = (base_axis=='x')? ex : ey;
            other_co = (base_axis=='x')? ey : ex;
            other_lim = -other_lim;
        }

        // x,yの長いほうの座標軸で判定
        if (base_co<0)     *p_res=-base_co;
        if (base_co>=img_lim) *p_res= base_co-img_lim+1;

        // x,yの短いほうの座標軸で判定
        if (!other_lim) continue;
        tmp = other_co + ((*p_res) * (2*other_lim) + abs(base_lim))/(2*base_lim);

        int aim_co;
        if (tmp<0) { aim_co = -other_co; }
        else if (tmp>=img_lim_other) { aim_co = other_co-img_lim_other+1; }
        else continue;  // 範囲内

        tmp = aim_co*2*abs(base_lim)-abs(base_lim);
        *p_res = tmp/abs(2*other_lim);
        if (i==0 && tmp%abs(2*other_lim)) (*p_res)++;
        if (i==1) (*p_res)++;
    }
    *p_end = abs(base_lim) - *p_end;
    return 0;
}

// コンストラクタ
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){
    if (m_col_table.m_cols_len<=index) return 0;
    m_col_table.set_col(index, r, g, b);
    return 0;
}
// カラーマスク設定
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_07.cpp

// 直線描画

#include "gt_img_07.h"

GT_DIB gt_dib_1;
DWORD col=0xffffff;
static OPENFILENAME ofn = {0};
static char filename[MAX_PATH];
POINT start_po, end_po;
CHOOSECOLOR cc = {0};
DWORD col_costom[8] = {0};
BYTE r=255, g=255, b=255;
COLORREF color=0xffffff;
int col_index=0;

// メニュー
on_command(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
    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_COL:
        cc.lStructSize  = sizeof (CHOOSECOLOR);
        cc.hwndOwner    = hwnd;
        cc.rgbResult    = color;
        cc.lpCustColors = col_costom;
        cc.Flags    = CC_FULLOPEN | CC_RGBINIT;
        if (!ChooseColor(&cc)) break;
        color = cc.rgbResult;
        r = GetRValue(color);
        g = GetGValue(color);
        b = GetBValue(color);

        // パレット
        col_index = 0;
        switch(gt_dib_1.m_bit_count) {
        case 1 : gt_dib_1.m_col_table.get_col_index<1>(r,g,b); break;
        case 4 : gt_dib_1.m_col_table.get_col_index<4>(r,g,b); break;
        case 8 : gt_dib_1.m_col_table.get_col_index<8>(r,g,b); break;
        }
        gt_dib_1.apply_palette_all();
        break;
    }
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
    static POINT mou_po, po_array[4];
    static int mou_down=0;
    static char s[50];
    RECT rc;
    int y;
    HRGN hrgn, hrgn2;

    switch (msg) {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (gt_dib_1.m_hdc){
            BitBlt( hdc, 20, 20, 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+20;
        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_LBUTTONDOWN:
        if (!mou_down){
            GetCursorPos(&start_po);
            ScreenToClient(hwnd, &start_po);
            mou_down=1;
        }
        break;
    case WM_LBUTTONUP:
        if (mou_down){
            mou_down=0;
            GetCursorPos(&end_po);
            ScreenToClient(hwnd, &end_po);
            gt_dib_1.draw_line(start_po.x-20, start_po.y-20, end_po.x-20, end_po.y-20, r, g, b, 255);
            InvalidateRect(hwnd, NULL, 1);
        }
        mou_down=0;
        break;
    case WM_MOUSEMOVE:
        if (mou_down){
            hdc=GetDC(hwnd);
            GetClientRect(hwnd , &rc);
            if (mou_down==2){
                po_array[0] = po_array[3] = start_po;
                po_array[1] = po_array[2] = mou_po;
                po_array[2].x++;    po_array[3].x++;
                po_array[2].y++;    po_array[3].y++;
                hrgn = CreatePolygonRgn(po_array , 4 , WINDING);
                SelectObject(hdc, hrgn);        
                PatBlt(hdc,  0, 0, rc.right, rc.bottom, PATINVERT);
                DeleteObject(hrgn);
            }
            mou_down=2;

            GetCursorPos(&mou_po);
            ScreenToClient(hwnd, &mou_po);
            po_array[0] = po_array[3] = start_po;
            po_array[1] = po_array[2] = mou_po;
            po_array[2].x++;    po_array[3].x++;
            po_array[2].y++;    po_array[3].y++;
            hrgn2 = CreatePolygonRgn(po_array , 4 , WINDING);
            SelectObject(hdc, hrgn2);       
            PatBlt(hdc,  0, 0, rc.right, rc.bottom, PATINVERT);
            DeleteObject(hrgn2);
            ReleaseDC(hwnd, hdc);
        }
        break;
    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;

        gt_dib_1.create(201, 151, 24, 0);
        gt_dib_1.apply_dc(hwnd);
        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, 300, 250, 
            NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) return 1;

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

    return msg.wParam;
}


 007-06  前へ←  ホームへ  →次へ  007-08