Ugly fonts

I can now run all standard VPython demos with VPython based on wxPython. There are no doubt some minor bugs to iron out, but this is a major milestone. The only platform-dependent code remaining is tiny functions for GetProcAddress, which perhaps is a candidate for providing in wxPython.

I’m very grateful to the generous and knowledgeable help the wxPython community has given me on this. Thank you!

I’m disturbed by the very ugly fonts provided (sans-serif wx.FONTFAMILY_SWISS, serif wx.FONTFAMILY_ROMAN, and monospace wx.FONTFAMILY_MODERN). I attach an image comparing text displayed at the top in the older VPython (which uses Verdana, requiring a lot of platform-dependent code) and text displayed in the new VPython using wx.FONTFAMILY_SWISS. Similarly ugly fonts are seen with the other two wxPython fonts.

The ugly fonts in the image look like they are bold, but choosing bold weight (wx.FONTWEIGHT_BOLD) increases the width of the strokes even more.

Might there be some pen or other setting I’ve missed that results in the font being so unattractive?

fonts.jpg

We've been discussing a similar issue over on wxPython-dev the past couple days. It looks to me like what happens if you draw the same text at the same location several times without clearing the window first. The anti-aliasing of later draws is blending with the same pixels from earlier draws, resulting in a "muddy" look.

We haven't tracked down the cause in the other case yet, so anything you can do to help would be appreciated. What version of wxPython are you using? What is the type of the widget where you see the problem? Can you make a small runnable sample that demonstrates the problem?

···

On 12/21/12 9:28 AM, Bruce Sherwood wrote:

I can now run all standard VPython demos with VPython based on wxPython.
There are no doubt some minor bugs to iron out, but this is a major
milestone. The only platform-dependent code remaining is tiny functions
for GetProcAddress, which perhaps is a candidate for providing in wxPython.

I'm very grateful to the generous and knowledgeable help the wxPython
community has given me on this. Thank you!

I'm disturbed by the very ugly fonts provided (sans-serif
wx.FONTFAMILY_SWISS, serif wx.FONTFAMILY_ROMAN, and monospace
wx.FONTFAMILY_MODERN). I attach an image comparing text displayed at the
top in the older VPython (which uses Verdana, requiring a lot of
platform-dependent code) and text displayed in the new VPython
using wx.FONTFAMILY_SWISS. Similarly ugly fonts are seen with the other
two wxPython fonts.

The ugly fonts in the image look like they are bold, but choosing bold
weight (wx.FONTWEIGHT_BOLD) increases the width of the strokes even more.

Might there be some pen or other setting I've missed that results in the
font being so unattractive?

--
Robin Dunn
Software Craftsman

I’m using 2.9.4 on Windows. I can try to make a minimal program, but I can’t do it right now because I’ll be gone for the rest of the day. The following function is the heart of the matter, but there is some post-processing in C++ to convert to RGBA, with the opacity for all background pixels set to zero and for all other pixels set to 255.

def text_to_bitmap(text, color=(1,1,1), background=(0,0,0), opacity=1,

height=13, font=‘sans’,

style=‘normal’, weight=‘normal’):

Algorithm provided by Chris Barker in the wxPython forum

global _dc

if font == ‘sans’:

wfont = _wx.FONTFAMILY_SWISS

fudge = 12.0/13.0 # fudge factor for backwards compatibility

elif font == ‘serif’:

wfont = _wx.FONTFAMILY_ROMAN

fudge = 10.0/13.0 # fudge factor for backwards compatibility

elif font == ‘monospace’:

wfont = _wx.FONTFAMILY_MODERN

fudge = 10.0/13.0 # fudge factor for backwards compatibility

else:

raise ValueError(“font should be ‘serif’, ‘sans’, or ‘monospace’”)

if style == ‘normal’:

wstyle = _wx.FONTSTYLE_NORMAL

elif style == ‘italic’:

wstyle = _wx.FONTSTYLE_ITALIC

else:

raise ValueError(“font style should be ‘normal’ or ‘italic’”)

if weight == ‘normal’:

wweight = _wx.FONTWEIGHT_NORMAL

elif weight == ‘bold’:

wweight = _wx.FONTWEIGHT_BOLD

else:

raise ValueError(“font weight should be ‘normal’ or ‘bold’”)

if _dc == None:

_dc = _wx.MemoryDC()

height = int(fudge*height + 0.5)

_dc.SetFont(_wx.Font(height, wfont, wstyle, wweight))

while text[0] == ‘\n’: text = text[1:]

while text[-1] == ‘\n’: text = text[:-1]

lines = text.split(’\n’)

maxwidth = 0

totalheight = 0

heights = []

for line in lines:

if line == ‘’: line = ’ ’

w,h = _dc.GetTextExtent(line)

h += 1

w += 2

if w > maxwidth: maxwidth = w

heights.append(totalheight)

totalheight += h

bmp = _wx.EmptyBitmap(maxwidth,totalheight)

_dc.SelectObject(bmp)

fore = (int(255*color[0]),

int(255*color[1]),

int(255*color[2]))

_dc.SetTextForeground(fore)

Make background only slightly different from foreground

