diff --git a/Source/Compatibility/Fonts/DisplayListTextHandle.cs b/Source/Compatibility/Fonts/DisplayListTextHandle.cs new file mode 100644 index 00000000..44395d58 --- /dev/null +++ b/Source/Compatibility/Fonts/DisplayListTextHandle.cs @@ -0,0 +1,37 @@ +#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 OpenTK.Graphics.OpenGL; + +namespace OpenTK.Graphics +{ + [Obsolete()] + class DisplayListTextHandle : TextHandle + { + public DisplayListTextHandle(int handle) : base(handle) { } + + public override string ToString() + { + return String.Format("TextHandle (display list): {0}", Handle); + } + + protected override void Dispose(bool manual) + { + if (!disposed) + { + if (manual) + { + GL.DeleteLists(Handle, 1); + } + disposed = true; + } + } + } +} diff --git a/Source/Compatibility/Fonts/DisplayListTextPrinter.cs b/Source/Compatibility/Fonts/DisplayListTextPrinter.cs new file mode 100644 index 00000000..2db546e2 --- /dev/null +++ b/Source/Compatibility/Fonts/DisplayListTextPrinter.cs @@ -0,0 +1,57 @@ +#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 OpenTK.Graphics.OpenGL; + +namespace OpenTK.Graphics +{ + /// + /// Provides text printing through OpenGL 1.1 Display Lists. + /// + [Obsolete()] + class DisplayListTextPrinter : ITextPrinterImplementation + { + #region IPrinter Members + + public TextHandle Load(Vector2[] vertices, ushort[] indices, int index_count) + { + DisplayListTextHandle handle = new DisplayListTextHandle(GL.GenLists(1)); + + GL.NewList(handle.Handle, ListMode.Compile); + + this.Draw(vertices, indices, index_count); + + GL.EndList(); + + return handle; + } + + public void Draw(TextHandle handle) + { + GL.CallList(handle.Handle); + } + + public void Draw(Vector2[] vertices, ushort[] indices, int index_count) + { + GL.Begin(BeginMode.Triangles); + + for (int i = 0; i < index_count; i++) + //foreach (ushort index in indices) + { + GL.TexCoord2(vertices[indices[i] + 1]); + GL.Vertex2(vertices[indices[i]]); + } + + GL.End(); + } + + #endregion + } +} diff --git a/Source/Compatibility/Fonts/Glyph.cs b/Source/Compatibility/Fonts/Glyph.cs new file mode 100644 index 00000000..8cbb83c4 --- /dev/null +++ b/Source/Compatibility/Fonts/Glyph.cs @@ -0,0 +1,176 @@ +#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; + +namespace OpenTK.Graphics +{ + using Graphics = System.Drawing.Graphics; + + /// + /// Represents a single character of a specific Font. + /// + [Obsolete] + struct Glyph : IPackable + { + char character; + Font font; + SizeF size; + + #region --- Constructors --- + + // Constructs a new Glyph that represents the given character and Font. + public Glyph(char c, Font f, SizeF s) + { + if (f == null) + throw new ArgumentNullException("f", "You must specify a valid font"); + character = c; + font = f; + size = s; + } + + #endregion + + #region --- Public Methods --- + + #region public char Character + + /// + /// Gets the character represented by this Glyph. + /// + public char Character + { + get { return character; } + private set { character = value; } + } + + #endregion + + #region public Font Font + + /// + /// Gets the Font of this Glyph. + /// + public Font Font + { + get { return font; } + private set + { + if (value == null) + throw new ArgumentNullException("Font", "Glyph font cannot be null"); + + font = value; + } + } + + #endregion + + #region public override bool Equals(object obj) + + /// + /// Checks whether the given object is equal (memberwise) to the current Glyph. + /// + /// The obj to check. + /// True, if the object is identical to the current Glyph. + public override bool Equals(object obj) + { + if (obj is Glyph) + return this.Equals((Glyph)obj); + return base.Equals(obj); + } + + #endregion + + #region public override string ToString() + + /// + /// Describes this Glyph object. + /// + /// Returns a System.String describing this Glyph. + public override string ToString() + { + return String.Format("'{0}', {1} {2}, {3} {4}, ({5}, {6})", Character, Font.Name, font.Style, font.Size, font.Unit, Width, Height); + } + + #endregion + + #region public override int GetHashCode() + + /// + /// Calculates the hashcode for this Glyph. + /// + /// A System.Int32 containing a hashcode that uniquely identifies this Glyph. + public override int GetHashCode() + { + return character.GetHashCode() ^ font.GetHashCode() ^ size.GetHashCode(); + } + + #endregion + + #region public SizeF Size + + /// + /// Gets the size of this Glyph. + /// + public SizeF Size { get { return size; } } + + #endregion + + #region public RectangleF Rectangle + + /// + /// Gets the bounding box of this Glyph. + /// + public RectangleF Rectangle { get { return new RectangleF(PointF.Empty, Size); } } + + #endregion + + #endregion + + #region --- IPackable Members --- + + /// + /// Gets an integer representing the width of the Glyph in pixels. + /// + public int Width + { + get + { + return (int)System.Math.Ceiling(size.Width); + } + } + + /// + /// Gets an integer representing the height of the Glyph in pixels. + /// + public int Height + { + get + { + return (int)System.Math.Ceiling(size.Height); + } + } + + #endregion + + #region --- IEquatable Members --- + + /// + /// Compares the current Glyph with the given Glyph. + /// + /// The Glyph to compare to. + /// True if both Glyphs represent the same character of the same Font, false otherwise. + public bool Equals(Glyph other) + { + return Character == other.Character && Font == other.Font && Size == other.Size; + } + + #endregion + } +} diff --git a/Source/Compatibility/Fonts/IFont.cs b/Source/Compatibility/Fonts/IFont.cs new file mode 100644 index 00000000..d73ddbb9 --- /dev/null +++ b/Source/Compatibility/Fonts/IFont.cs @@ -0,0 +1,21 @@ +#region --- License --- +/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos + * See license.txt for license info + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace OpenTK.Graphics +{ + [Obsolete] + public interface IFont : IDisposable + { + void LoadGlyphs(string glyphs); + float Height { get; } + void MeasureString(string str, out float width, out float height); + } +} diff --git a/Source/Compatibility/Fonts/IPrinterImplementation.cs b/Source/Compatibility/Fonts/IPrinterImplementation.cs new file mode 100644 index 00000000..08218bf9 --- /dev/null +++ b/Source/Compatibility/Fonts/IPrinterImplementation.cs @@ -0,0 +1,45 @@ +#region --- License --- +/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos + * See license.txt for license info + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + + +namespace OpenTK.Graphics +{ + /// + /// Defines the interface for TextPrinter implementations. + /// + [Obsolete("Use ITextOutputProvider instead")] + public interface ITextPrinterImplementation + { + /// + /// Caches a text fragment for future use. + /// + /// The vertex array for the text fragment. + /// The index array for the text fragment. Please use the indexCount parameter + /// instead of indices.Count, as the indices array may be larger than necessary for performance reasons. + /// The actual number of indices in the text fragment. + /// A TextHandle that can be used to draw the text fragment. + TextHandle Load(Vector2[] vertices, ushort[] indices, int indexCount); + + /// + /// Draws the specified cached text fragment. + /// + /// The TextHandle corresponding to the desired text fragment. + void Draw(TextHandle handle); + + /// + /// Draws a text fragment, without caching. + /// + /// The vertex array for the text fragment. + /// The index array for the text fragment. Please use the indexCount parameter + /// instead of indices.Count, as the indices array may be larger than necessary for performance reasons. + /// The actual number of indices in the text fragment. + void Draw(Vector2[] vertices, ushort[] indices, int indexCount); + } +} diff --git a/Source/Compatibility/Fonts/TextHandle.cs b/Source/Compatibility/Fonts/TextHandle.cs new file mode 100644 index 00000000..10cedf35 --- /dev/null +++ b/Source/Compatibility/Fonts/TextHandle.cs @@ -0,0 +1,88 @@ +#region --- License --- +/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos + * See license.txt for license info + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + /// + /// Represents a handle to cached text. + /// + [Obsolete("Use TextPrinter.Print instead")] + public class TextHandle : IDisposable + { + internal string Text; + internal System.Drawing.Font GdiPFont; + + /// + /// Constructs a new TextHandle, + /// + /// + internal TextHandle(int handle) + { + Handle = handle; + } + + internal TextHandle(string text, System.Drawing.Font font) + { + Text = text; + GdiPFont = font; + } + + private int handle; + protected TextureFont font; + protected bool disposed; + + /// + /// Gets the handle of the cached text run. Call the OpenTK.Graphics.ITextPrinter.Draw() method + /// to draw the text represented by this TextHandle. + /// + public int Handle + { + get { return handle; } + protected set { handle = value; } + } + + /// + /// Gets the TextureFont used for this text run. + /// + public TextureFont Font + { + get { return font; } + internal set { font = value; } + } + + #region public override string ToString() + + /// + /// Returns a System.String that represents the current TextHandle. + /// + /// a System.String that descibes the current TextHandle. + public override string ToString() + { + return String.Format("TextHandle: {0}", Handle); + } + + #endregion + + #region --- IDisposable Members --- + + /// + /// Frees the resource consumed by the TextHandle. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + protected virtual void Dispose(bool manual) { } + ~TextHandle() { this.Dispose(false); } + + #endregion + } +} diff --git a/Source/Compatibility/Fonts/TextureFont.cs b/Source/Compatibility/Fonts/TextureFont.cs new file mode 100644 index 00000000..b8d434f8 --- /dev/null +++ b/Source/Compatibility/Fonts/TextureFont.cs @@ -0,0 +1,563 @@ +#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.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Diagnostics; + +using OpenTK.Graphics.OpenGL; +using OpenTK.Platform; + +namespace OpenTK.Graphics +{ + using Graphics = System.Drawing.Graphics; + using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat; + using System.Text.RegularExpressions; + + [Obsolete("Use System.Drawing.Font instead")] + public class TextureFont : IFont + { + internal Font font; + Dictionary loaded_glyphs = new Dictionary(64); + + Bitmap bmp; + Graphics gfx; + // TODO: We need to be able to use multiple font sheets. + static int texture; + static TexturePacker pack; + static int texture_width, texture_height; + static readonly StringFormat default_string_format = StringFormat.GenericTypographic; // Check the constructor, too, for additional flags. + static readonly StringFormat load_glyph_string_format = StringFormat.GenericDefault; + static SizeF maximum_graphics_size; + + int[] data = new int[256]; // Used to upload the glyph buffer to the OpenGL texture. + + object upload_lock = new object(); + + static readonly char[] newline_characters = new char[] { '\n', '\r' }; + + #region --- Constructor --- + + /// + /// Constructs a new TextureFont, using the specified System.Drawing.Font. + /// + /// The System.Drawing.Font to use. + public TextureFont(Font font) + { + if (font == null) + 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); + maximum_graphics_size = gfx.ClipBounds.Size; + + // Adjust font rendering mode. Small sizes look blurry without gridfitting, so turn + // that on. Increasing contrast also seems to help. + if (font.Size <= 18.0f) + { + gfx.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + //gfx.TextContrast = 11; + } + else + { + gfx.TextRenderingHint = TextRenderingHint.AntiAlias; + //gfx.TextContrast = 0; + } + + default_string_format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; + } + + /// + /// Constructs a new TextureFont, using the specified parameters. + /// + /// The System.Drawing.FontFamily to use for the typeface. + /// The em size to use for the typeface. + public TextureFont(FontFamily family, float emSize) + : this(new Font(family, emSize)) + { } + + /// + /// Constructs a new TextureFont, using the specified parameters. + /// + /// The System.Drawing.FontFamily to use for the typeface. + /// The em size to use for the typeface. + /// The style to use for the typeface. + public TextureFont(FontFamily family, float emSize, FontStyle style) + : this(new Font(family, emSize, style)) + { } + + #endregion + + #region --- Public Methods --- + + #region public void LoadGlyphs(string glyphs) + + /// + /// Prepares the specified glyphs for rendering. + /// + /// The glyphs to prepare for rendering. + public void LoadGlyphs(string glyphs) + { + RectangleF rect = new RectangleF(); + foreach (char c in glyphs) + { + if (Char.IsWhiteSpace(c)) + continue; + + try + { + if (!loaded_glyphs.ContainsKey(c)) + LoadGlyph(c, out rect); + } + catch (Exception e) + { + Debug.Print(e.ToString()); + throw; + } + } + } + + #endregion + + #region public void LoadGlyph(char glyph) + + /// + /// Prepares the specified glyph for rendering. + /// + /// The glyph to prepare for rendering. + public void LoadGlyph(char glyph) + { + RectangleF rect = new RectangleF(); + if (!loaded_glyphs.ContainsKey(glyph)) + LoadGlyph(glyph, out rect); + } + + #endregion + + #region public bool GlyphData(char glyph, out float width, out float height, out RectangleF textureRectangle, out int texture) + + /// + /// Returns the characteristics of a loaded glyph. + /// + /// The character corresponding to this glyph. + /// The width of this glyph. + /// The height of this glyph (line spacing). + /// The bounding box of the texture buffer of this glyph. + /// The handle to the texture that contains this glyph. + /// True if the glyph has been loaded, false otherwise. + /// + public bool GlyphData(char glyph, out float width, out float height, out RectangleF textureRectangle, 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 float Height + + /// + /// Gets a float indicating the default line spacing of this font. + /// + public float Height + { + get { return font.Height; } + } + + #endregion + + #region public float Width + + /// + /// Gets a float indicating the default size of this font, in points. + /// + public float Width + { + get { return font.SizeInPoints; } + } + + #endregion + + #region public void MeasureString(string str, out float width, out float height, bool accountForOverhangs) + + /// + /// Measures the width of the specified string. + /// + /// The string to measure. + /// The measured width. + /// The measured height. + /// If true, adds space to account for glyph overhangs. Set to true if you wish to measure a complete string. Set to false if you wish to perform layout on adjacent strings. + [Obsolete("Returns invalid results - use MeasureText() instead")] + public void MeasureString(string str, out float width, out float height, bool accountForOverhangs) + { + System.Drawing.StringFormat format = accountForOverhangs ? System.Drawing.StringFormat.GenericDefault : System.Drawing.StringFormat.GenericTypographic; + + System.Drawing.SizeF size = gfx.MeasureString(str, font, 16384, format); + height = size.Height; + width = size.Width; + } + + #endregion + + #region public void MeasureString(string str, out float width, out float height) + + /// + /// Measures the width of the specified string. + /// + /// The string to measure. + /// The measured width. + /// The measured height. + /// + [Obsolete("Returns invalid results - use MeasureText() instead")] + public void MeasureString(string str, out float width, out float height) + { + MeasureString(str, out width, out height, true); + } + + #endregion + + #region public RectangleF MeasureText(string text) + + /// + /// Calculates size information for the specified text. + /// + /// The string to measure. + /// A RectangleF containing the bounding box for the specified text. + public RectangleF MeasureText(string text) + { + return MeasureText(text, SizeF.Empty, default_string_format, null); + } + + #endregion + + #region public RectangleF MeasureText(string text, SizeF bounds) + + /// + /// Calculates size information for the specified text. + /// + /// The string to measure. + /// A SizeF structure containing the maximum desired width and height of the text. Default is SizeF.Empty. + /// A RectangleF containing the bounding box for the specified text. + public RectangleF MeasureText(string text, SizeF bounds) + { + return MeasureText(text, bounds, default_string_format, null); + } + + #endregion + + #region public RectangleF MeasureText(string text, SizeF bounds, StringFormat format) + + /// + /// Calculates size information for the specified text. + /// + /// The string to measure. + /// A SizeF structure containing the maximum desired width and height of the text. Pass SizeF.Empty to disable wrapping calculations. A width or height of 0 disables the relevant calculation. + /// A StringFormat object which specifies the measurement format of the string. Pass null to use the default StringFormat (StringFormat.GenericTypographic). + /// A RectangleF containing the bounding box for the specified text. + public RectangleF MeasureText(string text, SizeF bounds, StringFormat format) + { + return MeasureText(text, bounds, format, null); + } + + #endregion + + #region public RectangleF MeasureText(string text, SizeF bounds, StringFormat format, IList ranges) + + IntPtr[] regions = new IntPtr[GdiPlus.MaxMeasurableCharacterRanges]; + CharacterRange[] characterRanges = new CharacterRange[GdiPlus.MaxMeasurableCharacterRanges]; + + /// + /// Calculates size information for the specified text. + /// + /// The string to measure. + /// A SizeF structure containing the maximum desired width and height of the text. Pass SizeF.Empty to disable wrapping calculations. A width or height of 0 disables the relevant calculation. + /// A StringFormat object which specifies the measurement format of the string. Pass null to use the default StringFormat (StringFormat.GenericDefault). + /// Fills the specified IList of RectangleF structures with position information for individual characters. If this argument is null, these calculations are skipped. + /// A RectangleF containing the bounding box for the specified text. + public RectangleF MeasureText(string text, SizeF bounds, StringFormat format, List ranges) + { + if (String.IsNullOrEmpty(text)) + return RectangleF.Empty; + + if (bounds == SizeF.Empty) + bounds = maximum_graphics_size; + + if (format == null) + format = default_string_format; + + // TODO: What should we do in this case? + if (ranges == null) + ranges = new List(); + + ranges.Clear(); + + PointF origin = PointF.Empty; + SizeF size = SizeF.Empty; + + IntPtr native_graphics = GdiPlus.GetNativeGraphics(gfx); + IntPtr native_font = GdiPlus.GetNativeFont(font); + IntPtr native_string_format = GdiPlus.GetNativeStringFormat(format); + + RectangleF layoutRect = new RectangleF(PointF.Empty, bounds); + + int height = 0; + + // It seems that the mere presence of \n and \r characters + // is enough for Mono to botch the layout (even if these + // characters are not processed.) We'll need to find a + // different way to perform layout on Mono, probably + // through Pango. + // Todo: This workaround allocates memory. + //if (Configuration.RunningOnMono) + { + string[] lines = text.Replace("\r", String.Empty).Split(newline_characters); + foreach (string s in lines) + { + ranges.AddRange(GetCharExtents( + s, height, 0, s.Length, layoutRect, + native_graphics, native_font, native_string_format)); + height += font.Height; + } + } + + return new RectangleF(ranges[0].X, ranges[0].Y, ranges[ranges.Count - 1].Right, ranges[ranges.Count - 1].Bottom); + } + + #endregion + + #endregion + + #region --- Private Methods --- + + #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(TextureTarget.Texture2D, texture); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear); + if (GL.SupportsExtension("Version12")) + { + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge); + } + else + { + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.Clamp); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.Clamp); + } + + GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Alpha, texture_width, texture_height, 0, + OpenTK.Graphics.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); + } + + #endregion + + #region private void LoadGlyph(char c, out RectangleF rectangle) + + // Adds the specified caharacter to the texture packer. + private void LoadGlyph(char c, out RectangleF rectangle) + { + if (pack == null) + PrepareTexturePacker(); + + RectangleF glyph_rect = MeasureText(c.ToString(), SizeF.Empty, load_glyph_string_format); + SizeF glyph_size = new SizeF(glyph_rect.Right, glyph_rect.Bottom); // We need to do this, since the origin might not be (0, 0) + Glyph g = new Glyph(c, font, glyph_size); + Rectangle rect; + + try + { + pack.Add(g, out rect); + } + catch (InvalidOperationException expt) + { + // TODO: The TexturePacker is full, create a new font sheet. + Trace.WriteLine(expt); + throw; + } + + GL.BindTexture(TextureTarget.Texture2D, texture); + + gfx.Clear(System.Drawing.Color.Transparent); + gfx.DrawString(g.Character.ToString(), g.Font, System.Drawing.Brushes.White, 0.0f, 0.0f, default_string_format); + + BitmapData bitmap_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, + System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + GL.PushClientAttrib(ClientAttribMask.ClientPixelStoreBit); + try + { + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1.0f); + GL.PixelStore(PixelStoreParameter.UnpackRowLength, bmp.Width); + GL.TexSubImage2D(TextureTarget.Texture2D, 0, (int)rect.Left, (int)rect.Top, + rect.Width, rect.Height, + OpenTK.Graphics.PixelFormat.Rgba, + PixelType.UnsignedByte, bitmap_data.Scan0); + } + finally + { + GL.PopClientAttrib(); + } + + bmp.UnlockBits(bitmap_data); + + rectangle = 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, rectangle); + } + + #endregion + + #region GetCharExtents + + // Gets the bounds of each character in a line of text. + // The line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges). + IEnumerable GetCharExtents(string text, int height, int line_start, int line_length, + RectangleF layoutRect, IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format) + { + RectangleF rect = new RectangleF(); + int line_end = line_start + line_length; + while (line_start < line_end) + { + //if (text[line_start] == '\n' || text[line_start] == '\r') + //{ + // line_start++; + // continue; + //} + + int num_characters = (line_end - line_start) > GdiPlus.MaxMeasurableCharacterRanges ? + GdiPlus.MaxMeasurableCharacterRanges : + line_end - line_start; + int status = 0; + + for (int i = 0; i < num_characters; i++) + { + characterRanges[i] = new CharacterRange(line_start + i, 1); + + IntPtr region; + status = GdiPlus.CreateRegion(out region); + regions[i] = region; + Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); + } + + status = GdiPlus.SetStringFormatMeasurableCharacterRanges(native_string_format, num_characters, characterRanges); + Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); + + status = GdiPlus.MeasureCharacterRanges(native_graphics, text, text.Length, + native_font, ref layoutRect, native_string_format, num_characters, regions); + Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); + + for (int i = 0; i < num_characters; i++) + { + GdiPlus.GetRegionBounds(regions[i], native_graphics, ref rect); + Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); + GdiPlus.DeleteRegion(regions[i]); + Debug.Assert(status == 0, String.Format("GDI+ error: {0}", status)); + + rect.Y += height; + yield return rect; + } + + line_start += num_characters; + } + } + + #endregion + + #endregion + + #region --- Internal Methods --- + + #region internal int Texture + + /// + /// Gets the handle to the texture were this font resides. + /// + internal int Texture + { + get { return TextureFont.texture; } + } + + #endregion + + #endregion + + #region --- IDisposable Members --- + + bool disposed; + + /// + /// Releases all resources used by this OpenTK.Graphics.TextureFont. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + Dispose(true); + } + + private void Dispose(bool manual) + { + if (!disposed) + { + pack = null; + if (manual) + { + GL.DeleteTextures(1, ref texture); + font.Dispose(); + gfx.Dispose(); + } + + disposed = true; + } + } + + /// + /// Finalizes this TextureFont. + /// + ~TextureFont() + { + Dispose(false); + } + + #endregion + } +} diff --git a/Source/Compatibility/Fonts/VboTextPrinter.cs b/Source/Compatibility/Fonts/VboTextPrinter.cs new file mode 100644 index 00000000..15333ddb --- /dev/null +++ b/Source/Compatibility/Fonts/VboTextPrinter.cs @@ -0,0 +1,104 @@ +#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.Graphics.OpenGL; + +namespace OpenTK.Graphics +{ + /// + /// Provides text printing through OpenGL 1.5 vertex buffer objects. + /// + [Obsolete] + 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, int index_count) + { + VboTextHandle handle = new VboTextHandle(++allocated_handles); + + GL.GenBuffers(1, out handle.vbo_id); + GL.BindBuffer(BufferTarget.ArrayBuffer, handle.vbo_id); + GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * vector2_size), vertices, + BufferUsageHint.StaticDraw); + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + + GL.GenBuffers(1, out handle.ebo_id); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle.ebo_id); + GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(ushort)), indices, + BufferUsageHint.StaticDraw); + GL.BindBuffer(BufferTarget.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(BufferTarget.ArrayBuffer, vbo.vbo_id); + GL.BindBuffer(BufferTarget.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, DrawElementsType.UnsignedShort, IntPtr.Zero); + //GL.DrawArrays(BeginMode.LineLoop, 0, vbo.element_count); + + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); + + GL.DisableClientState(EnableCap.VertexArray); + //GL.DisableClientState(EnableCap.TextureCoordArray); + + //GL.PopClientAttrib(); + } + + public void Draw(Vector2[] vertices, ushort[] indices, int index_count) + { + throw new NotImplementedException(); + } + + #endregion + } + + #region class VboTextHandle : TextHandle + + /// + /// Contains the necessary information to print text through the VboTextPrinter implementation. + /// + [Obsolete] + 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 +} diff --git a/Source/Compatibility/IPackable.cs b/Source/Compatibility/IPackable.cs new file mode 100644 index 00000000..15b04931 --- /dev/null +++ b/Source/Compatibility/IPackable.cs @@ -0,0 +1,22 @@ +#region --- License --- +/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos + * See license.txt for license info + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK +{ + /// + /// Represents an item that can be packed with the TexturePacker. + /// + /// The type of the packable item. + interface IPackable : IEquatable + { + int Width { get; } + int Height { get; } + } +} diff --git a/Source/Compatibility/IPoolable.cs b/Source/Compatibility/IPoolable.cs new file mode 100644 index 00000000..68d83f36 --- /dev/null +++ b/Source/Compatibility/IPoolable.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK +{ + interface IPoolable : IDisposable + { + void OnAcquire(); + void OnRelease(); + } + + interface IPoolable : IPoolable where T : IPoolable, new() + { + ObjectPool Owner { get; set; } + } +} diff --git a/Source/Compatibility/ObjectPool.cs b/Source/Compatibility/ObjectPool.cs new file mode 100644 index 00000000..3ad38112 --- /dev/null +++ b/Source/Compatibility/ObjectPool.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK +{ + class ObjectPool where T : IPoolable, new() + { + Queue pool = new Queue(); + + public ObjectPool() + { } + + public T Acquire() + { + T item; + + if (pool.Count > 0) + { + item = pool.Dequeue(); + item.OnAcquire(); + } + else + { + item = new T(); + item.Owner = this; + item.OnAcquire(); + } + + return item; + } + + public void Release(T item) + { + if (item == null) + throw new ArgumentNullException("item"); + + item.OnRelease(); + pool.Enqueue(item); + } + } +} diff --git a/Source/OpenTK/Platform/GdiPlus.cs b/Source/Compatibility/Platform/GdiPlus.cs similarity index 100% rename from Source/OpenTK/Platform/GdiPlus.cs rename to Source/Compatibility/Platform/GdiPlus.cs diff --git a/Source/OpenTK/Platform/IGdiPlusInternals.cs b/Source/Compatibility/Platform/IGdiPlusInternals.cs similarity index 100% rename from Source/OpenTK/Platform/IGdiPlusInternals.cs rename to Source/Compatibility/Platform/IGdiPlusInternals.cs diff --git a/Source/OpenTK/Platform/Windows/WinGdiPlusInternals.cs b/Source/Compatibility/Platform/Windows/WinGdiPlusInternals.cs similarity index 100% rename from Source/OpenTK/Platform/Windows/WinGdiPlusInternals.cs rename to Source/Compatibility/Platform/Windows/WinGdiPlusInternals.cs diff --git a/Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs b/Source/Compatibility/Platform/X11/X11GdiPlusInternals.cs similarity index 100% rename from Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs rename to Source/Compatibility/Platform/X11/X11GdiPlusInternals.cs diff --git a/Source/Compatibility/TexturePacker.cs b/Source/Compatibility/TexturePacker.cs new file mode 100644 index 00000000..41be16a6 --- /dev/null +++ b/Source/Compatibility/TexturePacker.cs @@ -0,0 +1,195 @@ +#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; +using System.Drawing.Imaging; + +namespace OpenTK +{ + class TexturePacker where T : IPackable + { + Node root; + + #region --- Constructors --- + + public TexturePacker(int width, int height) + { + if (width <= 0) + throw new ArgumentOutOfRangeException("width", width, "Must be greater than zero."); + if (height <= 0) + throw new ArgumentOutOfRangeException("height", height, "Must be greater than zero."); + + root = new Node(); + root.Rect = new Rectangle(0, 0, width, width); + } + + #endregion + + #region --- Public Methods --- + + #region public Rectangle Add(T item) + + // Packs the given item into the free space of the TexturePacker. Returns the Rectangle of the packed item. + public void Add(T item, out Rectangle rect) + { + if (item.Width > root.Rect.Width || item.Height > root.Rect.Height) + throw new InvalidOperationException("The item is too large for this TexturePacker"); + + Node node; + //if (!items.ContainsKey(item)) + { + node = root.Insert(item); + + // Tree is full and insertion failed: + if (node == null) + throw new TexturePackerFullException(); + + //items.Add(item, node); + rect = node.Rect; + } + //throw new ArgumentException("The item already exists in the TexturePacker.", "item"); + } + + #endregion + + #region public void Clear() + + /// + /// Discards all packed items. + /// + public void Clear() + { + //items.Clear(); + root.Clear(); + } + + #endregion + + #region public void ChangeSize(int new_width, int new_height) + + /// + /// Changes the dimensions of the TexturePacker surface. + /// + /// The new width of the TexturePacker surface. + /// The new height of the TexturePacker surface. + /// Changing the size of the TexturePacker surface will implicitly call TexturePacker.Clear(). + /// + public void ChangeSize(int new_width, int new_height) + { + throw new NotImplementedException(); + } + + #endregion + + #endregion + + #region Node + + class Node + { + public Node() + { + } + + Node left, right; + Rectangle rect; + int use_count; + + public Rectangle Rect { get { return rect; } set { rect = value; } } + public Node Left { get { return left; } set { left = value; } } + public Node Right { get { return right; } set { right = value; } } + + #region --- Constructor --- + + public bool Leaf + { + get { return left == null && right == null; } + } + + #endregion + + #region public Node Insert(T item) + + public Node Insert(T item) + { + if (!this.Leaf) + { + // Recurse towards left child, and if that fails, towards the right. + Node new_node = left.Insert(item); + return new_node ?? right.Insert(item); + } + else + { + // We have recursed to a leaf. + + // If it is not empty go back. + if (use_count != 0) + return null; + + // If this leaf is too small go back. + if (rect.Width < item.Width || rect.Height < item.Height) + return null; + + // If this leaf is the right size, insert here. + if (rect.Width == item.Width && rect.Height == item.Height) + { + use_count = 1; + return this; + } + + // This leaf is too large, split it up. We'll decide which way to split + // by checking the width and height difference between this rectangle and + // out item's bounding box. If the width difference is larger, we'll split + // horizontaly, else verticaly. + left = new Node(); + right = new Node(); + + int dw = this.rect.Width - item.Width + 1; + int dh = this.rect.Height - item.Height + 1; + + if (dw > dh) + { + left.rect = new Rectangle(rect.Left, rect.Top, item.Width, rect.Height); + right.rect = new Rectangle(rect.Left + item.Width, rect.Top, rect.Width - item.Width, rect.Height); + } + else + { + left.rect = new Rectangle(rect.Left, rect.Top, rect.Width, item.Height); + right.rect = new Rectangle(rect.Left, rect.Top + item.Height, rect.Width, rect.Height - item.Height); + } + + return left.Insert(item); + } + } + + #endregion + + #region public void Clear() + + public void Clear() + { + if (left != null) + left.Clear(); + if (right != null) + right.Clear(); + + left = right = null; + } + + #endregion + } + + #endregion + } + + class TexturePackerFullException : Exception + { + public TexturePackerFullException() : base("There is not enough space to add this item. Consider calling the Clear() method.") { } + } +} diff --git a/Source/Utilities/Graphics/Texture2D.cs b/Source/Utilities/Graphics/Texture2D.cs index 5b46df5e..1d85c742 100644 --- a/Source/Utilities/Graphics/Texture2D.cs +++ b/Source/Utilities/Graphics/Texture2D.cs @@ -31,7 +31,6 @@ using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.Diagnostics; -using OpenTK.Graphics.OpenGL; namespace OpenTK.Graphics { @@ -137,7 +136,7 @@ namespace OpenTK.Graphics GL.TexSubImage2D(TextureTarget.Texture2D, mipLevel, target.Left, target.Top, target.Width, target.Height, - OpenTK.Graphics.OpenGL.PixelFormat.Bgra, + OpenTK.Graphics.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0); } finally @@ -159,7 +158,7 @@ namespace OpenTK.Graphics TextureRegion2D region = new TextureRegion2D(rect); - GL.GetTexImage(TextureTarget.Texture2D, mipLevel, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, region.Data); + GL.GetTexImage(TextureTarget.Texture2D, mipLevel, OpenTK.Graphics.PixelFormat.Bgra, PixelType.UnsignedByte, region.Data); return region; } @@ -223,7 +222,7 @@ namespace OpenTK.Graphics SetDefaultTextureParameters(id); GL.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat, Width, Height, 0, - OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); + OpenTK.Graphics.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); return id; }