007-02  前へ←  ホームへ  →次へ  007-04

No.007 - 03  画像クラス


CreateDibSectionに頼らない自前画像クラスを作り、
リソース悪食を回避します。


・悪の根源
CreateDibSection
CreateDibSection がもうちょっとまともな関数ならこんな苦労はねえよ、っていう話です。

多くのアプリケーションで画面のちらつきを無くすためにCreateDibSectionで裏画面を用意し、そこからBitBltしてやっています。
ですが画像処理を行いたい場合、もう何枚か裏画面が必要になるときがあります。
この何枚かの裏画面が重要です。
Win98, 95のリソースは2MBまでなのにCreateDibSectionはリソースを食うのでそう何枚も裏画面が作れないのです。
画像ファイルをいくつも読み込むときも同様です。
CreateDibSectionはそんなに呼べません。

ということで自前画像クラスを作り、そこからDIBにコピーして行くやり方を採ります。


画像クラス
自前画像クラスとDIBクラスの違いはHDCと、HBITMAP を持つか持たないかの差だけなので
自前画像クラスを定義したあとそれ継承のDIBクラスを作るだけです。



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

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

gt_img_parts.hファイルは前回のものと全く同じものを使うのでここには書きません。前回を参照願います。


・GT_IMG
クラス
カラーテーブル、カラーマスク、幅、高さ、ビット深度などの基本的な情報を持つクラスです。
メモリ管理が完全自前なのでリソースを食いません。
ピクセル配列DIBと完全互換なので同じピクセル反復子が使えます。
create()関数で生成し、 destroy()で破棄します。

set_pixel()関数でピクセル描画、get_pixel()関数でピクセル値取得です。

矩形塗りつぶし関数。
fill()は矩形塗りつぶしです。アプリケーションから呼びます。
fill_pixput();はtemplate で、内部でprivate関数的に呼びます。


・GT_DIB
クラス
上記GT_IMG クラスの派生クラスで、
DIBを扱うためにHDCHBITMAPをメンバとして備えたクラスです。

GT_IMG クラス同様create()関数で生成し、 destroy()で破棄します。
create()destroy()関数はオーバーライドしています。

apply_dc()は与えられた引数のHDC互換DCを作成します。
apply_palette_all();関数で作成したHDCにパレットを適用させます。



実行結果は前回と全く変わりません。
矩形塗りつぶしだけです。


resource_03.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_COL         3000



gt_img_03.rc

#include "resource_03.h"

//リソーススクリプト
GT_DIB_MENU MENU {
    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_COL
}



gt_img_03.h

// 矩形塗りつぶし

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

    // 矩形塗りつぶし
    fill(int x, int y, int cx, int cy, BYTE r, BYTE g, BYTE b, int rate);
    template <int d_bit> void fill_pixput(int x, int y, int cx, int cy, BYTE r, BYTE g, BYTE b, int rate);

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

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


//==========矩形操作==========
// 矩形塗りつぶしのインライン・テンプレート
template <int d_bit> inline void 
GT_IMG::fill_pixput(int x, int y, int cx, int cy, BYTE r, BYTE g, BYTE b, int rate){
    PIXEL_ITR<d_bit,0> d_pix;
    d_pix.init(m_pbits, m_alpha, m_byte_width, m_width, m_height, &m_col_table);
    if (rate<0) rate=0;
    if (rate>=255) rate=255;
    int ix, iy;
    BYTE col_index;
    if (d_bit<=8){  // (パレット版) 
        col_index=m_col_table.get_col_index<d_bit>(r*rate/255, g*rate/255, b*rate/255);
        for(iy=0;iy<cy;iy++){
            d_pix.set_pos(x, y+iy);
            for(ix=0;ix<cx;ix++){ d_pix.set_col(col_index); d_pix++; }
        }
    }
    else {  // (RGB版) 
        for(iy=0;iy<cy;iy++){
            d_pix.set_pos(x, y+iy);
            for(ix=0;ix<cx;ix++){ d_pix.set_col(r, g, b, rate); d_pix++; }
        }
    }
};
// 矩形塗りつぶし
GT_IMG::fill(int x, int y, int cx, int cy, BYTE r, BYTE g, BYTE b, int rate){
    if (!m_pbits) return -1;

    if (cx<0) { x+=cx; cx=-cx; };
    if (cy<0) { y+=cy; cy=-cy; };

    // マイナス座標の補正
    if (x<0) { cx+=x; x=0; };
    if (y<0) { cy+=y; y=0; };
    // 限界コピー幅の補正
    if ((x+cx)>=m_width) { cx=m_width -x;};
    if ((y+cy)>=m_height){ cy=m_height-y;};

    // コピー
    switch(m_bit_count){
    case 1 : fill_pixput<1> (x, y, cx, cy, r, g, b, rate);   break;
    case 4 : fill_pixput<4> (x, y, cx, cy, r, g, b, rate);   break;
    case 8 : fill_pixput<8> (x, y, cx, cy, r, g, b, rate);   break;
    case 16: fill_pixput<16>(x, y, cx, cy, r, g, b, rate);   break;
    case 24: fill_pixput<24>(x, y, cx, cy, r, g, b, rate);   break;
    case 32: fill_pixput<32>(x, y, cx, cy, r, g, b, rate);   break;
    }
    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 void GT_IMG::set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b){
    if (m_col_table.m_cols_len<=index) return ;
    m_col_table.set_col(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();
    void 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(){
    if (!m_col_table.m_cols_len) return -1;
    RGBQUAD *p_rgb, *rgb_table;
    rgb_table=new RGBQUAD[m_col_table.m_cols_len];
    for (int i=0;i<m_col_table.m_cols_len;i++){
        p_rgb=&(rgb_table[i]);
        p_rgb->rgbRed   =m_col_table.m_cols[i].m_r;
        p_rgb->rgbGreen =m_col_table.m_cols[i].m_g;
        p_rgb->rgbBlue  =m_col_table.m_cols[i].m_b;
        p_rgb->rgbReserved=0;
    }
    if (m_hdc) SetDIBColorTable(m_hdc, 0, m_col_table.m_cols_len, rgb_table);
    delete[] rgb_table;
    return 0;
}
// パレットに色設定
inline void GT_DIB::set_pal_col(BYTE index, BYTE r, BYTE g, BYTE b){
    if (m_col_table.m_cols_len<=index) return ;
    m_col_table.set_col(index, r, g, b);

    RGBQUAD rgb;
    rgb.rgbRed      =r;
    rgb.rgbGreen    =g;
    rgb.rgbBlue     =b;
    rgb.rgbReserved =0;
    if (m_hdc) SetDIBColorTable(m_hdc, index, 1, &rgb);
}

// 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_03.cpp

// 矩形塗りつぶし

#include "resource_03.h"
#include "gt_img_03.h"

GT_DIB gt_dib_1;
CHOOSECOLOR cc = {0};
COLORREF color = 0, col_costom[16];
int col_index = 0;
BYTE r=0, g=255, b=255;

static char filename[MAX_PATH];
POINT start_po, end_po;

// メニュー
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, 1);
        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;
    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.fill(start_po.x-20, start_po.y-20,
                    end_po.x-start_po.x, end_po.y-start_po.y, 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[1].y=start_po.y;   po_array[3].y=mou_po.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[1].y=start_po.y;   po_array[3].y=mou_po.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;
    }
    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-02  前へ←  ホームへ  →次へ  007-04