Problem with wx.Bitmap.CopyToBuffer on macOS High Sierra

Hello everybody,

I got a bug using wx.Bitmap.CopyToBuffer on macOS High Sierra after modified the bitmap with dc functions using wx.MemoryDC().

I got the message :

/Users/robind/projects/buildbots/macosx-vm6/dist-osx-py27/Phoenix/ext/wxWidgets/src/osx/core/bitmap.cpp(431): assert “m_rawAccessCount == 0” failed in BeginRawAccess().

I got the problem only if I modify the bitmap using dc.

To show the bug, I wrote a simple program modifying the demo for BitmapFromBuffer.py.

the two variables at the top drawRect = True copyToBuffer = True are explicit and the bug arise only with the combinaison True, True and on macOS. On Windows, it works fine.

#!/usr/bin/env python

drawRect = True

copyToBuffer = True

import wx

import array

class TestPanel(wx.Panel):

def __init__(self, parent, log):

    self.log = log

    wx.Panel.__init__(self, parent, -1)

    self.Bind(wx.EVT_PAINT, self.OnPaint)

    self.width, self.height = 120,120

    self.MakeBitmapRGB(self.width, self.height)

def GetRGB(self, x, y, bpp):

    # calculate some colour values for this sample based on x,y position

    r = g = b = 0

    if y < self.height/3:                           r = 255

    if y >= self.height/3 and y <= 2*self.height/3: g = 255

    if y > 2*self.height/3:                         b = 255

    if bpp == 4:

        a = int(x * 255.0 / self.width)

        return r, g, b, a

    else:

        return r, g, b

def MakeBitmapRGB(self, width, height):

    # Make a bitmap using an array of RGB bytes

    bpp = 3  # bytes per pixel

    bytes = array.array('B', [0] * width*height*bpp)

    for y in range(height):

        for x in range(width):

            offset = y*width*bpp + x*bpp

            r,g,b = self.GetRGB(x, y, bpp)

            bytes[offset + 0] = r

            bytes[offset + 1] = g

            bytes[offset + 2] = b

    self.rgbBmp = wx.Bitmap().FromBuffer(width, height, bytes)

    

    if drawRect:

        mDC = wx.MemoryDC()

        mDC.SelectObject(self.rgbBmp)

        colour = wx.Colour(174, 174, 174)

        pen = wx.Pen(colour, 1, wx.SHORT_DASH)

        mDC.SetPen(pen)

        mDC.SetBrush(wx.Brush("grey", style=wx.TRANSPARENT))

        mDC.DrawRectangle(30, 40, 50, 60)

    if copyToBuffer:

        bytes2 = array.array('B', [0] * width*height*bpp)

        self.rgbBmp.CopyToBuffer(bytes2, wx.BitmapBufferFormat_RGB)

        self.rgbBmp = wx.Bitmap().FromBuffer(width, height, bytes2)

def DrawBitmapAndMessage(self, dc, bmp, msg, x_, y_):

    x, y = x_, y_

    # draw some text to help show the alpha

    dc.SetFont(self.GetFont())

    while y < y_ + self.height + 2*dc.GetCharHeight():

        dc.DrawText(msg, x,y)

        y += dc.GetCharHeight() + 5

    # draw the bitmap over the text

    dc.DrawBitmap(bmp, x+15,y_+15, True)

def OnPaint(self, evt):

    dc = wx.PaintDC(self)

    self.DrawBitmapAndMessage(dc, self.rgbBmp,  "No alpha channel in this image", 30,35)

def runTest(frame, nb, log):

win = TestPanel(nb, log)

return win

if name == ‘main’:

import sys,os

import run

run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

Thanks very match for any clue.

Pierre-Alain Moret

The error message, as it happens, is exactly correct. When the drawRect block completes, the bitmap is still selected into the DC. The MacOS implementation does not allow raw access to the bitmap bits while the bitmap is selected into a DC. Just add this to the end of the first block and you should achieve happiness:

mDC.SelectObject( wx.NullBitmap )

By the way. your “wx.Bitmap().FromBuffer( … )” causes an error for me. The correct call should be “wx.BitmapFromBuffer”.

···

On Mar 13, 2018, at 2:19 PM, Pierre-Alain Moret moret.pierre.alain@gmail.com wrote:

I got a bug using wx.Bitmap.CopyToBuffer on macOS High Sierra after modified the bitmap with dc functions using wx.MemoryDC().

,

    if drawRect:
        mDC = wx.MemoryDC()
        mDC.SelectObject(self.rgbBmp)
        colour = wx.Colour(174, 174, 174)
        pen = wx.Pen(colour, 1, wx.SHORT_DASH)
        mDC.SetPen(pen)
        mDC.SetBrush(wx.Brush("grey", style=wx.TRANSPARENT))
        mDC.DrawRectangle(30, 40, 50, 60)
    if copyToBuffer:
        bytes2 = array.array('B', [0] * width*height*bpp)
        self.rgbBmp.CopyToBuffer(bytes2, wx.BitmapBufferFormat_RGB)
        self.rgbBmp = wx.Bitmap().FromBuffer(width, height, bytes2)


Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Thank you very match, for this very clear explanation. Everything is completely clear for me now and you are right, it works well just by adding mDC.SelectObject( wx.NullBitmap ).
Concerning “wx.Bitmap().FromBuffer( … )” or “wx.BitmapFromBuffer”, the former was in the demo by Robin Dunn. I found an explanation in the following link:

https://wxpython.org/Phoenix/docs/html/wx.Bitmap.html#wx.Bitmap.FromBuffer

Pierre-Alain Moret

···

Le mercredi 14 mars 2018 06:31:43 UTC+1, Tim Roberts a écrit :

On Mar 13, 2018, at 2:19 PM, Pierre-Alain Moret moret.pie...@gmail.com wrote:

I got a bug using wx.Bitmap.CopyToBuffer on macOS High Sierra after modified the bitmap with dc functions using wx.MemoryDC().

,

    if drawRect:
        mDC = wx.MemoryDC()
        mDC.SelectObject(self.rgbBmp)
        colour = wx.Colour(174, 174, 174)
        pen = wx.Pen(colour, 1, wx.SHORT_DASH)
        mDC.SetPen(pen)
        mDC.SetBrush(wx.Brush("grey", style=wx.TRANSPARENT))
        mDC.DrawRectangle(30, 40, 50, 60)
    if copyToBuffer:
        bytes2 = array.array('B', [0] * width*height*bpp)
        self.rgbBmp.CopyToBuffer(bytes2, wx.BitmapBufferFormat_RGB)
        self.rgbBmp = wx.Bitmap().FromBuffer(width, height, bytes2)

The error message, as it happens, is exactly correct. When the drawRect block completes, the bitmap is still selected into the DC. The MacOS implementation does not allow raw access to the bitmap bits while the bitmap is selected into a DC. Just add this to the end of the first block and you should achieve happiness:

mDC.SelectObject( wx.NullBitmap )

By the way. your “wx.Bitmap().FromBuffer( … )” causes an error for me. The correct call should be “wx.BitmapFromBuffer”.


Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

It’s a static method so an instance is not needed to call it. Instead you can do it like this:

bmp = wx.Bitmap.FromBuffer(...)
···

On Wednesday, March 14, 2018 at 1:36:46 PM UTC-7, Pierre-Alain Moret wrote:

Thank you very match, for this very clear explanation. Everything is completely clear for me now and you are right, it works well just by adding mDC.SelectObject( wx.NullBitmap ).
Concerning “wx.Bitmap().FromBuffer( … )” or “wx.BitmapFromBuffer”, the former was in the demo by Robin Dunn. I found an explanation in the following link:

https://wxpython.org/Phoenix/docs/html/wx.Bitmap.html#wx.Bitmap.FromBuffer

Robin