From 88bfa8ee42d59d2ef1391d97948ac7355a4100dd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 23 Jun 2022 16:08:00 -0700 Subject: [PATCH] Mark EnumerateMetafileProc's callbackData parameter as nullable --- .../ref/System.Drawing.Common.cs | 2 +- .../src/System/Drawing/Graphics.cs | 6908 ++++++++--------- 2 files changed, 3455 insertions(+), 3455 deletions(-) diff --git a/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs b/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs index 651968b679408..7dc6dce240296 100644 --- a/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs +++ b/src/libraries/System.Drawing.Common/ref/System.Drawing.Common.cs @@ -647,7 +647,7 @@ public void TranslateClip(float dx, float dy) { } public void TranslateTransform(float dx, float dy) { } public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { } public delegate bool DrawImageAbort(System.IntPtr callbackdata); - public delegate bool EnumerateMetafileProc(System.Drawing.Imaging.EmfPlusRecordType recordType, int flags, int dataSize, System.IntPtr data, System.Drawing.Imaging.PlayRecordCallback callbackData); + public delegate bool EnumerateMetafileProc(System.Drawing.Imaging.EmfPlusRecordType recordType, int flags, int dataSize, System.IntPtr data, System.Drawing.Imaging.PlayRecordCallback? callbackData); } public enum GraphicsUnit { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.cs index 06b6958ee3ec8..497c61509b6e0 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.cs @@ -1,3454 +1,3454 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel; -using System.Diagnostics; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.Drawing.Internal; -using System.Drawing.Text; -using System.Globalization; -using System.Numerics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -#if NET7_0_OR_GREATER -using System.Runtime.InteropServices.Marshalling; -#endif -using Gdip = System.Drawing.SafeNativeMethods.Gdip; - -namespace System.Drawing -{ - /// - /// Encapsulates a GDI+ drawing surface. - /// - public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext - { -#if FINALIZATION_WATCH - static readonly TraceSwitch GraphicsFinalization = new TraceSwitch("GraphicsFinalization", "Tracks the creation and destruction of finalization"); - internal static string GetAllocationStack() { - if (GraphicsFinalization.TraceVerbose) { - return Environment.StackTrace; - } - else { - return "Enabled 'GraphicsFinalization' switch to see stack of allocation"; - } - } - private string allocationSite = Graphics.GetAllocationStack(); -#endif - - /// - /// The context state previous to the current Graphics context (the head of the stack). - /// We don't keep a GraphicsContext for the current context since it is available at any time from GDI+ and - /// we don't want to keep track of changes in it. - /// - private GraphicsContext? _previousContext; - - private static readonly object s_syncObject = new object(); - - // Object reference used for printing; it could point to a PrintPreviewGraphics to obtain the VisibleClipBounds, or - // a DeviceContext holding a printer DC. - private object? _printingHelper; - - // GDI+'s preferred HPALETTE. - private static IntPtr s_halftonePalette; - - // pointer back to the Image backing a specific graphic object - private Image? _backingImage; - - /// - /// Handle to native DC - obtained from the GDI+ graphics object. We need to cache it to implement - /// IDeviceContext interface. - /// - private IntPtr _nativeHdc; - - public delegate bool DrawImageAbort(IntPtr callbackdata); - -#if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(DrawImageAbort), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources)] - internal unsafe struct DrawImageAbortMarshaller - { - private delegate Interop.BOOL DrawImageAbortNative(IntPtr callbackdata); - private DrawImageAbortNative? _managed; - private delegate* unmanaged _nativeFunction; - public DrawImageAbortMarshaller(DrawImageAbort? managed) - { - _managed = managed is null ? null : data => managed(data) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; - _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); - } - - public delegate* unmanaged ToNativeValue() - { - return _nativeFunction; - } - - public void FreeNative() - { - GC.KeepAlive(_managed); - } - } -#endif - - /// - /// Callback for EnumerateMetafile methods. - /// This method can then call Metafile.PlayRecord to play the record that was just enumerated. - /// - /// if >= MinRecordType, it's an EMF+ record - /// always 0 for EMF records - /// size of the data, or 0 if no data - /// pointer to the data, or NULL if no data (UINT32 aligned) - /// pointer to callbackData, if any - /// False to abort enumerating, true to continue. - public delegate bool EnumerateMetafileProc( - EmfPlusRecordType recordType, - int flags, - int dataSize, - IntPtr data, - PlayRecordCallback callbackData); - -#if NET7_0_OR_GREATER - [CustomTypeMarshaller(typeof(EnumerateMetafileProc), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources)] - internal unsafe struct EnumerateMetafileProcMarshaller - { - private delegate Interop.BOOL EnumerateMetafileProcNative( - EmfPlusRecordType recordType, - int flags, - int dataSize, - IntPtr data, - IntPtr callbackData); - private EnumerateMetafileProcNative? _managed; - private delegate* unmanaged _nativeFunction; - public EnumerateMetafileProcMarshaller(EnumerateMetafileProc? managed) - { - _managed = managed is null ? null : (recordType, flags, dataSize, data, callbackData) => - managed(recordType, flags, dataSize, data, callbackData == IntPtr.Zero ? null! : Marshal.GetDelegateForFunctionPointer(callbackData)) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; - _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); - } - - public delegate* unmanaged ToNativeValue() - { - return _nativeFunction; - } - - public void FreeNative() - { - GC.KeepAlive(_managed); - } - } -#endif - - /// - /// Constructor to initialize this object from a native GDI+ Graphics pointer. - /// - private Graphics(IntPtr gdipNativeGraphics) - { - if (gdipNativeGraphics == IntPtr.Zero) - throw new ArgumentNullException(nameof(gdipNativeGraphics)); - - NativeGraphics = gdipNativeGraphics; - } - - /// - /// Creates a new instance of the class from the specified handle to a device context. - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Graphics FromHdc(IntPtr hdc) - { - if (hdc == IntPtr.Zero) - throw new ArgumentNullException(nameof(hdc)); - - return FromHdcInternal(hdc); - } - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Graphics FromHdcInternal(IntPtr hdc) - { - Gdip.CheckStatus(Gdip.GdipCreateFromHDC(hdc, out IntPtr nativeGraphics)); - return new Graphics(nativeGraphics); - } - - /// - /// Creates a new instance of the Graphics class from the specified handle to a device context and handle to a device. - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Graphics FromHdc(IntPtr hdc, IntPtr hdevice) - { - Gdip.CheckStatus(Gdip.GdipCreateFromHDC2(hdc, hdevice, out IntPtr nativeGraphics)); - return new Graphics(nativeGraphics); - } - - /// - /// Creates a new instance of the class from a window handle. - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Graphics FromHwnd(IntPtr hwnd) => FromHwndInternal(hwnd); - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public static Graphics FromHwndInternal(IntPtr hwnd) - { - Gdip.CheckStatus(Gdip.GdipCreateFromHWND(hwnd, out IntPtr nativeGraphics)); - return new Graphics(nativeGraphics); - } - - /// - /// Creates an instance of the class from an existing . - /// - public static Graphics FromImage(Image image) - { - ArgumentNullException.ThrowIfNull(image); - - if ((image.PixelFormat & PixelFormat.Indexed) != 0) - throw new ArgumentException(SR.GdiplusCannotCreateGraphicsFromIndexedPixelFormat, nameof(image)); - - Gdip.CheckStatus(Gdip.GdipGetImageGraphicsContext( - new HandleRef(image, image.nativeImage), - out IntPtr nativeGraphics)); - - return new Graphics(nativeGraphics) { _backingImage = image }; - } - - [EditorBrowsable(EditorBrowsableState.Never)] - public void ReleaseHdcInternal(IntPtr hdc) - { - Gdip.CheckStatus(!Gdip.Initialized ? Gdip.Ok : - Gdip.GdipReleaseDC(new HandleRef(this, NativeGraphics), hdc)); - _nativeHdc = IntPtr.Zero; - } - - /// - /// Deletes this , and frees the memory allocated for it. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { -#if DEBUG && FINALIZATION_WATCH - if (!disposing && _nativeGraphics != IntPtr.Zero) - { - Debug.WriteLine("System.Drawing.Graphics: ***************************************************"); - Debug.WriteLine("System.Drawing.Graphics: Object Disposed through finalization:\n" + allocationSite); - } -#endif - while (_previousContext != null) - { - // Dispose entire stack. - GraphicsContext? context = _previousContext.Previous; - _previousContext.Dispose(); - _previousContext = context; - } - - if (NativeGraphics != IntPtr.Zero) - { - try - { - if (_nativeHdc != IntPtr.Zero) // avoid a handle leak. - { - ReleaseHdc(); - } - - if (PrintingHelper is DeviceContext printerDC) - { - printerDC.Dispose(); - _printingHelper = null; - } - -#if DEBUG - int status = !Gdip.Initialized ? Gdip.Ok : -#endif - Gdip.GdipDeleteGraphics(new HandleRef(this, NativeGraphics)); - -#if DEBUG - Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); -#endif - } - catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) - { - } - finally - { - NativeGraphics = IntPtr.Zero; - } - } - } - - ~Graphics() => Dispose(false); - - /// - /// Handle to native GDI+ graphics object. This object is created on demand. - /// - internal IntPtr NativeGraphics { get; private set; } - - public Region Clip - { - get - { - var region = new Region(); - int status = Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(region, region.NativeRegion)); - Gdip.CheckStatus(status); - - return region; - } - set => SetClip(value, CombineMode.Replace); - } - - public RectangleF ClipBounds - { - get - { - Gdip.CheckStatus(Gdip.GdipGetClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); - return rect; - } - } - - /// - /// Gets or sets the associated with this . - /// - public CompositingMode CompositingMode - { - get - { - Gdip.CheckStatus(Gdip.GdipGetCompositingMode(new HandleRef(this, NativeGraphics), out CompositingMode mode)); - return mode; - } - set - { - if (value < CompositingMode.SourceOver || value > CompositingMode.SourceCopy) - throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingMode)); - - Gdip.CheckStatus(Gdip.GdipSetCompositingMode(new HandleRef(this, NativeGraphics), value)); - } - } - - public CompositingQuality CompositingQuality - { - get - { - Gdip.CheckStatus(Gdip.GdipGetCompositingQuality(new HandleRef(this, NativeGraphics), out CompositingQuality cq)); - return cq; - } - set - { - if (value < CompositingQuality.Invalid || value > CompositingQuality.AssumeLinear) - throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingQuality)); - - Gdip.CheckStatus(Gdip.GdipSetCompositingQuality(new HandleRef(this, NativeGraphics), value)); - } - } - - public float DpiX - { - get - { - Gdip.CheckStatus(Gdip.GdipGetDpiX(new HandleRef(this, NativeGraphics), out float dpi)); - return dpi; - } - } - - public float DpiY - { - get - { - Gdip.CheckStatus(Gdip.GdipGetDpiY(new HandleRef(this, NativeGraphics), out float dpi)); - return dpi; - } - } - - /// - /// Gets or sets the interpolation mode associated with this Graphics. - /// - public InterpolationMode InterpolationMode - { - get - { - Gdip.CheckStatus(Gdip.GdipGetInterpolationMode(new HandleRef(this, NativeGraphics), out InterpolationMode mode)); - return mode; - } - set - { - if (value < InterpolationMode.Invalid || value > InterpolationMode.HighQualityBicubic) - throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(InterpolationMode)); - - // GDI+ interprets the value of InterpolationMode and sets a value accordingly. - // Libgdiplus does not, so do this manually here. - switch (value) - { - case InterpolationMode.Default: - case InterpolationMode.Low: - value = InterpolationMode.Bilinear; - break; - case InterpolationMode.High: - value = InterpolationMode.HighQualityBicubic; - break; - case InterpolationMode.Invalid: - throw new ArgumentException(SR.GdiplusInvalidParameter); - default: - break; - } - - Gdip.CheckStatus(Gdip.GdipSetInterpolationMode(new HandleRef(this, NativeGraphics), value)); - } - } - - public bool IsClipEmpty - { - get - { - Gdip.CheckStatus(Gdip.GdipIsClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); - return isEmpty; - } - } - - public bool IsVisibleClipEmpty - { - get - { - Gdip.CheckStatus(Gdip.GdipIsVisibleClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); - return isEmpty; - } - } - - public float PageScale - { - get - { - Gdip.CheckStatus(Gdip.GdipGetPageScale(new HandleRef(this, NativeGraphics), out float scale)); - return scale; - } - set - { - // Libgdiplus doesn't perform argument validation, so do this here for compatability. - if (value <= 0 || value > 1000000032) - throw new ArgumentException(SR.GdiplusInvalidParameter); - - Gdip.CheckStatus(Gdip.GdipSetPageScale(new HandleRef(this, NativeGraphics), value)); - } - } - - public GraphicsUnit PageUnit - { - get - { - Gdip.CheckStatus(Gdip.GdipGetPageUnit(new HandleRef(this, NativeGraphics), out GraphicsUnit unit)); - return unit; - } - set - { - if (value < GraphicsUnit.World || value > GraphicsUnit.Millimeter) - throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(GraphicsUnit)); - - // GDI+ doesn't allow GraphicsUnit.World as a valid value for PageUnit. - // Libgdiplus doesn't perform argument validation, so do this here. - if (value == GraphicsUnit.World) - throw new ArgumentException(SR.GdiplusInvalidParameter); - - Gdip.CheckStatus(Gdip.GdipSetPageUnit(new HandleRef(this, NativeGraphics), value)); - } - } - - public PixelOffsetMode PixelOffsetMode - { - get - { - Gdip.CheckStatus(Gdip.GdipGetPixelOffsetMode(new HandleRef(this, NativeGraphics), out PixelOffsetMode mode)); - return mode; - } - set - { - if (value < PixelOffsetMode.Invalid || value > PixelOffsetMode.Half) - throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PixelOffsetMode)); - - // GDI+ doesn't allow PixelOffsetMode.Invalid as a valid value for PixelOffsetMode. - // Libgdiplus doesn't perform argument validation, so do this here. - if (value == PixelOffsetMode.Invalid) - throw new ArgumentException(SR.GdiplusInvalidParameter); - - Gdip.CheckStatus(Gdip.GdipSetPixelOffsetMode(new HandleRef(this, NativeGraphics), value)); - } - } - - public Point RenderingOrigin - { - get - { - Gdip.CheckStatus(Gdip.GdipGetRenderingOrigin(new HandleRef(this, NativeGraphics), out int x, out int y)); - return new Point(x, y); - } - set - { - Gdip.CheckStatus(Gdip.GdipSetRenderingOrigin(new HandleRef(this, NativeGraphics), value.X, value.Y)); - } - } - - public SmoothingMode SmoothingMode - { - get - { - Gdip.CheckStatus(Gdip.GdipGetSmoothingMode(new HandleRef(this, NativeGraphics), out SmoothingMode mode)); - return mode; - } - set - { - if (value < SmoothingMode.Invalid || value > SmoothingMode.AntiAlias) - throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(SmoothingMode)); - - // GDI+ interprets the value of SmoothingMode and sets a value accordingly. - // Libgdiplus does not, so do this manually here. - switch (value) - { - case SmoothingMode.Default: - case SmoothingMode.HighSpeed: - value = SmoothingMode.None; - break; - case SmoothingMode.HighQuality: - value = SmoothingMode.AntiAlias; - break; - case SmoothingMode.Invalid: - throw new ArgumentException(SR.GdiplusInvalidParameter); - default: - break; - } - - Gdip.CheckStatus(Gdip.GdipSetSmoothingMode(new HandleRef(this, NativeGraphics), value)); - } - } - - public int TextContrast - { - get - { - Gdip.CheckStatus(Gdip.GdipGetTextContrast(new HandleRef(this, NativeGraphics), out int textContrast)); - return textContrast; - } - set - { - Gdip.CheckStatus(Gdip.GdipSetTextContrast(new HandleRef(this, NativeGraphics), value)); - } - } - - /// - /// Gets or sets the rendering mode for text associated with this . - /// - public TextRenderingHint TextRenderingHint - { - get - { - Gdip.CheckStatus(Gdip.GdipGetTextRenderingHint(new HandleRef(this, NativeGraphics), out TextRenderingHint hint)); - return hint; - } - set - { - if (value < TextRenderingHint.SystemDefault || value > TextRenderingHint.ClearTypeGridFit) - throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(TextRenderingHint)); - - Gdip.CheckStatus(Gdip.GdipSetTextRenderingHint(new HandleRef(this, NativeGraphics), value)); - } - } - - /// - /// Gets or sets the world transform for this . - /// - public Matrix Transform - { - get - { - var matrix = new Matrix(); - Gdip.CheckStatus(Gdip.GdipGetWorldTransform( - new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix))); - - return matrix; - } - set - { - Gdip.CheckStatus(Gdip.GdipSetWorldTransform( - new HandleRef(this, NativeGraphics), new HandleRef(value, value.NativeMatrix))); - } - } - - /// - /// Gets or sets the world transform elements for this . - /// - /// - /// This is a more performant alternative to that does not need disposal. - /// - public unsafe Matrix3x2 TransformElements - { - get - { - Gdip.CheckStatus(Gdip.GdipCreateMatrix(out IntPtr nativeMatrix)); - - try - { - Gdip.CheckStatus(Gdip.GdipGetWorldTransform( - new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); - - Matrix3x2 matrix = default; - Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(null, nativeMatrix), (float*)&matrix)); - return matrix; - } - finally - { - if (nativeMatrix != IntPtr.Zero) - { - Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); - } - } - } - set - { - IntPtr nativeMatrix = Matrix.CreateNativeHandle(value); - - try - { - Gdip.CheckStatus(Gdip.GdipSetWorldTransform( - new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); - } - finally - { - if (nativeMatrix != IntPtr.Zero) - { - Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); - } - } - } - } - - public IntPtr GetHdc() - { - IntPtr hdc; - Gdip.CheckStatus(Gdip.GdipGetDC(new HandleRef(this, NativeGraphics), out hdc)); - - _nativeHdc = hdc; // need to cache the hdc to be able to release with a call to IDeviceContext.ReleaseHdc(). - return _nativeHdc; - } - - [EditorBrowsable(EditorBrowsableState.Advanced)] - public void ReleaseHdc(IntPtr hdc) => ReleaseHdcInternal(hdc); - - public void ReleaseHdc() => ReleaseHdcInternal(_nativeHdc); - - /// - /// Forces immediate execution of all operations currently on the stack. - /// - public void Flush() => Flush(FlushIntention.Flush); - - /// - /// Forces execution of all operations currently on the stack. - /// - public void Flush(FlushIntention intention) - { - Gdip.CheckStatus(Gdip.GdipFlush(new HandleRef(this, NativeGraphics), intention)); - } - - public void SetClip(Graphics g) => SetClip(g, CombineMode.Replace); - - public void SetClip(Graphics g, CombineMode combineMode) - { - ArgumentNullException.ThrowIfNull(g); - - Gdip.CheckStatus(Gdip.GdipSetClipGraphics( - new HandleRef(this, NativeGraphics), - new HandleRef(g, g.NativeGraphics), - combineMode)); - } - - public void SetClip(Rectangle rect) => SetClip(rect, CombineMode.Replace); - - public void SetClip(Rectangle rect, CombineMode combineMode) - { - Gdip.CheckStatus(Gdip.GdipSetClipRectI( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - combineMode)); - } - - public void SetClip(RectangleF rect) => SetClip(rect, CombineMode.Replace); - - public void SetClip(RectangleF rect, CombineMode combineMode) - { - Gdip.CheckStatus(Gdip.GdipSetClipRect( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - combineMode)); - } - - public void SetClip(GraphicsPath path) => SetClip(path, CombineMode.Replace); - - public void SetClip(GraphicsPath path, CombineMode combineMode) - { - ArgumentNullException.ThrowIfNull(path); - - Gdip.CheckStatus(Gdip.GdipSetClipPath( - new HandleRef(this, NativeGraphics), - new HandleRef(path, path._nativePath), - combineMode)); - } - - public void SetClip(Region region, CombineMode combineMode) - { - ArgumentNullException.ThrowIfNull(region); - - Gdip.CheckStatus(Gdip.GdipSetClipRegion( - new HandleRef(this, NativeGraphics), - new HandleRef(region, region.NativeRegion), - combineMode)); - } - - public void IntersectClip(Rectangle rect) - { - Gdip.CheckStatus(Gdip.GdipSetClipRectI( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - CombineMode.Intersect)); - } - - public void IntersectClip(RectangleF rect) - { - Gdip.CheckStatus(Gdip.GdipSetClipRect( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - CombineMode.Intersect)); - } - - public void IntersectClip(Region region) - { - ArgumentNullException.ThrowIfNull(region); - - Gdip.CheckStatus(Gdip.GdipSetClipRegion( - new HandleRef(this, NativeGraphics), - new HandleRef(region, region.NativeRegion), - CombineMode.Intersect)); - } - - public void ExcludeClip(Rectangle rect) - { - Gdip.CheckStatus(Gdip.GdipSetClipRectI( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - CombineMode.Exclude)); - } - - public void ExcludeClip(Region region) - { - ArgumentNullException.ThrowIfNull(region); - - Gdip.CheckStatus(Gdip.GdipSetClipRegion( - new HandleRef(this, NativeGraphics), - new HandleRef(region, region.NativeRegion), - CombineMode.Exclude)); - } - - public void ResetClip() - { - Gdip.CheckStatus(Gdip.GdipResetClip(new HandleRef(this, NativeGraphics))); - } - - public void TranslateClip(float dx, float dy) - { - Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); - } - - public void TranslateClip(int dx, int dy) - { - Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); - } - - public bool IsVisible(int x, int y) => IsVisible(new Point(x, y)); - - public bool IsVisible(Point point) - { - Gdip.CheckStatus(Gdip.GdipIsVisiblePointI( - new HandleRef(this, NativeGraphics), - point.X, point.Y, - out bool isVisible)); - - return isVisible; - } - - public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y)); - - public bool IsVisible(PointF point) - { - Gdip.CheckStatus(Gdip.GdipIsVisiblePoint( - new HandleRef(this, NativeGraphics), - point.X, point.Y, - out bool isVisible)); - - return isVisible; - } - - public bool IsVisible(int x, int y, int width, int height) - { - return IsVisible(new Rectangle(x, y, width, height)); - } - - public bool IsVisible(Rectangle rect) - { - Gdip.CheckStatus(Gdip.GdipIsVisibleRectI( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - out bool isVisible)); - - return isVisible; - } - - public bool IsVisible(float x, float y, float width, float height) - { - return IsVisible(new RectangleF(x, y, width, height)); - } - - public bool IsVisible(RectangleF rect) - { - Gdip.CheckStatus(Gdip.GdipIsVisibleRect( - new HandleRef(this, NativeGraphics), - rect.X, rect.Y, rect.Width, rect.Height, - out bool isVisible)); - - return isVisible; - } - - /// - /// Resets the world transform to identity. - /// - public void ResetTransform() - { - Gdip.CheckStatus(Gdip.GdipResetWorldTransform(new HandleRef(this, NativeGraphics))); - } - - /// - /// Multiplies the that represents the world transform and . - /// - public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); - - /// - /// Multiplies the that represents the world transform and . - /// - public void MultiplyTransform(Matrix matrix, MatrixOrder order) - { - ArgumentNullException.ThrowIfNull(matrix); - - // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws - // with the libgdiplus backend. Simulate a nop for compatability with GDI+. - if (matrix.NativeMatrix == IntPtr.Zero) - return; - - Gdip.CheckStatus(Gdip.GdipMultiplyWorldTransform( - new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix), order)); - } - - public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); - - public void TranslateTransform(float dx, float dy, MatrixOrder order) - { - Gdip.CheckStatus(Gdip.GdipTranslateWorldTransform(new HandleRef(this, NativeGraphics), dx, dy, order)); - } - - public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); - - public void ScaleTransform(float sx, float sy, MatrixOrder order) - { - Gdip.CheckStatus(Gdip.GdipScaleWorldTransform(new HandleRef(this, NativeGraphics), sx, sy, order)); - } - - public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); - - public void RotateTransform(float angle, MatrixOrder order) - { - Gdip.CheckStatus(Gdip.GdipRotateWorldTransform(new HandleRef(this, NativeGraphics), angle, order)); - } - - /// - /// Draws an arc from the specified ellipse. - /// - public void DrawArc(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawArc( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Draws an arc from the specified ellipse. - /// - public void DrawArc(Pen pen, RectangleF rect, float startAngle, float sweepAngle) - { - DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Draws an arc from the specified ellipse. - /// - public void DrawArc(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawArcI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Draws an arc from the specified ellipse. - /// - public void DrawArc(Pen pen, Rectangle rect, float startAngle, float sweepAngle) - { - DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Draws a cubic bezier curve defined by four ordered pairs that represent points. - /// - public void DrawBezier(Pen pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawBezier( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - x1, y1, x2, y2, x3, y3, x4, y4)); - } - - /// - /// Draws a cubic bezier curve defined by four points. - /// - public void DrawBezier(Pen pen, PointF pt1, PointF pt2, PointF pt3, PointF pt4) - { - DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); - } - - /// - /// Draws a cubic bezier curve defined by four points. - /// - public void DrawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) - { - DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); - } - - /// - /// Draws the outline of a rectangle specified by . - /// - /// A Pen that determines the color, width, and style of the rectangle. - /// A Rectangle structure that represents the rectangle to draw. - public void DrawRectangle(Pen pen, RectangleF rect) - { - DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Draws the outline of a rectangle specified by . - /// - public void DrawRectangle(Pen pen, Rectangle rect) - { - DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Draws the outline of the specified rectangle. - /// - public void DrawRectangle(Pen pen, float x, float y, float width, float height) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawRectangle( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - x, y, width, height)); - } - - /// - /// Draws the outline of the specified rectangle. - /// - public void DrawRectangle(Pen pen, int x, int y, int width, int height) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawRectangleI( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - x, y, width, height)); - } - - /// - /// Draws the outlines of a series of rectangles. - /// - public unsafe void DrawRectangles(Pen pen, RectangleF[] rects) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(rects); - - fixed (RectangleF* r = rects) - { - CheckErrorStatus(Gdip.GdipDrawRectangles( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - r, rects.Length)); - } - } - - /// - /// Draws the outlines of a series of rectangles. - /// - public unsafe void DrawRectangles(Pen pen, Rectangle[] rects) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(rects); - - fixed (Rectangle* r = rects) - { - CheckErrorStatus(Gdip.GdipDrawRectanglesI( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - r, rects.Length)); - } - } - - /// - /// Draws the outline of an ellipse defined by a bounding rectangle. - /// - public void DrawEllipse(Pen pen, RectangleF rect) - { - DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Draws the outline of an ellipse defined by a bounding rectangle. - /// - public void DrawEllipse(Pen pen, float x, float y, float width, float height) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawEllipse( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height)); - } - - /// - /// Draws the outline of an ellipse specified by a bounding rectangle. - /// - public void DrawEllipse(Pen pen, Rectangle rect) - { - DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Draws the outline of an ellipse defined by a bounding rectangle. - /// - public void DrawEllipse(Pen pen, int x, int y, int width, int height) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawEllipseI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height)); - } - - /// - /// Draws the outline of a pie section defined by an ellipse and two radial lines. - /// - public void DrawPie(Pen pen, RectangleF rect, float startAngle, float sweepAngle) - { - DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Draws the outline of a pie section defined by an ellipse and two radial lines. - /// - public void DrawPie(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawPie( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Draws the outline of a pie section defined by an ellipse and two radial lines. - /// - public void DrawPie(Pen pen, Rectangle rect, float startAngle, float sweepAngle) - { - DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Draws the outline of a pie section defined by an ellipse and two radial lines. - /// - public void DrawPie(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawPieI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Draws the outline of a polygon defined by an array of points. - /// - public unsafe void DrawPolygon(Pen pen, PointF[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawPolygon( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws the outline of a polygon defined by an array of points. - /// - public unsafe void DrawPolygon(Pen pen, Point[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawPolygonI( - new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws the lines and curves defined by a . - /// - public void DrawPath(Pen pen, GraphicsPath path) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(path); - - CheckErrorStatus(Gdip.GdipDrawPath( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - new HandleRef(path, path._nativePath))); - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, PointF[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurve( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, PointF[] points, float tension) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurve2( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - tension)); - } - } - - public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments) - { - DrawCurve(pen, points, offset, numberOfSegments, 0.5f); - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments, float tension) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurve3( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - offset, - numberOfSegments, - tension)); - } - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, Point[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurveI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, Point[] points, float tension) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurve2I( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - tension)); - } - } - - /// - /// Draws a curve defined by an array of points. - /// - public unsafe void DrawCurve(Pen pen, Point[] points, int offset, int numberOfSegments, float tension) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawCurve3I( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - offset, - numberOfSegments, - tension)); - } - } - - /// - /// Draws a closed curve defined by an array of points. - /// - public unsafe void DrawClosedCurve(Pen pen, PointF[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawClosedCurve( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws a closed curve defined by an array of points. - /// - public unsafe void DrawClosedCurve(Pen pen, PointF[] points, float tension, FillMode fillmode) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawClosedCurve2( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - tension)); - } - } - - /// - /// Draws a closed curve defined by an array of points. - /// - public unsafe void DrawClosedCurve(Pen pen, Point[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawClosedCurveI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws a closed curve defined by an array of points. - /// - public unsafe void DrawClosedCurve(Pen pen, Point[] points, float tension, FillMode fillmode) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawClosedCurve2I( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length, - tension)); - } - } - - /// - /// Fills the entire drawing surface with the specified color. - /// - public void Clear(Color color) - { - Gdip.CheckStatus(Gdip.GdipGraphicsClear(new HandleRef(this, NativeGraphics), color.ToArgb())); - } - - /// - /// Fills the interior of a rectangle with a . - /// - public void FillRectangle(Brush brush, RectangleF rect) - { - FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Fills the interior of a rectangle with a . - /// - public void FillRectangle(Brush brush, float x, float y, float width, float height) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillRectangle( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height)); - } - - /// - /// Fills the interior of a rectangle with a . - /// - public void FillRectangle(Brush brush, Rectangle rect) - { - FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Fills the interior of a rectangle with a . - /// - public void FillRectangle(Brush brush, int x, int y, int width, int height) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillRectangleI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height)); - } - - /// - /// Fills the interiors of a series of rectangles with a . - /// - public unsafe void FillRectangles(Brush brush, RectangleF[] rects) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(rects); - - fixed (RectangleF* r = rects) - { - CheckErrorStatus(Gdip.GdipFillRectangles( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - r, rects.Length)); - } - } - - /// - /// Fills the interiors of a series of rectangles with a . - /// - public unsafe void FillRectangles(Brush brush, Rectangle[] rects) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(rects); - - fixed (Rectangle* r = rects) - { - CheckErrorStatus(Gdip.GdipFillRectanglesI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - r, rects.Length)); - } - } - - /// - /// Fills the interior of a polygon defined by an array of points. - /// - public void FillPolygon(Brush brush, PointF[] points) - { - FillPolygon(brush, points, FillMode.Alternate); - } - - /// - /// Fills the interior of a polygon defined by an array of points. - /// - public unsafe void FillPolygon(Brush brush, PointF[] points, FillMode fillMode) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipFillPolygon( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length, - fillMode)); - } - } - - /// - /// Fills the interior of a polygon defined by an array of points. - /// - public void FillPolygon(Brush brush, Point[] points) - { - FillPolygon(brush, points, FillMode.Alternate); - } - - /// - /// Fills the interior of a polygon defined by an array of points. - /// - public unsafe void FillPolygon(Brush brush, Point[] points, FillMode fillMode) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipFillPolygonI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length, - fillMode)); - } - } - - /// - /// Fills the interior of an ellipse defined by a bounding rectangle. - /// - public void FillEllipse(Brush brush, RectangleF rect) - { - FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Fills the interior of an ellipse defined by a bounding rectangle. - /// - public void FillEllipse(Brush brush, float x, float y, float width, float height) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillEllipse( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height)); - } - - /// - /// Fills the interior of an ellipse defined by a bounding rectangle. - /// - public void FillEllipse(Brush brush, Rectangle rect) - { - FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); - } - - /// - /// Fills the interior of an ellipse defined by a bounding rectangle. - /// - public void FillEllipse(Brush brush, int x, int y, int width, int height) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillEllipseI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height)); - } - - /// - /// Fills the interior of a pie section defined by an ellipse and two radial lines. - /// - public void FillPie(Brush brush, Rectangle rect, float startAngle, float sweepAngle) - { - FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Fills the interior of a pie section defined by an ellipse and two radial lines. - /// - /// A Brush that determines the characteristics of the fill. - /// A Rectangle structure that represents the bounding rectangle that defines the ellipse from which the pie section comes. - /// Angle in degrees measured clockwise from the x-axis to the first side of the pie section. - /// Angle in degrees measured clockwise from the parameter to the second side of the pie section. - public void FillPie(Brush brush, RectangleF rect, float startAngle, float sweepAngle) - { - FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); - } - - /// - /// Fills the interior of a pie section defined by an ellipse and two radial lines. - /// - public void FillPie(Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillPie( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Fills the interior of a pie section defined by an ellipse and two radial lines. - /// - public void FillPie(Brush brush, int x, int y, int width, int height, int startAngle, int sweepAngle) - { - ArgumentNullException.ThrowIfNull(brush); - - CheckErrorStatus(Gdip.GdipFillPieI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - x, y, width, height, - startAngle, - sweepAngle)); - } - - /// - /// Fills the interior a closed curve defined by an array of points. - /// - public unsafe void FillClosedCurve(Brush brush, PointF[] points) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipFillClosedCurve( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length)); - } - } - - /// - /// Fills the interior of a closed curve defined by an array of points. - /// - public void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode) - { - FillClosedCurve(brush, points, fillmode, 0.5f); - } - - public unsafe void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode, float tension) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipFillClosedCurve2( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length, - tension, - fillmode)); - } - } - - /// - /// Fills the interior a closed curve defined by an array of points. - /// - public unsafe void FillClosedCurve(Brush brush, Point[] points) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipFillClosedCurveI( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length)); - } - } - - public void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode) - { - FillClosedCurve(brush, points, fillmode, 0.5f); - } - - public unsafe void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode, float tension) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipFillClosedCurve2I( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - p, points.Length, - tension, - fillmode)); - } - } - - /// - /// Draws a string with the specified font. - /// - public void DrawString(string? s, Font font, Brush brush, float x, float y) - { - DrawString(s, font, brush, new RectangleF(x, y, 0, 0), null); - } - - public void DrawString(string? s, Font font, Brush brush, PointF point) - { - DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), null); - } - - public void DrawString(string? s, Font font, Brush brush, float x, float y, StringFormat? format) - { - DrawString(s, font, brush, new RectangleF(x, y, 0, 0), format); - } - - public void DrawString(string? s, Font font, Brush brush, PointF point, StringFormat? format) - { - DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), format); - } - - public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle) - { - DrawString(s, font, brush, layoutRectangle, null); - } - - public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat? format) - { - ArgumentNullException.ThrowIfNull(brush); - if (string.IsNullOrEmpty(s)) - return; - ArgumentNullException.ThrowIfNull(font); - - CheckErrorStatus(Gdip.GdipDrawString( - new HandleRef(this, NativeGraphics), - s, - s.Length, - new HandleRef(font, font.NativeFont), - ref layoutRectangle, - new HandleRef(format, format?.nativeFormat ?? IntPtr.Zero), - new HandleRef(brush, brush.NativeBrush))); - } - - public SizeF MeasureString( - string? text, - Font font, - SizeF layoutArea, - StringFormat? stringFormat, - out int charactersFitted, - out int linesFilled) - { - if (string.IsNullOrEmpty(text)) - { - charactersFitted = 0; - linesFilled = 0; - return SizeF.Empty; - } - - if (font == null) - throw new ArgumentNullException(nameof(font)); - - RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); - RectangleF boundingBox = default; - - Gdip.CheckStatus(Gdip.GdipMeasureString( - new HandleRef(this, NativeGraphics), - text, - text.Length, - new HandleRef(font, font.NativeFont), - ref layout, - new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), - ref boundingBox, - out charactersFitted, - out linesFilled)); - - return boundingBox.Size; - } - - public SizeF MeasureString(string? text, Font font, PointF origin, StringFormat? stringFormat) - { - if (string.IsNullOrEmpty(text)) - return SizeF.Empty; - if (font == null) - throw new ArgumentNullException(nameof(font)); - - RectangleF layout = new RectangleF(origin.X, origin.Y, 0, 0); - RectangleF boundingBox = default; - - Gdip.CheckStatus(Gdip.GdipMeasureString( - new HandleRef(this, NativeGraphics), - text, - text.Length, - new HandleRef(font, font.NativeFont), - ref layout, - new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), - ref boundingBox, - out _, - out _)); - - return boundingBox.Size; - } - - public SizeF MeasureString(string? text, Font font, SizeF layoutArea) => MeasureString(text, font, layoutArea, null); - - public SizeF MeasureString(string? text, Font font, SizeF layoutArea, StringFormat? stringFormat) - { - if (string.IsNullOrEmpty(text)) - return SizeF.Empty; - if (font == null) - throw new ArgumentNullException(nameof(font)); - - RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); - RectangleF boundingBox = default; - - Gdip.CheckStatus(Gdip.GdipMeasureString( - new HandleRef(this, NativeGraphics), - text, - text.Length, - new HandleRef(font, font.NativeFont), - ref layout, - new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), - ref boundingBox, - out _, - out _)); - - return boundingBox.Size; - } - - public SizeF MeasureString(string? text, Font font) - { - return MeasureString(text, font, new SizeF(0, 0)); - } - - public SizeF MeasureString(string? text, Font font, int width) - { - return MeasureString(text, font, new SizeF(width, 999999)); - } - - public SizeF MeasureString(string? text, Font font, int width, StringFormat? format) - { - return MeasureString(text, font, new SizeF(width, 999999), format); - } - - public Region[] MeasureCharacterRanges(string? text, Font font, RectangleF layoutRect, StringFormat? stringFormat) - { - if (string.IsNullOrEmpty(text)) - return Array.Empty(); - if (font == null) - throw new ArgumentNullException(nameof(font)); - - Gdip.CheckStatus(Gdip.GdipGetStringFormatMeasurableCharacterRangeCount( - new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), - out int count)); - - IntPtr[] gpRegions = new IntPtr[count]; - Region[] regions = new Region[count]; - - for (int f = 0; f < count; f++) - { - regions[f] = new Region(); - gpRegions[f] = regions[f].NativeRegion; - } - - Gdip.CheckStatus(Gdip.GdipMeasureCharacterRanges( - new HandleRef(this, NativeGraphics), - text, - text.Length, - new HandleRef(font, font.NativeFont), - ref layoutRect, - new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), - count, - gpRegions)); - - return regions; - } - - /// - /// Draws the specified image at the specified location. - /// - public void DrawImage(Image image, PointF point) - { - DrawImage(image, point.X, point.Y); - } - - public void DrawImage(Image image, float x, float y) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImage( - new HandleRef(this, NativeGraphics), new HandleRef(image, image.nativeImage), - x, y); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, RectangleF rect) - { - DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); - } - - public void DrawImage(Image image, float x, float y, float width, float height) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - x, y, - width, height); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, Point point) - { - DrawImage(image, point.X, point.Y); - } - - public void DrawImage(Image image, int x, int y) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - x, y); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, Rectangle rect) - { - DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); - } - - public void DrawImage(Image image, int x, int y, int width, int height) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - x, y, - width, height); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImageUnscaled(Image image, Point point) - { - DrawImage(image, point.X, point.Y); - } - - public void DrawImageUnscaled(Image image, int x, int y) - { - DrawImage(image, x, y); - } - - public void DrawImageUnscaled(Image image, Rectangle rect) - { - DrawImage(image, rect.X, rect.Y); - } - - public void DrawImageUnscaled(Image image, int x, int y, int width, int height) - { - DrawImage(image, x, y); - } - - public void DrawImageUnscaledAndClipped(Image image, Rectangle rect) - { - ArgumentNullException.ThrowIfNull(image); - - int width = Math.Min(rect.Width, image.Width); - int height = Math.Min(rect.Height, image.Height); - - // We could put centering logic here too for the case when the image - // is smaller than the rect. - DrawImage(image, rect, 0, 0, width, height, GraphicsUnit.Pixel); - } - - // Affine or perspective blt - // destPoints.Length = 3: rect => parallelogram - // destPoints[0] <=> top-left corner of the source rectangle - // destPoints[1] <=> top-right corner - // destPoints[2] <=> bottom-left corner - // destPoints.Length = 4: rect => quad - // destPoints[3] <=> bottom-right corner - // - // @notes Perspective blt only works for bitmap images. - - public unsafe void DrawImage(Image image, PointF[] destPoints) - { - ArgumentNullException.ThrowIfNull(image); - ArgumentNullException.ThrowIfNull(destPoints); - - int count = destPoints.Length; - if (count != 3 && count != 4) - throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); - - fixed (PointF* p = destPoints) - { - int status = Gdip.GdipDrawImagePoints( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - p, count); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - } - - public unsafe void DrawImage(Image image, Point[] destPoints) - { - ArgumentNullException.ThrowIfNull(image); - ArgumentNullException.ThrowIfNull(destPoints); - - int count = destPoints.Length; - if (count != 3 && count != 4) - throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); - - fixed (Point* p = destPoints) - { - int status = Gdip.GdipDrawImagePointsI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - p, count); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - } - - public void DrawImage(Image image, float x, float y, RectangleF srcRect, GraphicsUnit srcUnit) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImagePointRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - x, y, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - (int)srcUnit); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, int x, int y, Rectangle srcRect, GraphicsUnit srcUnit) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImagePointRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - x, y, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - (int)srcUnit); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRectRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - destRect.X, destRect.Y, destRect.Width, destRect.Height, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - srcUnit, - NativeMethods.NullHandleRef, - null, - NativeMethods.NullHandleRef); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage(Image image, Rectangle destRect, Rectangle srcRect, GraphicsUnit srcUnit) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRectRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - destRect.X, destRect.Y, destRect.Width, destRect.Height, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - srcUnit, - NativeMethods.NullHandleRef, - null, - NativeMethods.NullHandleRef); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public unsafe void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit) - { - ArgumentNullException.ThrowIfNull(image); - ArgumentNullException.ThrowIfNull(destPoints); - - int count = destPoints.Length; - if (count != 3 && count != 4) - throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); - - fixed (PointF* p = destPoints) - { - int status = Gdip.GdipDrawImagePointsRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - p, destPoints.Length, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - srcUnit, - NativeMethods.NullHandleRef, - null, - NativeMethods.NullHandleRef); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - } - - public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes? imageAttr) - { - DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); - } - - public void DrawImage( - Image image, - PointF[] destPoints, - RectangleF srcRect, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr, - DrawImageAbort? callback) - { - DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); - } - - public unsafe void DrawImage( - Image image, - PointF[] destPoints, - RectangleF srcRect, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr, - DrawImageAbort? callback, - int callbackData) - { - ArgumentNullException.ThrowIfNull(image); - ArgumentNullException.ThrowIfNull(destPoints); - - int count = destPoints.Length; - if (count != 3 && count != 4) - throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); - - fixed (PointF* p = destPoints) - { - int status = Gdip.GdipDrawImagePointsRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - p, destPoints.Length, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - srcUnit, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), - callback, - new HandleRef(null, (IntPtr)callbackData)); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - } - - public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit) - { - DrawImage(image, destPoints, srcRect, srcUnit, null, null, 0); - } - - public void DrawImage( - Image image, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr) - { - DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); - } - - public void DrawImage( - Image image, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr, - DrawImageAbort? callback) - { - DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); - } - - public unsafe void DrawImage( - Image image, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr, - DrawImageAbort? callback, - int callbackData) - { - ArgumentNullException.ThrowIfNull(image); - ArgumentNullException.ThrowIfNull(destPoints); - - int count = destPoints.Length; - if (count != 3 && count != 4) - throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); - - fixed (Point* p = destPoints) - { - int status = Gdip.GdipDrawImagePointsRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - p, destPoints.Length, - srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, - srcUnit, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), - callback, - new HandleRef(null, (IntPtr)callbackData)); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - } - - public void DrawImage( - Image image, - Rectangle destRect, - float srcX, - float srcY, - float srcWidth, - float srcHeight, - GraphicsUnit srcUnit) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); - } - - public void DrawImage( - Image image, - Rectangle destRect, - float srcX, - float srcY, - float srcWidth, - float srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttrs) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, null); - } - - public void DrawImage( - Image image, - Rectangle destRect, - float srcX, - float srcY, - float srcWidth, - float srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttrs, - DrawImageAbort? callback) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, callback, IntPtr.Zero); - } - - public void DrawImage( - Image image, - Rectangle destRect, - float srcX, - float srcY, - float srcWidth, - float srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttrs, - DrawImageAbort? callback, - IntPtr callbackData) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRectRect( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - destRect.X, destRect.Y, destRect.Width, destRect.Height, - srcX, srcY, srcWidth, srcHeight, - srcUnit, - new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), - callback, - new HandleRef(null, callbackData)); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - public void DrawImage( - Image image, - Rectangle destRect, - int srcX, - int srcY, - int srcWidth, - int srcHeight, - GraphicsUnit srcUnit) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); - } - - public void DrawImage( - Image image, - Rectangle destRect, - int srcX, - int srcY, - int srcWidth, - int srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, null); - } - - public void DrawImage( - Image image, - Rectangle destRect, - int srcX, - int srcY, - int srcWidth, - int srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttr, - DrawImageAbort? callback) - { - DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, callback, IntPtr.Zero); - } - - public void DrawImage( - Image image, - Rectangle destRect, - int srcX, - int srcY, - int srcWidth, - int srcHeight, - GraphicsUnit srcUnit, - ImageAttributes? imageAttrs, - DrawImageAbort? callback, - IntPtr callbackData) - { - ArgumentNullException.ThrowIfNull(image); - - int status = Gdip.GdipDrawImageRectRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(image, image.nativeImage), - destRect.X, destRect.Y, destRect.Width, destRect.Height, - srcX, srcY, srcWidth, srcHeight, - srcUnit, - new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), - callback, - new HandleRef(null, callbackData)); - - IgnoreMetafileErrors(image, ref status); - CheckErrorStatus(status); - } - - /// - /// Draws a line connecting the two specified points. - /// - public void DrawLine(Pen pen, PointF pt1, PointF pt2) - { - DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); - } - - /// - /// Draws a series of line segments that connect an array of points. - /// - public unsafe void DrawLines(Pen pen, PointF[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawLines( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - - /// - /// Draws a line connecting the two specified points. - /// - public void DrawLine(Pen pen, int x1, int y1, int x2, int y2) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawLineI(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); - } - - /// - /// Draws a line connecting the two specified points. - /// - public void DrawLine(Pen pen, Point pt1, Point pt2) - { - DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); - } - - /// - /// Draws a series of line segments that connect an array of points. - /// - public unsafe void DrawLines(Pen pen, Point[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawLinesI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, - points.Length)); - } - } - - /// - /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. - /// - public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize) - { - CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize); - } - - /// - /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. - /// - public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize) - { - CopyFromScreen(sourceX, sourceY, destinationX, destinationY, blockRegionSize, CopyPixelOperation.SourceCopy); - } - - /// - /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size - /// and specified raster operation. - /// - public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize, CopyPixelOperation copyPixelOperation) - { - CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize, copyPixelOperation); - } - - public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); - } - - public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback, IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoint, callback, callbackData, null); - } - - public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); - } - - public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback, IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoint, callback, callbackData, null); - } - - public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); - } - - public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback, IntPtr callbackData) - { - EnumerateMetafile(metafile, destRect, callback, callbackData, null); - } - - public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); - } - - public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback, IntPtr callbackData) - { - EnumerateMetafile(metafile, destRect, callback, callbackData, null); - } - - public void EnumerateMetafile(Metafile metafile, PointF[] destPoints, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - PointF[] destPoints, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero, null); - } - - public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); - } - - public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback, IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoints, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - PointF destPoint, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - PointF destPoint, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - Point destPoint, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - Point destPoint, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - RectangleF destRect, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - RectangleF destRect, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - Rectangle destRect, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - Rectangle destRect, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - PointF[] destPoints, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - PointF[] destPoints, - RectangleF srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); - } - - public void EnumerateMetafile( - Metafile metafile, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback) - { - EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); - } - - public void EnumerateMetafile( - Metafile metafile, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit srcUnit, - EnumerateMetafileProc callback, - IntPtr callbackData) - { - EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); - } - - public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] pts) - { - ArgumentNullException.ThrowIfNull(pts); - - fixed (PointF* p = pts) - { - Gdip.CheckStatus(Gdip.GdipTransformPoints( - new HandleRef(this, NativeGraphics), - (int)destSpace, - (int)srcSpace, - p, - pts.Length)); - } - } - - public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] pts) - { - ArgumentNullException.ThrowIfNull(pts); - - fixed (Point* p = pts) - { - Gdip.CheckStatus(Gdip.GdipTransformPointsI( - new HandleRef(this, NativeGraphics), - (int)destSpace, - (int)srcSpace, - p, - pts.Length)); - } - } - - /// - /// GDI+ will return a 'generic error' when we attempt to draw an Emf - /// image with width/height == 1. Here, we will hack around this by - /// resetting the errorstatus. Note that we don't do simple arg checking - /// for height || width == 1 here because transforms can be applied to - /// the Graphics object making it difficult to identify this scenario. - /// - private static void IgnoreMetafileErrors(Image image, ref int errorStatus) - { - if (errorStatus != Gdip.Ok && image.RawFormat.Equals(ImageFormat.Emf)) - errorStatus = Gdip.Ok; - } - - /// - /// Creates a Region class only if the native region is not infinite. - /// - internal Region? GetRegionIfNotInfinite() - { - Gdip.CheckStatus(Gdip.GdipCreateRegion(out IntPtr regionHandle)); - try - { - Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(null, regionHandle)); - Gdip.CheckStatus(Gdip.GdipIsInfiniteRegion( - new HandleRef(null, regionHandle), - new HandleRef(this, NativeGraphics), - out int isInfinite)); - - if (isInfinite != 0) - { - // Infinite - return null; - } - - Region region = new Region(regionHandle); - regionHandle = IntPtr.Zero; - return region; - } - finally - { - if (regionHandle != IntPtr.Zero) - { - Gdip.GdipDeleteRegion(new HandleRef(null, regionHandle)); - } - } - } - - /// - /// Represents an object used in connection with the printing API, it is used to hold a reference to a - /// PrintPreviewGraphics (fake graphics) or a printer DeviceContext (and maybe more in the future). - /// - internal object? PrintingHelper - { - get => _printingHelper; - set - { - Debug.Assert(_printingHelper == null, "WARNING: Overwritting the printing helper reference!"); - _printingHelper = value; - } - } - - /// - /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size - /// and specified raster operation. - /// - public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) - { - switch (copyPixelOperation) - { - case CopyPixelOperation.Blackness: - case CopyPixelOperation.NotSourceErase: - case CopyPixelOperation.NotSourceCopy: - case CopyPixelOperation.SourceErase: - case CopyPixelOperation.DestinationInvert: - case CopyPixelOperation.PatInvert: - case CopyPixelOperation.SourceInvert: - case CopyPixelOperation.SourceAnd: - case CopyPixelOperation.MergePaint: - case CopyPixelOperation.MergeCopy: - case CopyPixelOperation.SourceCopy: - case CopyPixelOperation.SourcePaint: - case CopyPixelOperation.PatCopy: - case CopyPixelOperation.PatPaint: - case CopyPixelOperation.Whiteness: - case CopyPixelOperation.CaptureBlt: - case CopyPixelOperation.NoMirrorBitmap: - break; - default: - throw new InvalidEnumArgumentException(nameof(copyPixelOperation), (int)copyPixelOperation, typeof(CopyPixelOperation)); - } - - int destWidth = blockRegionSize.Width; - int destHeight = blockRegionSize.Height; - - IntPtr screenDC = Interop.User32.GetDC(IntPtr.Zero); - try - { - IntPtr targetDC = GetHdc(); - int result = Interop.Gdi32.BitBlt( - targetDC, - destinationX, - destinationY, - destWidth, - destHeight, - screenDC, - sourceX, - sourceY, - (Interop.Gdi32.RasterOp)copyPixelOperation); - - //a zero result indicates a win32 exception has been thrown - if (result == 0) - { - throw new Win32Exception(); - } - } - finally - { - Interop.User32.ReleaseDC(IntPtr.Zero, screenDC); - ReleaseHdc(); - } - } - - public Color GetNearestColor(Color color) - { - int nearest = color.ToArgb(); - Gdip.CheckStatus(Gdip.GdipGetNearestColor(new HandleRef(this, NativeGraphics), ref nearest)); - return Color.FromArgb(nearest); - } - - /// - /// Draws a line connecting the two specified points. - /// - public void DrawLine(Pen pen, float x1, float y1, float x2, float y2) - { - ArgumentNullException.ThrowIfNull(pen); - - CheckErrorStatus(Gdip.GdipDrawLine(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); - } - - /// - /// Draws a series of cubic Bezier curves from an array of points. - /// - public unsafe void DrawBeziers(Pen pen, PointF[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (PointF* p = points) - { - CheckErrorStatus(Gdip.GdipDrawBeziers( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, points.Length)); - } - } - - /// - /// Draws a series of cubic Bezier curves from an array of points. - /// - public unsafe void DrawBeziers(Pen pen, Point[] points) - { - ArgumentNullException.ThrowIfNull(pen); - ArgumentNullException.ThrowIfNull(points); - - fixed (Point* p = points) - { - CheckErrorStatus(Gdip.GdipDrawBeziersI( - new HandleRef(this, NativeGraphics), - new HandleRef(pen, pen.NativePen), - p, - points.Length)); - } - } - - /// - /// Fills the interior of a path. - /// - public void FillPath(Brush brush, GraphicsPath path) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(path); - - CheckErrorStatus(Gdip.GdipFillPath( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - new HandleRef(path, path._nativePath))); - } - - /// - /// Fills the interior of a . - /// - public void FillRegion(Brush brush, Region region) - { - ArgumentNullException.ThrowIfNull(brush); - ArgumentNullException.ThrowIfNull(region); - - CheckErrorStatus(Gdip.GdipFillRegion( - new HandleRef(this, NativeGraphics), - new HandleRef(brush, brush.NativeBrush), - new HandleRef(region, region.NativeRegion))); - } - - public void DrawIcon(Icon icon, int x, int y) - { - ArgumentNullException.ThrowIfNull(icon); - - if (_backingImage != null) - { - // We don't call the icon directly because we want to stay in GDI+ all the time - // to avoid alpha channel interop issues between gdi and gdi+ - // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. - DrawImage(icon.ToBitmap(), x, y); - } - else - { - icon.Draw(this, x, y); - } - } - - /// - /// Draws this image to a graphics object. The drawing command originates on the graphics - /// object, but a graphics object generally has no idea how to render a given image. So, - /// it passes the call to the actual image. This version crops the image to the given - /// dimensions and allows the user to specify a rectangle within the image to draw. - /// - public void DrawIcon(Icon icon, Rectangle targetRect) - { - ArgumentNullException.ThrowIfNull(icon); - - if (_backingImage != null) - { - // We don't call the icon directly because we want to stay in GDI+ all the time - // to avoid alpha channel interop issues between gdi and gdi+ - // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. - DrawImage(icon.ToBitmap(), targetRect); - } - else - { - icon.Draw(this, targetRect); - } - } - - /// - /// Draws this image to a graphics object. The drawing command originates on the graphics - /// object, but a graphics object generally has no idea how to render a given image. So, - /// it passes the call to the actual image. This version stretches the image to the given - /// dimensions and allows the user to specify a rectangle within the image to draw. - /// - public void DrawIconUnstretched(Icon icon, Rectangle targetRect) - { - ArgumentNullException.ThrowIfNull(icon); - - if (_backingImage != null) - { - DrawImageUnscaled(icon.ToBitmap(), targetRect); - } - else - { - icon.DrawUnstretched(this, targetRect); - } - } - - public void EnumerateMetafile( - Metafile metafile, - PointF destPoint, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoint( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destPoint, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - public void EnumerateMetafile( - Metafile metafile, - Point destPoint, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destPoint, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public void EnumerateMetafile( - Metafile metafile, - RectangleF destRect, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRect( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destRect, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public void EnumerateMetafile( - Metafile metafile, - Rectangle destRect, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destRect, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public unsafe void EnumerateMetafile( - Metafile metafile, - PointF[] destPoints, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - ArgumentNullException.ThrowIfNull(destPoints); - - if (destPoints.Length != 3) - throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); - - fixed (PointF* p = destPoints) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoints( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - p, destPoints.Length, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - } - - public unsafe void EnumerateMetafile( - Metafile metafile, - Point[] destPoints, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - ArgumentNullException.ThrowIfNull(destPoints); - - if (destPoints.Length != 3) - throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); - - fixed (Point* p = destPoints) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointsI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - p, destPoints.Length, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - } - - public void EnumerateMetafile( - Metafile metafile, - PointF destPoint, - RectangleF srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoint( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destPoint, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public void EnumerateMetafile( - Metafile metafile, - Point destPoint, - Rectangle srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destPoint, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public void EnumerateMetafile( - Metafile metafile, - RectangleF destRect, - RectangleF srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRect( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destRect, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public void EnumerateMetafile( - Metafile metafile, - Rectangle destRect, - Rectangle srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRectI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - ref destRect, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - - public unsafe void EnumerateMetafile( - Metafile metafile, - PointF[] destPoints, - RectangleF srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - ArgumentNullException.ThrowIfNull(destPoints); - - if (destPoints.Length != 3) - throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); - - fixed (PointF* p = destPoints) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoints( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - p, destPoints.Length, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - } - - public unsafe void EnumerateMetafile( - Metafile metafile, - Point[] destPoints, - Rectangle srcRect, - GraphicsUnit unit, - EnumerateMetafileProc callback, - IntPtr callbackData, - ImageAttributes? imageAttr) - { - ArgumentNullException.ThrowIfNull(destPoints); - - if (destPoints.Length != 3) - throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); - - fixed (Point* p = destPoints) - { - Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointsI( - new HandleRef(this, NativeGraphics), - new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), - p, destPoints.Length, - ref srcRect, - unit, - callback, - callbackData, - new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); - } - } - - /// - /// Combines current Graphics context with all previous contexts. - /// When BeginContainer() is called, a copy of the current context is pushed into the GDI+ context stack, it keeps track of the - /// absolute clipping and transform but reset the public properties so it looks like a brand new context. - /// When Save() is called, a copy of the current context is also pushed in the GDI+ stack but the public clipping and transform - /// properties are not reset (cumulative). Consecutive Save context are ignored with the exception of the top one which contains - /// all previous information. - /// The return value is an object array where the first element contains the cumulative clip region and the second the cumulative - /// translate transform matrix. - /// WARNING: This method is for internal FX support only. - /// - [EditorBrowsable(EditorBrowsableState.Never)] -#if NETCOREAPP3_1_OR_GREATER - [Obsolete(Obsoletions.GetContextInfoMessage, DiagnosticId = Obsoletions.GetContextInfoDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] -#endif - [SupportedOSPlatform("windows")] - public object GetContextInfo() - { - GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out Region? cumulativeClip); - return new object[] { cumulativeClip ?? new Region(), new Matrix(cumulativeTransform) }; - } - - private void GetContextInfo(out Matrix3x2 cumulativeTransform, bool calculateClip, out Region? cumulativeClip) - { - cumulativeClip = calculateClip ? GetRegionIfNotInfinite() : null; // Current context clip. - cumulativeTransform = TransformElements; // Current context transform. - Vector2 currentOffset = default; // Offset of current context. - Vector2 totalOffset = default; // Absolute coordinate offset of top context. - - GraphicsContext? context = _previousContext; - - if (!cumulativeTransform.IsIdentity) - { - currentOffset = cumulativeTransform.Translation; - } - - while (context is not null) - { - if (!context.TransformOffset.IsEmpty()) - { - cumulativeTransform.Translate(context.TransformOffset); - } - - if (!currentOffset.IsEmpty()) - { - // The location of the GDI+ clip region is relative to the coordinate origin after any translate transform - // has been applied. We need to intersect regions using the same coordinate origin relative to the previous - // context. - - // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. - cumulativeClip?.Translate(currentOffset.X, currentOffset.Y); - totalOffset.X += currentOffset.X; - totalOffset.Y += currentOffset.Y; - } - - // Context only stores clips if they are not infinite. Intersecting a clip with an infinite clip is a no-op. - if (calculateClip && context.Clip is not null) - { - // Intersecting an infinite clip with another is just a copy of the second clip. - if (cumulativeClip is null) - { - cumulativeClip = context.Clip; - } - else - { - cumulativeClip.Intersect(context.Clip); - } - } - - currentOffset = context.TransformOffset; - - // Ignore subsequent cumulative contexts. - do - { - context = context.Previous; - - if (context == null || !context.Next!.IsCumulative) - { - break; - } - } while (context.IsCumulative); - } - - if (!totalOffset.IsEmpty()) - { - // We need now to reset the total transform in the region so when calling Region.GetHRgn(Graphics) - // the HRegion is properly offset by GDI+ based on the total offset of the graphics object. - - // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. - cumulativeClip?.Translate(-totalOffset.X, -totalOffset.Y); - } - } - -#if NETCOREAPP3_1_OR_GREATER - /// - /// Gets the cumulative offset. - /// - /// The cumulative offset. - [EditorBrowsable(EditorBrowsableState.Never)] - [SupportedOSPlatform("windows")] - public void GetContextInfo(out PointF offset) - { - GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: false, out _); - Vector2 translation = cumulativeTransform.Translation; - offset = new PointF(translation.X, translation.Y); - } - - /// - /// Gets the cumulative offset and clip region. - /// - /// The cumulative offset. - /// The cumulative clip region or null if the clip region is infinite. - [EditorBrowsable(EditorBrowsableState.Never)] - [SupportedOSPlatform("windows")] - public void GetContextInfo(out PointF offset, out Region? clip) - { - GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out clip); - Vector2 translation = cumulativeTransform.Translation; - offset = new PointF(translation.X, translation.Y); - } -#endif - - public RectangleF VisibleClipBounds - { - get - { - if (PrintingHelper is PrintPreviewGraphics ppGraphics) - return ppGraphics.VisibleClipBounds; - - Gdip.CheckStatus(Gdip.GdipGetVisibleClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); - - return rect; - } - } - - /// - /// Saves the current context into the context stack. - /// - private void PushContext(GraphicsContext context) - { - Debug.Assert(context != null && context.State != 0, "GraphicsContext object is null or not valid."); - - if (_previousContext != null) - { - // Push context. - context.Previous = _previousContext; - _previousContext.Next = context; - } - _previousContext = context; - } - - /// - /// Pops all contexts from the specified one included. The specified context is becoming the current context. - /// - private void PopContext(int currentContextState) - { - Debug.Assert(_previousContext != null, "Trying to restore a context when the stack is empty"); - GraphicsContext? context = _previousContext; - - // Pop all contexts up the stack. - while (context != null) - { - if (context.State == currentContextState) - { - _previousContext = context.Previous; - - // This will dipose all context object up the stack. - context.Dispose(); - return; - } - context = context.Previous; - } - Debug.Fail("Warning: context state not found!"); - } - - public GraphicsState Save() - { - GraphicsContext context = new GraphicsContext(this); - int status = Gdip.GdipSaveGraphics(new HandleRef(this, NativeGraphics), out int state); - - if (status != Gdip.Ok) - { - context.Dispose(); - throw Gdip.StatusException(status); - } - - context.State = state; - context.IsCumulative = true; - PushContext(context); - - return new GraphicsState(state); - } - - public void Restore(GraphicsState gstate) - { - Gdip.CheckStatus(Gdip.GdipRestoreGraphics(new HandleRef(this, NativeGraphics), gstate.nativeState)); - PopContext(gstate.nativeState); - } - - public GraphicsContainer BeginContainer(RectangleF dstrect, RectangleF srcrect, GraphicsUnit unit) - { - GraphicsContext context = new GraphicsContext(this); - - int status = Gdip.GdipBeginContainer( - new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); - - if (status != Gdip.Ok) - { - context.Dispose(); - throw Gdip.StatusException(status); - } - - context.State = state; - PushContext(context); - - return new GraphicsContainer(state); - } - - public GraphicsContainer BeginContainer() - { - GraphicsContext context = new GraphicsContext(this); - int status = Gdip.GdipBeginContainer2(new HandleRef(this, NativeGraphics), out int state); - - if (status != Gdip.Ok) - { - context.Dispose(); - throw Gdip.StatusException(status); - } - - context.State = state; - PushContext(context); - - return new GraphicsContainer(state); - } - - public void EndContainer(GraphicsContainer container) - { - ArgumentNullException.ThrowIfNull(container); - - Gdip.CheckStatus(Gdip.GdipEndContainer(new HandleRef(this, NativeGraphics), container.nativeGraphicsContainer)); - PopContext(container.nativeGraphicsContainer); - } - - public GraphicsContainer BeginContainer(Rectangle dstrect, Rectangle srcrect, GraphicsUnit unit) - { - GraphicsContext context = new GraphicsContext(this); - - int status = Gdip.GdipBeginContainerI( - new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); - - if (status != Gdip.Ok) - { - context.Dispose(); - throw Gdip.StatusException(status); - } - - context.State = state; - PushContext(context); - - return new GraphicsContainer(state); - } - - public void AddMetafileComment(byte[] data) - { - ArgumentNullException.ThrowIfNull(data); - - Gdip.CheckStatus(Gdip.GdipComment(new HandleRef(this, NativeGraphics), data.Length, data)); - } - - public static IntPtr GetHalftonePalette() - { - if (s_halftonePalette == IntPtr.Zero) - { - lock (s_syncObject) - { - if (s_halftonePalette == IntPtr.Zero) - { - AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; - AppDomain.CurrentDomain.ProcessExit += OnDomainUnload; - - s_halftonePalette = Gdip.GdipCreateHalftonePalette(); - } - } - } - return s_halftonePalette; - } - - // This is called from AppDomain.ProcessExit and AppDomain.DomainUnload. - private static void OnDomainUnload(object? sender, EventArgs e) - { - if (s_halftonePalette != IntPtr.Zero) - { - Interop.Gdi32.DeleteObject(s_halftonePalette); - s_halftonePalette = IntPtr.Zero; - } - } - - /// - /// GDI+ will return a 'generic error' with specific win32 last error codes when - /// a terminal server session has been closed, minimized, etc... We don't want - /// to throw when this happens, so we'll guard against this by looking at the - /// 'last win32 error code' and checking to see if it is either 1) access denied - /// or 2) proc not found and then ignore it. - /// - /// The problem is that when you lock the machine, the secure desktop is enabled and - /// rendering fails which is expected (since the app doesn't have permission to draw - /// on the secure desktop). Not sure if there's anything you can do, short of catching - /// the desktop switch message and absorbing all the exceptions that get thrown while - /// it's the secure desktop. - /// - private static void CheckErrorStatus(int status) - { - if (status == Gdip.Ok) - return; - - // Generic error from GDI+ can be GenericError or Win32Error. - if (status == Gdip.GenericError || status == Gdip.Win32Error) - { - int error = Marshal.GetLastWin32Error(); - if (error == SafeNativeMethods.ERROR_ACCESS_DENIED || error == SafeNativeMethods.ERROR_PROC_NOT_FOUND || - // Here, we'll check to see if we are in a terminal services session... - (((Interop.User32.GetSystemMetrics(NativeMethods.SM_REMOTESESSION) & 0x00000001) != 0) && (error == 0))) - { - return; - } - } - - // Legitimate error, throw our status exception. - throw Gdip.StatusException(status); - } - } -} +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Globalization; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Encapsulates a GDI+ drawing surface. + /// + public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext + { +#if FINALIZATION_WATCH + static readonly TraceSwitch GraphicsFinalization = new TraceSwitch("GraphicsFinalization", "Tracks the creation and destruction of finalization"); + internal static string GetAllocationStack() { + if (GraphicsFinalization.TraceVerbose) { + return Environment.StackTrace; + } + else { + return "Enabled 'GraphicsFinalization' switch to see stack of allocation"; + } + } + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + /// + /// The context state previous to the current Graphics context (the head of the stack). + /// We don't keep a GraphicsContext for the current context since it is available at any time from GDI+ and + /// we don't want to keep track of changes in it. + /// + private GraphicsContext? _previousContext; + + private static readonly object s_syncObject = new object(); + + // Object reference used for printing; it could point to a PrintPreviewGraphics to obtain the VisibleClipBounds, or + // a DeviceContext holding a printer DC. + private object? _printingHelper; + + // GDI+'s preferred HPALETTE. + private static IntPtr s_halftonePalette; + + // pointer back to the Image backing a specific graphic object + private Image? _backingImage; + + /// + /// Handle to native DC - obtained from the GDI+ graphics object. We need to cache it to implement + /// IDeviceContext interface. + /// + private IntPtr _nativeHdc; + + public delegate bool DrawImageAbort(IntPtr callbackdata); + +#if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(DrawImageAbort), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources)] + internal unsafe struct DrawImageAbortMarshaller + { + private delegate Interop.BOOL DrawImageAbortNative(IntPtr callbackdata); + private DrawImageAbortNative? _managed; + private delegate* unmanaged _nativeFunction; + public DrawImageAbortMarshaller(DrawImageAbort? managed) + { + _managed = managed is null ? null : data => managed(data) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); + } + + public delegate* unmanaged ToNativeValue() + { + return _nativeFunction; + } + + public void FreeNative() + { + GC.KeepAlive(_managed); + } + } +#endif + + /// + /// Callback for EnumerateMetafile methods. + /// This method can then call Metafile.PlayRecord to play the record that was just enumerated. + /// + /// if >= MinRecordType, it's an EMF+ record + /// always 0 for EMF records + /// size of the data, or 0 if no data + /// pointer to the data, or NULL if no data (UINT32 aligned) + /// pointer to callbackData, if any + /// False to abort enumerating, true to continue. + public delegate bool EnumerateMetafileProc( + EmfPlusRecordType recordType, + int flags, + int dataSize, + IntPtr data, + PlayRecordCallback? callbackData); + +#if NET7_0_OR_GREATER + [CustomTypeMarshaller(typeof(EnumerateMetafileProc), CustomTypeMarshallerKind.Value, Direction = CustomTypeMarshallerDirection.In, Features = CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.UnmanagedResources)] + internal unsafe struct EnumerateMetafileProcMarshaller + { + private delegate Interop.BOOL EnumerateMetafileProcNative( + EmfPlusRecordType recordType, + int flags, + int dataSize, + IntPtr data, + IntPtr callbackData); + private EnumerateMetafileProcNative? _managed; + private delegate* unmanaged _nativeFunction; + public EnumerateMetafileProcMarshaller(EnumerateMetafileProc? managed) + { + _managed = managed is null ? null : (recordType, flags, dataSize, data, callbackData) => + managed(recordType, flags, dataSize, data, callbackData == IntPtr.Zero ? null : Marshal.GetDelegateForFunctionPointer(callbackData)) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); + } + + public delegate* unmanaged ToNativeValue() + { + return _nativeFunction; + } + + public void FreeNative() + { + GC.KeepAlive(_managed); + } + } +#endif + + /// + /// Constructor to initialize this object from a native GDI+ Graphics pointer. + /// + private Graphics(IntPtr gdipNativeGraphics) + { + if (gdipNativeGraphics == IntPtr.Zero) + throw new ArgumentNullException(nameof(gdipNativeGraphics)); + + NativeGraphics = gdipNativeGraphics; + } + + /// + /// Creates a new instance of the class from the specified handle to a device context. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdc(IntPtr hdc) + { + if (hdc == IntPtr.Zero) + throw new ArgumentNullException(nameof(hdc)); + + return FromHdcInternal(hdc); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdcInternal(IntPtr hdc) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHDC(hdc, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates a new instance of the Graphics class from the specified handle to a device context and handle to a device. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdc(IntPtr hdc, IntPtr hdevice) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHDC2(hdc, hdevice, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates a new instance of the class from a window handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHwnd(IntPtr hwnd) => FromHwndInternal(hwnd); + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHwndInternal(IntPtr hwnd) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHWND(hwnd, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates an instance of the class from an existing . + /// + public static Graphics FromImage(Image image) + { + ArgumentNullException.ThrowIfNull(image); + + if ((image.PixelFormat & PixelFormat.Indexed) != 0) + throw new ArgumentException(SR.GdiplusCannotCreateGraphicsFromIndexedPixelFormat, nameof(image)); + + Gdip.CheckStatus(Gdip.GdipGetImageGraphicsContext( + new HandleRef(image, image.nativeImage), + out IntPtr nativeGraphics)); + + return new Graphics(nativeGraphics) { _backingImage = image }; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ReleaseHdcInternal(IntPtr hdc) + { + Gdip.CheckStatus(!Gdip.Initialized ? Gdip.Ok : + Gdip.GdipReleaseDC(new HandleRef(this, NativeGraphics), hdc)); + _nativeHdc = IntPtr.Zero; + } + + /// + /// Deletes this , and frees the memory allocated for it. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { +#if DEBUG && FINALIZATION_WATCH + if (!disposing && _nativeGraphics != IntPtr.Zero) + { + Debug.WriteLine("System.Drawing.Graphics: ***************************************************"); + Debug.WriteLine("System.Drawing.Graphics: Object Disposed through finalization:\n" + allocationSite); + } +#endif + while (_previousContext != null) + { + // Dispose entire stack. + GraphicsContext? context = _previousContext.Previous; + _previousContext.Dispose(); + _previousContext = context; + } + + if (NativeGraphics != IntPtr.Zero) + { + try + { + if (_nativeHdc != IntPtr.Zero) // avoid a handle leak. + { + ReleaseHdc(); + } + + if (PrintingHelper is DeviceContext printerDC) + { + printerDC.Dispose(); + _printingHelper = null; + } + +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteGraphics(new HandleRef(this, NativeGraphics)); + +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + } + finally + { + NativeGraphics = IntPtr.Zero; + } + } + } + + ~Graphics() => Dispose(false); + + /// + /// Handle to native GDI+ graphics object. This object is created on demand. + /// + internal IntPtr NativeGraphics { get; private set; } + + public Region Clip + { + get + { + var region = new Region(); + int status = Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(region, region.NativeRegion)); + Gdip.CheckStatus(status); + + return region; + } + set => SetClip(value, CombineMode.Replace); + } + + public RectangleF ClipBounds + { + get + { + Gdip.CheckStatus(Gdip.GdipGetClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); + return rect; + } + } + + /// + /// Gets or sets the associated with this . + /// + public CompositingMode CompositingMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetCompositingMode(new HandleRef(this, NativeGraphics), out CompositingMode mode)); + return mode; + } + set + { + if (value < CompositingMode.SourceOver || value > CompositingMode.SourceCopy) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingMode)); + + Gdip.CheckStatus(Gdip.GdipSetCompositingMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public CompositingQuality CompositingQuality + { + get + { + Gdip.CheckStatus(Gdip.GdipGetCompositingQuality(new HandleRef(this, NativeGraphics), out CompositingQuality cq)); + return cq; + } + set + { + if (value < CompositingQuality.Invalid || value > CompositingQuality.AssumeLinear) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingQuality)); + + Gdip.CheckStatus(Gdip.GdipSetCompositingQuality(new HandleRef(this, NativeGraphics), value)); + } + } + + public float DpiX + { + get + { + Gdip.CheckStatus(Gdip.GdipGetDpiX(new HandleRef(this, NativeGraphics), out float dpi)); + return dpi; + } + } + + public float DpiY + { + get + { + Gdip.CheckStatus(Gdip.GdipGetDpiY(new HandleRef(this, NativeGraphics), out float dpi)); + return dpi; + } + } + + /// + /// Gets or sets the interpolation mode associated with this Graphics. + /// + public InterpolationMode InterpolationMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetInterpolationMode(new HandleRef(this, NativeGraphics), out InterpolationMode mode)); + return mode; + } + set + { + if (value < InterpolationMode.Invalid || value > InterpolationMode.HighQualityBicubic) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(InterpolationMode)); + + // GDI+ interprets the value of InterpolationMode and sets a value accordingly. + // Libgdiplus does not, so do this manually here. + switch (value) + { + case InterpolationMode.Default: + case InterpolationMode.Low: + value = InterpolationMode.Bilinear; + break; + case InterpolationMode.High: + value = InterpolationMode.HighQualityBicubic; + break; + case InterpolationMode.Invalid: + throw new ArgumentException(SR.GdiplusInvalidParameter); + default: + break; + } + + Gdip.CheckStatus(Gdip.GdipSetInterpolationMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public bool IsClipEmpty + { + get + { + Gdip.CheckStatus(Gdip.GdipIsClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); + return isEmpty; + } + } + + public bool IsVisibleClipEmpty + { + get + { + Gdip.CheckStatus(Gdip.GdipIsVisibleClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); + return isEmpty; + } + } + + public float PageScale + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPageScale(new HandleRef(this, NativeGraphics), out float scale)); + return scale; + } + set + { + // Libgdiplus doesn't perform argument validation, so do this here for compatability. + if (value <= 0 || value > 1000000032) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPageScale(new HandleRef(this, NativeGraphics), value)); + } + } + + public GraphicsUnit PageUnit + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPageUnit(new HandleRef(this, NativeGraphics), out GraphicsUnit unit)); + return unit; + } + set + { + if (value < GraphicsUnit.World || value > GraphicsUnit.Millimeter) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(GraphicsUnit)); + + // GDI+ doesn't allow GraphicsUnit.World as a valid value for PageUnit. + // Libgdiplus doesn't perform argument validation, so do this here. + if (value == GraphicsUnit.World) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPageUnit(new HandleRef(this, NativeGraphics), value)); + } + } + + public PixelOffsetMode PixelOffsetMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPixelOffsetMode(new HandleRef(this, NativeGraphics), out PixelOffsetMode mode)); + return mode; + } + set + { + if (value < PixelOffsetMode.Invalid || value > PixelOffsetMode.Half) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PixelOffsetMode)); + + // GDI+ doesn't allow PixelOffsetMode.Invalid as a valid value for PixelOffsetMode. + // Libgdiplus doesn't perform argument validation, so do this here. + if (value == PixelOffsetMode.Invalid) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPixelOffsetMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public Point RenderingOrigin + { + get + { + Gdip.CheckStatus(Gdip.GdipGetRenderingOrigin(new HandleRef(this, NativeGraphics), out int x, out int y)); + return new Point(x, y); + } + set + { + Gdip.CheckStatus(Gdip.GdipSetRenderingOrigin(new HandleRef(this, NativeGraphics), value.X, value.Y)); + } + } + + public SmoothingMode SmoothingMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetSmoothingMode(new HandleRef(this, NativeGraphics), out SmoothingMode mode)); + return mode; + } + set + { + if (value < SmoothingMode.Invalid || value > SmoothingMode.AntiAlias) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(SmoothingMode)); + + // GDI+ interprets the value of SmoothingMode and sets a value accordingly. + // Libgdiplus does not, so do this manually here. + switch (value) + { + case SmoothingMode.Default: + case SmoothingMode.HighSpeed: + value = SmoothingMode.None; + break; + case SmoothingMode.HighQuality: + value = SmoothingMode.AntiAlias; + break; + case SmoothingMode.Invalid: + throw new ArgumentException(SR.GdiplusInvalidParameter); + default: + break; + } + + Gdip.CheckStatus(Gdip.GdipSetSmoothingMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public int TextContrast + { + get + { + Gdip.CheckStatus(Gdip.GdipGetTextContrast(new HandleRef(this, NativeGraphics), out int textContrast)); + return textContrast; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetTextContrast(new HandleRef(this, NativeGraphics), value)); + } + } + + /// + /// Gets or sets the rendering mode for text associated with this . + /// + public TextRenderingHint TextRenderingHint + { + get + { + Gdip.CheckStatus(Gdip.GdipGetTextRenderingHint(new HandleRef(this, NativeGraphics), out TextRenderingHint hint)); + return hint; + } + set + { + if (value < TextRenderingHint.SystemDefault || value > TextRenderingHint.ClearTypeGridFit) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(TextRenderingHint)); + + Gdip.CheckStatus(Gdip.GdipSetTextRenderingHint(new HandleRef(this, NativeGraphics), value)); + } + } + + /// + /// Gets or sets the world transform for this . + /// + public Matrix Transform + { + get + { + var matrix = new Matrix(); + Gdip.CheckStatus(Gdip.GdipGetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix))); + + return matrix; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(value, value.NativeMatrix))); + } + } + + /// + /// Gets or sets the world transform elements for this . + /// + /// + /// This is a more performant alternative to that does not need disposal. + /// + public unsafe Matrix3x2 TransformElements + { + get + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix(out IntPtr nativeMatrix)); + + try + { + Gdip.CheckStatus(Gdip.GdipGetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); + + Matrix3x2 matrix = default; + Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(null, nativeMatrix), (float*)&matrix)); + return matrix; + } + finally + { + if (nativeMatrix != IntPtr.Zero) + { + Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); + } + } + } + set + { + IntPtr nativeMatrix = Matrix.CreateNativeHandle(value); + + try + { + Gdip.CheckStatus(Gdip.GdipSetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); + } + finally + { + if (nativeMatrix != IntPtr.Zero) + { + Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); + } + } + } + } + + public IntPtr GetHdc() + { + IntPtr hdc; + Gdip.CheckStatus(Gdip.GdipGetDC(new HandleRef(this, NativeGraphics), out hdc)); + + _nativeHdc = hdc; // need to cache the hdc to be able to release with a call to IDeviceContext.ReleaseHdc(). + return _nativeHdc; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void ReleaseHdc(IntPtr hdc) => ReleaseHdcInternal(hdc); + + public void ReleaseHdc() => ReleaseHdcInternal(_nativeHdc); + + /// + /// Forces immediate execution of all operations currently on the stack. + /// + public void Flush() => Flush(FlushIntention.Flush); + + /// + /// Forces execution of all operations currently on the stack. + /// + public void Flush(FlushIntention intention) + { + Gdip.CheckStatus(Gdip.GdipFlush(new HandleRef(this, NativeGraphics), intention)); + } + + public void SetClip(Graphics g) => SetClip(g, CombineMode.Replace); + + public void SetClip(Graphics g, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipSetClipGraphics( + new HandleRef(this, NativeGraphics), + new HandleRef(g, g.NativeGraphics), + combineMode)); + } + + public void SetClip(Rectangle rect) => SetClip(rect, CombineMode.Replace); + + public void SetClip(Rectangle rect, CombineMode combineMode) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + combineMode)); + } + + public void SetClip(RectangleF rect) => SetClip(rect, CombineMode.Replace); + + public void SetClip(RectangleF rect, CombineMode combineMode) + { + Gdip.CheckStatus(Gdip.GdipSetClipRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + combineMode)); + } + + public void SetClip(GraphicsPath path) => SetClip(path, CombineMode.Replace); + + public void SetClip(GraphicsPath path, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipSetClipPath( + new HandleRef(this, NativeGraphics), + new HandleRef(path, path._nativePath), + combineMode)); + } + + public void SetClip(Region region, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + combineMode)); + } + + public void IntersectClip(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Intersect)); + } + + public void IntersectClip(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Intersect)); + } + + public void IntersectClip(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + CombineMode.Intersect)); + } + + public void ExcludeClip(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Exclude)); + } + + public void ExcludeClip(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + CombineMode.Exclude)); + } + + public void ResetClip() + { + Gdip.CheckStatus(Gdip.GdipResetClip(new HandleRef(this, NativeGraphics))); + } + + public void TranslateClip(float dx, float dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); + } + + public void TranslateClip(int dx, int dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); + } + + public bool IsVisible(int x, int y) => IsVisible(new Point(x, y)); + + public bool IsVisible(Point point) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePointI( + new HandleRef(this, NativeGraphics), + point.X, point.Y, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y)); + + public bool IsVisible(PointF point) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePoint( + new HandleRef(this, NativeGraphics), + point.X, point.Y, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(int x, int y, int width, int height) + { + return IsVisible(new Rectangle(x, y, width, height)); + } + + public bool IsVisible(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(float x, float y, float width, float height) + { + return IsVisible(new RectangleF(x, y, width, height)); + } + + public bool IsVisible(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + out bool isVisible)); + + return isVisible; + } + + /// + /// Resets the world transform to identity. + /// + public void ResetTransform() + { + Gdip.CheckStatus(Gdip.GdipResetWorldTransform(new HandleRef(this, NativeGraphics))); + } + + /// + /// Multiplies the that represents the world transform and . + /// + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + /// + /// Multiplies the that represents the world transform and . + /// + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws + // with the libgdiplus backend. Simulate a nop for compatability with GDI+. + if (matrix.NativeMatrix == IntPtr.Zero) + return; + + Gdip.CheckStatus(Gdip.GdipMultiplyWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix), order)); + } + + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipTranslateWorldTransform(new HandleRef(this, NativeGraphics), dx, dy, order)); + } + + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipScaleWorldTransform(new HandleRef(this, NativeGraphics), sx, sy, order)); + } + + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + public void RotateTransform(float angle, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipRotateWorldTransform(new HandleRef(this, NativeGraphics), angle, order)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawArc( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, RectangleF rect, float startAngle, float sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawArcI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, Rectangle rect, float startAngle, float sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws a cubic bezier curve defined by four ordered pairs that represent points. + /// + public void DrawBezier(Pen pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawBezier( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x1, y1, x2, y2, x3, y3, x4, y4)); + } + + /// + /// Draws a cubic bezier curve defined by four points. + /// + public void DrawBezier(Pen pen, PointF pt1, PointF pt2, PointF pt3, PointF pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Draws a cubic bezier curve defined by four points. + /// + public void DrawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Draws the outline of a rectangle specified by . + /// + /// A Pen that determines the color, width, and style of the rectangle. + /// A Rectangle structure that represents the rectangle to draw. + public void DrawRectangle(Pen pen, RectangleF rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of a rectangle specified by . + /// + public void DrawRectangle(Pen pen, Rectangle rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of the specified rectangle. + /// + public void DrawRectangle(Pen pen, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawRectangle( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of the specified rectangle. + /// + public void DrawRectangle(Pen pen, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawRectangleI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outlines of a series of rectangles. + /// + public unsafe void DrawRectangles(Pen pen, RectangleF[] rects) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(rects); + + fixed (RectangleF* r = rects) + { + CheckErrorStatus(Gdip.GdipDrawRectangles( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + r, rects.Length)); + } + } + + /// + /// Draws the outlines of a series of rectangles. + /// + public unsafe void DrawRectangles(Pen pen, Rectangle[] rects) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(rects); + + fixed (Rectangle* r = rects) + { + CheckErrorStatus(Gdip.GdipDrawRectanglesI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + r, rects.Length)); + } + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, RectangleF rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawEllipse( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of an ellipse specified by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, Rectangle rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawEllipseI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, RectangleF rect, float startAngle, float sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawPie( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, Rectangle rect, float startAngle, float sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawPieI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws the outline of a polygon defined by an array of points. + /// + public unsafe void DrawPolygon(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawPolygon( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws the outline of a polygon defined by an array of points. + /// + public unsafe void DrawPolygon(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawPolygonI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws the lines and curves defined by a . + /// + public void DrawPath(Pen pen, GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(path); + + CheckErrorStatus(Gdip.GdipDrawPath( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + new HandleRef(path, path._nativePath))); + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments) + { + DrawCurve(pen, points, offset, numberOfSegments, 0.5f); + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve3( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + offset, + numberOfSegments, + tension)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve3I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + offset, + numberOfSegments, + tension)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, PointF[] points, float tension, FillMode fillmode) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, Point[] points, float tension, FillMode fillmode) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Fills the entire drawing surface with the specified color. + /// + public void Clear(Color color) + { + Gdip.CheckStatus(Gdip.GdipGraphicsClear(new HandleRef(this, NativeGraphics), color.ToArgb())); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, RectangleF rect) + { + FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillRectangle( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, Rectangle rect) + { + FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillRectangleI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interiors of a series of rectangles with a . + /// + public unsafe void FillRectangles(Brush brush, RectangleF[] rects) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(rects); + + fixed (RectangleF* r = rects) + { + CheckErrorStatus(Gdip.GdipFillRectangles( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + r, rects.Length)); + } + } + + /// + /// Fills the interiors of a series of rectangles with a . + /// + public unsafe void FillRectangles(Brush brush, Rectangle[] rects) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(rects); + + fixed (Rectangle* r = rects) + { + CheckErrorStatus(Gdip.GdipFillRectanglesI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + r, rects.Length)); + } + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public void FillPolygon(Brush brush, PointF[] points) + { + FillPolygon(brush, points, FillMode.Alternate); + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public unsafe void FillPolygon(Brush brush, PointF[] points, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillPolygon( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + fillMode)); + } + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public void FillPolygon(Brush brush, Point[] points) + { + FillPolygon(brush, points, FillMode.Alternate); + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public unsafe void FillPolygon(Brush brush, Point[] points, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillPolygonI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + fillMode)); + } + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, RectangleF rect) + { + FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillEllipse( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, Rectangle rect) + { + FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillEllipseI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, Rectangle rect, float startAngle, float sweepAngle) + { + FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + /// A Brush that determines the characteristics of the fill. + /// A Rectangle structure that represents the bounding rectangle that defines the ellipse from which the pie section comes. + /// Angle in degrees measured clockwise from the x-axis to the first side of the pie section. + /// Angle in degrees measured clockwise from the parameter to the second side of the pie section. + public void FillPie(Brush brush, RectangleF rect, float startAngle, float sweepAngle) + { + FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillPie( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillPieI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Fills the interior a closed curve defined by an array of points. + /// + public unsafe void FillClosedCurve(Brush brush, PointF[] points) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length)); + } + } + + /// + /// Fills the interior of a closed curve defined by an array of points. + /// + public void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode) + { + FillClosedCurve(brush, points, fillmode, 0.5f); + } + + public unsafe void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode, float tension) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + tension, + fillmode)); + } + } + + /// + /// Fills the interior a closed curve defined by an array of points. + /// + public unsafe void FillClosedCurve(Brush brush, Point[] points) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length)); + } + } + + public void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode) + { + FillClosedCurve(brush, points, fillmode, 0.5f); + } + + public unsafe void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode, float tension) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + tension, + fillmode)); + } + } + + /// + /// Draws a string with the specified font. + /// + public void DrawString(string? s, Font font, Brush brush, float x, float y) + { + DrawString(s, font, brush, new RectangleF(x, y, 0, 0), null); + } + + public void DrawString(string? s, Font font, Brush brush, PointF point) + { + DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), null); + } + + public void DrawString(string? s, Font font, Brush brush, float x, float y, StringFormat? format) + { + DrawString(s, font, brush, new RectangleF(x, y, 0, 0), format); + } + + public void DrawString(string? s, Font font, Brush brush, PointF point, StringFormat? format) + { + DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), format); + } + + public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle) + { + DrawString(s, font, brush, layoutRectangle, null); + } + + public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat? format) + { + ArgumentNullException.ThrowIfNull(brush); + if (string.IsNullOrEmpty(s)) + return; + ArgumentNullException.ThrowIfNull(font); + + CheckErrorStatus(Gdip.GdipDrawString( + new HandleRef(this, NativeGraphics), + s, + s.Length, + new HandleRef(font, font.NativeFont), + ref layoutRectangle, + new HandleRef(format, format?.nativeFormat ?? IntPtr.Zero), + new HandleRef(brush, brush.NativeBrush))); + } + + public SizeF MeasureString( + string? text, + Font font, + SizeF layoutArea, + StringFormat? stringFormat, + out int charactersFitted, + out int linesFilled) + { + if (string.IsNullOrEmpty(text)) + { + charactersFitted = 0; + linesFilled = 0; + return SizeF.Empty; + } + + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out charactersFitted, + out linesFilled)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font, PointF origin, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return SizeF.Empty; + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(origin.X, origin.Y, 0, 0); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out _, + out _)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font, SizeF layoutArea) => MeasureString(text, font, layoutArea, null); + + public SizeF MeasureString(string? text, Font font, SizeF layoutArea, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return SizeF.Empty; + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out _, + out _)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font) + { + return MeasureString(text, font, new SizeF(0, 0)); + } + + public SizeF MeasureString(string? text, Font font, int width) + { + return MeasureString(text, font, new SizeF(width, 999999)); + } + + public SizeF MeasureString(string? text, Font font, int width, StringFormat? format) + { + return MeasureString(text, font, new SizeF(width, 999999), format); + } + + public Region[] MeasureCharacterRanges(string? text, Font font, RectangleF layoutRect, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return Array.Empty(); + if (font == null) + throw new ArgumentNullException(nameof(font)); + + Gdip.CheckStatus(Gdip.GdipGetStringFormatMeasurableCharacterRangeCount( + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + out int count)); + + IntPtr[] gpRegions = new IntPtr[count]; + Region[] regions = new Region[count]; + + for (int f = 0; f < count; f++) + { + regions[f] = new Region(); + gpRegions[f] = regions[f].NativeRegion; + } + + Gdip.CheckStatus(Gdip.GdipMeasureCharacterRanges( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layoutRect, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + count, + gpRegions)); + + return regions; + } + + /// + /// Draws the specified image at the specified location. + /// + public void DrawImage(Image image, PointF point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImage(Image image, float x, float y) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImage( + new HandleRef(this, NativeGraphics), new HandleRef(image, image.nativeImage), + x, y); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, RectangleF rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } + + public void DrawImage(Image image, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + width, height); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Point point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImage(Image image, int x, int y) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Rectangle rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } + + public void DrawImage(Image image, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + width, height); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImageUnscaled(Image image, Point point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImageUnscaled(Image image, int x, int y) + { + DrawImage(image, x, y); + } + + public void DrawImageUnscaled(Image image, Rectangle rect) + { + DrawImage(image, rect.X, rect.Y); + } + + public void DrawImageUnscaled(Image image, int x, int y, int width, int height) + { + DrawImage(image, x, y); + } + + public void DrawImageUnscaledAndClipped(Image image, Rectangle rect) + { + ArgumentNullException.ThrowIfNull(image); + + int width = Math.Min(rect.Width, image.Width); + int height = Math.Min(rect.Height, image.Height); + + // We could put centering logic here too for the case when the image + // is smaller than the rect. + DrawImage(image, rect, 0, 0, width, height, GraphicsUnit.Pixel); + } + + // Affine or perspective blt + // destPoints.Length = 3: rect => parallelogram + // destPoints[0] <=> top-left corner of the source rectangle + // destPoints[1] <=> top-right corner + // destPoints[2] <=> bottom-left corner + // destPoints.Length = 4: rect => quad + // destPoints[3] <=> bottom-right corner + // + // @notes Perspective blt only works for bitmap images. + + public unsafe void DrawImage(Image image, PointF[] destPoints) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePoints( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, count); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public unsafe void DrawImage(Image image, Point[] destPoints) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (Point* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, count); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, float x, float y, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImagePointRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + (int)srcUnit); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, int x, int y, Rectangle srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImagePointRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + (int)srcUnit); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Rectangle destRect, Rectangle srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public unsafe void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes? imageAttr) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); + } + + public void DrawImage( + Image image, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); + } + + public unsafe void DrawImage( + Image image, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback, + int callbackData) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, (IntPtr)callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit) + { + DrawImage(image, destPoints, srcRect, srcUnit, null, null, 0); + } + + public void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); + } + + public void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); + } + + public unsafe void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback, + int callbackData) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (Point* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, (IntPtr)callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, callback, IntPtr.Zero); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback, + IntPtr callbackData) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcX, srcY, srcWidth, srcHeight, + srcUnit, + new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, callback, IntPtr.Zero); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback, + IntPtr callbackData) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcX, srcY, srcWidth, srcHeight, + srcUnit, + new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, PointF pt1, PointF pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Draws a series of line segments that connect an array of points. + /// + public unsafe void DrawLines(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawLines( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, int x1, int y1, int x2, int y2) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawLineI(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, Point pt1, Point pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Draws a series of line segments that connect an array of points. + /// + public unsafe void DrawLines(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawLinesI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); + } + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. + /// + public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize) + { + CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize); + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. + /// + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize) + { + CopyFromScreen(sourceX, sourceY, destinationX, destinationY, blockRegionSize, CopyPixelOperation.SourceCopy); + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size + /// and specified raster operation. + /// + public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize, CopyPixelOperation copyPixelOperation) + { + CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize, copyPixelOperation); + } + + public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, PointF[] destPoints, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero, null); + } + + public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); + } + + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (PointF* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformPoints( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); + } + } + + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (Point* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformPointsI( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); + } + } + + /// + /// GDI+ will return a 'generic error' when we attempt to draw an Emf + /// image with width/height == 1. Here, we will hack around this by + /// resetting the errorstatus. Note that we don't do simple arg checking + /// for height || width == 1 here because transforms can be applied to + /// the Graphics object making it difficult to identify this scenario. + /// + private static void IgnoreMetafileErrors(Image image, ref int errorStatus) + { + if (errorStatus != Gdip.Ok && image.RawFormat.Equals(ImageFormat.Emf)) + errorStatus = Gdip.Ok; + } + + /// + /// Creates a Region class only if the native region is not infinite. + /// + internal Region? GetRegionIfNotInfinite() + { + Gdip.CheckStatus(Gdip.GdipCreateRegion(out IntPtr regionHandle)); + try + { + Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(null, regionHandle)); + Gdip.CheckStatus(Gdip.GdipIsInfiniteRegion( + new HandleRef(null, regionHandle), + new HandleRef(this, NativeGraphics), + out int isInfinite)); + + if (isInfinite != 0) + { + // Infinite + return null; + } + + Region region = new Region(regionHandle); + regionHandle = IntPtr.Zero; + return region; + } + finally + { + if (regionHandle != IntPtr.Zero) + { + Gdip.GdipDeleteRegion(new HandleRef(null, regionHandle)); + } + } + } + + /// + /// Represents an object used in connection with the printing API, it is used to hold a reference to a + /// PrintPreviewGraphics (fake graphics) or a printer DeviceContext (and maybe more in the future). + /// + internal object? PrintingHelper + { + get => _printingHelper; + set + { + Debug.Assert(_printingHelper == null, "WARNING: Overwritting the printing helper reference!"); + _printingHelper = value; + } + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size + /// and specified raster operation. + /// + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) + { + switch (copyPixelOperation) + { + case CopyPixelOperation.Blackness: + case CopyPixelOperation.NotSourceErase: + case CopyPixelOperation.NotSourceCopy: + case CopyPixelOperation.SourceErase: + case CopyPixelOperation.DestinationInvert: + case CopyPixelOperation.PatInvert: + case CopyPixelOperation.SourceInvert: + case CopyPixelOperation.SourceAnd: + case CopyPixelOperation.MergePaint: + case CopyPixelOperation.MergeCopy: + case CopyPixelOperation.SourceCopy: + case CopyPixelOperation.SourcePaint: + case CopyPixelOperation.PatCopy: + case CopyPixelOperation.PatPaint: + case CopyPixelOperation.Whiteness: + case CopyPixelOperation.CaptureBlt: + case CopyPixelOperation.NoMirrorBitmap: + break; + default: + throw new InvalidEnumArgumentException(nameof(copyPixelOperation), (int)copyPixelOperation, typeof(CopyPixelOperation)); + } + + int destWidth = blockRegionSize.Width; + int destHeight = blockRegionSize.Height; + + IntPtr screenDC = Interop.User32.GetDC(IntPtr.Zero); + try + { + IntPtr targetDC = GetHdc(); + int result = Interop.Gdi32.BitBlt( + targetDC, + destinationX, + destinationY, + destWidth, + destHeight, + screenDC, + sourceX, + sourceY, + (Interop.Gdi32.RasterOp)copyPixelOperation); + + //a zero result indicates a win32 exception has been thrown + if (result == 0) + { + throw new Win32Exception(); + } + } + finally + { + Interop.User32.ReleaseDC(IntPtr.Zero, screenDC); + ReleaseHdc(); + } + } + + public Color GetNearestColor(Color color) + { + int nearest = color.ToArgb(); + Gdip.CheckStatus(Gdip.GdipGetNearestColor(new HandleRef(this, NativeGraphics), ref nearest)); + return Color.FromArgb(nearest); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, float x1, float y1, float x2, float y2) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawLine(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); + } + + /// + /// Draws a series of cubic Bezier curves from an array of points. + /// + public unsafe void DrawBeziers(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawBeziers( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a series of cubic Bezier curves from an array of points. + /// + public unsafe void DrawBeziers(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawBeziersI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); + } + } + + /// + /// Fills the interior of a path. + /// + public void FillPath(Brush brush, GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(path); + + CheckErrorStatus(Gdip.GdipFillPath( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + new HandleRef(path, path._nativePath))); + } + + /// + /// Fills the interior of a . + /// + public void FillRegion(Brush brush, Region region) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(region); + + CheckErrorStatus(Gdip.GdipFillRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + new HandleRef(region, region.NativeRegion))); + } + + public void DrawIcon(Icon icon, int x, int y) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + // We don't call the icon directly because we want to stay in GDI+ all the time + // to avoid alpha channel interop issues between gdi and gdi+ + // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. + DrawImage(icon.ToBitmap(), x, y); + } + else + { + icon.Draw(this, x, y); + } + } + + /// + /// Draws this image to a graphics object. The drawing command originates on the graphics + /// object, but a graphics object generally has no idea how to render a given image. So, + /// it passes the call to the actual image. This version crops the image to the given + /// dimensions and allows the user to specify a rectangle within the image to draw. + /// + public void DrawIcon(Icon icon, Rectangle targetRect) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + // We don't call the icon directly because we want to stay in GDI+ all the time + // to avoid alpha channel interop issues between gdi and gdi+ + // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. + DrawImage(icon.ToBitmap(), targetRect); + } + else + { + icon.Draw(this, targetRect); + } + } + + /// + /// Draws this image to a graphics object. The drawing command originates on the graphics + /// object, but a graphics object generally has no idea how to render a given image. So, + /// it passes the call to the actual image. This version stretches the image to the given + /// dimensions and allows the user to specify a rectangle within the image to draw. + /// + public void DrawIconUnstretched(Icon icon, Rectangle targetRect) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + DrawImageUnscaled(icon.ToBitmap(), targetRect); + } + else + { + icon.DrawUnstretched(this, targetRect); + } + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoint( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRect( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (PointF* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoints( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (Point* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoint( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRect( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (PointF* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoints( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (Point* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + /// + /// Combines current Graphics context with all previous contexts. + /// When BeginContainer() is called, a copy of the current context is pushed into the GDI+ context stack, it keeps track of the + /// absolute clipping and transform but reset the public properties so it looks like a brand new context. + /// When Save() is called, a copy of the current context is also pushed in the GDI+ stack but the public clipping and transform + /// properties are not reset (cumulative). Consecutive Save context are ignored with the exception of the top one which contains + /// all previous information. + /// The return value is an object array where the first element contains the cumulative clip region and the second the cumulative + /// translate transform matrix. + /// WARNING: This method is for internal FX support only. + /// + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETCOREAPP3_1_OR_GREATER + [Obsolete(Obsoletions.GetContextInfoMessage, DiagnosticId = Obsoletions.GetContextInfoDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] +#endif + [SupportedOSPlatform("windows")] + public object GetContextInfo() + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out Region? cumulativeClip); + return new object[] { cumulativeClip ?? new Region(), new Matrix(cumulativeTransform) }; + } + + private void GetContextInfo(out Matrix3x2 cumulativeTransform, bool calculateClip, out Region? cumulativeClip) + { + cumulativeClip = calculateClip ? GetRegionIfNotInfinite() : null; // Current context clip. + cumulativeTransform = TransformElements; // Current context transform. + Vector2 currentOffset = default; // Offset of current context. + Vector2 totalOffset = default; // Absolute coordinate offset of top context. + + GraphicsContext? context = _previousContext; + + if (!cumulativeTransform.IsIdentity) + { + currentOffset = cumulativeTransform.Translation; + } + + while (context is not null) + { + if (!context.TransformOffset.IsEmpty()) + { + cumulativeTransform.Translate(context.TransformOffset); + } + + if (!currentOffset.IsEmpty()) + { + // The location of the GDI+ clip region is relative to the coordinate origin after any translate transform + // has been applied. We need to intersect regions using the same coordinate origin relative to the previous + // context. + + // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. + cumulativeClip?.Translate(currentOffset.X, currentOffset.Y); + totalOffset.X += currentOffset.X; + totalOffset.Y += currentOffset.Y; + } + + // Context only stores clips if they are not infinite. Intersecting a clip with an infinite clip is a no-op. + if (calculateClip && context.Clip is not null) + { + // Intersecting an infinite clip with another is just a copy of the second clip. + if (cumulativeClip is null) + { + cumulativeClip = context.Clip; + } + else + { + cumulativeClip.Intersect(context.Clip); + } + } + + currentOffset = context.TransformOffset; + + // Ignore subsequent cumulative contexts. + do + { + context = context.Previous; + + if (context == null || !context.Next!.IsCumulative) + { + break; + } + } while (context.IsCumulative); + } + + if (!totalOffset.IsEmpty()) + { + // We need now to reset the total transform in the region so when calling Region.GetHRgn(Graphics) + // the HRegion is properly offset by GDI+ based on the total offset of the graphics object. + + // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. + cumulativeClip?.Translate(-totalOffset.X, -totalOffset.Y); + } + } + +#if NETCOREAPP3_1_OR_GREATER + /// + /// Gets the cumulative offset. + /// + /// The cumulative offset. + [EditorBrowsable(EditorBrowsableState.Never)] + [SupportedOSPlatform("windows")] + public void GetContextInfo(out PointF offset) + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: false, out _); + Vector2 translation = cumulativeTransform.Translation; + offset = new PointF(translation.X, translation.Y); + } + + /// + /// Gets the cumulative offset and clip region. + /// + /// The cumulative offset. + /// The cumulative clip region or null if the clip region is infinite. + [EditorBrowsable(EditorBrowsableState.Never)] + [SupportedOSPlatform("windows")] + public void GetContextInfo(out PointF offset, out Region? clip) + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out clip); + Vector2 translation = cumulativeTransform.Translation; + offset = new PointF(translation.X, translation.Y); + } +#endif + + public RectangleF VisibleClipBounds + { + get + { + if (PrintingHelper is PrintPreviewGraphics ppGraphics) + return ppGraphics.VisibleClipBounds; + + Gdip.CheckStatus(Gdip.GdipGetVisibleClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); + + return rect; + } + } + + /// + /// Saves the current context into the context stack. + /// + private void PushContext(GraphicsContext context) + { + Debug.Assert(context != null && context.State != 0, "GraphicsContext object is null or not valid."); + + if (_previousContext != null) + { + // Push context. + context.Previous = _previousContext; + _previousContext.Next = context; + } + _previousContext = context; + } + + /// + /// Pops all contexts from the specified one included. The specified context is becoming the current context. + /// + private void PopContext(int currentContextState) + { + Debug.Assert(_previousContext != null, "Trying to restore a context when the stack is empty"); + GraphicsContext? context = _previousContext; + + // Pop all contexts up the stack. + while (context != null) + { + if (context.State == currentContextState) + { + _previousContext = context.Previous; + + // This will dipose all context object up the stack. + context.Dispose(); + return; + } + context = context.Previous; + } + Debug.Fail("Warning: context state not found!"); + } + + public GraphicsState Save() + { + GraphicsContext context = new GraphicsContext(this); + int status = Gdip.GdipSaveGraphics(new HandleRef(this, NativeGraphics), out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + context.IsCumulative = true; + PushContext(context); + + return new GraphicsState(state); + } + + public void Restore(GraphicsState gstate) + { + Gdip.CheckStatus(Gdip.GdipRestoreGraphics(new HandleRef(this, NativeGraphics), gstate.nativeState)); + PopContext(gstate.nativeState); + } + + public GraphicsContainer BeginContainer(RectangleF dstrect, RectangleF srcrect, GraphicsUnit unit) + { + GraphicsContext context = new GraphicsContext(this); + + int status = Gdip.GdipBeginContainer( + new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public GraphicsContainer BeginContainer() + { + GraphicsContext context = new GraphicsContext(this); + int status = Gdip.GdipBeginContainer2(new HandleRef(this, NativeGraphics), out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public void EndContainer(GraphicsContainer container) + { + ArgumentNullException.ThrowIfNull(container); + + Gdip.CheckStatus(Gdip.GdipEndContainer(new HandleRef(this, NativeGraphics), container.nativeGraphicsContainer)); + PopContext(container.nativeGraphicsContainer); + } + + public GraphicsContainer BeginContainer(Rectangle dstrect, Rectangle srcrect, GraphicsUnit unit) + { + GraphicsContext context = new GraphicsContext(this); + + int status = Gdip.GdipBeginContainerI( + new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public void AddMetafileComment(byte[] data) + { + ArgumentNullException.ThrowIfNull(data); + + Gdip.CheckStatus(Gdip.GdipComment(new HandleRef(this, NativeGraphics), data.Length, data)); + } + + public static IntPtr GetHalftonePalette() + { + if (s_halftonePalette == IntPtr.Zero) + { + lock (s_syncObject) + { + if (s_halftonePalette == IntPtr.Zero) + { + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + AppDomain.CurrentDomain.ProcessExit += OnDomainUnload; + + s_halftonePalette = Gdip.GdipCreateHalftonePalette(); + } + } + } + return s_halftonePalette; + } + + // This is called from AppDomain.ProcessExit and AppDomain.DomainUnload. + private static void OnDomainUnload(object? sender, EventArgs e) + { + if (s_halftonePalette != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(s_halftonePalette); + s_halftonePalette = IntPtr.Zero; + } + } + + /// + /// GDI+ will return a 'generic error' with specific win32 last error codes when + /// a terminal server session has been closed, minimized, etc... We don't want + /// to throw when this happens, so we'll guard against this by looking at the + /// 'last win32 error code' and checking to see if it is either 1) access denied + /// or 2) proc not found and then ignore it. + /// + /// The problem is that when you lock the machine, the secure desktop is enabled and + /// rendering fails which is expected (since the app doesn't have permission to draw + /// on the secure desktop). Not sure if there's anything you can do, short of catching + /// the desktop switch message and absorbing all the exceptions that get thrown while + /// it's the secure desktop. + /// + private static void CheckErrorStatus(int status) + { + if (status == Gdip.Ok) + return; + + // Generic error from GDI+ can be GenericError or Win32Error. + if (status == Gdip.GenericError || status == Gdip.Win32Error) + { + int error = Marshal.GetLastWin32Error(); + if (error == SafeNativeMethods.ERROR_ACCESS_DENIED || error == SafeNativeMethods.ERROR_PROC_NOT_FOUND || + // Here, we'll check to see if we are in a terminal services session... + (((Interop.User32.GetSystemMetrics(NativeMethods.SM_REMOTESESSION) & 0x00000001) != 0) && (error == 0))) + { + return; + } + } + + // Legitimate error, throw our status exception. + throw Gdip.StatusException(status); + } + } +}