How to capture underlying bitmap of wxMemoryDC?

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
AdmosJagashvili
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Oct 29, 2023 10:07 pm

How to capture underlying bitmap of wxMemoryDC?

Post by AdmosJagashvili »

See attached code.

My assumption is: When you call any wxDC "draw" method, it updates the underlying wxWindow to store information, that a stack-based DC can use to pick up where it left off (since you are supposed to not store it), and I would assume that it also leaves bitmap information.

As the code is, you can draw on the square in the middle. When you hit "submit", as of right now, I have it so when you close out the dialog, it should tell you the width and height of the bitmap. It says 0 no matter what. So I did see in the documentation that you are supposed to set a bitmap, so instead of in the code:

Code: Select all

void MyPanel::OnPaint(wxPaintEvent&)
{
    wxAutoBufferedPaintDC dc(this);

    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();

    // draw lines the user created
    wxPen linePen(wxPenInfo().Colour(*wxWHITE).Width(3));
    wxDCPenChanger lineUserPenChanger(dc, linePen);

    for (const auto& line : lines)
    {
        dc.DrawLines(line.size(), &line[0]);
    }
}
I tried:

Code: Select all

void MyPanel::OnPaint(wxPaintEvent&)
{
    wxBitmap bm;
    wxAutoBufferedPaintDC dc(this);
    dc.SelectObjectAsSource(bm);

    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();

    // draw lines the user created
    wxPen linePen(wxPenInfo().Colour(*wxWHITE).Width(3));
    wxDCPenChanger lineUserPenChanger(dc, linePen);

    for (const auto& line : lines)
    {
        dc.DrawLines(line.size(), &line[0]);
    }
}
Notice how I also make the bitmap a stack variable. The example code here does not specify whether or not "test_bitmap" is local or persistent storage. Anyways, if you try running it with that replaced code it basically kills it. Is there some way for me to be able to actually grab the bitmap after drawing on it and hitting submit? I am not super familiar with bitmaps either so maybe the width/height is not the evidence I should be looking for that it is working.
Attachments
FontCreator.h
(1.43 KiB) Downloaded 34 times
FontCreator.cpp
(4.32 KiB) Downloaded 36 times
User avatar
doublemax
Moderator
Moderator
Posts: 19032
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to capture underlying bitmap of wxMemoryDC?

Post by doublemax »

AdmosJagashvili wrote: Sat Feb 10, 2024 11:25 pm My assumption is: When you call any wxDC "draw" method, it updates the underlying wxWindow to store information, that a stack-based DC can use to pick up where it left off (since you are supposed to not store it), and I would assume that it also leaves bitmap information.
This is a wrong assumption.

If you draw onto any non-buffered wxDC, the drawing is not buffered in any bitmap, and there is no way to access the data afterwards.

If you use a wxBuffered[Paint]DC, all drawing operations are diverted into a wxBitmap using a wxMemoryDC, and when it gets destroyed, its content is drawn into the "real" wxDC.

If you pass the same bitmap to wxBuffered[Paint]DC, the content is persistent between calls.

What exactly are you trying to achieve?
Use the source, Luke!
AdmosJagashvili
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Oct 29, 2023 10:07 pm

Re: How to capture underlying bitmap of wxMemoryDC?

Post by AdmosJagashvili »

I would like to have the user draw on that graphics context, and then when they hit "submit", I am able to grab what they drew as a bitmap.
User avatar
doublemax
Moderator
Moderator
Posts: 19032
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to capture underlying bitmap of wxMemoryDC?

Post by doublemax »

Assuming the most simple case: A fixed drawing area that fits on the screen (so that you don't need scrolling) and no option to zoom into the bitmap:
Have a wxBitmap with the required size.
Catch the mouse event, calculate the drawing position relative to the drawing area, use the current settings (color, pen size, etc) to draw into the bitmap using a wxMemoryDC.
In the paint event handler, you just draw this bitmap.
Use the source, Luke!
AdmosJagashvili
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Oct 29, 2023 10:07 pm

Re: How to capture underlying bitmap of wxMemoryDC?

Post by AdmosJagashvili »

Thanks, I'll give it a whirl.
AdmosJagashvili
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Oct 29, 2023 10:07 pm

Re: How to capture underlying bitmap of wxMemoryDC?

Post by AdmosJagashvili »

The documentation has not been super helpful about the specifics of how bitmaps work, the result doesn't have a black background or the white wxPen, what am I doing wrong here? So I drew into the bitmap in AddPoint(), which itself is triggered by several of the mouse events:

Code: Select all

void MyPanel::AddPoint(const wxPoint& point)
{
    wxMemoryDC dc(bm);

    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();

    // draw lines the user created
    wxPen linePen(wxPenInfo().Colour(*wxWHITE).Width(3));
    wxDCPenChanger lineUserPenChanger(dc, linePen);

    dc.DrawPoint(point);

    Refresh();
    Update();
}

And then when it's time to paint, I called DrawBitmap:

Code: Select all

    

   void MyPanel::OnPaint(wxPaintEvent&)
{
    wxMemoryDC dc(bm);
    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();
    wxPen linePen(wxPenInfo().Colour(*wxWHITE).Width(3));
    wxDCPenChanger lineUserPenChanger(dc, linePen);
    dc.DrawBitmap(dc.GetSelectedBitmap(), wxPoint(0, 0));
}
 
Note, "bm" is a class member wxBitmap object.
Attachments
FontCreator.h
(1.39 KiB) Downloaded 5 times
FontCreator.cpp
(4.42 KiB) Downloaded 27 times
User avatar
doublemax
Moderator
Moderator
Posts: 19032
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to capture underlying bitmap of wxMemoryDC?

Post by doublemax »

Code: Select all

    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();
If you want to capture what the user drew over a period of time, you shouldn't clear the bitmap here.

The paint event handler makes no sense at all, it should be something like this.

Code: Select all

void MyPanel::OnPaint(wxPaintEvent&)
{
  wxPaintDC dc(this);
  dc.DrawBitmap( bm );
}
Edit:

Code: Select all

    dc.DrawPoint(point);

    Refresh();
    Update();
Before calling Update() you must make sure that the bitmap is selected out of the wxMemoryDC. Either by calling wxMemoryDC::Select(wxNullBitmap) or by destroying the wxMemoryDC.
Use the source, Luke!
AdmosJagashvili
Earned a small fee
Earned a small fee
Posts: 12
Joined: Sun Oct 29, 2023 10:07 pm

Re: How to capture underlying bitmap of wxMemoryDC?

Post by AdmosJagashvili »

Thanks, it works well now! As a side note, I notice that changing the width of wxPen doesn't actually change the width of the pen like it did before, it seems to just follow my mouse, and if I drag quickly, it leaves a lot of empty space in between dots. Is there a forum post lingering around about that?
Attachments
FontCreator.h
(1.39 KiB) Downloaded 32 times
FontCreator.cpp
(3.99 KiB) Downloaded 32 times
User avatar
doublemax
Moderator
Moderator
Posts: 19032
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: How to capture underlying bitmap of wxMemoryDC?

Post by doublemax »

DrawPoint() does not use the width of the pen (as it says in the docs).

You can draw a line using the same point as start and end.

And if you want a continuous line, you'll have to remember the previous point coordinates and draw a line from that to the current one.
Use the source, Luke!
Post Reply