18

GDI+ グラフィックを使用して 4000*3000 の画像を画面に描画していますが、非常に遅いです。約300msかかります。10ms未満しか占有しないことを願っています。

Bitmap *bitmap = Bitmap::FromFile("XXXX",...);

// - - - - - - - - - - - - - - - - - - - - - - // この部分約 300 ミリ秒かかります。

int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
DrawImage(bitmap,0,0,width,height);

//----------------------------------------------

後でビットマップを編集したいので、CachedBitmap を使用できません。

どうすれば改善できますか?それとも何か間違っていますか?

このネイティブ GDI 関数も画像を画面に描画し、わずか 1 ミリ秒かかります。

SetStretchBltMode(hDC, COLORONCOLOR);   
StretchDIBits(hDC, rcDest.left, rcDest.top, 
        rcDest.right-rcDest.left, rcDest.bottom-rcDest.top, 
        0, 0, width, height,
        BYTE* dib, dibinfo, DIB_RGB_COLORS, SRCCOPY);

//------------------------------------------------ --------------

StretchDIBits を使用する場合は、BITMAPINFO を渡す必要がありますが、Gdi+ ビットマップ オブジェクトから BITMAPINFO を取得するにはどうすればよいですか? 私は FreeImage lib で実験を行いました。FreeImageplus オブジェクトを使用して StretchDIBits を呼び出すと、非常に高速に描画されます。しかし今、ビットマップを描画し、ビットマップのビット配列にアルゴリズムを記述する必要があります。ビットマップ オブジェクトがある場合、どうすれば BITMAPINFO を取得できますか? 本当に迷惑です-___________-|

4

9 に答える 9

17

GDI+ を使用している場合、イメージを高速にレンダリングするには TextureBrush クラスが必要です。私はそれを使っていくつかの 2D ゲームを書き、約 30 FPS を達成しました。

私は C++ で .NET コードを書いたことがないので、C# っぽい例を次に示します。

Bitmap bmp = new Bitmap(...)
TextureBrush myBrush = new TextureBrush(bmp)

private void Paint(object sender, PaintEventArgs e):
{
    //Don't draw the bitmap directly. 
    //Only draw TextureBrush inside the Paint event.
    e.Graphics.FillRectangle(myBrush, ...)
}
于 2008-11-05T16:03:48.203 に答える
3

解像度4000x3000の画面がありますか?わお!

そうでない場合は、画像の表示部分のみを描画する必要があります。はるかに高速になります...

[最初のコメントの後で編集]私の発言は確かに少しばかげています、私はDrawImageが不要なピクセルをマスク/スキップすると思います。

編集後(StretchDIBitsを表示)、速度差の原因として考えられるのは、 StretchDIBitsがハードウェアアクセラレーションされていること(「ドライバーがJPEGまたはPNGファイルイメージをサポートできない場合」がヒントです...)である可能性がありますが、DrawImageはGPUの代わりにCPUパワーに依存して、Cでコーディングされていること(私にはその証拠がありません!)...

正しく思い出せば、DIBイメージは高速です(「デバイスに依存しない」にもかかわらず)。高速Win32アニメーション:「CreateDIBSectionを使用して高速アニメーションを実行する」を参照してください。OK、古いWindowsバージョン(1996!)のDIBとGDIに適用されますが、それでも正しいと思います。

[編集]多分Bitmap::GetHBITMAP関数はStretchDIBitsを使用するのに役立つかもしれません(テストされていません...)。

于 2008-11-05T10:47:53.847 に答える
2

ちょっとした考え; 描画する前に画像の幅と高さを取得する代わりに、画像を読み込むときにこれらの値をキャッシュしてみませんか?

于 2008-11-05T11:54:05.007 に答える
2

大した違いはないと思いますが、実際に画像のサイズを変更する必要はないので、サイズ変更を(試行) しないDrawImageのオーバーロードを使用してみてください。

DrawImage(bitmap,0,0);

私が言ったように、DrawImageがビットマップの高さをチェックし、サイズ変更が必要ない場合は、このオーバーロードを呼び出すだけなので、違いがあるとは思えません。(実際の作業を実行せずに、1,200 万ピクセルすべてを処理しないことを願っています)。

更新: 私の熟考は間違っています。私はそれ以来知りましたが、みんなのコメントは私の古い答えを思い出させました:あなたは目的地のサイズを指定したいのです。ソースサイズと一致しますが:

DrawImage(bitmap, 0, 0, bitmap.GetWidth, bitmap.GetHeight);

bitmapその理由は、dpiの dpi と宛先の dpi のdpi の違いによるものです。GDI+ はスケーリングを実行して、イメージを適切な「サイズ」(インチ単位) にします。


昨年 10 月以来、私が自分で学んだことは、ビットマップの「キャッシュされた」バージョンを本当に描画したいということです。CachedBitmapGDI+ にはクラスがあります。使い方にはコツがあります。しかし、最終的には、それを行う(Delphi)コードの関数ビットがあります。

注意点は、CachedBitmapが無効になる可能性があることです。つまり、描画には使用できません。これは、ユーザーが解像度や色深度 (リモート デスクトップなど) を変更した場合に発生します。その場合、DrawImageは失敗し、以下を再作成する必要がありますCachedBitmap

class procedure TGDIPlusHelper.DrawCachedBitmap(image: TGPImage;
      var cachedBitmap: TGPCachedBitmap;
      Graphics: TGPGraphics; x, y: Integer; width, height: Integer);
var
   b: TGPBitmap;
begin
   if (image = nil) then
   begin
      //i've chosen to not throw exceptions during paint code - it gets very nasty
      Exit;
   end;

   if (graphics = nil) then
   begin
      //i've chosen to not throw exceptions during paint code - it gets very nasty
      Exit;
   end;

   //Check if we have to invalidate the cached image because of size mismatch
   //i.e. if the user has "zoomed" the UI
   if (CachedBitmap <> nil) then
   begin
      if (CachedBitmap.BitmapWidth <> width) or (CachedBitmap.BitmapHeight <> height) then
         FreeAndNil(CachedBitmap); //nil'ing it will force it to be re-created down below
   end;

   //Check if we need to create the "cached" version of the bitmap
   if CachedBitmap = nil then
   begin
      b := TGDIPlusHelper.ResizeImage(image, width, height);
      try
         CachedBitmap := TGPCachedBitmap.Create(b, graphics);
      finally
         b.Free;
      end;
end;

    if (graphics.DrawCachedBitmap(cachedBitmap, x, y) <> Ok) then
begin
       //The calls to DrawCachedBitmap failed
       //The API is telling us we have to recreate the cached bitmap
       FreeAndNil(cachedBitmap);
       b := TGDIPlusHelper.ResizeImage(image, width, height);
       try
          CachedBitmap := TGPCachedBitmap.Create(b, graphics);
       finally
          b.Free;
       end;

       graphics.DrawCachedBitmap(cachedBitmap, x, y);
    end;
 end;

cachedBitmap参照によって渡されます。キャッシュされたDrawCachedBitmapバージョンへの最初の呼び出しが作成されます。次に、後続の呼び出しでそれを渡します。次に例を示します。

Image imgPrintInvoice = new Image.FromFile("printer.png");
CachedBitmap imgPrintInvoiceCached = null;

...

int glyphSize = 16 * (GetCurrentDpi() / 96);

DrawCachedBitmap(imgPrintInvoice , ref imgPrintInvoiceCached , graphics, 
      0, 0, glyphSize, glyphSize);

現在の DPI を考慮して、ルーチンを使用してボタンにグリフを描画します。Internet Explorer チームは、ユーザーが高 dpi を実行しているときに画像を描画するために同じことを使用できました (つまり、GDI+ を使用しているため、ズーム画像の描画が非常に遅くなります)。

于 2009-10-24T12:46:52.853 に答える
2

残念なことに、私が同様の問題を抱えていたとき、GDI+ は GDI よりもはるかに遅く、一般的にハードウェア アクセラレーションは行われていないことがわかっていましたが、現在 Microsoft は WPF に移行しており、GDI+ を改善するために戻ってくることはありません!

すべてのグラフィックス カード メーカーは 3D パフォーマンスに移行しており、2D アクセラレーションには関心がないようです。どの機能がハードウェア アクセラレーションされているか、またはアクセラレーション可能かどうかについての明確な情報源はありません。GDI+ を使用して .NET でアプリを作成したので、非常にイライラします。適切なレベルまで高速化するために、まったく別のテクノロジに変更することに満足していません。

于 2008-11-05T16:25:30.047 に答える
2

補間モードを明示的に NearestNeighbor に設定した場合の影響を調べます (この例では、補間は実際には必要ないように見えます!ただし、補間が不要な場合に高品質の補間を行うには 300 ミリ秒というコストがかかるため、試す)

もう 1 つの検討事項は、ビットマップの色深度の変更です。

于 2008-11-05T14:00:21.400 に答える
1

ファイルからのビットマップのコピーを使用してみてください。一部のファイルの FromFile 関数は「遅い」画像を返しますが、そのコピーはより高速に描画されます。

Bitmap *bitmap = Bitmap::FromFile("XXXX",...);

