Skip to content

Commit 80335ab

Browse files
committed
Add a basic WIC/Direct2D sample app
Adds enough functionality to load a bitmap from a file and render it to a Direct2D rendering target.
1 parent 698f574 commit 80335ab

22 files changed

+407
-9
lines changed

src/samples/DirectDraw/Direct2dDemo/Program.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Drawing;
5-
using System.Numerics;
65
using Windows;
76
using Windows.Win32.Graphics.Direct2D;
87

@@ -18,7 +17,10 @@ private class Direct2dDemo : MainWindow
1817
private SolidColorBrush? _lightSlateGrayBrush;
1918
private SolidColorBrush? _cornflowerBlueBrush;
2019

21-
public Direct2dDemo() : base(title: "Simple Direct2D Application", features: Features.EnableDirect2d)
20+
public Direct2dDemo() : base(
21+
title: "Simple Direct2D Application",
22+
backgroundColor: Color.White,
23+
features: Features.EnableDirect2d)
2224
{
2325
}
2426

@@ -33,9 +35,6 @@ protected override void RenderTargetCreated()
3335

3436
protected override void OnPaint()
3537
{
36-
RenderTarget.SetTransform(Matrix3x2.Identity);
37-
RenderTarget.Clear(Color.White);
38-
3938
SizeF size = RenderTarget.Size();
4039

4140
for (int x = 0; x < size.Width; x += 10)

src/samples/DirectDraw/DirectWriteDemo/Program.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ private class DirectWriteDemo : MainWindow
2424

2525
protected SolidColorBrush? _blackBrush;
2626

27-
public DirectWriteDemo() : base(title: "Simple DirectWrite Application", features: Features.EnableDirect2d)
27+
public DirectWriteDemo() : base(
28+
title: "Simple DirectWrite Application",
29+
backgroundColor: Color.CornflowerBlue,
30+
features: Features.EnableDirect2d)
2831
{
2932
_textFormat = new("Gabriola", fontSize: 64)
3033
{
@@ -45,8 +48,7 @@ protected override void RenderTargetCreated()
4548

4649
protected override void OnPaint()
4750
{
48-
RenderTarget.Clear(Color.CornflowerBlue);
49-
RenderTarget.DrawTextLayout(default, _textLayout!, _blackBrush!);
51+
RenderTarget.DrawTextLayout(origin: default, _textLayout!, _blackBrush!);
5052
}
5153

5254
protected override void OnSize(Size size)
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\..\..\thirtytwo\thirtytwo.csproj" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<None Update="Blue Marble 2012 Original.jpg">
14+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
15+
</None>
16+
</ItemGroup>
17+
18+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.Diagnostics.CodeAnalysis;
5+
using System.Drawing;
6+
using Windows;
7+
using Windows.Win32.Graphics.Direct2D;
8+
using Windows.Win32.Graphics.Imaging;
9+
using Bitmap = Windows.Win32.Graphics.Direct2D.Bitmap;
10+
11+
namespace ImagingDemo;
12+
13+
internal class Program
14+
{
15+
[STAThread]
16+
private static void Main() => Application.Run(new ImagingDemo());
17+
18+
private unsafe class ImagingDemo : MainWindow
19+
{
20+
private FormatConverter? _converter;
21+
private Bitmap? _bitmap;
22+
23+
24+
public ImagingDemo() : base(
25+
title: "Simple Windows Imaging Application",
26+
backgroundColor: Color.Black,
27+
features: Features.EnableDirect2d)
28+
{
29+
}
30+
31+
[MemberNotNull(nameof(_converter))]
32+
private void CreateBitmapFromFile(string fileName)
33+
{
34+
_converter?.Dispose();
35+
_bitmap?.Dispose();
36+
37+
using BitmapDecoder decoder = new(fileName);
38+
using BitmapFrameDecode frame = decoder.GetFrame(0);
39+
_converter = new(frame);
40+
}
41+
42+
protected override void RenderTargetCreated()
43+
{
44+
if (_converter is null)
45+
{
46+
CreateBitmapFromFile("Blue Marble 2012 Original.jpg");
47+
}
48+
49+
_bitmap?.Dispose();
50+
_bitmap = RenderTarget.CreateBitmapFromWicBitmap(_converter);
51+
base.RenderTargetCreated();
52+
}
53+
54+
protected override void OnPaint()
55+
{
56+
if (_bitmap is not null)
57+
{
58+
RenderTarget.DrawBitmap(_bitmap);
59+
}
60+
}
61+
62+
protected override void Dispose(bool disposing)
63+
{
64+
if (disposing)
65+
{
66+
_converter?.Dispose();
67+
_bitmap?.Dispose();
68+
}
69+
70+
base.Dispose(disposing);
71+
}
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"Blue Marble 2012 Original"" image is in the public domain.
2+
Credit: NASA/NOAA/GSFC/Suomi NPP/VIIRS/Norman Kuring
3+
Source: https://flickr.com/photos/nasacommons/31258502484/

src/thirtytwo/Application.cs

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Windows.Support;
77
using Windows.Win32.Graphics.Direct2D;
88
using Windows.Win32.Graphics.DirectWrite;
9+
using Windows.Win32.Graphics.Imaging;
910

1011
namespace Windows;
1112

@@ -15,6 +16,7 @@ public static unsafe class Application
1516
private static Direct2dFactory? s_direct2dFactory;
1617
private static DirectWriteFactory? s_directWriteFactory;
1718
private static DirectWriteGdiInterop? s_directWriteGdiInterop;
19+
private static ImagingFactory? s_imagingFactory;
1820

1921
internal static ActivationScope ThemingScope => new(GetStylesContext());
2022

@@ -229,4 +231,5 @@ public static string GetUserDefaultLocaleName()
229231
public static Direct2dFactory Direct2dFactory => s_direct2dFactory ??= new();
230232
public static DirectWriteFactory DirectWriteFactory => s_directWriteFactory ??= new();
231233
public static DirectWriteGdiInterop DirectWriteGdiInterop => s_directWriteGdiInterop ??= new();
234+
public static ImagingFactory ImagingFactory => s_imagingFactory ??= new();
232235
}

src/thirtytwo/NativeMethods.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,7 @@ VIRTUAL_KEY
362362
WIN32_ERROR
363363
WINDOWPOS
364364
WM_*
365-
XFORMCOORDS
365+
XFORMCOORDS
366+
IWICImagingFactory2
367+
CLSID_WICImagingFactory2
368+
GUID_WICPixelFormat32bppPBGRA
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
6+
namespace Windows.Win32.Graphics.Direct2D;
7+
8+
public unsafe class Bitmap : Image, IPointer<ID2D1Bitmap>
9+
{
10+
public unsafe new ID2D1Bitmap* Pointer => (ID2D1Bitmap*)base.Pointer;
11+
12+
public Bitmap(ID2D1Bitmap* bitmap) : base((ID2D1Image*)bitmap)
13+
{
14+
}
15+
16+
public static implicit operator ID2D1Bitmap*(Bitmap bitmap) => bitmap.Pointer;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
public enum BitmapInterpolationMode
7+
{
8+
/// <inheritdoc cref="D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR"/>
9+
NearestNeighbor = D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
10+
11+
/// <inheritdoc cref="D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_LINEAR"/>
12+
Linear = D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_LINEAR
13+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77

88
namespace Windows.Win32.Graphics.Direct2D;
99

10+
/// <summary>
11+
/// <see cref="HWND"/> render target.
12+
/// </summary>
13+
/// <devdoc>
14+
/// <see href="https://learn.microsoft.com/windows/win32/Direct2D/supported-pixel-formats-and-alpha-modes#supported-formats-for-id2d1hwndrendertarget">
15+
/// Supported Formats for ID2D1HwndRenderTarget
16+
/// </see>
17+
/// </devdoc>
1018
public unsafe class HwndRenderTarget : RenderTarget, IPointer<ID2D1HwndRenderTarget>
1119
{
1220
public new ID2D1HwndRenderTarget* Pointer => (ID2D1HwndRenderTarget*)base.Pointer;
@@ -34,6 +42,8 @@ public static HwndRenderTarget CreateForWindow<TFactory, TWindow>(
3442
where TFactory : IPointer<ID2D1Factory>
3543
where TWindow : IHandle<HWND>
3644
{
45+
// DXGI_FORMAT_B8G8R8A8_UNORM is the recommended pixel format for HwndRenderTarget for performance reasons.
46+
// DXGI_FORMAT_UNKNOWN and DXGI_FORMAT_UNKNOWN give DXGI_FORMAT_B8G8R8A8_UNORM and D2D1_ALPHA_MODE_IGNORE.
3747
D2D1_RENDER_TARGET_PROPERTIES properties = default;
3848
D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProperties = new()
3949
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
6+
namespace Windows.Win32.Graphics.Direct2D;
7+
8+
public unsafe class Image : Resource, IPointer<ID2D1Image>
9+
{
10+
public unsafe new ID2D1Image* Pointer => (ID2D1Image*)base.Pointer;
11+
12+
public Image(ID2D1Image* image) : base((ID2D1Resource*)image)
13+
{
14+
}
15+
16+
public static implicit operator ID2D1Image*(Image image) => image.Pointer;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
public enum InterpolationMode
7+
{
8+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR"/>
9+
NearestNeighbor = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
10+
11+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_LINEAR"/>
12+
Linear = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_LINEAR,
13+
14+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_CUBIC"/>
15+
Cubic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_CUBIC,
16+
17+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR"/>
18+
MultiSampleLinear = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR,
19+
20+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_ANISOTROPIC"/>
21+
Anisotropic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_ANISOTROPIC,
22+
23+
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC"/>
24+
HighQualityCubic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC
25+
}

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

+51
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Windows.Support;
77
using Windows.Win32.Graphics.Direct2D.Common;
88
using Windows.Win32.Graphics.DirectWrite;
9+
using Windows.Win32.Graphics.Imaging;
910

1011
namespace Windows.Win32.Graphics.Direct2D;
1112

@@ -115,4 +116,54 @@ public static void DrawTextLayout<TTarget, TLayout, TBrush>(
115116
GC.KeepAlive(textLayout);
116117
GC.KeepAlive(defaultFillBrush);
117118
}
119+
120+
/// <inheritdoc cref="ID2D1RenderTarget.CreateBitmapFromWicBitmap(IWICBitmapSource*, D2D1_BITMAP_PROPERTIES*, ID2D1Bitmap**)"/>
121+
public static Bitmap CreateBitmapFromWicBitmap<TRenderTarget, TBitmapSource>(
122+
this TRenderTarget target,
123+
TBitmapSource wicBitmap)
124+
where TRenderTarget : IPointer<ID2D1RenderTarget>
125+
where TBitmapSource : IPointer<IWICBitmapSource>
126+
{
127+
ID2D1Bitmap* d2dBitmap;
128+
target.Pointer->CreateBitmapFromWicBitmap(
129+
wicBitmap.Pointer,
130+
bitmapProperties: (D2D1_BITMAP_PROPERTIES*)null,
131+
&d2dBitmap).ThrowOnFailure();
132+
133+
Bitmap bitmap = new(d2dBitmap);
134+
GC.KeepAlive(target);
135+
GC.KeepAlive(wicBitmap);
136+
return bitmap;
137+
}
138+
139+
public static void DrawBitmap<TRenderTarget, TBitmap>(
140+
this TRenderTarget target,
141+
TBitmap bitmap,
142+
RectangleF destinationRectangle = default,
143+
float opacity = 1.0f,
144+
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.Linear)
145+
where TRenderTarget : IPointer<ID2D1RenderTarget>
146+
where TBitmap : IPointer<ID2D1Bitmap>
147+
{
148+
D2D_RECT_F destination = (D2D_RECT_F)destinationRectangle;
149+
if (destinationRectangle.IsEmpty)
150+
{
151+
D2D_SIZE_F size = target.Pointer->GetSizeHack();
152+
destination = new D2D_RECT_F { left = 0, top = 0, right = size.width, bottom = size.height };
153+
}
154+
else
155+
{
156+
destination = (D2D_RECT_F)destinationRectangle;
157+
}
158+
159+
target.Pointer->DrawBitmap(
160+
bitmap.Pointer,
161+
&destination,
162+
opacity,
163+
(D2D1_BITMAP_INTERPOLATION_MODE)interpolationMode,
164+
null);
165+
166+
GC.KeepAlive(target);
167+
GC.KeepAlive(bitmap);
168+
}
118169
}

src/thirtytwo/Win32/Graphics/DirectDrawBase.cs

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
namespace Windows.Win32.Graphics;
88

9+
/// <summary>
10+
/// Base class for DirectDraw objects.
11+
/// </summary>
12+
/// <devdoc>
13+
/// <see href="https://learn.microsoft.com/archive/msdn-magazine/2009/june/introducing-direct2d"/>
14+
///
15+
/// Direct3D, DirectWrite, and Direct2D use a lightweight version of the COM specification to manage object lifetime
16+
/// through interfaces derived from IUnknown. There's no need to initialize the COM run time and worry about apartments
17+
/// or proxies. It's just a convention to simplify resource management and allow APIs and applications to expose and
18+
/// consume objects in a well-defined way.
19+
/// </devdoc>
920
public unsafe abstract class DirectDrawBase<T> : DisposableBase.Finalizable, IPointer<T> where T : unmanaged
1021
{
1122
private nint _pointer;

0 commit comments

Comments
 (0)