Question

J'utilise un graphique GDI + pour dessiner une image 4000 * 3000 à l'écran, mais elle est vraiment lente. Cela prend environ 300ms. Je souhaite qu'il occupe juste moins de 10ms.

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

// ------------------------------------------------ // cette partie prend environ 300 ms, terrible!

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

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

Je ne peux pas utiliser CachedBitmap, car je souhaite modifier le bitmap ultérieurement.

Comment puis-je l'améliorer? Ou est-ce que quelque chose ne va pas?

Cette fonction GDI native dessine également l’image à l’écran et ne prend que 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);

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

Si je veux utiliser StretchDIBits, je dois passer BITMAPINFO, mais comment puis-je obtenir BITMAPINFO à partir d’un objet Gdi + Bitmap? J'ai fait l'expérience de FreeImage lib, j'appelle StretchDIBits en utilisant l'objet FreeImageplus, cela dessine très vite. Mais maintenant, je dois dessiner Bitmap et écrire un algorithme sur le tableau de bits de Bitmap. Comment puis-je obtenir BITMAPINFO si j'ai un objet Bitmap? C'est vraiment ennuyant -___________- |

Était-ce utile?

La solution

Vous avez un écran de résolution 4000 x 3000? Wow!

Sinon, vous ne devez dessiner que la partie visible de l'image, ce serait beaucoup plus rapide ...

[MODIFIER après le premier commentaire] Ma remarque est en effet un peu stupide, je suppose que DrawImage masquera / ignorera les pixels inutiles.

Après votre modification (affichant StretchDIBits), je suppose qu'une source possible de différence de vitesse pourrait provenir du fait que StretchDIBits accéléré matériellement (" Si le pilote ne peut pas prendre en charge l'image de fichier JPEG ou PNG ", c'est un indice ...) alors que DrawImage pourrait être (je n’ai pas de preuve à ce sujet!) codé en C, reposant sur la puissance du processeur au lieu de celle du GPU ...

Si je me souviens bien, les images DIB sont rapides (bien qu’elles soient "indépendantes du périphérique"). Voir Animation Win32 haute vitesse : " utilisez CreateDIBSection pour effectuer une animation à haute vitesse ". OK, cela s’applique à DIB contre GDI, dans l’ancienne version de Windows (1996!), Mais je pense que cela est toujours vrai.

[EDIT] Peut-être Bitmap :: GetHBITMAP pourrait vous aider à utiliser StretchDIBits (non testé ...).

Autres conseils

Si vous utilisez GDI +, la classe TextureBrush est ce dont vous avez besoin pour un rendu rapide des images. J'ai écrit quelques jeux 2D avec elle, obtenant environ 30 FPS ou plus.

Je n'ai jamais écrit de code .NET en C ++, voici donc un exemple en 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, ...)
}

Juste une pensée; au lieu de récupérer la largeur et la hauteur de l'image avant de dessiner, pourquoi ne pas mettre ces valeurs en cache lorsque vous chargez l'image?

Explorez l'impact de la définition explicite du mode d'interpolation sur NearestNeighbor (où, dans votre exemple, il semble que l'interpolation n'est pas réellement nécessaire! Mais 300 ms est le genre de coût de réalisation d'une interpolation de haute qualité lorsqu'aucune interpolation n'est nécessaire, donc ça vaut le coup d'essayer)

Une autre chose à explorer est la modification de la profondeur de couleur du bitmap.

Malheureusement, lorsque j'ai eu un problème similaire, j'ai constaté que GDI + est connu pour être beaucoup plus lent que GDI et que le matériel n'est pas accéléré en général, mais maintenant que Microsoft passe à WPF, il ne reviendra pas pour améliorer GDI +!

Tous les fabricants de cartes graphiques sont passés à la performance 3D et ne semblent pas s'intéresser à l'accélération 2D, et il n'existe aucune source d'information claire sur les fonctions accélérées ou non par le matériel. Très frustrant car après avoir écrit une application en .NET en utilisant GDI +, je ne suis pas heureux de passer à une technologie complètement différente pour l'accélérer à un niveau raisonnable.

Je ne pense pas qu'ils feront grand chose de différent, mais comme vous n'avez pas réellement besoin de redimensionner l'image, essayez d'utiliser la surcharge de DrawImage qui ne tente pas de redimensionner:

DrawImage(bitmap,0,0);

Comme je l'ai dit, je doute que cela fasse une différence, car je suis sûr que DrawImage vérifie la largeur et Hauteur du bitmap, et si aucun redimensionnement n’est nécessaire, appelle simplement cette surcharge. (J'espère que cela ne dérangera pas les 12 millions de pixels sans effectuer de travail réel).

Mise à jour: Mes réflexions sont fausses. Je l’ai découvert depuis, mais les commentaires me rappelaient mon ancienne réponse: vous voulez préciser la taille de la destination; même si elle correspond à la taille de la source:

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

Cela s'explique par les différences de dpi entre le dpi de bitmap et le dpi de la destination. GDI + effectuera une mise à l'échelle pour que l'image soit correctement "taille". (c'est-à-dire en pouces)

Ce que j’ai appris par moi-même depuis octobre dernier, c’est que vous voulez vraiment dessiner une version "mise en cache" de votre bitmap. Il existe une classe CachedBitmap dans GDI +. Il y a quelques astuces pour l'utiliser. Mais à la fin j'ai un bit de fonction (Delphi) qui le fait.

L'avertissement est que le CachedBitmap peut devenir invalide - ce qui signifie qu'il ne peut pas être utilisé pour dessiner. Cela se produit si l'utilisateur change de résolution ou de profondeur de couleur (par exemple, Remote Desktop). Dans ce cas, DrawImage échouera et vous devrez recréer le 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;

Le cachedBitmap est transmis par référence. Le premier appel à la version DrawCachedBitmap sera mis en cache . Vous le transmettez ensuite aux appels suivants, par exemple:

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

...

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

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

J’utilise cette routine pour dessiner des glyphes sur des boutons, en tenant compte du DPI actuel. L’équipe Internet Explorer aurait pu utiliser la même chose pour dessiner des images lorsque l’utilisateur exécutait une résolution dpi élevée (c’est-à-dire dessiner très lentement des images agrandies au zoom, car ils utilisent GDI +).

Essayez d’utiliser une copie de Bitmap à partir d’un fichier. La fonction FromFile sur certains fichiers retourne " slow " image, mais sa copie attirera plus rapidement.

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

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

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

/ * D'abord désolé pour l'anglais, et le code est en partie en polonais, mais c'est simple à comprendre. J'ai eu le même problème et j'ai trouvé la meilleure solution. Le voici.

Ne pas utiliser: Graphics Graphics (hdc); graphics.DrawImage (gpBitmap, 0, 0); C'est lent.

Utilisez: GetHBITMAP (Gdiplus :: Color (), & amp; g_hBitmap) pour HBITMAP et dessinez à l'aide de ma fonction ShowBitmapStretch ().

Vous pouvez le redimensionner et c'est beaucoup plus rapide! Artur Czekalski / Pologne

* /

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

J'ai effectué des recherches et je n'ai pas trouvé le moyen de rendre les images avec GDI / GDI + plus rapidement que

.
Graphics.DrawImage/DrawImageUnscaled

et en même temps simple comme ça.

Jusqu'à ce que & nbsp; j'ai découvert

ImageList.Draw(GFX,Point,Index)

et oui c'est vraiment si simple et rapide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top