Skip to content

Instantly share code, notes, and snippets.

@thefiddler
Last active January 30, 2024 23:33
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save thefiddler/8247687 to your computer and use it in GitHub Desktop.
GUI example using OpenTK and OpenGL 1.1.
// 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;
namespace Examples.Tutorial
{
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);
float angle;
/// <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(800, 600)
{
}
#endregion
#region OnLoad
protected override void OnLoad(EventArgs e)
{
renderer = new TextRenderer(Width, Height);
PointF position = PointF.Empty;
renderer.Clear(Color.Transparent);
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);
}
#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);
RenderCube((float)e.Time);
RenderGui();
SwapBuffers();
}
#endregion
#region RenderCube
void RenderCube(float time)
{
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(
MathHelper.PiOver2, Width / (float)Height, 1.0f, 128.0f);
Matrix4 modelview = Matrix4.LookAt(0, 3, 3, 0, 0, 0, 0, 1, 0);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref modelview);
GL.Rotate(angle, 0.0f, 1.0f, 0.0f);
angle += time * 100;
GL.Enable(EnableCap.DepthTest);
GL.Begin(BeginMode.Quads);
GL.Color3(Color.Red);
GL.Vertex3(-1.0f, -1.0f, -1.0f);
GL.Vertex3(-1.0f, 1.0f, -1.0f);
GL.Vertex3(1.0f, 1.0f, -1.0f);
GL.Vertex3(1.0f, -1.0f, -1.0f);
GL.Color3(Color.Green);
GL.Vertex3(-1.0f, -1.0f, -1.0f);
GL.Vertex3(1.0f, -1.0f, -1.0f);
GL.Vertex3(1.0f, -1.0f, 1.0f);
GL.Vertex3(-1.0f, -1.0f, 1.0f);
GL.Color3(Color.Blue);
GL.Vertex3(-1.0f, -1.0f, -1.0f);
GL.Vertex3(-1.0f, -1.0f, 1.0f);
GL.Vertex3(-1.0f, 1.0f, 1.0f);
GL.Vertex3(-1.0f, 1.0f, -1.0f);
GL.Color3(Color.Yellow);
GL.Vertex3(-1.0f, -1.0f, 1.0f);
GL.Vertex3(1.0f, -1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f);
GL.Vertex3(-1.0f, 1.0f, 1.0f);
GL.Color3(Color.Magenta);
GL.Vertex3(-1.0f, 1.0f, -1.0f);
GL.Vertex3(-1.0f, 1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, -1.0f);
GL.Color3(Color.Violet);
GL.Vertex3(1.0f, -1.0f, -1.0f);
GL.Vertex3(1.0f, 1.0f, -1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f);
GL.Vertex3(1.0f, -1.0f, 1.0f);
GL.End();
GL.Disable(EnableCap.DepthTest);
}
#endregion
#region RenderGui
void RenderGui()
{
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Disable(EnableCap.DepthTest);
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.Texture2D);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.BindTexture(TextureTarget.Texture2D, renderer.Texture);
GL.Begin(BeginMode.Quads);
GL.Color3(Color.White);
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();
GL.Disable(EnableCap.Texture2D);
GL.Disable(EnableCap.Blend);
GL.Enable(EnableCap.DepthTest);
}
#endregion
#region Main
public static void Main()
{
using (TextRendering example = new TextRendering())
{
Utilities.SetWindowTitle(example);
example.Run(30.0);
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment