Fonts - Yet again
Posted Wednesday, 18 June, 2008 - 16:34 by JTalton in
Windows XP
I converted my application over from using TAO.SDL and the SDL TTF library for rendering fonts to using the OpenTK fonts.
The OpenTK fonts don't have the correct pixel precision and the look blurry. Also the bottom of the font is getting cut off.
Am I missing a setting? I did find that if I change the font size to 8.25 instead of 9.0 the bottom of the font does not get cut off.
SDL Version

OpenTK Version (Updated with fixes resulting from this thread)

| Attachment | Size |
|---|---|
| Program.cs | 3.13 KB |




Comments
Jun 19
19:34:58Re: Fonts - Yet again
posted by the FiddlerThe bluriness is a known issue (the glyphs are not snapped to the pixel grid), which will be corrected in the next version.
I'm looking into the font size issue. IIRC, glyph size is always rounded up, however I haven't really tested with fractional sizes.
Jun 22
16:44:00Re: Fonts - Yet again
posted by the FiddlerOk, I've finally commited the code that fixes the bluriness. Can you please checkout the latest source and see how things are looking now?
I'm still working on the size issue.
Edit: I haven't been able to reproduce the size issue with the latest code. Could you please test if it still occurs?
Jun 22
23:26:36Re: Fonts - Yet again
posted by JTaltonSweet.
I'll check it out as soon as I can and let you know.
BTW I just have to say once again, how great I think the OpenTK project is. Thanks for putting so much work and thought into it.
Jun 22
00:43:07Re: Fonts - Yet again
posted by JTaltonI currently get an error when building on my XP box. I'll give my other box a try later to see if it is an environment thing. - UPDATE: Other machine shows the same problem.
C:\opentk\Build>build net
Building OpenTK using .Net
[!] Match returned no files: .\OpenTK.Utilities.dll.config
Creating NAnt build files
...Creating project: Build
...Creating project: Examples
...Creating project: OpenTK
...Creating project: Bind
...Creating project: OpenTK.Utilities
Unhandled Exception: System.InvalidOperationException: No process is associated
with this object.
at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.get_ProcessName()
at OpenTK.Build.Project.ExecuteProcess(String path, String args)
at OpenTK.Build.Project.Main(String[] args)
Jun 23
05:13:19Re: Fonts - Yet again
posted by the FiddlerMake sure nant is in your path! Alternatively, type
build vsand use the visual studio solution to build OpenTK.I'm going to revamp the build system once nant 0.86 is released - the Build.exe script is a little icky.
Jun 23
06:16:36Re: Fonts - Yet again
posted by JTaltonLooks good. Screen shot posted.
I'll test out the measurement functions another night.
Thanks!
Jun 23
08:23:26Re: Fonts - Yet again
posted by the FiddlerIndeed, looks much better. Out of curiosity, is cleartype enabled on your PC?
Jun 23
03:27:19Re: Fonts - Yet again
posted by JTaltonI enabled cleartype and it did not seem to make a difference.
Jun 24
05:42:00Re: Fonts - Yet again
posted by the FiddlerThanks for testing. That's normal I think - cleartype is disabled for sizes smaller than 10em to improve readability (compare the first and third screenshots).
I'm working on a more comprehensive font test now, to help weed out any remaining gremlins from this code. Did you notice any pixels getting cut off?
Jun 24
14:18:13Re: Fonts - Yet again
posted by JTaltonThere are some pixels still being cut off at different font sizes. Screen shot attached.
Also the font measurements are still a little off.
Jun 24
16:07:47Re: Fonts - Yet again
posted by InertiaIt's a wild guess, but could it be that the string contains some kind of termination character/sequence that is measured too? It kinda looks like as if there was an extra char in the string, or maybe a rounding error.
Jun 24
16:11:00Re: Fonts - Yet again
posted by the FiddlerWell
MeasureCharacterRangesdoesn't measure \r\n\0 characters (these are positioned at (0,0)), so that's probably not it.My guess is that the measurement is done with
MeasureStringwhile the positioning withMeasureCharacterRanges- and these two GDI+ methods return different results.I'll write a quick test to see if that's the matter - if it is, I'll change the internal code to use
MeasureCharacterRangesfor everything.Edit: Indeed,
MeasureStringreturns different results thanMeasureCharacterRanges. I am working on a fix right now.Edit 2: Fix commited. I'm working on the cutoff issue right now.
Edit 3: All known issues should be fixed now. I still plan to make changes to this code, but it should work fine now.
Jun 26
01:10:13Re: Fonts - Yet again
posted by JTaltonAre the changes committed to the SVN repository? I grabbed the latest code and didn't see much difference with the MeasureString.
Jun 27
07:44:17Re: Fonts - Yet again
posted by the FiddlerMeasureString should be deprecated in the latest version (or did I forget to commit that part? :) ). Try using MeasureText instead. Besides accuracy, this function can also return individual character positions, so you can place a cursor for example.
I'm cleaning up font API for 0.9.2, so expect some minor changes before the release.
Jun 28
15:43:21Re: Fonts - Yet again
posted by JTaltonAttached a new screen shot of my font test using the new MeasureText. The measurements are closer but still are a little off.
Also I was wondering about the space at the beginning of the text? Where does that come from?
Note: When I draw the rectangle to indicate the measurements I do round up the floats to the closest whole number.
I'm in no rush for a fix, so take your time and work on what you need to. Thanks!
Jun 28
17:47:35Re: Fonts - Yet again
posted by the FiddlerOk, I've just updated SVN to use
StringFormat.GenericDefaultinstead ofGenericTypographic, which deals with the measurement issue.I'm still looking where the extra space to the left comes from. I suspect it could be a caused by rounding during glyph rasterization (since it's always 1 extra pixel).
Jun 28
02:29:52Re: Fonts - Yet again
posted by JTaltonI believe the GdiPlus.MeasureCharacterRanges call on the first character in the text is coming back with an offset and that is being used for the drawing thus causing the string to be moved to the right and down.
Since the rectangle returned from MeasureText has this offset set as it's location, if I adjust the position of the text before I draw it, it shows up in the right place.
Jun 29
11:00:00Re: Fonts - Yet again
posted by the FiddlerOk, I fixed this issue at last. You were right about the offset - the problem was caused by a mismatch between glyph rasterization and measurement. The first used a GenericDefault StringFormat, while the latter used a GenericTypographic.
Both measurement and rasterization now explicitly defines GenericTypographic, which completely resolves the measurement issues. My tests now show a tight fit for the bounding box, without cut or missing overhangs for letters. As long as the font is correctly designed, it will hopefully show up correctly.
Can you please confirm that the issue is resolved?
Jun 29
20:34:23Re: Fonts - Yet again
posted by JTaltonFirst font loads fine.
Second font gives a "Divide by zero exception" in LoadGlyph on the 4th character.
Out of time at the moment, I'll debug it tonight.
Jun 29
20:40:00Re: Fonts - Yet again
posted by the FiddlerOk, this is starting to get weird. Here is what I get after the latest updates (just checked and these *are* in SVN):

