TextPrinter Update

I'm in the process of enhancing TextPrinter to support alignment and word wrap etc. (using MeasureCharacterRanges). I noticed the following code in PerformLayout:

           while (8 * charPositions.Length > vertices.Length)
               vertices = new Vector2[vertices.Length << 1];

            while (6 * charPositions.Length > indices.Length)
               indices = new ushort[indices.Length << 1];

Wouldn't it be much more efficient to do this?
           int newLength = vertices.Length;
            while (8 * charPositions.Length > newLength)
               newLength = newLength << 1;
            if (newLength > vertices.Length)
               vertices = new Vector2[newLength];

            newLength = indices.Length;
            while (6 * charPositions.Length > newLength)
               newLength = newLength << 1;
            if (newLength > indices.Length)
               indices = new ushort[newLength];

(Ignore the fact that I'm using charPositions.Length instead of text.length for the time being.)

Edit: Attached updated source code.

AttachmentSize
Fonts.zip8.54 KB

Comments

Re: TextPrinter Update
posted by the Fiddler

Correct, there's no reason to prefer the first over the second.

Re: TextPrinter Update
posted by BlueMonkMN

Well, I think I've got it done. There's one detail bugging me and I don't know what the source of this problem is. When I measure the size of the text and try to draw a rectangle behind it, the rectangle seems offset by about one pixel to left and top and/or one pixel too small vertically and horizontally. I don't know if it's a problem with the measurement or the aligning of the text on the rectangle. Anyway, it seems to be working well for the most part. I haven't optimized it too much because 1) I don't know that I could optimize it with all the considerations in mind that you know about, and 2) This function shouldn't be getting called too frequently.

I have 4 updated source files here. What should I do with them?

Re: TextPrinter Update
posted by the Fiddler

You can attach them here (just edit the opening post). Or you can send them by email, whichever you prefer :-)

Regarding the offset, try adding 0.375f or 0.5f to the vertex coordinates (not texture) - this should make the vertices align with the pixel grid (by default they align to the center of the pixels).

Re: TextPrinter Update
posted by BlueMonkMN

OK, I attached it to the post. Adding 0.5f to the text's vertex coordinates just seemed to make it blurry (as I suspected would happen). And I know the rectangle is being drawn at the right size and almost in the right place because when I draw a rectangle size 3x3 at 0,0 it draws a 3x3-pixel rectangle at the top left corner of the screen. The problem is it draws it 1 pixel too high, so I only see the bottom 6 pixels of it. That might explain a bit of the vertical problem, but not the horizontal problem, which is 2 or 3 pixels at least. The text extends outside the right side of the rectangle by 2 or 3 pixels. Maybe if you try a similar test with the updated code you'll have an idea.

I checked again, and in most cases the text extends outside the bottom and right sides of the rectangle by exactly 2 pixels. The top of the rectangle has 4 pixels above the top pixel of text and the left side of the rectangle has 2 pixels left of the leftmost pixel of text. In one case, however, the text only extends 1 pixel out the bottom and right sides of the rectangle.

Re: TextPrinter Update
posted by BlueMonkMN

I suspect that this offset has something to do with the way the glyphs acquired. I didn't look at or change any of that code. I guess the glyphs maybe have some buffer around them? I don't entirely understand how the process of acquiring a glyph works at first glance.

Re: TextPrinter Update
posted by BlueMonkMN

I found time to look into this a bit more and indeed it is the process of generating and measuring the glyphs that caused the discrepancy. Those StringFormatFlags really make a big difference. I just had to make sure the flags matched among 1) The measuring of the glyphs, 2) the drawing of the glyphs into the texture and 3) the measuring during the layout of the total string. I'll work on this a bit more and post an update soon.

Re: TextPrinter Update
posted by BlueMonkMN

OK, everything is working perfectly now. With the right StringFormat flags the glyphs are now being cached into the texture properly so they are drawn with the correct position and size when drawing a string. And after I took your advice about offsetting my rectangle by half a pixel, everything aligns perfectly. I've attached the updated code to the original post. Can this code be included in the OpenTK release?

Re: TextPrinter Update
posted by the Fiddler

I'm testing the code and, as expected, text looks much clearer.

This will replace the current implementation, but there are a couple of minor issues that should be fixed first:

  1. Specifying StringFormat.GenericTypographic in TextureFont will cause glyph overhangs to become clipped. You can check this by printing any italic font of a sufficient size (e.g. Arial italic 24em).
  2. Is there a specific reason why you removed MeasureString? We still need a function of that kind, to help find the bounding box of a string.
  3. Please add doc-string to new public functions! (e.g. GetCharPositions)

Ok, the first one isn't actually minor. I'll try to reproduce the off-by 1 pixel issue - the packing was loosened by 1 pixel on 0.9.1 to fix some filtering artifacts, which might be the root cause of this.

Edit: There's definitely something fishy going on with text centering and measuring. Working on it...

Re: TextPrinter Update
posted by BlueMonkMN

After investigating these questions, I find, I think, there's a problem in MeasureString versus MeasureCharacterRanges that will require storing the offsets of the glyphs within their stored image buffers in order to properly position and measure the size of text. I'm in a rush at the moment, but will respond to the other questions when I can. (MeasureString does something MeasureCharacterRanges can't seem to do, and vice versa.)

Re: TextPrinter Update
posted by the Fiddler

I'm still investigating, but it seems you are right - we may have to store the origin for each glyph.

What difference did you find in MeasureString vs MeasureCharacterRanges?

Edit: I just found a problem in the current code: Glyph.Width (Glyph.cs) is calculated using default Graphics settings, while the actual positioning is performed with AntiAlias or AntiAliasGridFit (TextureFont.cs).

This code smells bad, I'll probably have to rewrite it to make it more sane.

Re: TextPrinter Update
posted by BlueMonkMN

OK, responses (in no particular order):

1) MeasureString seems to properly measure the entire width of a string while MeasureCharacterRanges will never seem to always get the entire width. I don't think I ever managed to get the two to agree in all cases even when using the same StringFormat objects. MeasureCharacterRanges seems designed for highlighting characters and doesn't care if bits of the characters hang out of the range, regardless of the StringFormat provided, if I recall my results. I guess MeasureCharacterRanges doesn't allow character regions to overlap, so if a character overlaps another character significantly, that overlap (or part of it) may be excluded from this character's boundaries, even though it can draw beyond those boundaries. And MeasureString is the only reliable way I've found to entirely measure a large italic lowercase "f" in Times New Roman. (And even that method cots off a column or two of pixels if you try to measure a 20-point italic lowercase "f" in MS Sans Serif, but that's not TrueType, so that might be an error by design).

2) I removed MeasureString because I thought GetCharacterRanges' functionality of returning the bounding rectangle in the layout parameter would suffice. After seeing how limited GetCharacterRanges is, I see that may not be the case. It's unfortunate that there doesn't seem to be a reliable way to actually measure the visible part of a glyph. I considered just taking the whole glyph rectangles and getting a union of them, but many glyphs have extra pixels around them, and depending on how much space the final measurement method leaves, using the glyph rectangles to measure a string might be less accurate then MeasureString.

3) GenericDefault leaves extra space around the characters, so that's why I thought I should use GenericTypographic. For a while it looked like I might have to use GenericDefault when measuring them and GenericTypographic when drawing them. But I don't remember how that worked out in the end. That might have eliminated the need to track offsets, but caused problems measuring the output size, probably. I was surprised when I found yesterday that there are apparently more differences between GenericDefault and GenericTypographic than any amount of property switching can "fix". I can't seem to find any way to change the properties of a clone of GenericTypographic to behave like GenericDefault, even if I change all the properties to match. Not that I need to convert one into the other. I just thought that a StringFormat object was entirely defined by its visible properties, and I guess it's not.

4) Depending on how drastically the new code needs to change to work correctly, it might make sense to postpone adding the Doc-Strings until they're settled down a bit.

5) I noticed the first character of each line appeared on the screen to be shifted by some fraction of a pixel because they appeared blurry (for a 20 point font). I wonder if this is related to AntiAlias setting. I wonder if MeasureCharacterRanges is offsetting characters by a fraction of a pixel due to a certain (visible or invisible) setting. This could be a problem if we get some glyphs being anti-aliased twice in the process of caching then drawing.

Re: TextPrinter Update
posted by objarni

All these things trouble OpenTK. Fonts are complicated!

Maybe consider moving general text functionality into TK.Util?

And have a robust builtin text font in the OpenTK.dll? Might even be a good-looking font, so as to improve the "OpenTK brand". The current situation with artefacts here and there does not do OpenTK any good..

And neither does it do games built on OpenTK any good (right now I'm thinking of going the bitmapped text way in Dogfight2008 because of these artefacts..).

Re: TextPrinter Update
posted by BlueMonkMN

General text functionality is already in the utilities library. It no longer exists in the OpenTK DLL itself, as I understand it. Also, the existing functionality (with MeasureCharacterRanges) is "robust" for simple fonts. It only starts behaving oddly when you start using more complicated fonts (large fonts with kerning and italics). So it might be better to have a single font solution rather than multiple solutions. I think these problems will get worked out and then we'll have the best of both worlds: good font support for both simple and complicated/fancy text. The idea of providing a framework for a bitmapped font is interesting, though, because a good free font design tool is hard to find and use, and a lot of folks might prefer to just draw their font as a bunch of images. But I wouldn't see that as a core OpenTK component either. I think it's more important to get the commonly accepted font formats / text output to work well first, assuming it's do-able.

Re: TextPrinter Update
posted by the Fiddler

Well, drawing text from a bitmap is trivial to implement. There's really nothing wrong with that, and for many games it could be the right approach. On the other hand, OpenTK is not meant just for games and the simple approach just doesn't cut it in the general case.

Once the kinks are worked out, I think that the current GDI+/OpenGL approach will be a good tradeoff between quality (truetype fonts, hinting), speed and implementation difficulty. For the vast majority of uses it should be enough - and the rest don't really need to be covered by the core library.

@BlueMonkMN: I'm still comparing and contrasting differences between MeasureCharacterRanges and MeasureString, trying to figure out exactly how StringFormat affects each. It's getting a bit annoying, but I think the result will be worth it in the end.

Re: TextPrinter Update
posted by Inertia

And have a robust builtin text font in the OpenTK.dll?
How would that look like though? With Serifs or without? We would probably end up with c&p 'Times New Roman' in a dozen possible sizes, and it won't be exactly the sizes people do want.
If Dogfight is locked to a single screen resolution, a simple bitmap solution might be your best choice since you can tweak it until it's just right. I'd recommend using TextPrinter tho and having some patience, there's still several construction sites at the core library and that obviously has higher priority than Fonts.
Please note that OpenTK is also open-source for the simple reason that people may contribute code, rather than making requests, wait and pray that it makes it into the next release.

Might even be a good-looking font, so as to improve the "OpenTK brand".
Don't take my (C), patent, or space-for-rent remarks too seriously, I'm just making fun of desperate capitalists and 'company warfare'. The .com in OpenTK.com should be understood as .community rather than commercial. I actually do prefer OpenTK.Net as it's more descriptive about the general idea.

Re: TextPrinter Update
posted by objarni

Look here, I'm all for open source and community collaboration. I'm also against software patents even though I work in the software industry. Still, that does not mean I'm against the idea of a brand. Do you think Linux is brandless?

OpenTK IS a brand, whether you like it or not. How much score do you want it to have?

Re: TextPrinter Update
posted by Inertia

Well if you look at it from that angle, OpenTK is made out of "OpenGL", "OpenAL", ".Net", "Mono" plus some convenience. I don't think it can exceed your personal perception of those brands, what OpenTK basically does is building a bridge between them to simplify cross-platform development using them.
I believe the projects built with OpenTK are more verbose what it is capable of (and thereby define the 'brand'), than any 'marketing deceptions' we could come up with.

Re: TextPrinter Update
posted by objarni

I'm just gonna be a little evil, pardon me.


Well if you look at it from that angle, Ubuntu is made out of "debian archiver", "Firefox browser", "Compiz", "grub bootloader" plus some convenience. I don't think it can exceed your personal perception of those brands, what Ubuntu basically does is building a bridge between them to simplify the use of the Linux operating system.

I hope you get my point.

Re: TextPrinter Update
posted by Inertia

I'm afraid I don't. Ubuntu, Suse, Red Hat & whatever are just Linux distributions to me, no preference for any of those. However if I had to chose one I'd pick Ubuntu since many people are talking positively about it, not because they have a pretty built-in font, shiny logo or whatever.

OpenTK moves away from branding as far as possible. It does not care if the box runs on "AMD" or "Intel" CPUs, "x86" or "x64", does not care what brand the graphics or sound card are - if it supports the necessary extensions - and doesn't care about the underlying operating system either.

I don't think it will help OpenTK in any way if we put 'a free Power-Ranger-Action figure in every box' ... err ... 'a free won't-suit-your-needs built-in font'. Our clientel are not 10 year old "will buy it when it's shiny" stereotypes, and like I said before the projects built ontop of OpenTK will speak a much stronger language about it.

The only "brand" that could be considered a direct competitor to OpenTK is the Tao Framework, but I actually do encourage people to look into it for functionality that is missing in OpenTK, because Tao has a wider scope of included libraries.

Re: TextPrinter Update
posted by objarni

I'm afraid I don't. Ubuntu, Suse, Red Hat & whatever are just Linux distributions to me, no preference for any of those. However if I had to chose one I'd pick Ubuntu since many people are talking positively about it, not because they have a pretty built-in font, shiny logo or whatever.

"Have you tried OpenTK?"
"Yeah, it's a great OpenGL API for C#. They say they have font rendering support, but actually it's quite buggy (artefacts etc.). You'll have to do your own anyway."

Compared to:

"Have you tried OpenTK?"
"Yeah, it's a great OpenGL API for C#. They've got a simple builtin font so you can get started coding games day one. They've also got general font rendering, but it's a bit buggy right now, hopefully it'll come around."

Re: TextPrinter Update
posted by Inertia

Probably more like:

"Have you tried OpenTK?"
"Yeah, it's a great OpenGL API for C#. They got some simple built-in font noone uses - I feel bad for the guy who wasted days of his life tweaking that thing - because their Font class allows chosing any .ttf font at any size you want.
There's also this awesome Fluid simulation demo in the projects section. It's quite fun to play around with that, you should take a look."

Seriously, why add a default font, but not a default Texture, default Soundfile, etc.? Those are artist products, they don't even give the slightest hint about the programmer's API.

Re: TextPrinter Update
posted by objarni

Inertia, the fonts of OpenTK right now are not up to par.

It's been a few months in that not-working-state now. Judging from the tons of discussions on the font topic, it is nothing that will be crystal clear anytime soon either. Fix them, and I'll stop whining.

Until the general font rendering are working, a small USABLE font would be a great tension reliever.

And, such a font could have positive effects on the OpenTK brand, instead of negative as is the case for the artefacts.

Artefacts just don't look good, and human beeings have good eyes! For good or worse.

my 2cc

Re: TextPrinter Update
posted by the Fiddler

Objarni, I think you are blowing the issue way out of proportion:

  1. The artifacts only affect specific intel chips/drivers.
  2. The lack of grid-fitting causes glyphs to appear blurred on Windows (not Linux), but this is not world shattering. Besides, a bitmap font such as the one you suggest would suffer from the very same issues.

If you really find these issues disturbing, implementing and debugging a simple bitmap font-renderer won't take more than a day. You can then package it into a dll so that other people can benefit (I'll gladly host the code if you wish).

Also, despite what it looks like, this issue isn't dead and buried - I'm actively testing native solutions like Uniscribe/Pango/Atsui (the ones mozilla/gecko is based on). I'm not saying these are suitable for OpenTK (imagine 2-3 KLOC for each one, not counting the p/invokes), I'm just indicating that fonts are a topic without easy solutions.

Re: TextPrinter Update
posted by BlueMonkMN

Are you still "comparing and contrasting differences between MeasureCharacterRanges and MeasureString, trying to figure out exactly how StringFormat affects each"? Are you waiting for more from me, or are you going to pick it up from here? I've been busy with other things lately, and am not sure I can get back into the swing of text output, but I don't want to hold things up -- I can look into it again if nobody else is getting anywhere with this.

Re: TextPrinter Update
posted by the Fiddler

I think I've found a solution - expect an update soon.

Re: TextPrinter Update
posted by BlueMonkMN

Are we talking days, weeks or months?

Re: TextPrinter Update
posted by the Fiddler

Ahem, this is already implemented. Take a look at this thread.

Re: TextPrinter Update
posted by BlueMonkMN

My understanding after reading the other thread is that this is still a work in progress. I'm not clear on whether the interface is finalized, though. If the interface is pretty much set (for measuring and outputting text) I could at least integrate that and then just wait for the implementation to work itself out. Are you still working on ideas that might change the interface? (Is it committed to source control?)