#region License // // The Open Toolkit Library License // // Copyright (c) 2006 - 2011 the Open Toolkit library. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #endregion using System; using System.Collections.Generic; using System.Drawing; using System.Text; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; namespace Examples.Tutorial { [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); /// /// Uses System.Drawing for 2d text rendering. /// public class TextRenderer : IDisposable { Bitmap bmp; Graphics gfx; int texture; Rectangle dirty_region; bool disposed; #region Constructors /// /// Constructs a new instance. /// /// The width of the backing store in pixels. /// The height of the backing store in pixels. 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 /// /// Clears the backing store to the specified color. /// /// A . public void Clear(Color color) { gfx.Clear(color); dirty_region = new Rectangle(0, 0, bmp.Width, bmp.Height); } /// /// Draws the specified string to the backing store. /// /// The to draw. /// The that will be used. /// The that will be used. /// 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. 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)); } /// /// Gets a 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. /// 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.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()) { Utilities.SetWindowTitle(example); example.Run(30.0); } } #endregion } }