using System.Drawing; using System.Drawing.Drawing2D; using static Vanara.PInvoke.Gdi32; using static Vanara.PInvoke.User32; namespace Vanara.PInvoke; /// Extension methods to convert GdiObj handle variants to their .NET equivalents. public static class GdiObjExtensions2 { /// /// Draws on a device context ( ) via a DIB section. This is useful when you need to draw on a transparent background. /// /// The device context. /// The bounds of the device context to paint. /// The draw method. public static void DrawViaDIB(this IDeviceContext hdc, in RECT bounds, Action drawMethod) { IntPtr h = hdc.GetHdc(); try { GdiObjExtensions.DrawViaDIB(h, bounds, drawMethod); } finally { hdc.ReleaseHdc(); } } /// Gets the copy of the device handle in an IDeviceContext supporting GDI+. /// The instance. /// A instance that contains a copy of the context. public static SafeHDC GetCompatibleSafeHDC(this IDeviceContext? dc) { if (dc is null) { return new(default, false); } SafeHDC hdc = CreateCompatibleDC(dc.GetHdc()); dc.ReleaseHdc(); return hdc; } /// Creates a from an preserving transparency, if possible. /// The SafeHBITMAP value. /// The Bitmap instance. If is a NULL handle, is returned. public static Bitmap ToBitmap(this SafeHBITMAP hbmp) => ToBitmap((HBITMAP)hbmp); /// Creates a from an preserving transparency, if possible. /// The HBITMAP value. /// The Bitmap instance. If is a NULL handle, is returned. public static Bitmap ToBitmap(this in HBITMAP hbmp) => Image.FromHbitmap((IntPtr)hbmp); /// Creates a managed from a HICON instance. /// A managed bitmap instance. public static Bitmap? ToBitmap(this in HICON hIcon) => hIcon.IsNull ? null : (Bitmap)Bitmap.FromHicon((IntPtr)hIcon).Clone(); /// Creates a managed from a SafeHICON instance. /// A managed bitmap instance. public static Bitmap? ToBitmap(this SafeHICON hIcon) => ToBitmap((HICON)hIcon); /// Creates a managed from this HBRUSH instance. /// The HBRUSH value. /// A managed brush instance. public static Brush? ToBrush(this in HBRUSH hbr) => hbr.IsNull ? null : new NativeBrush(hbr); /// Creates a managed from this HBRUSH instance. /// The HBRUSH value. /// A managed brush instance. public static Brush? ToBrush(this SafeHBRUSH hbr) => ((HBRUSH)hbr).ToBrush(); /// Creates a from an . /// The HFONT value. /// The Font instance. public static Font? ToFont(this in HFONT hf) => hf.IsNull ? null : Font.FromHfont((IntPtr)hf); /// Creates a from an . /// The HFONT value. /// The Font instance. public static Font? ToFont(this SafeHFONT hf) => ((HFONT)hf).ToFont(); /// Creates a managed from an HICON instance. /// A managed icon instance. public static Icon? ToIcon(this in HICON hIcon) => hIcon.IsNull ? null : (Icon)Icon.FromHandle((IntPtr)hIcon).Clone(); /// Creates a managed from a SafeHICON instance. /// A managed icon instance. public static Icon? ToIcon(this SafeHICON hIcon) => ToIcon((HICON)hIcon); /// Creates a from an . /// The HPEN value. /// The Pen instance. public static Pen? ToPen(this in HPEN hpen) { using ISafeMemoryHandle ptr = GetObject(hpen); EXTLOGPEN lpen = ptr.ToStructure(); Pen? pen = null; switch (lpen.elpBrushStyle) { case BrushStyle.BS_DIBPATTERN: case BrushStyle.BS_DIBPATTERNPT: DIBColorMode lw = (DIBColorMode)(uint)lpen.elpColor; SafeHBRUSH hb = CreateDIBPatternBrushPt(lpen.elpHatch, lw); pen = new Pen(((HBRUSH)hb).ToBrush()!); break; case BrushStyle.BS_HATCHED: HatchBrush hbr = new((System.Drawing.Drawing2D.HatchStyle)lpen.elpHatch.ToInt32(), lpen.elpColor); pen = new Pen(hbr); break; case BrushStyle.BS_PATTERN: TextureBrush pbr = new(Image.FromHbitmap(lpen.elpHatch)); pen = new Pen(pbr); break; case BrushStyle.BS_HOLLOW: case BrushStyle.BS_SOLID: default: pen = new Pen(lpen.elpColor) { DashStyle = (DashStyle)lpen.Style }; if (pen.DashStyle == DashStyle.Custom && lpen.elpNumEntries > 0) { uint[] styleArray = lpen.elpStyleEntry.ToArray((int)lpen.elpNumEntries) ?? new uint[0]; pen.DashPattern = Array.ConvertAll(styleArray, i => (float)i); } break; } if (lpen.Type == Gdi32.PenType.PS_GEOMETRIC) { pen.LineJoin = lpen.Join == PenJoin.PS_JOIN_MITER ? LineJoin.Miter : (lpen.Join == PenJoin.PS_JOIN_BEVEL ? LineJoin.Bevel : LineJoin.Round); pen.EndCap = pen.StartCap = lpen.EndCap == PenEndCap.PS_ENDCAP_FLAT ? LineCap.Flat : (lpen.EndCap == PenEndCap.PS_ENDCAP_SQUARE ? LineCap.Square : LineCap.Round); pen.Width = LogicalWidthToDeviceWidth((int)lpen.elpWidth); } else { pen.Width = lpen.elpWidth; } return pen; } /// Creates a from an . /// The HPEN value. /// The Pen instance. public static Pen? ToPen(this SafeHPEN hpen) => ((HPEN)hpen).ToPen(); /// Creates a from an . /// The HRGN value. /// The Region instance. public static Region? ToRegion(this in HRGN hrgn) => hrgn.IsNull ? null : Region.FromHrgn((IntPtr)hrgn); /// Creates a from an . /// The HRGN value. /// The Region instance. public static Region? ToRegion(this SafeHRGN hrgn) => ((HRGN)hrgn).ToRegion(); // TODO: Fix code below to process different bpp bitmaps w/o flipping //{ // const Imaging.PixelFormat fmt = Imaging.PixelFormat.Format32bppArgb; // // If hbmp is NULL handle, return null if (hbmp.IsNull) return null; // // Get detail and bail if not 32bit, empty or an old style BMP var (bpp, width, height, scanBytes, bits, isdib) = GetInfo(hbmp); // if (bpp != Image.GetPixelFormatSize(fmt) || height == 0 || !isdib) return Image.FromHbitmap((IntPtr)hbmp); // // Create bitmap from detail and flip if upside-down var bmp = new Bitmap(width, height, scanBytes, fmt, bits); if (height < 0) // bmp.RotateFlip(RotateFlipType.Rotate180FlipNone); return bmp; // static (ushort bpp, int width, int height, int scanBytes, IntPtr bits, bool isdib) GetInfo(in HBITMAP hbmp) { var dibSz = // Marshal.SizeOf(typeof(DIBSECTION)); using var mem = GetObject(hbmp, dibSz); if (mem.Size == dibSz) { var dib = // mem.ToStructure(); return (dib.dsBm.bmBitsPixel, dib.dsBmih.biWidth, dib.dsBmih.biHeight, dib.dsBm.bmWidthBytes, // dib.dsBm.bmBits, true); } else { var bmp = mem.ToStructure(); return (bmp.bmBitsPixel, bmp.bmWidth, bmp.bmHeight, // bmp.bmWidthBytes, bmp.bmBits, false); } } //} #if WPF && !NETSTANDARD2_0 /// /// Creates a from an preserving transparency, if possible. /// /// The HBITMAP value. /// The BitmapSource instance. If is a NULL handle, is returned. public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this in HBITMAP hbmp) { // If hbmp is NULL handle, return null if (hbmp.IsNull) return null; try { return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap((IntPtr)hbmp, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); } catch (System.ComponentModel.Win32Exception) { return null; } } /// /// Creates a from an preserving transparency, if possible. /// /// The SafeHBITMAP value. /// The BitmapSource instance. If is a NULL handle, is returned. public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this SafeHBITMAP hbmp) => ((HBITMAP)hbmp).ToBitmapSource(); #endif private class NativeBrush : Brush { public NativeBrush(HBRUSH hBrush) { LOGBRUSH lb = GetObject(hBrush); using SafeHBRUSH b2 = CreateBrushIndirect(lb); SetNativeBrush(b2.DangerousGetHandle()); b2.SetHandleAsInvalid(); } public override object Clone() => this; } } /// A self-releasing pattern for IDeviceContext.GetHdc and ReleaseHdc. /// public class SafeTempHDC : IDisposable, IGraphicsObjectHandle { private readonly IDeviceContext? dc; private readonly IntPtr hdc; /// Initializes a new instance of the class with an . /// The instance. public SafeTempHDC(IDeviceContext? dc) { this.dc = dc; hdc = dc?.GetHdc() ?? default; } /// Gets a value indicating whether this instance has a NULL handle. /// if this has a NULL handle; otherwise, . public bool IsNull => hdc == default; /// Performs an implicit conversion from to . /// The instance. /// The result of the conversion. public static implicit operator HDC(SafeTempHDC o) => o.hdc; /// public IntPtr DangerousGetHandle() => hdc; /// Releases claimed HDC. public void Dispose() => dc?.ReleaseHdc(); }