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();
}
}