using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SharpGL { /// /// A FontOutline entry contains the details of a font face. /// internal class FontOutlineEntry { /// /// Gets or sets the HDC. /// /// /// The HDC. /// public IntPtr HDC { get; set; } /// /// Gets or sets the HRC. /// /// /// The HRC. /// public IntPtr HRC { get; set; } /// /// Gets or sets the name of the face. /// /// /// The name of the face. /// public string FaceName { get; set; } /// /// Gets or sets the height. /// /// /// The height. /// public int Height { get; set; } /// /// Gets or sets the list base. /// /// /// The list base. /// public uint ListBase { get; set; } /// /// Gets or sets the list count. /// /// /// The list count. /// public uint ListCount { get; set; } /// /// Gets or sets the deviation. /// /// /// The deviation. /// public float Deviation { get; set; } /// /// Gets or sets the extrusion. /// /// /// The extrusion. /// public float Extrusion { get; set; } /// /// Gets or sets the font outline format. /// /// /// The font outline format. /// public FontOutlineFormat FontOutlineFormat { get; set; } /// /// Gets or sets the glyph metrics. /// /// /// The glyph metrics. /// public GLYPHMETRICSFLOAT[] GlyphMetrics { get; set; } } /// /// The font outline format. /// public enum FontOutlineFormat { /// /// Render using lines. /// Lines = 0, /// /// Render using polygons. /// Polygons = 1 } /// /// The GLYPHMETRICSFLOAT structure contains information about the placement and orientation of a glyph in a character cell. /// public struct GLYPHMETRICSFLOAT { /// /// Specifies the width of the smallest rectangle (the glyph's black box) that completely encloses the glyph.. /// public float gmfBlackBoxX; /// /// Specifies the height of the smallest rectangle (the glyph's black box) that completely encloses the glyph. /// public float gmfBlackBoxY; /// /// Specifies the x and y coordinates of the upper-left corner of the smallest rectangle that completely encloses the glyph. /// public POINTFLOAT gmfptGlyphOrigin; /// /// Specifies the horizontal distance from the origin of the current character cell to the origin of the next character cell. /// public float gmfCellIncX; /// /// Specifies the vertical distance from the origin of the current character cell to the origin of the next character cell. /// public float gmfCellIncY; } /// /// Point structure used in Win32 interop. /// public struct POINTFLOAT { /// /// The x coord value. /// public float x; /// /// The y coord value. /// public float y; } /// /// This class wraps the functionality of the wglUseFontOutlines function to /// allow straightforward rendering of text. /// public class FontOutlines { private FontOutlineEntry CreateFontOutlineEntry(OpenGL gl, string faceName, int height, float deviation, float extrusion, FontOutlineFormat fontOutlineFormat) { // Make the OpenGL instance current. gl.MakeCurrent(); // Create the font based on the face name. var hFont = Win32.CreateFont(height, 0, 0, 0, Win32.FW_DONTCARE, 0, 0, 0, Win32.DEFAULT_CHARSET, Win32.OUT_OUTLINE_PRECIS, Win32.CLIP_DEFAULT_PRECIS, Win32.CLEARTYPE_QUALITY, Win32.VARIABLE_PITCH, faceName); // Select the font handle. var hOldObject = Win32.SelectObject(gl.RenderContextProvider.DeviceContextHandle, hFont); // Create the list base. var listBase = gl.GenLists(1); // Create space for the glyph metrics. var glyphMetrics = new GLYPHMETRICSFLOAT[255]; // Create the font bitmaps. bool result = Win32.wglUseFontOutlines(gl.RenderContextProvider.DeviceContextHandle, 0, 255, listBase, deviation, extrusion, (int)fontOutlineFormat, glyphMetrics); // Reselect the old font. Win32.SelectObject(gl.RenderContextProvider.DeviceContextHandle, hOldObject); // Free the font. Win32.DeleteObject(hFont); // Create the font bitmap entry. var foe = new FontOutlineEntry() { HDC = gl.RenderContextProvider.DeviceContextHandle, HRC = gl.RenderContextProvider.RenderContextHandle, FaceName = faceName, Height = height, ListBase = listBase, ListCount = 255, Deviation = deviation, Extrusion = extrusion, FontOutlineFormat = fontOutlineFormat, GlyphMetrics = glyphMetrics }; // Add the font bitmap entry to the internal list. fontOutlineEntries.Add(foe); return foe; } /// /// Draws the text. /// /// The gl. /// Name of the face. /// Size of the font. /// The deviation. /// The extrusion. /// The text. public void DrawText(OpenGL gl, string faceName, float fontSize, float deviation, float extrusion, string text) { // Pass to the glyph metrics version of the function. GLYPHMETRICSFLOAT[] glyphMetrics; DrawText(gl, faceName, fontSize, deviation, extrusion, text, out glyphMetrics); } /// /// Draws the text. /// /// The gl. /// Name of the face. /// Size of the font. /// The deviation. /// The extrusion. /// The text. /// public void DrawText(OpenGL gl, string faceName, float fontSize, float deviation, float extrusion, string text, out GLYPHMETRICSFLOAT[] glyphMetrics) { // Get the font size in pixels. var fontHeight = (int)(fontSize * (16.0f / 12.0f)); // Do we have a font bitmap entry for this OpenGL instance and face name? var result = (from fbe in fontOutlineEntries where fbe.HDC == gl.RenderContextProvider.DeviceContextHandle && fbe.HRC == gl.RenderContextProvider.RenderContextHandle && String.Compare(fbe.FaceName, faceName, StringComparison.OrdinalIgnoreCase) == 0 && fbe.Height == fontHeight && fbe.Deviation == deviation && fbe.Extrusion == extrusion select fbe).ToList(); // Get the FBE or null. var fontOutlineEntry = result.FirstOrDefault(); // If we don't have the FBE, we must create it. if (fontOutlineEntry == null) fontOutlineEntry = CreateFontOutlineEntry(gl, faceName, fontHeight, deviation, extrusion, FontOutlineFormat.Polygons); // Set the list base. gl.ListBase(fontOutlineEntry.ListBase); // Create an array of lists for the glyphs. var lists = text.Select(c => (byte) c).ToArray(); // Call the lists for the string. gl.CallLists(lists.Length, lists); gl.Flush(); // Return the glyph metrics used. glyphMetrics = fontOutlineEntry.GlyphMetrics; } /// /// The cache of font outline entries. /// private readonly List fontOutlineEntries = new List(); } }