フォトレタッチソフト開発記第三回>カスタムフィルタ

 今回は、フィルタメニューに行列などのパラメータを自由に設定できるカスタムフィルタを追加し、編集メニューを新設してクリップボードへのコピー機能も入れてみます。カスタムフィルタは、設定用のモーダルダイアログを作成し3×3行列や加算値・除算値を設定できるようにしました。

カスタムフィルタ

 カスタムフィルタ用のモーダルダイアログは、モーダルダイアログの実験で作ったダイアログに入力欄を追加する形で作ります。カスタムフィルタ設定用のモーダルダイアログを呼び出す関数FilterDialog()を定義し、ダイアログで設定されたパラメータをフィルタ情報構造体FILTER9INFOに書き込んで返すようにしましょう。

  FILTER9INFO FilterDialog() { // カスタムフィルタダイアログ関数

      HWND hwDialog;
      static FILTER9INFO f9Param;
      MSG msg;

      // ダイアログのウインドウプロシージャ−に構造体のアドレスを渡す
      DialogProc(NULL,(UINT)(&f9Param),0,0);

      // ダイアログボックス作成
      hwDialog = CreateWindow("CDialog","3X3フィルタ",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,
        320,160,NULL,NULL,hInst,NULL);

      // メインウインドウを無効化してモーダルに
      EnableWindow(hwMain,FALSE);

      // メッセージループ
      while (f9Param.lfDiv==0) { // ダイアログが結果を返すまでループ

          GetMessage(&msg,NULL,0,0);

          TranslateMessage(&msg);
          DispatchMessage(&msg);

      }

      // メインウインドウを有効にしてモーダル解除
      EnableWindow(hwMain,TRUE);
      BringWindowToTop(hwMain);

      // ダイアログの設定結果を返す
      return f9Param;

  }

 今回は、FILTER9INFO構造体f9Paramの除算値が0(当然無効な値ですね)の間処理を繰り返すようにします。この関数を

  f9Info=FilterDialog();

  cdib.Filter9(&f9Info);

  InvalidateRgn(hwnd,NULL,FALSE);
  UpdateWindow (hwnd);

 という感じで呼び出せばフィルタ処理を行えるわけです。

 では、次はダイアログのウインドウプロシージャ−。まず、最初にFilterDialog()のローカル変数f9Paramのアドレスをウインドウプロシージャに渡します。

  if (hwnd==NULL) {

      // 構造体のアドレスを保存
      lpParam=(FILTER9INFO *)iMsg;
      lpParam->lfDiv=0;

      return 0;

  }

 ダイアログ内に配置する部品は、行列と加算値・除算値の入力欄、それにボタンですからWM_CREATEは以下のようになります。

  case WM_CREATE: // ウインドウ作成時の処理

      // ボタン作成
      btOK=CreateWindow("BUTTON","実行",WS_CHILD|WS_VISIBLE,
            8,90,80,32,hwnd,(HMENU)0,hInst,NULL);

      btCancel=CreateWindow("BUTTON","取消",WS_CHILD|WS_VISIBLE,
                96,90,80,32,hwnd,(HMENU)1,hInst,NULL);

      // フィルタ行列入力欄生成
      for (i=0;i<3;i++)
          for (j=0;j<3;j++)
              hwEd[j][i]=CreateWindow("Edit",NULL,WS_CHILD | WS_VISIBLE |WS_BORDER,
                          8+j*56,4+(2-i)*24,48,22,hwnd,(HMENU)(j+i*3+2),hInst,NULL);

      hwLv=CreateWindow("STATIC","除算値",WS_CHILD | WS_VISIBLE|SS_CENTER,
            180,30,56,22,hwnd,(HMENU)11,hInst,NULL);

      hwEdv=CreateWindow("Edit",NULL,WS_CHILD | WS_VISIBLE |WS_BORDER,
             184,52,48,22,hwnd,(HMENU)12,hInst,NULL);

      hwLp=CreateWindow("STATIC","加算値",WS_CHILD | WS_VISIBLE|SS_CENTER,
            244,30,56,22,hwnd,(HMENU)11,hInst,NULL);

      hwEdp=CreateWindow("Edit",NULL,WS_CHILD | WS_VISIBLE |WS_BORDER,
             248,52,48,22,hwnd,(HMENU)14,hInst,NULL);

      return 0;

 ダイアログのコマンドは実行・取消ボタンですから、この2つのボタンに対応するコマンドメッセージをWM_COMMANDで処理しましょう。

  case WM_COMMAND: // コマンドメッセージ

      switch (LOWORD(wParam)) {

          case 0: // 実行ボタン

              for (i=0;i<3;i++) // フィルタの行列取得
                  for (j=0;j<3;j++) {

                      GetWindowText(hwEd[j][i],lpszBuf,64);
                      lpParam->lfMatrix[j+i*3]=atof(lpszBuf);

                  }

              GetWindowText(hwEdv,lpszBuf,64);
              lpParam->lfDiv=atof(lpszBuf);

              GetWindowText(hwEdp,lpszBuf,64);
              lpParam->lfAdd=atof(lpszBuf);

              DestroyWindow(hwnd);

              break;

          case 1: // 取消ボタン

              lpParam->lfDiv=9999;
              DestroyWindow(hwnd);

              break;

      }

      return 0;

 フィルタ処理ではF9FILTERINFOflDivが1000以上なら無効にして、lfDivが9999なら未設定という事にしました。今回は、実行ボタン以外でダイアログが閉じられるとlfDivを9999にして未設定で終了させるようにしてあります。

  case WM_DESTROY : // 終了処理

      if (lpParam->lfDiv==0) // 設定されていなければ9999
          lpParam->lfDiv=9999;

      return 0;

 これで、ダイアログが閉じられるとf9Param*lpParam)のlfDivが0というダイアログ関数のループ条件が偽になるので、結果とともに制御が返されます。

