Revision: 2195
Author: mike.popoloski
Date: Thu Mar 22 19:09:22 2012
Log: Initial commit of font rendering code.
http://code.google.com/p/slimdx/source/detail?r=2195
Added:
/branches/lite/SlimDX.Toolkit/Drawing/StateSaver.cs
/branches/lite/SlimDX.Toolkit/Fonts
/branches/lite/SlimDX.Toolkit/Fonts/CreateOptions.cs
/branches/lite/SlimDX.Toolkit/Fonts/Font.cs
/branches/lite/SlimDX.Toolkit/Fonts/Geometry
/branches/lite/SlimDX.Toolkit/Fonts/Geometry/GlyphVertex.cs
/branches/lite/SlimDX.Toolkit/Fonts/Geometry/TextGeometry.cs
/branches/lite/SlimDX.Toolkit/Fonts/Geometry/TextRenderer.cs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphAtlas.cs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphData.cs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphProvider.cs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphSheet.cs
/branches/lite/SlimDX.Toolkit/Fonts/Glyphs/HeightRange.cs
/branches/lite/SlimDX.Toolkit/Fonts/Rendering
/branches/lite/SlimDX.Toolkit/Fonts/Rendering/FontShaders.cs
/branches/lite/SlimDX.Toolkit/Fonts/Rendering/NativeMethods.cs
/branches/lite/SlimDX.Toolkit/Fonts/Rendering/RenderData.cs
/branches/lite/SlimDX.Toolkit/Fonts/Rendering/RenderTargetWrapper.cs
/branches/lite/SlimDX.Toolkit/Fonts/Rendering/VertexDrawer.cs
Modified:
/branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Drawing/StateSaver.cs Thu Mar 22 19:09:22
2012
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.Direct3D11;
+using Buffer = SlimDX.Direct3D11.Buffer;
+using SlimMath;
+using SlimDX.DXGI;
+
+namespace SlimDX.Toolkit
+{
+ /// <summary>
+ /// A helper class to save and restore the rendering state for a
device context.
+ /// </summary>
+ /// <remarks>
+ /// This class saves the following bits of state from the device
context:
+ /// <list type="bullet">
+ /// <item><description>Primitive Topology</description></item>
+ /// <item><description>Input Layout</description></item>
+ /// <item><description>Blend State (including the blend factor and
sample mask)</description></item>
+ /// <item><description>Depth-Stencil State (including the reference
value)</description></item>
+ /// <item><description>Rasterizer State</description></item>
+ /// <item><description>Pixel shader sampler states</description></item>
+ /// <item><description>Vertex shader</description></item>
+ /// <item><description>Pixel shader</description></item>
+ /// <item><description>Geometry shader (if
supported)</description></item>
+ /// <item><description>Hull shader (if supported)</description></item>
+ /// <item><description>Domain shader (if
supported)</description></item>
+ /// <item><description>Any class instances set for any of the
shaders</description></item>
+ /// <item><description>Any set constant buffers for the vertex or
geometry shaders</description></item>
+ /// <item><description>Any shader resources set for the pixel or
geometry shaders</description></item>
+ /// <item><description>Any set vertex buffer
bindings</description></item>
+ /// <item><description>Index buffer (including the format and current
offset)</description></item>
+ /// </list>
+ /// </remarks>
+ public class StateSaver : IDisposable
+ {
+ FeatureLevel featureLevel;
+ DeviceContext context;
+ PrimitiveTopology primitiveTopology;
+ InputLayout inputLayout;
+ BlendState blendState;
+ Color4 blendFactor;
+ int blendSampleMask;
+ DepthStencilState depthStencilState;
+ int depthStencilReference;
+ RasterizerState rasterizerState;
+ VertexShader vertexShader;
+ PixelShader pixelShader;
+ GeometryShader geometryShader;
+ HullShader hullShader;
+ DomainShader domainShader;
+ ClassInstance[] vsInstances;
+ ClassInstance[] psInstances;
+ ClassInstance[] gsInstances;
+ ClassInstance[] hsInstances;
+ ClassInstance[] dsInstances;
+ Buffer[] vsConstantBuffers;
+ Buffer[] gsConstantBuffers;
+ SamplerState[] samplerStates;
+ ShaderResourceView[] psResources;
+ ShaderResourceView[] gsResources;
+ VertexBufferBinding[] vertexBuffers;
+ Buffer indexBuffer;
+ Format indexFormat;
+ int indexOffset;
+ bool saveState;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StateSaver"/>
class.
+ /// </summary>
+ /// <param name="context">The context for which to save
state.</param>
+ /// <param name="saveState">if set to <c>true</c>, save the
current state. This parameter exists to make it easy to dynamically use the
state saver in a using-block.</param>
+ public StateSaver(DeviceContext context, bool saveState = true)
+ {
+ if (!saveState)
+ return;
+
+ this.saveState = saveState;
+ this.context = context;
+ featureLevel = context.Device.FeatureLevel;
+
+ primitiveTopology = context.InputAssembler.PrimitiveTopology;
+ inputLayout = context.InputAssembler.InputLayout;
+ rasterizerState = context.Rasterizer.State;
+ vertexShader = context.VertexShader.Get(256, out vsInstances);
+ vsConstantBuffers = context.VertexShader.GetConstantBuffers(0,
1);
+ pixelShader = context.PixelShader.Get(256, out psInstances);
+ psResources = context.PixelShader.GetShaderResources(0, 1);
+ samplerStates = context.PixelShader.GetSamplers(0, 1);
+
+ context.OutputMerger.GetBlendState(ref blendState, ref
blendFactor, ref blendSampleMask);
+ context.OutputMerger.GetDepthStencilState(ref
depthStencilState, ref depthStencilReference);
+
+ if (featureLevel >= FeatureLevel.Level_10_0)
+ {
+ geometryShader = context.GeometryShader.Get(256, out
gsInstances);
+ gsConstantBuffers =
context.GeometryShader.GetConstantBuffers(0, 1);
+ gsResources = context.GeometryShader.GetShaderResources(0,
1);
+
+ if (featureLevel >= FeatureLevel.Level_11_0)
+ {
+ hullShader = context.HullShader.Get(256, out
hsInstances);
+ domainShader = context.DomainShader.Get(256, out
dsInstances);
+ }
+ }
+
+ vertexBuffers = context.InputAssembler.GetVertexBuffers(0, 1);
+ context.InputAssembler.GetIndexBuffer(out indexBuffer, out
indexFormat, out indexOffset);
+ }
+
+ /// <summary>
+ /// Restores the previously saved state.
+ /// </summary>
+ public void Dispose()
+ {
+ if (!saveState)
+ return;
+
+ context.InputAssembler.PrimitiveTopology = primitiveTopology;
+ context.InputAssembler.InputLayout = inputLayout;
+ context.Rasterizer.State = rasterizerState;
+ context.VertexShader.Set(vertexShader, vsInstances);
+ context.VertexShader.SetConstantBuffers(vsConstantBuffers, 0,
1);
+ context.PixelShader.Set(pixelShader, psInstances);
+ context.PixelShader.SetSamplers(samplerStates, 0, 1);
+ context.PixelShader.SetShaderResources(psResources, 0, 1);
+
+ context.OutputMerger.SetBlendState(blendState, blendFactor,
blendSampleMask);
+ context.OutputMerger.SetDepthStencilState(depthStencilState,
depthStencilReference);
+
+ if (featureLevel >= FeatureLevel.Level_10_0)
+ {
+ context.GeometryShader.Set(geometryShader, gsInstances);
+
context.GeometryShader.SetConstantBuffers(gsConstantBuffers, 0, 1);
+ context.GeometryShader.SetShaderResources(gsResources, 0,
1);
+
+ if (featureLevel >= FeatureLevel.Level_11_0)
+ {
+ context.HullShader.Set(hullShader, hsInstances);
+ context.DomainShader.Set(domainShader, dsInstances);
+ }
+ }
+
+ context.InputAssembler.SetVertexBuffers(0, vertexBuffers);
+ context.InputAssembler.SetIndexBuffer(indexBuffer,
indexFormat, indexOffset);
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/CreateOptions.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.DirectWrite;
+
+namespace SlimDX.Toolkit
+{
+ /// <summary>Describes a single font.</summary>
+ /// <remarks>If FontFamily is <c>null</c> when creating a font object,
no default font will be set up.
+ /// This is perfectly valid when drawing text using one of the
DrawTextLayout methods.
+ /// However, the DrawText methods will silently fail if no default
font is set up.<br/>
+ /// If FontFamily is not <c>null</c>, the FontWeight, FontStyle and
FontStretch members must be set to valid values according to the
DirectWrite documentation.
+ /// Zero is not a valid value for these.</remarks>
+ public class DWriteFontParameters
+ {
+ /// <summary>The name of the font-family. Valid values include
<i>Arial</i>, <i>Courier New</i>, etc. as long as the specified font is
installed.
+ /// Unavailable fonts will automatically fall back to a different
font.
+ /// This member can be set to <c>null</c>, if no default font is
desired when using the structure to create a font-wrapper.</summary>
+ public string FontFamily { get; set; }
+
+ /// <summary>The font weight. See DirectWrite
documentation.</summary>
+ public FontWeight FontWeight { get; set; }
+
+ /// <summary>The font style. See DirectWrite
documentation.</summary>
+ public FontStyle FontStyle { get; set; }
+
+ /// <summary>The font stretch. See DirectWrite
documentation.</summary>
+ public FontStretch FontStretch { get; set; }
+
+ /// <summary>The locale. <c>null</c> for default.</summary>
+ public string Locale { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see
cref="DWriteFontParameters"/> class.
+ /// </summary>
+ /// <param name="fontFamily">The font family.</param>
+ public DWriteFontParameters(string fontFamily)
+ {
+ FontFamily = fontFamily;
+ FontWeight = FontWeight.Normal;
+ FontStyle = FontStyle.Normal;
+ FontStretch = FontStretch.Normal;
+ Locale = "";
+ }
+ }
+
+ /// <summary>Describes settings used for creating Font
objects.</summary>
+ public class CreateOptions
+ {
+ /// <summary>The width of the glyph sheet textures to store glyph
images in. 0 defaults to 512.</summary>
+ public int GlyphSheetWidth { get; set; }
+
+ /// <summary>The height of the glyph sheet textures to store glyph
images in. 0 defaults to 512.</summary>
+ public int GlyphSheetHeight { get; set; }
+
+ /// <summary>The maximum number of glyphs per texture. A buffer of
<i>MaxGlyphCountPerSheet * 32</i> bytes is preallocated for each sheet. 0
defaults to 2048.</summary>
+ public int MaxGlyphCountPerSheet { get; set; }
+
+ /// <summary>The number of mip-levels for the glyph sheet
textures. 0 defaults to 1.</summary>
+ public int SheetMipLevels { get; set; }
+
+ /// <summary>If set to <c>true</c>, the sampler-state is created
with anisotropic filtering.</summary>
+ public bool AnisotropicFiltering { get; set; }
+
+ /// <summary>The maximum width of a single glyph.
+ /// This value is used to decide how large the DirectWrite render
target needs to be, which is used when drawing glyph images to put in the
atlas.
+ /// 0 defaults to 384.</summary>
+ public int MaxGlyphWidth { get; set; }
+
+ /// <summary>The maximum height of a single glyph.
+ /// This value is used to decide how large the DirectWrite render
target needs to be, which is used when drawing glyph images to put in the
atlas.
+ /// 0 defaults to 384.</summary>
+ public int MaxGlyphHeight { get; set; }
+
+ /// <summary>If set to <c>true</c>, no geometry shader is
used.</summary>
+ public bool DisableGeometryShader { get; set; }
+
+ /// <summary>The size in bytes of the dynamic vertex buffer to
upload glyph vertices to when drawing a string. 0 defaults to 4096 *
16.<br/>
+ /// Each glyph vertex is either 16 or 20 bytes in size, and each
glyph requires either 1 or 4 vertices depending on if the geometry shader
is used.</summary>
+ public int VertexBufferSize { get; set; }
+
+ /// <summary>Description of the default font. See
DWriteFontParameters.</summary>
+ public DWriteFontParameters DefaultFontParameters { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CreateOptions"/>
class.
+ /// </summary>
+ /// <param name="fontFamily">The font family.</param>
+ public CreateOptions(string fontFamily)
+ {
+ DefaultFontParameters = new DWriteFontParameters(fontFamily);
+ GlyphSheetWidth = 512;
+ GlyphSheetHeight = 512;
+ MaxGlyphCountPerSheet = 2048;
+ SheetMipLevels = 1;
+ MaxGlyphWidth = 384;
+ MaxGlyphHeight = 384;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Font.cs Thu Mar 22 19:09:22 2012
@@ -0,0 +1,351 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.DirectWrite;
+using SlimDX.Direct3D11;
+using SlimDX.Toolkit.Fonts;
+using System.Drawing;
+using SlimMath;
+
+namespace SlimDX.Toolkit
+{
+ #region TextOptions
+
+ /// <summary>Specifies various text drawing options.</summary>
+ /// <remarks>These flags can be used for any methods that take a flags
parameter. Not all flags have meaning for all methods however.
+ /// Consult the documentation page for a particular method for
information on which flags are valid.</remarks>
+ [Flags]
+ public enum TextOptions
+ {
+ /// <summary>Text is left-aligned. This is the default.</summary>
+ Left = 0x0,
+
+ /// <summary>Text is centered horizontally.</summary>
+ Center = 0x1,
+
+ /// <summary>Text is right-aligned.</summary>
+ Right = 0x2,
+
+ /// <summary>Text is aligned at the top of the layout-box. This is
the default.</summary>
+ Top = 0x0,
+
+ /// <summary>Text is centered vertically.</summary>
+ VerticalCenter = 0x4,
+
+ /// <summary>Text is aligned at the bottom of the
layout-box.</summary>
+ Bottom = 0x8,
+
+ /// <summary>No automatic wrapping when the text overflows the
layout-box.</summary>
+ NoWordWrap = 0x10,
+
+ /// <summary>Text is drawn without anti-aliasing.</summary>
+ Aliased = 0x20,
+
+ /// <summary>If a clip-rect is specified together with this flag,
all text is clipped to inside the specified rectangle.</summary>
+ ClipRect = 0x40,
+
+ /// <summary>No geometry shader is used when drawing glyphs.
Indexed quads are constructed on the CPU instead of in the geometry
shader.</summary>
+ NoGeometryShader = 0x80,
+
+ /// <summary>The transform matrix and the clip-rect is not updated
in the internal constant-buffer. Can be used as an optimization when a
previous call has already set the correct data.</summary>
+ ConstantsPrepared = 0x100,
+
+ /// <summary>The internal vertex and index buffer (if used) are
assumed to already be bound. Can be used as an optimization when a previous
call has already set the buffers.</summary>
+ BuffersPrepared = 0x200,
+
+ /// <summary>The correct shaders/constant-buffer etc. are assumed
to already be bound. Can be used as an optimization when a previous call
has already set the states, or to override the default states.</summary>
+ StatePrepared = 0x400,
+
+ /// <summary>Can be used as an optimization on subsequent calls,
when drawing several strings with the same settings.</summary>
+ ImmediateCall = ConstantsPrepared | BuffersPrepared |
StatePrepared,
+
+ /// <summary>When a draw method returns, the device-context will
have been restored to the same state as before the call.</summary>
+ RestoreState = 0x800,
+
+ /// <summary>Any new glyphs added during a call are not flushed to
the device-resources.
+ /// It is a good idea to use this flag for text-operations on
deferred contexts, when drawing text on multiple threads simultaneously, in
order to guarantee the proper order of operations.</summary>
+ NoFlush = 0x1000,
+
+ /// <summary>Any new glyphs will be cached in the atlas and
glyph-maps, but no geometry is drawn.</summary>
+ CacheOnly = 0x2000,
+
+ /// <summary>No new glyphs will be added to the atlas or
glyph-maps. Any glyphs not already present in the atlas will be replaced
with a default fall-back glyph (empty box).</summary>
+ NoNewGlyphs = 0x4000,
+
+ /// <summary>A text-layout will be run through DirectWrite and new
fonts will be prepared, but no actual drawing will take place, and no
additional glyphs will be cached.</summary>
+ AnalyzeOnly = 0x8000
+ };
+
+ #endregion
+
+ /// <summary>
+ /// Provides an interface for rendering text with a given font.
+ /// </summary>
+ public class Font : IDisposable
+ {
+ Factory factory;
+ GlyphProvider glyphProvider;
+ GlyphAtlas glyphAtlas;
+ TextFormat defaultFormat;
+ TextRenderer renderer;
+ TextGeometry geometry;
+ FeatureLevel featureLevel;
+ RenderData renderStates;
+ VertexDrawer glyphDrawer;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Font"/> class.
+ /// </summary>
+ /// <param name="factory">The DirectWrite factory.</param>
+ /// <param name="device">The graphics device.</param>
+ /// <param name="fontFamily">The font family.</param>
+ public Font(Factory factory, Device device, string fontFamily)
+ : this(factory, device, new CreateOptions(fontFamily))
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Font"/> class.
+ /// </summary>
+ /// <param name="factory">The DirectWrite factory.</param>
+ /// <param name="device">The graphics device.</param>
+ /// <param name="options">Various creation options.</param>
+ public Font(Factory factory, Device device, CreateOptions options)
+ {
+ this.factory = factory;
+ featureLevel = device.FeatureLevel;
+ glyphAtlas = new GlyphAtlas(device, options.GlyphSheetWidth,
options.GlyphSheetHeight, !options.DisableGeometryShader, true,
options.MaxGlyphCountPerSheet, options.SheetMipLevels, 4096);
+ glyphProvider = new GlyphProvider(factory, glyphAtlas,
options.MaxGlyphWidth, options.MaxGlyphHeight);
+ glyphDrawer = new VertexDrawer(device,
options.VertexBufferSize);
+ renderStates = new
RenderData(device, !options.DisableGeometryShader,
options.AnisotropicFiltering);
+ geometry = new TextGeometry();
+ renderer = new TextRenderer(glyphProvider);
+
+ if
(!string.IsNullOrEmpty(options.DefaultFontParameters.FontFamily))
+ {
+ defaultFormat = factory.CreateTextFormat(
+ options.DefaultFontParameters.FontFamily,
+ options.DefaultFontParameters.FontWeight,
+ options.DefaultFontParameters.FontStyle,
+ options.DefaultFontParameters.FontStretch,
+ 32.0f,
+ !string.IsNullOrEmpty(options.DefaultFontParameters.Locale) ?
options.DefaultFontParameters.Locale : ""
+ );
+ }
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ if (glyphAtlas != null)
+ glyphAtlas.Dispose();
+ if (glyphProvider != null)
+ glyphProvider.Dispose();
+ if (renderStates != null)
+ renderStates.Dispose();
+ if (glyphDrawer != null)
+ glyphDrawer.Dispose();
+ if (defaultFormat != null)
+ defaultFormat.Dispose();
+
+ glyphAtlas = null;
+ glyphProvider = null;
+ renderStates = null;
+ glyphDrawer = null;
+ defaultFormat = null;
+ }
+
+ /// <summary>
+ /// Draws a string of text.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ /// <param name="text">The text to draw.</param>
+ /// <param name="size">The font size.</param>
+ /// <param name="x">The X position.</param>
+ /// <param name="y">The Y position.</param>
+ /// <param name="color">The font color.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ public void DrawText(DeviceContext context, string text, float
size, float x, float y, int color, TextOptions flags)
+ {
+ DrawText(context, text, null, size, x, y, color, flags);
+ }
+
+ /// <summary>
+ /// Draws a string of text.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ /// <param name="text">The text to draw.</param>
+ /// <param name="fontFamily">The font family.</param>
+ /// <param name="size">The font size.</param>
+ /// <param name="x">The X position.</param>
+ /// <param name="y">The Y position.</param>
+ /// <param name="color">The font color.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ public void DrawText(DeviceContext context, string text, string
fontFamily, float size, float x, float y, int color, TextOptions flags)
+ {
+ flags |= TextOptions.NoWordWrap;
+
+ var layout = CreateTextLayout(text, fontFamily, size, new
RectangleF(x, y, 0, 0), flags);
+ DrawTextLayout(context, layout, x, y, color, flags);
+
+ layout.Dispose();
+ }
+
+ /// <summary>
+ /// Draws a string of text.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ /// <param name="text">The text to draw.</param>
+ /// <param name="fontFamily">The font family.</param>
+ /// <param name="size">The font size.</param>
+ /// <param name="layoutBounds">The layout bounds.</param>
+ /// <param name="color">The font color.</param>
+ /// <param name="clipBounds">The clipping bounds.</param>
+ /// <param name="transformMatrix">The transform matrix.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ public void DrawText(DeviceContext context, string text, string
fontFamily, float size, RectangleF layoutBounds, int color, RectangleF
clipBounds, Matrix transformMatrix, TextOptions flags)
+ {
+ var layout = CreateTextLayout(text, fontFamily, size,
layoutBounds, flags);
+ DrawTextLayout(context, layout, layoutBounds.X,
layoutBounds.Y, color, clipBounds, transformMatrix, flags);
+
+ layout.Dispose();
+ }
+
+ /// <summary>
+ /// Draws a span of formatted text.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ /// <param name="textLayout">The formatted text span to
draw.</param>
+ /// <param name="originX">The X origin of the text.</param>
+ /// <param name="originY">The Y origin of the text.</param>
+ /// <param name="color">The text color.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ public void DrawTextLayout(DeviceContext context, TextLayout
textLayout, float originX, float originY, int color, TextOptions flags)
+ {
+ bool needsGeometry = (flags & TextOptions.AnalyzeOnly) == 0 &&
(flags & TextOptions.CacheOnly) == 0;
+ if (needsGeometry)
+ geometry.Clear();
+
+ AnalyzeTextLayout(context, textLayout, originX, originY,
color, flags, geometry);
+ if (needsGeometry)
+ unsafe { DrawGeometry(context, geometry, null, null,
flags); }
+ }
+
+ /// <summary>
+ /// Draws a span of formatted text.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ /// <param name="textLayout">The formatted text span to
draw.</param>
+ /// <param name="originX">The X origin of the text.</param>
+ /// <param name="originY">The Y origin of the text.</param>
+ /// <param name="color">The text color.</param>
+ /// <param name="clipBounds">The clipping bounds.</param>
+ /// <param name="transformMatrix">The transform matrix.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ public void DrawTextLayout(DeviceContext context, TextLayout
textLayout, float originX, float originY, int color, RectangleF clipBounds,
Matrix transformMatrix, TextOptions flags)
+ {
+ bool needsGeometry = (flags & TextOptions.AnalyzeOnly) == 0 &&
(flags & TextOptions.CacheOnly) == 0;
+ if (needsGeometry)
+ geometry.Clear();
+
+ AnalyzeTextLayout(context, textLayout, originX, originY,
color, flags, geometry);
+ if (needsGeometry)
+ unsafe { DrawGeometry(context, geometry, &clipBounds,
&transformMatrix, flags); }
+ }
+
+ /// <summary>
+ /// Measures the text and returns the bounding metrics.
+ /// </summary>
+ /// <param name="text">The text to measure.</param>
+ /// <param name="fontFamily">The font family.</param>
+ /// <param name="size">The font size.</param>
+ /// <param name="layoutBounds">The layout bounds.</param>
+ /// <param name="flags">Text rendering flags.</param>
+ /// <returns>A rectangle denoting the extents of the measured
text.</returns>
+ public RectangleF MeasureText(string text, string fontFamily,
float size, RectangleF layoutBounds, TextOptions flags)
+ {
+ var result = new RectangleF(layoutBounds.Left,
layoutBounds.Top, 0, 0);
+ var layout = CreateTextLayout(text, fontFamily, size,
layoutBounds, flags);
+ var metrics = layout.OverhangMetrics;
+
+ result.X = (float)Math.Floor(layoutBounds.Left - metrics.Left);
+ result.Y = (float)Math.Floor(layoutBounds.Top - metrics.Top);
+ result.Width = (float)Math.Ceiling(layoutBounds.Left +
metrics.Right) - result.X;
+ result.Height = (float)Math.Ceiling(layoutBounds.Top +
metrics.Bottom) - result.Y;
+
+ return result;
+ }
+
+ /// <summary>
+ /// Flushes the glyph atlas.
+ /// </summary>
+ /// <param name="context">The rendering context.</param>
+ public void Flush(DeviceContext context)
+ {
+ glyphAtlas.Flush(context);
+ }
+
+ TextLayout CreateTextLayout(string text, string fontFamily, float
size, RectangleF bounds, TextOptions flags)
+ {
+ if (defaultFormat == null)
+ throw new InvalidOperationException("No default format set
for font.");
+
+ var layout = new TextLayout(factory, text, defaultFormat,
bounds.Width, bounds.Height);
+ var range = new TextRange(0, text.Length);
+
+ layout.SetFontSize(size, range);
+ if (fontFamily != null)
+ layout.SetFontFamilyName(fontFamily, range);
+
+ if ((flags & TextOptions.NoWordWrap) != 0)
+ layout.WordWrapping = WordWrapping.NoWrap;
+
+ if ((flags & TextOptions.Right) != 0)
+ layout.TextAlignment = TextAlignment.Trailing;
+ else if ((flags & TextOptions.Center) != 0)
+ layout.TextAlignment = TextAlignment.Center;
+
+ if ((flags & TextOptions.Bottom) != 0)
+ layout.ParagraphAlignment = ParagraphAlignment.Far;
+ else if ((flags & TextOptions.VerticalCenter) != 0)
+ layout.ParagraphAlignment = ParagraphAlignment.Center;
+
+ return layout;
+ }
+
+ void AnalyzeTextLayout(DeviceContext context, TextLayout layout,
float originX, float originY, int color, TextOptions flags, TextGeometry
geometry)
+ {
+ renderer.DrawTextLayout(layout, originX, originY, color,
flags, geometry);
+
+ // flush the glyph atlas in case any new glyphs were added
+ if ((flags & TextOptions.NoFlush) == 0)
+ glyphAtlas.Flush(context);
+ }
+
+ unsafe void DrawGeometry(DeviceContext context, TextGeometry
geometry, RectangleF* clipBounds, Matrix* transformMatrix, TextOptions
flags)
+ {
+ var vertexData = geometry.GetGlyphVertices();
+ if (vertexData.Vertices.Length > 0 || (flags &
TextOptions.RestoreState) == 0)
+ {
+ // check if we can use geometry shaders
+ if (featureLevel < FeatureLevel.Level_10_0 |
| !renderStates.HasGeometryShader)
+ flags |= TextOptions.NoGeometryShader;
+
+ using (new StateSaver(context, (flags &
TextOptions.RestoreState) != 0))
+ {
+ // set states and shaders
+ if ((flags & TextOptions.StatePrepared) == 0)
+ renderStates.SetStates(context, flags);
+ if ((flags & TextOptions.ConstantsPrepared) == 0)
+ renderStates.UpdateShaderConstants(context,
clipBounds, transformMatrix);
+
+ // draw glyphs
+ glyphDrawer.DrawVertices(context, glyphAtlas,
vertexData, flags);
+ }
+ }
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Geometry/GlyphVertex.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimMath;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ struct GlyphVertex
+ {
+ public static readonly int SizeInBytes =
Marshal.SizeOf(typeof(GlyphVertex));
+
+ public Vector2 Position;
+ public int GlyphIndex;
+ public int Color;
+ }
+
+ struct VertexData
+ {
+ public int[] VertexCounts;
+ public GlyphVertex[] Vertices;
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Geometry/TextGeometry.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ /// <summary>
+ /// Maintains the geometry for a set of glyphs.
+ /// </summary>
+ class TextGeometry
+ {
+ List<GlyphVertex> vertices = new List<GlyphVertex>();
+ GlyphVertex[] sortedVertices;
+ int[] vertexCounts;
+ int maxSheetIndex;
+ bool sorted;
+
+ public void Clear()
+ {
+ vertices.Clear();
+ maxSheetIndex = 0;
+ sorted = false;
+ }
+
+ public void AddVertex(GlyphVertex vertex)
+ {
+ vertices.Add(vertex);
+ maxSheetIndex = Math.Max(maxSheetIndex, vertex.GlyphIndex >>
16);
+ sorted = false;
+ }
+
+ public VertexData GetGlyphVertices()
+ {
+ var result = new VertexData();
+ if (vertices.Count == 0)
+ return result;
+
+ int sheetCount = maxSheetIndex + 1;
+ if (!sorted)
+ {
+ // sort the vertices and keep track of counts for each
sheet
+ sortedVertices = new GlyphVertex[vertices.Count];
+ vertexCounts = new int[sheetCount];
+ var indices = new int[sheetCount];
+ foreach (var vertex in vertices)
+ vertexCounts[vertex.GlyphIndex >> 16]++;
+
+ int currentIndex = 0;
+ for (int i = 0; i < sheetCount; i++)
+ {
+ indices[i] = currentIndex;
+ currentIndex += vertexCounts[i];
+ }
+
+ foreach (var vertex in vertices)
+ {
+ int sheetIndex = vertex.GlyphIndex >> 16;
+ int index = indices[sheetIndex];
+ sortedVertices[index] = vertex;
+ sortedVertices[index].GlyphIndex &= 0xffff;
+
+ indices[sheetIndex]++;
+ }
+
+ sorted = true;
+ }
+
+ result.VertexCounts = vertexCounts;
+ result.Vertices = sortedVertices;
+
+ return result;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Geometry/TextRenderer.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.DirectWrite;
+using System.Runtime.InteropServices;
+using SlimMath;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ /// <summary>
+ /// Converts formatted spans of text into geometry.
+ /// </summary>
+ class TextRenderer : ITextRenderer
+ {
+ GlyphProvider glyphProvider;
+ FontFace cachedFontFace;
+ float cachedFontSize;
+ GlyphMap cachedGlyphMap;
+ TextOptions currentFlags;
+ int currentColor;
+
+ public TextRenderer(GlyphProvider glyphProvider)
+ {
+ this.glyphProvider = glyphProvider;
+ currentColor = unchecked((int)0xff000000);
+ }
+
+ public void DrawTextLayout(TextLayout layout, float originX, float
originY, int color, TextOptions flags, TextGeometry geometry)
+ {
+ currentColor = color;
+ currentFlags = flags;
+ cachedGlyphMap = null;
+ cachedFontFace = null;
+ cachedFontSize = 0.0f;
+
+ var handle = GCHandle.Alloc(geometry);
+
+ try
+ {
+ layout.Draw(GCHandle.ToIntPtr(handle), this, originX,
originY);
+ }
+ finally
+ {
+ handle.Free();
+ }
+ }
+
+ public Result DrawGlyphRun(IntPtr drawingContext, float
baselineOriginX, float baselineOriginY, MeasuringMode measuringMode,
GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, IntPtr
clientDrawingEffect)
+ {
+ GlyphMap glyphMap;
+ if (glyphRun.FontFace == cachedFontFace && glyphRun.FontSize
== cachedFontSize)
+ glyphMap = cachedGlyphMap;
+ else
+ {
+ glyphMap =
glyphProvider.GetGlyphMapFromFont(glyphRun.FontFace, glyphRun.FontSize,
currentFlags);
+
+ // cache the glyph map as it's likely to be used in
subsequent glyph runs
+ cachedGlyphMap = glyphMap;
+ cachedFontFace = glyphRun.FontFace;
+ cachedFontSize = glyphRun.FontSize;
+ }
+
+ // skip if we're not interested in drawing
+ if ((currentFlags & TextOptions.AnalyzeOnly) != 0)
+ return ResultCode.Success;
+
+ // check if we should draw or if we should just cache the
glyphs
+ if ((currentFlags & TextOptions.CacheOnly) != 0)
+ {
+ for (int i = 0; i < glyphRun.GlyphCount; i++)
+ glyphProvider.GetAtlasIdFromGlyphIndex(glyphMap,
glyphRun.GlyphIndices[i], glyphRun.FontFace, currentFlags);
+ }
+ else
+ {
+ var vertex = new GlyphVertex();
+ vertex.Position.Y = (float)Math.Floor(baselineOriginY +
0.5f);
+ vertex.Color = currentColor;
+
+ // add a vertex for each glyph in the run
+ var geometry =
(TextGeometry)GCHandle.FromIntPtr(drawingContext).Target;
+ if (geometry != null)
+ {
+ float x = (float)Math.Floor(baselineOriginX + 0.5f);
+ for (int i = 0; i < glyphRun.GlyphCount; i++)
+ {
+ vertex.GlyphIndex =
glyphProvider.GetAtlasIdFromGlyphIndex(glyphMap, glyphRun.GlyphIndices[i],
glyphRun.FontFace, currentFlags);
+ if ((glyphRun.BidiLevel & 0x1) != 0)
+ x -= glyphRun.GlyphAdvances[i];
+
+ vertex.Position.X = (float)Math.Floor(x + 0.5f);
+ geometry.AddVertex(vertex);
+
+ if ((glyphRun.BidiLevel & 0x1) == 0)
+ x += glyphRun.GlyphAdvances[i];
+ }
+ }
+ }
+
+ return ResultCode.Success;
+ }
+
+ public Result DrawInlineObject(IntPtr drawingContext, float
baselineOriginX, float baselineOriginY, InlineObject inlineObject, bool
isSideways, bool isRightToLeft, IntPtr clientDrawingEffect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result DrawStrikethrough(IntPtr drawingContext, float
baselineOriginX, float baselineOriginY, Strikethrough strikethrough, IntPtr
clientDrawingEffect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result DrawUnderline(IntPtr drawingContext, float
baselineOriginX, float baselineOriginY, Underline underline, IntPtr
clientDrawingEffect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Matrix3x2 GetCurrentTransform(IntPtr drawingContext)
+ {
+ return Matrix3x2.Identity;
+ }
+
+ public float GetPixelsPerDip(IntPtr drawingContext)
+ {
+ return 96.0f;
+ }
+
+ public bool IsPixelSnappingDisabled(IntPtr drawingContext)
+ {
+ return false;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphAtlas.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.Direct3D11;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ class GlyphAtlas : IDisposable
+ {
+ GlyphSheet[] glyphSheets;
+ int currentSheetIndex;
+ int sheetCount;
+ int maxSheetCount = 4096;
+ Device device;
+ int sheetWidth;
+ int sheetHeight;
+ bool hardwareCoordBuffer;
+ bool allowOversizedGlyph;
+ int mipLevelCount;
+ int maxGlyphCount;
+ int flushedSheetIndex;
+
+ public GlyphAtlas(Device device, int sheetWidth, int sheetHeight,
bool useCoordBuffer, bool allowOversizedGlyph, int maxGlyphCount, int
mipLevelCount, int maxSheetCount)
+ {
+ this.device = device;
+ this.sheetWidth = sheetWidth;
+ this.sheetHeight = sheetHeight;
+ this.hardwareCoordBuffer = useCoordBuffer;
+ this.allowOversizedGlyph = allowOversizedGlyph;
+ this.mipLevelCount = mipLevelCount;
+ this.maxGlyphCount = maxGlyphCount;
+
+ if (maxSheetCount > 0 && maxSheetCount < 655536)
+ this.maxSheetCount = maxSheetCount;
+ glyphSheets = new GlyphSheet[this.maxSheetCount];
+
+ // create a default glyph
+ unsafe
+ {
+ byte* glyph0 = stackalloc byte[256];
+ for (int i = 0; i < 256; i++)
+ glyph0[i] = 0xff;
+
+ var metrics =
+ InsertGlyph(new GlyphImageData
+ {
+ GlyphPixels = new IntPtr(glyph0),
+ RowPitch = 16,
+ PixelStride = 1,
+ Metrics = new GlyphBounds
+ {
+ Width = 16,
+ Height = 16
+ }
+ });
+ }
+ }
+
+ public void Dispose()
+ {
+ if (glyphSheets != null)
+ {
+ foreach (var sheet in glyphSheets)
+ {
+ if (sheet != null)
+ sheet.Dispose();
+ }
+ }
+
+ glyphSheets = null;
+ }
+
+ public void Flush(DeviceContext context)
+ {
+ int first = flushedSheetIndex;
+ flushedSheetIndex = currentSheetIndex;
+
+ for (int i = first; i < sheetCount; i++)
+ glyphSheets[i].Flush(context);
+ }
+
+ public GlyphCoords[] GetGlyphCoords(int sheetIndex)
+ {
+ if (sheetIndex < sheetCount)
+ return glyphSheets[sheetIndex].GetGlyphCoords();
+
+ return null;
+ }
+
+ public void BindSheet(DeviceContext context, int sheetIndex,
TextOptions flags)
+ {
+ if (sheetIndex < sheetCount)
+ glyphSheets[sheetIndex].BindSheet(context, flags);
+ }
+
+ public int InsertGlyph(GlyphImageData data)
+ {
+ int sheetIndex = 0;
+ int index = int.MaxValue;
+ int start = currentSheetIndex;
+ int end = sheetCount;
+
+ // attempt to insert the glyph
+ for (int i = start; i < end; i++)
+ {
+ index = glyphSheets[i].InsertGlyph(data);
+ if (index != int.MaxValue)
+ {
+ sheetIndex = i;
+ break;
+ }
+ }
+
+ // try to create a new glyph sheet on failure
+ if (index == int.MaxValue && sheetCount < maxSheetCount)
+ {
+ var sheet = new GlyphSheet(device, sheetWidth,
sheetHeight, hardwareCoordBuffer, allowOversizedGlyph, maxGlyphCount,
mipLevelCount);
+ index = sheet.InsertGlyph(data);
+
+ int newSheetIndex = InsertSheet(sheet);
+ if (newSheetIndex != int.MaxValue)
+ sheetIndex = newSheetIndex;
+ else
+ index = int.MaxValue;
+ }
+
+ if (index == int.MaxValue)
+ return int.MaxValue;
+
+ return (sheetIndex << 16) | index;
+ }
+
+ int InsertSheet(GlyphSheet sheet)
+ {
+ int sheetIndex = int.MaxValue;
+ if (sheetCount < maxSheetCount)
+ {
+ sheetIndex = sheetCount;
+ glyphSheets[sheetIndex] = sheet;
+ sheetCount++;
+
+ int activeCount = 4;
+ if (sheetCount > currentSheetIndex + activeCount)
+ {
+ glyphSheets[currentSheetIndex].CloseSheet();
+ currentSheetIndex++;
+ }
+ }
+
+ return sheetIndex;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphData.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ struct GlyphBounds
+ {
+ public float OffsetX;
+ public float OffsetY;
+ public int Width;
+ public int Height;
+ }
+
+ struct GlyphCoords
+ {
+ public static readonly int SizeInBytes =
Marshal.SizeOf(typeof(GlyphCoords));
+
+ public float TexCoordLeft;
+ public float TexCoordTop;
+ public float TexCoordRight;
+ public float TexCoordBottom;
+ public float PositionLeft;
+ public float PositionTop;
+ public float PositionRight;
+ public float PositionBottom;
+ };
+
+ class GlyphImageData
+ {
+ public GlyphBounds Metrics;
+ public IntPtr GlyphPixels;
+ public int RowPitch;
+ public int PixelStride;
+ };
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphProvider.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.DirectWrite;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ using FontKey = Tuple<FontFace, TextOptions, float>;
+ using SlimDX.DirectWrite;
+
+ class GlyphMap
+ {
+ public float FontSize;
+ public TextOptions Flags;
+ public int[] Glyphs;
+ }
+
+ /// <summary>
+ /// Handles matching glyphs to images in an atlas.
+ /// </summary>
+ class GlyphProvider : IDisposable
+ {
+ Dictionary<FontKey, GlyphMap> fontMap = new Dictionary<FontKey,
GlyphMap>();
+ RenderTargetWrapper renderTarget;
+ GlyphAtlas glyphAtlas;
+
+ public GlyphProvider(Factory factory, GlyphAtlas glyphAtlas, int
maxGlyphWidth, int maxGlyphHeight)
+ {
+ this.glyphAtlas = glyphAtlas;
+
+ int renderTargetWidth = 384;
+ if (maxGlyphWidth > 0 && maxGlyphWidth <= 8192)
+ renderTargetWidth = maxGlyphWidth;
+ int renderTargetHeight = 384;
+ if (maxGlyphHeight > 0 && maxGlyphHeight <= 8192)
+ renderTargetHeight = maxGlyphHeight;
+
+ renderTarget = new RenderTargetWrapper(factory,
renderTargetWidth, renderTargetHeight);
+ }
+
+ public void Dispose()
+ {
+ if (renderTarget != null)
+ renderTarget.Dispose();
+ renderTarget = null;
+
+ foreach (var key in fontMap.Keys)
+ key.Item1.Dispose();
+ fontMap.Clear();
+ }
+
+ public GlyphMap GetGlyphMapFromFont(FontFace fontFace, float
fontSize, TextOptions flags)
+ {
+ GlyphMap glyphMap;
+ var key = new FontKey(fontFace, flags & TextOptions.Aliased,
fontSize);
+ if (fontMap.TryGetValue(key, out glyphMap))
+ return glyphMap;
+
+ if ((flags & TextOptions.NoNewGlyphs) != 0)
+ return null;
+
+ glyphMap = new GlyphMap()
+ {
+ Flags = flags,
+ FontSize = fontSize,
+ Glyphs = Enumerable.Repeat(int.MaxValue,
fontFace.GlyphCount).ToArray()
+ };
+
+ fontMap.Add(key, glyphMap);
+ InsertNewGlyph(glyphMap, 0, fontFace);
+
+ return glyphMap;
+ }
+
+ public int GetAtlasIdFromGlyphIndex(GlyphMap glyphMap, int index,
FontFace fontFace, TextOptions flags)
+ {
+ if (glyphMap == null || index >= glyphMap.Glyphs.Length)
+ return 0;
+
+ int atlasId = glyphMap.Glyphs[index];
+ if (atlasId == int.MaxValue && (flags &
TextOptions.NoNewGlyphs) == 0)
+ atlasId = InsertNewGlyph(glyphMap, index, fontFace);
+
+ // fallback to the default glyph on failure
+ if (atlasId == int.MaxValue)
+ {
+ atlasId = glyphMap.Glyphs[0];
+ if ((flags & TextOptions.NoNewGlyphs) == 0)
+ {
+ if (atlasId == int.MaxValue)
+ atlasId = index == 0 ? 0 :
GetAtlasIdFromGlyphIndex(glyphMap, 0, fontFace, flags);
+ glyphMap.Glyphs[index] = atlasId;
+ }
+
+ if (atlasId == int.MaxValue)
+ atlasId = 0;
+ }
+
+ return atlasId;
+ }
+
+ int InsertNewGlyph(GlyphMap glyphMap, int index, FontFace fontFace)
+ {
+ var renderingMode = RenderingMode.Default;
+ var measuringMode = MeasuringMode.Natural;
+ if ((glyphMap.Flags & TextOptions.Aliased) != 0)
+ {
+ renderingMode = RenderingMode.Aliased;
+ measuringMode = MeasuringMode.GdiClassic;
+ }
+
+ var data = renderTarget.DrawGlyph(fontFace, index,
glyphMap.FontSize, renderingMode, measuringMode);
+ int atlasId = glyphMap.Glyphs[index];
+ if (atlasId == int.MaxValue)
+ {
+ atlasId = glyphAtlas.InsertGlyph(data);
+ glyphMap.Glyphs[index] = atlasId;
+ }
+
+ return atlasId;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Glyphs/GlyphSheet.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,320 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using SlimDX.Direct3D11;
+using Device = SlimDX.Direct3D11.Device;
+using Buffer = SlimDX.Direct3D11.Buffer;
+using Resource = SlimDX.Direct3D11.Resource;
+using SlimDX.DXGI;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ /// <summary>
+ /// Represents a texture containing a sheet of glyph images.
+ /// </summary>
+ class GlyphSheet : IDisposable
+ {
+ bool closed;
+ bool isStatic;
+ int count;
+ int maxCount = 2048;
+ int updatedCount;
+ int alignWidth;
+ int sheetWidth;
+ int sheetHeight;
+ int mipCount;
+ bool allowOversizedGlyph;
+ bool hardwareCoordBuffer;
+ GlyphCoords[] glyphCoords;
+ DataStream textureData;
+ Rectangle dirtyRect;
+ HeightRange heightRange;
+ Buffer coordBuffer;
+ Texture2D texture;
+ ShaderResourceView textureView;
+ ShaderResourceView bufferView;
+
+ public GlyphSheet(Device device, int sheetWidth, int sheetHeight,
bool useCoordBuffer, bool allowOversizedGlyph, int maxGlyphCount, int
mipLevelCount)
+ {
+ this.sheetWidth = sheetWidth > 0 ? sheetWidth : 512;
+ this.sheetHeight = sheetHeight > 0 ? sheetHeight : 512;
+ this.allowOversizedGlyph = allowOversizedGlyph;
+
+ if (useCoordBuffer && device.FeatureLevel >=
FeatureLevel.Level_10_0)
+ hardwareCoordBuffer = true;
+
+ if (maxGlyphCount > 0 && maxGlyphCount < 65535)
+ this.maxCount = maxGlyphCount;
+
+ if (mipLevelCount > 1)
+ {
+ // reasonable mip limit considering borders
+ mipCount = Math.Min(mipLevelCount, 5);
+ alignWidth = (1 << (mipCount - 1));
+ }
+ else
+ {
+ mipCount = 1;
+ alignWidth = 1;
+ }
+
+ // Storage
+ int textureSize = sheetWidth * sheetHeight;
+ int mipSize = textureSize;
+ for (int i = 1; i < mipCount; i++)
+ {
+ mipSize >>= 2;
+ textureSize += mipSize;
+ }
+
+ textureData = new DataStream(textureSize, true, true);
+ for (int i = 0; i < textureSize; i++)
+ textureData.WriteByte(0);
+
+ glyphCoords = new GlyphCoords[maxCount];
+ heightRange = new HeightRange(sheetWidth / alignWidth);
+
+ CreateDeviceResources(device);
+ }
+
+ public void Dispose()
+ {
+ if (bufferView != null)
+ bufferView.Dispose();
+
+ if (coordBuffer != null)
+ coordBuffer.Dispose();
+
+ if (textureView != null)
+ textureView.Dispose();
+
+ if (texture != null)
+ texture.Dispose();
+
+ if (textureData != null)
+ textureData.Dispose();
+
+ bufferView = null;
+ coordBuffer = null;
+ textureView = null;
+ texture = null;
+ textureData = null;
+ }
+
+ public int InsertGlyph(GlyphImageData glyph)
+ {
+ if (closed || count >= maxCount)
+ return int.MaxValue;
+
+ // position the glyph
+ int width = glyph.Metrics.Width;
+ int height = glyph.Metrics.Height;
+ int blockWidth = width / alignWidth + 1;
+ int blockHeight = height / alignWidth + 1;
+
+ if (width % alignWidth != 0)
+ blockWidth++;
+ if (height % alignWidth != 0)
+ blockHeight++;
+
+ int blockX, blockY;
+ int positionX, positionY;
+ if (count > 0 || !allowOversizedGlyph)
+ {
+ if (alignWidth + width + alignWidth > sheetWidth)
+ return int.MaxValue;
+
+ // position the glyph at the lowest Y possible (fills
reasonably well with different sized glyphs)
+ blockX = heightRange.FindMin(blockWidth, out blockY);
+
+ positionX = alignWidth + blockX * alignWidth;
+ positionY = alignWidth + blockY * alignWidth;
+
+ if (positionY + height + alignWidth > sheetHeight)
+ return int.MaxValue;
+ }
+ else
+ {
+ blockX = blockY = 0;
+ positionX = positionY = alignWidth;
+ }
+
+ heightRange.Update(blockX, blockWidth, blockY + blockHeight);
+
+ // store glyph coordinates
+ float coordOffset = alignWidth * 0.5f;
+ int alignedWidth = (blockWidth - 1) * alignWidth;
+ int alignedHeight = (blockHeight - 1) * alignWidth;
+ var coords = new GlyphCoords()
+ {
+ TexCoordLeft = (positionX - coordOffset) /
(float)sheetWidth,
+ TexCoordTop = (positionY - coordOffset) /
(float)sheetHeight,
+ TexCoordRight = (positionX + alignedWidth + coordOffset) /
(float)sheetWidth,
+ TexCoordBottom = (positionY + alignedHeight + coordOffset)
/ (float)sheetHeight,
+ PositionLeft = glyph.Metrics.OffsetX - coordOffset,
+ PositionTop = glyph.Metrics.OffsetY - coordOffset,
+ PositionRight = glyph.Metrics.OffsetX + alignedWidth +
coordOffset,
+ PositionBottom = glyph.Metrics.OffsetY + alignedHeight +
coordOffset
+ };
+
+ int index = count;
+ glyphCoords[index] = coords;
+
+ // blit the pixels
+ unsafe
+ {
+ for (int i = 0; i < height && i < sheetHeight - positionY;
i++)
+ {
+ byte* src = (byte*)glyph.GlyphPixels + (i *
glyph.RowPitch);
+ byte* dest = (byte*)textureData.DataPointer +
(positionY + i) * sheetWidth + positionX;
+ for (int j = 0; j < width && j < sheetWidth -
positionX; j++)
+ dest[j] = src[j * glyph.PixelStride];
+ }
+ }
+
+ // update the dirty rect to be flushed to device texture
+ if (updatedCount == 0)
+ {
+ dirtyRect.X = positionX - alignWidth;
+ dirtyRect.Y = positionY - alignWidth;
+ dirtyRect.Width = Math.Min(positionX + width + alignWidth,
sheetWidth) - dirtyRect.X;
+ dirtyRect.Height = Math.Min(positionY + height +
alignWidth, sheetHeight) - dirtyRect.Y;
+ }
+ else
+ {
+ dirtyRect.X = Math.Min(dirtyRect.Left, positionX -
alignWidth);
+ dirtyRect.Y = Math.Min(dirtyRect.Y, positionY -
alignWidth);
+ dirtyRect.Width = Math.Min(Math.Max(dirtyRect.Right,
positionX + width + alignWidth), sheetWidth) - dirtyRect.X;
+ dirtyRect.Height = Math.Min(Math.Max(dirtyRect.Bottom,
positionY + height + alignWidth), sheetHeight) - dirtyRect.Y;
+ }
+
+ count++;
+ updatedCount++;
+
+ return index;
+ }
+
+ public void BindSheet(DeviceContext context, TextOptions flags)
+ {
+ context.PixelShader.SetShaderResource(textureView, 0);
+ if ((flags & TextOptions.NoGeometryShader) == 0 &&
hardwareCoordBuffer)
+ context.GeometryShader.SetShaderResource(bufferView, 0);
+ }
+
+ public GlyphCoords[] GetGlyphCoords()
+ {
+ return glyphCoords;
+ }
+
+ public void CloseSheet()
+ {
+ closed = true;
+ }
+
+ public void Flush(DeviceContext context)
+ {
+ if (isStatic)
+ return;
+
+ if (updatedCount > 0)
+ {
+ // update the coordinate buffer
+ if (hardwareCoordBuffer)
+ {
+ int startIndex = count - updatedCount;
+ int endIndex = count;
+
+ var data = new DataStream(glyphCoords, true, false);
+ data.Position += startIndex;
+ context.UpdateSubresource(new DataBox(0, 0, data),
coordBuffer, 0, new ResourceRegion(startIndex * GlyphCoords.SizeInBytes, 0,
0, endIndex * GlyphCoords.SizeInBytes, 1, 1));
+ data.Close();
+ }
+
+ // update the texture
+ if (dirtyRect.Right > dirtyRect.Left && dirtyRect.Bottom >
dirtyRect.Top)
+ {
+ textureData.Position = 0;
+ var region = new ResourceRegion(dirtyRect.Left,
dirtyRect.Top, 0, dirtyRect.Right, dirtyRect.Bottom, 1);
+
+ // update each mip level
+ for (int i = 0; i < mipCount; i++)
+ {
+ long oldPosition = textureData.Position;
+ textureData.Position += region.Top * (sheetWidth
>> i) + region.Left;
+
+ context.UpdateSubresource(new DataBox(sheetWidth
>> i, 0, textureData), texture, Resource.CalculateSubresourceIndex(i, 0,
mipCount), region);
+ if (i + 1 < mipCount)
+ {
+ region.Left >>= 1;
+ region.Right >>= 1;
+ region.Top >>= 1;
+ region.Bottom >>= 1;
+
+ long nextMip = oldPosition + (sheetWidth >> i)
* (sheetHeight >> i);
+ for (int j = region.Top; j < region.Bottom;
j++) unsafe
+ {
+ byte* src0 =
(byte*)textureData.Position + oldPosition + j * 2 * (sheetWidth >> i);
+ byte* src1 = src0 + (sheetWidth >> i);
+ byte* dst =
(byte*)textureData.Position + nextMip + j * (sheetWidth >> (i + 1));
+
+ for (int k = region.Left; k <
region.Right; k++)
+ {
+ int src = src0[k * 2] + src0[k * 2
+ 1] + src1[k * 2] + src1[k * 2 + 1];
+ dst[k] = (byte)(src >> 2);
+ }
+ }
+
+ textureData.Position = nextMip;
+ }
+ }
+ }
+ }
+
+ updatedCount = 0;
+ if (closed)
+ {
+ // the sheet is now static; save memory
+ isStatic = true;
+ textureData.Dispose();
+ textureData = null;
+ }
+ }
+
+ void CreateDeviceResources(Device device)
+ {
+ texture = new Texture2D(device, new Texture2DDescription
+ {
+ Width = sheetWidth,
+ Height = sheetHeight,
+ ArraySize = 1,
+ Format = Format.R8_UNorm,
+ SampleDescription = new SampleDescription(1, 0),
+ Usage = ResourceUsage.Default,
+ MipLevels = mipCount,
+ BindFlags = BindFlags.ShaderResource
+ });
+
+ textureView = new ShaderResourceView(device, texture);
+
+ if (hardwareCoordBuffer)
+ {
+ coordBuffer = new Buffer(device, new BufferDescription
+ {
+ SizeInBytes = maxCount * GlyphCoords.SizeInBytes,
+ Usage = ResourceUsage.Default,
+ BindFlags = BindFlags.ShaderResource
+ });
+
+ bufferView = new ShaderResourceView(device, coordBuffer,
new ShaderResourceViewDescription
+ {
+ Format = Format.R32G32B32A32_Float,
+ Dimension = ShaderResourceViewDimension.Buffer,
+ ElementOffset = 0,
+ ElementWidth = maxCount * 2 // two float4 per
glyphcoords
+ });
+ }
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Glyphs/HeightRange.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ class HeightRange
+ {
+ int totalWidth;
+ int[] heights;
+
+ public HeightRange(int totalWidth)
+ {
+ this.totalWidth = totalWidth;
+ heights = new int[totalWidth];
+ }
+
+ public int FindMin(int width, out int minY)
+ {
+ if (width > totalWidth)
+ width = totalWidth;
+
+ int currentMax = FindMax(0, width);
+ int currentMin = currentMax;
+ int minX = 0;
+
+ for (int i = 1; i < totalWidth - width; i++)
+ {
+ if (heights[i + width - 1] >= currentMax)
+ currentMax = heights[i + width - 1];
+ else if (heights[i - 1] == currentMax)
+ {
+ currentMax = FindMax(i, width);
+ if (currentMax < currentMin)
+ {
+ currentMin = currentMax;
+ minX = i;
+ }
+ }
+ }
+
+ minY = currentMin;
+ return minX;
+ }
+
+ public int FindMax(int startX, int width)
+ {
+ int currentMax = heights[startX];
+ for (int i = 1; i < width; ++i)
+ currentMax = Math.Max(currentMax, heights[startX + i]);
+
+ return currentMax;
+ }
+
+ public void Update(int startX, int width, int height)
+ {
+ if (width > totalWidth)
+ width = totalWidth;
+
+ for (int i = 0; i < width; ++i)
+ heights[startX + i] = height;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Rendering/FontShaders.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+using SlimMath;
+using System.Drawing;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ static class FontShaders
+ {
+ public const string EmptyVertexShader = @"
+struct GSIn {
+ float3 PositionIndex : POSITIONINDEX;
+ float4 GlyphColor : GLYPHCOLOR;
+};
+
+GSIn VS(GSIn Input) {
+ return Input;
+}
+";
+ public const string SimpleVertexShader = @"
+cbuffer ShaderConstants : register(b0) {
+ float4x4 TransformMatrix : packoffset(c0);
+};
+
+struct VSIn {
+ float4 Position : POSITION;
+ float4 GlyphColor : GLYPHCOLOR;
+};
+
+struct VSOut {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+};
+
+VSOut VS(VSIn Input) {
+ VSOut Output;
+
+ Output.Position = mul(TransformMatrix, float4(Input.Position.xy, 0.0f,
1.0f));
+ Output.GlyphColor = Input.GlyphColor;
+ Output.TexCoord =
Input.Position.zw;
+
+ return Output;
+}
+";
+ public const string ClippingVertexShader = @"
+cbuffer ShaderConstants : register(b0) {
+ float4x4 TransformMatrix : packoffset(c0);
+ float4 ClipRect : packoffset(c4);
+};
+
+struct VSIn {
+ float4 Position : POSITION;
+ float4 GlyphColor : GLYPHCOLOR;
+};
+
+struct VSOut {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+ float4 ClipDistance : CLIPDISTANCE;
+};
+
+VSOut VS(VSIn Input) {
+ VSOut Output;
+
+ Output.Position = mul(TransformMatrix, float4(Input.Position.xy, 0.0f,
1.0f));
+ Output.GlyphColor = Input.GlyphColor;
+ Output.TexCoord =
Input.Position.zw;
+ Output.ClipDistance = ClipRect + float4(Input.Position.xy,
-Input.Position.xy);
+
+ return Output;
+}
+";
+ public const string SimpleGeometryShader = @"
+cbuffer ShaderConstants : register(b0) {
+ float4x4 TransformMatrix : packoffset(c0);
+};
+
+Buffer<float4> tex0 : register(t0);
+
+struct GSIn {
+ float3 PositionIndex : POSITIONINDEX;
+ float4 GlyphColor : GLYPHCOLOR;
+};
+
+struct GSOut {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+};
+
+[maxvertexcount(4)]
+void GS(point GSIn Input[1], inout TriangleStream<GSOut> TriStream) {
+ const float2 basePosition = Input[0].PositionIndex.xy;
+ const uint glyphIndex = asuint(Input[0].PositionIndex.z);
+
+ float4 texCoords = tex0.Load(uint2(glyphIndex*2, 0));
+ float4 offsets = tex0.Load(uint2(glyphIndex*2+1, 0));
+
+ GSOut Output;
+ Output.GlyphColor = Input[0].GlyphColor;
+
+ float4 positions = basePosition.xyxy + offsets;
+
+ Output.Position = mul(TransformMatrix, float4(positions.xy, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.xy;
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(positions.zy, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.zy;
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(positions.xw, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.xw;
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(
positions.zw, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.zw;
+ TriStream.Append(Output);
+
+ TriStream.RestartStrip();
+}
+";
+ public const string ClippingGeometryShader = @"
+cbuffer ShaderConstants : register(b0) {
+ float4x4 TransformMatrix : packoffset(c0);
+ float4 ClipRect : packoffset(c4);
+};
+
+Buffer<float4> tex0 : register(t0);
+
+struct GSIn {
+ float3 PositionIndex : POSITIONINDEX;
+ float4 GlyphColor : GLYPHCOLOR;
+};
+
+struct GSOut {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+ float4 ClipDistance : SV_ClipDistance;
+};
+
+[maxvertexcount(4)]
+void GS(point GSIn Input[1], inout TriangleStream<GSOut> TriStream) {
+ const float2 basePosition = Input[0].PositionIndex.xy;
+ const uint glyphIndex = asuint(Input[0].PositionIndex.z);
+
+ float4 texCoords = tex0.Load(uint2(glyphIndex*2, 0));
+ float4 offsets = tex0.Load(uint2(glyphIndex*2+1, 0));
+
+ GSOut Output;
+ Output.GlyphColor = Input[0].GlyphColor;
+
+ float4 positions = basePosition.xyxy + offsets;
+
+ Output.Position = mul(TransformMatrix, float4(positions.xy, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.xy;
+ Output.ClipDistance = ClipRect + float4(positions.xy, -positions.xy);
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(positions.zy, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.zy;
+ Output.ClipDistance = ClipRect + float4(positions.zy, -positions.zy);
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(positions.xw, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.xw;
+ Output.ClipDistance = ClipRect + float4(positions.xw, -positions.xw);
+ TriStream.Append(Output);
+
+ Output.Position = mul(TransformMatrix, float4(
positions.zw, 0.0f, 1.0f));
+ Output.TexCoord = texCoords.zw;
+ Output.ClipDistance = ClipRect + float4(
positions.zw, -
positions.zw);
+ TriStream.Append(Output);
+
+ TriStream.RestartStrip();
+}
+";
+ public const string SimplePixelShader = @"
+SamplerState sampler0 : register(s0);
+Texture2D<float> tex0 : register(t0);
+
+struct PSIn {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+};
+
+float4 PS(PSIn Input) : SV_Target {
+ float a = tex0.Sample(sampler0, Input.TexCoord);
+
+ if(a == 0.0f)
+ discard;
+
+ return (a * Input.GlyphColor.a) * float4(Input.GlyphColor.rgb, 1.0f);
+}
+";
+ public const string ClippingPixelShader = @"
+SamplerState sampler0 : register(s0);
+Texture2D<float> tex0 : register(t0);
+
+struct PSIn {
+ float4 Position : SV_Position;
+ float4 GlyphColor : COLOR;
+ float2 TexCoord : TEXCOORD;
+ float4 ClipDistance : CLIPDISTANCE;
+};
+
+float4 PS(PSIn Input) : SV_Target {
+ clip(Input.ClipDistance);
+
+ float a = tex0.Sample(sampler0, Input.TexCoord);
+
+ if(a == 0.0f)
+ discard;
+
+ return (a * Input.GlyphColor.a) * float4(Input.GlyphColor.rgb, 1.0f);
+}
+";
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Rendering/NativeMethods.cs Thu Mar
22 19:09:22 2012
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ static class NativeMethods
+ {
+ public const int OBJ_BITMAP = 7;
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RECT
+ {
+ public int left, top, right, bottom;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct BITMAP
+ {
+ public int bmType;
+ public int bmWidth;
+ public int bmHeight;
+ public int bmWidthBytes;
+ public short bmPlanes;
+ public short bmBitsPixel;
+ public IntPtr bmBits;
+ }
+
+ [DllImport("gdi32.dll")]
+ public static extern IntPtr CreateSolidBrush(uint crColor);
+
+ [DllImport("gdi32.dll")]
+ public static extern bool DeleteObject(IntPtr hObject);
+
+ [DllImport("gdi32.dll")]
+ public static extern IntPtr GetCurrentObject(IntPtr hdc, uint
uObjectType);
+
+ [DllImport("gdi32.dll")]
+ public static extern int GetObject(IntPtr hgdiobj, int cbBuffer,
IntPtr lpvObject);
+
+ [DllImport("user32.dll")]
+ public static extern int FillRect(IntPtr hDC, ref RECT lprc,
IntPtr hbr);
+
+ public static BITMAP GetBitmap(IntPtr hbitmap)
+ {
+ var result = new BITMAP();
+ unsafe { GetObject(hbitmap, Marshal.SizeOf(result), new
IntPtr(&result)); }
+ return result;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Rendering/RenderData.cs Thu Mar 22
19:09:22 2012
@@ -0,0 +1,260 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.Direct3D11;
+using Buffer = SlimDX.Direct3D11.Buffer;
+using Device = SlimDX.Direct3D11.Device;
+using MapFlags = SlimDX.Direct3D11.MapFlags;
+using System.Drawing;
+using SlimMath;
+using SlimDX.D3DCompiler;
+using SlimDX.DXGI;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ class RenderData : IDisposable
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ struct ShaderConstants
+ {
+ public static readonly int SizeInBytes =
Marshal.SizeOf(typeof(ShaderConstants));
+
+ public Matrix Transform;
+ public RectangleF ClipRect;
+ }
+
+ HashSet<IDisposable> resources = new HashSet<IDisposable>(); //
I'm lazy
+ Device device;
+ FeatureLevel featureLevel;
+ VertexShader emptyVertexShader;
+ VertexShader simpleVertexShader;
+ VertexShader clippingVertexShader;
+ PixelShader simplePixelShader;
+ PixelShader clippingPixelShader;
+ GeometryShader simpleGeometryShader;
+ GeometryShader clippingGeometryShader;
+ InputLayout quadInputLayout;
+ InputLayout pointInputLayout;
+ ConstantBuffer<ShaderConstants> constantBuffer;
+ BlendState blendState;
+ SamplerState samplerState;
+ DepthStencilState depthStencilState;
+ RasterizerState rasterizerState;
+
+ public bool HasGeometryShader { get; private set; }
+
+ public RenderData(Device device, bool useGeometryShader, bool
anisotropicFiltering)
+ {
+ this.device = device;
+ featureLevel = device.FeatureLevel;
+
+ CreateQuadShaders();
+ CreatePixelShaders();
+ CreateConstantBuffer();
+ CreateRenderStates(anisotropicFiltering);
+ if (useGeometryShader)
+ CreateGlyphShaders();
+ }
+
+ public void Dispose()
+ {
+ foreach (var resource in resources)
+ resource.Dispose();
+ resources.Clear();
+ }
+
+ public void SetStates(DeviceContext context, TextOptions flags)
+ {
+ if (HasGeometryShader && (flags &
TextOptions.NoGeometryShader) == 0)
+ {
+ // point vertices using a geometry shader
+ context.InputAssembler.PrimitiveTopology =
PrimitiveTopology.PointList;
+ context.InputAssembler.InputLayout = pointInputLayout;
+ context.VertexShader.Set(emptyVertexShader);
+ context.GeometryShader.Set((flags &
TextOptions.ClipRect) != 0 ? clippingGeometryShader : simpleGeometryShader);
+ context.PixelShader.Set(simplePixelShader);
+
context.GeometryShader.SetConstantBuffer(constantBuffer.Buffer, 0);
+ }
+ else
+ {
+ // quads constructed on the CPU
+ context.InputAssembler.PrimitiveTopology =
PrimitiveTopology.TriangleList;
+ context.InputAssembler.InputLayout = quadInputLayout;
+ context.VertexShader.Set((flags & TextOptions.ClipRect) !=
0 ? clippingVertexShader : simpleVertexShader);
+ context.PixelShader.Set((flags & TextOptions.ClipRect) !=
0 ? clippingPixelShader : simplePixelShader);
+
context.VertexShader.SetConstantBuffer(constantBuffer.Buffer, 0);
+
+ if (featureLevel >= FeatureLevel.Level_10_0)
+ context.GeometryShader.Set(null);
+ }
+
+ // clear out unused shaders
+ if (featureLevel >= FeatureLevel.Level_11_0)
+ {
+ context.DomainShader.Set(null);
+ context.HullShader.Set(null);
+ }
+
+ // set render states
+ context.OutputMerger.SetBlendState(blendState, new Color4(0),
-1);
+ context.OutputMerger.SetDepthStencilState(depthStencilState,
0);
+ context.Rasterizer.State = rasterizerState;
+ context.PixelShader.SetSampler(samplerState, 0);
+ }
+
+ public unsafe void UpdateShaderConstants(DeviceContext context,
RectangleF* clipRect, Matrix* transform)
+ {
+ var constants = new ShaderConstants();
+ if (transform != null)
+ constants.Transform = *transform;
+ else
+ {
+ float w = 512.0f;
+ float h = 512.0f;
+
+ var vp = context.Rasterizer.GetViewport();
+ if (vp.Width >= 1.0f && vp.Height >= 1.0f)
+ {
+ w = vp.Width;
+ h = vp.Height;
+ }
+
+ constants.Transform = Matrix.OrthoOffCenterLH(0.0f, w, h,
0.0f, 0.0f, 1.0f);
+ }
+
+ if (clipRect != null)
+ {
+ constants.ClipRect = *clipRect;
+ constants.ClipRect.X *= -1;
+ constants.ClipRect.Y *= -1;
+ }
+ else
+ {
+ constants.ClipRect.X = float.MaxValue;
+ constants.ClipRect.Y = float.MaxValue;
+ constants.ClipRect.Width = float.MaxValue;
+ constants.ClipRect.Height = float.MaxValue;
+ }
+
+ constantBuffer.SetData(context, ref constants);
+ }
+
+ void CreateQuadShaders()
+ {
+ // compile vertex shaders
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.SimpleVertexShader, "VS",
device.VertexShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ {
+ simpleVertexShader = new VertexShader(device, bytecode);
+ quadInputLayout = new InputLayout(device, bytecode, new[] {
+ new InputElement("POSITION", 0,
Format.R32G32B32A32_Float, 0),
+ new InputElement("GLYPHCOLOR", 0,
Format.R8G8B8A8_UNorm, 0)
+ });
+ }
+
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.ClippingVertexShader, "VS",
device.VertexShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ clippingVertexShader = new VertexShader(device, bytecode);
+
+ resources.Add(simpleVertexShader);
+ resources.Add(quadInputLayout);
+ resources.Add(clippingVertexShader);
+ }
+
+ void CreatePixelShaders()
+ {
+ // compile pixel shaders
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.SimplePixelShader, "PS",
device.PixelShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ simplePixelShader = new PixelShader(device, bytecode);
+
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.ClippingPixelShader, "PS",
device.PixelShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ clippingPixelShader = new PixelShader(device, bytecode);
+
+ resources.Add(simplePixelShader);
+ resources.Add(clippingPixelShader);
+ }
+
+ void CreateGlyphShaders()
+ {
+ if (featureLevel < FeatureLevel.Level_10_0)
+ return;
+
+ // compile geometry shaders
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.SimpleGeometryShader, "GS",
device.GeometryShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ simpleGeometryShader = new GeometryShader(device,
bytecode);
+
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.ClippingGeometryShader, "GS",
device.GeometryShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ clippingGeometryShader = new GeometryShader(device,
bytecode);
+
+ // compile empty vertex shader
+ using (var bytecode =
ShaderBytecode.Compile(FontShaders.EmptyVertexShader, "VS",
device.VertexShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ {
+ emptyVertexShader = new VertexShader(device, bytecode);
+ pointInputLayout = new InputLayout(device, bytecode, new[]
{
+ new InputElement("POSITIONINDEX", 0,
Format.R32G32B32_Float, 0),
+ new InputElement("GLYPHCOLOR", 0,
Format.B8G8R8A8_UNorm, 0)
+ });
+ }
+
+ HasGeometryShader = true;
+ resources.Add(simpleGeometryShader);
+ resources.Add(clippingGeometryShader);
+ resources.Add(emptyVertexShader);
+ resources.Add(pointInputLayout);
+ }
+
+ void CreateConstantBuffer()
+ {
+ constantBuffer = new ConstantBuffer<ShaderConstants>(device);
+ resources.Add(constantBuffer);
+ }
+
+ void CreateRenderStates(bool anisotropicFiltering)
+ {
+ var blendDesc = new BlendStateDescription();
+ for (int i = 0; i < 4; i++)
+ {
+ blendDesc.RenderTargets[i] = new
RenderTargetBlendDescription
+ {
+ BlendEnable = true,
+ SourceBlend = BlendOption.One,
+ DestinationBlend = BlendOption.InverseSourceAlpha,
+ BlendOperation = BlendOperation.Add,
+ SourceBlendAlpha = BlendOption.One,
+ DestinationBlendAlpha = BlendOption.Zero,
+ BlendOperationAlpha = BlendOperation.Add,
+ RenderTargetWriteMask = ColorWriteMaskFlags.All
+ };
+ }
+
+ blendState = BlendState.FromDescription(device, blendDesc);
+ samplerState = SamplerState.FromDescription(device, new
SamplerDescription
+ {
+ AddressU = TextureAddressMode.Clamp,
+ AddressV = TextureAddressMode.Clamp,
+ AddressW = TextureAddressMode.Clamp,
+ MaximumLod = float.MaxValue,
+ Filter = anisotropicFiltering ? Filter.Anisotropic :
Filter.MinMagMipLinear,
+ MaximumAnisotropy = anisotropicFiltering ? featureLevel >=
FeatureLevel.Level_9_2 ? 5 : 2 : 0
+ });
+
+ rasterizerState = RasterizerState.FromDescription(device, new
RasterizerStateDescription
+ {
+ FillMode = FillMode.Solid,
+ CullMode = CullMode.None,
+ IsFrontCounterclockwise = false,
+ IsDepthClipEnabled = true
+ });
+
+ depthStencilState = DepthStencilState.FromDescription(device,
new DepthStencilStateDescription
+ {
+ IsDepthEnabled = false
+ });
+
+ resources.Add(blendState);
+ resources.Add(samplerState);
+ resources.Add(rasterizerState);
+ resources.Add(depthStencilState);
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Rendering/RenderTargetWrapper.cs
Thu Mar 22 19:09:22 2012
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SlimDX.DirectWrite;
+using System.Drawing;
+using SlimMath;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ /// <summary>
+ /// Wrapper around the DWrite render target. Handles drawing of glyphs.
+ /// </summary>
+ class RenderTargetWrapper : IDisposable
+ {
+ Dictionary<RenderingMode, RenderingParameters> parameterMap = new
Dictionary<RenderingMode, RenderingParameters>();
+ IntPtr blackBrush;
+ BitmapRenderTarget renderTarget;
+ int maxWidth = 384;
+ int maxHeight = 384;
+ IntPtr hdc;
+ IntPtr bits;
+ int rowPitch;
+ int stride;
+
+ public RenderTargetWrapper(Factory factory, int renderTargetWidth,
int renderTargetHeight)
+ {
+ if (renderTargetWidth > 0)
+ maxWidth = renderTargetWidth;
+ if (renderTargetHeight > 0)
+ maxHeight = renderTargetHeight;
+
+ using (var interop = factory.GetGdiInterop())
+ {
+ renderTarget =
interop.CreateBitmapRenderTarget(IntPtr.Zero, maxWidth, maxHeight);
+ renderTarget.PixelsPerDip = 1.0f;
+
+ hdc = renderTarget.MemoryDC;
+ blackBrush = NativeMethods.CreateSolidBrush(0);
+
+ var hbitmap = NativeMethods.GetCurrentObject(hdc,
NativeMethods.OBJ_BITMAP);
+ var bitmap = NativeMethods.GetBitmap(hbitmap);
+ bits = bitmap.bmBits;
+ rowPitch = bitmap.bmWidthBytes;
+ stride = bitmap.bmBitsPixel / 8;
+ }
+
+ var modes = new[] { RenderingMode.Default,
RenderingMode.Aliased };
+ foreach (var mode in modes)
+ {
+ var renderingParams =
factory.CreateCustomRenderingParameters(1.0f, 0.0f, 0.0f,
PixelGeometry.Flat, mode);
+ parameterMap.Add(mode, renderingParams);
+ }
+ }
+
+ public void Dispose()
+ {
+ foreach (var value in parameterMap.Values)
+ value.Dispose();
+ parameterMap.Clear();
+
+ if (blackBrush != IntPtr.Zero)
+ NativeMethods.DeleteObject(blackBrush);
+
+ if (renderTarget != null)
+ renderTarget.Dispose();
+
+ blackBrush = IntPtr.Zero;
+ renderTarget = null;
+ }
+
+ public GlyphImageData DrawGlyph(FontFace fontFace, int index,
float fontSize, RenderingMode renderingMode, MeasuringMode measuringMode)
+ {
+ // calculate pixel measurements
+ var indices = new[] { (short)index };
+ var fontMetrics = fontFace.Metrics;
+ var glyphMetrics = fontFace.GetDesignGlyphMetrics(indices,
false);
+ var data = InitGlyphData(fontMetrics, glyphMetrics[0],
fontSize);
+
+ // set up drawing
+ var run = new GlyphRun()
+ {
+ FontFace = fontFace,
+ FontSize = fontSize,
+ GlyphCount = 1,
+ GlyphIndices = indices,
+ GlyphAdvances = new[] { 0.0f },
+ GlyphOffsets = new[] { new GlyphOffset() }
+ };
+
+ // clear the background
+ var br = new NativeMethods.RECT();
+ br.left = br.top = 0;
+ br.right = 2 + data.Width + 5;
+ br.bottom = 2 + data.Height + 5;
+ NativeMethods.FillRect(hdc, ref br, blackBrush);
+
+ RenderingParameters renderingParams;
+ if (!parameterMap.TryGetValue(renderingMode, out
renderingParams))
+ renderingParams = parameterMap[RenderingMode.Default];
+
+ // draw the glyph
+ Rectangle rect;
+ renderTarget.DrawGlyphRun(2.0f - data.OffsetX, 2.0f -
data.OffsetY, measuringMode, run, renderingParams, new Color4(1.0f, 1.0f,
1.0f), out rect);
+
+ // clip to valid render target to avoid buffer overruns in
case the glyph was too large
+ rect.X = Math.Max(rect.X, 0);
+ rect.Y = Math.Max(rect.Y, 0);
+ rect.Width = Math.Min(rect.Width, maxWidth);
+ rect.Height = Math.Min(rect.Height, maxHeight);
+
+ // return the glyph data
+ data.OffsetX += rect.Left - 2.0f;
+ data.OffsetY += rect.Top - 2.0f;
+ data.Width = rect.Width;
+ data.Height = rect.Height;
+
+ var result = new GlyphImageData()
+ {
+ Metrics = data,
+ GlyphPixels = bits + (rect.Y * rowPitch) + (rect.X *
stride),
+ RowPitch = rowPitch,
+ PixelStride = stride
+ };
+
+ return result;
+ }
+
+ GlyphBounds InitGlyphData(FontMetrics fontMetrics, GlyphMetrics
glyphMetrics, float fontSize)
+ {
+ // calculate pixel-space coordinates
+ float fscale = fontSize / fontMetrics.DesignUnitsPerEm;
+ float l = glyphMetrics.LeftSideBearing * fscale;
+ float t = glyphMetrics.TopSideBearing * fscale;
+ float r = glyphMetrics.RightSideBearing * fscale;
+ float b = glyphMetrics.BottomSideBearing * fscale;
+ float v = glyphMetrics.VerticalOrigin * fscale;
+ float aw = glyphMetrics.AdvanceWidth * fscale;
+ float ah = glyphMetrics.AdvanceHeight * fscale;
+
+ var result = new GlyphBounds();
+ result.OffsetX = (float)Math.Floor(l);
+ result.OffsetY = (float)(Math.Floor(t) - Math.Floor(v));
+ result.Width = (int)(aw - r - l + 2.0f);
+ result.Height = (int)(ah - b - t + 2.0f);
+
+ return result;
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Fonts/Rendering/VertexDrawer.cs Thu Mar
22 19:09:22 2012
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+using SlimMath;
+using SlimDX.Direct3D11;
+using Buffer = SlimDX.Direct3D11.Buffer;
+using Device = SlimDX.Direct3D11.Device;
+using MapFlags = SlimDX.Direct3D11.MapFlags;
+using SlimDX.DXGI;
+
+namespace SlimDX.Toolkit.Fonts
+{
+ [StructLayout(LayoutKind.Sequential)]
+ struct QuadVertex
+ {
+ public static readonly int SizeInBytes =
Marshal.SizeOf(typeof(QuadVertex));
+
+ public Vector2 Position;
+ public Vector2 TexCoords;
+ public int Color;
+ }
+
+ class VertexDrawer : IDisposable
+ {
+ Buffer vertexBuffer;
+ Buffer indexBuffer;
+ int bufferSize = 4096 * 16;
+ int maxIndexCount;
+
+ public VertexDrawer(Device device, int vertexBufferSize)
+ {
+ if (vertexBufferSize >= 1024)
+ {
+ if (device.FeatureLevel < FeatureLevel.Level_9_2)
+ vertexBufferSize = Math.Min(vertexBufferSize, 512 *
1024);
+ bufferSize = vertexBufferSize;
+ }
+
+ maxIndexCount = (bufferSize * 3) / (2 *
QuadVertex.SizeInBytes);
+ if (maxIndexCount < 64)
+ maxIndexCount = 64;
+
+ CreateBuffers(device);
+ }
+
+ public void Dispose()
+ {
+ if (vertexBuffer != null)
+ vertexBuffer.Dispose();
+
+ if (indexBuffer != null)
+ indexBuffer.Dispose();
+
+ vertexBuffer = null;
+ indexBuffer = null;
+ }
+
+ public void DrawVertices(DeviceContext context, GlyphAtlas
glyphAtlas, VertexData vertexData, TextOptions flags)
+ {
+ int stride = 0;
+ if ((flags & TextOptions.NoGeometryShader) == 0)
+ stride = GlyphVertex.SizeInBytes;
+ else
+ {
+ stride = QuadVertex.SizeInBytes;
+ if ((flags & TextOptions.BuffersPrepared) == 0)
+ context.InputAssembler.SetIndexBuffer(indexBuffer,
Format.R16_UInt, 0);
+ }
+
+ if ((flags & TextOptions.BuffersPrepared) == 0)
+ context.InputAssembler.SetVertexBuffers(0, new
VertexBufferBinding(vertexBuffer, stride, 0));
+
+ if ((flags & TextOptions.NoGeometryShader) == 0)
+ DrawVertices(context, glyphAtlas, vertexData);
+ else
+ DrawQuads(context, glyphAtlas, vertexData);
+ }
+
+ void DrawVertices(DeviceContext context, GlyphAtlas glyphAtlas,
VertexData vertexData)
+ {
+ int total = vertexData.Vertices.Length;
+ if (vertexData.VertexCounts.Length == 0 || total == 0)
+ return;
+
+ int maxVertexCount = bufferSize / GlyphVertex.SizeInBytes;
+ int currentSheet = 0;
+ int activeSheet = int.MaxValue;
+ int currentVertex = 0;
+ int nextSheet = vertexData.VertexCounts[0];
+
+ // draw all vertices
+ while (currentVertex < total)
+ {
+ // fill the vertex buffer
+ int count = Math.Min(total - currentVertex,
maxVertexCount);
+ var box = context.MapSubresource(vertexBuffer,
MapMode.WriteDiscard, MapFlags.None);
+ box.Data.WriteRange(vertexData.Vertices, currentVertex,
count);
+ context.UnmapSubresource(vertexBuffer, 0);
+
+ // draw all glyphs in the buffer
+ int drawnVertices = 0;
+ while (drawnVertices < count)
+ {
+ while (currentVertex >= nextSheet)
+ {
+ currentSheet++;
+ nextSheet += vertexData.VertexCounts[currentSheet];
+ }
+
+ // bind sheet shaders
+ if (currentSheet != activeSheet)
+ {
+ glyphAtlas.BindSheet(context, currentSheet, 0);
+ activeSheet = currentSheet;
+ }
+
+ int drawCount = Math.Min(count - drawnVertices,
nextSheet - currentVertex);
+ context.Draw(drawCount, drawnVertices);
+
+ drawnVertices += drawCount;
+ currentVertex += drawCount;
+ }
+ }
+ }
+
+ void DrawQuads(DeviceContext context, GlyphAtlas glyphAtlas,
VertexData vertexData)
+ {
+ int total = vertexData.Vertices.Length;
+ if (vertexData.VertexCounts.Length == 0 || total == 0)
+ return;
+
+ int maxVertexCount = bufferSize / QuadVertex.SizeInBytes;
+ if (maxVertexCount > 4 * (maxIndexCount / 6))
+ maxVertexCount = 4 * (maxIndexCount / 6);
+ if (maxVertexCount % 4 != 0)
+ maxVertexCount -= (maxVertexCount % 4);
+
+ GlyphCoords[] sheetCoords = null;
+ int currentSheet = 0;
+ int activeSheet = int.MaxValue;
+ int currentVertex = 0;
+ int nextSheet = vertexData.VertexCounts[0];
+
+ // draw all vertices
+ while (currentVertex < total)
+ {
+ // fill the vertex buffer
+ int count = Math.Min((total - currentVertex) * 4,
maxVertexCount);
+ var box = context.MapSubresource(vertexBuffer,
MapMode.WriteDiscard, MapFlags.None);
+
+ // convert to quads
+ int savedCurrentSheet = currentSheet;
+ int savedActiveSheet = activeSheet;
+ int savedNextSheetStart = nextSheet;
+ int savedCurrentVertex = currentVertex;
+
+ int drawnVertices = 0;
+ while (drawnVertices < count)
+ {
+ while (currentVertex >= nextSheet)
+ {
+ currentSheet++;
+ nextSheet += vertexData.VertexCounts[currentSheet];
+ }
+
+ // bind sheet shaders
+ if (currentSheet != activeSheet)
+ {
+ sheetCoords =
glyphAtlas.GetGlyphCoords(currentSheet);
+ activeSheet = currentSheet;
+ }
+
+ int drawCount = Math.Min(count - drawnVertices,
(nextSheet - currentVertex) * 4);
+ for (int i = 0; i < drawCount / 4; i++)
+ {
+ var vertex = vertexData.Vertices[currentVertex +
i];
+ var coords = sheetCoords[vertex.GlyphIndex];
+
+ var output = new QuadVertex();
+ output.Color = vertex.Color;
+ output.Position = vertex.Position + new
Vector2(coords.PositionLeft, coords.PositionTop);
+ output.TexCoords = new
Vector2(coords.TexCoordLeft, coords.TexCoordTop);
+ box.Data.Write(output);
+
+ output.Position.X = vertex.Position.X +
coords.PositionRight;
+ output.TexCoords.X = coords.TexCoordRight;
+ box.Data.Write(output);
+
+ output.Position.Y = vertex.Position.Y +
coords.PositionBottom;
+ output.TexCoords.Y = coords.TexCoordBottom;
+ box.Data.Write(output);
+
+ output.Position.X = vertex.Position.X +
coords.PositionLeft;
+ output.TexCoords.X = coords.TexCoordLeft;
+ box.Data.Write(output);
+ }
+
+ drawnVertices += drawCount;
+ currentVertex += drawCount / 4;
+ }
+
+ context.UnmapSubresource(vertexBuffer, 0);
+
+ // draw all glyphs in the buffer
+ currentSheet = savedCurrentSheet;
+ activeSheet = savedActiveSheet;
+ nextSheet = savedNextSheetStart;
+ currentVertex = savedCurrentVertex;
+ drawnVertices = 0;
+
+ while (drawnVertices < count)
+ {
+ while (currentVertex >= nextSheet)
+ {
+ currentSheet++;
+ nextSheet += vertexData.VertexCounts[currentSheet];
+ }
+
+ // bind sheet shaders
+ if (currentSheet != activeSheet)
+ {
+ glyphAtlas.BindSheet(context, currentSheet,
TextOptions.NoGeometryShader);
+ activeSheet = currentSheet;
+ }
+
+ int drawCount = Math.Min(count - drawnVertices,
(nextSheet - currentVertex) * 4);
+ context.DrawIndexed((drawCount / 2) * 3, 0,
drawnVertices);
+
+ drawnVertices += drawCount;
+ currentVertex += drawCount / 4;
+ }
+ }
+ }
+
+ void CreateBuffers(Device device)
+ {
+ vertexBuffer = new Buffer(device, new BufferDescription
+ {
+ SizeInBytes = bufferSize,
+ Usage = ResourceUsage.Dynamic,
+ BindFlags = BindFlags.VertexBuffer,
+ CpuAccessFlags = CpuAccessFlags.Write
+ });
+
+ var indices = new DataStream(sizeof(short) * maxIndexCount,
true, true);
+ for (int i = 0; i < maxIndexCount / 6; i++)
+ {
+ indices.Write((short)(i * 4));
+ indices.Write((short)(i * 4 + 1));
+ indices.Write((short)(i * 4 + 2));
+ indices.Write((short)(i * 4 + 1));
+ indices.Write((short)(i * 4 + 3));
+ indices.Write((short)(i * 4 + 2));
+ }
+
+ indices.Position = 0;
+ indexBuffer = new Buffer(device, indices, new BufferDescription
+ {
+ SizeInBytes = sizeof(short) * maxIndexCount,
+ Usage = ResourceUsage.Immutable,
+ BindFlags = BindFlags.IndexBuffer
+ });
+
+ indices.Dispose();
+ }
+ }
+}
=======================================
--- /branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj Tue Mar 20 11:00:08
2012
+++ /branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj Thu Mar 22 19:09:22
2012
@@ -46,6 +46,22 @@
<ItemGroup>
<Compile Include="Drawing\CommonStates.cs" />
<Compile Include="Drawing\ConstantBuffer.cs" />
+ <Compile Include="Drawing\StateSaver.cs" />
+ <Compile Include="Fonts\CreateOptions.cs" />
+ <Compile Include="Fonts\Font.cs" />
+ <Compile Include="Fonts\Geometry\GlyphVertex.cs" />
+ <Compile Include="Fonts\Geometry\TextGeometry.cs" />
+ <Compile Include="Fonts\Geometry\TextRenderer.cs" />
+ <Compile Include="Fonts\Glyphs\GlyphAtlas.cs" />
+ <Compile Include="Fonts\Glyphs\GlyphData.cs" />
+ <Compile Include="Fonts\Glyphs\GlyphProvider.cs" />
+ <Compile Include="Fonts\Glyphs\GlyphSheet.cs" />
+ <Compile Include="Fonts\Glyphs\HeightRange.cs" />
+ <Compile Include="Fonts\Rendering\FontShaders.cs" />
+ <Compile Include="Fonts\Rendering\NativeMethods.cs" />
+ <Compile Include="Fonts\Rendering\RenderData.cs" />
+ <Compile Include="Fonts\Rendering\RenderTargetWrapper.cs" />
+ <Compile Include="Fonts\Rendering\VertexDrawer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sprites\SpriteContextResources.cs" />
<Compile Include="Sprites\SpriteDeviceResources.cs" />