Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Issues with drawing on the HDC of QWidget by functions from other libs.

Issues with drawing on the HDC of QWidget by functions from other libs.

Scheduled Pinned Locked Moved Solved General and Desktop
15 Posts 4 Posters 1.9k Views 2 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.
  • Christian EhrlicherC Christian Ehrlicher

    @Pl45m4 aYou should paint during QWidget::paintEvent(). Your widget is not even visible in your code above when you're executing L_PaintDC().

    Pl45m4P Offline
    Pl45m4P Offline
    Pl45m4
    wrote on last edited by
    #4

    @Christian-Ehrlicher Why me? :D
    It's not my topic :)

    That would be the next thing I would have replied.
    The HWND / winID is used before the widget is shown, so it might even change and you're addressing an invalid window


    If debugging is the process of removing software bugs, then programming must be the process of putting them in.

    ~E. W. Dijkstra

    1 Reply Last reply
    1
    • Pl45m4P Pl45m4

      @Yurika

      Debug your application and check what values you receive in the process.

      YurikaY Offline
      YurikaY Offline
      Yurika
      wrote on last edited by
      #5

      @Christian-Ehrlicher @Pl45m4 OMG, thanks a lot for informing me of these! I've moved all of the L_PaintDC related codes into the paintEvent(QPaintEvent* e) function and it successfully displays the image.

      But there some new issues came out. When I drag or resize the window, the image sometimes flickers and sometimes disappears completely. And when I drag the window again, sometimes the image reappears. These also happen when I restore the window after minimizing it. Here is my new codes:

      Claim QPaintEvent override:

      protected:
      	void paintEvent(QPaintEvent* e) override;
      

      Definition:

      void VM_imagewindow::paintEvent(QPaintEvent* e)
      {
          HWND hWnd = HWND(winId());  //  Get this window's Handler
          HDC hdc = GetDC(hWnd);  //  Get the device context
          RECT rc = { 0, 0, BITMAPWIDTH(&targetBitmap), BITMAPHEIGHT(&targetBitmap) };    //  Define dst rectangle
          int i;  //  For debug
          if (targetBitmap.Flags.Allocated)
              //  L_PaintDC returns 1 in this case
              i = L_PaintDC(hdc, &targetBitmap, NULL, NULL, &rc, NULL, SRCCOPY);
      }
      

      By the way, I'm actually using the QMdiArea and QMdiSubWindow which handles my VM_imagewindow because I want my software to be like photoshop.

      Is there anything I'm doing wrong?

      Thanks for helping me.

      Christian EhrlicherC Pl45m4P 2 Replies Last reply
      0
      • YurikaY Yurika

        @Christian-Ehrlicher @Pl45m4 OMG, thanks a lot for informing me of these! I've moved all of the L_PaintDC related codes into the paintEvent(QPaintEvent* e) function and it successfully displays the image.

        But there some new issues came out. When I drag or resize the window, the image sometimes flickers and sometimes disappears completely. And when I drag the window again, sometimes the image reappears. These also happen when I restore the window after minimizing it. Here is my new codes:

        Claim QPaintEvent override:

        protected:
        	void paintEvent(QPaintEvent* e) override;
        

        Definition:

        void VM_imagewindow::paintEvent(QPaintEvent* e)
        {
            HWND hWnd = HWND(winId());  //  Get this window's Handler
            HDC hdc = GetDC(hWnd);  //  Get the device context
            RECT rc = { 0, 0, BITMAPWIDTH(&targetBitmap), BITMAPHEIGHT(&targetBitmap) };    //  Define dst rectangle
            int i;  //  For debug
            if (targetBitmap.Flags.Allocated)
                //  L_PaintDC returns 1 in this case
                i = L_PaintDC(hdc, &targetBitmap, NULL, NULL, &rc, NULL, SRCCOPY);
        }
        

        By the way, I'm actually using the QMdiArea and QMdiSubWindow which handles my VM_imagewindow because I want my software to be like photoshop.

        Is there anything I'm doing wrong?

        Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #6

        @Yurika said in Issues with drawing on the HDC of QWidget by functions from other libs.:

        (targetBitmap.Flags.Allocated)

        Is this always true after a resize?
        A resize/move event should trigger a repaint.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        YurikaY 1 Reply Last reply
        0
        • YurikaY Yurika

          @Christian-Ehrlicher @Pl45m4 OMG, thanks a lot for informing me of these! I've moved all of the L_PaintDC related codes into the paintEvent(QPaintEvent* e) function and it successfully displays the image.

          But there some new issues came out. When I drag or resize the window, the image sometimes flickers and sometimes disappears completely. And when I drag the window again, sometimes the image reappears. These also happen when I restore the window after minimizing it. Here is my new codes:

          Claim QPaintEvent override:

          protected:
          	void paintEvent(QPaintEvent* e) override;
          

          Definition:

          void VM_imagewindow::paintEvent(QPaintEvent* e)
          {
              HWND hWnd = HWND(winId());  //  Get this window's Handler
              HDC hdc = GetDC(hWnd);  //  Get the device context
              RECT rc = { 0, 0, BITMAPWIDTH(&targetBitmap), BITMAPHEIGHT(&targetBitmap) };    //  Define dst rectangle
              int i;  //  For debug
              if (targetBitmap.Flags.Allocated)
                  //  L_PaintDC returns 1 in this case
                  i = L_PaintDC(hdc, &targetBitmap, NULL, NULL, &rc, NULL, SRCCOPY);
          }
          

          By the way, I'm actually using the QMdiArea and QMdiSubWindow which handles my VM_imagewindow because I want my software to be like photoshop.

          Is there anything I'm doing wrong?

          Pl45m4P Offline
          Pl45m4P Offline
          Pl45m4
          wrote on last edited by
          #7

          @Yurika said in Issues with drawing on the HDC of QWidget by functions from other libs.:

          But there some new issues came out. When I drag or resize the window, the image sometimes flickers and sometimes disappears completely. And when I drag the window again, sometimes the image reappears. These also happen when I restore the window after minimizing it.

          This sounds like the behavior described in this topic here
          @hskoglund 's answer there might work out for you:

          • https://forum.qt.io/post/596932

          If debugging is the process of removing software bugs, then programming must be the process of putting them in.

          ~E. W. Dijkstra

          YurikaY 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            @Yurika said in Issues with drawing on the HDC of QWidget by functions from other libs.:

            (targetBitmap.Flags.Allocated)

            Is this always true after a resize?
            A resize/move event should trigger a repaint.

            YurikaY Offline
            YurikaY Offline
            Yurika
            wrote on last edited by
            #8

            @Christian-Ehrlicher Yes it is. Is repaint also a virtual function defined in QWidget? So did you mean I have to do something inside the repaint function?

            Thanks for helping me.

            1 Reply Last reply
            0
            • Pl45m4P Pl45m4

              @Yurika said in Issues with drawing on the HDC of QWidget by functions from other libs.:

              But there some new issues came out. When I drag or resize the window, the image sometimes flickers and sometimes disappears completely. And when I drag the window again, sometimes the image reappears. These also happen when I restore the window after minimizing it.

              This sounds like the behavior described in this topic here
              @hskoglund 's answer there might work out for you:

              • https://forum.qt.io/post/596932
              YurikaY Offline
              YurikaY Offline
              Yurika
              wrote on last edited by
              #9

              @Pl45m4 I tried it but unfortunately it doesn't work.

              Thanks for helping me.

              1 Reply Last reply
              0
              • Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by Chris Kawa
                #10

                The "problem" here is that Qt painting, since 4.something is double buffered, meaning paintEvent does not paint directly onto the window DC, but to a backbuffer that is then BitBlt to the window.

                This means that when you create another DC on the same surface you're basically racing with Qt as to who gets to paint last. It might look like something is working, but, as with any race, it's just a coincidence on your particular setup (Windows version, graphics adapter and driver etc. etc.). Might work for some static painting, but the timing changes slightly on resize and you get flicker. On other machine it might flicker in other scenarios, all the time even.

                Qt does not expose a window DC that you can draw to, so the only way to do it in sync is to use Qt's drawing facilities i.e. draw using QPainter in paintEvent. Since you have a foreign API for drawing you can create a bitmap to draw to and then wrap it in QImage interface and paint to the window using one of the QPainter's functions. Just make sure that the bitmap and QImage have compatible data formats.

                YurikaY 1 Reply Last reply
                2
                • Chris KawaC Chris Kawa

                  The "problem" here is that Qt painting, since 4.something is double buffered, meaning paintEvent does not paint directly onto the window DC, but to a backbuffer that is then BitBlt to the window.

                  This means that when you create another DC on the same surface you're basically racing with Qt as to who gets to paint last. It might look like something is working, but, as with any race, it's just a coincidence on your particular setup (Windows version, graphics adapter and driver etc. etc.). Might work for some static painting, but the timing changes slightly on resize and you get flicker. On other machine it might flicker in other scenarios, all the time even.

                  Qt does not expose a window DC that you can draw to, so the only way to do it in sync is to use Qt's drawing facilities i.e. draw using QPainter in paintEvent. Since you have a foreign API for drawing you can create a bitmap to draw to and then wrap it in QImage interface and paint to the window using one of the QPainter's functions. Just make sure that the bitmap and QImage have compatible data formats.

                  YurikaY Offline
                  YurikaY Offline
                  Yurika
                  wrote on last edited by
                  #11

                  @Chris-Kawa Thank you a lot for telling me these. That's so enlightening!
                  I'm a little frustrated knowing these since I'm literally neither an expert on Qt nor programming :( But I hope I can get over these.

                  So in summary of your explanation, from my understanding, is there a way to disable Qt paint function and only apply the drawing function from the foreign API to paint the bitmap on QWidget's DC?

                  Or as you said, I can create a bitmap to draw to and then wrap it in QImage. I can probably understand the principle but can you provide me more information about how can I create a bitmap and also get its DC to draw my target image using L_PaintDC function? Thank you!

                  Thanks for helping me.

                  Chris KawaC 1 Reply Last reply
                  0
                  • YurikaY Yurika

                    @Chris-Kawa Thank you a lot for telling me these. That's so enlightening!
                    I'm a little frustrated knowing these since I'm literally neither an expert on Qt nor programming :( But I hope I can get over these.

                    So in summary of your explanation, from my understanding, is there a way to disable Qt paint function and only apply the drawing function from the foreign API to paint the bitmap on QWidget's DC?

                    Or as you said, I can create a bitmap to draw to and then wrap it in QImage. I can probably understand the principle but can you provide me more information about how can I create a bitmap and also get its DC to draw my target image using L_PaintDC function? Thank you!

                    Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on last edited by Chris Kawa
                    #12

                    @Yurika said:

                    is there a way to disable Qt paint function and only apply the drawing function from the foreign API to paint the bitmap on QWidget's DC

                    You can use some flags when creating your widget, like Qt::WA_NoSystemBackground, to disable redrawing of the widget background, but this is a bit finicky and you'll get in trouble again when mixing this with e.g. child widgets, that draw on top of your widget.

                    Using a bitmap buffer and drawing it in paint event is a bit more work (not that much), but assures it behaves correctly, as it's just native Qt painting in this case.

                    As to how to do it - first make some variables in your class:

                    HDC renderDC;
                    HBITMAP renderBitmap;
                    QImage renderImage;
                    

                    Then in your setup code (e.g. in constructor) create the bitmap, DC for it and the QImage wrapper:

                    // Create a DC for rendering
                    HDC displayDC = ::GetDC(NULL);
                    renderDC = ::CreateCompatibleDC(displayDC);
                    ::ReleaseDC(NULL, displayDC);
                    
                    // Create a bitmap buffer
                    BITMAPINFO bmi {};
                    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
                    bmi.bmiHeader.biWidth       = width;
                    bmi.bmiHeader.biHeight      = -height; // yup, negative for top-down order
                    bmi.bmiHeader.biPlanes      = 1;
                    bmi.bmiHeader.biBitCount    = 32;
                    bmi.bmiHeader.biCompression = BI_RGB;
                    uchar* data = nullptr;
                    renderBitmap = ::CreateDIBSection(renderDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&data), NULL, 0);
                    
                    // Create a Qt wrapper over the bitmap data
                    renderImage = QImage(data, width, height, QImage::Format_RGB32);
                    

                    Don't forget to clean it up somewhere (e.g. in the destructor):

                    ::DeleteObject(renderBitmap);
                    ::DeleteDC(renderDC);
                    

                    and then you can use it in the paintEvent like this:

                    HGDIOBJ prevObj = ::SelectObject(renderDC, renderBitmap);
                    // Draw to renderDC using your foreign api here
                    ::SelectObject(renderDC, prevObj);
                    
                    QPainter painter(this);
                    painter.drawImage(0,0, renderImage);
                    
                    YurikaY 1 Reply Last reply
                    3
                    • Chris KawaC Chris Kawa

                      @Yurika said:

                      is there a way to disable Qt paint function and only apply the drawing function from the foreign API to paint the bitmap on QWidget's DC

                      You can use some flags when creating your widget, like Qt::WA_NoSystemBackground, to disable redrawing of the widget background, but this is a bit finicky and you'll get in trouble again when mixing this with e.g. child widgets, that draw on top of your widget.

                      Using a bitmap buffer and drawing it in paint event is a bit more work (not that much), but assures it behaves correctly, as it's just native Qt painting in this case.

                      As to how to do it - first make some variables in your class:

                      HDC renderDC;
                      HBITMAP renderBitmap;
                      QImage renderImage;
                      

                      Then in your setup code (e.g. in constructor) create the bitmap, DC for it and the QImage wrapper:

                      // Create a DC for rendering
                      HDC displayDC = ::GetDC(NULL);
                      renderDC = ::CreateCompatibleDC(displayDC);
                      ::ReleaseDC(NULL, displayDC);
                      
                      // Create a bitmap buffer
                      BITMAPINFO bmi {};
                      bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
                      bmi.bmiHeader.biWidth       = width;
                      bmi.bmiHeader.biHeight      = -height; // yup, negative for top-down order
                      bmi.bmiHeader.biPlanes      = 1;
                      bmi.bmiHeader.biBitCount    = 32;
                      bmi.bmiHeader.biCompression = BI_RGB;
                      uchar* data = nullptr;
                      renderBitmap = ::CreateDIBSection(renderDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&data), NULL, 0);
                      
                      // Create a Qt wrapper over the bitmap data
                      renderImage = QImage(data, width, height, QImage::Format_RGB32);
                      

                      Don't forget to clean it up somewhere (e.g. in the destructor):

                      ::DeleteObject(renderBitmap);
                      ::DeleteDC(renderDC);
                      

                      and then you can use it in the paintEvent like this:

                      HGDIOBJ prevObj = ::SelectObject(renderDC, renderBitmap);
                      // Draw to renderDC using your foreign api here
                      ::SelectObject(renderDC, prevObj);
                      
                      QPainter painter(this);
                      painter.drawImage(0,0, renderImage);
                      
                      YurikaY Offline
                      YurikaY Offline
                      Yurika
                      wrote on last edited by
                      #13

                      @Chris-Kawa I just tried this and it does work! No flicker anymore and everything seems to be fine! Thank you so much!

                      Just want to understand the mechanics behind these, can you explain a little bit about why do I have to create the bitmap buffer? What if I don't do this step?

                      Thanks for helping me.

                      Chris KawaC 1 Reply Last reply
                      0
                      • YurikaY Yurika has marked this topic as solved on
                      • YurikaY Yurika

                        @Chris-Kawa I just tried this and it does work! No flicker anymore and everything seems to be fine! Thank you so much!

                        Just want to understand the mechanics behind these, can you explain a little bit about why do I have to create the bitmap buffer? What if I don't do this step?

                        Chris KawaC Offline
                        Chris KawaC Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on last edited by
                        #14

                        @Yurika Your foreign API draws to a DC and Qt doesn't expose the window DC that it uses to you. The code creates an intermediate buffer and then selects it in a DC that can draw to it. This basically lets your function draw to the buffer. Then a QImage wraps over the same buffer storage so that you can use Qt API (QPainter) to draw to the Qt's backbuffer.

                        So what happens is:

                        • Make a custom DC compatible with your display.
                        • Make a win32 DIB buffer.
                        • Make a QImage that shares the storage memory with the buffer (this means you can access the same buffer via win32 and Qt APIs).
                        • Select the buffer into the DC
                        • Draw to the DC (which effectively means to the buffer)
                        • Create a QPainter (Qt API that paints to the backbuffer)
                        • Draw the buffer as QImage to the Qt's backbuffer, which then will be blit to the window when appropriate (usually at monitor's V-Sync).

                        If your drawing function just paints a picture (HBITMAP) then you can skip the additional buffer and your L_PaintDC function and make the QImage wrapper directly over the source bitmap. The example I posted is just more generic and lets you do any type of painting to the DC.

                        YurikaY 1 Reply Last reply
                        3
                        • Chris KawaC Chris Kawa

                          @Yurika Your foreign API draws to a DC and Qt doesn't expose the window DC that it uses to you. The code creates an intermediate buffer and then selects it in a DC that can draw to it. This basically lets your function draw to the buffer. Then a QImage wraps over the same buffer storage so that you can use Qt API (QPainter) to draw to the Qt's backbuffer.

                          So what happens is:

                          • Make a custom DC compatible with your display.
                          • Make a win32 DIB buffer.
                          • Make a QImage that shares the storage memory with the buffer (this means you can access the same buffer via win32 and Qt APIs).
                          • Select the buffer into the DC
                          • Draw to the DC (which effectively means to the buffer)
                          • Create a QPainter (Qt API that paints to the backbuffer)
                          • Draw the buffer as QImage to the Qt's backbuffer, which then will be blit to the window when appropriate (usually at monitor's V-Sync).

                          If your drawing function just paints a picture (HBITMAP) then you can skip the additional buffer and your L_PaintDC function and make the QImage wrapper directly over the source bitmap. The example I posted is just more generic and lets you do any type of painting to the DC.

                          YurikaY Offline
                          YurikaY Offline
                          Yurika
                          wrote on last edited by
                          #15

                          @Chris-Kawa That makes sense. Thanks very much!

                          Thanks for helping me.

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved