[wxPython-users] LCD vs CRT Monitors Issues (!)

Andrea Gavana andrea.gavana at gmail.com
Fri Nov 2 05:12:05 PDT 2007


Hi Robin and All,

On 11/2/07, Robin Dunn wrote:
> Andrea Gavana wrote:
>
> > Curiously, just replacing this line:
> >
> > fnt =3D wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
> >
> > with this line:
> >
> > fnt =3D wx.SystemSettings_GetFont(wx.SYS_ANSI_VAR_FONT)
> >
> > Fixed the issue. I have no idea why, but it works.
>
> Probably because the font used in that case on your system is either a
> bitmap font (instead of a vector format like TTF) or doesn't specify any
> hinting that the system will use for anti-aliased drawing.
>
> I expect that the only way to truly solve this for any font is to make
> sure that the text only gets drawn once.  If it needs to be drawn again
> for some reason, such as it is part of the update region in a paint
> event, then you either need to redraw everything 'behind' it too, or
> don't use dc.SetBackgroundMode(wx.TRANSPARENT).  To better see what is
> happening you should get a tool that lets you zoom in on the display and
> see the individual pixels magnified.  If you look at some text then if
> it's using anti-aliasing (or 'font smoothing' or 'ClearType') you'll see
> some pixels along the curves and corners that are not fully black, or
> that may even have some color.  If the same text gets redrawn at the
> same place then those aliased pixels will be blended with the ones that
> are already there and they will become a little bit darker, and a little
> more dark the next time it is drawn.

I found that the issue is *not* related to multiple drawing of the
text, but actually on a strange behavior of wx.Mask/wx.MemoryDC. Let
me explain.

To speed up the drawing, the first time I paint the menubar (and after
every resize event), I paint the menu label (with its accelerator) to
a bitmap using a wx.MemoryDC() and I save a reference to this bitmap
in the menu class. All the next paint event (generated by
EVT_ENTER_WINDOW, EVT_LEAVE_WINDOW and others) use this bitmap instead
of redrawing the text because it's much faster (redrawing the text
requires more calculations, background drawing, line drawing etc...).

Ok, the problem is, I paint the menu background using the following code:

bmp =3D wx.EmptyBitmap(rect.width, rect.height)
memDc =3D wx.MemoryDC()
memDc.SelectObject(bmp)

# Fill the bitmap with the maksing colour
memDc.SetPen(wx.Pen(wx.Colour(0, 128, 128)))
memDc.SetBrush(wx.Brush(wx.Colour(0, 128, 128)))
memDc.DrawRectangle(0, 0, rect.width, rect.height)

And then I do all the voodoo to correctly paint the menu label. In the
end, what I do to store the bitmap is:

bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
item.SetTextBitmap(bmp)

The problem is with wx.Mask: it doesn't mask correctly the
wx.Colour(0, 128, 128), or whatever other colour you choose for the
wx.Pen, wx.Brush and wx.Mask. I attach a simple example which
demonstrates the problem: you will notice that the text saved to a
bitmap (and then painted on the DC) is blurred. Obviously you will see
this strange behavior on LCD screens or on CRT screens with ClearType
on.

Does anyone know what I can do to obtain the bitmap with the correct
mask? Any other solution?

Thank you for your suggestions.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/
-------------- next part --------------
import wx

class MaskProblem(wx.Panel):

    def __init__(self, parent):

        wx.Panel.__init__(self, parent)
        self.SetBackgroundColour(wx.WHITE)

        self.CreateBitmap()

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
        self.Bind(wx.EVT_SIZE, self.OnSize)


    def GetText(self):

        return "&Edit", 1


    def CreateBitmap(self):

        rect =3D wx.Rect(0, 0, 100, 100)
        bmp =3D wx.EmptyBitmap(rect.width, rect.height)
        memDc =3D wx.MemoryDC()
        memDc.SelectObject(bmp)

        self.DrawText(memDc, rect, kind=3D1)
        memDc.SelectObject(wx.NullBitmap)

        # Set masking colour to the bitmap
        bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
        self.bitmap =3D bmp


    def DrawText(self, dc, rect, kind=3D0):

        labelOnly, location =3D self.GetText()
        fnt =3D wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
        fnt.SetPointSize(20)        =


        if kind:
            # Fill the bitmap with the maksing colour
            dc.SetPen(wx.Pen(wx.Colour(0, 128, 128)))
            dc.SetBrush(wx.Brush(wx.Colour(0, 128, 128)))
            dc.DrawRectangle(0, 0, rect.width, rect.height)

        dc.SetFont(fnt)
            =

        # underline the first '&'
        before =3D labelOnly[0:location]
        underlineLetter =3D labelOnly[location] =

        after =3D labelOnly[location+1:]

        ww, hh =3D dc.GetTextExtent(labelOnly)
        textOffset =3D (rect.width - ww) / 2
            =

        # before
        dc.DrawText(before, textOffset, 0)

        # underlineLetter
        if "__WXGTK__" not in wx.Platform:
            w1, h =3D dc.GetTextExtent(before)
            fnt.SetUnderlined(True)
            dc.SetFont(fnt)
            dc.DrawText(underlineLetter, textOffset + w1, 0)
        else:
            w1, h =3D dc.GetTextExtent(before)
            dc.DrawText(underlineLetter, textOffset + w1, 0)

            # Draw the underline ourselves since using the Underline in GTK=
, =

            # causes the line to be too close to the letter
            =

            uderlineLetterW, uderlineLetterH =3D dc.GetTextExtent(underline=
Letter)
            dc.DrawLine(rect.x + w1 + textOffset, rect.y + uderlineLetterH =
- 2,
                        rect.x + w1 + textOffset + uderlineLetterW, rect.y =
+ uderlineLetterH - 2)

        # after
        w2, h =3D dc.GetTextExtent(underlineLetter)
        fnt.SetUnderlined(False)
        dc.SetFont(fnt)

        dc.DrawText(after,  w1 + w2 + textOffset, 0)
        =


    def OnPaint(self, event):

        dc =3D wx.BufferedPaintDC(self)
        =

        dc.SetBrush(wx.WHITE_BRUSH)
        dc.Clear()

        fullRect =3D self.GetClientRect()
        dc.DrawRectangleRect(fullRect)

        dc.DrawBitmap(self.bitmap, 0, 40, True)
        rect =3D wx.Rect(0, 0, 100, 100)
        self.DrawText(dc, rect)

        fnt =3D wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
        dc.SetFont(fnt)
        dc.DrawText(" =3D=3D> Masked Bitmap", 200, 50)
        dc.DrawText(" =3D=3D> Correct Text",  200, 10) =

    =


    def OnErase(self, event):

        pass


    def OnSize(self, event):

        self.Refresh()
        event.Skip()


if __name__ =3D=3D "__main__":
    =

    app =3D wx.PySimpleApp()
    frame =3D wx.Frame(None, -1, "MemDC Troubles", size=3D(500, 400))
    panel =3D MaskProblem(frame)
    frame.CenterOnScreen()
    frame.Show()
    app.MainLoop()

    =

    =

               =20


More information about the wxpython-users mailing list