画面の描画ちらつきが解消できない


yosiki  2011-09-02 23:48:31  No: 72899  IP: 192.*.*.*

WindowsのGUI操作できるソフトを作成しているのですが、
Windows画面の描画のちらつきが発生します。
そのため、こちらで質問いたしました。

OS    :Windows XP
ツール:VC++2005 St
形式  :MFC、ダイアログベース

私のプログラムの中の、デバイスコンテキスト部分と思う箇所、
怪しいと思っている箇所をなるべく抜粋しました。
1秒間に1回、ONタイマで再描画を行っています。
裏画面用のビットマップを作成してBitBlt()を使用した
プログラムに変えて試していますが、真っ黒になったりしてしまいます。

BOOL Cxx_xx::OnInitDialog()
{
  CDialog::OnInitDialog();
  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX < 0xF000);
  CMenu* pSysMenu = GetSystemMenu(FALSE);
  if (pSysMenu != NULL){
    CString strAboutMenu;
    strAboutMenu.LoadString(IDS_ABOUTBOX);
    if (!strAboutMenu.IsEmpty()){
      pSysMenu->AppendMenu(MF_SEPARATOR);
      pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
  }
  SetIcon(m_hIcon, TRUE);      
  SetIcon(m_hIcon, FALSE);

  UpdateData(FALSE);
  m_pict.MoveWindow(RESO_1024MODE_PICT_SX, RESO_1024MODE_PICT_SY, 905, 560);
}

void Cxx_xx::OnPaint()
{
  if (IsIconic()){
    CPaintDC dc(this); // 描画のデバイス コンテキスト
    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    // クライアントの四角形領域内の中央
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2;
    // アイコンの描画
    dc.DrawIcon(x, y, m_hIcon);}
  else
  {
  
      CClientDC  myPictDC(&m_pict);
    CPen    myPEN, *oldPEN;
    CRect    myRECT;
    CDC* pDC=m_pict.GetDC();
    m_pict.GetClientRect(myRECT);
    
    
    //本描画処理
    pDC->FillSolidRect(myRECT,RGB(250,250,240));
    myPEN.CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
    oldPEN = pDC->SelectObject(&myPEN);
    pDC->MoveTo(0,100);
    pDC->LineTo(100,100);
    
    m_pict.ReleaseDC(pDC);
  }


  CDialog::OnPaint();
}


//1秒のonタイマ
void Cxx_xx::OnTimer(UINT_PTR nIDEvent){

  Invalidate(FALSE);

}

void Cxx_xx::DoDataExchange(CDataExchange* pDX)
{
  DDX_Control(pDX, IDC_PICT, m_pict);
}


情報不足とのことでしたら、追記致します。
よろしくお願い致します。

編集 削除
yosiki  2011-09-03 12:36:41  No: 72900  IP: 192.*.*.*

追記です。

OnPaint()のみを以下に変更して、
裏画像を使用したダブルバッファリング処理に
しましたが、今度は何も表示されなくなってしまいました。。

void Cfullcolor_illumiDlg::OnPaint()
{
  if (IsIconic()){
    CPaintDC dc(this); // 描画のデバイス コンテキスト
    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    // クライアントの四角形領域内の中央
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2;
    // アイコンの描画
    dc.DrawIcon(x, y, m_hIcon);}
  else
  {
     CClientDC  myPictDC(&m_pict);
    CPen    myPEN, *oldPEN;
    CRect    myRECT;
    CDC* pDC=m_pict.GetDC();
    m_pict.GetClientRect(myRECT);

    //ダブルバッファリング処理用
      CDC     memDC;
    HDC     db_mem;
      pDC->CreateCompatibleDC(m_pict.GetDC());
      CBitmap memBmp;
      memBmp.CreateCompatibleBitmap(m_pict.GetDC(), myRECT.Width(), myRECT.Height());
      CBitmap* memOldBmp=pDC->SelectObject(&memBmp);

    switch(timer_paint_flag){
        case 0:
        break;

        case 1:
          //秒数の箇所だけ上塗り
          m_pict.GetClientRect(myRECT);
          pDC->FillSolidRect(DISP_TIME_SX - 2, 200, 120, 20, RGB(192,192,192));
          pDC->FillSolidRect(myRECT,RGB(192, 192, 192));
        break;

        case 2:
          for(loop_day = 0; loop_day < 32; loop_day++){
                //日毎の横線  描写
              if(loop_day == 0){
              }
              else{
                  pDC->MoveTo(0, (loop_day * reso_magni1));
                  pDC->LineTo(X_LINE_RIGHT_MAX, (loop_day * reso_magni1));
              }
          }
          break;

        default:
          break;
      }

      //ダブルバッファリング処理(仮画像→表示画像 へ転送処理)
        BitBlt(pDC->GetSafeHdc(), 0, 0, myRECT.Width(), myRECT.Height(), pDC->GetSafeHdc(), 0, 0, SRCCOPY);
        //仮想デバイスコンテキストのビットマップを初期化
        pDC->SelectObject(memOldBmp);
        //CreateCompatibleDCのリソース開放DeleteDC(memDC);
        //仮想デバイスコンテキストのビットマップを廃棄
        memBmp.DeleteObject();
    }
  CDialog::OnPaint();
}

