diff --git a/src/samples/DirectDraw/Direct2dDemo/Program.cs b/src/samples/DirectDraw/Direct2dDemo/Program.cs
index 58b55a0..fb9a53c 100644
--- a/src/samples/DirectDraw/Direct2dDemo/Program.cs
+++ b/src/samples/DirectDraw/Direct2dDemo/Program.cs
@@ -4,7 +4,6 @@
using System.Drawing;
using System.Numerics;
using Windows;
-using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Direct2D;
namespace Direct2dDemo;
@@ -23,63 +22,63 @@ public Direct2dDemo() : base(title: "Simple Direct2D Application", features: Fea
{
}
- protected override void RenderTargetCreated(HwndRenderTarget renderTarget)
+ protected override void RenderTargetCreated()
{
_lightSlateGrayBrush?.Dispose();
_cornflowerBlueBrush?.Dispose();
- _lightSlateGrayBrush = renderTarget.CreateSolidColorBrush(Color.LightSlateGray);
- _cornflowerBlueBrush = renderTarget.CreateSolidColorBrush(Color.CornflowerBlue);
- base.RenderTargetCreated(renderTarget);
+ _lightSlateGrayBrush = RenderTarget.CreateSolidColorBrush(Color.LightSlateGray);
+ _cornflowerBlueBrush = RenderTarget.CreateSolidColorBrush(Color.CornflowerBlue);
+ base.RenderTargetCreated();
}
- protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
+ protected override void OnPaint()
{
- switch (message)
- {
- case MessageType.Paint:
- if (IsDirect2dEnabled(out var renderTarget))
- {
- renderTarget.SetTransform(Matrix3x2.Identity);
- renderTarget.Clear(Color.White);
+ RenderTarget.SetTransform(Matrix3x2.Identity);
+ RenderTarget.Clear(Color.White);
- SizeF size = renderTarget.Size();
+ SizeF size = RenderTarget.Size();
- for (int x = 0; x < size.Width; x += 10)
- {
- renderTarget.DrawLine(
- new(x, 0), new(x, size.Height),
- _lightSlateGrayBrush!,
- 0.5f);
- }
+ for (int x = 0; x < size.Width; x += 10)
+ {
+ RenderTarget.DrawLine(
+ new(x, 0), new(x, size.Height),
+ _lightSlateGrayBrush!,
+ 0.5f);
+ }
- for (int y = 0; y < size.Height; y += 10)
- {
- renderTarget.DrawLine(
- new(0, y), new(size.Width, y),
- _lightSlateGrayBrush!,
- 0.5f);
- }
+ for (int y = 0; y < size.Height; y += 10)
+ {
+ RenderTarget.DrawLine(
+ new(0, y), new(size.Width, y),
+ _lightSlateGrayBrush!,
+ 0.5f);
+ }
- RectangleF rectangle1 = RectangleF.FromLTRB(
- size.Width / 2 - 50,
- size.Height / 2 - 50,
- size.Width / 2 + 50,
- size.Height / 2 + 50);
+ RectangleF rectangle1 = RectangleF.FromLTRB(
+ size.Width / 2 - 50,
+ size.Height / 2 - 50,
+ size.Width / 2 + 50,
+ size.Height / 2 + 50);
- RectangleF rectangle2 = RectangleF.FromLTRB(
- size.Width / 2 - 100,
- size.Height / 2 - 100,
- size.Width / 2 + 100,
- size.Height / 2 + 100);
+ RectangleF rectangle2 = RectangleF.FromLTRB(
+ size.Width / 2 - 100,
+ size.Height / 2 - 100,
+ size.Width / 2 + 100,
+ size.Height / 2 + 100);
- renderTarget.FillRectangle(rectangle1, _lightSlateGrayBrush!);
- renderTarget.DrawRectangle(rectangle2, _cornflowerBlueBrush!);
- }
+ RenderTarget.FillRectangle(rectangle1, _lightSlateGrayBrush!);
+ RenderTarget.DrawRectangle(rectangle2, _cornflowerBlueBrush!);
+ }
- return (LRESULT)0;
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _lightSlateGrayBrush?.Dispose();
+ _cornflowerBlueBrush?.Dispose();
}
- return base.WindowProcedure(window, message, wParam, lParam);
+ base.Dispose(disposing);
}
}
}
diff --git a/src/samples/DirectDraw/DirectWriteDemo/DirectWriteDemo.csproj b/src/samples/DirectDraw/DirectWriteDemo/DirectWriteDemo.csproj
new file mode 100644
index 0000000..6888128
--- /dev/null
+++ b/src/samples/DirectDraw/DirectWriteDemo/DirectWriteDemo.csproj
@@ -0,0 +1,11 @@
+
+
+
+ WinExe
+
+
+
+
+
+
+
diff --git a/src/samples/DirectDraw/DirectWriteDemo/Program.cs b/src/samples/DirectDraw/DirectWriteDemo/Program.cs
new file mode 100644
index 0000000..8bac399
--- /dev/null
+++ b/src/samples/DirectDraw/DirectWriteDemo/Program.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using Windows;
+using Windows.Win32.Graphics.Direct2D;
+using Windows.Win32.Graphics.DirectWrite;
+using FontWeight = Windows.Win32.Graphics.DirectWrite.FontWeight;
+
+namespace DirectWriteDemo;
+
+internal class Program
+{
+ [STAThread]
+ private static void Main() => Application.Run(new DirectWriteDemo());
+
+ private class DirectWriteDemo : MainWindow
+ {
+ private const string Message = "Hello World From ... DirectWrite!";
+
+ protected TextFormat _textFormat;
+ protected TextLayout? _textLayout;
+ protected Typography _typography;
+
+ protected SolidColorBrush? _blackBrush;
+
+ public DirectWriteDemo() : base(title: "Simple DirectWrite Application", features: Features.EnableDirect2d)
+ {
+ _textFormat = new("Gabriola", fontSize: 64)
+ {
+ TextAlignment = TextAlignment.Center,
+ ParagraphAlignment = ParagraphAlignment.Center
+ };
+
+ _typography = new();
+ _typography.AddFontFeature(FontFeatureTag.StylisticSet7);
+ }
+
+ protected override void RenderTargetCreated()
+ {
+ _blackBrush?.Dispose();
+ _blackBrush = RenderTarget.CreateSolidColorBrush(Color.Black);
+ base.RenderTargetCreated();
+ }
+
+ protected override void OnPaint()
+ {
+ RenderTarget.Clear(Color.CornflowerBlue);
+ RenderTarget.DrawTextLayout(default, _textLayout!, _blackBrush!);
+ }
+
+ protected override void OnSize(Size size)
+ {
+ if (_textLayout is not null)
+ {
+ _textLayout.MaxHeight = size.Height;
+ _textLayout.MaxWidth = size.Width;
+ return;
+ }
+
+ _textLayout = new(Message, _textFormat, RenderTarget.Size());
+
+ // (21, 12) is the range around "DirectWrite!"
+ _textLayout.SetFontSize(100, (21, 12));
+ _textLayout.SetTypography(_typography, (0, Message.Length));
+ _textLayout.SetUnderline(true, (21, 12));
+ _textLayout.SetFontWeight(FontWeight.Bold, (21, 12));
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _textFormat.Dispose();
+ _textLayout?.Dispose();
+ _blackBrush?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/thirtytwo/Application.cs b/src/thirtytwo/Application.cs
index 4dfbf06..e18c500 100644
--- a/src/thirtytwo/Application.cs
+++ b/src/thirtytwo/Application.cs
@@ -5,13 +5,15 @@
using System.Runtime.InteropServices;
using Windows.Support;
using Windows.Win32.Graphics.Direct2D;
+using Windows.Win32.Graphics.DirectWrite;
namespace Windows;
public static unsafe class Application
{
private static ActivationContext? s_visualStylesContext;
- private static Factory? s_direct2dFactory;
+ private static Direct2dFactory? s_direct2dFactory;
+ private static DirectWriteFactory? s_directWriteFactory;
internal static ActivationScope ThemingScope => new(GetStylesContext());
@@ -207,5 +209,6 @@ public static void EnumerateThreadWindows(
using var enumerator = new ThreadWindowEnumerator(threadId, callback);
}
- public static Factory Direct2dFactory => s_direct2dFactory ??= new();
+ public static Direct2dFactory Direct2dFactory => s_direct2dFactory ??= new();
+ public static DirectWriteFactory DirectWriteFactory => s_directWriteFactory ??= new();
}
\ No newline at end of file
diff --git a/src/thirtytwo/NativeMethods.txt b/src/thirtytwo/NativeMethods.txt
index f7522bf..b8768f6 100644
--- a/src/thirtytwo/NativeMethods.txt
+++ b/src/thirtytwo/NativeMethods.txt
@@ -350,4 +350,6 @@ WM_*
XFORMCOORDS
ID2D1Factory
D2D1CreateFactory
-D2DERR_RECREATE_TARGET
\ No newline at end of file
+D2DERR_RECREATE_TARGET
+DWriteCreateFactory
+IDWriteFactory
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_POINT_F.cs b/src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_POINT_F.cs
new file mode 100644
index 0000000..1d0cd7b
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_POINT_F.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+
+namespace Windows.Win32.Graphics.Direct2D.Common;
+
+public partial struct D2D_POINT_2F
+{
+ public D2D_POINT_2F(float x, float y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public static implicit operator D2D_POINT_2F(PointF value) =>
+ new(value.X, value.Y);
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/Factory.cs b/src/thirtytwo/Win32/Graphics/Direct2D/Direct2dFactory.cs
similarity index 91%
rename from src/thirtytwo/Win32/Graphics/Direct2D/Factory.cs
rename to src/thirtytwo/Win32/Graphics/Direct2D/Direct2dFactory.cs
index 2303f6c..a8aebcd 100644
--- a/src/thirtytwo/Win32/Graphics/Direct2D/Factory.cs
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/Direct2dFactory.cs
@@ -6,13 +6,13 @@
namespace Windows.Win32.Graphics.Direct2D;
-public unsafe class Factory : DisposableBase.Finalizable, IPointer
+public unsafe class Direct2dFactory : DisposableBase.Finalizable, IPointer
{
private readonly AgileComPointer _factory;
public unsafe ID2D1Factory* Pointer { get; private set; }
- public Factory(
+ public Direct2dFactory(
D2D1_FACTORY_TYPE factoryType = D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED,
D2D1_DEBUG_LEVEL factoryOptions = D2D1_DEBUG_LEVEL.D2D1_DEBUG_LEVEL_NONE)
{
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/DrawTextOptions.cs b/src/thirtytwo/Win32/Graphics/Direct2D/DrawTextOptions.cs
new file mode 100644
index 0000000..57cd5e1
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/DrawTextOptions.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.Direct2D;
+
+///
+/// Modifications made to the draw text call that influence how the text is rendered.
+/// []
+///
+[Flags]
+public enum DrawTextOptions
+{
+ ///
+ NoSnap = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NO_SNAP,
+
+ ///
+ Clip = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_CLIP,
+
+ ///
+ EnableColorFont = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT,
+
+ ///
+ DisableColorBitmapSnapping = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING,
+
+ ///
+ None = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NONE
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
index f69b11a..2c4b326 100644
--- a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
@@ -5,6 +5,7 @@
using System.Numerics;
using Windows.Support;
using Windows.Win32.Graphics.Direct2D.Common;
+using Windows.Win32.Graphics.DirectWrite;
namespace Windows.Win32.Graphics.Direct2D;
@@ -92,4 +93,25 @@ public static SizeF Size(this T target) where T : IPointer
GC.KeepAlive(target);
return *(SizeF*)&size;
}
+
+ public static void DrawTextLayout(
+ this TTarget target,
+ PointF origin,
+ TLayout textLayout,
+ TBrush defaultFillBrush,
+ DrawTextOptions options = DrawTextOptions.None)
+ where TTarget : IPointer
+ where TLayout : IPointer
+ where TBrush : IPointer
+ {
+ target.Pointer->DrawTextLayout(
+ origin,
+ textLayout.Pointer,
+ defaultFillBrush.Pointer,
+ (D2D1_DRAW_TEXT_OPTIONS)options);
+
+ GC.KeepAlive(target);
+ GC.KeepAlive(textLayout);
+ GC.KeepAlive(defaultFillBrush);
+ }
}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/DirectWriteFactory.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/DirectWriteFactory.cs
new file mode 100644
index 0000000..3c6323a
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/DirectWriteFactory.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Windows.Support;
+using Windows.Win32.System.Com;
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+public unsafe class DirectWriteFactory : DisposableBase.Finalizable, IPointer
+{
+ private readonly AgileComPointer _factory;
+
+ public unsafe IDWriteFactory* Pointer { get; private set; }
+
+ public DirectWriteFactory(DWRITE_FACTORY_TYPE factoryType = DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED)
+ {
+ IDWriteFactory* factory;
+ Interop.DWriteCreateFactory(
+ factoryType,
+ IID.Get(),
+ (void**)&factory).ThrowOnFailure();
+
+ Pointer = factory;
+
+ // Ensure that this can be disposed on the finalizer thread by giving the "last" ref count
+ // to an agile pointer.
+ _factory = new AgileComPointer(factory, takeOwnership: true);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Pointer = null;
+
+ if (disposing)
+ {
+ _factory.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeature.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeature.cs
new file mode 100644
index 0000000..9511781
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeature.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+public readonly struct FontFeature
+{
+ ///
+ /// The feature OpenType name identifier.
+ ///
+ public readonly FontFeatureTag NameTag;
+
+ ///
+ /// Execution parameter of the feature.
+ ///
+ ///
+ /// The parameter should be non-zero to enable the feature. Once enabled, a feature can't be disabled again within
+ /// the same range. Features requiring a selector use this value to indicate the selector index.
+ ///
+ public readonly uint Parameter;
+
+ public FontFeature(FontFeatureTag nameTag, uint parameter)
+ {
+ NameTag = nameTag;
+ Parameter = parameter;
+ }
+
+ public FontFeature(FontFeatureTag nameTag, bool enable = true)
+ {
+ NameTag = nameTag;
+ Parameter = enable ? 1u : 0u;
+ }
+
+ public static implicit operator FontFeature(FontFeatureTag tag) => new(tag);
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeatureTag.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeatureTag.cs
new file mode 100644
index 0000000..a7be1cc
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/FontFeatureTag.cs
@@ -0,0 +1,253 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// Typographic feature of text supplied by the font. []
+///
+public enum FontFeatureTag : uint
+{
+ ///
+ AlternativeFractions = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_ALTERNATIVE_FRACTIONS,
+
+ ///
+ PetiteCapitalsFromCapitals = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_PETITE_CAPITALS_FROM_CAPITALS,
+
+ ///
+ SmallCapitalsFromCapitals = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SMALL_CAPITALS_FROM_CAPITALS,
+
+ ///
+ ContextualAlternates = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES,
+
+ ///
+ CaseSensitiveForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CASE_SENSITIVE_FORMS,
+
+ ///
+ GlyphCompositionDecomposition = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION,
+
+ ///
+ ContextualLigatures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_LIGATURES,
+
+ ///
+ CapitalSpacing = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CAPITAL_SPACING,
+
+ ///
+ ContextualSwash = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_SWASH,
+
+ ///
+ CursivePositioning = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_CURSIVE_POSITIONING,
+
+ ///
+ Default = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_DEFAULT,
+
+ ///
+ DiscretionaryLigatures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_DISCRETIONARY_LIGATURES,
+
+ ///
+ ExpertForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_EXPERT_FORMS,
+
+ ///
+ Fractions = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_FRACTIONS,
+
+ ///
+ FullWidth = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_FULL_WIDTH,
+
+ ///
+ HalfForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HALF_FORMS,
+
+ ///
+ HalantForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HALANT_FORMS,
+
+ ///
+ AlternateHalfWidth = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_ALTERNATE_HALF_WIDTH,
+
+ ///
+ HistoricalForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HISTORICAL_FORMS,
+
+ ///
+ HorizontalKanaAlternates = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HORIZONTAL_KANA_ALTERNATES,
+
+ ///
+ HistoricalLigatures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HISTORICAL_LIGATURES,
+
+ ///
+ HalfWidth = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HALF_WIDTH,
+
+ ///
+ HojoKanjiForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_HOJO_KANJI_FORMS,
+
+ ///
+ Jis04Forms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_JIS04_FORMS,
+
+ ///
+ Jis78Forms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_JIS78_FORMS,
+
+ ///
+ Jis83Forms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_JIS83_FORMS,
+
+ ///
+ Jis90Forms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_JIS90_FORMS,
+
+ ///
+ Kerning = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_KERNING,
+
+ ///
+ StandardLigatures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STANDARD_LIGATURES,
+
+ ///
+ LiningFigures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_LINING_FIGURES,
+
+ ///
+ LocalizedForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_LOCALIZED_FORMS,
+
+ ///
+ MarkPositioning = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_MARK_POSITIONING,
+
+ ///
+ MathematicalGreek = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_MATHEMATICAL_GREEK,
+
+ ///
+ MarkToMarkPositioning = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING,
+
+ ///
+ AlternateAnnotationForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_ALTERNATE_ANNOTATION_FORMS,
+
+ ///
+ NlcKanjiForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_NLC_KANJI_FORMS,
+
+ ///
+ OldStyleFigures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_OLD_STYLE_FIGURES,
+
+ ///
+ Ordinals = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_ORDINALS,
+
+ ///
+ ProportionalAlternateWidth = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_PROPORTIONAL_ALTERNATE_WIDTH,
+
+ ///
+ PetiteCapitals = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_PETITE_CAPITALS,
+
+ ///
+ ProportionalFigures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_PROPORTIONAL_FIGURES,
+
+ ///
+ ProportionalWidths = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_PROPORTIONAL_WIDTHS,
+
+ ///
+ QuarterWidths = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_QUARTER_WIDTHS,
+
+ ///
+ RequiredLigatures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_REQUIRED_LIGATURES,
+
+ ///
+ RubyNotationForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_RUBY_NOTATION_FORMS,
+
+ ///
+ StylisticAlternates = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_ALTERNATES,
+
+ ///
+ ScientificInferiors = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SCIENTIFIC_INFERIORS,
+
+ ///
+ SmallCapitals = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SMALL_CAPITALS,
+
+ ///
+ SimplifiedForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SIMPLIFIED_FORMS,
+
+ ///
+ StylisticSet1 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_1,
+
+ ///
+ StylisticSet2 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_2,
+
+ ///
+ StylisticSet3 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_3,
+
+ ///
+ StylisticSet4 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_4,
+
+ ///
+ StylisticSet5 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_5,
+
+ ///
+ StylisticSet6 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_6,
+
+ ///
+ StylisticSet7 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,
+
+ ///
+ StylisticSet8 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_8,
+
+ ///
+ StylisticSet9 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_9,
+
+ ///
+ StylisticSet10 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_10,
+
+ ///
+ StylisticSet11 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_11,
+
+ ///
+ StylisticSet12 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_12,
+
+ ///
+ StylisticSet13 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_13,
+
+ ///
+ StylisticSet14 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_14,
+
+ ///
+ StylisticSet15 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_15,
+
+ ///
+ StylisticSet16 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_16,
+
+ ///
+ StylisticSet17 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_17,
+
+ ///
+ StylisticSet18 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_18,
+
+ ///
+ StylisticSet19 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_19,
+
+ ///
+ StylisticSet20 = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_20,
+
+ ///
+ Subscript = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SUBSCRIPT,
+
+ ///
+ Superscript = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SUPERSCRIPT,
+
+ ///
+ Swash = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SWASH,
+
+ ///
+ Titling = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_TITLING,
+
+ ///
+ TraditionalNameForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_TRADITIONAL_NAME_FORMS,
+
+ ///
+ TabularFigures = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_TABULAR_FIGURES,
+
+ ///
+ TraditionalForms = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_TRADITIONAL_FORMS,
+
+ ///
+ ThirdWidths = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_THIRD_WIDTHS,
+
+ ///
+ Unicase = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_UNICASE,
+
+ ///
+ VerticalWriting = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING,
+
+ ///
+ VerticalAlternatesAndRotation = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_VERTICAL_ALTERNATES_AND_ROTATION,
+
+ ///
+ SlashedZero = DWRITE_FONT_FEATURE_TAG.DWRITE_FONT_FEATURE_TAG_SLASHED_ZERO,
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/FontStretch.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/FontStretch.cs
new file mode 100644
index 0000000..61147ca
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/FontStretch.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// The font stretch enumeration describes relative change from the normal aspect ratio as specified by a font
+/// designer for the glyphs in a font. []
+///
+///
+/// Values less than 1 or greater than 9 are considered to be invalid, and they are rejected by font API functions.
+///
+public enum FontStretch : uint
+{
+ ///
+ Undefined = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_UNDEFINED,
+
+ ///
+ UltraCondensed = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
+
+ ///
+ ExtraCondensed = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_EXTRA_CONDENSED,
+
+ ///
+ Condensed = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_CONDENSED,
+
+ ///
+ SemiCondensed = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_SEMI_CONDENSED,
+
+ ///
+ Normal = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_NORMAL,
+
+ ///
+ SemiExpanded = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_SEMI_EXPANDED,
+
+ ///
+ Expanded = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_EXPANDED,
+
+ ///
+ ExtraExpanded = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
+
+ ///
+ UltraExpanded = DWRITE_FONT_STRETCH.DWRITE_FONT_STRETCH_ULTRA_EXPANDED
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/FontStyle.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/FontStyle.cs
new file mode 100644
index 0000000..44996f8
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/FontStyle.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// The font style enumeration describes the slope style of a font face, such as Normal, Italic or Oblique.
+/// []
+///
+public enum FontStyle : uint
+{
+ ///
+ Normal = DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL,
+
+ ///
+ Oblique = DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_OBLIQUE,
+
+ ///
+ Italic = DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_ITALIC
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/FontWeight.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/FontWeight.cs
new file mode 100644
index 0000000..7de5f36
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/FontWeight.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// The font weight enumeration describes common values for degree of blackness or thickness of strokes of characters in a font.
+/// []
+///
+///
+/// Font weight values less than 1 or greater than 999 are considered to be invalid, and they are rejected by font API functions.
+///
+public enum FontWeight : uint
+{
+ ///
+ Thin = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_THIN,
+
+ ///
+ ExtraLight = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_EXTRA_LIGHT,
+
+ ///
+ Light = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_LIGHT,
+
+ ///
+ SemiLight = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_SEMI_LIGHT,
+
+ ///
+ Normal = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_NORMAL,
+
+ ///
+ Medium = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_MEDIUM,
+
+ ///
+ SemiBold = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_SEMI_BOLD,
+
+ ///
+ Bold = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_BOLD,
+
+ ///
+ ExtraBold = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_EXTRA_BOLD,
+
+ ///
+ Black = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_BLACK,
+
+ ///
+ ExtraBlack = DWRITE_FONT_WEIGHT.DWRITE_FONT_WEIGHT_EXTRA_BLACK,
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/ParagraphAlignment.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/ParagraphAlignment.cs
new file mode 100644
index 0000000..dd9a9ac
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/ParagraphAlignment.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// Alignment of paragraph text along the flow direction axis relative to the
+/// flow's beginning and ending edge of the layout box.
+///
+public enum ParagraphAlignment : uint
+{
+ ///
+ Near = DWRITE_PARAGRAPH_ALIGNMENT.DWRITE_PARAGRAPH_ALIGNMENT_NEAR,
+
+ ///
+ Far = DWRITE_PARAGRAPH_ALIGNMENT.DWRITE_PARAGRAPH_ALIGNMENT_FAR,
+
+ ///
+ Center = DWRITE_PARAGRAPH_ALIGNMENT.DWRITE_PARAGRAPH_ALIGNMENT_CENTER
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/TextAlignment.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/TextAlignment.cs
new file mode 100644
index 0000000..94c95ba
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/TextAlignment.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// Alignment of paragraph text along the reading direction axis relative to
+/// the leading and trailing edge of the layout box. []
+///
+public enum TextAlignment : uint
+{
+ ///
+ Leading = DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_LEADING,
+
+ ///
+ Trailing = DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_TRAILING,
+
+ ///
+ Center = DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_CENTER,
+
+ ///
+ Justified = DWRITE_TEXT_ALIGNMENT.DWRITE_TEXT_ALIGNMENT_JUSTIFIED
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/TextFormat.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/TextFormat.cs
new file mode 100644
index 0000000..f5d77ed
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/TextFormat.cs
@@ -0,0 +1,99 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Windows.Support;
+using Windows.Win32.System.Com;
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+public unsafe class TextFormat : DisposableBase.Finalizable, IPointer
+{
+ private readonly AgileComPointer _format;
+
+ public unsafe IDWriteTextFormat* Pointer { get; private set; }
+
+ public TextFormat(IDWriteTextFormat* format)
+ {
+ Pointer = format;
+
+ // Ensure that this can be disposed on the finalizer thread by giving the "last" ref count
+ // to an agile pointer.
+ _format = new AgileComPointer(format, takeOwnership: true);
+ }
+
+ public TextFormat(
+ string fontFamilyName,
+ float fontSize,
+ FontWeight fontWeight = FontWeight.Normal,
+ FontStyle fontStyle = FontStyle.Normal,
+ FontStretch fontStretch = FontStretch.Normal,
+ string localeName = "en-us") : this(Create(fontFamilyName, fontSize, fontWeight, fontStyle, fontStretch, localeName))
+ {
+ }
+
+ private static IDWriteTextFormat* Create(
+ string fontFamilyName,
+ float fontSize,
+ FontWeight fontWeight,
+ FontStyle fontStyle,
+ FontStretch fontStretch,
+ string localeName)
+ {
+ IDWriteTextFormat* format;
+
+ fixed (char* fn = fontFamilyName)
+ {
+ Application.DirectWriteFactory.Pointer->CreateTextFormat(
+ fontFamilyName,
+ null,
+ (DWRITE_FONT_WEIGHT)fontWeight,
+ (DWRITE_FONT_STYLE)fontStyle,
+ (DWRITE_FONT_STRETCH)fontStretch,
+ fontSize,
+ localeName,
+ &format).ThrowOnFailure();
+ }
+
+ return format;
+ }
+
+ public TextAlignment TextAlignment
+ {
+ get
+ {
+ TextAlignment alignment = (TextAlignment)Pointer->GetTextAlignment();
+ GC.KeepAlive(this);
+ return alignment;
+ }
+ set
+ {
+ Pointer->SetTextAlignment((DWRITE_TEXT_ALIGNMENT)value).ThrowOnFailure();
+ GC.KeepAlive(this);
+ }
+ }
+
+ public ParagraphAlignment ParagraphAlignment
+ {
+ get
+ {
+ ParagraphAlignment alignment = (ParagraphAlignment)Pointer->GetParagraphAlignment();
+ GC.KeepAlive(this);
+ return alignment;
+ }
+ set
+ {
+ Pointer->SetParagraphAlignment((DWRITE_PARAGRAPH_ALIGNMENT)value).ThrowOnFailure();
+ GC.KeepAlive(this);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Pointer = null;
+
+ if (disposing)
+ {
+ _format.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/TextLayout.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/TextLayout.cs
new file mode 100644
index 0000000..2a38821
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/TextLayout.cs
@@ -0,0 +1,156 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using Windows.Support;
+using Windows.Win32.System.Com;
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+public unsafe class TextLayout : DisposableBase.Finalizable, IPointer
+{
+ private readonly AgileComPointer _layout;
+
+ public unsafe IDWriteTextLayout* Pointer { get; private set; }
+
+ public TextLayout(IDWriteTextLayout* layout)
+ {
+ Pointer = layout;
+
+ // Ensure that this can be disposed on the finalizer thread by giving the "last" ref count
+ // to an agile pointer.
+ _layout = new AgileComPointer(layout, takeOwnership: true);
+ }
+
+ public TextLayout(
+ string text,
+ TextFormat format,
+ SizeF maxSize) : this(Create(text, format, maxSize.Width, maxSize.Height))
+ {
+ }
+
+ public TextLayout(
+ string text,
+ TextFormat format,
+ float maxWidth,
+ float maxHeight) : this(Create(text, format, maxWidth, maxHeight))
+ {
+ }
+
+ private static IDWriteTextLayout* Create(
+ string text,
+ TextFormat format,
+ float maxWidth,
+ float maxHeight)
+ {
+ IDWriteTextLayout* layout;
+
+ fixed (char* t = text)
+ {
+ Application.DirectWriteFactory.Pointer->CreateTextLayout(
+ text,
+ (uint)text.Length,
+ format.Pointer,
+ maxWidth,
+ maxHeight,
+ &layout).ThrowOnFailure();
+ }
+
+ return layout;
+ }
+
+ public void SetFontSize(float fontSize, TextRange textRange)
+ {
+ Pointer->SetFontSize(fontSize, textRange);
+ GC.KeepAlive(this);
+ }
+
+ public float MaxWidth
+ {
+ get
+ {
+ float maxWidth = Pointer->GetMaxWidth();
+ GC.KeepAlive(this);
+ return maxWidth;
+ }
+ set
+ {
+ Pointer->SetMaxWidth(value);
+ GC.KeepAlive(this);
+ }
+ }
+
+ public float MaxHeight
+ {
+ get
+ {
+ float maxHeight = Pointer->GetMaxHeight();
+ GC.KeepAlive(this);
+ return maxHeight;
+ }
+ set
+ {
+ Pointer->SetMaxHeight(value);
+ GC.KeepAlive(this);
+ }
+ }
+
+ public TextAlignment TextAlignment
+ {
+ get
+ {
+ TextAlignment alignment = (TextAlignment)Pointer->GetTextAlignment();
+ GC.KeepAlive(this);
+ return alignment;
+ }
+ set
+ {
+ Pointer->SetTextAlignment((DWRITE_TEXT_ALIGNMENT)value);
+ GC.KeepAlive(this);
+ }
+ }
+
+ public ParagraphAlignment ParagraphAlignment
+ {
+ get
+ {
+ ParagraphAlignment alignment = (ParagraphAlignment)Pointer->GetParagraphAlignment();
+ GC.KeepAlive(this);
+ return alignment;
+ }
+ set
+ {
+ Pointer->SetParagraphAlignment((DWRITE_PARAGRAPH_ALIGNMENT)value);
+ GC.KeepAlive(this);
+ }
+ }
+
+ public void SetTypography(T typography, TextRange textRange) where T : IPointer
+ {
+ Pointer->SetTypography(typography.Pointer, textRange);
+ GC.KeepAlive(this);
+ GC.KeepAlive(typography);
+ }
+
+ public void SetUnderline(bool hasUnderline, TextRange textRange)
+ {
+ Pointer->SetUnderline(hasUnderline, textRange);
+ GC.KeepAlive(this);
+ }
+
+ public void SetFontWeight(FontWeight fontWeight, TextRange textRange)
+ {
+ Pointer->SetFontWeight((DWRITE_FONT_WEIGHT)fontWeight, textRange);
+ GC.KeepAlive(this);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Pointer = null;
+
+ if (disposing)
+ {
+ _layout.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/TextRange.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/TextRange.cs
new file mode 100644
index 0000000..8330484
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/TextRange.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.CompilerServices;
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+///
+/// Specifies a range of text positions where format is applied.
+/// []
+///
+public unsafe readonly struct TextRange
+{
+ ///
+ /// The start text position of the range.
+ ///
+ public readonly uint StartPosition;
+
+ ///
+ /// The number of text positions in the range.
+ ///
+ public readonly uint Length;
+
+ public TextRange(uint startPosition, uint length)
+ {
+ StartPosition = startPosition;
+ Length = length;
+ }
+
+ public static implicit operator TextRange((int StartPosition, int Length) tuple)
+ => new((uint)tuple.StartPosition, (uint)tuple.Length);
+
+ public static implicit operator DWRITE_TEXT_RANGE(TextRange range) =>
+ Unsafe.As(ref range);
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/DirectWrite/Typography.cs b/src/thirtytwo/Win32/Graphics/DirectWrite/Typography.cs
new file mode 100644
index 0000000..7d16526
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/DirectWrite/Typography.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.CompilerServices;
+using Windows.Support;
+using Windows.Win32.System.Com;
+
+namespace Windows.Win32.Graphics.DirectWrite;
+
+public unsafe class Typography : DisposableBase.Finalizable, IPointer
+{
+ private readonly AgileComPointer _typography;
+
+ public unsafe IDWriteTypography* Pointer { get; private set; }
+
+ public Typography(IDWriteTypography* typography)
+ {
+ Pointer = typography;
+
+ // Ensure that this can be disposed on the finalizer thread by giving the "last" ref count
+ // to an agile pointer.
+ _typography = new AgileComPointer(typography, takeOwnership: true);
+ }
+
+ public Typography() : this(Create())
+ {
+ }
+
+ private static IDWriteTypography* Create()
+ {
+ IDWriteTypography* typography;
+ Application.DirectWriteFactory.Pointer->CreateTypography(&typography).ThrowOnFailure();
+ return typography;
+ }
+
+ ///
+ public void AddFontFeature(FontFeature fontFeature)
+ {
+ Pointer->AddFontFeature(Unsafe.As(ref fontFeature)).ThrowOnFailure();
+ GC.KeepAlive(this);
+ }
+
+ ///
+ public void GetFontFeature(uint fontFeatureIndex, out FontFeature fontFeature)
+ {
+ fixed (void* f = &fontFeature)
+ {
+ Pointer->GetFontFeature(fontFeatureIndex, (DWRITE_FONT_FEATURE*)f);
+ GC.KeepAlive(this);
+ }
+ }
+
+ ///
+ public uint FontFeatureCount
+ {
+ get
+ {
+ uint result = Pointer->GetFontFeatureCount();
+ GC.KeepAlive(this);
+ return result;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Pointer = null;
+
+ if (disposing)
+ {
+ _typography.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Window.Features.cs b/src/thirtytwo/Window.Features.cs
new file mode 100644
index 0000000..7a24d41
--- /dev/null
+++ b/src/thirtytwo/Window.Features.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Windows;
+
+public unsafe partial class Window
+{
+ [Flags]
+ public enum Features
+ {
+ ///
+ /// Set this flag to enable Direct2D rendering.
+ ///
+ EnableDirect2d = 0b0000_0000_0000_0000_0000_0000_0000_0001,
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Window.cs b/src/thirtytwo/Window.cs
index 9286e4b..8d15882 100644
--- a/src/thirtytwo/Window.cs
+++ b/src/thirtytwo/Window.cs
@@ -10,7 +10,7 @@
namespace Windows;
-public unsafe class Window : ComponentBase, IHandle, ILayoutHandler
+public unsafe partial class Window : ComponentBase, IHandle, ILayoutHandler
{
private static readonly object s_lock = new();
private static readonly ConcurrentDictionary> s_windows = new();
@@ -46,18 +46,20 @@ public unsafe class Window : ComponentBase, IHandle, ILayoutHandler
private readonly HBRUSH _backgroundBrush;
- protected HwndRenderTarget? Direct2dRenderTarget { get; private set; }
+ private HwndRenderTarget? _renderTarget;
+
+ protected HwndRenderTarget RenderTarget => _renderTarget ?? throw new InvalidOperationException();
private string? _text;
private uint _lastDpi;
private readonly Features _features;
- [MemberNotNullWhen(true, nameof(Direct2dRenderTarget))]
+ [MemberNotNullWhen(true, nameof(_renderTarget))]
protected bool IsDirect2dEnabled()
{
bool enabled = _features.AreFlagsSet(Features.EnableDirect2d);
- if (enabled && Direct2dRenderTarget is null)
+ if (enabled && _renderTarget is null)
{
UpdateRenderTarget(Handle, this.GetClientRectangle().Size);
}
@@ -65,13 +67,6 @@ protected bool IsDirect2dEnabled()
return enabled;
}
- [MemberNotNullWhen(true, nameof(Direct2dRenderTarget))]
- protected bool IsDirect2dEnabled([NotNullWhen(true)] out HwndRenderTarget? renderTarget)
- {
- renderTarget = Direct2dRenderTarget;
- return IsDirect2dEnabled();
- }
-
///
/// The window handle. This will be after the window is destroyed.
///
@@ -186,24 +181,31 @@ public void SetFont(string typeFace, int pointSize)
this.SetFontHandle(_lastCreatedFont);
}
- [MemberNotNull(nameof(Direct2dRenderTarget))]
private void UpdateRenderTarget(HWND window, Size size)
{
- if (Direct2dRenderTarget is null)
+ if (_renderTarget is null)
{
- Direct2dRenderTarget = HwndRenderTarget.CreateForWindow(Application.Direct2dFactory, window, size);
- RenderTargetCreated(Direct2dRenderTarget);
+ _renderTarget = HwndRenderTarget.CreateForWindow(Application.Direct2dFactory, window, size);
+ RenderTargetCreated();
}
else
{
- Direct2dRenderTarget.Resize(size);
+ _renderTarget.Resize(size);
}
}
///
/// Called whenever the Direct2D render target has been created or recreated.
///
- protected virtual void RenderTargetCreated(HwndRenderTarget renderTarget)
+ protected virtual void RenderTargetCreated()
+ {
+ }
+
+ protected virtual void OnPaint()
+ {
+ }
+
+ protected virtual void OnSize(Size size)
{
}
@@ -242,20 +244,26 @@ private LRESULT WindowProcedureInternal(HWND window, uint message, WPARAM wParam
break;
case Interop.WM_SIZE:
+ Size size = new(lParam.LOWORD, lParam.HIWORD);
+
// Check the flag directly here so we don't create then resize.
if (_features.AreFlagsSet(Features.EnableDirect2d))
{
- UpdateRenderTarget(window, new Size(lParam.LOWORD, lParam.HIWORD));
+ UpdateRenderTarget(window, size);
}
+ OnSize(size);
+
break;
case Interop.WM_PAINT:
- if (IsDirect2dEnabled(out var renderTarget))
+ if (IsDirect2dEnabled())
{
- renderTarget.BeginDraw();
+ _renderTarget.BeginDraw();
}
+ OnPaint();
+
break;
}
@@ -275,11 +283,11 @@ private LRESULT WindowProcedureInternal(HWND window, uint message, WPARAM wParam
if (message == Interop.WM_PAINT && IsDirect2dEnabled())
{
- Direct2dRenderTarget.EndDraw(out bool recreateTarget);
+ _renderTarget.EndDraw(out bool recreateTarget);
if (recreateTarget)
{
- Direct2dRenderTarget.Dispose();
- Direct2dRenderTarget = null;
+ _renderTarget.Dispose();
+ _renderTarget = null;
UpdateRenderTarget(window, this.GetClientRectangle().Size);
}
}
@@ -526,13 +534,4 @@ private static WNDPROC GetDefaultWindowProcedure()
Debug.Assert(!address.IsNull);
return (WNDPROC)(void*)address.Value;
}
-
- [Flags]
- public enum Features
- {
- ///
- /// Set this flag to enable Direct2D rendering.
- ///
- EnableDirect2d = 0b0000_0000_0000_0000_0000_0000_0000_0001,
- }
}
\ No newline at end of file
diff --git a/thirtytwo.sln b/thirtytwo.sln
index 5dd6b4e..62b8a8e 100644
--- a/thirtytwo.sln
+++ b/thirtytwo.sln
@@ -59,6 +59,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DirectDraw", "DirectDraw",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct2dDemo", "src\samples\DirectDraw\Direct2dDemo\Direct2dDemo.csproj", "{1EC566E8-C337-42FF-9BA4-DB66BFAE5B8A}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DirectWriteDemo", "src\samples\DirectDraw\DirectWriteDemo\DirectWriteDemo.csproj", "{24BAE093-7546-4ED1-AF5C-2A62AD244C63}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -145,6 +147,10 @@ Global
{1EC566E8-C337-42FF-9BA4-DB66BFAE5B8A}.Debug|x64.Build.0 = Debug|x64
{1EC566E8-C337-42FF-9BA4-DB66BFAE5B8A}.Release|x64.ActiveCfg = Release|x64
{1EC566E8-C337-42FF-9BA4-DB66BFAE5B8A}.Release|x64.Build.0 = Release|x64
+ {24BAE093-7546-4ED1-AF5C-2A62AD244C63}.Debug|x64.ActiveCfg = Debug|x64
+ {24BAE093-7546-4ED1-AF5C-2A62AD244C63}.Debug|x64.Build.0 = Debug|x64
+ {24BAE093-7546-4ED1-AF5C-2A62AD244C63}.Release|x64.ActiveCfg = Release|x64
+ {24BAE093-7546-4ED1-AF5C-2A62AD244C63}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -170,6 +176,7 @@ Global
{D4EA97FD-45D3-4A26-843F-63427F0F1BFE} = {13110246-EBE1-441B-B721-B0614D62B13B}
{7107A105-51E2-46EB-859D-E2DD2FD51839} = {C059056C-D771-4CF5-A93F-AA7140FF7827}
{1EC566E8-C337-42FF-9BA4-DB66BFAE5B8A} = {7107A105-51E2-46EB-859D-E2DD2FD51839}
+ {24BAE093-7546-4ED1-AF5C-2A62AD244C63} = {7107A105-51E2-46EB-859D-E2DD2FD51839}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3761BFC9-DBEF-4186-BB8B-BC0D84ED9AE5}