From 3c79815596cf7bb73fc573be4d4596e61fddfea9 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Tue, 6 Nov 2007 20:59:15 +0000 Subject: [PATCH] Font renderer implementation. --- Source/OpenTK/Fonts/DisplayListTextPrinter.cs | 47 +++++++++ Source/OpenTK/Fonts/IPrinterImplementation.cs | 17 ++++ Source/OpenTK/Fonts/TextHandle.cs | 22 +++++ Source/OpenTK/Fonts/TextPrinter.cs | 94 +++--------------- Source/OpenTK/Fonts/TextureFont.cs | 79 ++++++++++++--- Source/OpenTK/Fonts/VboTextPrinter.cs | 99 +++++++++++++++++++ 6 files changed, 264 insertions(+), 94 deletions(-) create mode 100644 Source/OpenTK/Fonts/DisplayListTextPrinter.cs create mode 100644 Source/OpenTK/Fonts/IPrinterImplementation.cs create mode 100644 Source/OpenTK/Fonts/TextHandle.cs create mode 100644 Source/OpenTK/Fonts/VboTextPrinter.cs diff --git a/Source/OpenTK/Fonts/DisplayListTextPrinter.cs b/Source/OpenTK/Fonts/DisplayListTextPrinter.cs new file mode 100644 index 00000000..304cad90 --- /dev/null +++ b/Source/OpenTK/Fonts/DisplayListTextPrinter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenTK.OpenGL; + +namespace OpenTK.Fonts +{ + /// + /// Provides text printing through OpenGL 1.1 Display Lists. + /// + class DisplayListTextPrinter : ITextPrinterImplementation + { + #region IPrinter Members + + public TextHandle Load(OpenTK.Math.Vector2[] vertices, ushort[] indices) + { + DisplayListTextHandle handle = new DisplayListTextHandle(GL.GenLists(1)); + + GL.NewList(handle.Handle, OpenTK.OpenGL.Enums.ListMode.Compile); + GL.Begin(OpenTK.OpenGL.Enums.BeginMode.Triangles); + + foreach (ushort index in indices) + { + GL.TexCoord2(vertices[index + 1]); + GL.Vertex2(vertices[index]); + } + + GL.End(); + GL.EndList(); + + return handle; + } + + public void Draw(TextHandle handle) + { + GL.CallList(handle.Handle); + } + + #endregion + } + + public class DisplayListTextHandle : TextHandle + { + public DisplayListTextHandle(int handle) : base(handle) { } + } +} diff --git a/Source/OpenTK/Fonts/IPrinterImplementation.cs b/Source/OpenTK/Fonts/IPrinterImplementation.cs new file mode 100644 index 00000000..42303747 --- /dev/null +++ b/Source/OpenTK/Fonts/IPrinterImplementation.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenTK.Math; + +namespace OpenTK.Fonts +{ + /// + /// Defines the interface for TextPrinter implementations. + /// + interface ITextPrinterImplementation + { + TextHandle Load(Vector2[] vertices, ushort[] indices); + void Draw(TextHandle handle); + } +} diff --git a/Source/OpenTK/Fonts/TextHandle.cs b/Source/OpenTK/Fonts/TextHandle.cs new file mode 100644 index 00000000..be4946fe --- /dev/null +++ b/Source/OpenTK/Fonts/TextHandle.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Fonts +{ + public class TextHandle + { + internal TextHandle(int handle) + { + Handle = handle; + } + + public readonly int Handle; + internal TextureFont font; + + public override string ToString() + { + return String.Format("TextHandle: {0}", Handle); + } + } +} diff --git a/Source/OpenTK/Fonts/TextPrinter.cs b/Source/OpenTK/Fonts/TextPrinter.cs index 4400e513..edaf5b75 100644 --- a/Source/OpenTK/Fonts/TextPrinter.cs +++ b/Source/OpenTK/Fonts/TextPrinter.cs @@ -9,11 +9,11 @@ using System.Collections.Generic; using System.Text; using System.Drawing; using System.Text.RegularExpressions; +using System.Runtime.InteropServices; using OpenTK.Math; using OpenTK.OpenGL; using OpenTK.OpenGL.Enums; -using System.Runtime.InteropServices; namespace OpenTK.Fonts @@ -27,7 +27,7 @@ namespace OpenTK.Fonts //static char[] split_chars = new char[] { ' ', '\n', '\t', ',', '.', '/', '?', '!', ';', '\\', '-', '+', '*', '=' }; static bool use_vbo, use_arb_vbo, use_display_list; static bool functionality_checked = false; - static IPrinter printer; + static ITextPrinterImplementation printer; #region --- Constructor --- @@ -43,11 +43,12 @@ namespace OpenTK.Fonts /// static void CheckNeededFunctionality() { - printer = - GL.SupportsExtension("VERSION_1_5") ? new VboPrinter() : + printer = new DisplayListTextPrinter(); + /* + GL.SupportsExtension("VERSION_1_5") ? new VboTextPrinter() : GL.SupportsExtension("ARB_vertex_buffer_object") ? null : GL.SupportsExtension("VERSION_1_1") ? null : null; - + */ if (printer == null) throw new NotSupportedException("DefaultLayoutProvider requires at least OpenGL 1.1 support."); @@ -79,8 +80,8 @@ namespace OpenTK.Fonts if (text == null) throw new ArgumentNullException("Parameter cannot be null.", "text"); - if (text.Length > 4096) - throw new ArgumentOutOfRangeException("text", text.Length, "Text length must be between 1 and 4096 characters"); + if (text.Length > 8192) + throw new ArgumentOutOfRangeException("text", text.Length, "Text length must be between 1 and 8192 characters"); Vector2[] vertices = new Vector2[8 * text.Length]; // Interleaved, vertex, texcoord, vertex, etc... ushort[] indices = new ushort[6 * text.Length]; @@ -90,6 +91,8 @@ namespace OpenTK.Fonts float char_width, char_height, measured_width, measured_height; int texture; + font.LoadGlyphs(text); + // Every character comprises of 4 vertices, forming two triangles. We generate an index array which // indexes vertices in a triangle-strip fashion. To create a single strip for the whole string, we // need to add a degenerate triangle (0 height) to connect the last vertex of the previous line with @@ -168,7 +171,8 @@ namespace OpenTK.Fonts GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); - GL.Enable(EnableCap.Blend); + //GL.Disable(EnableCap.Texture2d); + GL.BindTexture(TextureTarget.Texture2d, handle.font.Texture); printer.Draw(handle); @@ -179,78 +183,4 @@ namespace OpenTK.Fonts #endregion } - - public class TextHandle - { - internal TextHandle(int handle) - { - Handle = handle; - } - - public readonly int Handle; - internal TextureFont font; - } - - class VboTextHandle : TextHandle - { - public VboTextHandle(int handle) : base(handle) { } - - internal int vbo_id; // vertex buffer object id. - internal int ebo_id; // index buffer object id. - internal int element_count; // Number of elements in the ebo. - } - - interface IPrinter - { - TextHandle Load(Vector2[] vertices, ushort[] indices); - void Draw(TextHandle handle); - } - - class VboPrinter : IPrinter - { - static int allocated_handles; - static int vector2_size = Marshal.SizeOf(new Vector2()); - - #region --- IPrinter Members --- - - public TextHandle Load(Vector2[] vertices, ushort[] indices) - { - VboTextHandle handle = new VboTextHandle(++allocated_handles); - GL.GenBuffers(1, out handle.vbo_id); - GL.GenBuffers(1, out handle.ebo_id); - - GL.BindBuffer(Version15.ArrayBuffer, handle.vbo_id); - GL.BufferData(Version15.ArrayBuffer, (IntPtr)(vertices.Length * vector2_size), vertices, - Version15.StaticDraw); - - GL.BindBuffer(Version15.ElementArrayBuffer, handle.ebo_id); - GL.BufferData(Version15.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(ushort)), indices, - Version15.StaticDraw); - handle.element_count = indices.Length; - - return handle; - } - - public void Draw(TextHandle handle) - { - VboTextHandle vbo = (VboTextHandle)handle; - - GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit); - - GL.BindBuffer(Version15.StaticDraw, vbo.vbo_id); - GL.BindBuffer(Version15.ElementArrayBuffer, vbo.ebo_id); - - GL.EnableClientState(EnableCap.VertexArray); - GL.EnableClientState(EnableCap.TextureCoordArray); - - GL.TexCoordPointer(2, TexCoordPointerType.Float, vector2_size, (IntPtr)vector2_size); - GL.VertexPointer(2, VertexPointerType.Float, vector2_size, IntPtr.Zero); - - GL.DrawElements(BeginMode.Triangles, vbo.element_count, All.UnsignedShort, IntPtr.Zero); - - GL.PopClientAttrib(); - } - - #endregion - } } diff --git a/Source/OpenTK/Fonts/TextureFont.cs b/Source/OpenTK/Fonts/TextureFont.cs index 39936da3..d81fe3b3 100644 --- a/Source/OpenTK/Fonts/TextureFont.cs +++ b/Source/OpenTK/Fonts/TextureFont.cs @@ -15,15 +15,17 @@ using OpenTK.OpenGL; using OpenTK.OpenGL.Enums; using System.Drawing.Imaging; using OpenTK.Platform; +using System.Drawing; namespace OpenTK.Fonts { public class TextureFont : IFont { - System.Drawing.Font font; + Font font; Dictionary loaded_glyphs = new Dictionary(64); - System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1)); + Bitmap bmp; + Graphics gfx; static int texture; static TexturePacker pack; static int texture_width, texture_height; @@ -41,6 +43,9 @@ namespace OpenTK.Fonts throw new ArgumentNullException("font", "Argument to TextureFont constructor cannot be null."); this.font = font; + + bmp = new Bitmap(font.Height * 2, font.Height * 2); + gfx = Graphics.FromImage(bmp); } #endregion @@ -94,13 +99,28 @@ namespace OpenTK.Fonts #endregion + #region public void LoadGlyph(char glyph) + + /// + /// Prepares the specified glyph for rendering. + /// + /// The glyph to prepare for rendering. + public void LoadGlyph(char glyph) + { + Box2 rect = new Box2(); + if (!loaded_glyphs.ContainsKey(glyph)) + LoadGlyph(glyph, out rect); + } + + #endregion + #region private void LoadGlyph(char c, out Box2 rectangle) /// /// Adds a glyph to the texture packer. /// /// The character of the glyph. - /// A RectangleF that will hold the data for this glyph. + /// An OpenTK.Math.Box2 that will hold the data for this glyph. private void LoadGlyph(char c, out Box2 rectangle) { if (pack == null) @@ -109,25 +129,48 @@ namespace OpenTK.Fonts Glyph g = new Glyph(c, font); System.Drawing.Rectangle rect = pack.Add(g); - using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap( - rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) - using (System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bmp)) + //using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) + //using (System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bmp)) { - // Upload texture data. GL.BindTexture(TextureTarget.Texture2d, texture); - //gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; - //gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; - gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; - gfx.TextContrast = 0; + // Set font rendering mode. Smal sizes look blurry without gridfitting, so turn + // that on. Increasing contrast also seems to help. + if (font.Size <= 18.0f) + { + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; + gfx.TextContrast = 1; + } + else + { + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; + gfx.TextContrast = 0; + } + gfx.Clear(System.Drawing.Color.Transparent); gfx.DrawString(g.Character.ToString(), g.Font, System.Drawing.Brushes.White, 0.0f, 0.0f); - + /* BitmapData bmp_data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, rect.Width, rect.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); GL.TexSubImage2D(TextureTarget.Texture2d, 0, rect.Left, rect.Top, rect.Width, rect.Height, OpenTK.OpenGL.Enums.PixelFormat.Rgba, PixelType.UnsignedByte, bmp_data.Scan0); bmp.UnlockBits(bmp_data); + */ + + int[] data = new int[rect.Width * rect.Height]; + for (int y = 0; y < rect.Height; y++) + { + for (int x = 0; x < rect.Width; x++) + { + data[y * rect.Width + x] = bmp.GetPixel(x, y).ToArgb(); + } + } + unsafe + { + fixed (int* ptr = data) + GL.TexSubImage2D(TextureTarget.Texture2d, 0, rect.Left, rect.Top, rect.Width, rect.Height, + OpenTK.OpenGL.Enums.PixelFormat.Rgba, PixelType.UnsignedByte, (IntPtr)ptr); + } rectangle = new Box2( rect.Left / (float)texture_width, @@ -180,6 +223,18 @@ namespace OpenTK.Fonts #endregion + #region public float Width + + /// + /// Gets a float indicating the default line spacing of this font. + /// + public float Width + { + get { return font.SizeInPoints; } + } + + #endregion + #region internal int Texture /// diff --git a/Source/OpenTK/Fonts/VboTextPrinter.cs b/Source/OpenTK/Fonts/VboTextPrinter.cs new file mode 100644 index 00000000..2b60e4a1 --- /dev/null +++ b/Source/OpenTK/Fonts/VboTextPrinter.cs @@ -0,0 +1,99 @@ +#region --- License --- +/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos + * See license.txt for license info + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; + +using OpenTK.OpenGL; +using OpenTK.Math; +using OpenTK.OpenGL.Enums; + +namespace OpenTK.Fonts +{ + /// + /// Provides text printing through OpenGL 1.5 vertex buffer objects. + /// + class VboTextPrinter : ITextPrinterImplementation + { + static int allocated_handles; + static int vector2_size = Marshal.SizeOf(new Vector2()); + + #region --- IPrinter Members --- + + public TextHandle Load(Vector2[] vertices, ushort[] indices) + { + VboTextHandle handle = new VboTextHandle(++allocated_handles); + + GL.GenBuffers(1, out handle.vbo_id); + GL.BindBuffer(Version15.ArrayBuffer, handle.vbo_id); + GL.BufferData(Version15.ArrayBuffer, (IntPtr)(vertices.Length * vector2_size), vertices, + Version15.StaticDraw); + GL.BindBuffer(Version15.ArrayBuffer, 0); + + GL.GenBuffers(1, out handle.ebo_id); + GL.BindBuffer(Version15.ElementArrayBuffer, handle.ebo_id); + GL.BufferData(Version15.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(ushort)), indices, + Version15.StaticDraw); + GL.BindBuffer(Version15.ElementArrayBuffer, 0); + + handle.element_count = indices.Length; + return handle; + } + + public void Draw(TextHandle handle) + { + VboTextHandle vbo = (VboTextHandle)handle; + + //GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit); + + //GL.EnableClientState(EnableCap.TextureCoordArray); + GL.EnableClientState(EnableCap.VertexArray); + + GL.BindBuffer(Version15.StaticDraw, vbo.vbo_id); + GL.BindBuffer(Version15.ElementArrayBuffer, vbo.ebo_id); + + GL.TexCoordPointer(2, TexCoordPointerType.Float, vector2_size, (IntPtr)vector2_size); + GL.VertexPointer(2, VertexPointerType.Float, vector2_size, IntPtr.Zero); + + GL.DrawElements(BeginMode.Triangles, vbo.element_count, All.UnsignedShort, IntPtr.Zero); + //GL.DrawArrays(BeginMode.LineLoop, 0, vbo.element_count); + + GL.BindBuffer(Version15.ArrayBuffer, 0); + GL.BindBuffer(Version15.ElementArrayBuffer, 0); + + GL.DisableClientState(EnableCap.VertexArray); + //GL.DisableClientState(EnableCap.TextureCoordArray); + + //GL.PopClientAttrib(); + } + + #endregion + } + + #region class VboTextHandle : TextHandle + + /// + /// Contains the necessary information to print text through the VboTextPrinter implementation. + /// + class VboTextHandle : TextHandle + { + public VboTextHandle(int handle) : base(handle) { } + + internal int vbo_id; // vertex buffer object id. + internal int ebo_id; // index buffer object id. + internal int element_count; // Number of elements in the ebo. + + public override string ToString() + { + return String.Format("TextHandle (vbo): {0} ({1} element(s), vbo id: {2}, ebo id: {3}", + Handle, element_count / 6, vbo_id, ebo_id); + } + } + + #endregion +}