こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

MFCプログラミング

MFCのダブルバッファリングを用いて画面を切り替えるプログラムを作ったのですが
画面がちらついてしまいます、どう修正すればよいか教えてください
// CgraphView 描画

void CgraphView::OnDraw(CDC* pDC)
{

CgraphDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;

// TODO: この場所にネイティブ データ用の描画コードを追加します。
CRect myRect;
GetClientRect(myRect);
if(background_color == 0)
pDC->FillSolidRect(myRect, RGB(255, 255, 255));
else if(background_color == 1)
pDC->FillSolidRect(myRect, RGB(153, 204, 255));
else
pDC->FillSolidRect(myRect, RGB(255, 153, 204));
//ダブルバッファに関するコード
CRect rc;
GetClientRect(&rc);
bkDC.CreateCompatibleDC(pDC);
bkBMP.CreateCompatibleBitmap(pDC, rc.right, rc.bottom);
CBitmap Bitmap, *pOldBitmap;
pOldBitmap = bkDC.SelectObject(&bkBMP);
//これから、すべての描画は、裏画面bkDCにおいて行う
bkDC.FillSolidRect(rc, RGB(255, 255, 255));
int x, y, sx, sy;

if(draw_state == 0)
{
bkDC.SetTextColor(RGB(0, 0, 0));
bkDC.TextOut(400, 500, _T("Start"));

sx = 128;//表示するビットマップの横の大きさ
sy = 128;//表示するビットマップの縦の大きさ
y = 300;//表示するビットマップの左上の頂点のy座標

x = 50;
draw_school(x, y, sx, sy);

x = 200;
draw_health(x, y, sx, sy);

x = 350;
draw_environment(x, y, sx, sy);
}

else if(draw_state == 1)
{
CRect myRect;
GetClientRect(myRect);
pDC->FillSolidRect(myRect, RGB(255, 255, 255));
}


//裏画面bkDCにおいて、すべての描画を行った後
//裏画面を表画面に送る
pDC->BitBlt(0, 0, rc.right, rc.bottom, &bkDC, 0, 0, SRCCOPY);
bkDC.SelectObject(pOldBitmap);
//裏画面を消去
bkBMP.DeleteObject();
bkDC.DeleteDC();

void CgraphView::OnInitialUpdate()
{
CView::OnInitialUpdate();

// TODO: ここに特定なコードを追加するか、もしくは基本クラスを呼び出してください。
bitmap[0].LoadBitmap(IDB_BITMAP1);
bitmap[1].LoadBitmap(IDB_BITMAP2);
bitmap[2].LoadBitmap(IDB_BITMAP3);
bitmap[3].LoadBitmap(IDB_BITMAP4);
bitmap[4].LoadBitmap(IDB_BITMAP5);
bitmap[5].LoadBitmap(IDB_BITMAP6);
Bmp_ID = 0;
//タイマーをスタート
SetTimer(ID_BITMAP, TIMER_MS_BITMAP, NULL);
//画面再描画のタイマーをスタートする
SetTimer(ID_REDRAW, TIMER_MS_FPS, NULL);
Bmp_ID = 0;

}

void CgraphView::OnTimer(UINT_PTR nIDEvent)
{
// TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
if(nIDEvent == ID_BITMAP)
Bmp_ID = (Bmp_ID+1) % 2;

if(nIDEvent == ID_REDRAW)
InvalidateRect(NULL, FALSE);

CView::OnTimer(nIDEvent);
}

void CgraphView::OnDestroy()
{
CView::OnDestroy();

// TODO: ここにメッセージ ハンドラ コードを追加します。
KillTimer(ID_BITMAP);
KillTimer(ID_REDRAW);
}

void CgraphView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
mouse_x = point.x;
mouse_y = point.y;

if(draw_state == 0)
{
if(mouse_x > 400 && mouse_y > 500)
{
draw_state = 1;
}
}

CView::OnLButtonDown(nFlags, point);
}

BOOL CgraphView::OnEraseBkgnd(CDC* pDC)
{
// TODO: ここにメッセージ ハンドラ コードを追加するか、既定の処理を呼び出します。
return TRUE;

//return CView::OnEraseBkgnd(pDC);
}

投稿日時 - 2012-07-15 08:45:32

QNo.7591175

困ってます

質問者が選んだベストアンサー

