今回は、拡大縮処理を入れてみます。また、ダイアログのボタン・EDITをサブクラス化して、タブキーによるフォーカス移動も実装してみましょう。
拡大縮小処理は、グラフィック・DIB処理実験室で試した処理をDIBクラスに入れます。public関数resize(DWORD dwWid2, DWORD dwHei2)を定義し、この関数に新しいサイズを渡すと新しいサイズでビットマップを作り直すようにしましょう。そのためには、バッファを確保してそこに現在のピクセル列をコピーしてから、ビットマップメモリを破棄、その後新しいサイズでビットマップメモリを確保しなおせば良いですね。
// 作業用バッファを確保し、元画像をコピー lpOPix=(LPDWORD)GlobalAlloc(GPTR,dwWidth*dwHeight*4); CopyMemory(lpOPix,lpPixels,dwWidth*dwHeight*4); lfXRate=(double)dwWid2/(double)dwWidth; lfYRate=(double)dwHei2/(double)dwHeight; // 現在の画像を破棄し、拡大縮小後のサイズでビットマップを作り直す GlobalFree(lpDIB); lpDIB=GlobalAlloc(GPTR,sizeof(BITMAPINFO)+dwWid2*dwHei2*4); lpbiInfo=(LPBITMAPINFO)lpDIB; lpPixels=(LPDWORD)((LPBYTE)lpDIB+sizeof(BITMAPINFO)); lpbiInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); lpbiInfo->bmiHeader.biPlanes=1; lpbiInfo->bmiHeader.biBitCount=32; lpbiInfo->bmiHeader.biCompression=BI_RGB;
これで、新しいビットマップとピクセル列バッファが出来たので、後は現在のピクセル列を重み付でサンプリングして新しいピクセル列を作っていくだけです。ピクセル列が出来たら、DIBのヘッダを調整し以前のピクセル列バッファを破棄します。
// DIBのヘッダ・サイズ変数調整 lpbiInfo->bmiHeader.biWidth=dwWid2; dwWidth=lpbiInfo->bmiHeader.biWidth; lpbiInfo->bmiHeader.biHeight=dwHei2; dwHeight=lpbiInfo->bmiHeader.biHeight; GlobalFree(lpOPix);
拡大縮小に関しては、大きさを指定するモーダルダイアログを作りました。このダイアログには、幅・高さを指定するEDIT2つと実行・取消2つのボタンを配置します。また、このダイアログ自体は値を返しませんが、呼び出された後で拡大縮小が実行されるか取消されるまでループするので、処理が行われた事をあらわすフラグdwDoneを用意し、ダイアログのウインドウプロシージャーに渡します。ダイアログのウインドウプロシージャーは、拡大縮小・取消どちらかの処理が行われるとdwDoneフラグをセットし、フラグを監視しながらループしているダイアログ関数がフラグセットを検知するとループを抜けて終了、という流れです。
void resize() { // 拡大縮小ダイアログ
HWND hwDialog;
static DWORD dwDone;
MSG msg;
dwDone=0;
// ダイアログボックス作成
hwDialog = CreateWindow("CResizeDialog","リサイズ",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,
240,128,NULL,NULL,hInst,NULL);
// ダイアログのウインドウプロシージャ−にフラグ変数のアドレスを渡す
ResizeDialogProc(NULL,(UINT)(&dwDone),0,0);
// メインウインドウを無効化してモーダルに
EnableWindow(hwMain,FALSE);
// メッセージループ
while (dwDone==0) { // ダイアログが結果を返すまでループ
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// モーダル解除
EnableWindow(hwMain,TRUE);
BringWindowToTop(hwMain);
}
ダイアログ内ウインドウのサブクラス化は、ウインドウのウインドウプロシージャーを独自関数に置換して元のウインドウプロシージャーのアドレスをWNDPROC型ポインタの配列に格納していきます。
case WM_CREATE: // ウインドウ作成時の処理
hwL1=CreateWindow("Static","幅",WS_CHILD | WS_VISIBLE,8,10,20,20,
hwnd,(HMENU)5,hInst,NULL);
hwResizeDialog[0]=CreateWindow("Edit",NULL,WS_CHILD | WS_VISIBLE |WS_BORDER,
32,8,48,22,hwnd,(HMENU)0,hInst,NULL);
lpfnResizeWin[0]=(WNDPROC)SetWindowLong(hwResizeDialog[0],GWL_WNDPROC,
(DWORD)ResizeWinProc);
hwL2=CreateWindow("Static","高さ",WS_CHILD | WS_VISIBLE,92,10,36,20,
hwnd,(HMENU)5,hInst,NULL);
hwResizeDialog[1]=CreateWindow("Edit",NULL,WS_CHILD | WS_VISIBLE |WS_BORDER,
136,8,48,22,hwnd,(HMENU)1,hInst,NULL);
lpfnResizeWin[1]=(WNDPROC)SetWindowLong(hwResizeDialog[1],GWL_WNDPROC,
(DWORD)ResizeWinProc);
// ボタン作成
hwResizeDialog[2]=CreateWindow("BUTTON","実行",WS_CHILD|WS_VISIBLE,
16,48,72,32,hwnd,(HMENU)2,hInst,NULL);
lpfnResizeWin[2]=(WNDPROC)SetWindowLong(hwResizeDialog[2],GWL_WNDPROC,
(DWORD)ResizeWinProc);
hwResizeDialog[3]=CreateWindow("BUTTON","取消",WS_CHILD|WS_VISIBLE,
104,48,72,32,hwnd,(HMENU)3,hInst,NULL);
lpfnResizeWin[3]=(WNDPROC)SetWindowLong(hwResizeDialog[3],GWL_WNDPROC,
(DWORD)ResizeWinProc);
ダイアログ内ウインドウ用のウインドウプロシージャーでは、タブキーが押されたらフォーカスを移動、他は何もせずに元の関数にメッセージを流します。
LRESULT CALLBACK ResizeWinProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
int i;
/* メッセージを受信したウインドウを特定 */
i=GetWindowLong(hwnd,GWL_ID);
switch (iMsg) {
case WM_CHAR:
if ((DWORD)wParam=='\t') { /* Tabキー */
/* 次のウインドウにフォーカス移動 */
SetFocus(hwResizeDialog[(i+1) % 4]);
return 0;
}
break;
}
return (CallWindowProc((int (__stdcall *)(void))lpfnResizeWin[i],hwnd,iMsg,wParam,lParam));
}
ついでに、カスタムフィルタダイアログのボタンやEDITも同様にサブクラス化してTABキーで移動できるようにしておきました。
ビットマップを読み込んだら、フィルタ処理や拡大縮小をかけてみてください。ビットマップをコピーし、ドット絵でぃたで読み込むと、処理結果を詳しく分析できます。