so that antialiasing doesn’t create pixels with colors

very different from the foreground color:

back = [0,0,0]

for i in range(3):

if fore[i] == 0: back[i]=1

else: back[i] = fore[i]-1

brush = _wx.Brush(back)

_dc.SetBackground(brush)

_dc.Clear()

for n, line in enumerate(lines):

_dc.DrawText(line, 1, heights[n])

_dc.SelectObject( _wx.NullBitmap )

img = _wx.ImageFromBitmap(bmp)

data = fromstring(img.GetData(), dtype=uint8)

return maxwidth, totalheight, back, data

Bruce,

(note: try not to use JPEG for posting screen shots liek this -- it
adds its own fuzziness on top of what you are trying to show -- PNG is
a better option)

But anyway:

I'm guessing this may be similar to what Robin is suggesting, but for
a totally different reason:

There is some post-processing in
C++ to convert to RGBA, with the opacity for all background pixels set to
zero and for all other pixels set to 255.

If I follow the comments in your code, you've drawn black text on
almost black background, so that the "fractional" pixels from the
anti-aliasing will be almost black as well. Then you convert to RGBA,
making everything that isn't the background color opaque. This is
going to result on fuzzy, wider letters, as the "fractional" pixels
end up almost black, rather than what they should be given a certain
background.

This is a bit tricky to fix. Ideally, wx would be able to draw to an
RGBA buffer, using alpha for the anti-aliasing, but I suspect it can't
do that (might be worth a try with a GC, though). So you have a few
choices (If I'm right about the cause):

1) use non-anti-aliased fonts -- not sure what the options are here with wx.

2) draw big and scale down -- at least it will make the "fuzz" smaller

3) add the alpha channel in "right":

   a) draw your text black on white
   b) when you add the alpha channel make the alpha value scale with
the "blackness" of the pixel
   c) turn all non-white pixels to full black.
   d) if you want the text some other color, change the color at the last step

This would be painfully slow in pure python, but in C, C++, Cython or
numpy, should be quite doable, and it sounds like you have some C++
code in the loop there anyway.

-Chris

···

On Fri, Dec 21, 2012 at 10:09 AM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris, you’re absolutely right. I had chosen a background color very similar to the foreground color for DrawText in an attempt to solve some tricky antialiasing problems. Going back to an earlier scheme (abandoned at some point for the wrong reason) of using the user-specified background color leads to attractive fonts. I apologize for bothering everyone, and I’m REALLY impressed that you could diagnose the problem just from reading the code.

I would have benefited from being able to do the DrawText with RGBA rather than RGB. I did try the ignorant experiment of trying to set SetTextForeground and SetBackground with 4-component colors but wxPython didn’t accept this.

Robin, I have not seen the extra pixels you mention. I have VPython demos that make lots of text displays, in which DrawText produces a bitmap which I apply as a texture to a quad. This is all done in C++ after getting the bitmap from wxPython code, so my situation may be very different from those you’ve seen.

Bruce

P.S. I’ll try to remember to post png in the future, though I’m puzzled by this issue. When I click “View” for my posted image, it looks crisp.

Chris, you're absolutely right. I had chosen a background color very similar
to the foreground color for DrawText in an attempt to solve some tricky
antialiasing problems. Going back to an earlier scheme (abandoned at some
point for the wrong reason) of using the user-specified background color
leads to attractive fonts.

yup -- that should work well -- do you always know what background
color the user may want? If not then you could use my trick to get
text anti-aliased with the alpha channel.

I apologize for bothering everyone,

why? it was an interesting question.

and I'm
REALLY impressed that you could diagnose the problem just from reading the
code.

To be fair -- it was really Robin's suggestion that this was similar
to other issue's he's seen, put together with your description of how
you created the alpha layer.

Anti-aliasing and alpha-blending are weird things to mix -- when you
draw an anti-aliased font (or line, or...) you need to decide what a,
for example, 50% pixel should be. It could be 50% foreground, 50%
background, or it could be the foreground color with 50% transparency.
But for the most part, stuff is drawn on top of a particular color, so
the blending is done then.

I've had similar issues with icons drawn over a transparent background
-- they often don't look right around the edges if put on top of
anything that isn't close to white.

I would have benefited from being able to do the DrawText with RGBA rather
than RGB.

yes, though, likely it wouldn't have worked right anyway, due to the
issue above.

Robin, I have not seen the extra pixels you mention. I have VPython demos
that make lots of text displays, in which DrawText produces a bitmap which I
apply as a texture to a quad. This is all done in C++ after getting the
bitmap from wxPython code, so my situation may be very different from those
you've seen.

yup -- I think the issue's Robin has seen are if the same text is
drawn multiple times without clearing, the partial pixels get darker
each time, making the whole font look bigger and darker.

-Chris

P.S. I'll try to remember to post png in the future, though I'm puzzled by
this issue. When I click "View" for my posted image, it looks crisp.

It was just a bit fuzzed out, in a typical jpeg way -- it's a pet
peeve of mind -- using the better format for the image at hand.

···

On Fri, Dec 21, 2012 at 7:09 PM, Bruce Sherwood <bruce.sherwood@gmail.com> wrote:

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov