gdi+ Graphics::DrawImage really slow~~

后端 未结 9 1000
一个人的身影
一个人的身影 2020-12-13 10:48

I am using a GDI+ Graphic to draw a 4000*3000 image to screen, but it is really slow. It takes about 300ms. I wish it just occupy less than 10ms.

Bitmap *bit         


        
相关标签:
9条回答
  • 2020-12-13 11:29

    Try using copy of Bitmap from file. FromFile function on some files returns "slow" image, but its copy will draw faster.

    Bitmap *bitmap = Bitmap::FromFile("XXXX",...);
    
    Bitmap *bitmap2 = new Bitmap(bitmap); // make copy
    
    DrawImage(bitmap2,0,0,width,height);
    
    0 讨论(0)
  • 2020-12-13 11:33

    i don't think they'll make much of a different, but since you're not actually needing to resize the image, try using the overload of DrawImage that doesn't (attempt) to resize:

    DrawImage(bitmap,0,0);
    

    Like i said, i doubt it will make any difference, because i'm sure that DrawImage checks the Width and Height of the bitmap, and if there's no resizing needed, just calls this overload. (i would hope it doesn't bother going through all 12 million pixels performing no actual work).

    Update: My ponderings are wrong. i had since found out, but guys comment reminded me of my old answer: you want to specify the destination size; even though it matches the source size:

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

    The reason is because of dpi differences between the dpi of bitmap and the dpi of the destination. GDI+ will perform scaling to get the image to come out the right "size" (i.e. in inches)


    What i've learned on my own since last October is that you really want to draw a "cached" version of your bitmap. There is a CachedBitmap class in GDI+. There are some tricks to using it. But in there end i have a function bit of (Delphi) code that does it.

    The caveat is that the CachedBitmap can become invalid - meaning it can't be used to draw. This happens if the user changes resolutions or color depths (e.g. Remote Desktop). In that case the DrawImage will fail, and you have to re-created the 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;
    

    The cachedBitmap is passed in by reference. The first call to DrawCachedBitmap it cached version will be created. You then pass it in subsequent calls, e.g.:

    Image imgPrintInvoice = new Image.FromFile("printer.png");
    CachedBitmap imgPrintInvoiceCached = null;
    
    ...
    
    int glyphSize = 16 * (GetCurrentDpi() / 96);
    
    DrawCachedBitmap(imgPrintInvoice , ref imgPrintInvoiceCached , graphics, 
          0, 0, glyphSize, glyphSize);
    

    i use the routine to draw glyphs on buttons, taking into account the current DPI. The same could have been used by the Internet Explorer team to draw images when the user is running high dpi (ie is very slow drawing zoomed images, because they use GDI+).

    0 讨论(0)
  • 2020-12-13 11:36

    If you're using GDI+, the TextureBrush class is what you need for rendering images fast. I've written a couple of 2d games with it, getting around 30 FPS or so.

    I've never written .NET code in C++, so here's a C#-ish example:

    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, ...)
    }
    
    0 讨论(0)
提交回复
热议问题