GDI+ DrawImage() Speed Issue
-
I'm working on an application which uses a GDI+ bitmap and graphics object to double-buffer to the CView class (I don't use any Windows controls at all). This is all working very well, and I've certainly found GDI+ a little easier to work with than my previous experience working with GDI. However, there are times when I need to be able to sustain refresh rates of around 50Hz on certain small portions of the screen. The drawing to the background bitmap isn't a big problem here, but the call to DrawImage() in OnPaint() is as it's taking a long time and chewing up a lot of processor time. I've tried doing a GDI BitBlt() instead but while that function is fast, the creation of the HDC and HBITMAP and everything else needed isn't, and there's no speed advantage. Has anybody got anybody got any ideas on how to speed up these calls to DrawImage()? I just don't believe that it should take up so much processor time to do so little - I know the GDI+ calls don't use hardware acceleration, but still...
-
You dont need to create the objects every draw process. Create them at startup. Try to draw the only changed parts of the picture or work with regions. ->CRegion Or try subbitmaps.
Greetings from Germany
I'm creating the offscreen Bitmap and Graphics objects at startup, and I'm only updating a specific rectangle each screen update. It's just that the call to DrawImage() in OnPaint() takes a long time. For example, to copy a 150x200 pixel rectangle from the memory bitmap to the screen takes 2 to 3ms on my machine, which at 50Hz screen updating hogs a lot of processor time. What do you mean by subbitmaps?
-
I'm working on an application which uses a GDI+ bitmap and graphics object to double-buffer to the CView class (I don't use any Windows controls at all). This is all working very well, and I've certainly found GDI+ a little easier to work with than my previous experience working with GDI. However, there are times when I need to be able to sustain refresh rates of around 50Hz on certain small portions of the screen. The drawing to the background bitmap isn't a big problem here, but the call to DrawImage() in OnPaint() is as it's taking a long time and chewing up a lot of processor time. I've tried doing a GDI BitBlt() instead but while that function is fast, the creation of the HDC and HBITMAP and everything else needed isn't, and there's no speed advantage. Has anybody got anybody got any ideas on how to speed up these calls to DrawImage()? I just don't believe that it should take up so much processor time to do so little - I know the GDI+ calls don't use hardware acceleration, but still...
Redeye92 wrote:
I've tried doing a GDI BitBlt() instead but while that function is fast, the creation of the HDC and HBITMAP and everything else needed isn't, and there's no speed advantage.
But I think you dont need to do it always. Once you copied to a memory dc, then only need to use BitBlt instead of each DrawImage.
- ns ami -
-
I'm creating the offscreen Bitmap and Graphics objects at startup, and I'm only updating a specific rectangle each screen update. It's just that the call to DrawImage() in OnPaint() takes a long time. For example, to copy a 150x200 pixel rectangle from the memory bitmap to the screen takes 2 to 3ms on my machine, which at 50Hz screen updating hogs a lot of processor time. What do you mean by subbitmaps?
-
Redeye92 wrote:
I've tried doing a GDI BitBlt() instead but while that function is fast, the creation of the HDC and HBITMAP and everything else needed isn't, and there's no speed advantage.
But I think you dont need to do it always. Once you copied to a memory dc, then only need to use BitBlt instead of each DrawImage.
- ns ami -
You know I was hoping that was going to be possible, but it seems that you can't write to the Graphics object whilst you've got its HDC. The code I've been using to substitute DrawImage() in OnPaint() is :
m\_pbmpMemory->GetHBITMAP(Color::White, &m\_hMemBitmap); m\_hMemHDC = m\_gr->GetHDC(); SelectObject(m\_hMemHDC, m\_hMemBitmap); BitBlt(pDC->m\_hDC, rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), m\_hMemHDC, rcPaint.left, rcPaint.top, SRCCOPY); m\_gr->ReleaseHDC(m\_hMemHDC);
Am I missing something here which would save me having to do all the stuff around the BitBlt? I just can't see how to keep a hold of the HDC and HBITMAP between calls to OnPaint(), but I'd be a happy man if someone knows how to get around this.
-
You know I was hoping that was going to be possible, but it seems that you can't write to the Graphics object whilst you've got its HDC. The code I've been using to substitute DrawImage() in OnPaint() is :
m\_pbmpMemory->GetHBITMAP(Color::White, &m\_hMemBitmap); m\_hMemHDC = m\_gr->GetHDC(); SelectObject(m\_hMemHDC, m\_hMemBitmap); BitBlt(pDC->m\_hDC, rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), m\_hMemHDC, rcPaint.left, rcPaint.top, SRCCOPY); m\_gr->ReleaseHDC(m\_hMemHDC);
Am I missing something here which would save me having to do all the stuff around the BitBlt? I just can't see how to keep a hold of the HDC and HBITMAP between calls to OnPaint(), but I'd be a happy man if someone knows how to get around this.
Redeye92 wrote:
I just can't see how to keep a hold of the HDC and HBITMAP between calls to OnPaint(),
You can create a memory dc and keep it permanantly (unless the size is changed). For eg.
void MyDlg::CreateMemDC( CDC* pRefDC, int nWidth, int nHeight )
{
m_dcMem.CreateCompatibleDC( pRefDC );
m_bitmapMem.CreateCompatibleBitmap( pRefDC, nWidth, nHeight );
m_dcMem.SelectObject( &m_bitmapMem );
}void MyDlg::OnInitDlg()
{
// some code
CClientDC dc( this );
CreateMemDC( &dc, 300, 200 ); // or what ever size you needed
// draw the image to this m_dcMem
}void MyDlg::OnPaint()
{
BitBlt( pDC->m_hDC, .... .... ... , m_dcMem.m_hDC, ...... );
}Please let me know if not clear enough.
- ns ami -
-
Redeye92 wrote:
I just can't see how to keep a hold of the HDC and HBITMAP between calls to OnPaint(),
You can create a memory dc and keep it permanantly (unless the size is changed). For eg.
void MyDlg::CreateMemDC( CDC* pRefDC, int nWidth, int nHeight )
{
m_dcMem.CreateCompatibleDC( pRefDC );
m_bitmapMem.CreateCompatibleBitmap( pRefDC, nWidth, nHeight );
m_dcMem.SelectObject( &m_bitmapMem );
}void MyDlg::OnInitDlg()
{
// some code
CClientDC dc( this );
CreateMemDC( &dc, 300, 200 ); // or what ever size you needed
// draw the image to this m_dcMem
}void MyDlg::OnPaint()
{
BitBlt( pDC->m_hDC, .... .... ... , m_dcMem.m_hDC, ...... );
}Please let me know if not clear enough.
- ns ami -
I think maybe I haven't explained my problem properly here... The method you described is what I've always used before this project. But this time, instead of using GDI objects in memory, I've used GDI+ objects (which has made my offscreen drawing code a lot simpler and prettier, which was worth it). So, in my code m_pbmpMemory is a GDI+ bitmap and m_gr is a GDI+ Graphics object. My problem is that DrawImage() is slow to the screen (the offscreen stuff isn't too bad really). I can't find a way to BitBlt from my GDI+ bitmap to the screen other than the code I posted and I was wondering if anyone else could see something I'm missing. Hang on, I have an idea. I guess the other option would be to use GDI objects and then create the GDI+ Graphics object from that using Graphics::FromHDC() and store that to use for drawing to the offscreen bitmap. It looks like you can do it that way round, but not the other way (ie. create the Graphics object first, then get the HDC - you can only use one at a time so you need to release the HDC every time you draw to the Bitmap). I'll go an investigate and see if that works...
-
I'm working on an application which uses a GDI+ bitmap and graphics object to double-buffer to the CView class (I don't use any Windows controls at all). This is all working very well, and I've certainly found GDI+ a little easier to work with than my previous experience working with GDI. However, there are times when I need to be able to sustain refresh rates of around 50Hz on certain small portions of the screen. The drawing to the background bitmap isn't a big problem here, but the call to DrawImage() in OnPaint() is as it's taking a long time and chewing up a lot of processor time. I've tried doing a GDI BitBlt() instead but while that function is fast, the creation of the HDC and HBITMAP and everything else needed isn't, and there's no speed advantage. Has anybody got anybody got any ideas on how to speed up these calls to DrawImage()? I just don't believe that it should take up so much processor time to do so little - I know the GDI+ calls don't use hardware acceleration, but still...
If you just want to double-buffer an existing MFC window class, you might want to look at something specifically designed to do that[^]?
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
-
If you just want to double-buffer an existing MFC window class, you might want to look at something specifically designed to do that[^]?
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
Thanks for all the suggestions folks. After some quick experiments, the solution appears to be something that I should have tried earlier but for some reason it didn't occur to me. It looks like the best way to do double buffering quickly and efficiently in GDI+ is actually to do the double buffering part in GDI, but when you create the memory DC, also create a GDI+ Graphics object from it and store that as a member of the View/Dialog class. This way, you can draw to the offscreen GDI CBitmap object using either the DC or the Graphics object (whichever takes your fancy / suits your needs). For the record, in this application, the difference between doing a BitBlt() and a DrawImage() in the OnPaint() command is a speed increase by a factor of between 6 and 8 times!
-
Thanks for all the suggestions folks. After some quick experiments, the solution appears to be something that I should have tried earlier but for some reason it didn't occur to me. It looks like the best way to do double buffering quickly and efficiently in GDI+ is actually to do the double buffering part in GDI, but when you create the memory DC, also create a GDI+ Graphics object from it and store that as a member of the View/Dialog class. This way, you can draw to the offscreen GDI CBitmap object using either the DC or the Graphics object (whichever takes your fancy / suits your needs). For the record, in this application, the difference between doing a BitBlt() and a DrawImage() in the OnPaint() command is a speed increase by a factor of between 6 and 8 times!
I need to do something similar. Do you mind posting the code your using?
-
I need to do something similar. Do you mind posting the code your using?
Not at all... In OnPaint()
// Get a device context for painting CPaintDC dc(this); if(!m_pMemDC) { vUpdateBitmap(&dc); } // Get the client rect CRect rcClientRect; GetClientRect(&rcClientRect); // Get the actual width of the view we want to draw ULONG32 ulViewWidth = ulGetViewWidth(); // If the window size has changed for any reason, we need to redraw the bitmap // Put your if statement here. { vUpdateBitmap(&dc); } // Get the invalid rectangle CRect rcPaint; dc.GetClipBox(rcPaint); // Then just copy the bit of the memory bitmap that we need dc.BitBlt(rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), m_pMemDC, rcPaint.left, rcPaint.top, SRCCOPY);
The vUpdateBitmap function:// Recreate the memory bitmap and graphics objects if(pDC) { if(m_pMemDC) { delete m_pMemDC; } m_pMemDC = new CDC; // Make a compatible DC m_pMemDC->CreateCompatibleDC(pDC);; // Create a new bitmap CBitmap *m_pNewBitmap; m_pNewBitmap = new CBitmap; // Create the new bitmap m_pNewBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height()); m_pNewBitmap->SetBitmapDimension(rcClient.Width(), rcClient.Height()); // Select the new bitmap into the memory DC m_pMemDC->SelectObject(m_pNewBitmap); // Delete the old one delete m_pbmpMemBitmap; // Reset the view pointer m_pbmpMemBitmap = m_pNewBitmap; if(m_gr) { delete m_gr; } m_gr = Graphics::FromHDC(m_pMemDC->m_hDC); }
Then you can use the m_gr Graphics object to do your GDI+ drawing to the offscreen bitmap and the actual screen draw is done by the much faster BitBlt. Hope that helps