2007-11-01 23:17:57 +00:00
#region - - - License - - -
/ * Copyright ( c ) 2006 , 2007 Stefanos Apostolopoulos
* See license . txt for license info
* /
#endregion
using System ;
2007-10-20 10:31:59 +00:00
using System.Collections.Generic ;
using System.Text ;
using System.Drawing.Text ;
2008-01-23 12:42:07 +00:00
using System.Drawing ;
using System.Drawing.Imaging ;
using System.Runtime.InteropServices ;
using System.Diagnostics ;
2007-10-20 10:31:59 +00:00
using OpenTK.Math ;
2008-04-13 16:45:56 +00:00
using OpenTK.Graphics ;
2007-10-20 10:31:59 +00:00
using OpenTK.Platform ;
2008-03-08 14:38:10 +00:00
namespace OpenTK.Graphics
2007-10-20 10:31:59 +00:00
{
2008-01-23 12:42:07 +00:00
using Graphics = System . Drawing . Graphics ;
2008-04-13 16:45:56 +00:00
using PixelFormat = OpenTK . Graphics . PixelFormat ;
2008-11-14 00:00:54 +00:00
using System.Text.RegularExpressions ;
2008-01-23 12:42:07 +00:00
2007-11-01 23:17:57 +00:00
public class TextureFont : IFont
2007-10-20 10:31:59 +00:00
{
2007-11-06 20:59:15 +00:00
Font font ;
2008-06-22 16:29:13 +00:00
Dictionary < char , RectangleF > loaded_glyphs = new Dictionary < char , RectangleF > ( 64 ) ;
2007-11-01 23:17:57 +00:00
2007-11-06 20:59:15 +00:00
Bitmap bmp ;
Graphics gfx ;
2007-11-08 15:54:38 +00:00
// TODO: We need to be able to use multiple font sheets.
2007-10-20 10:31:59 +00:00
static int texture ;
static TexturePacker < Glyph > pack ;
static int texture_width , texture_height ;
2008-07-01 18:50:49 +00:00
static readonly StringFormat default_string_format = StringFormat . GenericTypographic ; // Check the constructor, too, for additional flags.
static readonly StringFormat load_glyph_string_format = StringFormat . GenericDefault ;
2008-06-24 18:27:51 +00:00
static SizeF maximum_graphics_size ;
int [ ] data = new int [ 256 ] ; // Used to upload the glyph buffer to the OpenGL texture.
2007-11-08 15:54:38 +00:00
object upload_lock = new object ( ) ;
2007-10-20 10:31:59 +00:00
2008-11-14 00:00:54 +00:00
static readonly char [ ] newline_characters = new char [ ] { '\n' , '\r' } ;
2007-11-01 23:17:57 +00:00
#region - - - Constructor - - -
2007-10-26 15:55:24 +00:00
/// <summary>
2008-06-24 18:27:51 +00:00
/// Constructs a new TextureFont, using the specified System.Drawing.Font.
2007-10-26 15:55:24 +00:00
/// </summary>
2007-11-01 23:17:57 +00:00
/// <param name="font">The System.Drawing.Font to use.</param>
2007-11-08 15:54:38 +00:00
public TextureFont ( Font font )
2007-10-20 10:31:59 +00:00
{
if ( font = = null )
throw new ArgumentNullException ( "font" , "Argument to TextureFont constructor cannot be null." ) ;
this . font = font ;
2007-11-06 20:59:15 +00:00
bmp = new Bitmap ( font . Height * 2 , font . Height * 2 ) ;
gfx = Graphics . FromImage ( bmp ) ;
2008-06-24 18:27:51 +00:00
maximum_graphics_size = gfx . ClipBounds . Size ;
2007-11-08 15:54:38 +00:00
// Adjust font rendering mode. Small sizes look blurry without gridfitting, so turn
// that on. Increasing contrast also seems to help.
if ( font . Size < = 18.0f )
{
gfx . TextRenderingHint = TextRenderingHint . AntiAliasGridFit ;
2008-06-29 10:56:51 +00:00
//gfx.TextContrast = 11;
2007-11-08 15:54:38 +00:00
}
else
{
gfx . TextRenderingHint = TextRenderingHint . AntiAlias ;
2008-06-29 10:56:51 +00:00
//gfx.TextContrast = 0;
2007-11-08 15:54:38 +00:00
}
2008-07-01 18:50:49 +00:00
default_string_format . FormatFlags | = StringFormatFlags . MeasureTrailingSpaces ;
2007-10-20 10:31:59 +00:00
}
2008-06-24 18:27:51 +00:00
/// <summary>
/// Constructs a new TextureFont, using the specified parameters.
/// </summary>
/// <param name="font">The System.Drawing.FontFamily to use for the typeface.</param>
/// <param name="emSize">The em size to use for the typeface.</param>
public TextureFont ( FontFamily family , float emSize )
: this ( new Font ( family , emSize ) )
{ }
/// <summary>
/// Constructs a new TextureFont, using the specified parameters.
/// </summary>
/// <param name="font">The System.Drawing.FontFamily to use for the typeface.</param>
/// <param name="emSize">The em size to use for the typeface.</param>
/// <param name="family">The style to use for the typeface.</param>
public TextureFont ( FontFamily family , float emSize , FontStyle style )
: this ( new Font ( family , emSize , style ) )
{ }
2007-11-01 23:17:57 +00:00
#endregion
2008-06-21 23:23:33 +00:00
#region - - - Public Methods - - -
2007-11-01 23:17:57 +00:00
#region public void LoadGlyphs ( string glyphs )
2007-10-20 10:31:59 +00:00
/// <summary>
2007-10-26 15:55:24 +00:00
/// Prepares the specified glyphs for rendering.
2007-10-20 10:31:59 +00:00
/// </summary>
2007-10-26 15:55:24 +00:00
/// <param name="glyphs">The glyphs to prepare for rendering.</param>
2007-10-20 10:31:59 +00:00
public void LoadGlyphs ( string glyphs )
{
2008-06-22 16:29:13 +00:00
RectangleF rect = new RectangleF ( ) ;
2007-10-20 10:31:59 +00:00
foreach ( char c in glyphs )
{
2008-11-14 00:00:54 +00:00
if ( Char . IsWhiteSpace ( c ) )
continue ;
2008-06-24 18:27:51 +00:00
try
{
if ( ! loaded_glyphs . ContainsKey ( c ) )
LoadGlyph ( c , out rect ) ;
}
catch ( Exception e )
{
Debug . Print ( e . ToString ( ) ) ;
throw ;
}
2007-10-20 10:31:59 +00:00
}
}
2007-11-01 23:17:57 +00:00
#endregion
2007-11-06 20:59:15 +00:00
#region public void LoadGlyph ( char glyph )
/// <summary>
/// Prepares the specified glyph for rendering.
/// </summary>
/// <param name="glyphs">The glyph to prepare for rendering.</param>
public void LoadGlyph ( char glyph )
{
2008-06-22 16:29:13 +00:00
RectangleF rect = new RectangleF ( ) ;
2007-11-06 20:59:15 +00:00
if ( ! loaded_glyphs . ContainsKey ( glyph ) )
LoadGlyph ( glyph , out rect ) ;
}
#endregion
2008-06-22 16:29:13 +00:00
#region public bool GlyphData ( char glyph , out float width , out float height , out RectangleF textureRectangle , out int texture )
2007-11-01 23:17:57 +00:00
2007-11-06 13:30:46 +00:00
/// <summary>
/// Returns the characteristics of a loaded glyph.
/// </summary>
/// <param name="glyph">The character corresponding to this glyph.</param>
/// <param name="width">The width of this glyph.</param>
/// <param name="height">The height of this glyph (line spacing).</param>
2008-04-04 19:46:08 +00:00
/// <param name="textureRectangle">The bounding box of the texture buffer of this glyph.</param>
2007-11-06 13:30:46 +00:00
/// <param name="texture">The handle to the texture that contains this glyph.</param>
/// <returns>True if the glyph has been loaded, false otherwise.</returns>
/// <seealso cref="LoadGlyphs"/>
2008-06-22 16:29:13 +00:00
public bool GlyphData ( char glyph , out float width , out float height , out RectangleF textureRectangle , out int texture )
2007-11-01 23:17:57 +00:00
{
if ( loaded_glyphs . TryGetValue ( glyph , out textureRectangle ) )
{
width = textureRectangle . Width * texture_width ;
height = textureRectangle . Height * texture_height ;
texture = TextureFont . texture ;
return true ;
}
width = height = texture = 0 ;
return false ;
}
#endregion
2007-11-06 13:30:46 +00:00
#region public float Height
2007-11-01 23:17:57 +00:00
2007-10-26 15:55:24 +00:00
/// <summary>
2007-11-06 13:30:46 +00:00
/// Gets a float indicating the default line spacing of this font.
2007-10-26 15:55:24 +00:00
/// </summary>
2007-11-06 13:30:46 +00:00
public float Height
2007-10-26 15:55:24 +00:00
{
2007-11-06 13:30:46 +00:00
get { return font . Height ; }
2007-10-20 10:31:59 +00:00
}
2007-11-01 23:17:57 +00:00
#endregion
2007-11-06 20:59:15 +00:00
#region public float Width
/// <summary>
2008-06-24 20:35:37 +00:00
/// Gets a float indicating the default size of this font, in points.
2007-11-06 20:59:15 +00:00
/// </summary>
public float Width
{
get { return font . SizeInPoints ; }
}
#endregion
2008-02-02 12:29:21 +00:00
#region public void MeasureString ( string str , out float width , out float height , bool accountForOverhangs )
/// <summary>
/// Measures the width of the specified string.
/// </summary>
/// <param name="str">The string to measure.</param>
/// <param name="width">The measured width.</param>
/// <param name="height">The measured height.</param>
2008-06-22 16:29:13 +00:00
/// <param name="accountForOverhangs">If true, adds space to account for glyph overhangs. Set to true if you wish to measure a complete string. Set to false if you wish to perform layout on adjacent strings.</param>
2008-06-24 18:27:51 +00:00
[Obsolete("Returns invalid results - use MeasureText() instead")]
2008-02-02 12:29:21 +00:00
public void MeasureString ( string str , out float width , out float height , bool accountForOverhangs )
{
System . Drawing . StringFormat format = accountForOverhangs ? System . Drawing . StringFormat . GenericDefault : System . Drawing . StringFormat . GenericTypographic ;
2008-06-22 16:29:13 +00:00
//format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
2008-02-02 12:29:21 +00:00
System . Drawing . SizeF size = gfx . MeasureString ( str , font , 16384 , format ) ;
height = size . Height ;
width = size . Width ;
2008-06-22 16:29:13 +00:00
//width = height = 0;
//RectangleF rect = new RectangleF(0, 0, 0, 0);
//ICollection<RectangleF> ranges = new List<RectangleF>();
//MeasureCharacterRanges(gfx, str, font, ref rect, format, ref ranges);
//foreach (RectangleF range in ranges)
//{
// width += range.Width;
// height = range.Height > height ?range.Height : height;
//}
2008-02-02 12:29:21 +00:00
// width = 0;
// height = 0;
// int i = 0;
// foreach (char c in str)
// {
// if (c != '\n' && c != '\r')
// {
// SizeF size = gfx.MeasureString(str.Substring(i, 1), font, 16384, System.Drawing.StringFormat.GenericTypographic);
// width += size.Width == 0 ? font.SizeInPoints * 0.5f : size.Width;
// if (height < size.Height)
// height = size.Height;
// }
// ++i;
// }
}
#endregion
2007-11-06 13:30:46 +00:00
#region public void MeasureString ( string str , out float width , out float height )
2007-11-01 23:17:57 +00:00
2007-10-26 15:55:24 +00:00
/// <summary>
/// Measures the width of the specified string.
/// </summary>
2007-11-06 13:30:46 +00:00
/// <param name="str">The string to measure.</param>
/// <param name="width">The measured width.</param>
/// <param name="height">The measured height.</param>
2008-02-02 12:29:21 +00:00
/// <seealso cref="public void MeasureString(string str, out float width, out float height, bool accountForOverhangs)"/>
2008-06-24 18:27:51 +00:00
[Obsolete("Returns invalid results - use MeasureText() instead")]
2007-11-06 13:30:46 +00:00
public void MeasureString ( string str , out float width , out float height )
2007-10-26 15:55:24 +00:00
{
2008-02-02 12:29:21 +00:00
MeasureString ( str , out width , out height , true ) ;
2007-10-26 15:55:24 +00:00
}
2007-10-20 10:31:59 +00:00
2007-11-01 23:17:57 +00:00
#endregion
2008-06-24 18:27:51 +00:00
#region public RectangleF MeasureText ( string text )
2008-06-22 16:29:13 +00:00
/// <summary>
2008-06-24 18:27:51 +00:00
/// Calculates size information for the specified text.
2008-06-22 16:29:13 +00:00
/// </summary>
/// <param name="text">The string to measure.</param>
2008-06-24 18:27:51 +00:00
/// <returns>A RectangleF containing the bounding box for the specified text.</returns>
public RectangleF MeasureText ( string text )
2008-06-22 16:29:13 +00:00
{
2008-06-24 18:27:51 +00:00
return MeasureText ( text , SizeF . Empty , default_string_format , null ) ;
2008-06-22 16:29:13 +00:00
}
#endregion
2008-06-24 18:27:51 +00:00
#region public RectangleF MeasureText ( string text , SizeF bounds )
2008-06-22 16:29:13 +00:00
/// <summary>
2008-06-24 18:27:51 +00:00
/// Calculates size information for the specified text.
2008-06-22 16:29:13 +00:00
/// </summary>
/// <param name="text">The string to measure.</param>
2008-06-24 18:27:51 +00:00
/// <param name="bounds">A SizeF structure containing the maximum desired width and height of the text. Default is SizeF.Empty.</param>
/// <returns>A RectangleF containing the bounding box for the specified text.</returns>
public RectangleF MeasureText ( string text , SizeF bounds )
2008-06-22 16:29:13 +00:00
{
2008-06-24 18:27:51 +00:00
return MeasureText ( text , bounds , default_string_format , null ) ;
2008-06-22 16:29:13 +00:00
}
#endregion
2008-06-24 18:27:51 +00:00
#region public RectangleF MeasureText ( string text , SizeF bounds , StringFormat format )
2008-06-21 23:23:33 +00:00
2008-06-24 18:27:51 +00:00
/// <summary>
/// Calculates size information for the specified text.
/// </summary>
/// <param name="text">The string to measure.</param>
/// <param name="bounds">A SizeF structure containing the maximum desired width and height of the text. Pass SizeF.Empty to disable wrapping calculations. A width or height of 0 disables the relevant calculation.</param>
2008-06-29 10:56:51 +00:00
/// <param name="format">A StringFormat object which specifies the measurement format of the string. Pass null to use the default StringFormat (StringFormat.GenericTypographic).</param>
2008-06-24 18:27:51 +00:00
/// <returns>A RectangleF containing the bounding box for the specified text.</returns>
public RectangleF MeasureText ( string text , SizeF bounds , StringFormat format )
{
return MeasureText ( text , bounds , format , null ) ;
}
2008-06-21 23:23:33 +00:00
2008-06-24 18:27:51 +00:00
#endregion
2008-06-22 16:29:13 +00:00
2008-06-24 18:27:51 +00:00
#region public RectangleF MeasureText ( string text , SizeF bounds , StringFormat format , IList < RectangleF > ranges )
2008-06-22 16:29:13 +00:00
2008-11-14 00:00:54 +00:00
IntPtr [ ] regions = new IntPtr [ GdiPlus . MaxMeasurableCharacterRanges ] ;
CharacterRange [ ] characterRanges = new CharacterRange [ GdiPlus . MaxMeasurableCharacterRanges ] ;
2008-06-24 18:27:51 +00:00
/// <summary>
/// Calculates size information for the specified text.
/// </summary>
/// <param name="text">The string to measure.</param>
/// <param name="bounds">A SizeF structure containing the maximum desired width and height of the text. Pass SizeF.Empty to disable wrapping calculations. A width or height of 0 disables the relevant calculation.</param>
2008-06-28 17:44:24 +00:00
/// <param name="format">A StringFormat object which specifies the measurement format of the string. Pass null to use the default StringFormat (StringFormat.GenericDefault).</param>
2008-06-24 18:27:51 +00:00
/// <param name="ranges">Fills the specified IList of RectangleF structures with position information for individual characters. If this argument is null, these calculations are skipped.</param>
/// <returns>A RectangleF containing the bounding box for the specified text.</returns>
2008-11-14 00:00:54 +00:00
public RectangleF MeasureText ( string text , SizeF bounds , StringFormat format , List < RectangleF > ranges )
2008-06-24 18:27:51 +00:00
{
2008-06-22 16:29:13 +00:00
if ( String . IsNullOrEmpty ( text ) )
2008-06-24 18:27:51 +00:00
return RectangleF . Empty ;
if ( bounds = = SizeF . Empty )
bounds = maximum_graphics_size ;
2008-06-22 16:29:13 +00:00
2008-06-24 18:27:51 +00:00
if ( format = = null )
format = default_string_format ;
2008-06-22 16:29:13 +00:00
2008-11-14 00:00:54 +00:00
// TODO: What should we do in this case?
if ( ranges = = null )
ranges = new List < RectangleF > ( ) ;
2008-06-24 20:35:37 +00:00
2008-11-14 00:00:54 +00:00
ranges . Clear ( ) ;
2008-06-22 16:29:13 +00:00
2008-06-24 20:35:37 +00:00
PointF origin = PointF . Empty ;
SizeF size = SizeF . Empty ;
2008-06-22 16:29:13 +00:00
2008-11-14 00:00:54 +00:00
IntPtr native_graphics = GdiPlus . GetNativeGraphics ( gfx ) ;
IntPtr native_font = GdiPlus . GetNativeFont ( font ) ;
IntPtr native_string_format = GdiPlus . GetNativeStringFormat ( format ) ;
2008-06-29 10:56:51 +00:00
2008-11-14 00:00:54 +00:00
RectangleF layoutRect = new RectangleF ( PointF . Empty , bounds ) ;
2008-06-22 16:29:13 +00:00
2008-11-14 00:00:54 +00:00
int height = 0 ;
// Todo: This allocates memory, see below for possible solutions.
string [ ] lines = text . Split ( newline_characters , StringSplitOptions . RemoveEmptyEntries ) ;
foreach ( string s in lines )
{
ranges . AddRange ( GetCharExtents (
s , height , 0 , s . Length , layoutRect ,
native_graphics , native_font , native_string_format ) ) ;
height + = font . Height ;
2008-06-22 16:29:13 +00:00
}
2008-06-24 20:35:37 +00:00
2008-11-14 00:00:54 +00:00
// 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.
//foreach (LineDelimiter d in SplitLines(text))
//{
// ranges.AddRange(ProcessLine(
// text, height, d.Start, d.Length, layoutRect,
// native_graphics, native_font, native_string_format));
// height += font.Height;
//}
return new RectangleF ( ranges [ 0 ] . X , ranges [ 0 ] . Y , ranges [ ranges . Count - 1 ] . Right , ranges [ ranges . Count - 1 ] . Bottom ) ;
2008-06-22 16:29:13 +00:00
}
#endregion
2008-06-29 10:56:51 +00:00
#endregion
#region - - - Private Methods - - -
2008-06-21 23:23:33 +00:00
#region private void PrepareTexturePacker ( )
/// <summary>
/// Calculates the optimal size for the font texture and TexturePacker, and creates both.
/// </summary>
private void PrepareTexturePacker ( )
{
// Calculate the size of the texture packer. We want a power-of-two size
// that is less than 1024 (supported in Geforce256-era cards), but large
// enough to hold at least 256 (16*16) font glyphs.
// TODO: Find the actual card limits, maybe?
int size = ( int ) ( font . Size * 16 ) ;
size = ( int ) System . Math . Pow ( 2.0 , System . Math . Ceiling ( System . Math . Log ( ( double ) size , 2.0 ) ) ) ;
if ( size > 1024 )
size = 1024 ;
texture_width = size ;
texture_height = size ;
pack = new TexturePacker < Glyph > ( texture_width , texture_height ) ;
GL . GenTextures ( 1 , out texture ) ;
GL . BindTexture ( TextureTarget . Texture2D , texture ) ;
GL . TexParameter ( TextureTarget . Texture2D , TextureParameterName . TextureMinFilter , ( int ) All . Linear ) ;
GL . TexParameter ( TextureTarget . Texture2D , TextureParameterName . TextureMagFilter , ( int ) All . Linear ) ;
2008-11-14 00:00:54 +00:00
GL . TexParameter ( TextureTarget . Texture2D , TextureParameterName . TextureWrapS , ( int ) All . ClampToEdge ) ;
GL . TexParameter ( TextureTarget . Texture2D , TextureParameterName . TextureWrapT , ( int ) All . ClampToEdge ) ;
2008-06-21 23:23:33 +00:00
GL . TexImage2D ( TextureTarget . Texture2D , 0 , PixelInternalFormat . Alpha , texture_width , texture_height , 0 ,
OpenTK . Graphics . PixelFormat . Rgba , PixelType . UnsignedByte , IntPtr . Zero ) ;
}
#endregion
2008-06-24 20:35:37 +00:00
#region private void LoadGlyph ( char c , out RectangleF rectangle )
2008-06-21 23:23:33 +00:00
2008-06-24 20:35:37 +00:00
// Adds the specified caharacter to the texture packer.
2008-06-22 16:29:13 +00:00
private void LoadGlyph ( char c , out RectangleF rectangle )
2008-06-21 23:23:33 +00:00
{
if ( pack = = null )
PrepareTexturePacker ( ) ;
2008-07-01 18:50:49 +00:00
RectangleF glyph_rect = MeasureText ( c . ToString ( ) , SizeF . Empty , load_glyph_string_format ) ;
2008-06-24 20:35:37 +00:00
SizeF glyph_size = new SizeF ( glyph_rect . Right , glyph_rect . Bottom ) ; // We need to do this, since the origin might not be (0, 0)
Glyph g = new Glyph ( c , font , glyph_size ) ;
Rectangle rect ;
2008-06-21 23:23:33 +00:00
try
{
pack . Add ( g , out rect ) ;
}
catch ( InvalidOperationException expt )
{
// TODO: The TexturePacker is full, create a new font sheet.
Trace . WriteLine ( expt ) ;
throw ;
}
GL . BindTexture ( TextureTarget . Texture2D , texture ) ;
gfx . Clear ( System . Drawing . Color . Transparent ) ;
2008-06-29 10:56:51 +00:00
gfx . DrawString ( g . Character . ToString ( ) , g . Font , System . Drawing . Brushes . White , 0.0f , 0.0f , default_string_format ) ;
2008-06-21 23:23:33 +00:00
BitmapData bitmap_data = bmp . LockBits ( new Rectangle ( 0 , 0 , bmp . Width , bmp . Height ) , ImageLockMode . ReadOnly ,
System . Drawing . Imaging . PixelFormat . Format32bppArgb ) ;
2008-11-14 00:00:54 +00:00
GL . PixelStore ( PixelStoreParameter . UnpackAlignment , 1.0f ) ;
GL . PixelStore ( PixelStoreParameter . UnpackRowLength , bmp . Width ) ;
GL . TexSubImage2D ( TextureTarget . Texture2D , 0 , ( int ) rect . Left , ( int ) rect . Top ,
rect . Width , rect . Height ,
OpenTK . Graphics . PixelFormat . Rgba ,
PixelType . UnsignedByte , bitmap_data . Scan0 ) ;
2008-06-21 23:23:33 +00:00
bmp . UnlockBits ( bitmap_data ) ;
2008-06-22 16:29:13 +00:00
rectangle = RectangleF . FromLTRB (
2008-06-21 23:23:33 +00:00
rect . Left / ( float ) texture_width ,
rect . Top / ( float ) texture_height ,
rect . Right / ( float ) texture_width ,
rect . Bottom / ( float ) texture_height ) ;
loaded_glyphs . Add ( g . Character , rectangle ) ;
2008-11-14 00:00:54 +00:00
}
#endregion
#region struct LineDelimiter
// Denotes the start and end of a line of text.
struct LineDelimiter
{
public int Start , Length ;
public int End { get { return Start + Length ; } }
public LineDelimiter ( int start , int length )
{
Start = start ;
Length = length ;
}
}
#endregion
#region SplitLines
// Splits the specified string into substrings separated by the
// \n and \r characters.
//IEnumerable<LineDelimiter> SplitLines(string text)
//{
// if (text == null)
// throw new ArgumentNullException("text");
// if (text.Length == 0)
// yield break;
// int segment_start = 0;
// int i = 0;
// for (; i < text.Length; i++)
// {
// if (text[i] == '\n' || text[i] == '\r')
// {
// if (i - segment_start > 0)
// yield return new LineDelimiter() { Start = segment_start, Length = i - segment_start };
// segment_start = i + 1;
// }
// }
// if (i - segment_start > 0)
// yield return new LineDelimiter() { Start = segment_start, Length = i - segment_start };
//}
#endregion
#region GetCharExtents
// Gets the bounds of each character in a line of text.
// The line is processed in blocks of 32 characters (GdiPlus.MaxMeasurableCharacterRanges).
IEnumerable < RectangleF > GetCharExtents ( string text , int height , int line_start , int line_length ,
RectangleF layoutRect , IntPtr native_graphics , IntPtr native_font , IntPtr native_string_format )
{
RectangleF rect = new RectangleF ( ) ;
int line_end = line_start + line_length ;
while ( line_start < line_end )
{
if ( text [ line_start ] = = '\n' | | text [ line_start ] = = '\r' )
{
line_start + + ;
continue ;
}
int num_characters = ( line_end - line_start ) > GdiPlus . MaxMeasurableCharacterRanges ?
GdiPlus . MaxMeasurableCharacterRanges :
line_end - line_start ;
int status = 0 ;
2008-06-21 23:23:33 +00:00
2008-11-14 00:00:54 +00:00
for ( int i = 0 ; i < num_characters ; i + + )
{
characterRanges [ i ] = new CharacterRange ( line_start + i , 1 ) ;
IntPtr region ;
status = GdiPlus . CreateRegion ( out region ) ;
regions [ i ] = region ;
if ( status ! = 0 )
Debug . Print ( "GDI+ error: {0}" , status ) ;
}
GdiPlus . SetStringFormatMeasurableCharacterRanges ( native_string_format , num_characters , characterRanges ) ;
status = GdiPlus . MeasureCharacterRanges ( native_graphics , text , text . Length ,
native_font , ref layoutRect , native_string_format , num_characters , regions ) ;
for ( int i = 0 ; i < num_characters ; i + + )
{
GdiPlus . GetRegionBounds ( regions [ i ] , native_graphics , ref rect ) ;
GdiPlus . DeleteRegion ( regions [ i ] ) ;
rect . Y + = height ;
yield return rect ;
}
line_start + = num_characters ;
}
2008-06-21 23:23:33 +00:00
}
#endregion
#endregion
#region - - - Internal Methods - - -
#region internal int Texture
/// <summary>
/// Gets the handle to the texture were this font resides.
/// </summary>
internal int Texture
{
get { return TextureFont . texture ; }
}
#endregion
#endregion
2007-11-01 23:17:57 +00:00
#region - - - IDisposable Members - - -
2007-10-20 10:31:59 +00:00
2007-10-26 15:55:24 +00:00
bool disposed ;
2007-10-20 10:31:59 +00:00
2007-10-26 15:55:24 +00:00
/// <summary>
2008-03-08 14:38:10 +00:00
/// Releases all resources used by this OpenTK.Graphics.TextureFont.
2007-10-26 15:55:24 +00:00
/// </summary>
public void Dispose ( )
{
GC . SuppressFinalize ( this ) ;
Dispose ( true ) ;
2007-10-20 10:31:59 +00:00
}
2007-10-26 15:55:24 +00:00
private void Dispose ( bool manual )
2007-10-20 10:31:59 +00:00
{
2007-10-26 15:55:24 +00:00
if ( ! disposed )
{
pack = null ;
if ( manual )
{
GL . DeleteTextures ( 1 , ref texture ) ;
font . Dispose ( ) ;
gfx . Dispose ( ) ;
}
disposed = true ;
}
2007-10-20 10:31:59 +00:00
}
2007-10-26 15:55:24 +00:00
~ TextureFont ( )
2007-10-20 10:31:59 +00:00
{
2007-10-26 15:55:24 +00:00
Dispose ( false ) ;
2007-10-20 10:31:59 +00:00
}
2007-10-26 15:55:24 +00:00
#endregion
2007-10-20 10:31:59 +00:00
}
}