none
カラービットマップの表示方法は? RRS feed

  • 質問

  • MFCの View:SurprisenDwaw(CDC*pDC)でカラービットマップを作成して画面に表示したいのですが、

    モノクロ画像しか出せません。

    ビットマップは、32ビットカラーを選択していますが、モノクロ表示になってしまします。

     

    フォーラムを探索しましたが、上手く一致するものが無く質問させて頂きました。

    初心者である為、あちこち参考に試してみたのですが、DDBとDIBの違いが今ひとつ理解できず

    カラー表示に至っていないように感じています。

    どなたか、ご教授ください。

     

    下に作成したコードを添付します。

     

      

    Code Snippet

    CDC myDC;
      CBitmap myBMP;
      void* ppvBits

     

     myDC.CreateCompatibleDC(pDC);   // 現在のデバイスコンテキストpDCと互換性のある
           // メモリデバイスコンテキストを作成する。

      HBITMAP hBitmap=::CreateDIBSection(NULL,lpbmi,DIB_RGB_COLORS,&ppvBits,NULL,0);
     

      p = (BYTE*)ppvBits;
      for(y=239;y>=0;y--){
       memcpy(p,byDisp+y*DISP_X_SIZE*4,DISP_X_SIZE*4);
       p+=DISP_X_SIZE*4;
      }
    #if 0
      myBMP.Attach(hBitmap);

      CBitmap* oldBMP=myDC.SelectObject(&myBMP);
      pDC->BitBlt(0,0,320,240,&myDC,0,0,SRCCOPY);
      myDC.SelectObject(oldBMP);
    #else
      HGDIOBJ old=SelectObject(myDC,hBitmap);

      pDC->BitBlt(0,0,640,240,&myDC,0,0,SRCCOPY);
                //コピー元デバイスコンテキストからhdcにコピー
      SelectObject(myDC,old);     //オブジェクトを選択
      DeleteObject(hBitmap);
      DeleteDC(myDC);

    #endif

     

     

     

    2008年11月17日 2:08

回答

  • こんな感じで塗りつぶすとどうなりますか?

    DWORD* p = (DWORD*)ppvBits;
    for (int i = 0; i < DISP_X_SIZE * DISP_Y_SIZE; ++i)
        *p++ = 0xff;

    2008年11月18日 4:17
  • biBitCount を 32 にしているので、1画素は 32bit = 4byte で表現されます。
    これを16進っぽく表現すると 0x00RRGGBB となります。
    (最上位の1バイトはアルファ値となる場合もありますが、今回はゼロということで)

    そして、p を DWORD* にキャストすると *p は DWORD(つまり4バイト)となり、
    0xff は 0x000000ff となって真っ青になるわけです。

    逆に、色はきちんと出ているとも言えます。
    ここで、最初のプログラムを見てみると、

    p = (BYTE*)ppvBits;
    for(y=239;y>=0;y--){
       memcpy(p,byDisp+y*DISP_X_SIZE*4,DISP_X_SIZE*4);
       p+=DISP_X_SIZE*4;
    }

    この部分で byDisp を p にコピーしているのですが、
    memcpy を使って byDisp の内容がそっくりそのまま p にコピーしているように見えます。
    これがモノクロになるということは、byDisp の内容がもともとモノクロということです。

    よって、Azulean さんのご指摘の通り、

    > ・コピー元のバッファのデータがおかしい? (byDispの記載なし)

    ということになります。

    2008年11月18日 7:59
  •  PATIO さんからの引用

    逆にmemsetした場合は、RGB全て同じ値が入るからグレースケールになってもあたりまえかなと。

    この辺を実感するための手法として、ペイントを使う方法を書いておきます。

     

    1.ペイントを起動する。

    2.色メニューから色の編集を選択する。

    3.色の作成ボタンを押す。

    4.赤・緑・青を同じ数字にする。(0~255のいずれか)

    5.いくつかの値を試してみて、赤・緑・青を同じ数値にしたときの色の特徴を見る。

     

    基本的に赤・緑・青(RGB)を同じ数値にすると、黒~白のグレースケール、白黒っぽくなります。

    ですので、memsetやFillMemoryではなく、ピクセルごとに色を設定するか、4バイト単位でコピーするかが必要になります。

    2008年11月18日 14:27
    モデレータ
  •  GREENMAIL さんからの引用

    実際のところは、よく分かっていないのですが、BVとのCOMインタフェースの為に使っていると思います。

    (自信なし)

    製作中のプログラムと接続する相手のプログラムとのインタフェースが、SafeArrayを要求している為、

    使っています。

    VB・COM周りであれば確かに仕方ないと思います。

    ただ、SafeArrayはどちらが解放するべきか、きっちりと意識して解放するようにして下さい。

    逆に解放してはならないタイミングもあるかもしれませんので。

    2008年11月22日 14:18
    モデレータ

すべての返信

  • 提示されたコードをベースに書いてみましたが、特にモノクロ表示にはなりませんでした。

     

    考えられる可能性:

    ・BITMAPINFO構造体の設定値がおかしい? (lpbmiの記載なし)

    ・コピー元のバッファのデータがおかしい? (byDispの記載なし)

     

    蛇足(問題解決には不要だけど、参考までに)

    ・CDC:: SelectObjectはHGDIOBJを引数に取るオーバーロードもあるので、APIのSelectObjectを直接使わない書き方もできます。

    ・CDCクラスにはDeleteDCメソッドがあります。

     

     

    2008年11月17日 14:48
    モデレータ
  • BITMAPINFO構造体の設定値は以下の通りです。

    Code Snippet
     lpbmi = new BITMAPINFO;
     lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
     lpbmi->bmiHeader.biWidth = DISP_X_SIZE;
     lpbmi->bmiHeader.biHeight = DISP_Y_SIZE;
     lpbmi->bmiHeader.biPlanes = 1;
     lpbmi->bmiHeader.biBitCount = 32;
     lpbmi->bmiHeader.biCompression = BI_RGB;
     lpbmi->bmiHeader.biSizeImage = 0;
     lpbmi->bmiHeader.biXPelsPerMeter = 0;
     lpbmi->bmiHeader.biYPelsPerMeter = 0;
     lpbmi->bmiHeader.biClrUsed = 0;
     lpbmi->bmiHeader.biClrImportant = 0;

     

     

    表示データに関しては、0x00~0xFFまでFILLして描画させてみましたが、黒から白まで段階的に

    変化していきます。

    やはり上手くいきません。

     

    何処か簡単なミスだと思うのですが、分かりません。

     

    2008年11月18日 1:27
  • こんな感じで塗りつぶすとどうなりますか?

    DWORD* p = (DWORD*)ppvBits;
    for (int i = 0; i < DISP_X_SIZE * DISP_Y_SIZE; ++i)
        *p++ = 0xff;

    2008年11月18日 4:17
  • 返信ありがとうございます。

    やってみました。

    すると、真青になりました。!?なんで?わからないです。

     

    ご教授、願います。

    2008年11月18日 4:23
  •  GREENMAIL さんからの引用

    返信ありがとうございます。

    やってみました。

    すると、真青になりました。!?なんで?わからないです。

     

    えーと、*pに0xffを入れたら多分青くなって当たり前な気がしますけれど。

    結局、0x000000ffを入れるのと同じ意味なんじゃないかと。

     

    逆にmemsetした場合は、RGB全て同じ値が入るからグレースケールになってもあたりまえかなと。

    まあ、この辺は書かれていないようなので解りませんけれど。

     

    2008年11月18日 6:18
  • biBitCount を 32 にしているので、1画素は 32bit = 4byte で表現されます。
    これを16進っぽく表現すると 0x00RRGGBB となります。
    (最上位の1バイトはアルファ値となる場合もありますが、今回はゼロということで)

    そして、p を DWORD* にキャストすると *p は DWORD(つまり4バイト)となり、
    0xff は 0x000000ff となって真っ青になるわけです。

    逆に、色はきちんと出ているとも言えます。
    ここで、最初のプログラムを見てみると、

    p = (BYTE*)ppvBits;
    for(y=239;y>=0;y--){
       memcpy(p,byDisp+y*DISP_X_SIZE*4,DISP_X_SIZE*4);
       p+=DISP_X_SIZE*4;
    }

    この部分で byDisp を p にコピーしているのですが、
    memcpy を使って byDisp の内容がそっくりそのまま p にコピーしているように見えます。
    これがモノクロになるということは、byDisp の内容がもともとモノクロということです。

    よって、Azulean さんのご指摘の通り、

    > ・コピー元のバッファのデータがおかしい? (byDispの記載なし)

    ということになります。

    2008年11月18日 7:59
  •  PATIO さんからの引用

    逆にmemsetした場合は、RGB全て同じ値が入るからグレースケールになってもあたりまえかなと。

    この辺を実感するための手法として、ペイントを使う方法を書いておきます。

     

    1.ペイントを起動する。

    2.色メニューから色の編集を選択する。

    3.色の作成ボタンを押す。

    4.赤・緑・青を同じ数字にする。(0~255のいずれか)

    5.いくつかの値を試してみて、赤・緑・青を同じ数値にしたときの色の特徴を見る。

     

    基本的に赤・緑・青(RGB)を同じ数値にすると、黒~白のグレースケール、白黒っぽくなります。

    ですので、memsetやFillMemoryではなく、ピクセルごとに色を設定するか、4バイト単位でコピーするかが必要になります。

    2008年11月18日 14:27
    モデレータ
  • おかげさまで、解決しました。

    ありがとうございました。

     

    表示データ(byDisp[])を作成する時に、1バイトずつセットしていたのですがRGBαのセット位置がずれていたのと、

    データを加工する方法が間違っていました。

     

    もう少し、表示データをいじって画面への表示傾向を掴むべきでした。ありがとうございました。

     

     

    あと少し、・・・・

    表示できたのは良かったのですが、しばらくするとメモリ不足でWindowsが落ちてしましました。

     

    今回の表示プログラムを、2秒毎にOnDraw()で表示更新しているのですが、メモリリークしてるみたいです。

     

    描画関数で開放しなければならない、物とは何があるのでしょうか?教えていただきたいです。

     

    その部分で、Createと名のつく関数は以下の2つなのですが、何かリークに関連ししそうな問題はあるでしょうか?

    ・SafeArrayCreateVector

    ・myBMP.CreateBitmap

    2008年11月19日 0:52
  •  GREENMAIL さんからの引用

    描画関数で開放しなければならない、物とは何があるのでしょうか?教えていただきたいです。

    それは描画関数で利用していて、解放が必要なものです。

    書いているコードによって違うため、一概には言えません。

    動的配列であったり、デバイスコンテキスト(DC)であったり、ビットマップであったり、ペンであったり、ブラシであったり、様々です。

     

     GREENMAIL さんからの引用

    その部分で、Createと名のつく関数は以下の2つなのですが、何かリークに関連ししそうな問題はあるでしょうか?

    ・SafeArrayCreateVector

    ・myBMP.CreateBitmap

    myBMPはCBitmapですよね。であれば、スコープを抜けた時点で自動的にDeleteObjectが実行されそうです。

    (明示的に呼んでも構いません)

     

    SafeArrayCreateVectorの戻り値をその後使わないのであれば、明示的に解放しなければなりません。

    SafeArrayCreateVectorのページから解放のための関数へのリンクがあります。

    http://msdn.microsoft.com/en-us/library/ms221558.aspx

    http://msdn.microsoft.com/en-us/library/ms221702.aspx

     

    ところで、なぜSafeArrayCreateVectorを使用しているのでしょうか?

    単純な配列の確保では実現できないことをされているのでしょうか?

    2008年11月19日 14:01
    モデレータ
  • ところで、なぜSafeArrayCreateVectorを使用しているのでしょうか?

    単純な配列の確保では実現できないことをされているのでしょうか?

     

    実際のところは、よく分かっていないのですが、BVとのCOMインタフェースの為に使っていると思います。

    (自信なし)

    製作中のプログラムと接続する相手のプログラムとのインタフェースが、SafeArrayを要求している為、

    使っています。

    2008年11月22日 6:18
  •  GREENMAIL さんからの引用

    実際のところは、よく分かっていないのですが、BVとのCOMインタフェースの為に使っていると思います。

    (自信なし)

    製作中のプログラムと接続する相手のプログラムとのインタフェースが、SafeArrayを要求している為、

    使っています。

    VB・COM周りであれば確かに仕方ないと思います。

    ただ、SafeArrayはどちらが解放するべきか、きっちりと意識して解放するようにして下さい。

    逆に解放してはならないタイミングもあるかもしれませんので。

    2008年11月22日 14:18
    モデレータ