james_lohr's picture

OpenTK Utilities TextPrinter : AccessViolationException was unhandled

I'm getting an unhandled exception in GdiPlusGlyphRasterizer.cs on line 342:

status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length,
native_font, ref layoutRect, native_string_format, num_characters, regions);

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Any ideas?

[edit]

The problem only occurs when loading an external font, which I do as follows:

PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddFontFile("Data/Boneca_De_Panoo.ttf");
FontFamily[] fontFamilies = pfc.Families;
 
my_font = new Font(fontFamilies[1], 18, FontStyle.Bold);

The odd thing is that it'll print a string to the screen fine in the externally loaded font, however, as soon as I change the string being printed, the exception is raised.

Has anyone else managed to get externally loaded fonts to work consistently?


Comments

Comment viewing options

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

I have been using AddMemoryFont to load external fonts and print large amounts of text without issue. Does the same issue occur with other fonts (check with Gentium for example, which is available for free). Is it specific to the string being printer, or does it occur regardless of the string?

james_lohr's picture

I've downloaded and tried Gentium, and I get the same exception.

The issue is very strange; I can print whatever I like with the externally loaded font as long as I don't add anything to what I printed from the very first frame. I can't imagine that makes much sense, so what I've done is to strip everything down until I had the bare minimum needed to recreate the exception.

I've attached the code, along with the font. I've tried about 6+ different fonts all of which give the same problem, but just to be sure, I've also attached the exact font I'm using here.

In fact the code is short enough to just post here, so here it is:

using System;
using System.Drawing;
using System.Drawing.Text;
 
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
 
 
namespace FontException
{
    public partial class Game : GameWindow
    {
        bool showText = false;
        TextPrinter printer = new TextPrinter(TextQuality.Medium);
        Font test_font;
 
        public Game()
            : base(640, 480, GraphicsMode.Default, "Font exception")
        {
            VSync = VSyncMode.On;
        }
 
 
        public override void OnLoad(EventArgs e)
        {
            //load external font
            PrivateFontCollection pfc = new PrivateFontCollection();  
            pfc.AddFontFile("GenBasR.ttf");
            test_font = new Font(pfc.Families[0], 40);
 
            GL.ClearColor(System.Drawing.Color.SteelBlue);
            GL.Disable(EnableCap.DepthTest);
            GL.Viewport(0, 0, 640, 480);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            Glu.Ortho2D(0, 640, 480, 0);
        }
 
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            if (Keyboard[Key.Escape])
                Exit();
 
 
            for (int n = 0; n < Enum.GetValues(typeof(Key)).Length; n++)
            {
            }
        }
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            printer.Begin();
 
            printer.Print("Testing", test_font, Color.LightSteelBlue);
            if (showText)
            {
                printer.Print("More Text", test_font, Color.LightSteelBlue);
            }
            printer.End();
            showText = true;
 
            SwapBuffers();
        }
 
        static void Main()
        {
            using (Game game = new Game())
            {
                game.Run(30.0, 0.0);
            }
        }
 
 
    }
}

Note that without the enumeration :

            for (int n = 0; n < Enum.GetValues(typeof(Key)).Length; n++)
            {
            }

The exception does not occur; however, I'm pretty sure it'll crop up again as a result of any other memory operations. Let me know if this code gives you the same exception. Otherwise I'll probably have to assume that it's not compatible with my windows dlls. - The exception appears to be originating from using the gdiplus.dll.

On a side note: why does OpenTK even need to use gdiplus.dll? - is there not sufficient information to construct glyphs and text fragments from the .ttf file alone?

AttachmentSize
GenBasR.zip100.49 KB
Game.cs1.87 KB
the Fiddler's picture

Thanks for the test case, I'll try to reproduce the issue.

The TextPrinter is using GDI and GDI+ for glyph rendering and text formatting, respectively. This allows text to be rendered with kerning, hinting and subpixel anti-aliasing, features which are between "non-trivial" and "impossible" to implement from scratch.

the Fiddler's picture

Ok, the error is that your PrivateFontCollection is going out of scope and is being collected by the GC. Declare it as a field and the code works as expected:

         PrivateFontCollection pfc = new PrivateFontCollection();  
 
        public override void OnLoad(EventArgs e)
        {
            //load external font
            pfc.AddFontFile("GenBasR.ttf");
            [...]
james_lohr's picture

Quote: The TextPrinter is using GDI and GDI+ for glyph rendering and text formatting, respectively. This allows text to be rendered with kerning, hinting and subpixel anti-aliasing, features which are between "non-trivial" and "impossible" to implement from scratch.

That makes sense, thanks for explaining.

Quote:. Ok, the error is that your PrivateFontCollection is going out of scope and is being collected by the GC. Declare it as a field and the code works as expected:

Firstly, apologies for wasting your time on a problem that was unrelated to OpenTK and of my own making. To clarify this issue, would I have come across a similar problem even if I'd just been using the externally loaded fonts in a plain windows form (without any reference to OpenTK)?

Either way, I've learnt a valuable lesson as this is the first time I've really been caught out by the garbage collector in years of C# and Java programming, so I'm very thankful that you spotted it for me. I'll certainly be less complacent with the GC in future.

Many thanks

the Fiddler's picture
james_lohr wrote:

Quote:. Ok, the error is that your PrivateFontCollection is going out of scope and is being collected by the GC. Declare it as a field and the code works as expected:

Firstly, apologies for wasting your time on a problem that was unrelated to OpenTK and of my own making. To clarify this issue, would I have come across a similar problem even if I'd just been using the externally loaded fonts in a plain windows form (without any reference to OpenTK)?

Either way, I've learnt a valuable lesson as this is the first time I've really been caught out by the garbage collector in years of C# and Java programming, so I'm very thankful that you spotted it for me. I'll certainly be less complacent with the GC in future.

Many thanks

It's definitely easier to spot bugs with two pair of eyes, especially smaller issues like this. :)

As far as OpenTK is concerned, you need to keep a reference to the PrivateFontCollection for as long as you wish to use it. Now, WinForms interact somewhat strangely with PFCs (many controls ignore them completely unless you play some tricks first), so I can't really tell what would happen if the instance went out of scope. However, I'd hesitate to rely on such behavior even if it seemed to work: behavior can and does change between runtimes and operating systems.