Bitmap *bitmap2 = new Bitmap(bitmap); // make copy

DrawImage(bitmap2,0,0,width,height);
于 2014-01-30T12:04:12.917 に答える
1

/* 最初に ma 英語で申し訳ありません。コードは部分的に洗練されていますが、簡単に理解できます。私は同じ問題を抱えていて、最良の解決策を見つけました。ここにあります。

使用しないでください: グラフィックス グラフィック (hdc); graphics.DrawImage(gpBitmap, 0, 0); 遅いです。

使用: HBITMAP に GetHBITMAP(Gdiplus::Color(), &g_hBitmap) を使用し、関数 ShowBitmapStretch() を使用して描画します。

サイズを変更でき、はるかに高速です。Artur Czekalski / ポーランド

*/

//--------Global-----------
Bitmap *g_pGDIBitmap; //for loading picture
int gRozXOkna, gRozYOkna; //size of working window
int gRozXObrazu, gRozYObrazu; //Size of picture X,Y
HBITMAP g_hBitmap = NULL; //for displaying on window
//------------------------------------------------------------------------------
int ShowBitmapStretch(HDC hdc, HBITMAP hBmp, int RozX, int RozY, int RozXSkal, int RozYSkal, int PozX, int PozY) 
{
    if (hBmp == NULL) return -1;
    HDC hdc_mem = CreateCompatibleDC(hdc); //utworzenie kontekstu pamięciowego
    if (NULL == hdc_mem) return -2;
    //Trzeba połączyć BMP z hdc_mem, tzn. umieścić bitmapę w naszym kontekście pamięciowym
    if (DeleteObject(SelectObject(hdc_mem, hBmp)) == NULL) return -3; 

    SetStretchBltMode(hdc, COLORONCOLOR); //important! for smoothness
    if (StretchBlt(hdc, PozX, PozY, RozXSkal, RozYSkal, hdc_mem, 0, 0, RozX, RozY, SRCCOPY) == 0) return -4;

    if (DeleteDC(hdc_mem) == 0) return -5;
    return 0; //OK
}
//---------------------------------------------------------------------------
void ClearBitmaps(void)
{
    if (g_hBitmap) { DeleteObject(g_hBitmap);  g_hBitmap = NULL; }
    if (g_pGDIBitmap) { delete g_pGDIBitmap;  g_pGDIBitmap = NULL; }
}
//---------------------------------------------------------------------------
void MyOpenFile(HWND hWnd, szFileName)
{
    ClearBitmaps(); //Important!
    g_pGDIBitmap = new Bitmap(szFileName); //load a picture from file

    if (g_pGDIBitmap == 0) return;
    //---Checking if picture was loaded
    gRozXObrazu = g_pGDIBitmap->GetWidth();
    gRozYObrazu = g_pGDIBitmap->GetHeight();
    if (gRozXObrazu == 0 || gRozYObrazu == 0) return;

    //---Uworzenie bitmapy do wyświatlaia; DO IT ONCE HERE!
    g_pGDIBitmap->GetHBITMAP(Gdiplus::Color(), &g_hBitmap); //creates a GDI bitmap from this Bitmap object
    if (g_hBitmap == 0) return;

    //---We need to force the window to redraw itself
    InvalidateRect(hWnd, NULL, TRUE);
    UpdateWindow(hWnd);
}
//---------------------------------------------------------------------------
void MyOnPaint(HDC hdc, HWND hWnd) //in case WM_PAINT; DO IT MANY TIMES
{
    if (g_hBitmap)
    {
        double SkalaX = 1.0, SkalaY = 1.0; //scale
        if (gRozXObrazu > gRozXOkna || gRozYObrazu > gRozYOkna || //too big picture, więc zmniejsz; 
           (gRozXObrazu < gRozXOkna && gRozYObrazu < gRozYOkna)) //too small picture, można powiększyć 
        {
            SkalaX = (double)gRozXOkna / (double)gRozXObrazu; //np. 0.7 dla zmniejszania; FOR DECREASE
            SkalaY = (double)gRozYOkna / (double)gRozYObrazu; //np. 1.7 dla powiększania; FOR INCREASE
            if (SkalaY < SkalaX) SkalaX = SkalaY; //ZAWSZE wybierz większe skalowanie, czyli mniejszą wartość i utaw w SkalaX
        }

    if (ShowBitmapStretch(hdc, g_hBitmap, gRozXObrazu, gRozYObrazu, (int)(gRozXObrazu*SkalaX), (int)(gRozYObrazu*SkalaX), 0, 0, msg) < 0) return;
于 2015-12-01T02:57:12.327 に答える