diff --git a/src/samples/DirectDraw/ImagingDemo/Program.cs b/src/samples/DirectDraw/ImagingDemo/Program.cs
index 1012f19..ddd397f 100644
--- a/src/samples/DirectDraw/ImagingDemo/Program.cs
+++ b/src/samples/DirectDraw/ImagingDemo/Program.cs
@@ -7,6 +7,7 @@
using Windows.Win32.Graphics.Direct2D;
using Windows.Win32.Graphics.Imaging;
using Bitmap = Windows.Win32.Graphics.Direct2D.Bitmap;
+using GdiPlusBitmap = Windows.Win32.Graphics.GdiPlus.Bitmap;
namespace ImagingDemo;
@@ -19,6 +20,7 @@ private unsafe class ImagingDemo : MainWindow
{
private FormatConverter? _converter;
private Bitmap? _bitmap;
+ private GdiPlusBitmap? _gdiPlusBitmap;
public ImagingDemo() : base(
@@ -29,6 +31,7 @@ public ImagingDemo() : base(
}
[MemberNotNull(nameof(_converter))]
+ [MemberNotNull(nameof(_gdiPlusBitmap))]
private void CreateBitmapFromFile(string fileName)
{
_converter?.Dispose();
@@ -37,16 +40,20 @@ private void CreateBitmapFromFile(string fileName)
using BitmapDecoder decoder = new(fileName);
using BitmapFrameDecode frame = decoder.GetFrame(0);
_converter = new(frame);
+
+ _gdiPlusBitmap = new(fileName);
}
protected override void RenderTargetCreated()
{
- if (_converter is null)
+ if (_converter is null || _gdiPlusBitmap is null)
{
CreateBitmapFromFile("Blue Marble 2012 Original.jpg");
}
_bitmap?.Dispose();
+
+ //_bitmap = RenderTarget.CreateBitmapFromGdiPlusBitmap(_gdiPlusBitmap);
_bitmap = RenderTarget.CreateBitmapFromWicBitmap(_converter);
base.RenderTargetCreated();
}
diff --git a/src/thirtytwo/GlobalSuppressions.cs b/src/thirtytwo/GlobalSuppressions.cs
index 3ebcd87..08bff05 100644
--- a/src/thirtytwo/GlobalSuppressions.cs
+++ b/src/thirtytwo/GlobalSuppressions.cs
@@ -12,5 +12,6 @@
[assembly: SuppressMessage("Design", "CA1069:Enums values should not be duplicated", Justification = "Matches Windows", Scope = "type", Target = "~T:Windows.MessageBoxStyles")]
[assembly: SuppressMessage("Design", "CA1069:Enums values should not be duplicated", Justification = "Matches Windows", Scope = "type", Target = "~T:Windows.WindowStyles")]
[assembly: SuppressMessage("Design", "CA1069:Enums values should not be duplicated", Justification = "Matches Windows", Scope = "type", Target = "~T:Windows.VirtualKey")]
+[assembly: SuppressMessage("Design", "CA1069:Enums values should not be duplicated", Justification = "Matches Windows", Scope = "type", Target = "~T:Windows.Win32.Graphics.GdiPlus.PixelFormat")]
[assembly: SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "CsWin32", Scope = "type", Target = "~T:Windows.Win32.Foundation.PCWSTR")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Thread Local", Scope = "member", Target = "~F:Windows.WindowClass.t_initializeProcedure")]
\ No newline at end of file
diff --git a/src/thirtytwo/NativeMethods.txt b/src/thirtytwo/NativeMethods.txt
index 3482d86..ee1ef57 100644
--- a/src/thirtytwo/NativeMethods.txt
+++ b/src/thirtytwo/NativeMethods.txt
@@ -16,6 +16,7 @@ CLEARTYPE_NATURAL_QUALITY
ClientToScreen
CLIPBOARD_FORMAT
CloseClipboard
+CLSID_WICImagingFactory2
CoCreateInstance
CoGetClassObject
CombineRgn
@@ -93,15 +94,36 @@ FONT_WEIGHT
FormatMessage
FrameRect
FVE_E_LOCKED_VOLUME
+GdipBitmapLockBits
+GdipBitmapUnlockBits
+GdipCreateBitmapFromDirectDrawSurface
+GdipCreateBitmapFromFile
+GdipCreateBitmapFromGdiDib
+GdipCreateBitmapFromGraphics
+GdipCreateBitmapFromHBITMAP
+GdipCreateBitmapFromHICON
+GdipCreateBitmapFromResource
+GdipCreateBitmapFromScan0
+GdipCreateBitmapFromStream
GdipCreateFromHDC
+GdipCreateHBITMAPFromBitmap
+GdipCreateHICONFromBitmap
GdipCreatePen1
GdipCreateSolidFill
GdipDeleteBrush
GdipDeleteGraphics
GdipDeletePen
+GdipDisposeImage
+GdipDrawImageRect
+GdipDrawImageRectRect
GdipDrawLines
GdipDrawLinesI
GdipFillEllipse
+GdipGetImageBounds
+GdipGetImageFlags
+GdipGetImageGraphicsContext
+GdipGetImagePixelFormat
+GdipGetImageRawFormat
GdiplusShutdown
GdiplusStartup
GdipSetSmoothingMode
@@ -168,6 +190,7 @@ GlobalFree
GlobalLock
GlobalSize
GlobalUnlock
+GUID_WICPixelFormat32bppPBGRA
HFONT
HKEY_*
HRESULT
@@ -194,6 +217,8 @@ IFileDialogCustomize
IFileDialogEvents
IFileOpenDialog
IGlobalInterfaceTable
+ImageFlags
+ImageLockMode
IMarshal
IModalWindow
InitCommonControlsEx
@@ -223,6 +248,7 @@ IsWindowEnabled
IsWindowVisible
IUIAutomationElement
IUnknown
+IWICImagingFactory2
KEY_INFORMATION_CLASS
KEY_NAME_INFORMATION
KF_*
@@ -262,6 +288,7 @@ OLEIVERB_*
OpenClipboard
PARAMDATA
PeekMessage
+PixelFormat*
PlaySound
POINTS
PolyBezier
@@ -281,8 +308,8 @@ RegQueryInfoKey
RegQueryValueEx
ReleaseCapture
ReleaseDC
-RestoreDC
RemoveClipboardFormatListener
+RestoreDC
ROLE_SYSTEM_*
RoundRect
S_FALSE
@@ -362,7 +389,4 @@ VIRTUAL_KEY
WIN32_ERROR
WINDOWPOS
WM_*
-XFORMCOORDS
-IWICImagingFactory2
-CLSID_WICImagingFactory2
-GUID_WICPixelFormat32bppPBGRA
\ No newline at end of file
+XFORMCOORDS
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTarget.cs b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTarget.cs
index e23c15f..59cce7b 100644
--- a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTarget.cs
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTarget.cs
@@ -1,7 +1,11 @@
// 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.Support;
+using Windows.Win32.Graphics.Direct2D.Common;
+using Windows.Win32.Graphics.Dxgi.Common;
+using Windows.Win32.Graphics.Imaging;
namespace Windows.Win32.Graphics.Direct2D;
@@ -13,5 +17,77 @@ public RenderTarget(ID2D1RenderTarget* renderTarget) : base((ID2D1Resource*)rend
{
}
+ ///
+ public Bitmap CreateBitmapFromWicBitmap(
+ TBitmapSource wicBitmap)
+ where TBitmapSource : IPointer
+ {
+ ID2D1Bitmap* d2dBitmap;
+ Pointer->CreateBitmapFromWicBitmap(
+ wicBitmap.Pointer,
+ bitmapProperties: (D2D1_BITMAP_PROPERTIES*)null,
+ &d2dBitmap).ThrowOnFailure();
+
+ Bitmap bitmap = new(d2dBitmap);
+ GC.KeepAlive(this);
+ GC.KeepAlive(wicBitmap);
+ return bitmap;
+ }
+
+ public Bitmap CreateBitmapFromGdiPlusBitmap(GdiPlus.Bitmap bitmap)
+ {
+ GdiPlus.PixelFormat pixelFormat = bitmap.PixelFormat;
+ RectangleF bounds = bitmap.Bounds;
+
+ const int BytesPerPixel = 4;
+
+ // We could let GDI+ do the buffer allocation, but for illustrative purposes I've done it here.
+ // Note that GDI+ always copies the data, even if it internally is in the desired format.
+ using BufferScope buffer = new((int)bounds.Width * (int)bounds.Height * BytesPerPixel);
+
+ fixed (byte* b = buffer)
+ {
+ GdiPlus.BitmapData bitmapData = new()
+ {
+ Width = (uint)bounds.Width,
+ Height = (uint)bounds.Height,
+ Stride = (int)bounds.Width * BytesPerPixel,
+ PixelFormat = (int)GdiPlus.PixelFormat.Format32bppArgb,
+ Scan0 = b
+ };
+
+ bitmap.LockBits(
+ new((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height),
+ GdiPlus.ImageLockMode.ImageLockModeUserInputBuf | GdiPlus.ImageLockMode.ImageLockModeRead,
+ GdiPlus.PixelFormat.Format32bppArgb,
+ ref bitmapData);
+
+ D2D1_BITMAP_PROPERTIES bitmapProperties = new()
+ {
+ pixelFormat = new D2D1_PIXEL_FORMAT
+ {
+ format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
+ alphaMode = D2D1_ALPHA_MODE.D2D1_ALPHA_MODE_IGNORE
+ },
+ dpiX = 96,
+ dpiY = 96
+ };
+
+ ID2D1Bitmap* newBitmap;
+ HRESULT result = Pointer->CreateBitmap(
+ new((uint)bounds.Width, (uint)bounds.Height),
+ b,
+ (uint)bitmapData.Stride,
+ &bitmapProperties,
+ &newBitmap);
+
+ bitmap.UnlockBits(ref bitmapData);
+ result.ThrowOnFailure();
+
+ GC.KeepAlive(this);
+ return new Bitmap(newBitmap);
+ }
+ }
+
public static implicit operator ID2D1RenderTarget*(RenderTarget renderTarget) => renderTarget.Pointer;
}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
index efc2aef..417313e 100644
--- a/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
+++ b/src/thirtytwo/Win32/Graphics/Direct2D/RenderTargetExtensions.cs
@@ -117,25 +117,6 @@ public static void DrawTextLayout(
GC.KeepAlive(defaultFillBrush);
}
- ///
- public static Bitmap CreateBitmapFromWicBitmap(
- this TRenderTarget target,
- TBitmapSource wicBitmap)
- where TRenderTarget : IPointer
- where TBitmapSource : IPointer
- {
- 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(
this TRenderTarget target,
TBitmap bitmap,
diff --git a/src/thirtytwo/Win32/Graphics/GdiPlus/Bitmap.cs b/src/thirtytwo/Win32/Graphics/GdiPlus/Bitmap.cs
new file mode 100644
index 0000000..9a34e57
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/GdiPlus/Bitmap.cs
@@ -0,0 +1,61 @@
+// 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.Runtime.CompilerServices;
+using Windows.Support;
+
+namespace Windows.Win32.Graphics.GdiPlus;
+
+public unsafe class Bitmap : Image, IPointer
+{
+ public unsafe new GpBitmap* Pointer => (GpBitmap*)base.Pointer;
+
+ public Bitmap(GpBitmap* bitmap) : base((GpImage*)bitmap) { }
+ public Bitmap(string filename) : this(Create(filename)) { }
+
+ private static GpBitmap* Create(string filename)
+ {
+ ArgumentNullException.ThrowIfNull(filename);
+ GdiPlus.Init();
+
+ fixed (char* fn = filename)
+ {
+ GpBitmap* bitmap;
+ Interop.GdipCreateBitmapFromFile(fn, &bitmap).ThrowIfFailed();
+ return bitmap;
+ }
+ }
+
+ ///
+ /// Locks a rectangular portion of this bitmap and provides a temporary buffer that you can use to read or write
+ /// pixel data in a specified format. Any pixel data that you write to the buffer is copied to the
+ /// object when you call .
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, ref BitmapData data)
+ {
+ // LockBits always creates a temporary copy of the data.
+ Interop.GdipBitmapLockBits(
+ Pointer,
+ (Rect*)&rect,
+ (uint)flags,
+ (int)format,
+ (BitmapData*)Unsafe.AsPointer(ref data)).ThrowIfFailed();
+
+ GC.KeepAlive(this);
+ }
+
+ public void UnlockBits(ref BitmapData data)
+ {
+ Interop.GdipBitmapUnlockBits(Pointer, (BitmapData*)Unsafe.AsPointer(ref data)).ThrowIfFailed();
+ GC.KeepAlive(this);
+ }
+
+ public static implicit operator GpBitmap*(Bitmap bitmap) => bitmap.Pointer;
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/GdiPlus/Image.cs b/src/thirtytwo/Win32/Graphics/GdiPlus/Image.cs
new file mode 100644
index 0000000..86e6372
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/GdiPlus/Image.cs
@@ -0,0 +1,78 @@
+// 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.Support;
+
+namespace Windows.Win32.Graphics.GdiPlus;
+
+public unsafe class Image : DisposableBase.Finalizable, IPointer
+{
+ private GpImage* _pointer;
+
+ public GpImage* Pointer => _pointer;
+
+ public Image(GpImage* pointer) => _pointer = pointer;
+
+ protected override void Dispose(bool disposing)
+ {
+ Status status = Interop.GdipDisposeImage(_pointer);
+ if (disposing)
+ {
+ status.ThrowIfFailed();
+ }
+
+ _pointer = null;
+ }
+
+ public PixelFormat PixelFormat
+ {
+ get
+ {
+ PixelFormat format;
+ Interop.GdipGetImagePixelFormat(Pointer, (int*)&format).ThrowIfFailed();
+ GC.KeepAlive(this);
+ return format;
+ }
+ }
+
+ public Guid RawFormat
+ {
+ get
+ {
+ Guid format;
+ Interop.GdipGetImageRawFormat(Pointer, &format).ThrowIfFailed();
+ GC.KeepAlive(this);
+ return format;
+ }
+ }
+
+ public ImageFlags Flags
+ {
+ get
+ {
+ ImageFlags flags;
+ Interop.GdipGetImageFlags(Pointer, (uint*)&flags).ThrowIfFailed();
+ GC.KeepAlive(this);
+ return flags;
+ }
+ }
+
+ ///
+ /// The bounds of the image in pixels.
+ ///
+ public RectangleF Bounds
+ {
+ get
+ {
+ RectangleF bounds;
+ Unit unit;
+ Interop.GdipGetImageBounds(Pointer, (RectF*)&bounds, &unit).ThrowIfFailed();
+
+ // GdipGetImageBounds is hardcoded to return Unit.Pixel
+ Debug.Assert(unit == Unit.UnitPixel);
+ GC.KeepAlive(this);
+ return bounds;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/GdiPlus/PixelFormat.cs b/src/thirtytwo/Win32/Graphics/GdiPlus/PixelFormat.cs
new file mode 100644
index 0000000..162f0a2
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/GdiPlus/PixelFormat.cs
@@ -0,0 +1,136 @@
+// Copyright (c) Jeremy W. Kuhne. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// Copied from Windows Forms, original license:
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Windows.Win32.Graphics.GdiPlus;
+
+///
+/// Specifies the format of the color data for each pixel in the image.
+///
+public enum PixelFormat
+{
+ ///
+ /// Specifies that pixel data contains color indexed values which means they are an index to colors in the
+ /// system color table, as opposed to individual color values.
+ ///
+ Indexed = (int)Interop.PixelFormatIndexed,
+
+ ///
+ /// Specifies that pixel data contains GDI colors.
+ ///
+ Gdi = (int)Interop.PixelFormatGDI,
+
+ ///
+ /// Specifies that pixel data contains alpha values that are not pre-multiplied.
+ ///
+ Alpha = (int)Interop.PixelFormatAlpha,
+
+ ///
+ /// Specifies that pixel format contains pre-multiplied alpha values.
+ ///
+ PAlpha = (int)Interop.PixelFormatPAlpha,
+
+ ///
+ /// Specifies that pixel format contains extended color values of 16 bits per channel.
+ ///
+ Extended = (int)Interop.PixelFormatExtended,
+
+ Canonical = (int)Interop.PixelFormatCanonical,
+
+ ///
+ /// Specifies that pixel format is undefined.
+ ///
+ Undefined = (int)Interop.PixelFormatUndefined,
+
+ ///
+ /// Specifies that pixel format doesn't matter.
+ ///
+ DontCare = (int)Interop.PixelFormatDontCare,
+
+ ///
+ /// Specifies that pixel format is 1 bit per pixel indexed color. The color table therefore has two colors in it.
+ ///
+ Format1bppIndexed = 1 | (1 << 8) | Indexed | Gdi,
+
+ ///
+ /// Specifies that pixel format is 4 bits per pixel indexed color. The color table therefore has 16 colors in it.
+ ///
+ Format4bppIndexed = 2 | (4 << 8) | Indexed | Gdi,
+
+ ///
+ /// Specifies that pixel format is 8 bits per pixel indexed color. The color table therefore has 256 colors in it.
+ ///
+ Format8bppIndexed = 3 | (8 << 8) | Indexed | Gdi,
+
+ ///
+ /// Specifies that pixel format is 16 bits per pixel. The color information specifies 65536 shades of gray.
+ ///
+ Format16bppGrayScale = 4 | (16 << 8) | Extended,
+
+ ///
+ /// Specifies that pixel format is 16 bits per pixel. The color information specifies 32768 shades of color of
+ /// which 5 bits are red, 5 bits are green and 5 bits are blue.
+ ///
+ Format16bppRgb555 = 5 | (16 << 8) | Gdi,
+
+ Format16bppRgb565 = 6 | (16 << 8) | Gdi,
+
+ ///
+ /// Specifies that pixel format is 16 bits per pixel. The color information specifies 32768 shades of color of
+ /// which 5 bits are red, 5 bits are green, 5 bits are blue and 1 bit is alpha.
+ ///
+ Format16bppArgb1555 = 7 | (16 << 8) | Alpha | Gdi,
+
+ ///
+ /// Specifies that pixel format is 24 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 8 bits are red, 8 bits are green and 8 bits are blue.
+ ///
+ Format24bppRgb = 8 | (24 << 8) | Gdi,
+
+ ///
+ /// Specifies that pixel format is 24 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 8 bits are red, 8 bits are green and 8 bits are blue.
+ ///
+ Format32bppRgb = 9 | (32 << 8) | Gdi,
+
+ ///
+ /// Specifies that pixel format is 32 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are alpha bits.
+ ///
+ Format32bppArgb = 10 | (32 << 8) | Alpha | Gdi | Canonical,
+
+ ///
+ /// Specifies that pixel format is 32 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are pre-multiplied alpha bits.
+ ///
+ Format32bppPArgb = 11 | (32 << 8) | Alpha | PAlpha | Gdi,
+
+ ///
+ /// Specifies that pixel format is 48 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are alpha bits.
+ ///
+ Format48bppRgb = 12 | (48 << 8) | Extended,
+
+ ///
+ /// Specifies pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color of
+ /// which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are alpha bits.
+ ///
+ Format64bppArgb = 13 | (64 << 8) | Alpha | Canonical | Extended,
+
+ ///
+ /// Specifies that pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are pre-multiplied
+ /// alpha bits.
+ ///
+ Format64bppPArgb = 14 | (64 << 8) | Alpha | PAlpha | Extended,
+
+ ///
+ /// Specifies that pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color
+ /// of which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are alpha bits.
+ ///
+ Max = 15,
+}
\ No newline at end of file
diff --git a/src/thirtytwo/Win32/Graphics/Imaging/Bitmap.cs b/src/thirtytwo/Win32/Graphics/Imaging/Bitmap.cs
new file mode 100644
index 0000000..a03e778
--- /dev/null
+++ b/src/thirtytwo/Win32/Graphics/Imaging/Bitmap.cs
@@ -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.Imaging;
+
+public unsafe class Bitmap : BitmapSource, IPointer
+{
+ public unsafe new IWICBitmap* Pointer => (IWICBitmap*)base.Pointer;
+
+ public Bitmap(IWICBitmap* bitmap) : base((IWICBitmapSource*)bitmap)
+ {
+ }
+
+ public static implicit operator IWICBitmap*(Bitmap bitmap) => bitmap.Pointer;
+}
\ No newline at end of file