Domanda

Sto usando un GDI + Graphic per disegnare un'immagine 4000 * 3000 sullo schermo, ma è molto lento. Ci vogliono circa 300ms. Vorrei che occupasse solo meno di 10ms.

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

// -------------------------------------------- // questa parte richiede circa 300 ms, terribile!

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

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

Non posso usare CachedBitmap, perché voglio modificare la bitmap in un secondo momento.

Come posso migliorarlo? O c'è qualcosa che non va?

Questa funzione GDI nativa disegna anche l'immagine sullo schermo e richiede solo 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);

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

Se voglio usare StretchDIBits, devo passare BITMAPINFO, ma come posso ottenere BITMAPINFO da un oggetto Bitdi Gdi +? Ho fatto l'esperimento con FreeImage lib, chiamo StretchDIBits usando l'oggetto FreeImageplus, disegna molto velocemente. Ma ora ho bisogno di disegnare Bitmap e scrivere alcuni algoritmi sull'array di bit di Bitmap, come posso ottenere BITMAPINFO se ho un oggetto Bitmap? È davvero fastidioso -___________- |

È stato utile?

Soluzione

Hai uno schermo con una risoluzione di 4000 x 3000? Wow!

In caso contrario, dovresti disegnare solo la parte visibile dell'immagine, sarebbe molto più veloce ...

[MODIFICA dopo il primo commento] La mia osservazione è davvero un po 'stupida, suppongo che DrawImage maschererà / salterà i pixel non necessari.

Dopo la modifica (che mostra StretchDIBits), immagino che una possibile fonte di differenza di velocità potrebbe derivare dal fatto che StretchDIBits è accelerato dall'hardware (" Se il driver non può supportare l'immagine del file JPEG o PNG " è un suggerimento ...) mentre DrawImage potrebbe essere (non ne ho alcuna prova!) codificato in C, basandosi sulla potenza della CPU anziché su quella della GPU ...

Se ricordo bene, le immagini DIB sono veloci (nonostante siano "indipendenti dal dispositivo"). Vedi High Speed ??Win32 Animation : " usa CreateDIBSection per realizzare animazioni ad alta velocità " ;. OK, si applica a DIB vs. GDI, nella vecchia versione di Windows (1996!), Ma penso che sia ancora vero.

[EDIT] Forse Bitmap :: GetHBITMAP potrebbe aiutarti ad usare StretchDIBits (non testato ...).

Altri suggerimenti

Se stai usando GDI +, la classe TextureBrush è ciò che ti serve per rendere velocemente le immagini. Ho scritto un paio di giochi 2D con circa 30 FPS o giù di lì.

Non ho mai scritto codice .NET in C ++, quindi ecco un esempio C # -ish:

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, ...)
}

Solo un pensiero; invece di recuperare la larghezza e l'altezza dell'immagine prima di disegnare, perché non memorizzare questi valori nella cache quando si carica l'immagine?

Esplora l'impatto dell'impostazione esplicita della modalità di interpolazione su Il più vicino (dove, nel tuo esempio, sembra che l'interpolazione non sia effettivamente necessaria! Ma 300ms è il tipo di costo per fare un'interpolazione di alta qualità quando non è necessaria l'interpolazione, quindi vale la pena provare)

Un'altra cosa da esplorare è cambiare la profondità del colore della bitmap.

Sfortunatamente quando ho avuto un problema simile, ho scoperto che GDI + è noto per essere molto più lento di GDI e generalmente non ha accelerato l'hardware, ma ora Microsoft è passata a WPF che non tornerà per migliorare GDI +!

Tutti i produttori di schede grafiche sono passati alle prestazioni 3D e non sembrano interessati all'accelerazione 2D e non esiste una chiara fonte di informazioni su quali funzioni sono o possono essere accelerate dall'hardware o meno. Molto frustrante perché dopo aver scritto un'app in .NET utilizzando GDI +, non sono felice di passare a una tecnologia completamente diversa per accelerarla a livelli ragionevoli.

non credo che faranno molto di più, ma poiché non è necessario ridimensionare l'immagine, prova a utilizzare il sovraccarico di DrawImage che non (tenta) di ridimensionare:

DrawImage(bitmap,0,0);

Come ho detto, dubito che farà alcuna differenza, perché sono sicuro che DrawImage controlla Larghezza e Altezza della bitmap e, se non è necessario ridimensionare, chiama solo questo sovraccarico. (Spero che non si preoccupi di passare attraverso tutti i 12 milioni di pixel senza eseguire alcun lavoro reale).

Aggiornamento: le mie riflessioni sono sbagliate. l'ho scoperto, ma i commenti dei ragazzi mi hanno ricordato la mia vecchia risposta: vuoi specificare la dimensione della destinazione; anche se corrisponde alla dimensione della fonte:

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

Il motivo è dovuto alle differenze di dpi tra il dpi di bitmap e il dpi della destinazione. GDI + eseguirà il ridimensionamento per ottenere l'immagine di "dimensioni" corrette (ovvero in pollici)


Quello che ho imparato da solo dallo scorso ottobre è che vuoi davvero disegnare una versione " cache " della tua bitmap. Esiste una classe CachedBitmap in GDI +. Ci sono alcuni trucchi per usarlo. Ma alla fine ho un po 'di codice (Delphi) che lo fa.

L'avvertenza è che il CachedBitmap può diventare non valido, il che significa che non può essere usato per disegnare. Ciò accade se l'utente modifica risoluzioni o profondità di colore (ad es. Desktop remoto). In tal caso il DrawImage fallirà e dovrai ricreare il 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;

Il cachedBitmap viene passato per riferimento. Verrà creata la prima chiamata a DrawCachedBitmap nella versione memorizzata nella cache . Lo si passa quindi nelle chiamate successive, ad es .:

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

...

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

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

Uso la routine per disegnare glifi sui pulsanti, tenendo conto dell'attuale DPI. Lo stesso avrebbe potuto essere usato dal team di Internet Explorer per disegnare immagini quando l'utente ha un dpi elevato (ovvero è molto lento nel disegnare immagini ingrandite, perché usano GDI +).

Prova a utilizzare la copia di Bitmap dal file. La funzione FromFile su alcuni file restituisce " lento " immagine, ma la sua copia verrà disegnata più velocemente.

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

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

DrawImage(bitmap2,0,0,width,height);

/ * Prima scusa per l'inglese, e il codice è in parte in polacco, ma è semplice da capire. Ho avuto lo stesso problema e ho trovato la soluzione migliore. Eccolo qui.

Non usare: grafica (hdc); graphics.DrawImage (gpBitmap, 0, 0); È lento.

Usa: GetHBITMAP (Gdiplus :: Color (), & amp; g_hBitmap) per HBITMAP e disegna usando la mia funzione ShowBitmapStretch ().

Puoi ridimensionarlo ed è molto più veloce! Artur Czekalski / Polonia

* /

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

Ho fatto qualche ricerca e non sono riuscito a trovare un modo per rendere le immagini con GDI / GDI + più veloci di

Graphics.DrawImage/DrawImageUnscaled

e allo stesso tempo semplice.

Fino a quando ho scoperto

ImageList.Draw(GFX,Point,Index)

e sì, è davvero così veloce e semplice.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top