diff --git a/Source/OpenTK/Fonts/TextureFont.cs b/Source/OpenTK/Fonts/TextureFont.cs index 29113a05..df26b0de 100644 --- a/Source/OpenTK/Fonts/TextureFont.cs +++ b/Source/OpenTK/Fonts/TextureFont.cs @@ -1,4 +1,10 @@ -using System; +#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.Drawing; @@ -11,21 +17,26 @@ using OpenTK.Platform; namespace OpenTK.Fonts { - public class TextureFont : IDisposable + public class TextureFont : IFont { - Font font; - Dictionary loaded_glyphs = new Dictionary(36); - Graphics gfx = Graphics.FromImage(new Bitmap(1, 1)); + //class BBox { public int X, Y, Width, Height; } + Font font; + //Dictionary loaded_glyphs = new Dictionary(36); + Dictionary loaded_glyphs = new Dictionary(64); + + Graphics gfx = Graphics.FromImage(new Bitmap(1, 1)); static int texture; static TexturePacker pack; static int texture_width, texture_height; float[] viewport = new float[6]; + #region --- Constructor --- + /// /// Constructs a new TextureFont object, using the specified System.Drawing.Font. /// - /// + /// The System.Drawing.Font to use. public TextureFont(Font font) { if (font == null) @@ -34,6 +45,41 @@ namespace OpenTK.Fonts this.font = font; } + #endregion + + #region private void PrepareTexturePacker() + + /// + /// Calculates the optimal size for the font texture and TexturePacker, and creates both. + /// + private void PrepareTexturePacker() + { + // Calculate the size of the texture packer. We want a power-of-two size + // that is less than 1024 (supported in Geforce256-era cards), but large + // enough to hold at least 256 (16*16) font glyphs. + // TODO: Find the actual card limits, maybe? + int size = (int)(font.Size * 16); + size = (int)System.Math.Pow(2.0, System.Math.Ceiling(System.Math.Log((double)size, 2.0))); + if (size > 1024) + size = 1024; + + texture_width = size; + texture_height = size; + pack = new TexturePacker(texture_width, texture_height); + + GL.GenTextures(1, out texture); + GL.BindTexture(GL.Enums.TextureTarget.TEXTURE_2D, texture); + GL.TexParameter(GL.Enums.TextureTarget.TEXTURE_2D, GL.Enums.TextureParameterName.TEXTURE_MIN_FILTER, (int)GL.Enums.All.LINEAR); + GL.TexParameter(GL.Enums.TextureTarget.TEXTURE_2D, GL.Enums.TextureParameterName.TEXTURE_MAG_FILTER, (int)GL.Enums.All.LINEAR); + + GL.TexImage2D(GL.Enums.TextureTarget.TEXTURE_2D, 0, GL.Enums.PixelInternalFormat.ALPHA, texture_width, texture_height, 0, + GL.Enums.PixelFormat.RGBA, GL.Enums.PixelType.UNSIGNED_BYTE, IntPtr.Zero); + } + + #endregion + + #region public void LoadGlyphs(string glyphs) + /// /// Prepares the specified glyphs for rendering. /// @@ -47,7 +93,11 @@ namespace OpenTK.Fonts } } - private int LoadGlyph(char c) + #endregion + + #region private RectangleF LoadGlyph(char c) + + private RectangleF LoadGlyph(char c) { if (pack == null) PrepareTexturePacker(); @@ -55,34 +105,68 @@ namespace OpenTK.Fonts Glyph g = new Glyph(c, font); Rectangle rect = pack.Add(g); - using (Bitmap bmp = new Bitmap(g.Width, g.Height, PixelFormat.Format32bppArgb)) + using (Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb)) using (Graphics gfx = Graphics.FromImage(bmp)) { // Upload texture and create Display List: GL.BindTexture(GL.Enums.TextureTarget.TEXTURE_2D, texture); - gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + //gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; //gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; gfx.Clear(Color.Transparent); gfx.DrawString(g.Character.ToString(), g.Font, Brushes.White, 0.0f, 0.0f); - BitmapData data = bmp.LockBits(new Rectangle(0, 0, g.Width, g.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - GL.TexSubImage2D(GL.Enums.TextureTarget.TEXTURE_2D, 0, rect.Left, rect.Top, rect.Width, rect.Height, - GL.Enums.PixelFormat.RGBA, GL.Enums.PixelType.UNSIGNED_BYTE, data.Scan0); - bmp.UnlockBits(data); + BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, rect.Width, rect.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + GL.TexSubImage2D(GL.Enums.TextureTarget.TEXTURE_2D, 0, rect.Left, rect.Top, rect.Width, rect.Height, + GL.Enums.PixelFormat.RGBA, GL.Enums.PixelType.UNSIGNED_BYTE, bmp_data.Scan0); + bmp.UnlockBits(bmp_data); + + RectangleF f_rect = RectangleF.FromLTRB( + rect.Left / (float)texture_width, + rect.Top / (float)texture_height, + rect.Right / (float)texture_width, + rect.Bottom / (float)texture_height); + + loaded_glyphs.Add(g.Character, f_rect); + + return f_rect; + + /* + byte[] data = new byte[rect.Width * rect.Height * 4]; + int i = 0; + for (int y = 0; y < rect.Height - 1; y++) + { + for (int x = 0; x < rect.Width - 1; x++) + { + data[i++] = bmp.GetPixel(x, y).R; + data[i++] = bmp.GetPixel(x, y).G; + data[i++] = bmp.GetPixel(x, y).B; + data[i++] = bmp.GetPixel(x, y).A; + } + } + + unsafe + { + fixed (byte* data_ptr = data) + GL.TexSubImage2D(GL.Enums.TextureTarget.TEXTURE_2D, 0, rect.Left, rect.Top, rect.Width, rect.Height, + GL.Enums.PixelFormat.RGBA, GL.Enums.PixelType.UNSIGNED_BYTE, (IntPtr)data_ptr); + } + float left = rect.Left / (float)texture_width; float bottom = rect.Bottom / (float)texture_height; float right = rect.Right / (float)texture_width; float top = rect.Top / (float)texture_height; float width = rect.Right - rect.Left; float height = rect.Bottom - rect.Top; - + */ + /* int list = GL.GenLists(1); GL.NewList(list, GL.Enums.ListMode.COMPILE); GL.Enable(GL.Enums.EnableCap.BLEND); - GL.BlendFunc(GL.Enums.BlendingFactorSrc.ONE, GL.Enums.BlendingFactorDest.ONE_MINUS_SRC_ALPHA); + GL.BlendFunc(GL.Enums.BlendingFactorSrc.SRC_ALPHA, GL.Enums.BlendingFactorDest.ONE_MINUS_SRC_ALPHA); GL.Begin(GL.Enums.BeginMode.QUADS); @@ -109,36 +193,71 @@ namespace OpenTK.Fonts loaded_glyphs.Add(g.Character, list); return list; + */ } } - /// - /// Calculates the optimal size for the font texture and TexturePacker, and creates both. - /// - private void PrepareTexturePacker() + #endregion + + public void Draw() { - // Calculate the size of the texture packer. We want a power-of-two size - // that is less than 1024 (supported in Geforce256-era cards), but large - // enough to hold at least 256 (16*16) font glyphs. - int size = (int)(font.Size * 16); - size = (int)System.Math.Pow(2.0, System.Math.Ceiling(System.Math.Log((double)size, 2.0))); - if (size > 1024) - size = 1024; - - texture_width = size; - texture_height = size; - pack = new TexturePacker(texture_width, texture_height); - - GL.GenTextures(1, out texture); - GL.BindTexture(GL.Enums.TextureTarget.TEXTURE_2D, texture); - GL.TexParameter(GL.Enums.TextureTarget.TEXTURE_2D, GL.Enums.TextureParameterName.TEXTURE_MIN_FILTER, (int)GL.Enums.All.LINEAR); - GL.TexParameter(GL.Enums.TextureTarget.TEXTURE_2D, GL.Enums.TextureParameterName.TEXTURE_MAG_FILTER, (int)GL.Enums.All.NEAREST); - - byte[] data = new byte[texture_height * texture_width * 4]; - GL.TexImage2D(GL.Enums.TextureTarget.TEXTURE_2D, 0, 4, texture_width, texture_height, 0, - GL.Enums.PixelFormat.RGBA, GL.Enums.PixelType.UNSIGNED_BYTE, data); + ILayoutProvider l = new DefaultLayoutProvider(); + l.PerformLayout("ato e -et uq39tu, /q3t.q/t.q3t q 34t34qwt .", this, + new RectangleF(9.0f, 9.0f, 300.0f, 100.0f), StringAlignment.Near, false); } + #region + + public bool GlyphData(char glyph, ref RectangleF textureRectangle, out float width, out float height, out int texture) + { + if (loaded_glyphs.TryGetValue(glyph, out textureRectangle)) + { + width = textureRectangle.Width * texture_width; + height = textureRectangle.Height * texture_height; + texture = TextureFont.texture; + return true; + } + width = height = texture = 0; + return false; + } + + #endregion + + #region public RectangleF FindRectangle(char c) + + /// + /// Looks up the specified glyph and returns it's bounding System.Drawing.Rectangle in the font texture. + /// This function will add the glyph to the texture font, if it hasn't been previoulsy loaded through LoadGlyphs(). + /// + /// The character representing the glyph. + /// The System.Drawing.Rectangle that represents the glyph's bounding box in the font texture. + /// + public RectangleF FindRectangle(char c) + { + RectangleF rect; + return loaded_glyphs.TryGetValue(c, out rect) ? rect : LoadGlyph(c); + } + + #endregion + + #region public int FindTexture(char c) + + /// + /// Returns the handle to the OpenGL texture where the specified glyph is located or zero, if the glyph has not been loaded. + /// + /// The character that corresponds to the glyph. + /// A handle to the OpenGL texture, or zero. + public int FindTexture(char c) + { + if (loaded_glyphs.ContainsKey(c)) + return texture; + return 0; + } + + #endregion + + #region public void Print(char c) +#if false /// /// Prints a glyph. /// @@ -159,6 +278,8 @@ namespace OpenTK.Fonts /// public void Print(char c) { + GL.BindTexture(GL.Enums.TextureTarget.TEXTURE_2D, texture); + int list; if (loaded_glyphs.TryGetValue(c, out list)) { @@ -169,7 +290,11 @@ namespace OpenTK.Fonts GL.CallList(LoadGlyph(c)); } } +#endif + #endregion + #region public void PrintFast(char c) +#if false /// /// Prints a previously loaded glyph. /// @@ -184,29 +309,39 @@ namespace OpenTK.Fonts { GL.CallList(loaded_glyphs[c]); } +#endif + #endregion + + #region public float Height /// /// Gets a float indicating the default line spacing of this font. /// - public float LineSpacing + public float Height { get { return font.Height; } } + #endregion + + #region public float MeasureString(string str) + /// /// Measures the width of the specified string. /// /// /// - public float MeasureWidth(string str) + public SizeF MeasureString(string str) { - float distance = gfx.MeasureString(str, font, 16384, StringFormat.GenericTypographic).Width; - if (distance == 0) - distance = font.SizeInPoints * 0.5f; - return distance; + SizeF size = gfx.MeasureString(str, font, 16384, StringFormat.GenericTypographic); + if (size.Width == 0) + size.Width = font.SizeInPoints * 0.5f; + return size; } - #region IDisposable Members + #endregion + + #region --- IDisposable Members --- bool disposed;