InvalidateRgn()について

このQ&Aのポイント
  • InvalidateRgn()を使用したプログラムで円の部分が欠ける問題について
  • ペンの太さに応じた余白を設定することで問題が解決した
  • ペンの太さによって欠ける部分が異なる理由について
回答を見る
  • ベストアンサー

InvalidateRgn()について

ボールが右下に動いていくだけのプログラムです。 はじめはInvalidateRect(hWnd,NULL,.TRUE)を使っていたのですが画面がちらつくので InvalidateRgnを使ってみました。そこで円の部分をリージョンで指定して背面消去したら 図1のようになってしまったので、 ペンの太さを考慮してなかったのが問題だと思って円の周辺に余裕を持ってリージョンを 設定して以下のようなプログラムになったのですが(スレッドの中身です) 図2のように進行方向の部分が欠けてしまいます。 その後余白の3の部分を7以上にしたら元の円のまま動きました。 ペンの太さは3なので十分だと思ったのですが6以下でなぜ欠けてしまうんでしょうか? while(1){ hRgn = CreateEllipticRgn(x - hankei - 3, y - hankei - 3, x + hankei + 3, y + hankei + 3); x = x + 4; y = y + 2; InvalidateRgn(hWnd,hRgn,TRUE); Sleep(100); }

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

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

InvalidateRgnで無効にした領域だけを再描画するため 右下が欠けたようになるのだと思います CreateEllipticRgnでリュージョンを生成するのを 描画側でやります 描画には FillRgnとFrameRgnで行い 描画位置更新イベント側では GetRgnBoxでリュージョンの矩形範囲を取得し 進行方向へ膨らませて InvalidateRectを実行して見ましょう // 描画側 hRgn = CreateEllipticRgn(x - hankei, y - hankei, x + hankei, y + hankei); HBRUSH hBrFrame = CreateSolidBrush(RGB(255,0,0)); // hBr: 塗りつぶしようのブラシは生成済みと仮定 FillRgn(hWnd, hRgn, hBr); FrameRgn(hWnd, hRgn, hBrFrame, 3, 3); DeleteObject(hBrFrame); // 消去側 RECT r = {0}; if (hRgn) {   // 矩形範囲を取得   GetRgnBox(hRgn, &r);   // 再描画の増分を考慮   r.right += 4;   r.bottom += 2;   /*     左上に動かすなら left, topを更新     右上なら right,top     左下なら left,bottom   */   InvalidateRect(hWnd, &r, TRUE);   // 使用済みなので リュージョンを破棄   if (DeleteObject(hRgn)) {     hRgn = NULL;   } } x += 4; y += 2;

luna3x
質問者

補足

回答ありがとうございます。 InvalidateRgnを使ったとき背面消去だけリージョンの範囲で行われて 描画は全範囲で行われるものと勘違いしていました。 消去も描画もリージョンの部分のみ行われていたから進行方向のはみだした 部分について描画されなかったんですね。 描画はペンとブラシでEllipse()を使っていたんですが、 FillRgnとFrameRgnでやるんですね。 これを使えばペンの太さとか気にせずに 描画した部分に対してぴったり無効化の範囲を指定できるって事でしょうか? また更新部分のリージョンの指定で以下のようなものを考えてみたのですが 矩形の方が処理が早いとか何かメリットがあるんでしょうか? それとも今回の質問の問題箇所がそれで解決されるので簡単にして書いただけですかね? またいくつか質問になってしまいましたが、よろしくお願い致します。 hRgn1 = CreateEllipticRgn(x - hankei, y - hankei, x + hankei, y + hankei); ボールの位置を変化させる(x,yを変化させる) hRgn2 = CreateEllipticRgn(x - hankei, y - hankei, x + hankei, y + hankei); CombineRgn(hRgn1, hRgn1, hRgn2, RGN_OR); InvalidateRgn(hWnd, hRgn1, TRUE); Sleep(50);

その他の回答 (1)

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.2

Ellipseでの描画は 指定された矩形範囲に内接する楕円を描画するのですが 内接するのはペンの中心です // Pen,Brushは選択済みとします Ellipse(hdc, 50, 50, 100, 100); hRgn = CreateEllipseRgn(50,50,100,100); InvalidateRgn(hWnd, hRgn, TRUE); といったコード実行すると Ellipseで描画した縁が消去されないと思います FillRgn/FrameRgnを使えば 生成したリュージョン内での描画になるので上記のような問題は解決されるでしょう 再描画に関してですが WM_PAINTイベントの実装により変わってきますよ BeginPaintを呼び出した際に 返される PAINTSTRUCT構造体psのrcPaintが無効にされた領域です rcPaintだけを BitBltなどで転送する実装だと GetRgnBoxで取得できる領域と等価だとおもいます 無関係にクライアント領域を書き直す実装だと 無駄が多いようにおもいます

