문제

GDI+ 그래픽을 사용하여 4000*3000 이미지를 화면에 그리는 중인데 속도가 정말 느립니다.약 300ms 정도 소요됩니다.10ms 미만만 차지하면 좋겠어요.

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

//-------------------------------------------- // 이 부분 약 300ms, 끔찍한!

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

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

나중에 비트맵을 편집하고 싶기 때문에 CachedBitmap을 사용할 수 없습니다.

어떻게 개선할 수 있나요?아니면 무슨 문제라도 있는 걸까요?

이 기본 GDI 함수는 이미지를 화면에 그리는데 1ms밖에 걸리지 않습니다.

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

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

StretchDIBits를 사용하려면 BITMAPINFO를 전달해야 합니다. 그런데 Gdi+ 비트맵 개체에서 BITMAPINFO를 어떻게 얻을 수 있나요?FreeImage lib로 실험을 했는데 FreeImageplus 개체를 사용하여 StretchDIBits를 호출했는데 정말 빠르게 그려졌습니다.하지만 이제 Bitmap을 그리고 Bitmap의 비트 배열에 일부 알고리즘을 작성해야 합니다. Bitmap 개체가 있는 경우 BITMAPINFO를 어떻게 얻을 수 있습니까?정말 짜증나네요 -___________-|

도움이 되었습니까?

해결책

4000 x 3000 해상도의 화면이 있습니까?우와!

그렇지 않다면 이미지에서 보이는 부분만 그리는 것이 훨씬 더 빠를 것입니다...

[첫 번째 댓글 후 편집] 내 발언은 실제로 약간 어리석습니다. DrawImage가 불필요한 픽셀을 마스크/건너뛸 것이라고 가정합니다.

편집 후(StretchDIBits 표시) 속도 차이의 가능한 원인은 다음과 같습니다. StretchDIBits 하드웨어 가속("드라이버가 JPEG 또는 PNG 파일 이미지를 지원할 수 없는 경우"는 힌트입니다...) DrawImage는 (증거가 없습니다!) C로 코딩되어 GPU 대신 CPU 성능에 의존할 수도 있습니다...

내가 올바르게 기억한다면 DIB 이미지는 빠릅니다("장치 독립적"임에도 불구하고).보다 고속 Win32 애니메이션: "CreateDIBSection을 사용하여 고속 애니메이션 수행".좋습니다. DIB와 DIB에 적용됩니다.이전 Windows 버전(1996!)의 GDI이지만 여전히 사실이라고 생각합니다.

[편집] 어쩌면 비트맵::GetHBITMAP 함수는 StretchDIBits를 사용하는 데 도움이 될 수 있습니다(테스트되지 않음...).

다른 팁

GDI+를 사용하는 경우 TextureBrush 클래스는 이미지를 빠르게 렌더링하는 데 필요합니다.나는 그것으로 약 30FPS 정도를 얻는 몇 가지 2D 게임을 작성했습니다.

저는 C++로 .NET 코드를 작성한 적이 없으므로 다음은 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, ...)
}

그냥 생각입니다.그리기 전에 이미지의 너비와 높이를 검색하는 대신 이미지를 로드할 때 이러한 값을 캐시하는 것은 어떨까요?

보간 모드를 NearestNeighbor로 명시적으로 설정하는 경우의 영향을 살펴보세요(예제에서는 보간이 실제로 필요하지 않은 것처럼 보입니다!).하지만 300ms는 보간이 필요하지 않을 때 고품질 보간을 수행하는 일종의 비용이므로 시도해 볼 가치가 있습니다.)

살펴볼 또 다른 사항은 비트맵의 색상 심도를 변경하는 것입니다.

불행히도 비슷한 문제가 발생했을 때 GDI+는 GDI보다 훨씬 느리고 일반적으로 하드웨어 가속이 아닌 것으로 알려져 있지만 이제 Microsoft는 WPF로 전환했으며 GDI+를 개선하기 위해 다시 돌아오지 않을 것입니다!

