Skip to content

Commit

Permalink
Add basic DirectWrite sample
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JeremyKuhne committed Feb 7, 2024
1 parent 9c96ca0 commit 4f83294
Show file tree
Hide file tree
Showing 24 changed files with 1,111 additions and 80 deletions.
85 changes: 42 additions & 43 deletions src/samples/DirectDraw/Direct2dDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Drawing;
using System.Numerics;
using Windows;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Direct2D;

namespace Direct2dDemo;
Expand All @@ -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);
}
}
}
11 changes: 11 additions & 0 deletions src/samples/DirectDraw/DirectWriteDemo/DirectWriteDemo.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\thirtytwo\thirtytwo.csproj" />
</ItemGroup>

</Project>
82 changes: 82 additions & 0 deletions src/samples/DirectDraw/DirectWriteDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
7 changes: 5 additions & 2 deletions src/thirtytwo/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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();
}
4 changes: 3 additions & 1 deletion src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,6 @@ WM_*
XFORMCOORDS
ID2D1Factory
D2D1CreateFactory
D2DERR_RECREATE_TARGET
D2DERR_RECREATE_TARGET
DWriteCreateFactory
IDWriteFactory
18 changes: 18 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_POINT_F.cs
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

namespace Windows.Win32.Graphics.Direct2D;

public unsafe class Factory : DisposableBase.Finalizable, IPointer<ID2D1Factory>
public unsafe class Direct2dFactory : DisposableBase.Finalizable, IPointer<ID2D1Factory>
{
private readonly AgileComPointer<ID2D1Factory> _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)
{
Expand Down
27 changes: 27 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/DrawTextOptions.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Modifications made to the draw text call that influence how the text is rendered.
/// [<see cref="D2D1_DRAW_TEXT_OPTIONS"/>]
/// </summary>
[Flags]
public enum DrawTextOptions
{
/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NO_SNAP"/>
NoSnap = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NO_SNAP,

/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_CLIP"/>
Clip = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_CLIP,

/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT"/>
EnableColorFont = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT,

/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING"/>
DisableColorBitmapSnapping = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING,

/// <inheritdoc cref="D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NONE"/>
None = D2D1_DRAW_TEXT_OPTIONS.D2D1_DRAW_TEXT_OPTIONS_NONE
}
22 changes: 22 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -92,4 +93,25 @@ public static SizeF Size<T>(this T target) where T : IPointer<ID2D1RenderTarget>
GC.KeepAlive(target);
return *(SizeF*)&size;
}

public static void DrawTextLayout<TTarget, TLayout, TBrush>(
this TTarget target,
PointF origin,
TLayout textLayout,
TBrush defaultFillBrush,
DrawTextOptions options = DrawTextOptions.None)
where TTarget : IPointer<ID2D1RenderTarget>
where TLayout : IPointer<IDWriteTextLayout>
where TBrush : IPointer<ID2D1Brush>
{
target.Pointer->DrawTextLayout(
origin,
textLayout.Pointer,
defaultFillBrush.Pointer,
(D2D1_DRAW_TEXT_OPTIONS)options);

GC.KeepAlive(target);
GC.KeepAlive(textLayout);
GC.KeepAlive(defaultFillBrush);
}
}
39 changes: 39 additions & 0 deletions src/thirtytwo/Win32/Graphics/DirectWrite/DirectWriteFactory.cs
Original file line number Diff line number Diff line change
@@ -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<IDWriteFactory>
{
private readonly AgileComPointer<IDWriteFactory> _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<IDWriteFactory>(),
(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<IDWriteFactory>(factory, takeOwnership: true);
}

protected override void Dispose(bool disposing)
{
Pointer = null;

if (disposing)
{
_factory.Dispose();
}
}
}
Loading

0 comments on commit 4f83294

Please sign in to comment.