>ソースコードをもう一度掲載します
>(1)をどの場所で行うかは今の所わからない現状です。

まったくNo.2内容を読み取れてませんね。
(2)に答えが書いてあります。

(a)裏画面の方を塗りつぶして
(b)裏画面の描画全部終わってから
(c)表画面に転送する

この(a)(b)(c)をちゃんと実践してますか?
それぞれ実勢しているソースコードを書きだして下さい。

あと少なくとも(b)に反している部分があります。
else if( draw_state == 1 ) {
CRect myRect;
GetClientRect( myRect );
pDC->FillSolidRect( myRect, RGB( 255, 255, 255 ) );
}
この処理が必要な理由も私にはわかりませんが、pDCで無いことは確かです。

それとグラフを表画面に直接書くとチラつくと思います。
なぜ裏画面に書かないのでしょうか?
これも(b)に反してます。
あちこちから何も考えずにサンプルコードをコピーしてくるのは止めて下さい。

投稿日時 - 2012-07-16 22:25:45

補足

細かく説明していただきありがとうございます
ちゃんとちらつかずに画面を表示させることができました。

投稿日時 - 2012-07-16 22:40:45

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(11)

ANo.10

説明が正しいか判別不能なのでOnDrawは全て掲載して下さい。

ちなみに(1)を排除するという言い方はマズかったですね。
形を変えて何処かで行わないと行けません。
それはどの様な形でしょうか?

投稿日時 - 2012-07-16 21:58:57

補足

ソースコードをもう一度掲載します
(1)をどの場所で行うかは今の所わからない現状です。


void CgraphView::OnDraw(CDC* pDC)
{

CgraphDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;

// TODO: この場所にネイティブ データ用の描画コードを追加します。
//ダブルバッファに関するコード
CRect rc;
GetClientRect(&rc);
bkDC.CreateCompatibleDC(pDC);
bkBMP.CreateCompatibleBitmap(pDC, rc.right, rc.bottom);
CBitmap Bitmap, *pOldBitmap;
pOldBitmap = bkDC.SelectObject(&bkBMP);
//これから、すべての描画は、裏画面bkDCにおいて行う
bkDC.FillSolidRect(rc, RGB(255, 255, 255));
int x, y, sx, sy;

if(draw_state == 0)
{
bkDC.SetTextColor(RGB(0, 0, 0));
bkDC.TextOut(400, 500, _T("Start"));

sx = 128;//表示するビットマップの横の大きさ
sy = 128;//表示するビットマップの縦の大きさ
y = 300;//表示するビットマップの左上の頂点のy座標

x = 50;
draw_school(x, y, sx, sy);

x = 200;
draw_health(x, y, sx, sy);

x = 350;
draw_environment(x, y, sx, sy);
}

else if(draw_state == 1)
{
CRect myRect;
GetClientRect(myRect);
pDC->FillSolidRect(myRect, RGB(255, 255, 255));
}


//裏画面bkDCにおいて、すべての描画を行った後
//裏画面を表画面に送る
pDC->BitBlt(0, 0, rc.right, rc.bottom, &bkDC, 0, 0, SRCCOPY);
bkDC.SelectObject(pOldBitmap);
//裏画面を消去
bkBMP.DeleteObject();
bkDC.DeleteDC();


int x0=50, y0=450, xsp=700, ysp=400, xp, yp ;
// float xmax, ymax;
int DataN=pDoc->DN, i;

//pDC->SetTextColor(RGB(0,0,255));
//pDC->TextOut(0,0,"Title");//グラフタイトル

CPen Pen1(PS_SOLID,1,RGB(0,0,0)); //ペンの初期化
CPen Pen2(PS_SOLID,2,RGB(0,0,0));

CPen* OldPen=pDC->SelectObject(&Pen1); //ペンの変更

if(DataN!=0){
pDC->SelectObject(Pen2);
pDC->MoveTo(x0,y0);
pDC->LineTo(x0+xsp,y0);
pDC->MoveTo(x0,y0);
pDC->LineTo(x0 ,y0-ysp);

for(i=0;i<=DataN;i++){
xp=(int)(x0+pDoc->Dx[i]/100.0*xsp);
yp=(int)(y0-pDoc->Dy[i]/100.0*ysp);
if(i==0) pDC->MoveTo(xp,yp);
else pDC->LineTo(xp,yp);
}
}
pDC->SelectObject(OldPen);
}

