diff --git a/Source/Examples/OpenGL/JuliaSetFractal.cs b/Source/Examples/OpenGL/JuliaSetFractal.cs
index 85951792..2ea7add4 100644
--- a/Source/Examples/OpenGL/JuliaSetFractal.cs
+++ b/Source/Examples/OpenGL/JuliaSetFractal.cs
@@ -164,30 +164,30 @@ namespace Examples.Tutorial
PixelType.UnsignedByte, data.Scan0);
bitmap.UnlockBits(data);
}
- #endregion Textures
-
+ #endregion Textures
+
Keyboard.KeyUp += KeyUp;
}
-
- int i = 0;
- void KeyUp(OpenTK.Input.KeyboardDevice sender, OpenTK.Input.Key e)
- {
- if (e == OpenTK.Input.Key.F12)
- {
+
+ int i = 0;
+ void KeyUp(OpenTK.Input.KeyboardDevice sender, OpenTK.Input.Key e)
+ {
+ if (e == OpenTK.Input.Key.F12)
+ {
Bitmap bmp = new Bitmap(this.Width, this.Height);
System.Drawing.Imaging.BitmapData data =
- bmp.LockBits(new Rectangle(0, 0, this.Width, this.Height),
+ bmp.LockBits(new Rectangle(0, 0, this.Width, this.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
- GL.ReadPixels(0, 0, this.Width, this.Height,
- OpenTK.Graphics.PixelFormat.Bgr,
+ GL.ReadPixels(0, 0, this.Width, this.Height,
+ OpenTK.Graphics.PixelFormat.Bgr,
OpenTK.Graphics.PixelType.UnsignedByte,
data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
- bmp.Save("Screenshot" + (i++).ToString() + ".png", ImageFormat.Png);
- }
- }
+ bmp.Save("Screenshot" + (i++).ToString() + ".png", ImageFormat.Png);
+ }
+ }
#endregion
@@ -241,7 +241,7 @@ namespace Examples.Tutorial
if (Keyboard[OpenTK.Input.Key.Escape])
{
this.Exit();
- }
+ }
}
#endregion
diff --git a/Source/Examples/Tests/MathSpeed.cs b/Source/Examples/Tests/MathSpeed.cs
index d4017abc..14efa5c6 100644
--- a/Source/Examples/Tests/MathSpeed.cs
+++ b/Source/Examples/Tests/MathSpeed.cs
@@ -16,7 +16,7 @@ namespace Examples.Tests
public class MathSpeed
{
public static void Main()
- {
+ {
/*
Stopwatch watch = new Stopwatch();
diff --git a/Source/Examples/Tutorial/Text.cs b/Source/Examples/Tutorial/Text.cs
index cabc7cf2..266779f5 100644
--- a/Source/Examples/Tutorial/Text.cs
+++ b/Source/Examples/Tutorial/Text.cs
@@ -23,10 +23,11 @@ namespace Examples.Tutorial
[Example("Text", ExampleCategory.Tutorial, 4)]
public class Text : GameWindow
{
- TextureFont serif = new TextureFont(new Font(FontFamily.GenericSerif, 24.0f));
+ Font serif2 = new Font(FontFamily.GenericSerif, 14.0f);
+ TextureFont serif = new TextureFont(new Font(FontFamily.GenericSerif, 12.0f));
TextureFont sans = new TextureFont(new Font(FontFamily.GenericSansSerif, 14.0f));
TextHandle poem_handle;
- ITextPrinter text = new TextPrinter();
+ TextPrinter text = new TextPrinter();
string poem = new StreamReader("Data/Poem.txt").ReadToEnd();
int lines; // How many lines the poem contains.
@@ -125,11 +126,14 @@ namespace Examples.Tutorial
// used in 2d graphics, and is necessary for achieving pixel-perfect glyph rendering.
// TextPrinter.End() restores your previous projection/modelview matrices.
text.Begin();
- GL.Color3(Color.LightBlue);
- text.Draw((1.0 / e.Time).ToString("F2"), sans);
+ //GL.Color3(Color.LightBlue);
+ //text.Draw((1.0 / e.Time).ToString("F2"), sans);
GL.Translate(0.0f, current_position, 0.0f);
GL.Color3(Color.White);
- text.Draw(poem_handle);
+ //text.Draw(poem_handle);
+ //text.Draw(poem, serif);
+ //GL.BindTexture(TextureTarget.Texture2D, 1);
+ text.Print(poem, serif2);
text.End();
SwapBuffers();
diff --git a/Source/Utilities/Fonts/Glyph.cs b/Source/Utilities/Fonts/Glyph.cs
index 25470c65..c64acfe5 100644
--- a/Source/Utilities/Fonts/Glyph.cs
+++ b/Source/Utilities/Fonts/Glyph.cs
@@ -111,12 +111,29 @@ namespace OpenTK.Graphics
/// A System.Int32 containing a hashcode that uniquely identifies this Glyph.
public override int GetHashCode()
{
- //return character.GetHashCode() ^ font.Style.GetHashCode() ^ font.Size.GetHashCode() ^ font.Unit.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 ---
@@ -154,7 +171,7 @@ namespace OpenTK.Graphics
/// 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;
+ return Character == other.Character && Font == other.Font && Size == other.Size;
}
#endregion
diff --git a/Source/Utilities/Fonts/TextPrinter.cs b/Source/Utilities/Fonts/TextPrinter.cs
index b59a5b0b..54613f94 100644
--- a/Source/Utilities/Fonts/TextPrinter.cs
+++ b/Source/Utilities/Fonts/TextPrinter.cs
@@ -12,11 +12,12 @@ using System.Text;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
+using System.Diagnostics;
using OpenTK.Math;
-using OpenTK.Graphics.OpenGL;
-using OpenTK.Graphics.OpenGL.Enums;
-using System.Diagnostics;
+using OpenTK.Graphics;
+using OpenTK.Graphics.Text;
+using OpenTK.Platform;
namespace OpenTK.Fonts { }
@@ -25,7 +26,7 @@ namespace OpenTK.Graphics
///
/// Provides methods to perform layout and print hardware accelerated text.
///
- public class TextPrinter : ITextPrinter
+ public sealed class TextPrinter : ITextPrinter
{
//static Regex break_point = new Regex("[ .,/*-+?\\!=]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
//static char[] split_chars = new char[]
@@ -42,11 +43,6 @@ namespace OpenTK.Graphics
#region --- Constructors ---
- ///
- /// Constructs a new TextPrinter object.
- ///
- public TextPrinter() { }
-
public TextPrinter(ITextPrinterImplementation implementation)
{
if (implementation == null)
@@ -359,5 +355,154 @@ namespace OpenTK.Graphics
#endregion
#endregion
+
+ #region New Implementation
+
+ #region Fields
+
+ Dictionary font_cache = new Dictionary();
+
+ GlyphCache glyph_cache;
+ IGlyphRasterizer glyph_rasterizer;
+ ITextOutputProvider text_output;
+
+ //TextExtents text_extents = new TextExtents();
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructs a new TextPrinter object.
+ ///
+ public TextPrinter()
+ : this(null, null)
+ {
+ }
+
+ TextPrinter(IGlyphRasterizer rasterizer, ITextOutputProvider output/*, IGlyphCacheProvider, ITextOutputProvider */)
+ {
+ if (rasterizer == null)
+ rasterizer = new GdiPlusGlyphRasterizer();
+
+ if (output == null)
+ output = new GL1TextOutputProvider();
+
+ glyph_rasterizer = rasterizer;
+ glyph_cache = new GlyphCache(rasterizer);
+ text_output = output;
+ }
+
+ #endregion
+
+ #region Public Members
+
+ #region Print
+
+ public void Print(string text, Font font)
+ {
+ Print(text, font, 0, RectangleF.Empty);
+ }
+
+ public void Print(string text, Font font, TextPrinterOptions options)
+ {
+ Print(text, font, options, RectangleF.Empty);
+ }
+
+ public void Print(string text, Font font, TextPrinterOptions options, RectangleF layoutRectangle)
+ {
+ if (String.IsNullOrEmpty(text))
+ return;
+
+ if (font == null)
+ throw new ArgumentNullException("font");
+
+ text_output.Print(new TextBlock(text, font, options, layoutRectangle), glyph_rasterizer, glyph_cache);
+
+ //glyph_rasterizer.MeasureText(text, font, options, layoutRectangle, ref text_extents);
+
+ //List vertices = new List();
+ //List indices = new List();
+ //PerformLayout(new TextBlock(text, font, layoutRectangle, options), text_extents, vertices, indices);
+
+ //GL.Begin(BeginMode.Triangles);
+ //foreach (int i in indices)
+ //{
+ // GL.TexCoord2(vertices[i + 1]);
+ // GL.Vertex2(vertices[i]);
+ //}
+ //GL.End();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Private Members
+
+ #region PerformLayout
+
+ void PerformLayout(TextBlock block, TextExtents extents, List vertices, List indices)
+ {
+ vertices.Clear();
+ vertices.Capacity = 4 * block.Text.Length;
+ indices.Clear();
+ indices.Capacity = 6 * block.Text.Length;
+
+ float x_pos = 0, y_pos = 0;
+ RectangleF rect = new RectangleF();
+ float char_width, char_height;
+
+ // Every character comprises of 4 vertices, forming two triangles. We generate an index array which
+ // indexes vertices in a triangle-list fashion.
+
+ int current = 0;
+ foreach (char c in block.Text)
+ {
+ if (c == '\n' || c == '\r')
+ continue;
+ else if (Char.IsWhiteSpace(c))
+ {
+ current++;
+ continue;
+ }
+ else if (!glyph_cache.Contains(c, block.Font))
+ glyph_cache.Add(c, block.Font);
+
+ //font.GlyphData(c, out char_width, out char_height, out rect, out texture);
+ CachedGlyphInfo cache_info = glyph_cache[c, block.Font];
+ RectangleF glyph_position = extents[current];
+
+ x_pos = glyph_position.X;
+ y_pos = glyph_position.Y;
+ char_width = glyph_position.Width;
+ char_height = glyph_position.Height;
+
+ // Interleaved array: Vertex, TexCoord, Vertex, ...
+ vertices.Add(new Vector2(x_pos, y_pos)); // Vertex
+ vertices.Add(new Vector2(rect.Left, rect.Top)); // Texcoord
+ vertices.Add(new Vector2(x_pos, y_pos + char_height));
+ vertices.Add(new Vector2(rect.Left, rect.Bottom));
+
+ vertices.Add(new Vector2(x_pos + char_width, y_pos + char_height));
+ vertices.Add(new Vector2(rect.Right, rect.Bottom));
+ vertices.Add(new Vector2(x_pos + char_width, y_pos));
+ vertices.Add(new Vector2(rect.Right, rect.Top));
+
+ indices.Add(vertices.Count - 8);
+ indices.Add(vertices.Count - 6);
+ indices.Add(vertices.Count - 4);
+
+ indices.Add(vertices.Count - 4);
+ indices.Add(vertices.Count - 2);
+ indices.Add(vertices.Count - 8);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
}
}
diff --git a/Source/Utilities/Fonts/TextPrinterOptions.cs b/Source/Utilities/Fonts/TextPrinterOptions.cs
new file mode 100644
index 00000000..55fb7dd0
--- /dev/null
+++ b/Source/Utilities/Fonts/TextPrinterOptions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenTK.Graphics
+{
+ [Flags]
+ public enum TextPrinterOptions
+ {
+ NoCache = 1,
+ RightToLeft = 2,
+ Vertical = 4,
+ }
+}
diff --git a/Source/Utilities/Graphics/CachedGlyphInfo.cs b/Source/Utilities/Graphics/CachedGlyphInfo.cs
new file mode 100644
index 00000000..e5154f18
--- /dev/null
+++ b/Source/Utilities/Graphics/CachedGlyphInfo.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics
+{
+ struct CachedGlyphInfo
+ {
+ public readonly AlphaTexture2D Texture;
+ public readonly RectangleF RectangleNormalized;
+ public Rectangle Rectangle
+ {
+ get
+ {
+ return new Rectangle(
+ (int)(RectangleNormalized.X * Texture.Width),
+ (int)(RectangleNormalized.Y * Texture.Height),
+ (int)(RectangleNormalized.Width * Texture.Width),
+ (int)(RectangleNormalized.Height * Texture.Height));
+ }
+ }
+
+ // Rect denotes the absolute position of the glyph in the texture [0, Texture.Width], [0, Texture.Height].
+ public CachedGlyphInfo(AlphaTexture2D texture, Rectangle rect)
+ {
+ Texture = texture;
+ RectangleNormalized = new RectangleF(
+ rect.X / (float)texture.Width,
+ rect.Y / (float)texture.Height,
+ rect.Width / (float)texture.Width,
+ rect.Height / (float)texture.Height);
+ }
+ }
+}
diff --git a/Source/Utilities/Graphics/GraphicsResourceException.cs b/Source/Utilities/Graphics/GraphicsResourceException.cs
new file mode 100644
index 00000000..8a7aa297
--- /dev/null
+++ b/Source/Utilities/Graphics/GraphicsResourceException.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenTK.Graphics
+{
+ ///
+ /// Represents exceptions related to IGraphicsResources.
+ ///
+ public class GraphicsResourceException : Exception
+ {
+ /// Constructs a new GraphicsResourceException.
+ public GraphicsResourceException() : base() { }
+ /// Constructs a new string with the specified error message.
+ public GraphicsResourceException(string message) : base(message) { }
+ }
+}
diff --git a/Source/Utilities/Graphics/IGraphicsResource.cs b/Source/Utilities/Graphics/IGraphicsResource.cs
new file mode 100644
index 00000000..7d5d4987
--- /dev/null
+++ b/Source/Utilities/Graphics/IGraphicsResource.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenTK.Graphics
+{
+ ///
+ /// Defines a common interface to all OpenGL resources.
+ ///
+ interface IGraphicsResource : IDisposable
+ {
+ ///
+ /// Gets the GraphicsContext that owns this resource.
+ ///
+ GraphicsContext Context { get; }
+
+ ///
+ /// Gets the Id of this IGraphicsResource.
+ ///
+ int Id { get; }
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs
new file mode 100644
index 00000000..5f00f8ea
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ class GL1TextOutputProvider : ITextOutputProvider
+ {
+ #region Constructors
+
+ public GL1TextOutputProvider() { }
+
+ #endregion
+
+ #region ITextOutputProvider Members
+
+ public void Print(TextBlock block, IGlyphRasterizer rasterizer, GlyphCache cache)
+ {
+ TextExtents extents = rasterizer.MeasureText(block);
+
+ foreach (char c in block.Text)
+ if (c != '\n' && c != '\r' && !Char.IsWhiteSpace(c) && !cache.Contains(c, block.Font))
+ cache.Add(c, block.Font);
+
+ GL.BindTexture(TextureTarget.Texture2D, 1);
+
+ //GL.Begin(BeginMode.Quads);
+
+ //GL.TexCoord2(0, 0);
+ //GL.Vertex2(0, 0);
+ //GL.TexCoord2(1, 0);
+ //GL.Vertex2(256, 0);
+ //GL.TexCoord2(1, 1);
+ //GL.Vertex2(256, 256);
+ //GL.TexCoord2(0, 1);
+ //GL.Vertex2(0, 256);
+
+ //GL.End();
+
+ //GL.Translate(0, 256, 0);
+
+ //GL.Disable(EnableCap.Texture2D);
+ //GL.BindTexture(TextureTarget.Texture2D, 1);
+ GL.Begin(BeginMode.Triangles);
+
+ int current = 0;
+ foreach (char c in block.Text)
+ {
+ if (c == '\n' || c == '\r')
+ continue;
+ else if (Char.IsWhiteSpace(c))
+ {
+ current++;
+ continue;
+ }
+
+ CachedGlyphInfo info = cache[c, block.Font];
+ RectangleF position = extents[current++];
+
+ position.Size = info.Rectangle.Size;
+
+ // Interleaved array: Vertex, TexCoord, Vertex, ...
+ GL.TexCoord2(info.RectangleNormalized.Left, info.RectangleNormalized.Top);
+ GL.Vertex2(position.Left, position.Top);
+ GL.TexCoord2(info.RectangleNormalized.Left, info.RectangleNormalized.Bottom);
+ GL.Vertex2(position.Left, position.Bottom);
+ GL.TexCoord2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom);
+ GL.Vertex2(position.Right, position.Bottom);
+
+ GL.TexCoord2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom);
+ GL.Vertex2(position.Right, position.Bottom);
+ GL.TexCoord2(info.RectangleNormalized.Right, info.RectangleNormalized.Top);
+ GL.Vertex2(position.Right, position.Top);
+ GL.TexCoord2(info.RectangleNormalized.Left, info.RectangleNormalized.Top);
+ GL.Vertex2(position.Left, position.Top);
+ }
+
+ GL.End();
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/Glyph.cs b/Source/Utilities/Graphics/Text/Glyph.cs
new file mode 100644
index 00000000..aea2403b
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/Glyph.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ struct Glyph : IEquatable
+ {
+ char character;
+ Font font;
+
+ #region Constructors
+
+ ///
+ /// Constructs a new Glyph that represents the given character and Font.
+ ///
+ /// The character to represent.
+ /// The Font of the character.
+ public Glyph(char c, Font font)
+ {
+ if (font == null)
+ throw new ArgumentNullException("font");
+ character = c;
+ this.font = font;
+ }
+
+ #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}", Character, Font.Name, font.Style, font.Size, font.Unit);
+ }
+
+ #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();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IEquatable Members
+
+ public bool Equals(Glyph other)
+ {
+ return Character == other.Character && Font == other.Font;
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/GlyphCache.cs b/Source/Utilities/Graphics/Text/GlyphCache.cs
new file mode 100644
index 00000000..e862df9e
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/GlyphCache.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+using OpenTK.Graphics;
+using System.Diagnostics;
+
+namespace OpenTK.Graphics.Text
+{
+ class GlyphCache
+ {
+ #region Fields
+
+ IGlyphRasterizer rasterizer;
+ List sheets = new List();
+ Bitmap bmp = new Bitmap(256, 256);
+
+ Dictionary cached_glyphs = new Dictionary();
+
+ #endregion
+
+ #region Constructors
+
+ public GlyphCache(IGlyphRasterizer rasterizer)
+ {
+ if (rasterizer == null)
+ throw new ArgumentNullException("rasterizer");
+
+ this.rasterizer = rasterizer;
+ sheets.Add(new GlyphSheet());
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public void Add(char c, Font font)
+ {
+ Glyph glyph = new Glyph(c, font);
+ bool inserted = false;
+
+ using (Bitmap bmp = rasterizer.Rasterize(glyph))
+ {
+ foreach (GlyphSheet sheet in sheets)
+ {
+ try
+ {
+ InsertGlyph(glyph, bmp, sheet);
+ inserted = true;
+ break;
+ }
+ catch (TexturePackerFullException)
+ {
+ }
+ }
+
+ if (!inserted)
+ {
+ GlyphSheet sheet = new GlyphSheet();
+ sheets.Add(sheet);
+ InsertGlyph(glyph, bmp, sheet);
+ }
+ }
+ }
+
+ void InsertGlyph(Glyph glyph, Bitmap bmp, GlyphSheet sheet)
+ {
+ Rectangle source = new Rectangle(0, 0, bmp.Width, bmp.Height);
+ Rectangle target = sheet.Packer.Add(source);
+
+ sheet.Texture.WriteRegion(source, target, 0, bmp);
+
+ cached_glyphs.Add(glyph, new CachedGlyphInfo(sheet.Texture, target));
+ }
+
+ public bool Contains(char c, Font font)
+ {
+ return cached_glyphs.ContainsKey(new Glyph(c, font));
+ }
+
+ public CachedGlyphInfo this[char c, Font font]
+ {
+ get
+ {
+ return cached_glyphs[new Glyph(c, font)];
+ }
+ }
+
+ public CachedGlyphInfo this[Glyph glyph]
+ {
+ get
+ {
+ return cached_glyphs[glyph];
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/GlyphPacker.cs b/Source/Utilities/Graphics/Text/GlyphPacker.cs
new file mode 100644
index 00000000..1a492aa5
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/GlyphPacker.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ class GlyphPacker
+ {
+ Node root;
+
+ #region --- Constructors ---
+
+ public GlyphPacker(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.Rectangle = new Rectangle(0, 0, width, width);
+ }
+
+ #endregion
+
+ #region --- Public Methods ---
+
+ #region public Rectangle Add(Rectangle boundingBox)
+
+ ///
+ /// Adds boundingBox to the GlyphPacker.
+ ///
+ /// The bounding box of the item to pack.
+ /// A System.Drawing.Rectangle containing the coordinates of the packed item.
+ /// Occurs if the item is larger than the available TexturePacker area
+ /// Occurs if the item already exists in the TexturePacker.
+ public Rectangle Add(Rectangle boundingBox)
+ {
+ if (!root.Rectangle.Contains(boundingBox))
+ throw new InvalidOperationException("The item is too large for this TexturePacker");
+
+ Node node;
+ node = root.Insert(boundingBox);
+
+ // Tree is full and insertion failed:
+ if (node == null)
+ throw new TexturePackerFullException();
+
+ return node.Rectangle;
+ }
+
+ #endregion
+
+ #region public Rectangle Add(RectangleF boundingBox)
+
+ ///
+ /// Rounds boundingBox to the largest integer and adds the resulting Rectangle to the GlyphPacker.
+ ///
+ /// The bounding box of the item to pack.
+ /// A System.Drawing.Rectangle containing the coordinates of the packed item.
+ /// Occurs if the item is larger than the available TexturePacker area
+ /// Occurs if the item already exists in the TexturePacker.
+ public Rectangle Add(RectangleF boundingBox)
+ {
+ Rectangle bbox = new Rectangle(
+ (int)boundingBox.X, (int)boundingBox.Y,
+ (int)(boundingBox.Width + 0.5f), (int)(boundingBox.Height + 0.5f));
+
+ return Add(bbox);
+ }
+
+ #endregion
+
+ #region public void Clear()
+
+ ///
+ /// Discards all packed items.
+ ///
+ public void 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;
+ bool occupied;
+
+ public Rectangle Rectangle { 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 Node Insert(Rectangle bbox)
+
+ public Node Insert( Rectangle bbox)
+ {
+ if (!this.Leaf)
+ {
+ // Recurse towards left child, and if that fails, towards the right.
+ Node new_node = left.Insert(bbox);
+ return new_node ?? right.Insert(bbox);
+ }
+ else
+ {
+ // We have recursed to a leaf.
+
+ // If it is not empty go back.
+ if (occupied)
+ return null;
+
+ // If this leaf is too small go back.
+ if (rect.Width < bbox.Width || rect.Height < bbox.Height)
+ return null;
+
+ // If this leaf is the right size, insert here.
+ if (rect.Width == bbox.Width && rect.Height == bbox.Height)
+ {
+ occupied = true;
+ 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 - bbox.Width + 1;
+ int dh = this.rect.Height - bbox.Height + 1;
+
+ if (dw > dh)
+ {
+ left.rect = new Rectangle(rect.Left, rect.Top, bbox.Width, rect.Height);
+ right.rect = new Rectangle(rect.Left + bbox.Width, rect.Top, rect.Width - bbox.Width, rect.Height);
+ }
+ else
+ {
+ left.rect = new Rectangle(rect.Left, rect.Top, rect.Width, bbox.Height);
+ right.rect = new Rectangle(rect.Left, rect.Top + bbox.Height, rect.Width, rect.Height - bbox.Height);
+ }
+
+ return left.Insert(bbox);
+ }
+ }
+
+ #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/Text/GlyphSheet.cs b/Source/Utilities/Graphics/Text/GlyphSheet.cs
new file mode 100644
index 00000000..1a709eb2
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/GlyphSheet.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ class GlyphSheet
+ {
+ #region Fields
+
+ AlphaTexture2D texture;
+ GlyphPacker packer;
+
+ #endregion
+
+ #region Constructors
+
+ public GlyphSheet()
+ {
+ Texture = new AlphaTexture2D(256, 256);
+ Packer = new GlyphPacker(256, 256);
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public AlphaTexture2D Texture
+ {
+ get { return texture; }
+ private set { texture = value; }
+ }
+
+ public GlyphPacker Packer
+ {
+ get { return packer; }
+ private set { packer = value; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs b/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs
new file mode 100644
index 00000000..f8b2d2e8
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+using OpenTK.Graphics.Text;
+
+namespace OpenTK.Graphics.Text
+{
+ interface IGlyphRasterizer
+ {
+ Bitmap Rasterize(Glyph glyph);
+ //void Rasterize(Glyph glyph, ref Bitmap bmp);
+ TextExtents MeasureText(TextBlock block);
+ RectangleF MeasureGlyph(Glyph glyph);
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/ITextOutputProvider.cs b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs
new file mode 100644
index 00000000..4090d0e8
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenTK.Graphics.Text
+{
+ interface ITextOutputProvider
+ {
+ void Print(TextBlock block, IGlyphRasterizer rasterizer, GlyphCache cache);
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/TextBlock.cs b/Source/Utilities/Graphics/Text/TextBlock.cs
new file mode 100644
index 00000000..ed235cec
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/TextBlock.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ // Uniquely identifies a block of text. This structure can be used to identify text blocks for caching.
+ struct TextBlock : IEquatable
+ {
+ #region Fields
+
+ public readonly string Text;
+
+ public readonly Font Font;
+
+ public readonly RectangleF LayoutRectangle;
+
+ public readonly TextPrinterOptions Options;
+
+ public int UsageCount; // Used to identify old and unused blocks of text.
+
+ #endregion
+
+ #region Constructors
+
+ public TextBlock(string text, Font font, TextPrinterOptions options, RectangleF layoutRectangle)
+ {
+ Text = text;
+ Font = font;
+ LayoutRectangle = layoutRectangle;
+ Options = options;
+ UsageCount = 0;
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is TextBlock))
+ return false;
+
+ return Equals((TextBlock)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Text.GetHashCode() ^ Font.GetHashCode() ^ LayoutRectangle.GetHashCode() ^ Options.GetHashCode();
+ }
+
+ #endregion
+
+ #region IEquatable Members
+
+ public bool Equals(TextBlock other)
+ {
+ return
+ Text == other.Text &&
+ Font == other.Font &&
+ LayoutRectangle == other.LayoutRectangle &&
+ Options == other.Options;
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/TextBlockComparer.cs b/Source/Utilities/Graphics/Text/TextBlockComparer.cs
new file mode 100644
index 00000000..524ba106
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/TextBlockComparer.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace OpenTK.Graphics.Text
+{
+ class TextBlockComparer : IComparer
+ {
+ #region Constructors
+
+ public TextBlockComparer() { }
+
+ #endregion
+
+ #region IComparer Members
+
+ public int Compare(TextBlock x, TextBlock y)
+ {
+ return x.UsageCount.CompareTo(y.UsageCount);
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/Text/TextExtents.cs b/Source/Utilities/Graphics/Text/TextExtents.cs
new file mode 100644
index 00000000..98ae31bc
--- /dev/null
+++ b/Source/Utilities/Graphics/Text/TextExtents.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics.Text
+{
+ // Holds layout information about a TextBlock.
+ public struct TextExtents
+ {
+ #region Fields
+
+ RectangleF text_extents;
+ List glyph_extents;
+
+ #endregion
+
+ #region Constructors
+
+ public TextExtents(RectangleF bbox)
+ : this(bbox, null)
+ {
+ }
+
+ public TextExtents(RectangleF bbox, IEnumerable glyphExtents)
+ : this()
+ {
+ BoundingBox = bbox;
+
+ if (glyphExtents != null)
+ AddRange(glyphExtents);
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public RectangleF BoundingBox
+ {
+ get { return text_extents; }
+ internal set { text_extents = value; }
+ }
+
+ public RectangleF this[int i]
+ {
+ get { return (GlyphExtents as List)[i]; }
+ internal set { (GlyphExtents as List)[i] = value; }
+ }
+
+ public IEnumerable GlyphExtents
+ {
+ get
+ {
+ if (glyph_extents == null)
+ glyph_extents = new List();
+ return (IEnumerable)glyph_extents;
+ }
+ }
+
+ public int Count
+ {
+ get { return (GlyphExtents as List).Count; }
+ }
+
+ #endregion
+
+ #region Internal Members
+
+ internal void Add(RectangleF glyphExtent)
+ {
+ (GlyphExtents as List).Add(glyphExtent);
+ }
+
+ internal void AddRange(IEnumerable glyphExtents)
+ {
+ (GlyphExtents as List).AddRange(glyphExtents);
+ }
+
+ internal void Clear()
+ {
+ BoundingBox = RectangleF.Empty;
+ (GlyphExtents as List).Clear();
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/Graphics/TextureRegion2D.cs b/Source/Utilities/Graphics/TextureRegion2D.cs
new file mode 100644
index 00000000..d44969f4
--- /dev/null
+++ b/Source/Utilities/Graphics/TextureRegion2D.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace OpenTK.Graphics
+{
+ abstract class TextureRegion2D
+ {
+ Rectangle rectangle;
+
+ public Rectangle Rectangle { get { return rectangle; } protected set { rectangle = value; } }
+ }
+
+ ///
+ /// Holds part or the whole of a 2d OpenGL texture.
+ ///
+ class TextureRegion2D : TextureRegion2D where T : struct
+ {
+ #region Fields
+
+ T[,] data;
+
+ #endregion
+
+ #region Constructors
+
+ internal TextureRegion2D(Rectangle rect)
+ {
+ data = new T[rect.Width, rect.Height];
+ Rectangle = rect;
+ }
+
+ #endregion
+
+ #region Public Members
+
+ public T this[int x, int y]
+ {
+ get { return data[x, y]; }
+ set { data[x, y] = value; }
+ }
+
+ #endregion
+
+ #region Internal Members
+
+ internal T[,] Data { get { return data; } }
+
+ #endregion
+ }
+}
diff --git a/Source/Utilities/TexturePacker.cs b/Source/Utilities/TexturePacker.cs
index ec44aa55..acffc12e 100644
--- a/Source/Utilities/TexturePacker.cs
+++ b/Source/Utilities/TexturePacker.cs
@@ -54,7 +54,7 @@ namespace OpenTK
// Tree is full and insertion failed:
if (node == null)
- throw new InvalidOperationException("There is not enough space to add this item. Consider calling the Clear() method.");
+ throw new TexturePackerFullException();
//items.Add(item, node);
rect = node.Rect;
@@ -193,4 +193,9 @@ namespace OpenTK
#endregion
}
+
+ class TexturePackerFullException : Exception
+ {
+ public TexturePackerFullException() : base("There is not enough space to add this item. Consider calling the Clear() method.") { }
+ }
}