編集 削除
yosiki  2011-09-03 12:40:53  No: 72901  IP: 192.*.*.*

2回目の発言で抜粋ミスがあり
1回目とプログラムが変わっていました。(switch文など)
本質的な箇所の質問は、変わりません。
ご迷惑をおかけします。

編集 削除
仲澤@失業者  2011-09-05 11:33:02  No: 72902  IP: 192.*.*.*

いろいろと突っ込みどころがあるので、どれから手を付けてよいものやら
悩むところですが、気になる点だけ指摘しておきます。

1.OnPaintではCPaintDC以外は使ってはいけません。
  CClientDCなどを使わざるを得ないコードは別のメッセージ処理時
  のコードに移動しなければなりません。
2.一旦ダブルバッファのことは忘れましょう。
  普通のコードでまともに動かなければ、ダブルバッファを検討する
  段階にはありません。(最近多い「いきなり君」はやめましょう(vv;))
3.DLGは一般のウインドウより、強情に背景を塗りつぶします。
  クライアント領域の無効化を行う場合は、ターゲットの矩形部分
  だけにしましょう。大雑把なコードはみっともない表示の原因に
  なります。
4.InvalidateRect()だけでは再描画(WM_PAINT)は発生しません。
  その直後でUpdateWindow()を実行すると、その時点で、対象
  HWNDにWM_ERASEBKGND、WM_PAINTなどの再描画関連メッセージが
  送付されるます。

以上は、ほんの基本です。

編集 削除
ryo  2011-09-05 13:38:54  No: 72903  IP: 192.*.*.*

void Cxx_xx::OnPaint()
{
if (IsIconic()){

}
else
{

}
CDialog::OnPaint();
}

なんで、CDialog::OnPaint();がこんな位置に来てるの?

編集 削除
yosiki  2011-09-07 23:01:16  No: 72904  IP: 192.*.*.*

コメントありがとうございます。
ご参考として、読ませて頂きます。

}
CDialog::OnPaint();
}
は、確かに・・  
理由はありません。

編集 削除
モッチー  2019-10-20 11:15:30  No: 148295  IP: 192.*.*.*

>>yosiki  2011-09-03 21:36:41  No: 72900
>>裏画像を使用したダブルバッファリング処理に
>>しましたが、今度は何も表示されなくなってしまいました。。
>>//ダブルバッファリング処理(仮画像→表示画像 へ転送処理)
>>BitBlt(pDC->GetSafeHdc(), 0, 0, myRECT.Width(), myRECT.Height(), pDC->GetSafeHdc(), 0, 0, SRCCOPY); 

(1)根本的な記述ミスです。
BitBltの転送元と転送先のデバイスコンテキストのハンドルが同じでは何も表示できないでしょう。
スケッチブックの2ページ目から2ページ目にコピーしてっと、1ページ目に書いたイメージがコピーされないって言ってるようなもん。
二つ目の pDC->GetSafeHdc() を myPictDC.GetSafeHdc() の様に myPictDC を指定するべきでは?

(2)何故 BitBlt 関数を使いデバイスコンテキストのハンドルを渡しているのでしょうか?
CDialogクラスを継承したクラスの関数内では単に BitBlt を使うと
C++の文法上 ::BitBlt というように :: を付けたの同然、グローバル領域のWin32APIを直接呼び出している事になる。
MFC を使っている意味が無くなってしまう。CDC クラスを継承したオブジェクト内なら意味というかメリットが有るけれど。
MFCにはデバイスコンテキストクラスがあり、CDialogクラスを継承したクラス(Cfullcolor_illumiDlg)オブジェクトの中のOnPaint関数から、
CDCクラスやCPaintDC又は、CClientDC クラスのオブジェクトのインスタンスを使って例えば
CPaintDC pdc (this);
pdc.BitBlt (0, 0, myRECT.Width(), myRECT.Height(), &myPictDC, 0, 0, SRCCOPY);
としなきゃいけない。デバイスコンテキストのハンドルが CDC クラスなどでラッパーされているので、ハンドルを指定しなくて済む。

