Error drawing to MemoryDC

72 views
Skip to first unread message

Chris Weisiger

unread,
Aug 12, 2012, 9:39:22 PM8/12/12
to wx-u...@googlegroups.com
I'm working on a roguelike game project which is currently using wx's
paint DCs to draw the game. I'm aware that wx's builtin painting
system isn't normally ideal for games -- OpenGL or SDL is preferable
-- but using WX directly is advantageous in keeping the code and its
dependencies as simple as possible (important for getting other
developers on board with this project!), and it should be possible to
be fast too. That in mind, I want to draw to bitmaps for normal game
rendering, and then blit the bitmaps to the game window when the paint
handler is called. This lets me only update the parts of the bitmap
that have changed. Currently, I have to instead redraw the entire view
every time, which takes ~.1s for an 80x24 grid of tiles. That's just
slow enough to be irritating.

However, I'm running into an odd error when I attach the bitmap to the
MemoryDC. This in turn causes drawing operations performed with the
MemoryDC to fail. I made a test app demonstrating the problem:
http://pastebin.com/S2cr8zag

Auria on IRC reports that this test app works fine in wxGTK, which
leads me to suspect that there's a problem in the Cocoa port (I'm
running the latest download off of the wxpython.org site). Also, in
the test app, if I create the EmptyBitmap at 640x480 instead of
creating it at 1x1 and then resizing it, then it works. However, my
code has to be able to handle the screen being resized, so I need the
ability to resize the bitmaps I draw to as well.

Any suggestions or workarounds?

-Chris

Eran Ifrah

unread,
Aug 13, 2012, 2:32:12 AM8/13/12
to wx-u...@googlegroups.com
In C++, usually after I finish working with the wxMemoryDC I call:

memDc.SelectObject(wxNullBitmap);

From my experience, this line is a must for applying the changes to the target bitmap.

Next, I simply draw the bitmap:

dc.DrawBitmap(..);

It should be fast as using Blit()

-Chris

--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+u...@googlegroups.com
or visit http://groups.google.com/group/wx-users



--
Eran Ifrah
Author of the cross platform, open source C++ IDE: http://www.codelite.org

Chris Weisiger

unread,
Aug 13, 2012, 10:05:03 AM8/13/12
to wx-u...@googlegroups.com
On Sun, Aug 12, 2012 at 11:32 PM, Eran Ifrah <eran....@gmail.com> wrote:
> In C++, usually after I finish working with the wxMemoryDC I call:
>
> memDc.SelectObject(wxNullBitmap);
>
> From my experience, this line is a must for applying the changes to the
> target bitmap.
>
> Next, I simply draw the bitmap:
>
> dc.DrawBitmap(..);
>
> It should be fast as using Blit()

While that's good to know, I'm getting the error when I create the
MemoryDC (or select the Bitmap into it), not at the end of painting.
And *some* operations are still successfully performed on the Bitmap
-- specifically, the background of the 'a' gets painted, but the 'a'
itself does not. So I don't think there's a problem with the "draw the
bitmap to the PaintDC" portion of the code in my sample app.

-Chris

Chris Weisiger

unread,
Aug 13, 2012, 11:43:17 AM8/13/12
to wx-u...@googlegroups.com
On Mon, Aug 13, 2012 at 7:05 AM, Chris Weisiger <cwei...@msg.ucsf.edu> wrote:
> On Sun, Aug 12, 2012 at 11:32 PM, Eran Ifrah <eran....@gmail.com> wrote:
>> In C++, usually after I finish working with the wxMemoryDC I call:
>>
>> memDc.SelectObject(wxNullBitmap);
>>
>> From my experience, this line is a must for applying the changes to the
>> target bitmap.
>>
>> Next, I simply draw the bitmap:
>>
>> dc.DrawBitmap(..);
>>
>> It should be fast as using Blit()
>
> While that's good to know, I'm getting the error when I create the
> MemoryDC (or select the Bitmap into it), not at the end of painting.
> And *some* operations are still successfully performed on the Bitmap
> -- specifically, the background of the 'a' gets painted, but the 'a'
> itself does not. So I don't think there's a problem with the "draw the
> bitmap to the PaintDC" portion of the code in my sample app.

Just a followup -- I've managed to get a segmentation fault by
slightly modifying the script. Modified test script:
http://pastebin.com/XrUngZE9

Run it, then start increasing the size of the window. The segfault
seems to occur randomly at some point after the window exceeds the
original *height* of the bitmap. Oddly, I can increase its *width*
arbitrarily without having any trouble.

-Chris

Eric Jensen