Edit: I think I'm replying to an earlier version of your post. :-) Divide by zero in LoadGlyph? The only division is by the texture size (width and height of the glyph cache), which shouldn't ever be zero - unless there's no OpenGL context (which shouldn't be the case occur, since the first font loads fine).
Any ideas how to reproduce this?
Jun 29
01:23:04Re: Fonts - Yet again
posted by JTaltonName = "Microsoft Sans Serif" Size=7.0
The size of the size character (32 ' ') is 0 width 0 height from MeasureText() and thus pack.Add().
The divide by zero exception comes from
GL.TexSubImage2D(TextureTarget.Texture2D, 0, rect.Left, rect.Top, rect.Width, rect.Height, OpenTK.Graphics.PixelFormat.Rgba, PixelType.UnsignedByte, (IntPtr)data_ptr);
since rect.Width and Rect.Height are 0.
Attaching my test program to the original post.
Edit - BTW Font size 10 works for me. Output and measurements look good!
Jun 29
01:19:14Re: Fonts - Yet again
posted by JTaltonThe fonts are hanging down below my rectangle in the test. This is because the returned rectangle from MeasureText has a Y location of 1.998 and I am ignoring the location when drawing the gray rectangle, but the text drawing function uses this location offset.
It may be that this is the desired functionality since the 1.99 = ~2 which is how much the font in question hangs down on letters that hang (i.e. 'g' 'j'). So it is drawing the font at the location I specified but that is the baseline and not the bottom of what is drawn. If that makes sense. In a way that is a nice feature, and I can work around it in my code since I want the actual bottom of rendering by using the location offset before rendering the text.
Jul 01
15:12:12System.DivideByZeroException
posted by JTaltonCompiled on second machine and I am still getting this exception:
System.DivideByZeroException
GL.Delegates.TexSubImage2D.Invoke(target, level,xoffset, yoffset, width, height, format, type, pixels)
GL.TexSubImage2D(target=Texture2D, level=0, xoffset=8, yoffset=18, width=0, height=0, format=Rgba, type=UnsignedByte, pixels=)
TextureFont.LoadGlyph(c=' ', rectangle={X=0 Y=0 Width=0 Height=0})
TextureFont.LoadGlyphs(glyphs="Main Window")
TextPrinter.PerformLayout(text="Main Window", font={TextureFont}, width=0, wordWarp=false, alignment=Near, rightToLeft=false, vertices={}, indices={}, num_indices=66)
TextPrinter.Prepare(text="Main Window", font={TextureFont}, handle=null, width=0, wordWarp=false, alignment=Near, rightToLeft=false)
TextPrinter.Prepare(text="Main Window", font={TextureFont}, handle=null)
You can see by the stack trace that the space character is trying to be loaded with a width and height of zero which causes TexSubImage2D() to throw the exception.
Jul 01
16:40:11Re: Fonts - Yet again
posted by the Fiddler[Hanging glyphs]
The fonts are hanging down below my rectangle in the test
I think this depends on the font, actually. Different fonts use different coordinate systems (e.g. (0,0) could be the left-most pixel of the baseline or the glyph "center") - you can verify this is the case by loading a few different fonts (e.g. Verdana and Times New Roman at 7.0em - the latter has parts of the font hanging outside the rectangle while the first doesn't).
A strange thing I just happened to observe, is that the bounding box returned by
TextureFont.MeasureTextcorrectly surrounds all overhangs - only the individual rectangles show this effect. I'm not really sure *why* this happens, as the rectangle is calculated simply from the first and last glyph sizes.Ah, I'll have to research this a little more, I guess.
[DivideByZero exception]
I must be getting crazy, because I can't reproduce that. I've tested 10 different fonts (including MS Sans Serif) at sizes 5-10em with the "Main Window" string, and the ' ' is correctly measured.
I'm now grapsing at straws: which version of .Net are you using / building OpenTK against? If it is 2.0, do you have SP1 installed?
Jul 01
17:54:51Re: Fonts - Yet again
posted by JTaltonWindows XP - SP3
I believe .NET 2.0
GdiPlus.GetRegionBounds(regions[j], new HandleRef(gfx, GdiPlus.GetNativeGraphics(gfx)), ref rect);
The rect after this call for the space character has a width and height of zero.
I have reproduced this on two separate machines using the latest SVN code and different test code in each case.
Jul 01
18:35:00Re: Fonts - Yet again
posted by the FiddlerInteresting. I'm still not able to reproduce the issue (Vista x64 SP1 / .Net 2.0 SP1, Windows XP SP3 / .Net 2.0 SP1, Windows 2000 / .Net 2.0). I haven't tested on Linux yet, but this problem looks rather curious. Can you test what the following returns on your machine?
Just before the failing call, place the following lines. Then put a breakpoint on the MeasureText call, and examine the output of "ranges". Does the space character still exhibit a 0 width/height?
StringFormat format = StringFormat.GenericTypographic;
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
List<RectangleF> ranges = new List<RectangleF>();
textureFont.MeasureText(str, SizeF.Empty, format, ranges);
If yes, can you test against the sample default .Net MeasureCharacterRanges implementation? Create a new WinForms project and replace class Form1 with the following:
{
static readonly string text = "Main Window";
static readonly Font font = new Font(new FontFamily("Microsoft Sans Serif"), 7.0f);
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
StringFormat format = StringFormat.GenericDefault;
CharacterRange[] ranges = new CharacterRange[text.Length];
for (int i = 0; i < ranges.Length; i++)
ranges[i] = new CharacterRange(i, 1);
format.SetMeasurableCharacterRanges(ranges);
Region[] regions = e.Graphics.MeasureCharacterRanges(text, font, new RectangleF(0, 0, 1000.0f, 1000.0f), format);
Rectangle measure_string_size = new Rectangle(
new Point((int)regions[0].GetBounds(e.Graphics).Left,
(int)regions[0].GetBounds(e.Graphics).Top),
new Size((int)Math.Ceiling(regions[regions.Length - 1].GetBounds(e.Graphics).Right),
(int)Math.Ceiling(regions[regions.Length - 1].GetBounds(e.Graphics).Bottom)));
e.Graphics.FillRectangle(Brushes.White, measure_string_size);
for (int i = 0; i < regions.Length; i++)
{
Region region = regions[i];
e.Graphics.DrawString(text[i].ToString(), font, Brushes.Black,
new Point((int)region.GetBounds(e.Graphics).Left,
(int)region.GetBounds(e.Graphics).Top));
region.Dispose();
}
//SizeF temp_size = e.Graphics.MeasureString(text, font);
//Size measure_string_size = new Size((int)Math.Ceiling(temp_size.Width), (int)Math.Ceiling(temp_size.Height));
//e.Graphics.FillRectangle(Brushes.White, new Rectangle(new Point(0, 0), measure_string_size));
//e.Graphics.DrawString(text, font, Brushes.Black, new PointF(0, 0));
}
}
Does this code correctly display a space character on your test machine?
I'm trying to find out what's going wrong here.
On the upside, I've found a problem with italics getting cut off, which I'm now investigating. I'm also working on a new glyph packing method that will allow multiple texture sheets with glyph eviction (LRU scheme with configurable memory consumption).
Jul 01
00:21:50Re: Fonts - Yet again
posted by JTaltonTried the same tests I have been doing on a 3rd XP SP3 machine. Works flawlessly. So something about the first two machines is off. I'll run the tests you outlined when I get a chance.
Jul 02
11:20:36Re: Fonts - Yet again
posted by BlueMonkMNOops, I missed out on this whole discussion. But it looks like you're stuck the same place I was: MeasureString returns a true bounding box for the text and all it's overhanging bits while MeasureCharacterRanges seems more intended to return boundaries for the main parts of the characters (for the purpose of highlighting text or something), ignoring some outlying bits, especially in the case of large italic lowercase "f" characters in Times New Roman. That's what happened when I tried to convert everything to use MeasureCharacterRanges too. But maybe you improved it somewhat by taking into account the offset of the glyph into the cell where it's drawn? I never got that far. I'll try getting and testing the new code soon and see if my results are better than before.
Jul 02
15:50:49Re: Fonts - Yet again
posted by objarniJTalton: sorry for off topic question -- but the images on the buttons in Golem are very nice indeed. What software/tools did you use to draw them?
Jul 02
21:11:10Re: Fonts - Yet again
posted by JTaltonInkscape Open source Linux/MacOS/Windows vector graphics program.
I highly recommend it. Exports the SVG vector icons as PNG with full transparency support.
I also try to somewhat follow the Tango icon design guidelines.
The 4 icons on the left of the bar are using the new guidelines. I still need to work the others up.