Pregunta

Estoy usando un gráfico GDI + para dibujar una imagen de 4000 * 3000 en la pantalla, pero es muy lento. Tarda unos 300 ms. Deseo que solo ocupe menos de 10 ms.

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

// -------------------------------------------- // ¡esta parte dura unos 300 ms, terrible!

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

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

No puedo usar CachedBitmap, porque quiero editar el mapa de bits más tarde.

¿Cómo puedo mejorarlo? ¿O hay algo mal?

Esta función GDI nativa también dibuja la imagen en la pantalla, y solo toma 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 quiero usar StretchDIBits, necesito pasar BITMAPINFO, pero ¿cómo puedo obtener BITMAPINFO de un objeto de mapa de bits Gdi +? Hice el experimento por FreeImage lib, llamo StretchDIBits usando el objeto FreeImageplus, dibuja muy rápido. Pero ahora necesito dibujar Bitmap y escribir algún algoritmo en la matriz de bits de Bitmap, ¿cómo puedo obtener BITMAPINFO si tengo un objeto Bitmap? Es realmente molesto -___________- |

¿Fue útil?

Solución

¿Tiene una pantalla de resolución 4000 x 3000? ¡Guau!

Si no, debe dibujar solo la parte visible de la imagen, sería mucho más rápido ...

[EDITAR después del primer comentario] Mi comentario es realmente un poco estúpido, supongo que DrawImage enmascarará / saltará los píxeles innecesarios.

Después de su edición (que muestra StretchDIBits), creo que una posible fuente de diferencia de velocidad podría provenir del hecho de que StretchDIBits está acelerado por hardware (" Si el controlador no puede admitir la imagen de archivo JPEG o PNG " es una pista ...) mientras que DrawImage podría estar (¡no tengo pruebas de eso!) codificado en C, confiando en la potencia de la CPU en lugar de la GPU ...

Si recuerdo correctamente, las imágenes DIB son rápidas (a pesar de ser independientes del dispositivo). Consulte Alta velocidad Win32 Animation : " use CreateDIBSection para hacer animación de alta velocidad " ;. OK, se aplica a DIB frente a GDI, en la versión anterior de Windows (¡1996!), Pero creo que sigue siendo cierto.

[EDITAR] Quizás Bitmap :: GetHBITMAP podría ayudarlo a usar StretchDIBits (no probado ...).

Otros consejos

Si está utilizando GDI +, la clase TextureBrush es lo que necesita para renderizar imágenes rápidamente. He escrito un par de juegos 2D con él, obteniendo alrededor de 30 FPS más o menos.

Nunca he escrito código .NET en C ++, así que aquí hay un ejemplo de 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, ...)
}

Solo un pensamiento; en lugar de recuperar el ancho y el alto de la imagen antes de dibujar, ¿por qué no almacenar en caché estos valores cuando carga la imagen?

Explore el impacto de establecer explícitamente el modo de interpolación en NearestNeighbor (donde, en su ejemplo, ¡parece que la interpolación no es realmente necesaria! Pero 300 ms es el tipo de costo de realizar una interpolación de alta calidad cuando no se necesita interpolación, por lo vale la pena intentarlo)

Otra cosa para explorar es cambiar la profundidad de color del mapa de bits.

Desafortunadamente, cuando tuve un problema similar, ¡descubrí que GDI + es mucho más lento que GDI y generalmente no tiene aceleración de hardware, pero ahora Microsoft ha pasado a WPF, no volverán para mejorar GDI +!

Todos los fabricantes de tarjetas gráficas han pasado al rendimiento 3D y no parecen interesados ??en la aceleración 2D, y no hay una fuente clara de información sobre qué funciones son o no aceleradas por hardware. Muy frustrante porque después de haber escrito una aplicación en .NET usando GDI +, no estoy feliz de cambiar a una tecnología completamente diferente para acelerarla a niveles razonables.

no creo que hagan una gran diferencia, pero dado que no necesita cambiar el tamaño de la imagen, intente usar la sobrecarga de DrawImage que no (intenta) cambiar el tamaño:

DrawImage(bitmap,0,0);

Como dije, dudo que haga alguna diferencia, porque estoy seguro de que DrawImage verifica el Ancho y Altura del mapa de bits, y si no se necesita cambiar el tamaño, simplemente llama a esta sobrecarga. (Espero que no moleste pasar por los 12 millones de píxeles sin realizar un trabajo real).

Actualización: Mis reflexiones están mal. Desde entonces me enteré, pero los comentarios de los chicos me recordaron mi antigua respuesta: quieres especificar el tamaño del destino; a pesar de que coincide con el tamaño de la fuente:

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

La razón se debe a las diferencias de dpi entre el dpi de bitmap y el dpi del destino. GDI + realizará un escalado para que la imagen tenga el tamaño correcto. (es decir, en pulgadas)


Lo que aprendí por mi cuenta desde octubre pasado es que realmente quieres dibujar una versión " en caché " de tu mapa de bits. Hay una clase CachedBitmap en GDI +. Hay algunos trucos para usarlo. Pero al final tengo un bit de función de código (Delphi) que lo hace.

La advertencia es que CachedBitmap puede dejar de ser válido, lo que significa que no se puede usar para dibujar. Esto sucede si el usuario cambia resoluciones o profundidades de color (por ejemplo, Escritorio remoto). En ese caso, el DrawImage fallará y deberá volver a crear el 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;

El cachedBitmap se pasa por referencia. Se creará la primera llamada a DrawCachedBitmap it cached version. Luego lo pasa en llamadas posteriores, por ejemplo:

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 rutina para dibujar glifos en los botones, teniendo en cuenta el DPI actual. Lo mismo podría haber sido utilizado por el equipo de Internet Explorer para dibujar imágenes cuando el usuario está ejecutando un alto dpi (es decir, es muy lento dibujar imágenes con zoom, porque usan GDI +).

Intente usar una copia de Bitmap del archivo. La función FromFile en algunos archivos devuelve " lento " imagen, pero su copia se dibujará más rápido.

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

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

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

/ * Primero lo siento por mi inglés, y el código está parcialmente en polaco, pero es fácil de entender. Tuve el mismo problema y encontré la mejor solución. Aquí está.

No use: Gráficos gráficos (hdc); graphics.DrawImage (gpBitmap, 0, 0); Es lento.

Uso: GetHBITMAP (Gdiplus :: Color (), & amp; g_hBitmap) para HBITMAP y dibujar usando mi función ShowBitmapStretch ().

¡Puede cambiar su tamaño y es mucho más rápido! 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;

Investigué un poco y no pude encontrar una manera de procesar imágenes con GDI / GDI + más rápido que

Graphics.DrawImage/DrawImageUnscaled

y al mismo tiempo simple como eso.

Hasta & nbsp; descubrí

ImageList.Draw(GFX,Point,Index)

y sí, es realmente muy rápido y simple.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top