Skip to content

Commit

Permalink
Add a basic WIC/Direct2D sample app
Browse files Browse the repository at this point in the history
Adds enough functionality to load a bitmap from a file and render it to a Direct2D rendering target.
  • Loading branch information
JeremyKuhne committed Feb 11, 2024
1 parent 698f574 commit 80335ab
Show file tree
Hide file tree
Showing 22 changed files with 407 additions and 9 deletions.
9 changes: 4 additions & 5 deletions src/samples/DirectDraw/Direct2dDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Graphics.Direct2D;

Expand All @@ -18,7 +17,10 @@ private class Direct2dDemo : MainWindow
private SolidColorBrush? _lightSlateGrayBrush;
private SolidColorBrush? _cornflowerBlueBrush;

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

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

protected override void OnPaint()
{
RenderTarget.SetTransform(Matrix3x2.Identity);
RenderTarget.Clear(Color.White);

SizeF size = RenderTarget.Size();

for (int x = 0; x < size.Width; x += 10)
Expand Down
8 changes: 5 additions & 3 deletions src/samples/DirectDraw/DirectWriteDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ private class DirectWriteDemo : MainWindow

protected SolidColorBrush? _blackBrush;

public DirectWriteDemo() : base(title: "Simple DirectWrite Application", features: Features.EnableDirect2d)
public DirectWriteDemo() : base(
title: "Simple DirectWrite Application",
backgroundColor: Color.CornflowerBlue,
features: Features.EnableDirect2d)
{
_textFormat = new("Gabriola", fontSize: 64)
{
Expand All @@ -45,8 +48,7 @@ protected override void RenderTargetCreated()

protected override void OnPaint()
{
RenderTarget.Clear(Color.CornflowerBlue);
RenderTarget.DrawTextLayout(default, _textLayout!, _blackBrush!);
RenderTarget.DrawTextLayout(origin: default, _textLayout!, _blackBrush!);
}

protected override void OnSize(Size size)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/samples/DirectDraw/ImagingDemo/ImagingDemo.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

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

<ItemGroup>
<None Update="Blue Marble 2012 Original.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
73 changes: 73 additions & 0 deletions src/samples/DirectDraw/ImagingDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -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.Diagnostics.CodeAnalysis;
using System.Drawing;
using Windows;
using Windows.Win32.Graphics.Direct2D;
using Windows.Win32.Graphics.Imaging;
using Bitmap = Windows.Win32.Graphics.Direct2D.Bitmap;

namespace ImagingDemo;

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

private unsafe class ImagingDemo : MainWindow
{
private FormatConverter? _converter;
private Bitmap? _bitmap;


public ImagingDemo() : base(
title: "Simple Windows Imaging Application",
backgroundColor: Color.Black,
features: Features.EnableDirect2d)
{
}

[MemberNotNull(nameof(_converter))]
private void CreateBitmapFromFile(string fileName)
{
_converter?.Dispose();
_bitmap?.Dispose();

using BitmapDecoder decoder = new(fileName);
using BitmapFrameDecode frame = decoder.GetFrame(0);
_converter = new(frame);
}

protected override void RenderTargetCreated()
{
if (_converter is null)
{
CreateBitmapFromFile("Blue Marble 2012 Original.jpg");
}

_bitmap?.Dispose();
_bitmap = RenderTarget.CreateBitmapFromWicBitmap(_converter);
base.RenderTargetCreated();
}

protected override void OnPaint()
{
if (_bitmap is not null)
{
RenderTarget.DrawBitmap(_bitmap);
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_converter?.Dispose();
_bitmap?.Dispose();
}

base.Dispose(disposing);
}
}
}
3 changes: 3 additions & 0 deletions src/samples/DirectDraw/ImagingDemo/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"Blue Marble 2012 Original"" image is in the public domain.
Credit: NASA/NOAA/GSFC/Suomi NPP/VIIRS/Norman Kuring
Source: https://flickr.com/photos/nasacommons/31258502484/
3 changes: 3 additions & 0 deletions src/thirtytwo/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Windows.Support;
using Windows.Win32.Graphics.Direct2D;
using Windows.Win32.Graphics.DirectWrite;
using Windows.Win32.Graphics.Imaging;

namespace Windows;

Expand All @@ -15,6 +16,7 @@ public static unsafe class Application
private static Direct2dFactory? s_direct2dFactory;
private static DirectWriteFactory? s_directWriteFactory;
private static DirectWriteGdiInterop? s_directWriteGdiInterop;
private static ImagingFactory? s_imagingFactory;

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

Expand Down Expand Up @@ -229,4 +231,5 @@ public static string GetUserDefaultLocaleName()
public static Direct2dFactory Direct2dFactory => s_direct2dFactory ??= new();
public static DirectWriteFactory DirectWriteFactory => s_directWriteFactory ??= new();
public static DirectWriteGdiInterop DirectWriteGdiInterop => s_directWriteGdiInterop ??= new();
public static ImagingFactory ImagingFactory => s_imagingFactory ??= new();
}
5 changes: 4 additions & 1 deletion src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,7 @@ VIRTUAL_KEY
WIN32_ERROR
WINDOWPOS
WM_*
XFORMCOORDS
XFORMCOORDS
IWICImagingFactory2
CLSID_WICImagingFactory2
GUID_WICPixelFormat32bppPBGRA
17 changes: 17 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Bitmap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 Bitmap : Image, IPointer<ID2D1Bitmap>
{
public unsafe new ID2D1Bitmap* Pointer => (ID2D1Bitmap*)base.Pointer;

public Bitmap(ID2D1Bitmap* bitmap) : base((ID2D1Image*)bitmap)
{
}

public static implicit operator ID2D1Bitmap*(Bitmap bitmap) => bitmap.Pointer;
}
13 changes: 13 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/BitmapInterpolationMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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;

public enum BitmapInterpolationMode
{
/// <inheritdoc cref="D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR"/>
NearestNeighbor = D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,

/// <inheritdoc cref="D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_LINEAR"/>
Linear = D2D1_BITMAP_INTERPOLATION_MODE.D2D1_BITMAP_INTERPOLATION_MODE_LINEAR
}
10 changes: 10 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/HwndRenderTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

namespace Windows.Win32.Graphics.Direct2D;

/// <summary>
/// <see cref="HWND"/> render target.
/// </summary>
/// <devdoc>
/// <see href="https://learn.microsoft.com/windows/win32/Direct2D/supported-pixel-formats-and-alpha-modes#supported-formats-for-id2d1hwndrendertarget">
/// Supported Formats for ID2D1HwndRenderTarget
/// </see>
/// </devdoc>
public unsafe class HwndRenderTarget : RenderTarget, IPointer<ID2D1HwndRenderTarget>
{
public new ID2D1HwndRenderTarget* Pointer => (ID2D1HwndRenderTarget*)base.Pointer;
Expand Down Expand Up @@ -34,6 +42,8 @@ public static HwndRenderTarget CreateForWindow<TFactory, TWindow>(
where TFactory : IPointer<ID2D1Factory>
where TWindow : IHandle<HWND>
{
// DXGI_FORMAT_B8G8R8A8_UNORM is the recommended pixel format for HwndRenderTarget for performance reasons.
// DXGI_FORMAT_UNKNOWN and DXGI_FORMAT_UNKNOWN give DXGI_FORMAT_B8G8R8A8_UNORM and D2D1_ALPHA_MODE_IGNORE.
D2D1_RENDER_TARGET_PROPERTIES properties = default;
D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProperties = new()
{
Expand Down
17 changes: 17 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/Image.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 Image : Resource, IPointer<ID2D1Image>
{
public unsafe new ID2D1Image* Pointer => (ID2D1Image*)base.Pointer;

public Image(ID2D1Image* image) : base((ID2D1Resource*)image)
{
}

public static implicit operator ID2D1Image*(Image image) => image.Pointer;
}
25 changes: 25 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/InterpolationMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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;

public enum InterpolationMode
{
/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR"/>
NearestNeighbor = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,

/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_LINEAR"/>
Linear = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_LINEAR,

/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_CUBIC"/>
Cubic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_CUBIC,

/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR"/>
MultiSampleLinear = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR,

/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_ANISOTROPIC"/>
Anisotropic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_ANISOTROPIC,

/// <inheritdoc cref="D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC"/>
HighQualityCubic = D2D1_INTERPOLATION_MODE.D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC
}
51 changes: 51 additions & 0 deletions src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Windows.Support;
using Windows.Win32.Graphics.Direct2D.Common;
using Windows.Win32.Graphics.DirectWrite;
using Windows.Win32.Graphics.Imaging;

namespace Windows.Win32.Graphics.Direct2D;

Expand Down Expand Up @@ -115,4 +116,54 @@ public static void DrawTextLayout<TTarget, TLayout, TBrush>(
GC.KeepAlive(textLayout);
GC.KeepAlive(defaultFillBrush);
}

/// <inheritdoc cref="ID2D1RenderTarget.CreateBitmapFromWicBitmap(IWICBitmapSource*, D2D1_BITMAP_PROPERTIES*, ID2D1Bitmap**)"/>
public static Bitmap CreateBitmapFromWicBitmap<TRenderTarget, TBitmapSource>(
this TRenderTarget target,
TBitmapSource wicBitmap)
where TRenderTarget : IPointer<ID2D1RenderTarget>
where TBitmapSource : IPointer<IWICBitmapSource>
{
ID2D1Bitmap* d2dBitmap;
target.Pointer->CreateBitmapFromWicBitmap(
wicBitmap.Pointer,
bitmapProperties: (D2D1_BITMAP_PROPERTIES*)null,
&d2dBitmap).ThrowOnFailure();

Bitmap bitmap = new(d2dBitmap);
GC.KeepAlive(target);
GC.KeepAlive(wicBitmap);
return bitmap;
}

public static void DrawBitmap<TRenderTarget, TBitmap>(
this TRenderTarget target,
TBitmap bitmap,
RectangleF destinationRectangle = default,
float opacity = 1.0f,
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.Linear)
where TRenderTarget : IPointer<ID2D1RenderTarget>
where TBitmap : IPointer<ID2D1Bitmap>
{
D2D_RECT_F destination = (D2D_RECT_F)destinationRectangle;
if (destinationRectangle.IsEmpty)
{
D2D_SIZE_F size = target.Pointer->GetSizeHack();
destination = new D2D_RECT_F { left = 0, top = 0, right = size.width, bottom = size.height };
}
else
{
destination = (D2D_RECT_F)destinationRectangle;
}

target.Pointer->DrawBitmap(
bitmap.Pointer,
&destination,
opacity,
(D2D1_BITMAP_INTERPOLATION_MODE)interpolationMode,
null);

GC.KeepAlive(target);
GC.KeepAlive(bitmap);
}
}
11 changes: 11 additions & 0 deletions src/thirtytwo/Win32/Graphics/DirectDrawBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

namespace Windows.Win32.Graphics;

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

0 comments on commit 80335ab

Please sign in to comment.