投稿日時 - 2012-07-16 22:04:31

ANo.9

>(1)はわかりませんが、
直し方がわからないのは問題点が分からないからです。
(1)と(2)はセットで語られているのは分かりますか?
(1)の問題点を排除し、(2)の方法で修正するのです。
さて、何処を排除しますか?

>(2)は裏画面を表画面に送る処理を全ての描画が終わってからのところに持っていったのですが余計にちらついてしまいました

元から最後の処理にあるようですが?
最新のコードを掲載して下さい。

投稿日時 - 2012-07-16 21:44:44

補足

(1)では
CRect myRect;
GetClientRect(myRect);
if(background_color == 0)
pDC->FillSolidRect(myRect, RGB(255, 255, 255));
else if(background_color == 1)
pDC->FillSolidRect(myRect, RGB(153, 204, 255));
else
pDC->FillSolidRect(myRect, RGB(255, 153, 204));

の部分を排除しました



//裏画面bkDCにおいて、すべての描画を行った後
//裏画面を表画面に送る
pDC->BitBlt(0, 0, rc.right, rc.bottom, &bkDC, 0, 0, SRCCOPY);
bkDC.SelectObject(pOldBitmap);
//裏画面を消去
bkBMP.DeleteObject();
bkDC.DeleteDC();

の後にグラフを描画するコードが書かれていて
その後ろに持っていったということです

投稿日時 - 2012-07-16 21:51:59

ANo.8

では、(1)と(2)は何処を直すべきでしょう?
問題点は理解できたんですよね。

投稿日時 - 2012-07-16 21:15:49

補足

(1)はわかりませんが、(2)は裏画面を表画面に送る処理を全ての描画が終わってからのところに持っていったのですが余計にちらついてしまいました

投稿日時 - 2012-07-16 21:20:07

ANo.7

そこ以前の問題です。No.2の内容に(1)(2)と番号を振ったので質問全部に回答して下さい。

(1)そこで塗ったらCgraphView::OnEraseBkgnd()で背景塗らずに返した意味がありませんか? 塗りつぶした瞬間、画面がチラつきませんか??
質問:(1)が問題としているのは何処で何故でしょうか?

(2)
ダブルバッファリングしているのであれば、
「裏画面の方を塗りつぶして、裏画面の描画全部終わってから表画面に転送する」
というのが狙っている動作ではありませんか?
質問:(2)の代案はなぜ検討されていないのでしょうか?

># この場合クライアント領域全部の転送が必要ではありますが。
現状転送されてると思います。

>まぁ、ついでに言うなら裏画面用のデバイスコンテキストやビットマップの作成は先にやっておいて、WM_PAINTのたびに生成/破棄のコストを払うのも…>というのはありますが。
># WM_SIZEなどいくつかのメッセージに対応してデバイスコンテキストの再作>成などが必要になったりはしますけど。

これは難易度が高いので、それ以前の事ができてからです。
ちなみにOnDrawはWM_PAINTメッセージの結果呼び出されるのでOnDrawと言い換えて良いでしょう。

投稿日時 - 2012-07-16 19:23:53

補足

(1)が問題にしているのは背景を塗らずに返しているのに
再度塗り変えているため

(2)に関しては読み間違えていて、今理解できました

投稿日時 - 2012-07-16 19:29:30

ANo.6

No.2の何処の部分が分からないんでしょう?
そしてNo.2の回答が良く分からないにもかかわらず分からない点を何故質問しなかったのでしょうか?

>ダイアログで背景を変える設定をなくしても画面がちらつくのですが
>それはどうしてでしょうか

No.2の自分なりの理解として、この質問になったということですか?
それとも分らないことは無視したのでしょうか?
どのぐらい理解しているか知りたいのでNo.2の内容を解説してみて下さい。

投稿日時 - 2012-07-16 18:44:37

補足

No.2の

># この場合クライアント領域全部の転送が必要ではありますが。
>まぁ、ついでに言うなら裏画面用のデバイスコンテキストやビットマップの作成は先にやっておいて、WM_PAINTのたびに生成/破棄のコストを払うのも…>というのはありますが。
># WM_SIZEなどいくつかのメッセージに対応してデバイスコンテキストの再作>成などが必要になったりはしますけど。
の部分が理解できていません。ダブルバッファリングは仮想的なデバイスコンテキストに一度描画処理を行い、一通り描画が終わった段階で実際のデバイスコンテキストに描画結果を転送していることは理解しています