luna3x
質問者

お礼

試してみて、リージョンによる描画の違いなど理解しました。 ありがとうございました。

関連するQ&A

  • PathToRegion()について

    case WM_TIMER: hdc = GetDC(hwnd); SelectObject(hdc, font); SetBkMode(hdc, TRANSPARENT); BeginPath(hdc); TextOut(hdc, 0, 0, mt->ShowNowTime(mt), lstrlen(mt->ShowNowTime(mt))); EndPath(hdc); hRgn = PathToRegion(hdc); ←ここです。 SelectObject(hdc , GetStockObject(SYSTEM_FONT)); ReleaseDC(hwnd, hdc); SetWindowRgn(hwnd, hRgn, TRUE); InvalidateRect(hwnd, NULL, FALSE); return 0; 時計(文字のみ)のデスクトップアクセサリーをつくろうと思ってますが、 hRgn = hRgn = PathToRegion(hdc);で繰り返し同じhRgnで生成したら、 hRgnのオブジェクト自体はまた生成されるのでしょうか?それともリージョンの演算結果が変わるだけでしょうか? このソースはメモリリークをおこしているでしょうか? PathToRegionはどのような動作をしているのでしょうか? 分かる方教えてください。 (いい文章の表現方法がわからなかったため直接疑問点を書いてみました。文章へたですみません。)

  • InvalidateRectがうまくいかない

    ボールがあったとしてWM_TIMERによって 一定の速度でy方向に動いていたとします。 ある条件(例えば5回に1度とか)で y方向に行くのを一旦やめて x方向に一定の間隔で5マス移動するとします。 while(aaa() > 0){ x++; Sleep(100); InvalidateRect(hWnd, NULL, TRUE); } このようなプログラムです。 aaa()は5回呼ばれると0を返します。 これでx==0から始まったとすれば x==1,x==2,x==3,・・・・とボールが移動する様子が表示されると 思っていたのですが、 実際には5回Sleep(100)したくらいの時間が経ってから 一気にx==5の位置まで移動して表示されました。 デバッグして追ってみたら while文は5回ループして InvalidateRect(hWnd, NULL, TRUE)もちゃんと 5回行われているようでした。 どういうことなんでしょうか?

  • InvalidateRectの使い方について

    InvalidateRectの使い方ですが、ヘルプを見たところ、下記のように表記されていました。 BOOL InvalidateRect( HWND hWnd, // handle of window with changed update region CONST RECT *lpRect, // address of rectangle coordinates BOOL bErase // erase-background flag ); しかし、実際、使ってみると InvalidateRect(&rct,FALSE);とするだけで、コンパイルは通ります。 ※&rctはlpRectのように再描画する領域を指定しています。 私の希望としては、指定した部分だけ再描画をかけ、指定外の部分は、再描画されず、画像がのこり、最終的には一つの絵となって欲しいのですが。 これは引数が2つだから起きているアクシデントなのでしょうか。 InvalidateRctの使い方を教えていただければと思います。よろしくお願いします。

  • なぜCreateHatchBushの設定が途中で喪失するのか

    いつもお世話になります。 縦縞の四角形を表示するプログラムですが、ある一定の四角形を描画すると四角形の中の縦縞がなくなり、白色になります。 原因が分かりません。アドバイスをお願い致します。 (四角形をマウスドラッグ中に小さくすると黒い線がたくさんでてきますが、これはアプリケーションの仕様です) プロシージャソースは以下の通り。 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; static POINT start, end; static bool push; switch(msg) { case WM_CREATE: push = false; break; case WM_LBUTTONDOWN: start.x = LOWORD(lParam); start.y = HIWORD(lParam); push = true; break; case WM_MOUSEMOVE: if(push){ end.x = LOWORD(lParam); end.y = HIWORD(lParam); InvalidateRect(hWnd, NULL, FALSE); } break; case WM_LBUTTONUP: end.x = LOWORD(lParam); end.y = HIWORD(lParam); push = false; InvalidateRect(hWnd, NULL, FALSE); break; case WM_PAINT: HBRUSH hBrush; hDC = BeginPaint(hWnd, &ps); hBrush = CreateHatchBrush(HS_VERTICAL, RGB(255, 0, 0)); SelectObject(hDC, hBrush); Rectangle(hDC, start.x, start.y, end.x, end.y); DeleteObject(hBrush); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); } よろしくお願い致します。

  • [Windows][C] ゲームプログラムが実行途中に止まってしまいます

    いつもお世話になっております。 今アクションゲームの基盤となる簡単な動きをさせるプログラムを作成したのですが、なぜだか時間が経つと止まるという不思議な現象に陥っています。 プログラムの内容はジャンプや移動をするだけの単純なもので、描画やキャラの移動はスレッドで個別に行っています。 キャラが止まったとき、FPSは動いていたので、描画の方のスレッドにたぶん問題はないと思っています。 キャラが止まると同時にゲーム自体もおかしくなってしまうのですが、それはキャラ動作のスレッドが異常によって止まってしまったからなのでしょうか? 解決法のわかる方、すみませんがご指導お願いいたします。 以下に問題のあると思われるスレッドを貼ります。 描画やプロシージャに問題があると思われる方がいましたら補足で追加いたします。 /*************** キャラクターを動かす ****************/ DWORD WINAPI CharacterMove(LPVOID vdParam) {  HDC hdc;  RECT rect;  ThreadParam * param;  HRGN hit = CreateRectRgn(0, 0, 0, 0);  int JUMP = 0;  int DBJUMP = 0;  int position = 0;  int flag = 0;  param = (ThreadParam *)vdParam;  GetClientRect(param->owner, &rect);  while(TRUE){   /* 左移動 */   if(character.x > rect.left + 5){    if(GetAsyncKeyState(37)){     if(CombineRgn(           hit,           CreateEllipticRgn(character.x - 12, character.y, character.x - 12 + 25, character.y + 25),           S_RGN, RGN_AND) == NULLREGION){      character.x -= 12;     }    }   }   /* 右移動 */   if(character.x + 25 < rect.right - 5){    if(GetAsyncKeyState(39)){     if(CombineRgn(           hit,           CreateEllipticRgn(character.x + 12, character.y, character.x + 12 + 25, character.y + 25),           S_RGN, RGN_AND) == NULLREGION){      character.x += 12;     }    }   }   /*** ジャンプ ***/   if(CombineRgn(         hit,         CreateEllipticRgn(character.x, character.y + 1, character.x + 25, character.y + 1 + 25),         S_RGN, RGN_AND) != NULLREGION){    if(GetAsyncKeyState(38)){     JUMP = 32;    }   }   /***** ジャンプ時・落下時 *****/   if(CombineRgn(         hit,         CreateEllipticRgn(character.x, character.y + 1, character.x + 25, character.y + 1 + 25),         S_RGN, RGN_AND) == NULLREGION || JUMP == 32){    /*** 一度キーを離さないと2段ジャンプは成立しない ***/    if(!GetAsyncKeyState(38)){     flag = 1;    }    /*** 2段ジャンプ ***/    if(GetAsyncKeyState(38) && flag == 1 && DBJUMP < 1){     JUMP = 26;     DBJUMP++;    }    /*** 慣性 ***/    if(CombineRgn(          hit,          CreateEllipticRgn(character.x, character.y - JUMP, character.x + 25, character.y - JUMP + 25),          S_RGN, RGN_AND) != NULLREGION){     flag = 0;     DBJUMP = 0;     if(JUMP < 0){      while(CombineRgn(             hit,             CreateEllipticRgn(character.x, character.y + 1, character.x + 25, character.y + 1 + 25),             S_RGN, RGN_AND) == NULLREGION){       character.y++;      }     }else{      while(CombineRgn(             hit,             CreateEllipticRgn(character.x, character.y -1, character.x + 25, character.y + 1 + 25),             S_RGN, RGN_AND) == NULLREGION){       character.y--;      }     }     JUMP = 0;    }else{     character.y -= JUMP;    }    /* 最大落下速度 */    if(JUMP > -20){     JUMP -= 3;    }   }   Sleep(16);  }  DeleteObject(hit);  ReleaseDC(param->owner, hdc);  return TRUE; }

  • ダイアログアプリのウィンドウ領域について

    いつもお世話になります。 VC++6.0MFCダイアログベースでアプリを開発しているのですが、 ビットマップ画像(100×100)をダイアログに貼り付けています。 円の画像ですが、ビットマップなので四角の画像になっています。 そこで楕円のリージョンを作って、 // 楕円のリージョンを作る m_rgn.CreateEllipticRgn(0,0,100,100); // ウィンドウのリージョンを設定する SetWindowRgn((HRGN)m_rgn ,TRUE); こんな感じで表示したらウィンドウは丸くなっています。 そこで上記で作成したリージョンの上をマウスがオーバーしたら 画像を差し替えたいのですが、どうしても、ダイアログ の領域(四角)で反応してしまいます。 CPoint pos; CRect rect; GetCursorPos(&pos); GetWindowRect(&rect); if (rect.PtInRect(pos)) { // マウスが上に来ている m_bMouseOver = TRUE; } else { // マウスは上にはない m_bMouseOver = FALSE; } こんな感じでm_bMouseOverの中身を見て画像を差し替えています。 リージョンで作成した楕円の上だけで反応させるには どのようにすればいいのでしょうか? わかりにくいかもしれませんがアドバイスよろしくお願い致します。

  • MFCでウィンドウ枠をアニメーションさせたい

    こんにちは、VC++初心者です。 今MFCでデスクトップマスコットを作りたいと思っています。 MFCのSDIアプリでとりあえず作成している最中で、 BMP画像の表示はC***ViewクラスのOnDraw関数で表示させています。 問題はウィンドウ枠です。 スタイル指定をWS_POPUPにして、クライアント領域にのみ表示させていますが、 リージョンを使って、BMP画像を切り取った形のウィンドウにするのにてこずっています。 ちうか、そもそもOnDraw関数内でBMP表示とともに行っている、 SetWindowRgn関数がうまく動いてくれていない状態です(泣)。 ソースの一部をのせておきますで、どうかご判断ください。 宜しくお願いします。 CTestView::CTestView() { //コンストラクタでビットマップの読み込みとリージョンの設定 gazou.LoadBitmap( IDB_GAZOU); //CBitmapのメンバ変数 m_rgn[0].CreateEllipticRgn(0, 0, 48,48); //CRgnのメンバ変数1 m_rgn[1].CreateRectRgn(0, 0, 48,48); //CRgnのメンバ変数1 } void CTestView::OnDraw(CDC* pDC) { //画像表示及びリージョンの切り替え 中略... static int anim = 0; CDC dc; dc.CreateCompatibleDC( pDC); dc.SelectObject( gazou); pDC->BitBlt(0,0,48,48,&dc,0,0,SRCAND); //48*48ドットのBMP HRGN hrgn =(HRGN)m_rgn[anim].Detach(); SetWindowRgn( hrgn, true); dc.DeleteDC(); anim ^= 1; 中略... }

  • 数学II 領域の問題です

    数学II 領域の問題です (X^2+y^2-2)(x^2+y^2-4x+2)が0以下 で表される領域の面積を求めよ。 という問題です。 自分なりに考えて、 (1) x^2+y^2-2が0以上 かつ x^2+y^2-4x+2が0以下 (2) x^2+y^2-2が0以下 かつ x^2+y^2-4x+2が0以上 の場合に分けて、 図を書いてみると、 2つの円の重なりあう部分以外が求める面積なんですが、 その面積の求め方がわかりません。 どなたか教えて下さい(>_<)

  • スレッドの終了を知りたい(WindowsAPI)

    CreateThread()で作成したスレッドの終了を知りたい (具体的には、スレッドが終了するまで待機したい)のですが、 うまくいかず困っています。WindowsAPIに関する本やネットで調べた ところ、WaitForSingleObject()が適用できると考え、 以下のようなプログラムを作成したのですが、 元のスレッドがWaitForSingleObject()のところで 止まると同時に、CreateThread()で作成されたThread_1()も 止まってしまいます。アドバイスいただけますでしょうか。 ----プログラム(該当部分)ここから---- DWORD Thread_1(LPVOID param) {  int i;  char buff[128];  /* iが99のときのみ終了してよい */  while(g_iFlg == 1)  {   for(i = 0; i < 100; i++)   {    Sleep(100);    wsprintf(buff, "%d", i);    SetDlgItemText((HWND)param, IDC_STATIC_1, buff);   }  }  ExitThread(0);  return 0; } BOOL CALLBACK Proc_2(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {  switch(uMsg)  {   case WM_INITDIALOG:    g_iFlg = 1;    g_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Thread_1, (LPVOID)hDlg, 0, &g_dwThread);    return TRUE;   case WM_COMMAND:    switch(LOWORD(wParam))    {     case IDC_BUTTON_CANCEL:     case IDCANCEL:      g_iFlg = 0;      WaitForSingleObject(g_hThread, INFINITE);      CloseHandle(g_hThread);      EndDialog(hDlg, 0);      return TRUE;    }  }  return FALSE; } ----プログラム(該当部分)ここまで----

  • 色描画

    以下は、ウインドウプロシージャ内でのプログラムです。 RGB( )ですが、0xFFは、255の事ですよね? x*0xFF/rect.rightこの計算式は、何を求めているんですか? 教えてください。 HDC         hdc; PAINTSTRUCT  ps; COLORREF    color; LONG        x,y; RECT        rect; switch(umsg){   case  WM_PAINT:       hdc =BeginPaint(hwnd,&ps);       GetClientRect(hwnd,&rect);       for(y=0;y<rect.bottom;y++){          for(x=0;x<rect.right;x++){            color=RGB(x*0xFF/rect.right,0,0);            SetPixel(hdc,x,y,color);          }       }

専門家に質問してみよう