shawn75's picture

Text Writer

What is the simplest way to write text in OpenTK on screen. I need it for debugpurposes. Is the text printer still supported it does not work for me. I use the rift version of TK.

This is my current code but its a bit long for just text.

// This code was written for the OpenTK library and has been released
// to the Public Domain.
// It is provided "as is" without express or implied warranty of any kind.
 
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
 
namespace MyOrion
{
    //[Example("Text Rendering (2D)", ExampleCategory.OpenGL, "1.x", Documentation = "TextRendering")]
    class TextRendering : GameWindow
    {
        TextRenderer renderer;
        Font serif = new Font(FontFamily.GenericSerif, 24);
        Font sans = new Font(FontFamily.GenericSansSerif, 24);
        Font mono = new Font(FontFamily.GenericMonospace, 24);
 
        /// <summary>
        /// Uses System.Drawing for 2d text rendering.
        /// </summary>
        public class TextRenderer : IDisposable
        {
            Bitmap bmp;
            Graphics gfx;
            int texture;
            Rectangle dirty_region;
            bool disposed;
 
            #region Constructors
 
            /// <summary>
            /// Constructs a new instance.
            /// </summary>
            /// <param name="width">The width of the backing store in pixels.</param>
            /// <param name="height">The height of the backing store in pixels.</param>
            public TextRenderer(int width, int height)
            {
                if (width <= 0)
                    throw new ArgumentOutOfRangeException("width");
                if (height <= 0)
                    throw new ArgumentOutOfRangeException("height ");
                if (GraphicsContext.CurrentContext == null)
                    throw new InvalidOperationException("No GraphicsContext is current on the calling thread.");
 
                bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                gfx = Graphics.FromImage(bmp);
                gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
 
                texture = GL.GenTexture();
                GL.BindTexture(TextureTarget.Texture2D, texture);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0,
                    PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
            }
 
            #endregion
 
            #region Public Members
 
            /// <summary>
            /// Clears the backing store to the specified color.
            /// </summary>
            /// <param name="color">A <see cref="System.Drawing.Color"/>.</param>
            public void Clear(Color color)
            {
                gfx.Clear(color);
                dirty_region = new Rectangle(0, 0, bmp.Width, bmp.Height);
            }
 
            /// <summary>
            /// Draws the specified string to the backing store.
            /// </summary>
            /// <param name="text">The <see cref="System.String"/> to draw.</param>
            /// <param name="font">The <see cref="System.Drawing.Font"/> that will be used.</param>
            /// <param name="brush">The <see cref="System.Drawing.Brush"/> that will be used.</param>
            /// <param name="point">The location of the text on the backing store, in 2d pixel coordinates.
            /// The origin (0, 0) lies at the top-left corner of the backing store.</param>
            public void DrawString(string text, Font font, Brush brush, PointF point)
            {
                gfx.DrawString(text, font, brush, point);
 
                SizeF size = gfx.MeasureString(text, font);
                dirty_region = Rectangle.Round(RectangleF.Union(dirty_region, new RectangleF(point, size)));
                dirty_region = Rectangle.Intersect(dirty_region, new Rectangle(0, 0, bmp.Width, bmp.Height));
            }
 
            /// <summary>
            /// Gets a <see cref="System.Int32"/> that represents an OpenGL 2d texture handle.
            /// The texture contains a copy of the backing store. Bind this texture to TextureTarget.Texture2d
            /// in order to render the drawn text on screen.
            /// </summary>
            public int Texture
            {
                get
                {
                    UploadBitmap();
                    return texture;
                }
            }
 
            #endregion
 
            #region Private Members
 