投稿日時 - 2012-07-16 19:01:54

ANo.5

>ちらつかないようにするにはどうしたら良いでしょうかね

既にNo.2で答えが書かれています。今までの回答を全部見なおして良く考えて質問して下さい。
Wr5さんなんか同じ答えを繰り返し書いているわけですから疲れ果てていると思います。こんな事を繰り返していると回答者が誰もいなくなりますよ。

投稿日時 - 2012-07-16 18:00:20

補足

見てもよく分からないので質問させてもらっているのですが
OnPaintの中に書けばいいのですか

投稿日時 - 2012-07-16 18:24:54

ANo.4

>そのときとはどの時のことを言っているんでしょうか
>draw_stateは最初の画面では0、Startというテキストをクリックしたとき1になるようになっています

文脈的に「ダイアログで背景を変える設定をなくしても画面がちらつくのですが」がその時としか受け取れないですよ。
つまりdraw_stateが1ならチラついて当たり前です。なぜなら、そうプログラムを組んでいるのですから。

投稿日時 - 2012-07-16 17:33:28

補足

ちらつかないようにするにはどうしたら良いでしょうかね

投稿日時 - 2012-07-16 17:50:55

ANo.3

Wr5

>ダイアログで背景を変える設定をなくしても画面がちらつくのですが

そのときのdraw_stateの値はいくつですか?
1だったらpDCに対して塗りつぶししているんですからチラつくと思いますが。
# 「どこで?」なんて言わないですよね?

投稿日時 - 2012-07-16 14:00:27

補足

そのときとはどの時のことを言っているんでしょうか
draw_stateは最初の画面では0、Startというテキストをクリックしたとき1になるようになっています

投稿日時 - 2012-07-16 14:09:52

ANo.2

Wr5

>上の部分はダイアログで背景を変える設定です

そこで塗ったらCgraphView::OnEraseBkgnd()で背景塗らずに返した意味がありませんか?
塗りつぶした瞬間、画面がチラつきませんか??
ダブルバッファリングしているのであれば、
「裏画面の方を塗りつぶして、裏画面の描画全部終わってから表画面に転送する」
というのが狙っている動作ではありませんか?
# この場合クライアント領域全部の転送が必要ではありますが。

まぁ、ついでに言うなら裏画面用のデバイスコンテキストやビットマップの作成は先にやっておいて、WM_PAINTのたびに生成/破棄のコストを払うのも…というのはありますが。
# WM_SIZEなどいくつかのメッセージに対応してデバイスコンテキストの再作成などが必要になったりはしますけど。

投稿日時 - 2012-07-16 00:49:56

補足

ダイアログで背景を変える設定をなくしても画面がちらつくのですが
それはどうしてでしょうか

投稿日時 - 2012-07-16 10:25:58

ANo.1

Wr5

>BOOL CgraphView::OnEraseBkgnd(CDC* pDC)
>{
>return TRUE;
>}

は正しいとして……

>if(background_color == 0)
>pDC->FillSolidRect(myRect, RGB(255, 255, 255));
>else if(background_color == 1)
>pDC->FillSolidRect(myRect, RGB(153, 204, 255));
>else
>pDC->FillSolidRect(myRect, RGB(255, 153, 204));

が、なにをやっているのか説明できますか?
その上で、

>bkDC.FillSolidRect(rc, RGB(255, 255, 255));
>pDC->FillSolidRect(myRect, RGB(255, 255, 255));
>pDC->BitBlt(0, 0, rc.right, rc.bottom, &bkDC, 0, 0, SRCCOPY);

のそれぞれがどういう結果をもたらすのか理解できますか?

投稿日時 - 2012-07-15 12:56:59

補足

>if(background_color == 0)
>pDC->FillSolidRect(myRect, RGB(255, 255, 255));
>else if(background_color == 1)
>pDC->FillSolidRect(myRect, RGB(153, 204, 255));
>else
>pDC->FillSolidRect(myRect, RGB(255, 153, 204));

上の部分はダイアログで背景を変える設定です

投稿日時 - 2012-07-15 14:08:22

あなたにオススメの質問