Draw text over an existing bitmap

Hello everyone,

I’m trying to draw a text on existing bitmap, but when I use the Graphics Context’s DrawText method, the background is removed. But this happens only when I create background image from empty bitmap (using DrawText on Bitmap from loaded image works well). I think that the issue happens because I’m using MemoryDC to create monochromatic bitmap, but I’m quite new to wxPython, so I have no idea how to fix it.

I have created an example to show you what happens:

import wx

def GetEmptyBitmap(w, h, color=(0,0,0)):
“”"
Create monochromatic bitmap with desired background color.
Default is black
“”"
b = wx.EmptyBitmap(w, h)
dc = wx.MemoryDC(b)
dc.SetBrush(wx.Brush(color))
dc.DrawRectangle(0, 0, w, h)
return b

def drawTextOverBitmap(bitmap, text=’’, fontcolor=(255, 255, 255)):
“”"
Places text on the center of bitmap and returns modified bitmap.
Font color can be set as well (white default)
“”"
dc = wx.MemoryDC(bitmap)
gc = wx.GraphicsContext.Create(dc)
font = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
gc.SetFont(font, fontcolor)
w,h = dc.GetSize()
tw, th = dc.GetTextExtent(text)
gc.DrawText(text, (w - tw) / 2, (h - th) / 2)
return bitmap

app = wx.App()

bmp_from_img = bmp = wx.Image(location).Rescale(200, 100).ConvertToBitmap()
bmp_from_img = drawTextOverBitmap(bmp_from_img, “From Image”, (255,255,255))

bmp_from_empty = GetEmptyBitmap(200, 100, (255,0,0))
bmp_from_empty = drawTextOverBitmap(bmp_from_empty, “From Empty”, (255,255,255))

frame = wx.Frame(None)
st1 = wx.StaticBitmap(frame, -1, bmp_from_img, (0,0), (200,100))
st2 = wx.StaticBitmap(frame, -1, bmp_from_empty, (0, 100), (200, 100))
frame.Show()
app.MainLoop()

``

As I said, the StaticBitmap which uses the loaded image is displayed correctly, but the one created with EmptyBitmap has no background.

Do you have any ideas how to make it work?

Thank you

Milan Skála wrote:

I'm trying to draw a text on existing bitmap, but when I use the
Graphics Context's DrawText method, the background is removed. But
this happens only when I create background image from empty bitmap
(using DrawText on Bitmap from loaded image works well). I think that
the issue happens because I'm using MemoryDC to create monochromatic
bitmap, but I'm quite new to wxPython, so I have no idea how to fix it.

I can see what's happening, although my explanation is not entirely
satisfying.

First, you are not creating a monochromatic bitmap. The fact that you
can set the background color to "red" demonstrates that. If you check
the documentation, you'll see that there's a third parameter to
wx.Bitmap that specifies the depth. The default is
wx.BITMAP_SCREEN_DEPTH, which means the bitmap will be the same depth as
the desktop. In your case, it's a 32-bit format -- 24-bit color with alpha.

It appears to be the alpha value that's causing you trouble, although
I'm not sure why. When you draw your background rectangle, you are
getting a brush with 0 alpha, which means transparent. Then, the text
gets drawn in a color with alpha. That alpha value even gets applied in
the anti-aliasing at the edges, which is why the red background actually
shows through at the borders of your characters.

Now, by default, wx.Brush((255,0,0)) should create an opaque red brush,
so I'm not clear why that initial drawing is transparent. And even if I
specify opaque (255,0,0,255), it still comes out transparent. If I do
dc.GetPixel(10,10), the resulting color shows opaque: (255,0,0,255). I
still don't have an answer for this. In the early days of Windows, some
of the controls simulated transparency by establishing a standard that
the color of the upper left pixel was a background color, but even that
doesn't appear to be at work here.

The easy way to work around your problem is to specify a depth without
alpha when you create the bitmap:

    def GetEmptyBitmap(w, h, color=(0,0,0)):
        b = wx.Bitmap(w, h, 24)

···

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

I advice you to do

dc.SelectObject(wx.NullBitmap)

in order to properly commit your drawing.

This small example works well on my machine:

-- coding: utf-8 --

import wx

def main():
app = wx.App() # nothing works without it
bmp = wx.EmptyBitmap(300, 200)
dc = wx.MemoryDC(bmp)
dc.Clear() # erase everything with the default brush background
dc.DrawText(u’Hello world!’, 10, 10)
# Pay attention that any function in wxPython wants unicode, especially
# if you are not an English-speaking person.
dc.SelectObject(wx.NullBitmap)
bmp.SaveFile(‘test.bmp’, wx.BITMAP_TYPE_BMP)

if name==‘main’:
main()

``