• ベストアンサー

GDI+で高速な描画

GDI+を利用してお絵かきツールのようなものを作成しようと思っています。 しかしdrawImage()での画面への描画が非常に遅いようで、描いてみると線がカクカクしてしまいます。 アンチエイリアスやアルファブレンドが必要なのでGDI+を利用したいのですが、どうにか高速に描画させる方法はないでしょうか? 下記は現在のソースの一部です。 static Bitmap *offscreenBitmap //描画処理はこっちに static Graphics *offscreen; static Graphics *onscreen; //画面表示用 static RECT rect; //クライアント領域 static POINTS posPts,pts; //一つ前と現在のマウス座標 static BOOL bLButtonDown; Pen nomalPen(Color(100,0,0,0), 1); //描画用ペン switch(msg){  case WM_CREATE:   GetClientRect(hWnd,&rect);   offscreenBitmap = new Bitmap(rect.right, rect.bottom); //ビットマップ生成   offscreen = new Graphics(offscreenBitmap);   offscreen->SetCompositingMode(Gdiplus::CompositingModeSourceOver);   offscreen->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);//アンチエイリアス有効化   offscreen->Clear(Color(255,255,255)); //初期化   onscreen = new Graphics(hWnd); //ウインドウ表示用   onscreen->SetCompositingMode(CompositingModeSourceCopy);   break;  case WM_LBTTONDOWN:   bLButtonDown = TRUE;   posPts=MAKEPOINTS(lp)   break;  case WM_MOVE:   pts=MAKEPOINTS(lp)   if(bLButtonDown){    offscreen->DrawLine(&nomalPen, posPts.x, posPts.y, pts.x, pts.y);//線描画    posPts.x=pts.x;    posPts.y=pts.y;   }   InvalidateRect(hWnd,&rect,0);//ウインドウを更新   break;  case WM_PAINT:   onscreen->DrawImage(offscreenBitmap,0,0);//画面に書き出し   break; }

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

  • ベストアンサー
回答No.3

昔の絵描きツールでは一般に 1.MouseがDown->Move->UpまではキャッシュDC(GetDC())に書き出す。 2.この間描画データ(=1ストローク=座標の配列)は別途保管 3.Mouse Upで、裏DCに1ストロークを描画しなおし。 4.アンドInvalidateRectする(もちろんストロークデータを   含む矩形のみを無効化)。 5.WM_PAINTで「裏->表」転送を実行 てな感じです。最近はPCが早いのでこうなってない場合も多いです。 どこか参考になるところがあれば幸いです。

cabin_
質問者

お礼

回答ありがとうございます。 getDC()で得られるDCってことはこの場合Onscreenってことですかね。ちらつきを抑止したいのでこれは試していませんが、無効化の領域を絞ることである程度速度が改善しました。

その他の回答 (2)

回答No.2

 こんばんは。  offscreen->DrawLine(&nomalPen, posPts.x, posPts.y, pts.x, pts.y);  の代わりに  onscreen->DrawLine(&nomalPen, posPts.x, posPts.y, pts.x, pts.y);  へ直描きした場合が一番高速でした。  バックバッファ(オフスクリーン)への書き込みが相当に重たいようです。  初めてマウスを押したポイントと、離されたポイントの矩形領域を、バックバッファにストアした方が効果的かもしれません(アンドゥ・リドゥ程度ならば此れ出来そう)。  但し、リアルタイムにエフェクターを掛けながら描写をする場合は、どうしても作業用のバックバッファが必要になりそうです。  一応CachedBitmapと言うのが速いらしいのですが、  「参考資料」  http://lamoo.s53.xrea.com/develop/gdiplus/gdiplus_blt_old.html  http://lamoo.s53.xrea.com/develop/gdiplus/gdiplus_blt.html  画面に書き出す際に  onscreen->DrawImage(offscreenBitmap,0,0);  とする代わりに、こんな感じで呼び出す  CachedBitmap offcache(offscreenBitmap, onscreen);  onscreen->DrawCachedBitmap(&offcache, 0, 0);  らしいのですが、反ってスピードが落ちました・・・。  後、new Bitmap()の第3パラメータに「PixelFormat32bppRGB」とかを指定しても駄目でしょうか(少~しだけ速くなった気がした)。

cabin_
質問者

お礼

回答ありがとうございます。 元々GDIで作っていたのですがアンチエイリアスや回転処理が欲しくなったのでGDI+に切り替えようとしてのっけから躓いてました。 URLの速度比較を見る限りGDI+は圧倒的に遅いみたいですね… GDI+のメソッド云々よりもGdiplus::Bitmapが重いんですかね。GDI::DDBにGDI+のメソッドが使えればいいのに

