Skip to content

Commit 4f83294

Browse files
committed
Add basic DirectWrite sample
Adds basic DirectWrite sample. Add OnPaint and OnSize to Window. I don't want to have a zillion `On` virtuals and there is a mechanism to attatch a set of handlers (say for mouse or touch input). This is partially for performance reasons, but also code bloat.
1 parent 9c96ca0 commit 4f83294

24 files changed

+1111
-80
lines changed

src/samples/DirectDraw/Direct2dDemo/Program.cs

+42-43
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Drawing;
55
using System.Numerics;
66
using Windows;
7-
using Windows.Win32.Foundation;
87
using Windows.Win32.Graphics.Direct2D;
98

109
namespace Direct2dDemo;
@@ -23,63 +22,63 @@ public Direct2dDemo() : base(title: "Simple Direct2D Application", features: Fea
2322
{
2423
}
2524

26-
protected override void RenderTargetCreated(HwndRenderTarget renderTarget)
25+
protected override void RenderTargetCreated()
2726
{
2827
_lightSlateGrayBrush?.Dispose();
2928
_cornflowerBlueBrush?.Dispose();
30-
_lightSlateGrayBrush = renderTarget.CreateSolidColorBrush(Color.LightSlateGray);
31-
_cornflowerBlueBrush = renderTarget.CreateSolidColorBrush(Color.CornflowerBlue);
32-
base.RenderTargetCreated(renderTarget);
29+
_lightSlateGrayBrush = RenderTarget.CreateSolidColorBrush(Color.LightSlateGray);
30+
_cornflowerBlueBrush = RenderTarget.CreateSolidColorBrush(Color.CornflowerBlue);
31+
base.RenderTargetCreated();
3332
}
3433

35-
protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
34+
protected override void OnPaint()
3635
{
37-
switch (message)
38-
{
39-
case MessageType.Paint:
40-
if (IsDirect2dEnabled(out var renderTarget))
41-
{
42-
renderTarget.SetTransform(Matrix3x2.Identity);
43-
renderTarget.Clear(Color.White);
36+
RenderTarget.SetTransform(Matrix3x2.Identity);
37+
RenderTarget.Clear(Color.White);
4438

45-
SizeF size = renderTarget.Size();
39+
SizeF size = RenderTarget.Size();
4640

47-
for (int x = 0; x < size.Width; x += 10)
48-
{
49-
renderTarget.DrawLine(
50-
new(x, 0), new(x, size.Height),
51-
_lightSlateGrayBrush!,
52-
0.5f);
53-
}
41+
for (int x = 0; x < size.Width; x += 10)
42+
{
43+
RenderTarget.DrawLine(
44+
new(x, 0), new(x, size.Height),
45+
_lightSlateGrayBrush!,
46+
0.5f);
47+
}
5448

55-
for (int y = 0; y < size.Height; y += 10)
56-
{
57-
renderTarget.DrawLine(
58-
new(0, y), new(size.Width, y),
59-
_lightSlateGrayBrush!,
60-
0.5f);
61-
}
49+
for (int y = 0; y < size.Height; y += 10)
50+
{
51+
RenderTarget.DrawLine(
52+
new(0, y), new(size.Width, y),
53+
_lightSlateGrayBrush!,
54+
0.5f);
55+
}
6256

63-
RectangleF rectangle1 = RectangleF.FromLTRB(
64-
size.Width / 2 - 50,
65-
size.Height / 2 - 50,
66-
size.Width / 2 + 50,
67-
size.Height / 2 + 50);
57+
RectangleF rectangle1 = RectangleF.FromLTRB(
58+
size.Width / 2 - 50,
59+
size.Height / 2 - 50,
60+
size.Width / 2 + 50,
61+
size.Height / 2 + 50);
6862

69-
RectangleF rectangle2 = RectangleF.FromLTRB(
70-
size.Width / 2 - 100,
71-
size.Height / 2 - 100,
72-
size.Width / 2 + 100,
73-
size.Height / 2 + 100);
63+
RectangleF rectangle2 = RectangleF.FromLTRB(
64+
size.Width / 2 - 100,
65+
size.Height / 2 - 100,
66+
size.Width / 2 + 100,
67+
size.Height / 2 + 100);
7468

75-
renderTarget.FillRectangle(rectangle1, _lightSlateGrayBrush!);
76-
renderTarget.DrawRectangle(rectangle2, _cornflowerBlueBrush!);
77-
}
69+
RenderTarget.FillRectangle(rectangle1, _lightSlateGrayBrush!);
70+
RenderTarget.DrawRectangle(rectangle2, _cornflowerBlueBrush!);
71+
}
7872

79-
return (LRESULT)0;
73+
protected override void Dispose(bool disposing)
74+
{
75+
if (disposing)
76+
{
77+
_lightSlateGrayBrush?.Dispose();
78+
_cornflowerBlueBrush?.Dispose();
8079
}
8180

82-
return base.WindowProcedure(window, message, wParam, lParam);
81+
base.Dispose(disposing);
8382
}
8483
}
8584
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\..\thirtytwo\thirtytwo.csproj" />
9+
</ItemGroup>
10+
11+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Drawing;
5+
using Windows;
6+
using Windows.Win32.Graphics.Direct2D;
7+
using Windows.Win32.Graphics.DirectWrite;
8+
using FontWeight = Windows.Win32.Graphics.DirectWrite.FontWeight;
9+
10+
namespace DirectWriteDemo;
11+
12+
internal class Program
13+
{
14+
[STAThread]
15+
private static void Main() => Application.Run(new DirectWriteDemo());
16+
17+
private class DirectWriteDemo : MainWindow
18+
{
19+
private const string Message = "Hello World From ... DirectWrite!";
20+
21+
protected TextFormat _textFormat;
22+
protected TextLayout? _textLayout;
23+
protected Typography _typography;
24+
25+
protected SolidColorBrush? _blackBrush;
26+
27+
public DirectWriteDemo() : base(title: "Simple DirectWrite Application", features: Features.EnableDirect2d)
28+
{
29+
_textFormat = new("Gabriola", fontSize: 64)
30+
{
31+
TextAlignment = TextAlignment.Center,
32+
ParagraphAlignment = ParagraphAlignment.Center
33+
};
34+
35+
_typography = new();
36+
_typography.AddFontFeature(FontFeatureTag.StylisticSet7);
37+
}
38+
39+
protected override void RenderTargetCreated()
40+
{
41+
_blackBrush?.Dispose();
42+
_blackBrush = RenderTarget.CreateSolidColorBrush(Color.Black);
43+
base.RenderTargetCreated();
44+
}
45+
46+
protected override void OnPaint()
47+
{
48+
RenderTarget.Clear(Color.CornflowerBlue);
49+
RenderTarget.DrawTextLayout(default, _textLayout!, _blackBrush!);
50+
}
51+
52+
protected override void OnSize(Size size)
53+
{
54+
if (_textLayout is not null)
55+
{
56+
_textLayout.MaxHeight = size.Height;
57+
_textLayout.MaxWidth = size.Width;
58+
return;
59+
}
60+
61+
_textLayout = new(Message, _textFormat, RenderTarget.Size());
62+
63+
// (21, 12) is the range around "DirectWrite!"
64+
_textLayout.SetFontSize(100, (21, 12));
65+
_textLayout.SetTypography(_typography, (0, Message.Length));
66+
_textLayout.SetUnderline(true, (21, 12));
67+
_textLayout.SetFontWeight(FontWeight.Bold, (21, 12));
68+
}
69+
70+
protected override void Dispose(bool disposing)
71+
{
72+
if (disposing)
73+
{
74+
_textFormat.Dispose();
75+
_textLayout?.Dispose();
76+
_blackBrush?.Dispose();
77+
}
78+
79+
base.Dispose(disposing);
80+
}
81+
}
82+
}

src/thirtytwo/Application.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
using System.Runtime.InteropServices;
66
using Windows.Support;
77
using Windows.Win32.Graphics.Direct2D;
8+
using Windows.Win32.Graphics.DirectWrite;
89

910
namespace Windows;
1011

1112
public static unsafe class Application
1213
{
1314
private static ActivationContext? s_visualStylesContext;
14-
private static Factory? s_direct2dFactory;
15+
private static Direct2dFactory? s_direct2dFactory;
16+
private static DirectWriteFactory? s_directWriteFactory;
1517

1618
internal static ActivationScope ThemingScope => new(GetStylesContext());
1719

@@ -207,5 +209,6 @@ public static void EnumerateThreadWindows(
207209
using var enumerator = new ThreadWindowEnumerator(threadId, callback);
208210
}
209211

210-
public static Factory Direct2dFactory => s_direct2dFactory ??= new();
212+
public static Direct2dFactory Direct2dFactory => s_direct2dFactory ??= new();
213+
public static DirectWriteFactory DirectWriteFactory => s_directWriteFactory ??= new();
211214
}

src/thirtytwo/NativeMethods.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,6 @@ WM_*
350350
XFORMCOORDS
351351
ID2D1Factory
352352
D2D1CreateFactory
353-
D2DERR_RECREATE_TARGET
353+
D2DERR_RECREATE_TARGET
354+
DWriteCreateFactory
355+
IDWriteFactory
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Drawing;
5+
6+
namespace Windows.Win32.Graphics.Direct2D.Common;
7+
8+
public partial struct D2D_POINT_2F
9+
{
10+
public D2D_POINT_2F(float x, float y)
11+
{
12+
this.x = x;
13+
this.y = y;
14+
}
15+
16+
public static implicit operator D2D_POINT_2F(PointF value) =>
17+
new(value.X, value.Y);
18+
}

src/thirtytwo/Win32/Graphics/Direct2D/Factory.cs src/thirtytwo/Win32/Graphics/Direct2D/Direct2dFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
namespace Windows.Win32.Graphics.Direct2D;
88

9-
public unsafe class Factory : DisposableBase.Finalizable, IPointer<ID2D1Factory>
9+
public unsafe class Direct2dFactory : DisposableBase.Finalizable, IPointer<ID2D1Factory>
1010
{
1111
private readonly AgileComPointer<ID2D1Factory> _factory;
1212

1313
public unsafe ID2D1Factory* Pointer { get; private set; }
1414

15-
public Factory(
15+
public Direct2dFactory(
1616
D2D1_FACTORY_TYPE factoryType = D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED,
1717
D2D1_DEBUG_LEVEL factoryOptions = D2D1_DEBUG_LEVEL.D2D1_DEBUG_LEVEL_NONE)
1818
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace Windows.Win32.Graphics.Direct2D;
5+
6+
/// <summary>
7+
/// Modifications made to the draw text call that influence how the text is rendered.
8+
/// [<see cref="D2D1_DRAW_TEXT_OPTIONS"/>]
9+
/// </summary>
10+
[Flags]
11+
public enum DrawTextOptions
12+
{
13+
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NO_SNAP"/>
14+
NoSnap = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NO_SNAP,
15+
16+
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_CLIP"/>
17+
Clip = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_CLIP,
18+
19+
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT"/>
20+
EnableColorFont = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT,
21+
22+
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING"/>
23+
DisableColorBitmapSnapping = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING,
24+
25+
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NONE"/>
26+
None = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NONE
27+
}

src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs

+22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Numerics;
66
using Windows.Support;
77
using Windows.Win32.Graphics.Direct2D.Common;
8+
using Windows.Win32.Graphics.DirectWrite;
89

910
namespace Windows.Win32.Graphics.Direct2D;
1011

@@ -92,4 +93,25 @@ public static SizeF Size<T>(this T target) where T : IPointer<ID2D1RenderTarget>
9293
GC.KeepAlive(target);
9394
return *(SizeF*)&size;
9495
}
96+
97+
public static void DrawTextLayout<TTarget, TLayout, TBrush>(
98+
this TTarget target,
99+
PointF origin,
100+
TLayout textLayout,
101+
TBrush defaultFillBrush,
102+
DrawTextOptions options = DrawTextOptions.None)
103+
where TTarget : IPointer<ID2D1RenderTarget>
104+
where TLayout : IPointer<IDWriteTextLayout>
105+
where TBrush : IPointer<ID2D1Brush>
106+
{
107+
target.Pointer->DrawTextLayout(
108+
origin,
109+
textLayout.Pointer,
110+
defaultFillBrush.Pointer,
111+
(D2D1_DRAW_TEXT_OPTIONS)options);
112+
113+
GC.KeepAlive(target);
114+
GC.KeepAlive(textLayout);
115+
GC.KeepAlive(defaultFillBrush);
116+
}
95117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Windows.Support;
5+
using Windows.Win32.System.Com;
6+
7+
namespace Windows.Win32.Graphics.DirectWrite;
8+
9+
public unsafe class DirectWriteFactory : DisposableBase.Finalizable, IPointer<IDWriteFactory>
10+
{
11+
private readonly AgileComPointer<IDWriteFactory> _factory;
12+
13+
public unsafe IDWriteFactory* Pointer { get; private set; }
14+
15+
public DirectWriteFactory(DWRITE_FACTORY_TYPE factoryType = DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED)
16+
{
17+
IDWriteFactory* factory;
18+
Interop.DWriteCreateFactory(
19+
factoryType,
20+
IID.Get<IDWriteFactory>(),
21+
(void**)&factory).ThrowOnFailure();
22+
23+
Pointer = factory;
24+
25+
// Ensure that this can be disposed on the finalizer thread by giving the "last" ref count
26+
// to an agile pointer.
27+
_factory = new AgileComPointer<IDWriteFactory>(factory, takeOwnership: true);
28+
}
29+
30+
protected override void Dispose(bool disposing)
31+
{
32+
Pointer = null;
33+
34+
if (disposing)
35+
{
36+
_factory.Dispose();
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)