I am extending wx.BitmapButton to include some custom drawing on the
bitmap using cairo and I stumbled across what appears to be a bug in
the function ImageSurfaceFromBitmap within the wx.lib.wxcairo module.
I believe that when creating a surface from a bitmap (within
ImageSurfaceFromBitmap) the resulting surface needs to get a
mark_dirty() call before returning.
The documentation comments:
"mark_dirty() tells cairo that drawing has been done to Surface using
means other than cairo, and that cairo should reread any cached areas.
Note that you must call flush() before doing such drawing."
In this case I do not think flush() is needed since the surface is
just created - but the mark_dirty() is still needed.
See the code below demonstrating:
Download smiley.png from:
http://upload.wikimedia.org/wikipedia/en/6/6f/Smiley_Face.png
smiley_check.png should write back out correctly (oddly this works
since it must use the cache) and match smiley.png
smiley_fail.png will have the red nose only
smiley_succeed.png will have the face with the red nose
--------------------------------------------------------------------------
import cairo
import wx
from math import pi
FIX = False
# Extracted from wx.lib.wxcairo module
def ImageSurfaceFromBitmap(bitmap):
"""
Create an ImageSurface from a wx.Bitmap
"""
width, height = bitmap.GetSize()
if bitmap.HasAlpha():
format = cairo.FORMAT_ARGB32
fmt = wx.BitmapBufferFormat_ARGB32
else:
format = cairo.FORMAT_RGB24
fmt = wx.BitmapBufferFormat_RGB32
try:
stride = cairo.ImageSurface.format_stride_for_width(format,
width)
except AttributeError:
stride = width * 4
surface = cairo.ImageSurface(format, width, height)
bitmap.CopyToBuffer(surface.get_data(), fmt, stride)
if FIX:
surface.mark_dirty() # needed addition
return surface
def draw_red_nose(surface):
# copy surface into a new_surface
new_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 200, 200)
cr = cairo.Context(new_surface)
cr.set_source_surface(surface, 10, 10)
cr.paint()
# now draw a red nose
cr.set_source_rgb(255, 0, 0)
cr.arc(100, 100, 10, 0, 2 * pi)
cr.fill()
return new_surface
app = wx.PySimpleApp()
bitmap = wx.Bitmap('smiley.png')
surface = ImageSurfaceFromBitmap(bitmap)
surface.write_to_png('smiley_check.png') # this works
new_surface = draw_red_nose(surface)
new_surface.write_to_png('smiley_fail.png') # but this fails - no
smiley
# implement fix
FIX = True
surface = ImageSurfaceFromBitmap(bitmap)
new_surface = draw_red_nose(surface)
new_surface.write_to_png('smiley_succeed.png') # this works with fix