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を扱うためにHDC、HBITMAPをメンバとして備えたクラスです。 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; } |