回答No.1

GDI+を使ったことはありませんが、下記の(1)か(2)の方法で描画速度を上げられると思います。 (1) case WM_MOVE:でのInvalidateRectをやめ、代わりにonscreenにもoffscreenと同じ線描画を行う。 (2) case WM_MOVE:でのInvalidateRectで更新する領域をposPts.x, posPts.y, pts.x, pts.yの範囲にする。 case WM_PAINT:で更新を必要とする領域を取得(PAINTSTRUCT)し、その領域のみを再描画する。

cabin_
質問者

お礼

回答ありがとうございます。 描画中のちらつきは避けたかったので1の方法は用いずに2の方法を試したところかなり速度が向上しました。 GDIの描画処理自体も相当鈍重ですが画面の更新も結構リソース食うんですね

関連するQ&A

  • GDI+で画像表示後に画面がちらつく

    GDI+を使いクライアント領域に画像を表示後に、ウィンドウサイズを変更すると変更中に画面が激しくちらつきます。本に書いてあったバックバッファを使った方法も試したのですが変わりませんでした。 どうすればちらつかないようにできますか? --- 実行環境 --- Microsoft Visual C++ 2010 Express WIN32 ユニコードビルド C++ #include <windows.h> #include <GdiPlus.h> #pragma comment(lib,"gdiplus.lib") LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); TCHAR szClassName[] = TEXT("test"); Gdiplus::Bitmap *img1=NULL; Gdiplus::Bitmap *backbuf=NULL; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; BOOL bRet; Gdiplus::GdiplusStartupInput gdisi; ULONG_PTR gditoken; if(Gdiplus::GdiplusStartup(&gditoken,&gdisi,NULL)!= Gdiplus::Ok) return 0; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } delete img1,backbuf; Gdiplus::GdiplusShutdown(gditoken); return (int)msg.wParam; } ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = (LPCTSTR)szClassName; wc.hIconSm = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); return (RegisterClassEx(&wc)); } BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, TEXT("GDI+で画像表示"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; static int imgwidth; static int imgheight; switch (msg) { case WM_CREATE:{ img1=new Gdiplus::Bitmap(TEXT("test.jpg")); if(img1->GetLastStatus() != Gdiplus::Ok){ MessageBox(hWnd,TEXT("ファイルがありません"),NULL,MB_OK); } imgwidth=img1->GetWidth(); imgheight=img1->GetHeight(); backbuf=new Gdiplus::Bitmap(imgwidth,imgheight,PixelFormat32bppARGB); Gdiplus::Graphics g(backbuf); g.DrawImage(img1,0,0,imgwidth,imgheight); break;} case WM_PAINT:{ hdc=BeginPaint(hWnd,&ps); Gdiplus::Graphics g(hdc); g.DrawImage(backbuf,0,0,imgwidth,imgheight); EndPaint(hWnd,&ps); break;} case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

  • 色描画

    以下は、ウインドウプロシージャ内でのプログラムです。 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);          }       }

  • GDI+で描画した画像を消去するにはどうしたらいいですか?

    GDI+で描画した画像を消去するにはどうしたらいいですか? GDI+のGraphicsクラスのDrawImageメソッドで描画したPNG画像を 消去する方法が知りたいです。 画像の表示位置を図1から図2のように下側に移動させたいんですが 実行してみると図3のようになってしまいます。 なので図3の上側の画像を消去しないと移動したように見えないんです。

  • 再描画されない

    お世話になります。WS_POPUPで作成したウィンドウにGDI+を用いて画像を表示させています。 そのウィンドウに、他のウィンドウ(最大化されていない)を重ねて動かすと、重なっていた部分が再描画されず真っ白になってしまいます。 最大化されたウィンドで隠した場合は正常に表示されます。 VC+2005です。よろしくお願いします。m(__)m //ウィンドウプロシージャWM_PAINT内 hdc = BeginPaint(hWnd, &ps); Image myImage(fn); MoveWindow(hWnd, 0, 0, myImage.GetWidth(), myImage.GetHeight(), 0); Graphics MyGraphics(hdc); MyGraphics.DrawImage(&myImage, 0, 0, myImage.GetWidth(), myImage.GetHeight()); EndPaint(hWnd, &ps);

  • なぜ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); } よろしくお願い致します。

  • tabにdrawImageで画像を描画したい

    tabにdrawImageで画像を描画したい 以下のソースでtabbedpaneに対して、ImageIconを指定して画像をのせるではなく、 drawImageメソッドで描画したいです。 ご存知の方がおられましたら教えてください。 import javax.swing.*; import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; public class JTabbedPaneTest4 extends JFrame { Zoom zoom = null; public static void main(String[] args) { JTabbedPaneTest4 frame = new JTabbedPaneTest4(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setBounds(10, 10, 300, 200); frame.setVisible(true); } JTabbedPaneTest4() { JTabbedPane tabbedpane = new JTabbedPane(); JPanel tabPanel1 = new JPanel(); tabPanel1.add(new JButton("button1")); ImageIcon icon1 = new ImageIcon("img1.jpg"); zoom = new Zoom(icon1, 0, 0, 50, 50); //tabbedpane.addTab("tab1", icon1, tabPanel1);//ok //tabbedpane.add(zoom, tabPanel1);//ng(パネルに書き込まれてしまう) getContentPane().add(tabbedpane, BorderLayout.CENTER); } class Zoom extends JComponent { private static final long serialVersionUID = 1L; private ImageIcon icon = null; private int x = 0; private int y = 0; private int h = 0; private int w = 0; private double scale = 1.0d; public Zoom(ImageIcon icon, int x, int y, int w, int h) { super(); this.icon = icon; this.x = x; this.y = y; this.w = w; this.h = h; } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.scale(scale, scale); g2.drawImage(icon.getImage(), x, y, w, h, this); } } }

    • ベストアンサー
    • Java
  • C# DrawImage 物理サイズでなく、ピクセルサイズでの描画

    C# で、 Image img = new Bitmap(fileName); Point p = this.PointToClient( new Point( e.X, e.Y ) ); で画像と描画位置を決め、 e.Graphics.DrawImage(img, p); で、そこに画像を描画するのですが、写真によって、同じ1024*768ピクセルなのに、表示される大きさがばらばらです。 Graphics.DrawImage メソッド (Image, Point)の説明を読むと、「指定した位置に、指定した Image を元の物理サイズで描画します」とあり、「元の物理サイズで描画」に問題があることがわかりました。 質問ですが、「指定した位置に、指定した Image をピクセルサイズで描画」するメソッドは何でしょう?探し方が悪いのか、見つけられません。

  • WinAPIで画像を更新し続けるには

    本を見ながら、WindowsSDKを使ってビットマップを描画する 所まではできたのですが、動かそうとすると上手くいきません。 ソースを簡略かして載せますと、以下のようにしています。 case WM_KEYDOWN: x++; // static MyDrawBitMap( hWnd, x, 10 ); ------------------------------------------------ void MyDrawBitMap( HWND hWnd, int x, int y ) { hdc = BeginPaint(hWnd, &ps); BitBlt( hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hMemDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); このように書いてますが、ボタンを押しても画面に変化が出ません。 PAINTSTRUCTは再描画が必要な情報に関する構造体だとは解っていま すが、特に変更の必要の無い画像も再描画したい場合の処理はどうす れば良いのでしょうか?よろしくお願いします。

  • C言語で画面がおかしくなる

    最近C言語でプログラムを書き始めました。 二次関数を作るスクリプトなんですが、一定時間がたつと画面がおかしくなります。 どこが問題なのか探しても見つからないので、わかる方教えてください。 WinMainの部分は省いてます #include<windows.h> #include<stdio.h> #include <stdlib.h> #include<time.h> #define TIMER_ID 1 #define spy(num) (wy-num) int rnd(int r); void bset(HDC hdc, int x, int y); LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) { HDC hDC; static int DesktopX ,DesktopY; static HDC hdc; static HDC Mhdc; static HBITMAP BMPhdc; PAINTSTRUCT paint; RECT Rect; LOGPEN lopnPen; POINT CursorP, ScreenP; char str[250]; int i; static int wx, wy ,ky; static double a ,b ,c ,x ,y ,n ,m ,s ,t; switch (msg) { case WM_DESTROY: DeleteObject( BMPhdc ); DeleteDC( Mhdc ); PostQuitMessage(0); return 0; case WM_CREATE: SetTimer(hwnd, TIMER_ID, 10, NULL); hDC = GetDC(hwnd); hdc = CreateCompatibleDC( hDC ); GetClientRect(GetDesktopWindow() ,&Rect); DesktopX = Rect.right ;DesktopY = Rect.bottom; BMPhdc = CreateCompatibleBitmap( hDC, DesktopX, DesktopY ); SelectObject( hdc, BMPhdc ); ReleaseDC( hwnd, hDC ); GetClientRect(hwnd, &Rect); wx = Rect.right ;wy = Rect.bottom; x = rnd(wx); y = rnd(wy); n = rnd(wx); m = rnd(wy); return 0; case WM_TIMER: ScreenP.x = LOWORD(lp); ScreenP.y = HIWORD(lp); GetClientRect(hwnd, &Rect); wx = Rect.right ;wy = Rect.bottom; if( ky == 1 ){ x = rnd(wx); y = rnd(wy); n = rnd(wx); m = rnd(wy); } ClientToScreen(hwnd ,&ScreenP); GetCursorPos(&CursorP); s = CursorP.x-ScreenP.x; t = wy-(CursorP.y-ScreenP.y); InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_RBUTTONDOWN: ky = 1; return 0; case WM_RBUTTONUP: ky = 0; return 0; case WM_PAINT: Mhdc = BeginPaint(hwnd, &paint); SelectObject(hdc , CreateSolidBrush(RGB(255,255,255))); lopnPen.lopnStyle = PS_NULL; SelectObject(hdc , CreatePenIndirect(&lopnPen)); Rectangle(hdc , 0 , 0 , wx+1 , wy+1); SelectObject(hdc , CreateSolidBrush(RGB(255,0,0))); if ((s-n) != 0 && (n-x) != 0 && (s-x) != 0){ a = ((t-m)/(s-n) - (m-y)/(n-x)) / (s-x); b = (m-y)/(n-x) - a*(n+x); c = y - a*x*x -b*x; } bset(hdc, x,spy(y)); bset(hdc, n,spy(m)); bset(hdc, s,spy(t)); lopnPen.lopnStyle = PS_SOLID; lopnPen.lopnColor = RGB(0,0,255); SelectObject(hdc , CreatePenIndirect(&lopnPen)); MoveToEx(hdc , -1 , spy( a + b - c) , NULL); for(i = 0 ; i <= wx ;i++){ LineTo(hdc , i , spy( a*i*i - b*i - c)); } BitBlt(Mhdc, 0, 0, DesktopX, DesktopY, hdc, 0, 0, SRCCOPY ); EndPaint(hwnd, &paint); return 0; } return DefWindowProc(hwnd , msg , wp , lp); } int rnd(int r){ static int flag; if (flag == 0) { srand((unsigned int)time(NULL)); flag = 1; } return (int)(rand()*(r+1.0)/(1.0+RAND_MAX)); } void bset(HDC hdc, int x, int y){ static LOGPEN lopnPen; lopnPen.lopnStyle = PS_NULL; SelectObject(hdc , CreatePenIndirect(&lopnPen)); Rectangle(hdc , x-3 , y-3 , x+3 , y+3); return; }

  • JButtonの画像をactionPerformedメソッド内で再描画

    JButtonの画像をactionPerformedメソッド内で再描画したい。 以下のソースのようにして、再描画したいのです。 setIconメソッドではなく、 JButtonに対して描画したものに対して再描画したいです。 Graphics2DクラスについてJAVA APIで調べましたが、 仕組の理解に至りませんでした。 仕組みと方法を教えて頂きたいです。 よろしくお願いいたします。 import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; public class test extends JFrame implements ActionListener{ JButton b= new JButton(); public static void main(String a[]) { new test(); } public test() { super(); this.setSize(100,100); b.addActionListener(this); b.add(new Zoom(new ImageIcon("img1.jpg"),0,0,50,50)); this.add(b); this.setVisible(true); } class Zoom extends JComponent { private static final long serialVersionUID = 1L; private ImageIcon icon = null; private int x = 0; private int y = 0; private int h = 0; private int w = 0; private double scale = 1.0d; public Zoom(ImageIcon icon, int x, int y, int w, int h) { super(); this.icon = icon; this.x = x; this.y = y; this.w = w; this.h = h; } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.scale(scale, scale); ////////////////////////////////// //画僧を再描画したい。 //g2.clearRect(0, 0, 80, 80);//× g2.drawImage(icon.getImage(), x, y, w, h, this); } } public void actionPerformed(ActionEvent e) { if(e.getSource()==b){ System.out.print("ok"); //this.repaint();//× //b.repaint();//× b.add(new Zoom(new ImageIcon("img2.jpg"),0,0,50,50));//(再描画できない) //b.setIcon(new ImageIcon("img2.jpg"));//ok(再描画出来る) } } }

    • ベストアンサー
    • Java

専門家に質問してみよう