Skip to content

Commit

Permalink
Initial Direct2D Support
Browse files Browse the repository at this point in the history
Initial stab at integrated Direct2D support.
  • Loading branch information
JeremyKuhne committed Feb 6, 2024
1 parent c067a51 commit 085a430
Show file tree
Hide file tree
Showing 19 changed files with 689 additions and 34 deletions.
11 changes: 11 additions & 0 deletions src/samples/DirectDraw/Direct2dDemo/Direct2dDemo.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>
84 changes: 84 additions & 0 deletions src/samples/DirectDraw/Direct2dDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 System.Numerics;
using Windows;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Direct2D;

namespace Direct2dDemo;

internal class Program
{
[STAThread]
private static void Main() => Application.Run(new Direct2dDemo());

private class Direct2dDemo : MainWindow
{
private SolidColorBrush? _lightSlateGrayBrush;
private SolidColorBrush? _cornflowerBlueBrush;

public Direct2dDemo() : base(title: "Simple Direct2D Application", features: Features.EnableDirect2d)
{
}

protected override void RenderTargetCreated(HwndRenderTarget renderTarget)
{
_lightSlateGrayBrush?.Dispose();
_cornflowerBlueBrush?.Dispose();
_lightSlateGrayBrush = renderTarget.CreateSolidColorBrush(Color.LightSlateGray);
_cornflowerBlueBrush = renderTarget.CreateSolidColorBrush(Color.CornflowerBlue);
base.RenderTargetCreated(renderTarget);
}

protected override LRESULT WindowProcedure(HWND window, MessageType message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case MessageType.Paint:
if (IsDirect2dEnabled(out var renderTarget))
{
renderTarget.SetTransform(Matrix3x2.Identity);
renderTarget.Clear(Color.White);

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 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 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!);
}
return (LRESULT)0;
}

return base.WindowProcedure(window, message, wParam, lParam);
}
}
}
6 changes: 4 additions & 2 deletions src/thirtytwo/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public MainWindow(
WindowClass? windowClass = default,
nint parameters = default,
HMENU menuHandle = default,
HBRUSH backgroundBrush = default) : base(
HBRUSH backgroundBrush = default,
Features features = default) : base(
bounds,
title,
style,
Expand All @@ -27,6 +28,7 @@ public MainWindow(
windowClass,
parameters,
menuHandle,
backgroundBrush)
backgroundBrush,
features)
{ }
}
5 changes: 4 additions & 1 deletion src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,7 @@ VIRTUAL_KEY
WIN32_ERROR
WINDOWPOS
WM_*
XFORMCOORDS
XFORMCOORDS
ID2D1Factory
D2D1CreateFactory
D2DERR_RECREATE_TARGET
82 changes: 82 additions & 0 deletions src/thirtytwo/Support/EnumExtensions.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.Runtime.CompilerServices;

namespace System;

internal static class EnumExtensions
{
/// <summary>
/// Returns true if the given flag or flags are set.
/// </summary>
/// <remarks>
/// Simple wrapper for <see cref="Enum.HasFlag(Enum)"/> that gives you better intellisense.
/// </remarks>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static unsafe bool AreFlagsSet<T>(this T value, T flags) where T : unmanaged, Enum => value.HasFlag(flags);

/// <summary>
/// Sets the given flag or flags.
/// </summary>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static unsafe void SetFlags<T>(this ref T value, T flags) where T : unmanaged, Enum
{
fixed (T* v = &value)
{
// Note that the non-relevant if clauses will be omitted by the JIT so these become one statement.
if (sizeof(T) == sizeof(byte))
{
*(byte*)v |= *(byte*)&flags;
}
else if (sizeof(T) == sizeof(short))
{
*(short*)v |= *(short*)&flags;
}
else if (sizeof(T) == sizeof(int))
{
*(int*)v |= *(int*)&flags;
}
else if (sizeof(T) == sizeof(long))
{
*(long*)v |= *(long*)&flags;
}
else
{
throw new InvalidOperationException();
}
}
}

[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static unsafe void ClearFlags<T>(this ref T value, T flags) where T : unmanaged, Enum
{
fixed (T* v = &value)
{
// Note that the non-relevant if clauses will be omitted by the JIT so these become one statement.
if (sizeof(T) == sizeof(byte))
{
*(byte*)v &= (byte)~*(byte*)&flags;
}
else if (sizeof(T) == sizeof(short))
{
*(short*)v &= (short)~*(short*)&flags;
}
else if (sizeof(T) == sizeof(int))
{
*(int*)v &= ~*(int*)&flags;
}
else if (sizeof(T) == sizeof(long))
{
*(long*)v &= ~*(long*)&flags;
}
else
{
throw new InvalidOperationException();
}
}
}
}
19 changes: 19 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Brush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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;

namespace Windows.Win32.Graphics.Direct2D;

public unsafe class Brush : Resource, IPointer<ID2D1Brush>
{
public unsafe new ID2D1Brush* Pointer { get; private set; }

public Brush(ID2D1Brush* brush) : base((ID2D1Resource*)brush) => Pointer = brush;

protected override void Dispose(bool disposing)
{
Pointer = null;
base.Dispose(disposing);
}
}
20 changes: 20 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D1_COLOR_F.cs
Original file line number Diff line number Diff line change
@@ -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.

using System.Drawing;

namespace Windows.Win32.Graphics.Direct2D.Common;

public partial struct D2D1_COLOR_F
{
public D2D1_COLOR_F(float r, float g, float b, float a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}

public static explicit operator D2D1_COLOR_F(Color value) =>
new(value.R / 255.0f, value.G / 255.0f, value.B / 255.0f, value.A / 255.0f);
}
20 changes: 20 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_RECT_F.cs
Original file line number Diff line number Diff line change
@@ -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.

using System.Drawing;

namespace Windows.Win32.Graphics.Direct2D.Common;

public partial struct D2D_RECT_F
{
public D2D_RECT_F(float left, float top, float right, float bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}

public static implicit operator D2D_RECT_F(RectangleF value) =>
new(value.Left, value.Top, value.Right, value.Bottom);
}
18 changes: 18 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Common/D2D_SIZE_U.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_SIZE_U
{
public D2D_SIZE_U(uint width, uint height)
{
this.width = width;
this.height = height;
}

public static explicit operator D2D_SIZE_U(Size value) =>
new(checked((uint)value.Width), checked((uint)value.Height));
}
42 changes: 42 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Factory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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.Direct2D;

public unsafe class Factory : DisposableBase.Finalizable, IPointer<ID2D1Factory>
{
private readonly AgileComPointer<ID2D1Factory> _factory;

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

public Factory(
D2D1_FACTORY_TYPE factoryType = D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED,
D2D1_DEBUG_LEVEL factoryOptions = D2D1_DEBUG_LEVEL.D2D1_DEBUG_LEVEL_NONE)
{
ID2D1Factory* factory;
Interop.D2D1CreateFactory(
factoryType,
IID.Get<ID2D1Factory>(),
(D2D1_FACTORY_OPTIONS*)&factoryOptions,
(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<ID2D1Factory>(factory, takeOwnership: true);
}

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

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

0 comments on commit 085a430

Please sign in to comment.