            // Uploads the dirty regions of the backing store to the OpenGL texture.
            void UploadBitmap()
            {
                if (dirty_region != RectangleF.Empty)
                {
                    System.Drawing.Imaging.BitmapData data = bmp.LockBits(dirty_region,
                        System.Drawing.Imaging.ImageLockMode.ReadOnly,
                        System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
                    GL.BindTexture(TextureTarget.Texture2D, texture);
                    GL.TexSubImage2D(TextureTarget.Texture2D, 0,
                        dirty_region.X, dirty_region.Y, dirty_region.Width, dirty_region.Height,
                        PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
 
                    bmp.UnlockBits(data);
 
                    dirty_region = Rectangle.Empty;
                }
            }
 
            #endregion
 
            #region IDisposable Members
 
            void Dispose(bool manual)
            {
                if (!disposed)
                {
                    if (manual)
                    {
                        bmp.Dispose();
                        gfx.Dispose();
                        if (GraphicsContext.CurrentContext != null)
                            GL.DeleteTexture(texture);
                    }
 
                    disposed = true;
                }
            }
 
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
 
            ~TextRenderer()
            {
                Console.WriteLine("[Warning] Resource leaked: {0}.", typeof(TextRenderer));
            }
 
            #endregion
        }
 
        #region Constructor
 
        public TextRendering()
            : base(300, 300)
        {
        }
 
        #endregion
 
        #region OnLoad
 
        protected override void OnLoad(EventArgs e)
        {
            renderer = new TextRenderer(Width, Height);
            PointF position = PointF.Empty;
 
            renderer.Clear(Color.MidnightBlue);
            renderer.DrawString("The quick brown fox jumps over the lazy dog", serif, Brushes.White, position);
            position.Y += serif.Height;
            renderer.DrawString("The quick brown fox jumps over the lazy dog", sans, Brushes.White, position);
            position.Y += sans.Height;
            renderer.DrawString("The quick brown fox jumps over the lazy dog", mono, Brushes.White, position);
            position.Y += mono.Height;
        }
 
        #endregion
 
        #region OnUnload
 
        protected override void OnUnload(EventArgs e)
        {
            renderer.Dispose();
        }
 
        #endregion
 
        #region OnResize
 
        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(ClientRectangle);
 
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);
        }
 
        #endregion
 
        #region OnUpdateFrame
 
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            if (Keyboard[OpenTK.Input.Key.Escape])
            {
                this.Exit();
            }
        }
 
        #endregion
 
        #region OnRenderFrame
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
 
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, renderer.Texture);
            GL.Begin(BeginMode.Quads);
 
            GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(-1f, -1f);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(1f, -1f);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(1f, 1f);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(-1f, 1f);
 
            GL.End();
 
            SwapBuffers();
        }
 
        #endregion
 
        #region Main
		/*
        public static void Main()
        {
            using (TextRendering example = new TextRendering())
            {
                OpenTK. .SetWindowTitle(example);
                example.Run(30.0);
            }
        }*/
 
        #endregion
    }
}

Comments

Comment viewing options

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

The TextPrinter is not supported. Text rendering, in general, is outside the scope of OpenTK.

The simplest way to draw 2d text is the code you posted above. I'd advise moving public class TextRenderer to a separate file in your project. The code to use TextRenderer is quite simple: create a TextRenderer (once) then call its Clear() and DrawString() methods as necessary in your rendering code. Finally, bind TextRenderer.Texture and display it on screen.

class MyGame : GameWindow
{
    TextRenderer renderer;
    Font serif = new Font(FontFamily.GenericSerif, 24);
 
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        renderer = new TextRenderer(Width, Height);
    }
 
    protected override void OnRenderFrame(FrameEventArgs e)
    {
            // Update your text
            renderer.Clear(Color.Black);
            renderer.DrawString("Hello, world", serif, Brushes.White, new Vector2(0.0f, 0.0f);
 
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            // Your normal drawing would go here
            // ...
 
            // Finally, display your text
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
 
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, renderer.Texture);
            GL.Begin(BeginMode.Quads);
 
            GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(-1f, -1f);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(1f, -1f);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(1f, 1f);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(-1f, 1f);
 
            GL.End();
 
            SwapBuffers();
    }
}

This is simply example code. You can use a proper object-oriented if you wish.

Note that rendering text like this is a quite heavy operation. I have used this successfully in a VR application, but depending on your application you may improve performance by updating text every second or third frame instead of every frame.

shawn75's picture

Thank You will try :D