unread,
Aug 13, 2012, 12:01:37 PM8/13/12
to Chris Weisiger
Hello Chris,

Monday, August 13, 2012, 5:43:17 PM, you wrote:

CW> Just a followup -- I've managed to get a segmentation fault by
CW> slightly modifying the script. Modified test script:
CW> http://pastebin.com/XrUngZE9

CW> Run it, then start increasing the size of the window. The segfault
CW> seems to occur randomly at some point after the window exceeds the
CW> original *height* of the bitmap. Oddly, I can increase its *width*
CW> arbitrarily without having any trouble.

First of all, if you use wx[Auto]Buffered[Paint]DC you don't have to
draw into a wxBitmap first and then blit it into the dc, that's
exactly what the buffered DCs do internally.

The actual crash is probably cause by this line:
self.bitmap.SetSize((width, height))

If this directly related to the underlying C++ method, this will *not*
actually change the size of the bitmap (quite frankly, i don't know
what's the purpose of this method at all ;) ).

If the size of the bitmap is different from the size of the DC, you
have to recreate the bitmap.

HTH
Eric


Chris Weisiger

unread,
Aug 13, 2012, 12:09:25 PM8/13/12
to wx-u...@googlegroups.com
On Mon, Aug 13, 2012 at 9:01 AM, Eric Jensen <m...@j-dev.de> wrote:
> Hello Chris,
>
> Monday, August 13, 2012, 5:43:17 PM, you wrote:
>
> CW> Just a followup -- I've managed to get a segmentation fault by
> CW> slightly modifying the script. Modified test script:
> CW> http://pastebin.com/XrUngZE9
>
> CW> Run it, then start increasing the size of the window. The segfault
> CW> seems to occur randomly at some point after the window exceeds the
> CW> original *height* of the bitmap. Oddly, I can increase its *width*
> CW> arbitrarily without having any trouble.
>
> First of all, if you use wx[Auto]Buffered[Paint]DC you don't have to
> draw into a wxBitmap first and then blit it into the dc, that's
> exactly what the buffered DCs do internally.

I'm aware of that; the test app is not exactly replicating what I'm
intending to do in the actual use case. I need the bitmaps so I can
preserve drawn pixels across paint calls -- I'm trying to avoid having
to redraw the entire view every time, because that takes excessive
amounts of time.

>
> The actual crash is probably cause by this line:
> self.bitmap.SetSize((width, height))
>
> If this directly related to the underlying C++ method, this will *not*
> actually change the size of the bitmap (quite frankly, i don't know
> what's the purpose of this method at all ;) ).
>
> If the size of the bitmap is different from the size of the DC, you
> have to recreate the bitmap.

Tsk. I assumed that SetSize would reallocate memory as needed.

Recreating the Bitmap every time the window changes appears to avoid
the error I originally reported, anyway. So I guess that's the
"workaround" for this problem. Thanks for the help.

>
> HTH
> Eric

Robin Dunn

unread,
Aug 13, 2012, 1:24:39 PM8/13/12
to wx-u...@googlegroups.com
On 8/13/12 9:09 AM, Chris Weisiger wrote:
> On Mon, Aug 13, 2012 at 9:01 AM, Eric Jensen <m...@j-dev.de> wrote:

>>
>> The actual crash is probably cause by this line:
>> self.bitmap.SetSize((width, height))
>>
>> If this directly related to the underlying C++ method, this will *not*
>> actually change the size of the bitmap (quite frankly, i don't know
>> what's the purpose of this method at all ;) ).
>>
>> If the size of the bitmap is different from the size of the DC, you
>> have to recreate the bitmap.
>
> Tsk. I assumed that SetSize would reallocate memory as needed.
>
> Recreating the Bitmap every time the window changes appears to avoid
> the error I originally reported, anyway. So I guess that's the
> "workaround" for this problem. Thanks for the help.

The next step would probably be to only recreate the bitmap when the
size of the window changes. Then that bitmap can be used as your buffer
instead of creating a new one in each paint event, and your paint event
handler can then be reduced to a single line. There are some samples in
the wxPython demo and wiki that use this technique.

--
Robin Dunn
Software Craftsman
http://wxPython.org

Chris Weisiger

unread,
Aug 13, 2012, 1:34:56 PM8/13/12
to wx-u...@googlegroups.com
On Mon, Aug 13, 2012 at 10:24 AM, Robin Dunn <ro...@alldunn.com> wrote:
> On 8/13/12 9:09 AM, Chris Weisiger wrote:
>>
>> On Mon, Aug 13, 2012 at 9:01 AM, Eric Jensen <m...@j-dev.de> wrote:
>
>
>>>
>>> The actual crash is probably cause by this line:
>>> self.bitmap.SetSize((width, height))
>>>
>>> If this directly related to the underlying C++ method, this will *not*
>>> actually change the size of the bitmap (quite frankly, i don't know
>>> what's the purpose of this method at all ;) ).
>>>
>>> If the size of the bitmap is different from the size of the DC, you
>>> have to recreate the bitmap.
>>
>>
>> Tsk. I assumed that SetSize would reallocate memory as needed.
>>
>> Recreating the Bitmap every time the window changes appears to avoid
>> the error I originally reported, anyway. So I guess that's the
>> "workaround" for this problem. Thanks for the help.
>
>
> The next step would probably be to only recreate the bitmap when the size of
> the window changes. Then that bitmap can be used as your buffer instead of
> creating a new one in each paint event, and your paint event handler can
> then be reduced to a single line.

Exactly. Oh, the paint handler won't be quite a single line, since I
still need to update the portions of the bitmap that have been dirtied
by changes in game state (and I might as well do this in the paint
handler), but the important thing is that I won't be performing
thousands of DrawRectangle() and DrawText() calls every refresh.

It would be nice, by the way, if trying to increase a bitmap's area
with SetSize would throw an exception. Or if that function were
removed altogether. Or at the very least if there were a warning in
the documentation.

-Chris

Robin Dunn

unread,
Aug 13, 2012, 2:03:02 PM8/13/12
to wx-u...@googlegroups.com
On 8/13/12 10:34 AM, Chris Weisiger wrote:
> On Mon, Aug 13, 2012 at 10:24 AM, Robin Dunn <ro...@alldunn.com> wrote:
>> On 8/13/12 9:09 AM, Chris Weisiger wrote:
>>>
>>> On Mon, Aug 13, 2012 at 9:01 AM, Eric Jensen <m...@j-dev.de> wrote:
>>
>>
>>>>
>>>> The actual crash is probably cause by this line:
>>>> self.bitmap.SetSize((width, height))
>>>>
>>>> If this directly related to the underlying C++ method, this will *not*
>>>> actually change the size of the bitmap (quite frankly, i don't know
>>>> what's the purpose of this method at all ;) ).
>>>>
>>>> If the size of the bitmap is different from the size of the DC, you
>>>> have to recreate the bitmap.
>>>
>>>
>>> Tsk. I assumed that SetSize would reallocate memory as needed.
>>>
>>> Recreating the Bitmap every time the window changes appears to avoid
>>> the error I originally reported, anyway. So I guess that's the
>>> "workaround" for this problem. Thanks for the help.
>>
>>
>> The next step would probably be to only recreate the bitmap when the size of
>> the window changes. Then that bitmap can be used as your buffer instead of
>> creating a new one in each paint event, and your paint event handler can
>> then be reduced to a single line.
>
> Exactly. Oh, the paint handler won't be quite a single line, since I
> still need to update the portions of the bitmap that have been dirtied
> by changes in game state (and I might as well do this in the paint
> handler), but the important thing is that I won't be performing
> thousands of DrawRectangle() and DrawText() calls every refresh.

I don't know about your case, but in some cases at least it makes sense
to update the buffer bitmap at the point where the program state
changes, such as when a bitmap representing a game object moves from
position A to position B, and then do a RefreshRect to get a paint event
scheduled for the dirtied area. Then the paint event handler can be just:

def OnPaint(self, event):
dc = wx.BufferedPaintDC(self, self.buffer)


because no additional drawing needs to be done. The buffered dc object
is created, and then immediately is deleted because the end of the
function is reached, and at that time the buffered dc will blit the
buffer bitmap to the paint dc.


>
> It would be nice, by the way, if trying to increase a bitmap's area
> with SetSize would throw an exception. Or if that function were
> removed altogether. Or at the very least if there were a warning in
> the documentation.

$ pydoc wx.Bitmap.SetSize
Help on method SetSize in wx.Bitmap:

wx.Bitmap.SetSize = SetSize(*args, **kwargs) unbound wx._gdi.Bitmap method
SetSize(self, Size size)

Set the bitmap size (does not affect the existing bitmap data).

Chris Weisiger

unread,
Aug 13, 2012, 2:30:43 PM8/13/12
to wx-u...@googlegroups.com
I interpreted that to mean "any drawing you have done to the Bitmap
will be preserved". It really should say "does not reallocate memory"
IMO, and possibly even warn against trying to increase the size beyond
the maximum size of the bitmap. I realize this is pretty minor, but at
the same time if it saves one other person from going down this road
then the time investment will be paid back. :)

-Chris
Reply all
Reply to author
Forward
0 new messages