gdi+ grafika:: drawimage naprawdę wolno~~

Używam Grafiki GDI+ do rysowania obrazu 4000*3000 na ekranie, ale jest to naprawdę wolne. Zajmuje to około 300ms. szkoda, że nie zajmuje mniej niż 10ms.

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

//-------------------------------------------- // ta część trwa około 300ms, straszne!

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

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

Nie mogę użyć CachedBitmap, ponieważ chcę edytować bitmapę później.

Jak mogę to poprawić? Czy coś jest nie tak?

Ta natywna funkcja GDI również rysuje obraz na ekranie, a to zajmuje tylko 1 ms:

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);

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

Jeśli chcę używać StretchDIBits, muszę przekazać BITMAPINFO, ale jak mogę pobrać BITMAPINFO z obiektu Gdi + Bitmap? Zrobiłem eksperyment przez FreeImage lib, nazywam StretchDIBits za pomocą obiektu FreeImageplus, rysuje się naprawdę szybko. Ale teraz muszę narysować bitmapę i napisać jakiś algorytm na bitmapowej tablicy bitów, jak Mogę uzyskać BITMAPINFO, jeśli mam Obiekt bitmapowy? To naprawdę irytujące.-___________-|

Author: Ian Boyd, 2008-11-05

9 answers

Masz ekran o rozdzielczości 4000 x 3000? Wow!

Jeśli nie, należy narysować tylko widoczną część obrazu, byłoby to znacznie szybciej...

[edytuj po pierwszym komentarzu] moja uwaga jest rzeczywiście trochę głupia, przypuszczam, że DrawImage zamaskuje / pominie niepotrzebne piksele.

Po edycji (pokazującej StretchDIBits), domyślam się, że możliwe źródło różnicy prędkości może wynikać z faktu, że StretchDIBits jest przyspieszane sprzętowo ("Jeśli sterownik nie może obsługiwać JPEG lub PNG file image" to podpowiedź...) natomiast DrawImage może być (nie mam na to dowodów!) kodowane w C, polegające na mocy procesora zamiast GPU...

Jeśli dobrze pamiętam, obrazy DIB są szybkie (pomimo tego, że są "niezależne od urządzenia"). Zobacz Animacja High Speed Win32: "użyj CreateDIBSection, aby wykonać szybką animację ". OK, dotyczy to DIB vs. GDI, w starej wersji Windows (1996!), ale myślę, że to nadal prawda.

[edytuj] Maybe Bitmap:: GetHBITMAP funkcja może pomóc w użyciu StretchDIBits (nie testowane...).

 3
Author: PhiLho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 16:22:07

Jeśli używasz GDI+, Klasa TextureBrush jest tym, czego potrzebujesz do szybkiego renderowania obrazów. Napisałem kilka gier 2d z nim, uzyskując około 30 FPS lub tak.

Nigdy nie pisałem kodu. NET W C++, więc oto przykład w języku 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, ...)
}
 15
Author: Cybis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-10 19:48:10

To tylko myśl; zamiast pobierać szerokość i wysokość obrazu przed rysowaniem, dlaczego nie buforować tych wartości podczas ładowania obrazu?

 2
Author: Patrik Svensson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 11:54:05

Zbadaj wpływ jawnego ustawienia trybu interpolacji na NearestNeighbor (gdzie w twoim przykładzie wygląda na to, że interpolacja nie jest rzeczywiście potrzebna! Ale 300ms jest rodzajem kosztu wykonania wysokiej jakości interpolacji, gdy nie jest potrzebna interpolacja, więc warto spróbować)

Kolejną rzeczą do zbadania jest zmiana głębi kolorów bitmapy.

 2
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 14:00:21

Niestety, kiedy miałem podobny problem, stwierdziłem, że GDI+ jest znany jako znacznie wolniejszy niż GDI i ogólnie nie przyspieszany sprzętowo, ale teraz Microsoft przeszedł na WPF nie wrócą, aby poprawić GDI+!

