diff --git a/Documentation/Changelog.txt b/Documentation/Changelog.txt index 432684fb..0020f4ce 100644 --- a/Documentation/Changelog.txt +++ b/Documentation/Changelog.txt @@ -6,10 +6,31 @@ OpenTK 0.9.1 -> 0.9.2 + OpenTK + Platform + Added GdiPlus bindings. - + + Fixed a bug where the KeypardEnter key was reported as Enter on X11. + + Changed the Time X11 struct from int to IntPtr. + + Improved GLControl implementations. + + Graphics + + Added Color4 struct that can hold floating-point ARGB colors. + + Added several new overloads to the GL class. Most deal with OpenTK.Math and System.Drawing.Color interoperation. + + Math + + Added half and double precision structures. + + Added missing ref overloads. + + All structs now implement the IEquatable and ISerializable interfaces. + + General + + Fixed build warnings. + + Eliminated per-frame memory allocations in release builds. + + Utilities + Fonts + Updated the layout code to use the new GdiPlus bindings. + + Added support for near-, far- and center-aligned text. + + Added support for right-to-left and vertical text (not 100% complete). + + Added support for subpixel antialiasing (requires GL 1.2 or higher). + ++ Examples + + Improved ExampleLauncher behavior on recent Mono/Linux releases. + + Switched to the new TextPrinter implementation. + + Changed background color from SteelBlue to MidnightBlue. --------------------- OpenTK 0.9.0 -> 0.9.1 diff --git a/Source/Examples/ExampleLauncher.cs b/Source/Examples/ExampleLauncher.cs index 3bfbe189..5a761c04 100644 --- a/Source/Examples/ExampleLauncher.cs +++ b/Source/Examples/ExampleLauncher.cs @@ -247,7 +247,7 @@ namespace Examples //fileIO.Demand(); Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(true); + Application.SetCompatibleTextRenderingDefault(false); using (Form exampleLauncher = new ExampleLauncher()) { Application.Run(exampleLauncher); diff --git a/Source/Examples/OpenGL/FrameBufferObject.cs b/Source/Examples/OpenGL/FrameBufferObject.cs index c6ded8f4..c8c38e0e 100644 --- a/Source/Examples/OpenGL/FrameBufferObject.cs +++ b/Source/Examples/OpenGL/FrameBufferObject.cs @@ -8,6 +8,7 @@ using System; using System.Diagnostics; +using System.Drawing; using OpenTK; using OpenTK.Input; @@ -25,7 +26,7 @@ namespace Examples.Tutorial { } - TextureFont sans = new TextureFont(new System.Drawing.Font(System.Drawing.FontFamily.GenericSansSerif, 14.0f)); + Font sans = new Font(System.Drawing.FontFamily.GenericSansSerif, 16.0f); ITextPrinter text = new TextPrinter(); uint ColorTexture; @@ -236,7 +237,7 @@ namespace Examples.Tutorial GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); text.Begin(); - text.Draw((1.0 / e.Time).ToString("F2"), sans); + text.Print((1.0 / e.Time).ToString("F2"), sans, Color.White); text.End(); GL.PushMatrix(); diff --git a/Source/Examples/OpenGL/GluTessellation.cs b/Source/Examples/OpenGL/GluTessellation.cs index 5192532a..110d85dd 100644 --- a/Source/Examples/OpenGL/GluTessellation.cs +++ b/Source/Examples/OpenGL/GluTessellation.cs @@ -168,7 +168,7 @@ namespace Examples new double[] {400.0, 150.0, 0.0, 0.0, 1.0, 0.0} }; - GL.ClearColor(System.Drawing.Color.SteelBlue); + GL.ClearColor(System.Drawing.Color.MidnightBlue); tess = Glu.NewTess(); startList = GL.GenLists(3); diff --git a/Source/Examples/OpenGL/JuliaSetFractal.cs b/Source/Examples/OpenGL/JuliaSetFractal.cs index 85951792..e157fe21 100644 --- a/Source/Examples/OpenGL/JuliaSetFractal.cs +++ b/Source/Examples/OpenGL/JuliaSetFractal.cs @@ -55,7 +55,7 @@ namespace Examples.Tutorial // Text drawing (for fps) TextPrinter printer = new TextPrinter(); - TextureFont font = new TextureFont(new Font(FontFamily.GenericSansSerif, 14.0f)); + Font font = new Font(FontFamily.GenericSansSerif, 16.0f); #endregion private Fields @@ -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 @@ -289,8 +289,7 @@ namespace Examples.Tutorial // Then, render the fps: GL.UseProgram(0); printer.Begin(); - GL.Color3(Color.PaleGoldenrod); - printer.Draw((1 / e.Time).ToString("F2"), font); + printer.Print((1 / e.Time).ToString("F2"), font, Color.PaleGoldenrod, RectangleF.Empty, TextPrinterOptions.NoCache); printer.End(); SwapBuffers(); diff --git a/Source/Examples/Tests/GameWindowStates.cs b/Source/Examples/Tests/GameWindowStates.cs index 5d81a28d..c722c478 100644 --- a/Source/Examples/Tests/GameWindowStates.cs +++ b/Source/Examples/Tests/GameWindowStates.cs @@ -20,7 +20,7 @@ namespace Examples.Tests [Example("GameWindow states", ExampleCategory.Test)] public class GameWindowStates : GameWindow { - TextureFont font = new TextureFont(new Font(FontFamily.GenericSansSerif, 16.0f)); + Font font = new Font(FontFamily.GenericSansSerif, 16.0f); TextPrinter printer = new TextPrinter(); #region GetNext and GetPrevious methods for enums. @@ -67,7 +67,7 @@ namespace Examples.Tests this.Keyboard.KeyRepeat = true; this.Keyboard.KeyUp += new OpenTK.Input.KeyUpEvent(Keyboard_KeyUp); - GL.ClearColor(System.Drawing.Color.SteelBlue); + GL.ClearColor(System.Drawing.Color.MidnightBlue); } void Keyboard_KeyUp(KeyboardDevice sender, Key key) @@ -118,13 +118,14 @@ namespace Examples.Tests printer.Begin(); - printer.Draw("Instructions:", font); GL.Translate(0, font.Height, 0); - printer.Draw(String.Format("1 - cycle through window styles (current: {0}).", this.WindowState), font); + printer.Print("Instructions:", font, Color.White); GL.Translate(0, font.Height, 0); - printer.Draw(String.Format("2 - cycle through window borders (current: {0}).", this.WindowBorder), font); + printer.Print(String.Format("1 - cycle through window styles (current: {0}).", this.WindowState), font, Color.White, RectangleF.Empty); GL.Translate(0, font.Height, 0); - printer.Draw(String.Format("3 - toggle fullscreen (current: {0}).", - this.WindowState == WindowState.Fullscreen ? "enabled" : "disabled"), font); + printer.Print(String.Format("2 - cycle through window borders (current: {0}).", this.WindowBorder), font, Color.White, RectangleF.Empty); + GL.Translate(0, font.Height, 0); + printer.Print(String.Format("3 - toggle fullscreen (current: {0}).", + this.WindowState == WindowState.Fullscreen ? "enabled" : "disabled"), font, Color.White, RectangleF.Empty); printer.End(); 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/Fonts.cs b/Source/Examples/Tutorial/Fonts.cs index 0113dd9c..02f3608b 100644 --- a/Source/Examples/Tutorial/Fonts.cs +++ b/Source/Examples/Tutorial/Fonts.cs @@ -33,52 +33,50 @@ namespace Examples.Tutorial ITextPrinter printer = new TextPrinter(); const string text = "Hello, world!"; - TextHandle[] handles; // Used to cache the strings we want to print. - // Load some different TextureFont sizes to compare their quality. // You'll never need to load that many fonts in your application, // 3 or 4 should be more than enough. - TextureFont[] fonts = new TextureFont[] + Font[] fonts = new Font[] { - new TextureFont(new Font(FontFamily.GenericSerif, 8.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 10.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 12.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 14.0f)), + new Font(FontFamily.GenericSerif, 8.0f), + new Font(FontFamily.GenericSerif, 10.0f), + new Font(FontFamily.GenericSerif, 12.0f), + new Font(FontFamily.GenericSerif, 14.0f), - new TextureFont(new Font(FontFamily.GenericSerif, 16.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 18.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 20.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 22.0f)), + new Font(FontFamily.GenericSerif, 16.0f), + new Font(FontFamily.GenericSerif, 18.0f), + new Font(FontFamily.GenericSerif, 20.0f), + new Font(FontFamily.GenericSerif, 22.0f), - new TextureFont(new Font(FontFamily.GenericSerif, 24.0f, FontStyle.Bold)), - new TextureFont(new Font(FontFamily.GenericSerif, 26.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 28.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 30.0f)), + new Font(FontFamily.GenericSerif, 24.0f, FontStyle.Bold), + new Font(FontFamily.GenericSerif, 26.0f), + new Font(FontFamily.GenericSerif, 28.0f), + new Font(FontFamily.GenericSerif, 30.0f), - new TextureFont(new Font(FontFamily.GenericSerif, 32.0f, FontStyle.Italic)), - new TextureFont(new Font(FontFamily.GenericSerif, 34.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 36.0f)), - new TextureFont(new Font(FontFamily.GenericSerif, 38.0f)), + new Font(FontFamily.GenericSerif, 32.0f, FontStyle.Italic), + new Font(FontFamily.GenericSerif, 34.0f), + new Font(FontFamily.GenericSerif, 36.0f), + new Font(FontFamily.GenericSerif, 38.0f), - new TextureFont(new Font(FontFamily.GenericSansSerif, 8.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 10.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 12.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 14.0f)), + new Font(FontFamily.GenericSansSerif, 8.0f), + new Font(FontFamily.GenericSansSerif, 10.0f), + new Font(FontFamily.GenericSansSerif, 12.0f), + new Font(FontFamily.GenericSansSerif, 14.0f), - new TextureFont(new Font(FontFamily.GenericSansSerif, 16.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 18.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 20.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 22.0f)), + new Font(FontFamily.GenericSansSerif, 16.0f), + new Font(FontFamily.GenericSansSerif, 18.0f), + new Font(FontFamily.GenericSansSerif, 20.0f), + new Font(FontFamily.GenericSansSerif, 22.0f), - new TextureFont(new Font(FontFamily.GenericSansSerif, 24.0f, FontStyle.Bold)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 26.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 28.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 30.0f)), + new Font(FontFamily.GenericSansSerif, 24.0f, FontStyle.Bold), + new Font(FontFamily.GenericSansSerif, 26.0f), + new Font(FontFamily.GenericSansSerif, 28.0f), + new Font(FontFamily.GenericSansSerif, 30.0f), - new TextureFont(new Font(FontFamily.GenericSansSerif, 32.0f, FontStyle.Italic)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 34.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 36.0f)), - new TextureFont(new Font(FontFamily.GenericSansSerif, 38.0f)), + new Font(FontFamily.GenericSansSerif, 32.0f, FontStyle.Italic), + new Font(FontFamily.GenericSansSerif, 34.0f), + new Font(FontFamily.GenericSansSerif, 36.0f), + new Font(FontFamily.GenericSansSerif, 38.0f), }; #endregion @@ -98,11 +96,7 @@ namespace Examples.Tutorial /// public override void OnLoad(EventArgs e) { - GL.ClearColor(Color.SteelBlue); - - handles = new TextHandle[fonts.Length]; - for (int i = handles.Length; --i >= 0; ) - printer.Prepare(text, fonts[i], out handles[i]); + GL.ClearColor(Color.MidnightBlue); } #endregion @@ -117,9 +111,7 @@ namespace Examples.Tutorial /// public override void OnUnload(EventArgs e) { - foreach (TextHandle h in handles) - h.Dispose(); - foreach (TextureFont f in fonts) + foreach (Font f in fonts) f.Dispose(); } @@ -168,21 +160,21 @@ namespace Examples.Tutorial printer.Begin(); // Print using the first font. - for (int i = 0; i < handles.Length / 2; i++) + for (int i = 0; i < fonts.Length / 2; i++) { - printer.Draw(handles[i]); + printer.Print(text, fonts[i], Color.White); GL.Translate(0, fonts[i].Height, 0); } // Move to the right, and print using the second font. //float width, height; - //fonts[handles.Length / 2 - 1].MeasureString(text, out width, out height); - RectangleF rect = fonts[handles.Length / 2 - 1].MeasureText(text); + //fonts[handles.Length / 2 - 1].MeasureString(text, out width, out height); + RectangleF rect = printer.Measure(text, fonts[fonts.Length / 2 - 1]).BoundingBox; GL.LoadIdentity(); GL.Translate(rect.Width + 32.0f, 0, 0); - for (int i = handles.Length / 2; i < handles.Length; i++) + for (int i = fonts.Length / 2; i < fonts.Length; i++) { - printer.Draw(handles[i]); + printer.Print(text, fonts[i], Color.White); GL.Translate(0, fonts[i].Height, 0); } diff --git a/Source/Examples/Tutorial/T01_Simple_Window.cs b/Source/Examples/Tutorial/T01_Simple_Window.cs index bbd100b3..94d7df22 100644 --- a/Source/Examples/Tutorial/T01_Simple_Window.cs +++ b/Source/Examples/Tutorial/T01_Simple_Window.cs @@ -58,7 +58,7 @@ namespace Examples.Tutorial /// Not used. public override void OnLoad(EventArgs e) { - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); } #endregion @@ -110,7 +110,7 @@ namespace Examples.Tutorial GL.Begin(BeginMode.Triangles); - GL.Color3(Color.LightSteelBlue); + GL.Color3(Color.MidnightBlue); GL.Vertex2(-1.0f, 1.0f); GL.Color3(Color.SpringGreen); GL.Vertex2(0.0f, -1.0f); diff --git a/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs b/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs index ae6c9e46..bba719bb 100644 --- a/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs +++ b/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs @@ -14,7 +14,7 @@ using System.Windows.Forms; using System.Threading; using System.Drawing; -using OpenTK; +using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; @@ -48,7 +48,7 @@ namespace Examples.Tutorial { base.OnLoad(e); - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.DepthTest); } diff --git a/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs b/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs index 178de914..dcce0415 100644 --- a/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs +++ b/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs @@ -45,7 +45,7 @@ namespace Examples.Tutorial public override void OnLoad(EventArgs e) { - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.DepthTest); GL.MatrixMode(MatrixMode.Modelview); diff --git a/Source/Examples/Tutorial/T10_GLSL_Cube.cs b/Source/Examples/Tutorial/T10_GLSL_Cube.cs index 0b36314c..06c94beb 100644 --- a/Source/Examples/Tutorial/T10_GLSL_Cube.cs +++ b/Source/Examples/Tutorial/T10_GLSL_Cube.cs @@ -68,7 +68,7 @@ namespace Examples.Tutorial this.Exit(); } - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.DepthTest); CreateVBO(); diff --git a/Source/Examples/Tutorial/Text.cs b/Source/Examples/Tutorial/Text.cs index cabc7cf2..3dc9925e 100644 --- a/Source/Examples/Tutorial/Text.cs +++ b/Source/Examples/Tutorial/Text.cs @@ -23,10 +23,9 @@ namespace Examples.Tutorial [Example("Text", ExampleCategory.Tutorial, 4)] public class Text : GameWindow { - TextureFont serif = new TextureFont(new Font(FontFamily.GenericSerif, 24.0f)); - TextureFont sans = new TextureFont(new Font(FontFamily.GenericSansSerif, 14.0f)); - TextHandle poem_handle; - ITextPrinter text = new TextPrinter(); + Font serif = new Font(FontFamily.GenericSerif, 16.0f); + Font sans = new Font(FontFamily.GenericSansSerif, 18.0f); + TextPrinter text = new TextPrinter(); string poem = new StreamReader("Data/Poem.txt").ReadToEnd(); int lines; // How many lines the poem contains. @@ -44,11 +43,10 @@ namespace Examples.Tutorial public override void OnLoad(EventArgs e) { - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); current_position = initial_position; scroll_speed = -1.0f; - text.Prepare(poem, serif, out poem_handle); // Count the amount of lines in the text, to find out the correct // warparound position. We want the text to scroll until the last @@ -58,8 +56,7 @@ namespace Examples.Tutorial if (c == '\n') lines++; - warparound_position = - -(lines + 1) * serif.Height; + warparound_position = -(lines + 1) * serif.Height; } #endregion @@ -68,8 +65,10 @@ namespace Examples.Tutorial public override void OnUnload(EventArgs e) { - if (poem_handle != null) poem_handle.Dispose(); - if (serif != null) serif.Dispose(); + if (serif != null) + serif.Dispose(); + if (sans != null) + sans.Dispose(); } #endregion @@ -81,9 +80,7 @@ namespace Examples.Tutorial GL.Viewport(0, 0, Width, Height); initial_position = Height + serif.Height; // Start one line below the screen. - - warparound_position = - -(lines + 1) * serif.Height; + warparound_position = -(lines + 1) * serif.Height; } #endregion @@ -93,11 +90,11 @@ namespace Examples.Tutorial public override void OnUpdateFrame(UpdateFrameEventArgs e) { if (Keyboard[Key.Space]) - scroll_speed = 0.0f; + scroll_speed = 0; if (Keyboard[Key.Down]) - scroll_speed += 1; + scroll_speed += 10; if (Keyboard[Key.Up]) - scroll_speed -= 1; + scroll_speed -= 10; if (Keyboard[Key.Escape]) this.Exit(); } @@ -113,7 +110,7 @@ namespace Examples.Tutorial // We'll start printing from the lower left corner of the screen. The text // will slowly move updwards - the user can control the movement speed with // the keyboard arrows and the space bar. - current_position += scroll_speed * (float)e.ScaleFactor; + current_position += scroll_speed * (float)e.Time; if (scroll_speed > 0.0f && current_position > initial_position) current_position = warparound_position; else if (scroll_speed < 0.0f && current_position < warparound_position) @@ -125,13 +122,18 @@ 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.Translate(0.0f, current_position, 0.0f); - GL.Color3(Color.White); - text.Draw(poem_handle); + + // Print FPS counter. Since the counter changes per frame, + // it shouldn't be cached (TextPrinterOptions.NoCache). + text.Print((1.0 / e.Time).ToString("F2"), sans, Color.SpringGreen, new RectangleF(0, 0, Width, 0), TextPrinterOptions.NoCache, TextAlignment.Far); + + // Print the actual text. + GL.Translate(0, current_position, 0); + text.Print(poem, serif, Color.White, new RectangleF(Width / 2, 0, Width / 2, 0), TextPrinterOptions.Default, TextAlignment.Far); + text.Print(poem, serif, Color.White, new RectangleF(0, 0, Width / 2, 0)); + text.End(); - + SwapBuffers(); } diff --git a/Source/Examples/Tutorial/Textures.cs b/Source/Examples/Tutorial/Textures.cs index 45674714..3c3a1586 100644 --- a/Source/Examples/Tutorial/Textures.cs +++ b/Source/Examples/Tutorial/Textures.cs @@ -39,7 +39,7 @@ namespace Examples.Tutorial /// Not used. public override void OnLoad(EventArgs e) { - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.Texture2D); GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest); diff --git a/Source/Examples/WinForms/FontRendering.Designer.cs b/Source/Examples/WinForms/FontRendering.Designer.cs new file mode 100644 index 00000000..24bef8f1 --- /dev/null +++ b/Source/Examples/WinForms/FontRendering.Designer.cs @@ -0,0 +1,96 @@ +namespace Examples.WinForms +{ + partial class FontRendering + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.glControl1 = new OpenTK.GLControl(); + this.changeFont = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.fontDialog = new System.Windows.Forms.FontDialog(); + this.SuspendLayout(); + // + // glControl1 + // + this.glControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.glControl1.BackColor = System.Drawing.Color.Black; + this.glControl1.Location = new System.Drawing.Point(0, 40); + this.glControl1.Name = "glControl1"; + this.glControl1.Size = new System.Drawing.Size(700, 521); + this.glControl1.TabIndex = 0; + this.glControl1.VSync = false; + this.glControl1.Load += new System.EventHandler(this.glControl1_Load); + this.glControl1.Paint += new System.Windows.Forms.PaintEventHandler(this.glControl1_Paint); + this.glControl1.Resize += new System.EventHandler(this.glControl1_Resize); + // + // changeFont + // + this.changeFont.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.changeFont.Location = new System.Drawing.Point(568, 10); + this.changeFont.Name = "changeFont"; + this.changeFont.Size = new System.Drawing.Size(120, 23); + this.changeFont.TabIndex = 1; + this.changeFont.Text = "Change Font"; + this.changeFont.UseVisualStyleBackColor = true; + this.changeFont.Click += new System.EventHandler(this.changeFont_Click); + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(12, 11); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(550, 22); + this.textBox1.TabIndex = 2; + this.textBox1.Text = "The quick brown fox jumped over the lazy dogs. 0123456789"; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // FontRendering + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(700, 561); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.changeFont); + this.Controls.Add(this.glControl1); + this.Name = "FontRendering"; + this.Text = "FontRendering"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private OpenTK.GLControl glControl1; + private System.Windows.Forms.Button changeFont; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.FontDialog fontDialog; + } +} \ No newline at end of file diff --git a/Source/Examples/WinForms/FontRendering.cs b/Source/Examples/WinForms/FontRendering.cs new file mode 100644 index 00000000..e4e66356 --- /dev/null +++ b/Source/Examples/WinForms/FontRendering.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using OpenTK.Graphics; + +namespace Examples.WinForms +{ + [Example("Font rendering sample", ExampleCategory.WinForms)] + public partial class FontRendering : Form + { + #region Fields + + //float[] sizes = new float[] { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 28, 32, 36, 42, 48 }; + float[] sizes = new float[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 24 }; + List fonts = new List(); + + TextPrinter printer = new TextPrinter(); + + #endregion + + #region Constructors + + public FontRendering() + { + InitializeComponent(); + ResizeRedraw = true; + + UpdateFontList(fontDialog.Font); + glControl1_Resize(this, EventArgs.Empty); + } + + #endregion + + #region Private Members + + void UpdateFontList(Font base_font) + { + printer.Clear(); + + foreach (Font font in fonts) + font.Dispose(); + fonts.Clear(); + foreach (float size in sizes) + fonts.Add(new Font(base_font.Name, base_font.SizeInPoints + size, base_font.Style)); + } + + #endregion + + #region Events + + private void glControl1_Load(object sender, EventArgs e) + { + glControl1.MakeCurrent(); + } + + private void changeFont_Click(object sender, EventArgs e) + { + if (fontDialog.ShowDialog() == DialogResult.OK) + { + UpdateFontList(fontDialog.Font); + glControl1.Invalidate(); + } + } + + private void textBox1_TextChanged(object sender, EventArgs e) + { + glControl1.Invalidate(); + } + + private void glControl1_Paint(object sender, PaintEventArgs e) + { + glControl1.MakeCurrent(); + GL.ClearColor(Color.MidnightBlue); + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.MatrixMode(MatrixMode.Projection); + GL.LoadIdentity(); + GL.Ortho(0, glControl1.ClientSize.Width, glControl1.ClientSize.Height, 0, -1, 1); + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + + foreach (Font font in fonts) + { + printer.Print(textBox1.Text, font, Color.White); + GL.Translate(0, font.Height + 5, 0); + } + + glControl1.SwapBuffers(); + } + + private void glControl1_Resize(object sender, EventArgs e) + { + glControl1.MakeCurrent(); + + if (glControl1.ClientSize.Height == 0) + glControl1.ClientSize = new System.Drawing.Size(glControl1.ClientSize.Width, 1); + + GL.Viewport(0, 0, glControl1.ClientSize.Width, glControl1.ClientSize.Height); + } + + #endregion + + #region public static void Main() + + /// + /// Entry point of this example. + /// + [STAThread] + public static void Main() + { + using (FontRendering example = new FontRendering()) + { + Utilities.SetWindowTitle(example); + example.ShowDialog(); + } + } + + #endregion + } +} diff --git a/Source/Examples/WinForms/FontRendering.resx b/Source/Examples/WinForms/FontRendering.resx new file mode 100644 index 00000000..11e8cf35 --- /dev/null +++ b/Source/Examples/WinForms/FontRendering.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/Examples/WinForms/W01_First_Window.cs b/Source/Examples/WinForms/W01_First_Window.cs index 022192d2..e4d545b8 100644 --- a/Source/Examples/WinForms/W01_First_Window.cs +++ b/Source/Examples/WinForms/W01_First_Window.cs @@ -59,6 +59,8 @@ namespace Examples.WinForms private void glControl1_Paint(object sender, PaintEventArgs e) { + glControl1.MakeCurrent(); + GL.Clear(ClearBufferMask.ColorBufferBit); glControl1.SwapBuffers(); } diff --git a/Source/Examples/WinForms/W02_Immediate_Mode_Cube.cs b/Source/Examples/WinForms/W02_Immediate_Mode_Cube.cs index da4ecd22..04673145 100644 --- a/Source/Examples/WinForms/W02_Immediate_Mode_Cube.cs +++ b/Source/Examples/WinForms/W02_Immediate_Mode_Cube.cs @@ -53,7 +53,7 @@ namespace Examples.WinForms GL.GetString(StringName.Renderer) + " " + GL.GetString(StringName.Version); - GL.ClearColor(Color.SteelBlue); + GL.ClearColor(Color.MidnightBlue); GL.Enable(EnableCap.DepthTest); Application.Idle += Application_Idle; diff --git a/Source/OpenTK/Configuration.cs b/Source/OpenTK/Configuration.cs index 7acee9ea..729e7242 100644 --- a/Source/OpenTK/Configuration.cs +++ b/Source/OpenTK/Configuration.cs @@ -62,6 +62,10 @@ namespace OpenTK Type t = Type.GetType("Mono.Runtime"); if (t != null) runningOnMono = true; + + Debug.Print("Detected configuration: {0} / {1}", + RunningOnWindows ? "Windows" : RunningOnLinux ? "Linux" : RunningOnOSX ? "MacOS" : RunningOnX11 ? "X11" : "Unknown Platform", + RunningOnMono ? "Mono" : ".Net"); } #endregion diff --git a/Source/OpenTK/Graphics/GL/GLHelper.cs b/Source/OpenTK/Graphics/GL/GLHelper.cs index 9a6100dc..e9bc5798 100644 --- a/Source/OpenTK/Graphics/GL/GLHelper.cs +++ b/Source/OpenTK/Graphics/GL/GLHelper.cs @@ -659,6 +659,15 @@ namespace OpenTK.Graphics #endregion + #region public static void BlendColor() overloads + + public static void BlendColor(System.Drawing.Color color) + { + GL.BlendColor(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f); + } + + #endregion + #region public static void Material() overloads public static void Materialv(MaterialFace face, MaterialParameter pname, Vector4 @params) diff --git a/Source/OpenTK/Platform/GdiPlus.cs b/Source/OpenTK/Platform/GdiPlus.cs index 24538796..16a1df42 100644 --- a/Source/OpenTK/Platform/GdiPlus.cs +++ b/Source/OpenTK/Platform/GdiPlus.cs @@ -26,10 +26,10 @@ namespace OpenTK.Platform static GdiPlus() { - if (Configuration.RunningOnWindows) + if (Configuration.RunningOnWindows && !Configuration.RunningOnMono) internals = new Windows.WinGdiPlusInternals(); else - internals = new X11.X11GdiPlusInternals(); + internals = new X11.X11GdiPlusInternals(); // This class is Mono-specific and works on all platforms. } #endregion diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index d55282d0..9a89dbd0 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -470,11 +470,8 @@ namespace OpenTK.Platform.Windows #region SwapBuffers - /// - /// - /// - /// - [DllImport("gdi32.dll")] + [SuppressUnmanagedCodeSecurity] + [DllImport("gdi32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool SwapBuffers(IntPtr dc); @@ -1433,8 +1430,8 @@ namespace OpenTK.Platform.Windows #endregion #region PixelFormatDescriptor - - #pragma warning disable 0169 + + #pragma warning disable 0169 /// /// Describes a pixel format. It is used when interfacing with the WINAPI to create a new Context. @@ -1469,8 +1466,8 @@ namespace OpenTK.Platform.Windows internal int LayerMask; internal int VisibleMask; internal int DamageMask; - } - + } + #pragma warning restore 0169 #endregion diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 1bab8d18..95912c0d 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -140,8 +140,8 @@ namespace OpenTK.Platform.Windows public void SwapBuffers() { - - Functions.SwapBuffers(currentWindow.DeviceContext); + if (!Functions.SwapBuffers(currentWindow.DeviceContext)) + Debug.Print("SwapBuffers failed, error: {0}", Marshal.GetLastWin32Error()); } #endregion diff --git a/Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs b/Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs index b197517a..45407b8d 100644 --- a/Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs +++ b/Source/OpenTK/Platform/X11/X11GdiPlusInternals.cs @@ -14,6 +14,8 @@ using System.Reflection; namespace OpenTK.Platform.X11 { + // Note: This class is Mono-specific, not X11-specific! + // It works on all platforms (windows, linux, macos) as long as we are running on Mono. class X11GdiPlusInternals : IGdiPlusInternals { static readonly PropertyInfo native_graphics_property, native_font_property, native_string_format_property; diff --git a/Source/Utilities/Fonts/DisplayListTextHandle.cs b/Source/Utilities/Fonts/DisplayListTextHandle.cs index 192c8c7d..c1a00ebf 100644 --- a/Source/Utilities/Fonts/DisplayListTextHandle.cs +++ b/Source/Utilities/Fonts/DisplayListTextHandle.cs @@ -11,6 +11,7 @@ using OpenTK.Graphics.OpenGL; namespace OpenTK.Graphics { + [Obsolete()] class DisplayListTextHandle : TextHandle { public DisplayListTextHandle(int handle) : base(handle) { } diff --git a/Source/Utilities/Fonts/DisplayListTextPrinter.cs b/Source/Utilities/Fonts/DisplayListTextPrinter.cs index 5958dc35..835f9f1a 100644 --- a/Source/Utilities/Fonts/DisplayListTextPrinter.cs +++ b/Source/Utilities/Fonts/DisplayListTextPrinter.cs @@ -16,6 +16,7 @@ namespace OpenTK.Graphics /// /// Provides text printing through OpenGL 1.1 Display Lists. /// + [Obsolete()] class DisplayListTextPrinter : ITextPrinterImplementation { #region IPrinter Members diff --git a/Source/Utilities/Fonts/Glyph.cs b/Source/Utilities/Fonts/Glyph.cs index 25470c65..d124e2cc 100644 --- a/Source/Utilities/Fonts/Glyph.cs +++ b/Source/Utilities/Fonts/Glyph.cs @@ -16,6 +16,7 @@ namespace OpenTK.Graphics /// /// Represents a single character of a specific Font. /// + [Obsolete] struct Glyph : IPackable { char character; @@ -111,12 +112,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 +172,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/IPrinterImplementation.cs b/Source/Utilities/Fonts/IPrinterImplementation.cs index c849ed1f..0f901f10 100644 --- a/Source/Utilities/Fonts/IPrinterImplementation.cs +++ b/Source/Utilities/Fonts/IPrinterImplementation.cs @@ -15,6 +15,7 @@ namespace OpenTK.Graphics /// /// Defines the interface for TextPrinter implementations. /// + [Obsolete("Use ITextOutputProvider instead")] public interface ITextPrinterImplementation { /// diff --git a/Source/Utilities/Fonts/ITextPrinter.cs b/Source/Utilities/Fonts/ITextPrinter.cs deleted file mode 100644 index 00193ca7..00000000 --- a/Source/Utilities/Fonts/ITextPrinter.cs +++ /dev/null @@ -1,28 +0,0 @@ -#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 -{ - /// - /// Defines the interface for a TextPrinter. - /// - public interface ITextPrinter - { - void Prepare(string text, TextureFont font, out TextHandle handle); - void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp); - void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment); - void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment, bool rightToLeft); - void Draw(TextHandle handle); - void Draw(string text, TextureFont font); - void Begin(); - void End(); - } -} diff --git a/Source/Utilities/Fonts/TextHandle.cs b/Source/Utilities/Fonts/TextHandle.cs index fdb48f7b..321ff14a 100644 --- a/Source/Utilities/Fonts/TextHandle.cs +++ b/Source/Utilities/Fonts/TextHandle.cs @@ -13,17 +13,27 @@ namespace OpenTK.Graphics /// /// Represents a handle to cached text. /// - public abstract class TextHandle : IDisposable + [Obsolete("Use TextPrinter.Print instead")] + public class TextHandle : IDisposable { + internal string Text; + internal System.Drawing.Font GdiPFont; + /// /// Constructs a new TextHandle, /// /// - public TextHandle(int handle) + 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; diff --git a/Source/Utilities/Fonts/TextPrinter.cs b/Source/Utilities/Fonts/TextPrinter.cs deleted file mode 100644 index b59a5b0b..00000000 --- a/Source/Utilities/Fonts/TextPrinter.cs +++ /dev/null @@ -1,363 +0,0 @@ -#region --- License --- -/* Licensed under the MIT/X11 license. - * Copyright (c) 2006-2008 the OpenTK Team. - * This notice may not be removed from any source distribution. - * See license.txt for licensing details. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Text; -using System.Drawing; -using System.Text.RegularExpressions; -using System.Runtime.InteropServices; - -using OpenTK.Math; -using OpenTK.Graphics.OpenGL; -using OpenTK.Graphics.OpenGL.Enums; -using System.Diagnostics; - -namespace OpenTK.Fonts { } - -namespace OpenTK.Graphics -{ - /// - /// Provides methods to perform layout and print hardware accelerated text. - /// - public class TextPrinter : ITextPrinter - { - //static Regex break_point = new Regex("[ .,/*-+?\\!=]", RegexOptions.Compiled | RegexOptions.IgnoreCase); - //static char[] split_chars = new char[] - //{ - // ' ', '\n', '\t', ',', '.', '/', '?', '!', ';', '\\', '-', '+', '*', '=' - //}; - static ITextPrinterImplementation printer; - float[] viewport = new float[4]; - // Interleaved, vertex, texcoord, vertex, etc... Starts with 8 chars, will expand as needed. - Vector2[] vertices = new Vector2[8 * 8]; - ushort[] indices = new ushort[6 * 8]; - List ranges = new List(); - - - #region --- Constructors --- - - /// - /// Constructs a new TextPrinter object. - /// - public TextPrinter() { } - - public TextPrinter(ITextPrinterImplementation implementation) - { - if (implementation == null) - throw new ArgumentNullException("implementation"); - printer = implementation; - } - - #endregion - - #region --- Private Members --- - - #region static ITextPrinterImplementation Printer - - /// - /// Checks the machine's capabilities and selects the fastest method to print text. - /// - static ITextPrinterImplementation Printer - { - get - { - if (printer == null) - { - - printer = (ITextPrinterImplementation)new DisplayListTextPrinter(); - //GL.SupportsExtension("VERSION_1_5") ? - //(ITextPrinterImplementation)new VboTextPrinter() : - //GL.SupportsExtension("ARB_vertex_buffer_object") ? null : - //GL.SupportsExtension("VERSION_1_1") ? null : null; - if (printer == null) - throw new NotSupportedException("TextPrinter requires at least OpenGL 1.1 support."); - - Debug.Print("Using {0} for font printing.", printer); - } - return printer; - } - } - - #endregion - - #endregion - - #region --- ITextPrinter Members --- - - #region public void Prepare(string text, TextureFont font, out TextHandle handle) - - /// - /// Prepares text for drawing. - /// - /// The string to draw. - /// The font to use for drawing. - /// The handle to the cached text. Use this to draw the text with the Draw() function. - /// - public void Prepare(string text, TextureFont font, out TextHandle handle) - { - this.Prepare(text, font, out handle, 0, false, StringAlignment.Near, false); - } - - #endregion - - #region public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp) - - /// - /// Prepares text for drawing. - /// - /// The string to draw. - /// The font to use for drawing. - /// The handle to the cached text. Use this to draw the text with the Draw() function. - /// Not implemented. - /// Not implemented. - /// - public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp) - { - this.Prepare(text, font, out handle, width, wordWarp, StringAlignment.Near, false); - } - - #endregion - - #region public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment) - - /// - /// Prepares text for drawing. - /// - /// The string to draw. - /// The font to use for drawing. - /// The handle to the cached text. Use this to draw the text with the Draw() function. - /// Not implemented. - /// Not implemented. - /// Not implemented. - /// - public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment) - { - this.Prepare(text, font, out handle, width, wordWarp, alignment, false); - } - - #endregion - - #region public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment, bool rightToLeft) - - /// - /// Prepares text for drawing. - /// - /// The string to draw. - /// The font to use for drawing. - /// The handle to the cached text. Use this to draw the text with the Draw() function. - /// Not implemented. - /// Not implemented. - /// Not implemented. - /// Not implemented. - /// - /// Occurs when OpenGL 1.1 is not supported. - public void Prepare(string text, TextureFont font, out TextHandle handle, float width, bool wordWarp, StringAlignment alignment, bool rightToLeft) - { - int num_indices; - - PerformLayout(text, font, width, wordWarp, alignment, rightToLeft, ref vertices, ref indices, out num_indices); - - handle = Printer.Load(vertices, indices, num_indices); - handle.Font = font; - } - - #endregion - - #region void PerformLayout(string text, TextureFont font, float width, bool wordWarp, StringAlignment alignment, bool rightToLeft, ref Vector2[] vertices, ref ushort[] indices, out int num_indices) - - // Performs layout on the given string. - void PerformLayout(string text, TextureFont font, float width, bool wordWarp, StringAlignment alignment, - bool rightToLeft, ref Vector2[] vertices, ref ushort[] indices, out int num_indices) - { - if (text == null) - throw new ArgumentNullException("Parameter cannot be null.", "text"); - - if (text.Length > 8192) - throw new ArgumentOutOfRangeException("text", text.Length, "Text length must be between 1 and 8192 characters"); - - if (wordWarp || rightToLeft || alignment != StringAlignment.Near) - throw new NotImplementedException(); - - if (8 * text.Length > vertices.Length) - vertices = new Vector2[Math.Functions.NextPowerOfTwo(8 * text.Length)]; - - if (6 * text.Length > indices.Length) - indices = new ushort[Math.Functions.NextPowerOfTwo(6 * text.Length)]; - - num_indices = 6 * text.Length; - - //Vector2[] vertices = new Vector2[8 * text.Length]; // Interleaved, vertex, texcoord, vertex, etc... - //ushort[] indices = new ushort[6 * text.Length]; - float x_pos = 0, y_pos = 0; - ushort i = 0, index_count = 0, vertex_count = 0; - RectangleF rect = new RectangleF(); - float char_width, char_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-list fashion. - // This algorithm works for left-to-right scripts. - - if (alignment == StringAlignment.Near && !rightToLeft || alignment == StringAlignment.Far && rightToLeft) - { - font.MeasureText(text, SizeF.Empty, null, ranges); - - int current = 0; - - foreach (char c in text) - { - if (c == '\n' || c == '\r') - continue; - else if (Char.IsWhiteSpace(c)) - { - current++; - continue; - } - - RectangleF range = ranges[current++]; - - x_pos = range.X; - y_pos = range.Y; - - font.GlyphData(c, out char_width, out char_height, out rect, out texture); - - vertices[vertex_count].X = x_pos; // Vertex - vertices[vertex_count++].Y = y_pos; - vertices[vertex_count].X = rect.Left; // Texcoord - vertices[vertex_count++].Y = rect.Top; - vertices[vertex_count].X = x_pos; // Vertex - vertices[vertex_count++].Y = y_pos + char_height; - vertices[vertex_count].X = rect.Left; // Texcoord - vertices[vertex_count++].Y = rect.Bottom; - - vertices[vertex_count].X = x_pos + char_width; // Vertex - vertices[vertex_count++].Y = y_pos + char_height; - vertices[vertex_count].X = rect.Right; // Texcoord - vertices[vertex_count++].Y = rect.Bottom; - vertices[vertex_count].X = x_pos + char_width; // Vertex - vertices[vertex_count++].Y = y_pos; - vertices[vertex_count].X = rect.Right; // Texcoord - vertices[vertex_count++].Y = rect.Top; - - indices[index_count++] = (ushort)(vertex_count - 8); - indices[index_count++] = (ushort)(vertex_count - 6); - indices[index_count++] = (ushort)(vertex_count - 4); - - indices[index_count++] = (ushort)(vertex_count - 4); - indices[index_count++] = (ushort)(vertex_count - 2); - indices[index_count++] = (ushort)(vertex_count - 8); - - ++i; - } - } - else if (alignment != StringAlignment.Center) - { - throw new NotImplementedException("This feature is not yet implemented. Sorry for the inconvenience."); - } - else - { - throw new NotImplementedException("This feature is not yet implemented. Sorry for the inconvenience."); - } - } - - #endregion - - #region public void Draw(TextHandle handle) - - /// - /// Draws the cached text referred to by the TextHandle. - /// - /// The TextHandle to the cached text. - public void Draw(TextHandle handle) - { - GL.BindTexture(TextureTarget.Texture2D, handle.Font.Texture); - - Printer.Draw(handle); - } - - #endregion - - #region public void Draw(string text, TextureFont font) - - /// - /// Draws dynamic text without caching. Not implemented yet! - /// - /// The System.String to draw. - /// The OpenTK.Graphics.TextureFont to draw the text in. - public void Draw(string text, TextureFont font) - { - int num_indices; - PerformLayout(text, font, 0, false, StringAlignment.Near, false, ref vertices, ref indices, out num_indices); - - GL.BindTexture(TextureTarget.Texture2D, font.Texture); - - Printer.Draw(vertices, indices, num_indices); - } - - #endregion - - #region public void Begin() - - /// - /// Sets up OpenGL state for drawing text. - /// - public void Begin() - { - if (GraphicsContext.CurrentContext == null) - throw new GraphicsContextException("No GraphicsContext is current in the calling thread."); - - GL.GetFloat(GetPName.Viewport, viewport); - - // Prepare to draw text. We want pixel perfect precision, so we setup a 2D mode, - // with size equal to the window (in pixels). - // While we could also render text in 3D mode, it would be very hard to get - // pixel-perfect precision. - GL.MatrixMode(MatrixMode.Projection); - GL.PushMatrix(); - GL.LoadIdentity(); - GL.Ortho(viewport[0], viewport[2], viewport[3], viewport[1], -1.0, 1.0); - - GL.MatrixMode(MatrixMode.Modelview); - GL.PushMatrix(); - GL.LoadIdentity(); - - GL.PushAttrib(AttribMask.TextureBit | AttribMask.EnableBit | AttribMask.ColorBufferBit); - - GL.Enable(EnableCap.Texture2D); - GL.Enable(EnableCap.Blend); - GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); - - GL.Disable(EnableCap.DepthTest); - } - - #endregion - - #region public void End() - - /// - /// Restores OpenGL state. - /// - public void End() - { - GL.PopAttrib(); - - GL.MatrixMode(MatrixMode.Modelview); - GL.PopMatrix(); - - GL.MatrixMode(MatrixMode.Projection); - GL.PopMatrix(); - } - - #endregion - - #endregion - } -} diff --git a/Source/Utilities/Fonts/TextureFont.cs b/Source/Utilities/Fonts/TextureFont.cs index 6d30ce9f..dee839de 100644 --- a/Source/Utilities/Fonts/TextureFont.cs +++ b/Source/Utilities/Fonts/TextureFont.cs @@ -21,11 +21,12 @@ namespace OpenTK.Graphics { using Graphics = System.Drawing.Graphics; using PixelFormat = OpenTK.Graphics.PixelFormat; -using System.Text.RegularExpressions; + using System.Text.RegularExpressions; + [Obsolete("Use System.Drawing.Font instead")] public class TextureFont : IFont { - Font font; + internal Font font; Dictionary loaded_glyphs = new Dictionary(64); Bitmap bmp; diff --git a/Source/Utilities/Fonts/VboTextPrinter.cs b/Source/Utilities/Fonts/VboTextPrinter.cs index 3c4a5731..f6c37a0a 100644 --- a/Source/Utilities/Fonts/VboTextPrinter.cs +++ b/Source/Utilities/Fonts/VboTextPrinter.cs @@ -17,6 +17,7 @@ namespace OpenTK.Graphics /// /// Provides text printing through OpenGL 1.5 vertex buffer objects. /// + [Obsolete] class VboTextPrinter : ITextPrinterImplementation { static int allocated_handles; @@ -84,6 +85,7 @@ namespace OpenTK.Graphics /// /// Contains the necessary information to print text through the VboTextPrinter implementation. /// + [Obsolete] class VboTextHandle : TextHandle { public VboTextHandle(int handle) : base(handle) { } diff --git a/Source/Utilities/Graphics/AlphaTexture2D.cs b/Source/Utilities/Graphics/AlphaTexture2D.cs new file mode 100644 index 00000000..959f75cd --- /dev/null +++ b/Source/Utilities/Graphics/AlphaTexture2D.cs @@ -0,0 +1,61 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; + +namespace OpenTK.Graphics +{ + /// + /// Encapsulates an OpenGL texture. + /// + class AlphaTexture2D : Texture2D + { + #region Constructors + + /// + /// Constructs a new Texture. + /// + public AlphaTexture2D(int width, int height) + : base(width, height) + { } + + #endregion + + #region Protected Members + + protected override PixelInternalFormat InternalFormat + { + get { return PixelInternalFormat.Alpha; } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/GraphicsResourceException.cs b/Source/Utilities/Graphics/GraphicsResourceException.cs new file mode 100644 index 00000000..a1232dbc --- /dev/null +++ b/Source/Utilities/Graphics/GraphicsResourceException.cs @@ -0,0 +1,44 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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..0bebdb1c --- /dev/null +++ b/Source/Utilities/Graphics/IGraphicsResource.cs @@ -0,0 +1,49 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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/ITextPrinter.cs b/Source/Utilities/Graphics/ITextPrinter.cs new file mode 100644 index 00000000..e5cb68dc --- /dev/null +++ b/Source/Utilities/Graphics/ITextPrinter.cs @@ -0,0 +1,162 @@ +#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 OpenTK.Graphics.Text; + +namespace OpenTK.Graphics +{ + /// + /// Defines the interface for a TextPrinter. + /// + public interface ITextPrinter : IDisposable + { + #region Print + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + void Print(string text, Font font, Color color); + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + void Print(string text, Font font, Color color, RectangleF rect); + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options); + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + /// The OpenTK.Graphics.TextAlignment that will be used to print text. + void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options, TextAlignment alignment); + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + /// The OpenTK.Graphics.TextAlignment that will be used to print text. + /// The OpenTK.Graphics.TextDirection that will be used to print text. + void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options, TextAlignment alignment, TextDirection direction); + + #endregion + + #region Measure + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + TextExtents Measure(string text, Font font); + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + TextExtents Measure(string text, Font font, RectangleF rect); + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// The OpenTK.Graphics.TextAlignment that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options); + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// The OpenTK.Graphics.TextAlignment that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options, TextAlignment alignment); + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// The OpenTK.Graphics.TextAlignment that will be used to measure text. + /// The OpenTK.Graphics.TextDirection that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options, TextAlignment alignment, TextDirection direction); + + #endregion + + #region Begin + + /// + /// Sets up a resolution-dependent orthographic projection. + /// + void Begin(); + + #endregion + + /// + /// Restores the projection and modelview matrices to their previous state. + /// + #region End + + void End(); + + #endregion + + #region Obsolete + + [Obsolete("Use TextPrinter.Print instead")] + void Draw(TextHandle handle); + + [Obsolete("Use TextPrinter.Print instead")] + void Draw(string text, TextureFont font); + + [Obsolete("Use TextPrinter.Print instead")] + void Prepare(string text, TextureFont font, out TextHandle handle); + + #endregion + } +} diff --git a/Source/Utilities/Graphics/RgbaTexture2D.cs b/Source/Utilities/Graphics/RgbaTexture2D.cs new file mode 100644 index 00000000..8cc32c5a --- /dev/null +++ b/Source/Utilities/Graphics/RgbaTexture2D.cs @@ -0,0 +1,45 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + class RgbaTexture2D : Texture2D + { + public RgbaTexture2D(int width, int height) + : base(width, height) + { } + + protected override PixelInternalFormat InternalFormat + { + get { return PixelInternalFormat.Rgba; } + } + } +} diff --git a/Source/Utilities/Graphics/Text/CachedGlyphInfo.cs b/Source/Utilities/Graphics/Text/CachedGlyphInfo.cs new file mode 100644 index 00000000..b9bbf62b --- /dev/null +++ b/Source/Utilities/Graphics/Text/CachedGlyphInfo.cs @@ -0,0 +1,62 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + struct CachedGlyphInfo + { + public readonly Texture2D 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(Texture2D 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/Text/GL11TextOutputProvider.cs b/Source/Utilities/Graphics/Text/GL11TextOutputProvider.cs new file mode 100644 index 00000000..c0686134 --- /dev/null +++ b/Source/Utilities/Graphics/Text/GL11TextOutputProvider.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + sealed class GL11TextOutputProvider : GL1TextOutputProvider + { + #region Fields + + TextQuality quality; + GlyphCache cache; + + #endregion + + #region Constuctors + + public GL11TextOutputProvider(TextQuality quality) + { + if (quality == TextQuality.High || quality == TextQuality.Default) + this.quality = TextQuality.Medium; + else + this.quality = quality; + + cache = new GlyphCache(); + } + + #endregion + + #region Protected Members + + protected override void SetBlendFunction() + { + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); // For grayscale + } + + protected override void SetColor(Color color) + { + GL.Color3(color); + } + + protected override TextQuality TextQuality + { + get { return quality; } + } + + protected override GlyphCache Cache + { + get { return cache; } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/GL12TextOutputProvider.cs b/Source/Utilities/Graphics/Text/GL12TextOutputProvider.cs new file mode 100644 index 00000000..3942f87c --- /dev/null +++ b/Source/Utilities/Graphics/Text/GL12TextOutputProvider.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + sealed class GL12TextOutputProvider : GL1TextOutputProvider + { + #region Fields + + TextQuality quality; + GlyphCache cache; + + #endregion + + #region Constuctors + + public GL12TextOutputProvider(TextQuality quality) + { + this.quality = quality; + + cache = new GlyphCache(); + } + + #endregion + + protected override void SetBlendFunction() + { + GL.BlendFunc(BlendingFactorSrc.ConstantColorExt, BlendingFactorDest.OneMinusSrcColor); + } + + protected override void SetColor(Color color) + { + GL.BlendColor(color); + } + + protected override TextQuality TextQuality + { + get { return quality; } + } + + protected override GlyphCache Cache + { + get { return cache; } + } + } +} diff --git a/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs new file mode 100644 index 00000000..6731f35e --- /dev/null +++ b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs @@ -0,0 +1,268 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenTK.Math; +using System; + +namespace OpenTK.Graphics.Text +{ + abstract class GL1TextOutputProvider : ITextOutputProvider + { + #region Fields + + // Triangle lists, sorted by texture. + Dictionary> active_lists = new Dictionary>(); + Queue> inactive_lists = new Queue>(); + + #pragma warning disable 0649 + + struct Viewport { public float Left, Top, Right, Bottom; } + + #pragma warning restore 0649 + + bool disposed; + + #endregion + + #region Constructors + + public GL1TextOutputProvider() + { + inactive_lists.Enqueue(new List()); + } + + #endregion + + #region ITextOutputProvider Members + + #region Print + + public void Print(TextBlock block, Color color, IGlyphRasterizer rasterizer) + { + GL.PushAttrib(AttribMask.TextureBit | AttribMask.EnableBit | AttribMask.ColorBufferBit | AttribMask.DepthBufferBit); + + GL.Enable(EnableCap.Texture2D); + GL.Enable(EnableCap.Blend); + SetBlendFunction(); + + GL.Disable(EnableCap.DepthTest); + + using (TextExtents extents = rasterizer.MeasureText(block)) + { + // Build layout + int current = 0; + foreach (Glyph glyph in block) + { + if (glyph.IsWhiteSpace) + { + current++; + continue; + } + else if (!Cache.Contains(glyph)) + Cache.Add(glyph, rasterizer, TextQuality); + + CachedGlyphInfo info = Cache[glyph]; + RectangleF position = extents[current++]; + + // Use the real glyph width instead of the measured one (we want to achieve pixel perfect output). + position.Size = info.Rectangle.Size; + + if (!active_lists.ContainsKey(info.Texture)) + if (inactive_lists.Count > 0) + active_lists.Add(info.Texture, inactive_lists.Dequeue()); + else + active_lists.Add(info.Texture, new List()); + { + // Interleaved array: Vertex, TexCoord, Vertex, ... + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); + active_lists[info.Texture].Add(new Vector2(position.Left, position.Top)); + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Bottom)); + active_lists[info.Texture].Add(new Vector2(position.Left, position.Bottom)); + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); + active_lists[info.Texture].Add(new Vector2(position.Right, position.Bottom)); + + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); + active_lists[info.Texture].Add(new Vector2(position.Right, position.Bottom)); + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Top)); + active_lists[info.Texture].Add(new Vector2(position.Right, position.Top)); + active_lists[info.Texture].Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); + active_lists[info.Texture].Add(new Vector2(position.Left, position.Top)); + } + } + } + + // Render + foreach (Texture2D key in active_lists.Keys) + { + List list = active_lists[key]; + + key.Bind(); + + //if (!legacy_mode) + //{ + // GL.PushMatrix(); + // GL.GetFloat(GetPName.Viewport, viewport); + // GL.Scale(2.0 / (viewport[2] - viewport[0]), -2.0 / (viewport[3] - viewport[1]), 1); + //} + + SetColor(color); + + GL.Begin(BeginMode.Triangles); + + for (int i = 0; i < list.Count; i += 2) + { + GL.TexCoord2(list[i]); + GL.Vertex2(list[i + 1]); + } + + GL.End(); + + //if (!legacy_mode) + // GL.PopMatrix(); + } + + // Clean layout + foreach (List list in active_lists.Values) + { + list.Clear(); + inactive_lists.Enqueue(list); + } + + active_lists.Clear(); + + GL.PopAttrib(); + } + + #endregion + + #region Clear + + public void Clear() + { + Cache.Clear(); + } + + #endregion + + #region Begin + + public void Begin() + { + if (disposed) + throw new ObjectDisposedException(this.GetType().ToString()); + + GraphicsContext.Assert(); + + int current_matrix; + GL.GetInteger(GetPName.MatrixMode, out current_matrix); + + Viewport viewport = new Viewport(); + GL.GetFloat(GetPName.Viewport, out viewport.Left); + + // Prepare to draw text. We want pixel perfect precision, so we setup a 2D mode, + // with size equal to the window (in pixels). + // While we could also render text in 3D mode, it would be very hard to get + // pixel-perfect precision. + GL.MatrixMode(MatrixMode.Projection); + GL.PushMatrix(); + GL.LoadIdentity(); + GL.Ortho(viewport.Left, viewport.Right, viewport.Bottom, viewport.Top, -1.0, 1.0); + + GL.MatrixMode(MatrixMode.Modelview); + GL.PushMatrix(); + GL.LoadIdentity(); + + GL.MatrixMode((MatrixMode)current_matrix); + } + + #endregion + + #region End + + public void End() + { + if (disposed) + throw new ObjectDisposedException(this.GetType().ToString()); + + GraphicsContext.Assert(); + + int current_matrix; + GL.GetInteger(GetPName.MatrixMode, out current_matrix); + + GL.MatrixMode(MatrixMode.Modelview); + GL.PopMatrix(); + + GL.MatrixMode(MatrixMode.Projection); + GL.PopMatrix(); + + GL.MatrixMode((MatrixMode)current_matrix); + } + + #endregion + + #endregion + + #region Protected Members + + protected abstract void SetBlendFunction(); + + protected abstract void SetColor(Color color); + + protected abstract TextQuality TextQuality { get; } + + protected abstract GlyphCache Cache { get; } + + #endregion + + #region Static Members + + public static GL1TextOutputProvider Create(TextQuality quality) + { + if (!GL.SupportsFunction("BlendFunc") || quality == TextQuality.Low || quality == TextQuality.Medium) + return new GL11TextOutputProvider(quality); + else + return new GL12TextOutputProvider(quality); + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!disposed) + { + Cache.Dispose(); + disposed = true; + } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/GdiPlusGlyphRasterizer .cs b/Source/Utilities/Graphics/Text/GdiPlusGlyphRasterizer .cs new file mode 100644 index 00000000..d1825215 --- /dev/null +++ b/Source/Utilities/Graphics/Text/GdiPlusGlyphRasterizer .cs @@ -0,0 +1,480 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Text; +using OpenTK.Platform; + +namespace OpenTK.Graphics.Text +{ + sealed class GdiPlusGlyphRasterizer : IGlyphRasterizer + { + #region Fields + + Dictionary block_cache = new Dictionary(); + System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(new Bitmap(1, 1)); + + IntPtr[] regions = new IntPtr[GdiPlus.MaxMeasurableCharacterRanges]; + CharacterRange[] characterRanges = new CharacterRange[GdiPlus.MaxMeasurableCharacterRanges]; + + Bitmap glyph_surface; + System.Drawing.Graphics glyph_renderer; + + readonly List measured_glyphs = new List(256); + + readonly ObjectPool text_extents_pool = new ObjectPool(); + + // Check the constructor, too, for additional flags. + // Used for measuring text. Can set the leftToRight, rightToLeft, vertical and measure trailing spaces flags. + readonly StringFormat measure_string_format = new StringFormat(StringFormat.GenericDefault); + readonly StringFormat measure_string_format_tight = new StringFormat(StringFormat.GenericTypographic); + // Used for loading glyphs. Only use leftToRight! + readonly StringFormat load_glyph_string_format = new StringFormat(StringFormat.GenericDefault); + readonly StringFormat load_glyph_string_format_tight = new StringFormat(StringFormat.GenericTypographic); + + static readonly char[] newline_characters = new char[] { '\n', '\r' }; + + static readonly SizeF MaximumGraphicsClipSize; + + #endregion + + #region Constructors + + static GdiPlusGlyphRasterizer() + { + using (Bitmap bmp = new Bitmap(1, 1)) + using (System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bmp)) + { + MaximumGraphicsClipSize = gfx.ClipBounds.Size; + } + } + + public GdiPlusGlyphRasterizer() + { + measure_string_format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoClip; + measure_string_format_tight.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; + } + + #endregion + + #region IGlyphRasterizer Members + + #region Rasterize + + public Bitmap Rasterize(Glyph glyph) + { + return Rasterize(glyph, TextQuality.Default); + } + + public Bitmap Rasterize(Glyph glyph, TextQuality quality) + { + EnsureSurfaceSize(ref glyph_surface, ref glyph_renderer, glyph.Font); + SetTextRenderingOptions(glyph_renderer, glyph.Font, quality); + + RectangleF r2 = new RectangleF(); + + glyph_renderer.Clear(Color.Transparent); + + glyph_renderer.DrawString(glyph.Character.ToString(), glyph.Font, Brushes.White, Point.Empty, //new Point(glyph_surface.Width, 0), + glyph.Font.Style == FontStyle.Italic ? load_glyph_string_format : load_glyph_string_format_tight); + + r2 = FindEdges(glyph_surface, true); + + //if ((default_string_format.FormatFlags & StringFormatFlags.DirectionRightToLeft) != 0) + //{ + // glyph_renderer.DrawString(glyph.Character.ToString(), glyph.Font, Brushes.White, Point.Empty, //new Point(glyph_surface.Width, 0), + // load_glyph_string_format);//glyph.Font.Style == FontStyle.Italic ? load_glyph_string_format : default_string_format); + + // r2 = FindEdges(glyph_surface, true); + //} + //else + //{ + // glyph_renderer.DrawString(glyph.Character.ToString(), glyph.Font, Brushes.White, Point.Empty, + // load_glyph_string_format_tight); //glyph.Font.Style == FontStyle.Italic ? load_glyph_string_format : default_string_format); + + // r2 = FindEdges(glyph_surface, false); + //} + + return glyph_surface.Clone(r2, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + } + + #endregion + + #region MeasureText + + public TextExtents MeasureText(TextBlock block) + { + return MeasureText(block, TextQuality.Default); + } + + public TextExtents MeasureText(TextBlock block, TextQuality quality) + { + // First, check if we have cached this text block. Do not use block_cache.TryGetValue, to avoid thrashing + // the user's TextBlockExtents struct. + if (block_cache.ContainsKey(block)) + return block_cache[block]; + + // If this block is not cached, we have to measure it and (potentially) place it in the cache. + TextExtents extents = MeasureTextExtents(block, quality); + + if ((block.Options & TextPrinterOptions.NoCache) == 0) + block_cache.Add(block, extents); + + return extents; + } + + #endregion + + #region Clear + + public void Clear() + { + block_cache.Clear(); + } + + #endregion + + #endregion + + #region Private Members + + #region EnsureSurfaceSize + + void EnsureSurfaceSize(ref Bitmap bmp, ref System.Drawing.Graphics gfx, Font font) + { + if (bmp == null || bmp.Width < 2 * font.Size || bmp.Height < 2 * font.Size) + { + if (bmp != null) + bmp.Dispose(); + if (gfx != null) + gfx.Dispose(); + + bmp = new Bitmap((int)(2 * font.Size), (int)(2 * font.Size)); + gfx = System.Drawing.Graphics.FromImage(bmp); + } + } + + #endregion + + #region SetRenderingOptions + + // Modify rendering settings (antialiasing, grid fitting) to improve appearance. + void SetTextRenderingOptions(System.Drawing.Graphics gfx, Font font, TextQuality quality) + { + switch (quality) + { + case TextQuality.Default: + gfx.TextRenderingHint = TextRenderingHint.SystemDefault; + break; + + case TextQuality.High: + gfx.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; + break; + + case TextQuality.Medium: + if (font.Size <= 18.0f) + gfx.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + else + gfx.TextRenderingHint = TextRenderingHint.AntiAlias; + break; + + case TextQuality.Low: + if (font.Size <= 18.0f) + gfx.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit; + else + gfx.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; + break; + } + } + + #endregion + + #region MeasureTextExtents + + TextExtents MeasureTextExtents(TextBlock block, TextQuality quality) + { + // Todo: Parse layout options: + StringFormat format = block.Font.Italic ? measure_string_format : measure_string_format_tight; + //StringFormat format = measure_string_format_tight; + + if (block.Direction == TextDirection.Vertical) + format.FormatFlags |= StringFormatFlags.DirectionVertical; + else + format.FormatFlags &= ~StringFormatFlags.DirectionVertical; + + if (block.Direction == TextDirection.RightToLeft) + format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + else + format.FormatFlags &= ~StringFormatFlags.DirectionRightToLeft; + + if (block.Alignment == TextAlignment.Near) + format.Alignment = StringAlignment.Near; + else if (block.Alignment == TextAlignment.Center) + format.Alignment = StringAlignment.Center; + else + format.Alignment = StringAlignment.Far; + + TextExtents extents = text_extents_pool.Acquire(); + + RectangleF rect = block.Bounds; + // Work around Mono/GDI+ bug, which causes incorrect + // text wraping when block.Bounds == SizeF.Empty. + if (block.Bounds.Size == SizeF.Empty) + rect.Size = MaximumGraphicsClipSize; + + SetTextRenderingOptions(graphics, block.Font, quality); + + IntPtr native_graphics = GdiPlus.GetNativeGraphics(graphics); + IntPtr native_font = GdiPlus.GetNativeFont(block.Font); + IntPtr native_string_format = GdiPlus.GetNativeStringFormat(format); + + float max_width = 0, max_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 = block.Text.Replace("\r", String.Empty).Split('\n'); + foreach (string s in lines) + { + float width, height; + + extents.AddRange(MeasureGlyphExtents( + ref block, s, + native_graphics, native_font, native_string_format, + ref rect, out width, out height)); + + if ((block.Direction & TextDirection.Vertical) == 0) + rect.Y += block.Font.Height; + else + rect.X += block.Font.Height; + + if (width > max_width) + max_width = width; + if (height > max_height) + max_height = height; + } + } + + extents.BoundingBox = new RectangleF(extents[0].X, extents[0].Y, max_width, max_height); + + return extents; + } + + #endregion + + #region MeasureGlyphExtents + + // Gets the bounds of each character in a line of text. + // Each line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges). + IEnumerable MeasureGlyphExtents( + ref TextBlock block, string text, + IntPtr native_graphics, IntPtr native_font, IntPtr native_string_format, + ref RectangleF layoutRect, out float max_width, out float max_height) + { + measured_glyphs.Clear(); + max_width = layoutRect.Left; + max_height = layoutRect.Top; + float last_line_width = 0, last_line_height = 0; + + int current = 0; + while (current < text.Length) + { + int num_characters = (text.Length - current) > GdiPlus.MaxMeasurableCharacterRanges ? + GdiPlus.MaxMeasurableCharacterRanges : + text.Length - current; + int status = 0; + + // Prepare the character ranges and region structs for the measurement. + for (int i = 0; i < num_characters; i++) + { + if (text[current + i] == '\n' || text[current + i] == '\r') + throw new NotSupportedException(); + + characterRanges[i] = new CharacterRange(current + 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)); + + // Read back the results of the measurement. + for (int i = 0; i < num_characters; i++) + { + RectangleF rect = new RectangleF(); + + 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)); + + if (rect.Bottom > max_height) + max_height = rect.Bottom; + if (rect.Right > max_width) + max_width = rect.Right; + + if (rect.X > last_line_width) + last_line_width = rect.X; + if (rect.Y > last_line_height) + last_line_height = rect.Y; + + measured_glyphs.Add(rect); + } + + current += num_characters; + } + + // Make sure the current height is updated, if the the current line has wrapped due to word-wraping. + // Otherwise, the next line will overlap with the current one. + if (measured_glyphs.Count > 1) + { + if ((block.Direction & TextDirection.Vertical) == 0) + { + if (layoutRect.Y < last_line_height) + layoutRect.Y = last_line_height; + } + else + { + if (layoutRect.X < last_line_width) + layoutRect.X = last_line_width; + } + } + + return measured_glyphs; + } + + #endregion + + #region FindEdges + + #pragma warning disable 0649 + + struct Pixel { public byte B, G, R, A; } + + #pragma warning restore 0649 + + // Note: The bool parameter is not used at this point. + // We might need it if we ever load true rightToLeft glyphs. + Rectangle FindEdges(Bitmap bmp, bool rightToLeft) + { + BitmapData data = bmp.LockBits( + new Rectangle(0, 0, bmp.Width, bmp.Height), + ImageLockMode.ReadOnly, + System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + //Rectangle rect = rightToLeft ? + // Rectangle.FromLTRB(FindLeftEdge(bmp, data.Scan0), 0, bmp.Width - 1, FindBottomEdge(bmp, data.Scan0)) : + // Rectangle.FromLTRB(0, 0, FindRightEdge(bmp, data.Scan0), FindBottomEdge(bmp, data.Scan0)); + + Rectangle rect = + Rectangle.FromLTRB(0, 0, FindRightEdge(bmp, data.Scan0), FindBottomEdge(bmp, data.Scan0)); + //Rectangle.FromLTRB(FindLeftEdge(bmp, data.Scan0), 0, FindRightEdge(bmp, data.Scan0), FindBottomEdge(bmp, data.Scan0)); + + bmp.UnlockBits(data); + + return rect; + } + + #endregion + + #region Find[Left|Right|Top|Bottom]Edge + + // Iterates through the bmp, and returns the first row or line that contains a non-transparent pixels. + + int FindLeftEdge(Bitmap bmp, IntPtr ptr) + { + for (int x = 0; x < bmp.Width; x++) + for (int y = 0; y < bmp.Height; y++) + unsafe + { + if (((Pixel*)(ptr) + y * bmp.Width + x)->A != 0) + return x; + } + + return bmp.Width - 1; + } + + int FindRightEdge(Bitmap bmp, IntPtr ptr) + { + for (int x = bmp.Width - 1; x >= 0; x--) + for (int y = 0; y < bmp.Height; y++) + unsafe + { + if (((Pixel*)(ptr) + y * bmp.Width + x)->A != 0) + return x + 1; + } + + return 0; + } + + int FindTopEdge(Bitmap bmp, IntPtr ptr) + { + for (int y = 0; y < bmp.Height; y++) + for (int x = 0; x < bmp.Width; x++) + unsafe + { + if (((Pixel*)(ptr) + y * bmp.Width + x)->A != 0) + return y; + } + + return bmp.Height - 1; + } + + int FindBottomEdge(Bitmap bmp, IntPtr ptr) + { + for (int y = bmp.Height - 1; y >= 0; y--) + for (int x = 0; x < bmp.Width; x++) + unsafe + { + if (((Pixel*)(ptr) + y * bmp.Width + x)->A != 0) + return y + 1; + } + + return 0; + } + + #endregion + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/Glyph.cs b/Source/Utilities/Graphics/Text/Glyph.cs new file mode 100644 index 00000000..b48bb084 --- /dev/null +++ b/Source/Utilities/Graphics/Text/Glyph.cs @@ -0,0 +1,153 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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 bool IsWhiteSpace + + public bool IsWhiteSpace + { + get { return Char.IsWhiteSpace(Character); } + } + + #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..9497d27c --- /dev/null +++ b/Source/Utilities/Graphics/Text/GlyphCache.cs @@ -0,0 +1,155 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + abstract class GlyphCache : IGlyphCache + { + #region IGlyphCache Members + + public abstract void Add(Glyph glyph, IGlyphRasterizer rasterizer, TextQuality quality); + + public abstract bool Contains(Glyph glyph); + + public abstract CachedGlyphInfo this[Glyph glyph] { get; } + + public abstract void Clear(); + + public abstract void Dispose(); + + #endregion + } + + sealed class GlyphCache : GlyphCache where T : Texture2D + { + #region Fields + + List> sheets = new List>(); + Bitmap bmp = new Bitmap(32, 32); + + Dictionary cached_glyphs = new Dictionary(); + + bool disposed; + + const int SheetWidth = 512, SheetHeight = 512; + + #endregion + + #region Constructors + + public GlyphCache() + { + sheets.Add(new GlyphSheet(SheetWidth, SheetHeight)); + } + + #endregion + + #region IGlyphCache Members + + public override void Add(Glyph glyph, IGlyphRasterizer rasterizer, TextQuality quality) + { + if (rasterizer == null) + throw new ArgumentNullException("rasterizer"); + + bool inserted = false; + + using (Bitmap bmp = rasterizer.Rasterize(glyph, quality)) + { + Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); + foreach (GlyphSheet sheet in sheets) + { + inserted = InsertGlyph(glyph, bmp, rect, sheet); + if (inserted) + break; + } + + if (!inserted) + { + GlyphSheet sheet = new GlyphSheet(SheetWidth, SheetHeight); + sheets.Add(sheet); + InsertGlyph(glyph, bmp, rect, sheet); + } + } + } + + public override bool Contains(Glyph glyph) + { + return cached_glyphs.ContainsKey(glyph); + } + + public override CachedGlyphInfo this[Glyph glyph] + { + get + { + return cached_glyphs[glyph]; + } + } + public override void Clear() + { + for (int i = 0; i < sheets.Count; i++) + sheets[i].Dispose(); + + sheets.Clear(); + } + + #endregion + + #region Private Members + + // Asks the packer for an empty space and writes the glyph there. + bool InsertGlyph(Glyph glyph, Bitmap bmp, Rectangle source, GlyphSheet sheet) + { + Rectangle target = new Rectangle(); + if (!sheet.Packer.TryAdd(source, out target)) + return false; + + sheet.Texture.WriteRegion(source, target, 0, bmp); + cached_glyphs.Add(glyph, new CachedGlyphInfo(sheet.Texture, target)); + + return true; + } + + #endregion + + #region IDisposable Members + + public override void Dispose() + { + if (!disposed) + { + Clear(); + disposed = true; + } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/GlyphEnumerator.cs b/Source/Utilities/Graphics/Text/GlyphEnumerator.cs new file mode 100644 index 00000000..08d7d64b --- /dev/null +++ b/Source/Utilities/Graphics/Text/GlyphEnumerator.cs @@ -0,0 +1,107 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + class GlyphEnumerator : IEnumerator + { + #region Fields + + string text; + Font font; + + IEnumerator implementation; + + #endregion + + #region Constructors + + public GlyphEnumerator(string text, Font font) + { + if (text == null) + throw new ArgumentNullException("text"); + + if (font == null) + throw new ArgumentNullException("font"); + + this.text = text; + this.font = font; + + implementation = text.GetEnumerator(); + } + + #endregion + + #region IEnumerator Members + + public Glyph Current + { + get { return new Glyph(implementation.Current, font); } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + implementation.Dispose(); + } + + #endregion + + #region IEnumerator Members + + object System.Collections.IEnumerator.Current + { + get { return new Glyph(implementation.Current, font); } + } + + public bool MoveNext() + { + bool status; + do + { + status = implementation.MoveNext(); + } while (status && (implementation.Current == '\n' || implementation.Current == '\r')); + + return status; + } + + public void Reset() + { + implementation.Reset(); + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/GlyphPacker.cs b/Source/Utilities/Graphics/Text/GlyphPacker.cs new file mode 100644 index 00000000..9cda0dd0 --- /dev/null +++ b/Source/Utilities/Graphics/Text/GlyphPacker.cs @@ -0,0 +1,270 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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 bool TryAdd(Rectangle boundingBox) + + /// + /// Adds boundingBox to the GlyphPacker. + /// + /// The bounding box of the item to pack. + /// The System.Drawing.Rectangle that contains the position of the packed item. + /// True, if the item was successfully packed; false if the item is too big for this packer.. + /// Occurs if the item is larger than the available TexturePacker area + /// Occurs if the item cannot fit in the remaining packer space. + public bool TryAdd(Rectangle boundingBox, out Rectangle packedRectangle) + { + if (!root.Rectangle.Contains(boundingBox)) + { + packedRectangle = new Rectangle(); + return false; + } + + // Increase size so that the glyphs do not touch each other (to avoid rendering artifacts). + boundingBox.Width += 2; + boundingBox.Height += 2; + + Node node = root.Insert(boundingBox); + + // Tree is full and insertion failed: + if (node == null) + { + packedRectangle = new Rectangle(); + return false; + } + + packedRectangle = new Rectangle(node.Rectangle.X, node.Rectangle.Y, node.Rectangle.Width - 2, node.Rectangle.Height - 2); + return true; + } + + #endregion + + #region public Rectangle TryAdd(RectangleF boundingBox) + + /// + /// Adds boundingBox to the GlyphPacker. + /// + /// The bounding box of the item to pack. + /// The System.Drawing.RectangleF that contains the position of the packed item. + /// True, if the item was successfully packed; false if the item is too big for this packer.. + /// Occurs if the item is larger than the available TexturePacker area + /// Occurs if the item cannot fit in the remaining packer space. + public bool TryAdd(RectangleF boundingBox, out RectangleF packedRectangle) + { + Rectangle bbox = new Rectangle( + (int)boundingBox.X, (int)boundingBox.Y, + (int)(boundingBox.Width + 0.5f), (int)(boundingBox.Height + 0.5f)); + + return TryAdd(bbox, out packedRectangle); + } + + #endregion + + #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 cannot fit in the remaining packer space. + public Rectangle Add(Rectangle boundingBox) + { + if (!TryAdd(boundingBox, out boundingBox)) + throw new TexturePackerFullException(); + + return boundingBox; + } + + #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 + + #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..bc1cce1b --- /dev/null +++ b/Source/Utilities/Graphics/Text/GlyphSheet.cs @@ -0,0 +1,85 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + class GlyphSheet : IDisposable where T : Texture2D + { + #region Fields + + readonly T texture; + readonly GlyphPacker packer; + + bool disposed; + + #endregion + + #region Constructors + + public GlyphSheet(int width, int height) + { + texture = (T)typeof(T).GetConstructor(new Type[] { typeof(int), typeof(int) }).Invoke(new object[] { width, height }); + //texture.MagnificationFilter = TextureMagFilter.Nearest; + //texture.MinificationFilter = TextureMinFilter.Nearest; + packer = new GlyphPacker(width, height); + } + + #endregion + + #region Public Members + + public T Texture + { + get { return texture; } + } + + public GlyphPacker Packer + { + get { return packer; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!disposed) + { + texture.Dispose(); + disposed = true; + } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/IGlyphCache.cs b/Source/Utilities/Graphics/Text/IGlyphCache.cs new file mode 100644 index 00000000..fe5a60f6 --- /dev/null +++ b/Source/Utilities/Graphics/Text/IGlyphCache.cs @@ -0,0 +1,39 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; + +namespace OpenTK.Graphics.Text +{ + interface IGlyphCache : IDisposable + { + void Add(Glyph glyph, IGlyphRasterizer rasterizer, TextQuality quality); + bool Contains(Glyph glyph); + CachedGlyphInfo this[Glyph glyph] { get; } + void Clear(); + } +} diff --git a/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs b/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs new file mode 100644 index 00000000..54e311e7 --- /dev/null +++ b/Source/Utilities/Graphics/Text/IGlyphRasterizer.cs @@ -0,0 +1,45 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +using OpenTK.Graphics.Text; + +namespace OpenTK.Graphics.Text +{ + interface IGlyphRasterizer + { + Bitmap Rasterize(Glyph glyph); + Bitmap Rasterize(Glyph glyph, TextQuality quality); + TextExtents MeasureText(TextBlock block); + TextExtents MeasureText(TextBlock block, TextQuality quality); + void Clear(); + } +} diff --git a/Source/Utilities/Graphics/Text/ITextOutputProvider.cs b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs new file mode 100644 index 00000000..826dc890 --- /dev/null +++ b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs @@ -0,0 +1,42 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics.Text +{ + interface ITextOutputProvider : IDisposable + { + void Print(TextBlock block, Color color, IGlyphRasterizer rasterizer); + void Clear(); + void Begin(); + void End(); + } +} diff --git a/Source/Utilities/Graphics/Text/PoolableTextExtents.cs b/Source/Utilities/Graphics/Text/PoolableTextExtents.cs new file mode 100644 index 00000000..7b7c74a0 --- /dev/null +++ b/Source/Utilities/Graphics/Text/PoolableTextExtents.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics.Text +{ + class PoolableTextExtents : TextExtents, IPoolable + { + ObjectPool owner; + + #region Constructors + + public PoolableTextExtents() + { + } + + #endregion + + #region IPoolable Members + + ObjectPool IPoolable.Owner + { + get { return owner; } + set { owner = value; } + } + + #endregion + + #region IPoolable Members + + void IPoolable.OnAcquire() + { + Clear(); + } + + void IPoolable.OnRelease() + { + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/TextBlock.cs b/Source/Utilities/Graphics/Text/TextBlock.cs new file mode 100644 index 00000000..006d3b9d --- /dev/null +++ b/Source/Utilities/Graphics/Text/TextBlock.cs @@ -0,0 +1,124 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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, IEnumerable + { + #region Fields + + public readonly string Text; + + public readonly Font Font; + + public readonly RectangleF Bounds; + + public readonly TextPrinterOptions Options; + + public readonly TextAlignment Alignment; + + public readonly TextDirection Direction; + + public readonly int UsageCount; + + #endregion + + #region Constructors + + public TextBlock(string text, Font font, RectangleF bounds, TextPrinterOptions options, TextAlignment alignment, TextDirection direction) + { + Text = text; + Font = font; + Bounds = bounds; + Options = options; + Alignment = alignment; + Direction = direction; + 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() ^ Bounds.GetHashCode() ^ Options.GetHashCode(); + } + + public Glyph this[int i] + { + get { return new Glyph(Text[i], Font); } + } + + #endregion + + #region IEquatable Members + + public bool Equals(TextBlock other) + { + return + Text == other.Text && + Font == other.Font && + Bounds == other.Bounds && + Options == other.Options; + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return new GlyphEnumerator(Text, Font); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return new GlyphEnumerator(Text, Font); + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/Text/TextBlockComparer.cs b/Source/Utilities/Graphics/Text/TextBlockComparer.cs new file mode 100644 index 00000000..681524b2 --- /dev/null +++ b/Source/Utilities/Graphics/Text/TextBlockComparer.cs @@ -0,0 +1,51 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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/TextAlignment.cs b/Source/Utilities/Graphics/TextAlignment.cs new file mode 100644 index 00000000..4d88cf3d --- /dev/null +++ b/Source/Utilities/Graphics/TextAlignment.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + /// + /// Defines available alignments for text. + /// + public enum TextAlignment + { + /// The text is aligned to the near side (left for left-to-right text and right for right-to-left text). + Near = 0, + /// The text is aligned to the center. + Center, + /// The text is aligned to the far side (right for left-to-right text and left for right-to-left text). + Far + } +} diff --git a/Source/Utilities/Graphics/TextDirection.cs b/Source/Utilities/Graphics/TextDirection.cs new file mode 100644 index 00000000..151ca784 --- /dev/null +++ b/Source/Utilities/Graphics/TextDirection.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + /// + /// Defines available directions for text layout. + /// + public enum TextDirection + { + /// The text is layed out from left to right. + LeftToRight, + /// The text is layed out from right to left. + RightToLeft, + /// The text is layed out vertically. + Vertical + } +} diff --git a/Source/Utilities/Graphics/TextExtents.cs b/Source/Utilities/Graphics/TextExtents.cs new file mode 100644 index 00000000..6362f42f --- /dev/null +++ b/Source/Utilities/Graphics/TextExtents.cs @@ -0,0 +1,136 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace OpenTK.Graphics +{ + /// + /// Holds the results of a text measurement. + /// + public class TextExtents : IDisposable + { + #region Fields + + protected RectangleF text_extents; + protected List glyph_extents = new List(); + + public static readonly TextExtents Empty = new TextExtents(); + + #endregion + + #region Constructors + + internal TextExtents() + { + } + + #endregion + + #region Public Members + + /// + /// Gets the bounding box of the measured text. + /// + public RectangleF BoundingBox + { + get { return text_extents; } + internal set { text_extents = value; } + } + + /// + /// Gets the extents of each glyph in the measured text. + /// + /// The index of the glyph. + /// The extents of the specified glyph. + public RectangleF this[int i] + { + get { return glyph_extents[i]; } + internal set { glyph_extents[i] = value; } + } + + /// + /// Gets the extents of each glyph in the measured text. + /// + public IEnumerable GlyphExtents + { + get { return (IEnumerable)glyph_extents; } + } + + /// + /// Gets the number of the measured glyphs. + /// + public int Count + { + get { return glyph_extents.Count; } + } + + #endregion + + #region Internal Members + + internal void Add(RectangleF glyphExtent) + { + glyph_extents.Add(glyphExtent); + } + + internal void AddRange(IEnumerable glyphExtents) + { + glyph_extents.AddRange(glyphExtents); + } + + internal void Clear() + { + BoundingBox = RectangleF.Empty; + glyph_extents.Clear(); + } + + internal TextExtents Clone() + { + TextExtents extents = new TextExtents(); + extents.glyph_extents.AddRange(GlyphExtents); + extents.BoundingBox = BoundingBox; + return extents; + } + + #endregion + + #region IDisposable Members + + /// + /// Frees the resources consumed by this TextExtents instance. + /// + public virtual void Dispose() + { + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/TextPrinter.cs b/Source/Utilities/Graphics/TextPrinter.cs new file mode 100644 index 00000000..defb3c02 --- /dev/null +++ b/Source/Utilities/Graphics/TextPrinter.cs @@ -0,0 +1,337 @@ +#region --- License --- +/* Licensed under the MIT/X11 license. + * Copyright (c) 2006-2008 the OpenTK Team. + * This notice may not be removed from any source distribution. + * See license.txt for licensing details. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Text.RegularExpressions; +using System.Runtime.InteropServices; +using System.Diagnostics; + +using OpenTK.Math; +using OpenTK.Graphics; +using OpenTK.Graphics.Text; +using OpenTK.Platform; + +namespace OpenTK.Fonts { } + +namespace OpenTK.Graphics +{ + /// + /// Provides methods to perform layout and print hardware accelerated text. + /// + public sealed class TextPrinter : ITextPrinter + { + #region Fields + + IGlyphRasterizer glyph_rasterizer; + ITextOutputProvider text_output; + TextQuality text_quality; + + bool disposed; + + #endregion + + #region Constructors + + /// + /// Constructs a new TextPrinter instance. + /// + public TextPrinter() + : this(null, null, TextQuality.Default) { } + + /// + /// Constructs a new TextPrinter instance with the specified TextQuality level. + /// + /// The desired TextQuality of this TextPrinter. + public TextPrinter(TextQuality quality) + : this(null, null, quality) { } + + TextPrinter(IGlyphRasterizer rasterizer, ITextOutputProvider output, TextQuality quality) + { + glyph_rasterizer = rasterizer; + text_output = output; + text_quality = quality; + } + + #endregion + + #region ITextPrinter Members + + #region Print + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + public void Print(string text, Font font, Color color) + { + Print(text, font, color, RectangleF.Empty, TextPrinterOptions.Default, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + public void Print(string text, Font font, Color color, RectangleF rect) + { + Print(text, font, color, rect, TextPrinterOptions.Default, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + public void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options) + { + Print(text, font, color, rect, options, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + /// The OpenTK.Graphics.TextAlignment that will be used to print text. + public void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options, TextAlignment alignment) + { + Print(text, font, color, rect, options, alignment, TextDirection.LeftToRight); + } + + /// + /// Prints text using the specified color and layout options. + /// + /// The System.String to print. + /// The System.Drawing.Font that will be used to print text. + /// The System.Drawing.Color that will be used to print text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to print text. + /// The OpenTK.Graphics.TextAlignment that will be used to print text. + /// The OpenTK.Graphics.TextDirection that will be used to print text. + public void Print(string text, Font font, Color color, RectangleF rect, TextPrinterOptions options, TextAlignment alignment, TextDirection direction) + { + if (disposed) + throw new ObjectDisposedException(this.GetType().ToString()); + + if (!ValidateParameters(text, font, rect)) + return; + + TextOutput.Print(new TextBlock(text, font, rect, options, alignment, direction), color, Rasterizer); + } + + #endregion + + #region Measure + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + public TextExtents Measure(string text, Font font) + { + return Measure(text, font, RectangleF.Empty, TextPrinterOptions.Default, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + public TextExtents Measure(string text, Font font, RectangleF rect) + { + return Measure(text, font, rect, TextPrinterOptions.Default, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + public TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options) + { + return Measure(text, font, rect, options, TextAlignment.Near, TextDirection.LeftToRight); + } + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// The OpenTK.Graphics.TextAlignment that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + public TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options, TextAlignment alignment) + { + return Measure(text, font, rect, options, alignment, TextDirection.LeftToRight); + } + + /// + /// Measures text using the specified layout options. + /// + /// The System.String to measure. + /// The System.Drawing.Font that will be used to measure text. + /// The System.Drawing.Rectangle that defines the bounds for text layout. + /// The OpenTK.Graphics.TextPrinterOptions that will be used to measure text. + /// The OpenTK.Graphics.TextAlignment that will be used to measure text. + /// The OpenTK.Graphics.TextDirection that will be used to measure text. + /// An OpenTK.Graphics.TextExtents instance that contains the results of the measurement. + public TextExtents Measure(string text, Font font, RectangleF rect, TextPrinterOptions options, TextAlignment alignment, TextDirection direction) + { + if (disposed) + throw new ObjectDisposedException(this.GetType().ToString()); + + if (!ValidateParameters(text, font, rect)) + return TextExtents.Empty; + + return Rasterizer.MeasureText(new TextBlock(text, font, rect, options, alignment, direction)); + } + + #endregion + + #region Clear + + public void Clear() + { + if (disposed) + throw new ObjectDisposedException(this.GetType().ToString()); + + TextOutput.Clear(); + Rasterizer.Clear(); + } + + #endregion + + #region Begin + + /// + /// Sets up a resolution-dependent orthographic projection. + /// + public void Begin() + { + TextOutput.Begin(); + } + + #endregion + + #region Begin + + /// + /// Restores the projection and modelview matrices to their previous state. + /// + public void End() + { + TextOutput.End(); + } + + #endregion + + #region Obsolete + + [Obsolete("Use TextPrinter.Print instead")] + public void Draw(TextHandle handle) + { + Print(handle.Text, handle.GdiPFont, Color.White); + } + + [Obsolete("Use TextPrinter.Print instead")] + public void Draw(string text, TextureFont font) + { + Print(text, font.font, Color.White); + } + + [Obsolete("Use TextPrinter.Print instead")] + public void Prepare(string text, TextureFont font, out TextHandle handle) + { + handle = new TextHandle(text, font.font); + } + + #endregion + + #endregion + + #region Private Members + + IGlyphRasterizer Rasterizer + { + get + { + if (glyph_rasterizer == null) + glyph_rasterizer = new GdiPlusGlyphRasterizer(); + + return glyph_rasterizer; + } + + } + + ITextOutputProvider TextOutput + { + get + { + if (text_output == null) + text_output = GL1TextOutputProvider.Create(text_quality); + + return text_output; + } + } + + #endregion + + #region Static Members + + static bool ValidateParameters(string text, Font font, RectangleF rect) + { + if (String.IsNullOrEmpty(text)) + return false; + if (font == null) + throw new ArgumentNullException("font"); + if (rect.Width < 0 || rect.Height < 0) + throw new ArgumentOutOfRangeException("size"); + + return true; + } + + #endregion + + #region IDisposable Members + + /// + /// Frees the resources consumed by this TextPrinter object. + /// + public void Dispose() + { + if (!disposed) + { + TextOutput.Dispose(); + disposed = true; + } + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/TextPrinterOptions.cs b/Source/Utilities/Graphics/TextPrinterOptions.cs new file mode 100644 index 00000000..e8e530dd --- /dev/null +++ b/Source/Utilities/Graphics/TextPrinterOptions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + /// + /// Defines available options for the TextPrinter. + /// + [Flags] + public enum TextPrinterOptions + { + /// The TextPrinter will use default printing options. + Default = 0x0000, + /// The TextPrinter will not cache text blocks as they are measured or printed. + NoCache = 0x0001, + } +} diff --git a/Source/Utilities/Graphics/TextQuality.cs b/Source/Utilities/Graphics/TextQuality.cs new file mode 100644 index 00000000..67275d1c --- /dev/null +++ b/Source/Utilities/Graphics/TextQuality.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenTK.Graphics +{ + /// + /// Defines available quality levels for text printing. + /// + public enum TextQuality + { + /// Use the default quality, as specified by the operating system. + Default = 0, + /// Use fast, low quality text (typically non-antialiased) . + Low, + /// Use medium quality text (typically grayscale antialiased). + Medium, + /// Use slow, high quality text (typically subpixel antialiased). + High + } +} diff --git a/Source/Utilities/Graphics/Texture2D.cs b/Source/Utilities/Graphics/Texture2D.cs new file mode 100644 index 00000000..9a167c96 --- /dev/null +++ b/Source/Utilities/Graphics/Texture2D.cs @@ -0,0 +1,317 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; + +namespace OpenTK.Graphics +{ + abstract class Texture2D : IGraphicsResource, IEquatable + { + #region Fields + + GraphicsContext context; + int id; + int width, height; + bool disposed; + + TextureMagFilter mag_filter = TextureMagFilter.Linear; + TextureMinFilter min_filter = TextureMinFilter.Linear; + + #endregion + + #region Constructors + + public Texture2D(int width, int height) + { + if (width <= 0) + throw new ArgumentOutOfRangeException("width"); + + if (height <= 0) + throw new ArgumentOutOfRangeException("height"); + + Width = width; + Height = height; + } + + #endregion + + #region Public Members + + #region public int Width + + /// Gets the width of the texture. + public int Width { get { return width; } private set { width = value; } } + + #endregion + + #region public int Height + + /// Gets the height of the texture. + public int Height { get { return height; } private set { height = value; } } + + #endregion + + #region MagnificationFilter + + public TextureMagFilter MagnificationFilter + { + get { return mag_filter; } + set { mag_filter = value; } + } + + #endregion + + #region MinificationFilter + + public TextureMinFilter MinificationFilter + { + get { return min_filter; } + set { min_filter = value; } + } + + #endregion + + #region Bind + + public void Bind() + { + GL.BindTexture(TextureTarget.Texture2D, (this as IGraphicsResource).Id); + } + + #endregion + + #region WriteRegion + + public void WriteRegion(Rectangle source, Rectangle target, int mipLevel, Bitmap bitmap) + { + if (bitmap == null) + throw new ArgumentNullException("data"); + + GraphicsUnit unit = GraphicsUnit.Pixel; + if (!bitmap.GetBounds(ref unit).Contains(source)) + throw new InvalidOperationException("The source Rectangle is larger than the Bitmap."); + + if (mipLevel < 0) + throw new ArgumentOutOfRangeException("mipLevel"); + + Bind(); + + BitmapData data = null; + + GL.PushClientAttrib(ClientAttribMask.ClientPixelStoreBit); + try + { + data = bitmap.LockBits(source, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + GL.PixelStore(PixelStoreParameter.UnpackRowLength, bitmap.Width); + GL.TexSubImage2D(TextureTarget.Texture2D, mipLevel, + target.Left, target.Top, + target.Width, target.Height, + OpenTK.Graphics.PixelFormat.Bgra, + PixelType.UnsignedByte, data.Scan0); + } + finally + { + GL.PopClientAttrib(); + if (data != null) + bitmap.UnlockBits(data); + } + } + + #endregion + + #region ReadRegion + + public TextureRegion2D ReadRegion(Rectangle rect, int mipLevel) + { + if (mipLevel < 0) + throw new ArgumentOutOfRangeException("miplevel"); + + TextureRegion2D region = new TextureRegion2D(rect); + + GL.GetTexImage(TextureTarget.Texture2D, mipLevel, PixelFormat.Bgra, PixelType.UnsignedByte, region.Data); + + return region; + } + + #endregion + + #region Equals + + public override bool Equals(object obj) + { + if (obj is Texture2D) + return this.Equals((Texture2D)obj); + + return false; + } + + #endregion + + #region public override int GetHashCode() + + public override int GetHashCode() + { + return (this as IGraphicsResource).Id; + } + + #endregion + + #region public overrid string ToString() + + public override string ToString() + { + return String.Format("Texture2D #{0} ({1}x{2}, {3})", + (this as IGraphicsResource).Id.ToString(), + Width.ToString(), + Height.ToString(), + InternalFormat.ToString()); + } + + #endregion + + #endregion + + #region Protected Members + + #region InternalFormat + + protected abstract PixelInternalFormat InternalFormat { get; } + + #endregion + + #endregion + + #region Private Members + + int CreateTexture(int width, int height) + { + int id = GL.GenTexture(); + if (id == 0) + throw new GraphicsResourceException(String.Format("Texture creation failed, (Error: {0})", GL.GetError())); + + SetDefaultTextureParameters(id); + + GL.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat, Width, Height, 0, + OpenTK.Graphics.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); + + return id; + } + + void SetDefaultTextureParameters(int id) + { + // Ensure the texture is allocated. + GL.BindTexture(TextureTarget.Texture2D, id); + 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); + } + } + + #endregion + + #region IGraphicsResource Members + + #region IGraphicsResource.Context + + GraphicsContext IGraphicsResource.Context { get { return context; } } + + #endregion + + #region IGraphicsResource.Id + + int IGraphicsResource.Id + { + get + { + if (id == 0) + { + GraphicsContext.Assert(); + context = GraphicsContext.CurrentContext; + + id = CreateTexture(Width, Height); + } + + return id; + } + } + + #endregion + + #endregion + + #region IEquatable Members + + public bool Equals(Texture2D other) + { + return (this as IGraphicsResource).Id == (other as IGraphicsResource).Id; + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + void Dispose(bool manual) + { + if (!disposed) + { + if (manual) + { + GL.DeleteTexture(id); + } + disposed = true; + } + } + + ~Texture2D() + { + GraphicsContext context = (this as IGraphicsResource).Context; + if (context != null) + (context as IGraphicsContextInternal).RegisterForDisposal(this); + } + + #endregion + } +} diff --git a/Source/Utilities/Graphics/TextureRegion2D.cs b/Source/Utilities/Graphics/TextureRegion2D.cs new file mode 100644 index 00000000..9d02aa05 --- /dev/null +++ b/Source/Utilities/Graphics/TextureRegion2D.cs @@ -0,0 +1,79 @@ +#region License +// +// The Open Toolkit Library License +// +// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +#endregion + +using System; +using System.Collections.Generic; +using System.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/IPoolable.cs b/Source/Utilities/IPoolable.cs new file mode 100644 index 00000000..68d83f36 --- /dev/null +++ b/Source/Utilities/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/Utilities/ObjectPool.cs b/Source/Utilities/ObjectPool.cs new file mode 100644 index 00000000..3ad38112 --- /dev/null +++ b/Source/Utilities/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/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.") { } + } }