前回は、メインウインドウ上に直接ビットマップを描画していましたが、これだと扱いにくいのでビットマップ用のウインドウを作成し、そこにビットマップを描画するようにします。
ビットマップウインドウは、メインウインドウのチャイルドウインドウとしてメインウインドウのクライアント領域に配置します。ウインドウの大きさは、ビットマップとメインウインドウのクライアント領域の小さい方にあわせ、ビットマップがメインウインドウのクライアント領域より大きい場合はスクロールバーでスクロール表示するようにしました。
// ビットマップウインドウ生成 hwBMP=CreateWindow("CBMPWin",NULL,WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL, 0,0,1,1,hwnd,NULL,hInst,NULL);
このようにしてウインドウを作ったら、ビットマップの読み込み時やメインウインドウのサイズ変更時にビットマップウインドウの大きさを設定しなおし、ビットマップウインドウのスクロールバーを設定します。具体的には、ビットマップの大きさとウインドウの大きさを比べビットマップの方が大きければスクロールバーを配置します。そのためには、ウインドウのサイズ変更メッセージWM_SIZEの処理で
// 水平スクロールバーサイズ設定 if (LOWORD(lParam)<cdib.getWidth()) { iHSMin=0; iHSMax=cdib.getWidth(); iHSPage=LOWORD(lParam); } else { iHSMin=0; iHSMax=0; iHSPage=0; }
のような感じでスクロールバーのサイズを計算しましょう。iHSMinはスクロールバーの最小値、iHSMaxは最大値、iHSPageはスクロールバーの「つまみ」の大きさです。LOWORD(lParam)にはウインドウの横幅が入っているので、この処理によってビットマップの横幅を範囲としウインドウの大きさ分のつまみを持つ水平スクロールバーの大きさを計算できる事になります(範囲が0だとスクロールバーは作られません)。
スクロールバーのサイズを計算したら、その値をSCROLLINFO型の構造体sInfoに入れて、SetScrollInfo()を呼び出しスクロールバーに値を設定します。
case WM_SIZE: // ウインドウサイズ変更 sInfo.cbSize=sizeof(SCROLLINFO); sInfo.fMask=SIF_ALL; // 水平スクロールバーサイズ設定 if (LOWORD(lParam)<cdib.getWidth()) { iHSMin=0; iHSMax=cdib.getWidth(); iHSPage=LOWORD(lParam); } else { iHSMin=0; iHSMax=0; iHSPage=0; } // スクロールバー位置設定 iHSPos=min(iHSPos,iHSMax-iHSPage); // SCROLLINFO構造体設定 sInfo.nMin=iHSMin; sInfo.nMax=iHSMax; sInfo.nPage=iHSPage; // スクロールバー設定 SetScrollInfo(hwnd,SB_HORZ,&sInfo,TRUE); // 垂直スクロールバーサイズ設定 if (HIWORD(lParam)<cdib.getHeight()) { iVSMin=0; iVSMax=cdib.getHeight(); iVSPage=HIWORD(lParam); } else { iVSMin=0; iVSMax=0; iVSPage=0; } // スクロールバー位置設定 iVSPos=min(iVSPos,iVSMax-iVSPage); // SCROLLINFO構造体設定 sInfo.nMin=iVSMin; sInfo.nMax=iVSMax; sInfo.nPage=iVSPage; sInfo.nPos=iVSPos; // スクロールバー設定 SetScrollInfo(hwnd,SB_VERT,&sInfo,TRUE); // ウインドウ再描画 InvalidateRgn(hwnd,NULL,FALSE); UpdateWindow(hwnd); return 0;
スクロールバーが操作されるとWM_HSCROLL/WM_VSCROLLメッセージが送られてくるので、自分でスクロールバーの位置を設定し、ビットマップウインドウ内のビットマップも描画しなおします。
case WM_HSCROLL: // 水平スクロールバーメッセージ switch(LOWORD(wParam)) { case SB_LINEUP: iHSd=-1; break; case SB_LINEDOWN: iHSd=1; break; case SB_PAGEUP: iHSd=-iHSPage; break; case SB_PAGEDOWN: iHSd=iHSPage; break; case SB_THUMBTRACK: iHSd=HIWORD(wParam)-iHSPos; break; default: iHSd=0; } if (iHSd!=0) { iHSPos+=iHSd; iHSPos=max(iHSPos,0); iHSPos=min(iHSPos,iHSMax-iHSPage); SetScrollPos(hwnd,SB_HORZ,iHSPos,TRUE); InvalidateRgn(hwnd,NULL,FALSE); UpdateWindow(hwnd); } return 0;
・ビットマップ描画処理 case WM_PAINT: hdc=BeginPaint(hwnd,&ps); cdib.draw(hdc,iHSPos,-iVSPos); // DIB描画 EndPaint(hwnd,&ps); return 0;
次に、C32DIBクラスに3×3行列のフィルタ処理を行うメンバ関数Filter9()を追加します。この関数は、引数にフィルタの行列と加算値・除算値をまとめたFILTER9INFO構造体のアドレスを渡すにしました。
struct FILTER9INFO { double lfMatrix[9],lfAdd,lfDiv; }; void C32DIB::Filter9(FILTER9INFO *f9Info) { DWORD i,j,k,l,dwPixel; double lfR,lfG,lfB; LPDWORD lpWrk; lpWrk=(LPDWORD)GlobalAlloc(GPTR,dwWidth*dwHeight*4); CopyMemory(lpWrk,lpPixels,dwWidth*dwHeight*4); for (i=1;i<dwHeight-1;i++) /* フィルタ処理 */ for (j=1;j<dwWidth-1;j++) { lfR=0; lfG=0; lfB=0; for (k=0;k<3;k++) /* RGB各成分の値を行列値を掛けて加算 */ for (l=0;l<3;l++) { dwPixel=getPixel(j+l-1,i+k-1); lfR+=(double)(dwPixel & 0x00ff0000 >> 16)*f9Info->lfMatrix[l+k*3]; lfG+=(double)(dwPixel & 0x0000ff00 >> 8)*f9Info->lfMatrix[l+k*3]; lfB+=(double)(dwPixel & 0x000000ff)*f9Info->lfMatrix[l+k*3]; } lfR=f9Info->lfAdd+lfR/f9Info->lfDiv; lfG=f9Info->lfAdd+lfG/f9Info->lfDiv; lfB=f9Info->lfAdd+lfB/f9Info->lfDiv; if (lfR<0) lfR=0; if (lfR>255) lfR=255; if (lfG<0) lfG=0; if (lfG>255) lfG=255; if (lfB<0) lfB=0; if (lfB>255) lfB=255; lpWrk[j+i*dwWidth]=(DWORD)lfR*65536+(DWORD)lfG*256+(DWORD)lfB; } CopyMemory(lpPixels,lpWrk,dwWidth*dwHeight*4); GlobalFree(lpWrk); }
メニューから、あるいはドラッグ&ドラッグでBMPファイルを読み込んでウインドウの大きさを変えてみてください。
アーカイブには実行ファイルとソース・ヘッダ、VC++5のプロジェクトが収録されています。