Wszyscy producenci kart graficznych przeszli na wydajność 3D i nie wydają się zainteresowani akceleracją 2D, a nie ma jasnego źródła informacji o tym, które funkcje są lub mogą być przyspieszane sprzętowo, czy nie. Bardzo frustrujące, ponieważ po napisaniu aplikacja w. NET za pomocą GDI+, nie jestem zadowolony, aby przejść do zupełnie innej technologii, aby przyspieszyć go do rozsądnych poziomów.

 2
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 16:25:30

Myślę, że nie zmienią zbyt wiele, ale ponieważ nie musisz zmieniać rozmiaru obrazu, spróbuj użyć przeciążenia DrawImage , które nie próbuje (nie próbuje) zmieniać rozmiaru:

DrawImage(bitmap,0,0);

Jak już mówiłem, wątpię, żeby to coś zmieniło, ponieważ jestem pewny że DrawImage sprawdza Szerokość i Wysokość bitmapy, a jeśli nie ma potrzeby zmiany rozmiaru, po prostu wywołuje to przeciążenie. (mam nadzieję, że nie przeszkadza to przechodzenie przez wszystkie 12 milionów pikseli nie wykonujących rzeczywistej pracy).

Update: moje przemyślenia są błędne. od tego czasu dowiedziałem się, ale komentarze facetów przypomniały mi moją starą odpowiedź: ty chcesz podać rozmiar miejsca docelowego; mimo że pasuje do rozmiaru źródła:

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

Powodem są różnice dpi pomiędzy dpi bitmap a dpi miejsca przeznaczenia. GDI + wykona skalowanie, aby uzyskać odpowiedni "rozmiar" (tj. w calach)


What i ' ve nauczyłem się od października, że naprawdę chcesz narysować "buforowaną" wersję swojej bitmapy. W GDI + istnieje klasa CachedBitmap. Jest kilka sztuczek, aby go używać. Ale w końcu mam bit funkcji (Delphi) kodu, który to robi.

Zastrzeżenie jest takie, że CachedBitmap może stać się nieważna - co oznacza, że nie może być używana do rysowania. Dzieje się tak, gdy użytkownik zmienia rozdzielczość lub głębię kolorów (np. Pulpit zdalny). W takim przypadku DrawImage nie powiedzie się i trzeba odtworzyć 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 jest przekazywane przez odniesienie. Pierwsze wywołanie DrawCachedBitmap to buforowana wersja zostanie utworzona. Następnie przekazujesz go w kolejnych wywołaniach, np.:

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

...

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

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

Używam tej procedury do rysowania glifów na przyciskach, biorąc pod uwagę obecne DPI. To samo mogło być użyte przez zespół Internet Explorera do rysowania obrazów, gdy użytkownik pracuje z wysokim dpi (tj. jest bardzo powolne rysowanie powiększonych obrazów, ponieważ używają GDI+).

 2
Author: Ian Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:51:17

Spróbuj użyć kopii bitmapy z pliku. Funkcja FromFile na niektórych plikach zwraca "wolny" obraz, ale jego kopia rysuje się szybciej.

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

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

DrawImage(bitmap2,0,0,width,height);
 1
Author: eugeniy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-30 12:21:30

/* Najpierw przepraszam za ma angielski, A kod jest częściowo po polsku, ale jest prosty do zrozumienia. Miałem ten sam problem i znalazłem najlepsze rozwiązanie. Tutaj jest.

Dont use: Graphics graphics (hdc); graphics.DrawImage (gpBitmap, 0, 0); jest wolny.

Użyj: GetHBITMAP (gdiplus:: Color(), &g_hBitmap) dla HBITMAP i rysowania przy użyciu mojej funkcji ShowBitmapStretch ().

Możesz zmienić jego rozmiar i to znacznie szybciej! Artur Czekalski / Polska

*/

//--------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;
 1
Author: Sator666,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-01 02:57:12

Zrobiłem kilka badań i nie byłem w stanie znaleźć sposobu, aby renderować obrazy z GDI/GDI+ szybciej niż

Graphics.DrawImage/DrawImageUnscaled

I jednocześnie proste jak to.

Dopóki nie odkryłem

ImageList.Draw(GFX,Point,Index)
I tak, to naprawdę Takie Szybkie i proste.
 0
Author: Mostafa Saad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-01 19:07:13