フォトレタッチソフト開発記第五回>RGB成分調整

 今回は、RGB成分の調整ダイアログを追加します。成分を調整するトラックバーを持ちプレビューも可能なモーダルダイアログで、RGB各成分を調整できるようにしてみましょう。

ピクセルの取得と設定

 ダイアログを追加する前に、C32DIBクラスにピクセルのRGB各成分を取得・設定するメンバ関数を追加します。RGBDWORDで格納されているピクセルの下位24ビットにRGBの順に格納されているので、ピクセルの値から論理演算で個々の成分を分離しましょう。

  BYTE C32DIB::getR(DWORD dwX,DWORD dwY) {

      if (dwX<dwWidth && dwY<dwHeight)
          return (BYTE)((getPixel(dwX,dwY) & 0x00ff0000) >>16);
      else
          return 0;

  }

  BYTE C32DIB::getG(DWORD dwX,DWORD dwY) {

      if (dwX<dwWidth && dwY<dwHeight)
          return (BYTE)((getPixel(dwX,dwY) & 0x0000ff00) >>8);
      else
          return 0;

  }

  BYTE C32DIB::getB(DWORD dwX,DWORD dwY) {

      if (dwX<dwWidth && dwY<dwHeight)
          return (BYTE)(getPixel(dwX,dwY) & 0x000000ff);
      else
          return 0;

  }

 これでピクセルのRGB成分を個別に取得出来るようになったので、続いてピクセルのRGB成分を個別に設定する関数も作ってみましょう。設定関数では、「各成分が格納されているアドレス」のポインタに引数で渡された値を書き込んでいます。アドレス計算の時には、上位と下位バイト(とワードも)が逆転している(つまり、最下位バイトが最初に来る)点に注意してください。

  bool C32DIB::setR(DWORD dwX,DWORD dwY,int iR) {

      if (dwX>=getWidth() || dwY>=getHeight())
          return FALSE;

      if (iR<0)
          iR=0;
      else if (iR>255)
          iR=255;

      LPBYTE w=(LPBYTE)(lpPixels+dwX+dwY*getWidth())+2;
      *w=(BYTE)iR;

      return TRUE;

  }

  bool C32DIB::setG(DWORD dwX,DWORD dwY,int iR) {

      if (dwX>=getWidth() || dwY>=getHeight())
          return FALSE;

      if (iG<0)
          iG=0;
      else if (iG>255)
          iG=255;

      LPBYTE w=(LPBYTE)(lpPixels+dwX+dwY*getWidth())+1;
      *w=(BYTE)iG;

      return TRUE;

  }

  bool C32DIB::setG(DWORD dwX,DWORD dwY,int iB) {

      if (dwX>=getWidth() || dwY>=getHeight())
          return FALSE;

      if (iB<0)
          iB=0;
      else if (iB>255)
          iB=255;

      LPBYTE w=(LPBYTE)(lpPixels+dwX+dwY*getWidth());
      *w=(BYTE)iB;

      return TRUE;

  }

 RGBの取得・設定が出来るようになったら、次は「すべてのピクセルのRGB成分」を増減する関数を作ってみます。この関数では、まずピクセルから指定された成分を取得し指定された値を増減して再度その成分をピクセルに設定する、という処理をビットマップのすべてのピクセルに行うようにしました。

  void C32DIB::transR(int iV) {

      int r;

      for (DWORD i=0;i<getHeight();i++)
          for (DWORD j=0;j<getWidth();j++) {

              r=getR(j,i)+iV;
              setR(j,i,r);

          }

  }

RGB調整ダイアログ

 RGB成分調整ダイアログはリサイズダイアログと同様のモーダルとし、ダイアログの制御関数conv()のローカル変数dwDoneが0の間ダイアログメッセージを処理します。

  void conv() { // 成分調整ダイアログ

      HWND hwDialog;
      static DWORD dwDone;
      MSG msg;

      dwDone=0;

      // ダイアログボックス作成
      hwDialog=CreateWindow("CConvDialog","成分調整",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,
        380,200,NULL,NULL,hInst,NULL);

      // ダイアログのウインドウプロシージャ−にフラグ変数のアドレスを渡す
      ConvDialogProc(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);

  }

 ダイアログには、成分調整を行うトラックバーと実行/取消ボタンを追加し、また処理結果のプレビューを表示するためのC32DIBオブジェクトも作成しておきます。プレビュー用ビットマップは、現在のビットマップの幅・高さの大きい方を128ピクセルとし、他方も同様の縮尺で縮小します。これにより、縦横比を保ったまま128×128ピクセルの領域内に収まるようにできるわけです。ビットマップは、処理前の状態を保持するdibと表示用にvdibの2枚作っておきましょう。そして、現在のビットマップをdibに描いてサイズを調整したら、今回新たにオーバーロードしたC32DIBクラスの=演算子でその内容をvdibにコピーしておきます。

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

      // ボタン作成
      hwOK=CreateWindow("BUTTON","実行",WS_CHILD|WS_VISIBLE,
        16,128,72,32,hwnd,(HMENU)0,hInst,NULL);

      // ボタン作成
      hwCansel=CreateWindow("BUTTON","取消",WS_CHILD|WS_VISIBLE,
        100,128,72,32,hwnd,(HMENU)1,hInst,NULL);

      hwL1=CreateWindow("STATIC","R:0",WS_CHILD|WS_VISIBLE,
            4,12,64,28,hwnd,(HMENU)2,hInst,NULL);
      hwC1=CreateWindow(TRACKBAR_CLASS,NULL,WS_CHILD|WS_VISIBLE,
            64,8,160,32,hwnd,NULL,hInst,NULL);

      hwL2=CreateWindow("STATIC","G:0",WS_CHILD|WS_VISIBLE,
            4,52,64,28,hwnd,(HMENU)2,hInst,NULL);
      hwC2=CreateWindow(TRACKBAR_CLASS,NULL,WS_CHILD|WS_VISIBLE,
            64,48,160,32,hwnd,NULL,hInst,NULL);

      hwL3=CreateWindow("STATIC","B:0",WS_CHILD|WS_VISIBLE,
            4,92,64,28,hwnd,(HMENU)2,hInst,NULL);
      hwC3=CreateWindow(TRACKBAR_CLASS,NULL,WS_CHILD|WS_VISIBLE,
            64,88,160,32,hwnd,NULL,hInst,NULL);

      SendMessage(hwC1,TBM_SETRANGEMIN,1,0);
      SendMessage(hwC1,TBM_SETRANGEMAX,1,128);
      SendMessage(hwC1,TBM_SETPOS,1,64);

      SendMessage(hwC2,TBM_SETRANGEMIN,1,0);
      SendMessage(hwC2,TBM_SETRANGEMAX,1,128);
      SendMessage(hwC2,TBM_SETPOS,1,64);

      SendMessage(hwC3,TBM_SETRANGEMIN,1,0);
      SendMessage(hwC3,TBM_SETRANGEMAX,1,128);
      SendMessage(hwC3,TBM_SETPOS,1,64);

      dib=new C32DIB();
      vdib=new C32DIB();

      *dib=cdib;

      DWORD dwWid,dwHei;
      double lfRate;

      if (dib->getWidth()>dib->getHeight()) {

          if (dib->getWidth()>128)
              lfRate=128.0/(double)dib->getWidth();
          else
              lfRate=1;

      } else {

          if (dib->getHeight()>128)
              lfRate=128.0/(double)dib->getHeight();
          else
              lfRate=1;

      }

      dwWid=(DWORD)(dib->getWidth()*lfRate);
      dwHei=(DWORD)(dib->getHeight()*lfRate);

      dib->resize(dwWid,dwHei);
      *vdib=*dib;

 C32DIBクラスの=演算子は、以下のようにビットマップメモリの再確保とピクセル列のコピーを行うようにオーバーロードします。

  C32DIB& C32DIB::operator=(const C32DIB& t) { // =演算子をオーバーロード

      if (this!=&t) {

          if (getWidth()!=t.dwWidth || getHeight()!=t.dwHeight) {

              dwWidth=t.dwWidth;
              dwHeight=t.dwHeight;

              // DIB用メモリ確保
              GlobalFree(lpDIB);
              lpDIB=GlobalAlloc(GPTR,sizeof(BITMAPINFO)+dwWidth*dwHeight*4);
              // ポインタ設定
              lpbiInfo=(LPBITMAPINFO)lpDIB;
              lpPixels=(LPDWORD)((LPBYTE)lpDIB+sizeof(BITMAPINFO));

              // BITMAPINFO設定
              lpbiInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
              lpbiInfo->bmiHeader.biWidth=dwWidth;
              lpbiInfo->bmiHeader.biHeight=dwHeight;
              lpbiInfo->bmiHeader.biPlanes=1;
              lpbiInfo->bmiHeader.biBitCount=32;
              lpbiInfo->bmiHeader.biCompression=BI_RGB;

          }

          CopyMemory(lpPixels,t.lpPixels,dwWidth*dwHeight*4);

      }

      return *this;

  }

 成分調整スクロールバーが移動したら、スクロールバーの値を読んで成分の値を調整します。このためには、まず表示用ビットマップvdibに元画像dibをコピーしvdibの全ピクセルの成分をスクロールバーの値分だけ補正すれば良いですね。

  case WM_HSCROLL:

      // トラックバーの位置から補正値を算出
      iDC1=SendMessage(hwC1,TBM_GETPOS,0,0)-64;
      iDC2=SendMessage(hwC2,TBM_GETPOS,0,0)-64;
      iDC3=SendMessage(hwC3,TBM_GETPOS,0,0)-64;

      // ラベルに現在の値を表示
      wsprintf(str,"R:%d",iDC1);
      SetWindowText(hwL1,str);
      wsprintf(str,"G:%d",iDC2);
      SetWindowText(hwL2,str);
      wsprintf(str,"B:%d",iDC3);
      SetWindowText(hwL3,str);

      *vdib=*dib;
      vdib-<transR(iDC1);
      vdib-<transG(iDC2);
      vdib-<transB(iDC3);

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

 実行ボタンがクリックされたら、現在のビットマップに成分調整を施してダイアログウインドウを破棄します。

  iDC1=SendMessage(hwC1,TBM_GETPOS,0,0)-64;
  iDC2=SendMessage(hwC2,TBM_GETPOS,0,0)-64;
  iDC3=SendMessage(hwC3,TBM_GETPOS,0,0)-64;

  cdib.transR(iDC1);
  cdib.transG(iDC2);
  cdib.transB(iDC3);

  DestroyWindow(hwnd);

プログラム

 ビットマップを読み込んだら、設定−成分調整メニューでRGB成分を変更してみてください。

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