chandragon's picture

How to render text on the screen

Hi
I feel a bit of shame asking such a basic thing, but how do I render text on the screen ?
I searched for classes that could do that and found on internet that many people use a "TextPrinter" instance, but I don't find that class in OpenTK.Graphics.
I read in a post of this forum that it would be in "OpenTK.Compatibility", but I don't have it, has it been removed from the newer versions of OpenTK ? is there any substitute for the TextPrinter ?
Thanks for the answers !


Comments

Comment viewing options

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

Hello,

Here is a modified version of Chandragon's code, with the projection working. Hope this can help ppl landing on that thread.

Just create and instance of MyTextWriter and call Update/AddText whenever the text change. Call Draw in the drawing loop.

class MyTextWriter
        {
            private readonly Font TextFont = new Font(FontFamily.GenericSansSerif, 8);
            private readonly Bitmap TextBitmap;
            private List<PointF> _positions;
            private List<string> _lines;
            private List<Brush> _colours;
            private int _textureId;
            private Size _clientSize;
 
            public void Update(int ind, string newText)
            {
                if (ind < _lines.Count)
                {
                    _lines[ind] = newText;
                    UpdateText();
                }
            }
 
 
            public MyTextWriter(Size ClientSize, Size areaSize)
            {
                _positions = new List<PointF>();
                _lines = new List<string>();
                _colours = new List<Brush>();
 
                TextBitmap = new Bitmap(areaSize.Width, areaSize.Height);
                this._clientSize = ClientSize;
                _textureId = CreateTexture();
            }
 
            private int CreateTexture()
            {
                int textureId;
                GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (float)TextureEnvMode.Replace);//Important, or wrong color on some computers
                Bitmap bitmap = TextBitmap;
                GL.GenTextures(1, out textureId);
                GL.BindTexture(TextureTarget.Texture2D, textureId);
 
                BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
                //    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
                //GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Nearest);
                GL.Finish();
                bitmap.UnlockBits(data);
                return textureId;
            }
 
            public void Dispose()
            {
                if (_textureId > 0)
                    GL.DeleteTexture(_textureId);
            }
 
            public void Clear()
            {
                _lines.Clear();
                _positions.Clear();
                _colours.Clear();
            }
 
            public void AddLine(string s, PointF pos, Brush col)
            {
                _lines.Add(s);
                _positions.Add(pos);
                _colours.Add(col);
                UpdateText();
            }
 
            public void UpdateText()
            {
                if (_lines.Count > 0)
                {
                    using (Graphics gfx = Graphics.FromImage(TextBitmap))
                    {
                        gfx.Clear(Color.Black);
                        gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                        for (int i = 0; i < _lines.Count; i++)
                            gfx.DrawString(_lines[i], TextFont, _colours[i], _positions[i]);
                    }
 
                    System.Drawing.Imaging.BitmapData data = TextBitmap.LockBits(new Rectangle(0, 0, TextBitmap.Width, TextBitmap.Height),
                        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, TextBitmap.Width, TextBitmap.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
                    TextBitmap.UnlockBits(data);
                }
            }
 
            public void Draw()
            {
                GL.PushMatrix();
                GL.LoadIdentity();
 
                Matrix4 ortho_projection = Matrix4.CreateOrthographicOffCenter(0, _clientSize.Width, _clientSize.Height, 0, -1, 1);
                GL.MatrixMode(MatrixMode.Projection);
 
                GL.PushMatrix();//
                GL.LoadMatrix(ref ortho_projection);
 
                GL.Enable(EnableCap.Blend);
                GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.DstColor);
                GL.Enable(EnableCap.Texture2D);
                GL.BindTexture(TextureTarget.Texture2D, _textureId);
 
 
                GL.Begin(BeginMode.Quads);
                GL.TexCoord2(0, 0); GL.Vertex2(0, 0);
                GL.TexCoord2(1, 0); GL.Vertex2(TextBitmap.Width, 0);
                GL.TexCoord2(1, 1); GL.Vertex2(TextBitmap.Width, TextBitmap.Height);
                GL.TexCoord2(0, 1); GL.Vertex2(0, TextBitmap.Height);
                GL.End();
                GL.PopMatrix();
 
                GL.Disable(EnableCap.Blend);
                GL.Disable(EnableCap.Texture2D);
 
                GL.MatrixMode(MatrixMode.Modelview);
                GL.PopMatrix();
            }
        }
the Fiddler's picture

Thanks for the book page, this is a question that gets asked a lot. I will add the code to the sample browser.

juanfhj's picture

Hi fictiveLaark,

I managed to use your code, but the text background is always black. Can you show me where I can change that? THanks

c2woody's picture

Hope you already did the obvious and searched the code for Color.Black.

thorrablot's picture

Thanks for posting the code - this works pretty well, except I found I had to fall back to TextRenderingHint.SingleBitPerPixelGridFit (no AA) or the text looked really awful when overlaid on background images.

Has anyone tried support for BitmapFonts in OpenTK, e.g. http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=13

puklaus's picture

@Fiddler:
Why this excellent font library and routines isnt any more in core of otk? I liked these a lot, my bitmap font rendering system sucks
when comparing to them, and when I tried some dynamic texts on that one example, it does not work the way I needed (was calculating
wrong bboxes or so, dont remember but havent tested it so much, maybe I write wrong my code already).

praveendm's picture

Hi David,
Thanx for the code.
This works perfectly fine on 32 bit machine but when I compile on 64 bit machine, only white screen is displayed. Am I missing something??

Dread_Boy's picture

I used the class you provided, but I can't get it working.

How I tried to use it:

class Game : GameWindow
{
TextWriter text_writer;
public Game()
: base(1280, 720, GraphicsMode.Default, "A Dusty Path")
{
VSync = VSyncMode.On;
text_writer = new TextWriter(new Size(1280, 720), new Size(1280, 720));
text_writer.AddLine("Prva vrsta", new PointF(0, 0), Brushes.Red);
text_writer.AddLine("Druga vrsta", new PointF(100, 200), Brushes.Blue);
text_writer.Update(1, "2. vrstica");
}
[...]
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
text_writer.Draw();
SwapBuffers();
}

I'm not sure what areaSize actually does but as far as I can understand it controls the size of bitmap created from text. My project is empty except these two snippets, default methods and GL.ClearColor(1f, 1f, 1f, 1f); in Onload method. Can anybody help me please?

AltSoftLab's picture
Dread_Boy wrote:

Can anybody help me please?

You can use QuickFont or AltSketch.QuickFont basically to render text

Gottn's picture

I got this code working, only problem is that i get a trasparent black background behind the text. I tried to set gfx.Clear color to Color.Transparent, inside the UpdateText-function but nothing changed. I would like to know if there is a way to change the background color behind the text.