クリップボードへのコピー

 クリップボードへのコピーは、C32DIBクラスにクリップボードへのコピーを行う関数CopyDIBを定義する形で追加します。処理としては、単に24ビットDIBのメモリイメージを作ってクリップボードにCF_DIB形式でコピーするだけなので、特に難しい点はありません。

  void C32DIB::CopyDIB() { // DIBをクリップボードにコピー

      DWORD i,j,dwL;
      LPBITMAPINFOHEADER lpInfo;
      LPBYTE lpPixel;
      HGLOBAL hMem;
      LPBYTE lpMem;

      if ((dwWidth*3) % 4==0) // バッファの1ラインの長さを計算
          dwL=dwWidth*3;
      else
          dwL=dwWidth*3+(4-(dwWidth*3) % 4);

      // バッファ用のメモリブロック確保
      hMem=GlobalAlloc(GHND,sizeof(BITMAPINFOHEADER)+dwL*dwHeight);

      // メモリオブジェクトをロックしてアドレスを取得
      lpMem=(LPBYTE)GlobalLock(hMem);

      lpInfo=(LPBITMAPINFOHEADER)lpMem;
      lpPixel=lpMem+sizeof(BITMAPINFOHEADER);

      // 24ビットDIBヘッダ作成
      lpInfo->biSize=sizeof(BITMAPINFOHEADER);
      lpInfo->biWidth=dwWidth;
      lpInfo->biHeight=dwHeight;
      lpInfo->biPlanes=1;
      lpInfo->biBitCount=24;

      for (i=0;i<dwHeight;i++) // ピクセル列をバッファにコピー
          for (j=0;j<dwWidth;j++)
              CopyMemory(lpPixel+i*dwL+j*3,lpPixels+i*dwWidth+j,3);

      // バッファをアンロック
      GlobalUnlock(hMem);

      // クリップボードをオープンしクリア
      OpenClipboard(NULL);
      EmptyClipboard();

      // クリップボードにバッファの内容をコピー
      SetClipboardData(CF_DIB,hMem);

      // クリップボードをクローズ
      CloseClipboard();

  }

プログラム

 ビットマップを読み込んだら、フィルタ処理をかけてみてください。ビットマップをコピーし、ドット絵でぃたで読み込むと、処理結果を詳しく分析できます(今回は、これがやりたくてコピー機能を追加した)。

プログラムダウンロード(saib3.lzh/37KB)
戻る