Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. GDI+ DrawImage() Speed Issue

GDI+ DrawImage() Speed Issue

Scheduled Pinned Locked Moved C / C++ / MFC
graphicshelpwinformshardwareperformance
14 Posts 6 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Redeye92

    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...

    K Offline
    K Offline
    KarstenK
    wrote on last edited by
    #4

    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

    R 1 Reply Last reply
    0
    • K KarstenK

      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

      R Offline
      R Offline
      Redeye92
      wrote on last edited by
      #5

      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?

      K 1 Reply Last reply
      0
      • R Redeye92

        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...

        N Offline
        N Offline
        Nishad S
        wrote on last edited by
        #6

        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 -

        R 1 Reply Last reply
        0
        • R Redeye92

          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?

          K Offline
          K Offline
          KarstenK
          wrote on last edited by
          #7

          substitute the DrawImage call. Make a offscreen HBM and use the faster bitblt.

          Greetings from Germany

          1 Reply Last reply
          0
          • N Nishad S

            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 -

            R Offline
            R Offline
            Redeye92
            wrote on last edited by
            #8

            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.

            N 1 Reply Last reply
            0
            • R Redeye92

              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.

              N Offline
              N Offline
              Nishad S
              wrote on last edited by
              #9

              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 -

              R 1 Reply Last reply
              0
              • N Nishad S

                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 -

                R Offline
                R Offline
                Redeye92
                wrote on last edited by
                #10

                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...

                1 Reply Last reply
                0
                • R Redeye92

                  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...

                  S Offline
                  S Offline
                  Stuart Dootson
                  wrote on last edited by
                  #11

                  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

                  R 1 Reply Last reply
                  0
                  • S Stuart Dootson

                    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

                    R Offline
                    R Offline
                    Redeye92
                    wrote on last edited by
                    #12

                    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!

                    M 1 Reply Last reply
                    0
                    • R Redeye92

                      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!

                      M Offline
                      M Offline
                      micahferguson
                      wrote on last edited by
                      #13

                      I need to do something similar. Do you mind posting the code your using?

                      R 1 Reply Last reply
                      0
                      • M micahferguson

                        I need to do something similar. Do you mind posting the code your using?

                        R Offline
                        R Offline
                        Redeye92
                        wrote on last edited by
                        #14

                        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

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups