BlueMonkMN's picture

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

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

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

BlueMonkMN's picture

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?

the Fiddler's picture

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).

BlueMonkMN's picture

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.

BlueMonkMN's picture

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.

BlueMonkMN's picture

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.

BlueMonkMN's picture

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?

the Fiddler's picture

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...

BlueMonkMN's picture

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.)

the Fiddler's picture

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.