residue problem on drawing canvas

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

residue problem on drawing canvas

Post by dualband »

I created a test program attached here to show the problem I encountered. I am hoping someone can help me out.
This is on Windows 10 with wxWidgets 3.1.4
onpaintdebug.cpp
(5.55 KiB) Downloaded 72 times
In the test program, when I move the mouse cursor within the scope of rectangle, two lines will be drawn with XOR logic. When I move the mouse to new location, previous two lines will be redrawn and erased, and the new two lines at the new mouse location will be drawn. If the key "z" or "x" was pressed, the rectangle will be redrawn with smaller/bigger size. The screen shot below shows the normal operation. And, the redbox shows the sequence of the flow, which is normal
normal.png
normal.png (56.76 KiB) Viewed 1331 times
However, if you press 'x' or 'z' while you are moving the mouse, sometimes it will have two lines stick on the canvas and will not be erased. I noticed it is because there is a mouse movement happened before the canvas got repainted, like the pinkbox in the below screenshot
ng.png
ng.png (57.45 KiB) Viewed 1331 times
Is this because there is still one event in the mouse event queue and it got processed unexpectedly right after I cleared the flag and before repainting canvas? Is there any way to drain all the events from the queue before I clear the flag and start to draw?
catalin
Moderator
Moderator
Posts: 1618
Joined: Wed Nov 12, 2008 7:23 am
Location: Romania

Re: residue problem on drawing canvas

Post by catalin »

When moving the mouse, draw the XOR-ed lines in the same way you draw the rectangle -- on the memory bitmap, or directly in the OnPaint() handler..
Then just call Refresh().
Painting on the screen should always be done on wxPaintDC, unless you have very particular needs for other DC, and you really know what you are doing (if you don't [have | know], just draw on the former).
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: residue problem on drawing canvas

Post by dualband »

When moving the mouse, draw the XOR-ed lines in the same way you draw the rectangle -- on the memory bitmap, or directly in the OnPaint() handler..
Then just call Refresh().
Thanks for the suggestion.
But, I would argue, when moving the mouse, drawing XOR-ed lines in the memory bitmap then refresh it will sacrifice the performance a lot. I did try this suggestion and it resolved the issue I previous saw. But, the new problem is, it was flickering when I was moving the mouse.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: residue problem on drawing canvas

Post by doublemax »

But, I would argue, when moving the mouse, drawing XOR-ed lines in the memory bitmap then refresh it will sacrifice the performance a lot.
You wouldn't do that with this approach. In each redraw you would draw the background and then the lines on top.
But, the new problem is, it was flickering when I was moving the mouse.
That would be solved by using a wx[Auto]BufferedPaintDC instead of wxPaintDC.

Additional possible optimizations:
Call SetBackgroundStyle(wxBG_STYLE_PAINT) for MyCanvas.
Use wxBufferedPaintDC with a static bitmap that is only re-created when the size of the window changes.
Use the source, Luke!
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: residue problem on drawing canvas

Post by dualband »

You wouldn't do that with this approach. In each redraw you would draw the background and then the lines on top.
Can you elaborate more? Does "draw the background" mean dc.Clear() ? Not quite understand what I need to change in the codes. Sorry, if you have time, please you can take a look at my codes attached below.
onpaintdebug.cpp
(5.78 KiB) Downloaded 62 times
On the other hand, with your suggestion using wxBufferedPaintDC, the flickering issue was gone. The response time for mouse movement is okay with the small window. But if I maximize the window, I observe the response time for mouse movement is very long.
Not sure if I misunderstood any of your suggestions. But I still think redraw the whole thing every time when mouse moves is not a good idea. The performance will suffer with this way.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: residue problem on drawing canvas

Post by doublemax »

Can you elaborate more? Does "draw the background" mean dc.Clear() ?
I guess in a "real" application you don't just have a white background, there will be "something", e.g. the content of a wxBitmap. Them you draw your selection cross/rectangle on top.
On the other hand, with your suggestion using wxBufferedPaintDC, the flickering issue was gone. The response time for mouse movement is okay with the small window. But if I maximize the window, I observe the response time for mouse movement is very long.
Did you use a static bitmap for wxBufferedPaintDC? If not, a new background bitmap will be created for each redraw.
But I still think redraw the whole thing every time when mouse moves is not a good idea. The performance will suffer with this way.
Yes. but usually modern hardware should be fast enough for something trivial as this.

Can you explain what this part of your code will do in the "real" application?

BTW: You could also check the use of "wxDCOverlay" in the "drawing" sample. Unfortunately this class is not well documented, the sample is the best source of information.
Use the source, Luke!
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: residue problem on drawing canvas

Post by dualband »

Did you use a static bitmap for wxBufferedPaintDC? If not, a new background bitmap will be created for each redraw
Yes, the *m_bitmap is a member of myCanvas and it got created when myCanvas got instantiated. The bimap exists until the canvas got destroyed.
Yes. but usually modern hardware should be fast enough for something trivial as this.
If it only redraws once a while, yes, it is trivial. But when you move mouse in a distance of hundred pixels, the hundred times of redraw cannot keep up and results in a long response time.

This test program was created just to show the problem of my real application. The rectangle represents the data points in my real application. In the application, the canvas is a chart that draw multiple set of time series data. When mouse moves, the "+" follows the cursor and it shows the corresponding time and values of multiple set of data. When a key 'z' or 'x' got pressed, the drawings in the canvas got scaled and redraw. There are as many canvas can be created as needed by the users. Each of canvas all draw different set of time series data. If this test program shows the long latency on responding to mouse movement, it will even much worse in my real application.
User avatar
doublemax
Moderator
Moderator
Posts: 19158
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: residue problem on drawing canvas

Post by doublemax »

A while ago i wrote some code for grabbing a rectangle from the screen. It takes a screenshot and stores it in a bitmap, opens a borderless window that covers all displays and in the paint event handler, it displays the stored bitmap and draws a selection rectangle on top. With multiple displays connected, this area can become quite big. And there is still no noticeable drag when moving the mouse. Drawing a bitmap is a very fast operation. This could be even optimized more by redrawing only the "dirty" areas.

I post the code so you can try it out.
screen_selection_dialog.cpp
(4.29 KiB) Downloaded 69 times
screen_selection_dialog.h
(873 Bytes) Downloaded 61 times

Code sample for using above class:

Code: Select all

void MyFrame::OnStartCapture(wxCommandEvent& WXUNUSED(event))
{
  wxBitmap captured;

  {
    ScreenSelectionDialog dlg(this);
    if( dlg.ShowModal() == wxID_OK )
    {
      // if you just want the selection rectangle and process the area yourself
      wxRect r = dlg.GetSelectionRect();

      captured = dlg.GetCapturedArea();
    }
  }  

  if( captured.IsOk() )
  {
    wxFileDialog fsel(this, "save bitmap", "", "capture.png", "PNG files (*.png)|*.png", wxFD_SAVE|wxFD_OVERWRITE_PROMPT );
    if( fsel.ShowModal() == wxID_OK )
    {
      ::wxInitAllImageHandlers();
      captured.SaveFile( fsel.GetPath(), wxBITMAP_TYPE_PNG );
    }
  }
}
Use the source, Luke!
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: residue problem on drawing canvas

Post by dualband »

Thank you doublemax!
Yes, you are right. Your app is running very smoothly and I have found out the problem in my app.
It is the logging message for every mouse movement dragging the speed.
Thanks again for your help!
Post Reply