Pergunta

Eu estou usando um GDI + gráfico para desenhar uma imagem de 4000 * 3000 para a tela, mas é muito lento. Demora cerca de 300ms. Eu gostaria que apenas ocupam menos de 10ms.

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

// -------------------------------------------- // esta parte leva cerca de 300ms, terrível!

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

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

Eu não posso usar CachedBitmap, porque eu quero editar o bitmap mais tarde.

Como posso melhorá-lo? Ou é qualquer coisa errada?

Esta função GDI nativa também desenha a imagem na tela, e basta levar 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 eu quiser usar StretchDIBits, eu preciso passar BITMAPINFO, mas como posso obter BITMAPINFO de um objeto GDI + Bitmap? Eu fiz o experimento por FreeImage lib, eu chamo de StretchDIBits usando objeto FreeImageplus, é desenhar muito rápido. Mas agora eu preciso para chamar Bitmap, e escrever algum algoritmo em conjunto pedaços de Bitmap, como posso obter BITMAPINFO se eu tiver um objeto Bitmap? É realmente irritante -___________- |

Foi útil?

Solução

Você tem uma tela de 4000 x 3000 resolução? Uau!

Se não, você deve desenhar apenas a parte visível da imagem, seria muito mais rápido ...

[EDIT após o primeiro comentário] Minha observação é de fato um pouco estúpido, eu suponho DrawImage irá mascarar / saltar pixels desnecessários.

Depois de sua edição (mostrando StretchDIBits), eu acho que uma possível fonte de diferença de velocidade pode vir do fato de que StretchDIBits é acelerado por hardware ( " Se o motorista não pode apoiar o arquivo de imagem JPEG ou PNG " é uma dica ...) enquanto DrawImage pode ser (não tenho provas para isso!) codificado em C, contando com energia da CPU em vez de um de GPU ...

Se bem me lembro, imagens DIB são rápidas (apesar de ser "independente do dispositivo"). Consulte alta velocidade Win32 Animação : "< em> utilização CreateDIBSection para fazer animação de alta velocidade ". OK, ela se aplica a DIB vs. GDI, na versão do Windows de idade (1996!), Mas eu acho que ainda é verdade.

[EDIT] Talvez Bitmap :: GetHBITMAP função pode ajudá-lo a StretchDIBits de uso (não testado ...).

Outras dicas

Se você estiver usando GDI +, a classe TextureBrush é o que você precisa para processar imagens rapidamente. Eu escrevi um par de jogos 2D com ele, ficando em torno de 30 FPS ou assim.

Eu nunca escrevi código .NET em C ++, então aqui está um exemplo 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, ...)
}

Apenas um pensamento; em vez de recuperar a largura e altura da imagem antes de desenhar, por que não armazenar em cache esses valores quando você carrega a imagem?

Explorar o impacto do ajuste explicitamente o modo de interpolação para NearestNeighbor (onde, no seu exemplo, parece que a interpolação não é realmente necessário! Mas 300ms é o tipo de custo de fazer interpolação de alta qualidade quando sem interpolação é necessária, de modo vale a pena uma tentativa)

Outra coisa a explorar está a mudar a profundidade de cor do bitmap.

Infelizmente, quando eu tive um problema semelhante, eu achei que GDI + é conhecido por ser muito mais lento do GDI e geralmente não acelerado por hardware, mas agora a Microsoft se mudaram para WPF que não vai voltar a melhorar GDI +!

Todos os fabricantes de placas gráficas se mudaram para o desempenho 3D e não parecem interessados ??em aceleração 2D, e não há nenhuma fonte de informação clara sobre quais as funções ou pode ser acelerado por hardware ou não. Muito frustrante porque ter escrito um aplicativo no .NET usando GDI +, eu não estou feliz em mudar para uma tecnologia completamente diferente para acelerá-lo a níveis razoáveis.

eu não acho que eles vão fazer muita diferente, mas desde que você não está realmente a necessidade de redimensionar a imagem, tente usar a sobrecarga de DrawImage que não faz (tentativa) para redimensionar:

DrawImage(bitmap,0,0);

Como eu disse, eu duvido que ele vai fazer qualquer diferença, porque eu sou certeza que DrawImage verifica o Largura e altura do bitmap, e se não há nenhum redimensionamento necessário, apenas chama essa sobrecarga. (Eu espero que não se incomoda de passar por todos os 12 milhões de pixels realizando nenhum trabalho real).

Update: Meus ponderações estão errados. eu tinha encontrado uma vez, mas caras comentário me fez lembrar da minha velha resposta: você deseja para especificar o tamanho de destino; mesmo que corresponda ao tamanho fonte:

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

A razão é por causa das diferenças dpi entre o dpi de bitmap ea dpi do destino. GDI + realizará escala para obter a imagem para sair à direita "tamanho" (ou seja, em polegadas)


O que eu aprendi em minha própria desde outubro passado é que você realmente quer desenhar um "cache" versão do seu bitmap. Há uma classe CachedBitmap em GDI +. Existem alguns truques para usá-lo. Mas lá acabar eu tenho um pouco a função de código (Delphi) que faz isso.

A ressalva é que o CachedBitmap pode se tornar inválido - o que significa que não pode ser usado para desenhar. Isto acontece se o utilizador altera a resolução ou profundidades de cor (por exemplo remota da área de trabalho). Nesse caso, o DrawImage irá falhar, e você tem que re-criou o 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;

O cachedBitmap é passado por referência. A primeira chamada para DrawCachedBitmap ele cache versão será criada. Você, então, passá-lo em chamadas subseqüentes, por exemplo:.

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 utilizar a rotina de desenhar glifos em botões, tendo em conta a actual DPI. O mesmo poderia ter sido usado pela equipe Internet Explorer para desenhar imagens quando o usuário está executando alta dpi (isto é desenho muito lento imagens ampliadas, porque eles usam GDI +).

Tente usar cópia do mapa de bits a partir do arquivo. função FromFile em alguns arquivos de imagem retorna "lento", mas a sua cópia vai chamar mais rápido.

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

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

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

/ * Primeiro pena de ma Inglês, eo código é parte em polonês, mas é simples de entender. Eu tive o mesmo problema e eu encontrei a melhor solução. Aqui está.

Não use: gráficos Graphics (HDC); Graphics.drawImage (gpBitmap, 0, 0); É um processo lento.

Use:. GetHBITMAP (Gdiplus :: Cor (), & g_hBitmap) para HBITMAP e desenhar usando minha função ShowBitmapStretch ()

Você pode redimensioná-la e é muito mais rápido! Artur Czekalski / Poland

* /

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

Eu fiz alguma pesquisa e não foi capaz de encontrar uma maneira de renderizar imagens com GDI / GDI + mais rápido do que

Graphics.DrawImage/DrawImageUnscaled

e, ao mesmo tempo simples como isso.

Até que eu descobri

ImageList.Draw(GFX,Point,Index)

e sim, é realmente tão rápido e simples.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top