2008-11-05 6 views
17

4000 * 3000 이미지를 화면에 그릴 때 GDI + 그래픽을 사용하고 있지만 실제로 느립니다. 약 300ms가 걸립니다. 나는 그것이 단지 10ms 미만을 차지하기를 바란다.gdi + Graphics :: DrawImage really slow ~~

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

// ----------------------------------------- --- //이 부분은 약 300ms가 소요됩니다.

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

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

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

어떻게 향상시킬 수 있습니까? 또는 어떤 것이 잘못 되었습니까? ------------------

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

// :

이 기본 GDI의 기능은 화면에 이미지를 그립니다, 그리고 그것은 단지 1 밀리 받아 --------------------------------------------

만약 내가 StretchDIBits를 사용하고 싶습니다. BITMAPINFO를 전달해야합니다. 그러나 GDi + Bitmap 객체에서 BITMAPINFO를 얻으려면 어떻게해야합니까? 나는 FreeImage 라이브러리에 의해 실험을했는데, FreeImageplus 객체를 사용하여 StretchDIBits를 호출했다. 정말 빨랐다. 하지만 이제 Bitmap을 그려야하고 Bitmap의 비트 배열에 알고리즘을 작성해야합니다. Bitmap 개체가있는 경우 어떻게 BITMAPINFO를 얻을 수 있습니까? 정말 귀찮아 .- ___________- |

+0

두 번째 예제를 살펴보면 StretchDIBits가 GPU 가속화 (아마 DrawImage)라고 말하고 싶습니다. 내 업데이트 답변을 참조하십시오. StretchDIBits를 사용하면 문제를 만족스럽게 해결할 수 있습니까? – PhiLho

+0

감사합니다 PhiLho,하지만 StretchDIBits 사용할 수 없습니다 ~ 난 그냥 빨리 알고, 큰 이미지를 그릴 수 있습니다 ~ ~ 내가 왜 ~~/ – user25749

+0

사용할 수없는 문제를 추가 할 수 있습니다. 비트 맵을 변환 할 수 있습니다. Bitmap :: GetHBITMAP있는 GDI 개체 (내 대답 업데이트 링크 참조). – PhiLho

답변

3

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

, 당신은 이미지의 보이는 부분을 그릴해야하지 않으면, 내가의 drawImage 마스크 것입니다 가정

[첫번째 코멘트 후 EDIT]를 내 발언은 사실 약간 바보입니다 ... 훨씬 빠른 것/불필요한 픽셀을 건너 뜁니다. "드라이버가 JPEG 또는 PNG 파일 이미지을 지원할 수 경우"당신의 편집 (보여주는 StretchDIBits) 후

, 나는 속도 차이의 가능한 소스가 StretchDIBits 가속 하드웨어 (사실에서 올 수도 추측 힌트입니다. ..) 동안 DrawImage 수도 (나는 그것에 대한 증거가 없어!), GPU의 하나 대신에 CPU의 전력에 의존 코딩 ...

내가 올바르게 회상하면, DIB 이미지는 빠르다. "). High Speed Win32 Animation : "고속 애니메이션을 만들기위한 CreateDIBSection 사용"을 참조하십시오. 그래, DIB 대 GDI, 이전 Windows 버전 (1996!)에도 적용되지만 여전히 사실이라고 생각합니다.

[편집] 아마 Bitmap::GetHBITMAP 함수는 StretchDIBits (테스트하지 않은 ...)를 사용하는 데 도움이 될 수 있습니다.

+0

경계를 확인하지 않습니까? 이 함수가 화면 밖으로 벗어나는 것을 모른다면 바보처럼 보일 것입니다. – user25749

+0

Bitmap :: GetHBITMAP이 함수 자체가 언젠가 걸립니다 – user25749

+3

"C 코드가 될 수 있습니다"라는 말은 귀찮습니다. –

2

그냥 생각해보십시오. 그리기 전에 이미지의 너비와 높이를 검색하는 대신 이미지를로드 할 때 왜이 값을 캐시하지 않으시겠습니까?

+0

이것은 매우 이유 일 수 있습니다. .NET의 비트 맵은 프로그래밍 이력에서 가장 느린 Width/Height 게터를가집니다. 나는 여러 차례에 걸쳐 프로파일 링에서 CPU의 95 %를 차지하는 것으로 자라났습니다. –

2

명시 적으로 보간 모드를 NearestNeighbor로 설정했을 때의 영향을 살펴보십시오. (예를 들어, 보간이 실제로 필요하지 않은 것처럼 보입니다!하지만 보간이 필요없는 경우 300ms는 고품질 보간을 수행하는 데 소요되는 비용입니다. 그래서 그것의 가치가)

탐험 또 다른 것은 비트 맵의 ​​색상 깊이를 변경하는 것입니다.

15

GDI +를 사용하는 경우 TextureBrush 클래스는 이미지를 빠르게 렌더링하는 데 필요한 클래스입니다. 필자는 2d 게임을 써서 약 30fps 정도를 썼다. 불행하게도

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

은 다음과 같아야합니다 : 'e.Graphics.FillRectangle (myBrush, ClientRectangle); ' 하나의 질문 - 비트 맵이 바뀔 때마다 브러쉬를 다시 만들어야합니까? – argh

+0

맞습니다. Graphics.DrawBrush 메서드가 없습니다. 나는 대응하는 비트 맵을 수정 한 후에 같은 브러시를 재사용하려 한 적이 없으므로, 다시 만들 필요가 있는지 없는지는 알 수 없다. 내 GDI + 게임에서 모든 텍스처 브러시 개체는 프로그램로드시 만들어지며 각 텍스처 브러시는 스프라이트에 대한 단일 프레임 애니메이션입니다. – Cybis

+0

cybis와 같은 드로잉 메서드가 선택 될 수 있지만 코드에서 추가 컨트롤을 사용하여 저수준 dll을 사용하여 가장 빠른 방법을 얻을 수 있습니다. – dankyy1

2

나는 비슷한 문제가있을 때, 나는 GDI +는 GDI보다 훨씬 느린 것으로 알려져 것을 발견 : 나는 C++에서 .NET 코드 작성 적이

, 그래서 여기에 C#을 -ish 예제 일반적으로 하드웨어가 가속화 된 것은 아니지만 지금은 Microsoft가 WPF로 옮겼습니다. GDI +를 개선하기 위해 다시 돌아 오지 않을 것입니다!

모든 그래픽 카드 제조업체는 3D 성능으로 옮겼으며 2D 가속에 관심이 없어 보였고 하드웨어 가속 기능이 있는지 여부에 대한 명확한 정보는 없습니다. GDI +를 사용하여. NET에서 응용 프로그램을 작성했기 때문에 매우 실망 스러웠습니다. 나는 그것을 합리적인 수준까지 빠르게하기 위해 완전히 다른 기술로 변경하게되어 기쁘지 않습니다.

+0

이것은 전혀 관련이 없습니다. – argh

2

는 나는 그들이 다른 많은을 거라고 생각하지만, 실제로 이미지 크기를 조정 필요하지 않을 때문에, 크기를 조정하지 않습니다 DrawImage의 과부하 (시도)를 사용하지 않습니다

내가 말했듯이 내가 필요한 크기를 조절 존재하지 않는 경우는, 그냥이를 호출 확인의 drawImage 검사에게 와 비트 맵의 ​​ 높이것을이기 때문에
DrawImage(bitmap,0,0); 

, 난,이 모든 차이를 만들 것입니다 의심 초과 적재. (나는 그것이 실제 작업을 수행하지 않고 1200 만 화소를 처리하는 것을 방해하지 않기를 바랄 것이다).

업데이트 : 내 생각이 틀립니다. 나는 그 이후에 알아 냈지만, 사람들의 의견은 저의 오래된 대답을 생각 나게합니다 : 에 목적지 크기를 지정하고 싶습니다. 이 원본 크기와 일치하더라도 :

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

이유 때문에 bitmap의 dpi로하고, 대상으로 dpi 사이 dpi의 차이이다. GDI + 내가 지난 10 월 이후로 내 자신에 배운 내용을


당신이 정말 을 그리려는 것입니다 권리 (인치 예) "크기"나올 이미지를 얻기 위해 확장 수행합니다 "캐시 " 비트 맵 버전. GDI +에는 CachedBitmap 클래스가 있습니다. 그것을 사용하는 몇 가지 트릭이 있습니다. 하지만 거기에 결국 나는 그것을 (델파이) 코드의 기능 비트가있다.

경고는 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를 실행할 때 Internet Explorer 팀에서 이미지를 그릴 수 있습니다 (즉, GDI +를 사용하기 때문에 확대 된 이미지를 매우 느리게 그립니다).

+0

http://msdn.microsoft.com/en-us/library/1bttkazd(VS.71).aspx에이 동상에 대한 자세한 기사가 있습니다. – dankyy1

1

파일의 비트 맵 복사본을 사용해보십시오. 일부 파일의 FromFile 함수는 "느린"이미지를 반환하지만 복사본이 더 빨리 그려집니다.

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

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

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

/* 는 엄마 영어, 및 코드에 대한 유감 먼저 부분적으로 폴란드어이지만, 이해하기 간단합니다. 나는 똑같은 문제가있어서 가장 좋은 해결책을 찾았습니다. 여기있어.

사용하지 않음 : 그래픽 그래픽 (hdc); graphics.DrawImage (gpBitmap, 0, 0); 천천히.

HBITMAP 용 GetHBITMAP (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; 
0

좀 연구를 만들고 이미지를 렌더링 할 수있는 방법을 찾을 수 없습니다 한 아르투르 Czekalski/폴란드

GDI/GDI + 더

Graphics.DrawImage/DrawImageUnscaled 

과의보다 빠른

그것과 같은 간단한 시간.

나는
ImageList.Draw(GFX,Point,Index) 

을 발견까지하고 그래 정말 너무 빠르고 간단합니다.