모든 그래픽 카드 제조업체는 3D 성능으로 전환했으며 2D 가속에는 관심이 없는 것 같습니다. 또한 어떤 기능이 하드웨어 가속인지 여부에 대한 명확한 정보 소스가 없습니다.GDI+를 사용하여 .NET에서 앱을 작성했기 때문에 매우 실망스럽습니다. 합리적인 수준으로 속도를 높이기 위해 완전히 다른 기술로 변경하는 것이 마음에 들지 않습니다.

나는 그들이 크게 달라질 것이라고 생각하지 않지만 실제로 이미지 크기를 조정할 필요가 없으므로 이미지 그리기 크기를 조정하려고 시도하지 않습니다.

DrawImage(bitmap,0,0);

내가 말했듯이, 나는 그것이 어떤 변화를 가져올 것이라고 의심합니다. 왜냐하면 나는 확신하는 저것 이미지 그리기 확인한다 너비 그리고 크기 조정이 필요하지 않으면 이 오버로드를 호출하면 됩니다.(실제 작업을 수행하지 않고 1,200만 픽셀을 모두 처리하는 데 방해가 되지 않기를 바랍니다.)

업데이트:내 생각은 틀렸다.나는 그 이후로 알아냈지만, 사람들의 댓글을 보니 내 예전 대답이 생각나더군요.너 원하다 대상 크기를 지정합니다.소스 크기와 일치하더라도:

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

그 이유는 dpi 간의 dpi 차이 때문입니다. bitmap 그리고 목적지의 dpi.GDI+는 이미지가 올바른 "크기"(예:인치로)


지난 10월부터 제가 스스로 배운 것은 당신은 정말 그림을 그리고 싶어한다는 것입니다. "캐시됨" 비트맵 버전.이있다 CachedBitmap GDI+의 클래스입니다.그것을 사용하는 데는 몇 가지 요령이 있습니다.하지만 결국 나는 그것을 수행하는 (델파이) 코드의 기능 비트를 가지고 있습니다.

주의할 점은 CachedBitmap 유효하지 않게 될 수 있습니다. 즉, 그리는 데 사용할 수 없습니다.이는 사용자가 해상도나 색상 깊이를 변경한 경우에 발생합니다(예:원격 데스크탑).그 경우에는 DrawImage 실패하므로 다시 생성해야 합니다. 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;

그만큼 cachedBitmap 참조로 전달됩니다.첫 번째 호출 DrawCachedBitmap 그것 캐시된 버전이 생성됩니다.그런 다음 후속 호출에서 이를 전달합니다. 예:

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

...

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

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

나는 현재 DPI를 고려하여 버튼에 문자 모양을 그리는 루틴을 사용합니다.사용자가 높은 dpi를 실행할 때(즉, GDI+를 사용하기 때문에 확대/축소된 이미지를 그리는 속도가 매우 느림) Internet Explorer 팀에서도 동일한 방법을 사용하여 이미지를 그릴 수 있습니다.

파일의 비트맵 복사본을 사용해 보세요.일부 파일의 FromFile 함수는 "느린" 이미지를 반환하지만 해당 복사본은 더 빠르게 그려집니다.

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

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

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

/* 먼저 MA English에 대해 죄송합니다. 코드는 부분적으로 폴란드 인이지만 이해하기가 간단합니다.나는 같은 문제가 있었고 가장 좋은 해결책을 찾았습니다.여기있어.

사용하지 마세요:그래픽 그래픽(hdc);graphic.DrawImage(gpBitmap, 0, 0);느립니다.

사용:HBITMAP에 대해 HBITMAP(Gdiplus::Color(), &g_hBitmap)을 얻고 ShowBitmapStretch() 함수를 사용하여 그립니다.

크기를 조정할 수 있으며 훨씬 빠릅니다!아르투르 체칼스키 / 폴란드

*/

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

나는 약간의 연구를 했으나 GDI/GDI+를 사용하여 이미지를 보다 빠르게 렌더링하는 방법을 찾을 수 없었습니다.

Graphics.DrawImage/DrawImageUnscaled

동시에 간단합니다.

내가 발견할 때까지

ImageList.Draw(GFX,Point,Index)

네, 정말 빠르고 간단해요.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top