Now uses Alpha texture. Corrected issue with PixelInternalFormat enum not being used. Added preliminary layout code.

This commit is contained in:
the_fiddler 2007-11-01 23:17:57 +00:00
parent 339ae77c29
commit 22c1e5011d

View file

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