私が以前用いたメモリデバイスコンテキストは以下のようなもの(2004年頃から長らく使っていた)
void Cxx_xx::OnPaint ()
{
     // クライアントウインドウの領域(位置及び大きさ)を取得します
     CRect rcClientWnd;
     GetClientRect (&rcClientWnd);
     if (!rcClientWnd.Width () || !rcClientWnd.Height ())
     {
          // (水平又は垂直方向の大きさが 0 である) -- メモリデバイスコンテキストに割り当てるビットマップ領域の作成やイメージの転写などでビットマップ領域が無ければ意味がないので終了します
          return;
     }

     // デバイスコンテキストの属性と同じメモリデバイスコンテキストを作ります
     CPaintDC pdc (this);
     CDC pMemDC;
     if (pMemDC.CreateCompatibleDC (&pdc))
     {
          // (作成に成功) -- メモリデバイスコンテキスト用のビットマップ領域を確保します
          CBitmap pMemBmp;
          if (pMemBmp.CreateBitmap (rcClientWnd.Width (), rcClientWnd.Height ()))
          {
               // (作成に成功) -- メモリデバイスコンテキストにビットマップ領域をマップ(割り当てとも云う?)
               CBitmap* pOrgBmp = pMemDC.SelectObject (&pMemBmp);

               // ここからメモリデバイスコンテキストにイメージを書込みます
               int nOrgBkMode = pMemDC.SetBkMode (TRANSPARENT); // 透過モードに切り換えます(文字の背景や幾何学図形の描画に影響する)
               COLORREF crOrgText = pMemDC.SetTextColor (RGB (0xFF, 0x00, 0x00)); // 赤色に切り換えます

               // 背景を塗り潰しします。
/* 
   Description: 
     ダイアログの背景を塗り潰す色を取得して用いるか、使用したい背景の色を予め
     ウインドウクラス(MFCのではなくWindowシステム上のウインドウを作る上で定
     義するWNDCLASSとかいうもの)のhbBackgndだったかな綴り忘れた…。)を
     変更する。
     そしてOnBackgrndErase をオーバーライドして単にreturn (TRUE)する等色々しな
     ければならない。
 */
               COLORREF crBkgrnd = RGB (0xFF, 0xFF, 0xFF); // 白色の (赤色にしたい場合 RGB (0xFF, 0x00, 0x00) の様にします)
               CBrush pBrush (); // ブラシオブジェクトを作成します
               pMemDC.FillRect (&rcCleintWnd, &pBrush);

               // テキストを書込みます
               CString pstrTextMessage;
#ifdef    UNICODE
               pstrTextMessage = L"abc 0123"; // (UNICODEの場合)
#else
               pstrTextMessage = _T ("abc 0123"); // (非UNICODEの場合)
#endif    /* end of #ifdef UNICODE */

               pMemDC.TextOut (0, 0, pstrTextMessage); // テキストを左上隅に出力します

               pMemDC.SetTextColor (crOrgText); // オリジナルに戻します
               pMemDC.SetBkMode (nOrgBkMode); // オリジナルに戻します

               // 本家本元のデバイスコンテキストにイメージを転写します
               pdc.StretchBlt
               (
                    0,
                    0,
                    rcClientWnd.Width (),
                    rcClientWnd.Height (),
                    &pMemDC,
                    0,
                    0,
                    rcClientWnd.Width (),
                    rcClientWnd.Height (),
                    SRCCOPY
               );

               // メモリデバイスコンテキストのビットマップ領域をオリジナルに戻します
               pMemDC.SelectObject (pOrgBmp);
               // 用済みビットマップ領域を開放します
               pMemBmp.DeleteObject ();
          }
          // メモリデバイスコンテキストも開放します
          pMemDC.DeleteDC ();
     }
}

編集 削除
モッチー  2019-10-20 11:27:13  No: 148296  IP: 192.*.*.*

一部訂正します
誤)
CBrush pBrush (); // ブラシオブジェクトを作成します 
正)
CBrush pBrush (crBkgrnd); // ブラシオブジェクトを作成します 

編集 削除