using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.InteropServices; using static Vanara.PInvoke.Gdi32; namespace Vanara.PInvoke { /// Extension methods to convert GdiObj handle variants to their .NET equivalents. public static class GdiObjExtensions { /// Converts the generic GDI object handle to a specific handle. /// The handle type to which to convert. /// The generic GDI object handle. /// The converted handle of type . /// The conversion type specified is not valid for the supplied GDI object. public static T ConvertTo(this IGraphicsObjectHandle hObj) where T : IGraphicsObjectHandle { var ot = GetObjectType(hObj.DangerousGetHandle()); if (ot == 0) Win32Error.ThrowLastError(); if (!CorrespondingTypeAttribute.CanGet(ot, typeof(T))) throw new ArgumentException($"The conversion type specified is not valid for the supplied GDI object."); return (T)(object)hObj.DangerousGetHandle(); } /// 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 dc, in Rectangle bounds, Action drawMethod) { using var sdc = new SafeHDC(dc); DrawViaDIB(sdc, bounds, drawMethod); } /// 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 SafeHDC hdc, in Rectangle bounds, Action drawMethod) { // Create a memory DC so we can work off screen using var memoryHdc = hdc.GetCompatibleDCHandle(); // Create a device-independent bitmap and select it into our DC var info = new BITMAPINFO(bounds.Width, -bounds.Height); using (memoryHdc.SelectObject(CreateDIBSection(hdc, ref info, 0, out var pBits, IntPtr.Zero, 0))) { // Call method drawMethod(memoryHdc, bounds); // Copy to foreground BitBlt(hdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, RasterOperationMode.SRCCOPY); } } /// Determines whether the bitmap is a bottom-up DIB. /// The handle of the bitmap to assess. /// if the specified bitmap is a bottom-up DIB; otherwise, . public static bool IsBottomUpDIB(this in HBITMAP hbmp) { var dibSz = Marshal.SizeOf(typeof(DIBSECTION)); using var mem = GetObject(hbmp, dibSz); return mem.Size == dibSz && mem.ToStructure().dsBmih.biHeight > 0; } /// Determines whether the bitmap is a bottom-up DIB. /// The handle of the bitmap to assess. /// if the specified bitmap is a bottom-up DIB; otherwise, . public static bool IsDIB(this in HBITMAP hbmp) { var dibSz = Marshal.SizeOf(typeof(DIBSECTION)); using var mem = GetObject(hbmp, dibSz); return mem.Size == dibSz; } /// 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) { const System.Drawing.Imaging.PixelFormat fmt = System.Drawing.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 !NET20 && !NETSTANDARD2_0 && !NETCOREAPP2_0 && !NETCOREAPP2_1 /// 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 /// 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 from an . /// The HPEN value. /// The Pen instance. public static Pen ToPen(this in HPEN hpen) { using var ptr = GetObject(hpen); var lpen = ptr.ToStructure(); Pen pen = null; switch (lpen.elpBrushStyle) { case BrushStyle.BS_DIBPATTERN: case BrushStyle.BS_DIBPATTERNPT: var lw = (DIBColorMode)(uint)lpen.elpColor; var hb = CreateDIBPatternBrushPt(lpen.elpHatch, lw); pen = new Pen(((HBRUSH)hb).ToBrush()); break; case BrushStyle.BS_HATCHED: var hbr = new HatchBrush((System.Drawing.Drawing2D.HatchStyle)lpen.elpHatch.ToInt32(), lpen.elpColor); pen = new Pen(hbr); break; case BrushStyle.BS_PATTERN: var pbr = new TextureBrush(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) { var styleArray = lpen.elpStyleEntry.ToArray((int)lpen.elpNumEntries); 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(); private class NativeBrush : Brush { public NativeBrush(HBRUSH hBrush) { var lb = GetObject(hBrush); using var b2 = CreateBrushIndirect(lb); SetNativeBrush(b2.DangerousGetHandle()); b2.SetHandleAsInvalid(); } public override object Clone() => this; } } }