From 066662cfaca941c96aec4fe4e2b1bec8ed195d2c Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Sun, 3 Jun 2018 23:26:58 -0700 Subject: [PATCH 1/3] System.Drawing is doing a lot of unnecessary work. This is a start on trying to remove some of it. This change is meant to show the general pattern I intend to follow. - Fixes the managed array and uses it directly instead of copying multiple times - Adds some basic sanity tests to validate that we're getting the expected output - Splits existing negative tests into new files with sanity tests - Adds a performance project Output tests were generated and run before making the functional change. --- .../System.Drawing.Common.sln | 10 + .../src/System.Drawing.Common.csproj | 5 +- .../Drawing/Drawing2D/GraphicsPath.Windows.cs | 67 ++-- .../Drawing/Drawing2D/GraphicsPathIterator.cs | 67 ++-- .../src/System/Drawing/Drawing2D/Matrix.cs | 98 ++---- .../src/System/Drawing/Gdiplus.cs | 63 +--- .../System/Drawing/GdiplusNative.Windows.cs | 37 +-- .../src/System/Drawing/GdiplusNative.cs | 26 +- .../src/System/Drawing/Graphics.Windows.cs | 115 +++---- .../src/System/Drawing/Internal/GpPathData.cs | 16 + .../tests/DrawingTest.cs | 25 ++ .../tests/GraphicsTests.cs | 310 ------------------ .../tests/Graphics_DrawBezierTests.cs | 220 +++++++++++++ .../tests/Graphics_DrawLineTests.cs | 195 +++++++++++ .../tests/Performance/Configurations.props | 8 + .../Performance/Perf_Graphics_DrawBeziers.cs | 61 ++++ .../Performance/Perf_Graphics_Transforms.cs | 41 +++ ...em.Drawing.Common.Performance.Tests.csproj | 32 ++ .../tests/System.Drawing.Common.Tests.csproj | 3 + 19 files changed, 763 insertions(+), 636 deletions(-) create mode 100644 src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs create mode 100644 src/System.Drawing.Common/tests/DrawingTest.cs create mode 100644 src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs create mode 100644 src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs create mode 100644 src/System.Drawing.Common/tests/Performance/Configurations.props create mode 100644 src/System.Drawing.Common/tests/Performance/Perf_Graphics_DrawBeziers.cs create mode 100644 src/System.Drawing.Common/tests/Performance/Perf_Graphics_Transforms.cs create mode 100644 src/System.Drawing.Common/tests/Performance/System.Drawing.Common.Performance.Tests.csproj diff --git a/src/System.Drawing.Common/System.Drawing.Common.sln b/src/System.Drawing.Common/System.Drawing.Common.sln index fe67e93fc7d3..1ef8d5b29cd3 100644 --- a/src/System.Drawing.Common/System.Drawing.Common.sln +++ b/src/System.Drawing.Common/System.Drawing.Common.sln @@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Drawing.Common.Tests {191B3618-FECD-4ABD-9D6B-5AC90DC33621} = {191B3618-FECD-4ABD-9D6B-5AC90DC33621} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Drawing.Common.Performance.Tests", "tests\Performance\System.Drawing.Common.Performance.Tests.csproj", "{E66FFA55-0975-4F0D-8A18-24B2687FEDEA}" + ProjectSection(ProjectDependencies) = postProject + {191B3618-FECD-4ABD-9D6B-5AC90DC33621} = {191B3618-FECD-4ABD-9D6B-5AC90DC33621} + EndProjectSection +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Drawing.Common", "src\System.Drawing.Common.csproj", "{191B3618-FECD-4ABD-9D6B-5AC90DC33621}" ProjectSection(ProjectDependencies) = postProject {D7AEA698-275D-441F-B7A7-8491D1F0EFF0} = {D7AEA698-275D-441F-B7A7-8491D1F0EFF0} @@ -30,6 +35,10 @@ Global {4B93E684-0630-45F4-8F63-6C7788C9892F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU {4B93E684-0630-45F4-8F63-6C7788C9892F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU {4B93E684-0630-45F4-8F63-6C7788C9892F}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU {191B3618-FECD-4ABD-9D6B-5AC90DC33621}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU @@ -44,6 +53,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {4B93E684-0630-45F4-8F63-6C7788C9892F} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {191B3618-FECD-4ABD-9D6B-5AC90DC33621} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {D7AEA698-275D-441F-B7A7-8491D1F0EFF0} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} EndGlobalSection diff --git a/src/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/System.Drawing.Common/src/System.Drawing.Common.csproj index 269445e1e72d..001a950815ed 100644 --- a/src/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -378,5 +378,8 @@ + + + - + \ No newline at end of file diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs index b819ec198cff..977bfbea2b65 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.Windows.cs @@ -183,46 +183,36 @@ public FillMode FillMode } } - private PathData _GetPathData() + private unsafe PathData _GetPathData() { - int ptSize = Marshal.SizeOf(typeof(GPPOINTF)); + int count = PointCount; - int numPts = PointCount; + PathData pathData = new PathData() + { + Types = new byte[count], + Points = new PointF[count] + }; - PathData pathData = new PathData() { Types = new byte[numPts] }; + if (count == 0) + return pathData; - IntPtr memoryPathData = Marshal.AllocHGlobal(3 * IntPtr.Size); - IntPtr memoryPoints = Marshal.AllocHGlobal(checked(ptSize * numPts)); - try + fixed (byte* t = pathData.Types) + fixed (PointF* p = pathData.Points) { - GCHandle typesHandle = GCHandle.Alloc(pathData.Types, GCHandleType.Pinned); - try + GpPathData data = new GpPathData { - IntPtr typesPtr = typesHandle.AddrOfPinnedObject(); + Count = count, + Points = p, + Types = t + }; - Marshal.StructureToPtr(numPts, memoryPathData, false); - Marshal.StructureToPtr(memoryPoints, (IntPtr)((long)memoryPathData + IntPtr.Size), false); - Marshal.StructureToPtr(typesPtr, (IntPtr)((long)memoryPathData + 2 * IntPtr.Size), false); - - int status = SafeNativeMethods.Gdip.GdipGetPathData(new HandleRef(this, nativePath), memoryPathData); - - if (status != SafeNativeMethods.Gdip.Ok) - { - throw SafeNativeMethods.Gdip.StatusException(status); - } + int status = SafeNativeMethods.Gdip.GdipGetPathData(new HandleRef(this, nativePath), &data); - pathData.Points = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(memoryPoints, numPts); - } - finally + if (status != SafeNativeMethods.Gdip.Ok) { - typesHandle.Free(); + throw SafeNativeMethods.Gdip.StatusException(status); } } - finally - { - Marshal.FreeHGlobal(memoryPathData); - Marshal.FreeHGlobal(memoryPoints); - } return pathData; } @@ -1094,29 +1084,22 @@ public byte[] PathTypes return types; } } - public PointF[] PathPoints + + public unsafe PointF[] PathPoints { get { - int count = PointCount; - int size = Marshal.SizeOf(typeof(GPPOINTF)); - IntPtr buf = Marshal.AllocHGlobal(checked(count * size)); - try + PointF[] points = new PointF[PointCount]; + fixed(PointF* p = points) { - int status = SafeNativeMethods.Gdip.GdipGetPathPoints(new HandleRef(this, nativePath), new HandleRef(null, buf), count); + int status = SafeNativeMethods.Gdip.GdipGetPathPoints(new HandleRef(this, nativePath), p, points.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - - PointF[] points = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(buf, count); - return points; - } - finally - { - Marshal.FreeHGlobal(buf); } + return points; } } } diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs index 2e59504744ab..b30d67afb4a7 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs @@ -3,9 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Drawing.Internal; using System.Globalization; +using System.Runtime.InteropServices; namespace System.Drawing.Drawing2D { @@ -169,36 +168,26 @@ public unsafe int Enumerate(ref PointF[] points, ref byte[] types) if (points.Length != types.Length) throw SafeNativeMethods.Gdip.StatusException(SafeNativeMethods.Gdip.InvalidParameter); - int resultCount = 0; - int size = Marshal.SizeOf(typeof(GPPOINTF)); - int count = points.Length; - byte[] typesLocal = new byte[count]; + if (points.Length == 0) + return 0; - IntPtr memoryPts = Marshal.AllocHGlobal(checked(count * size)); - try + fixed (PointF* p = points) + fixed (byte* t = types) { - int status = SafeNativeMethods.Gdip.GdipPathIterEnumerate(new HandleRef(this, nativeIter), out resultCount, - memoryPts, typesLocal, count); + int status = SafeNativeMethods.Gdip.GdipPathIterEnumerate( + new HandleRef(this, nativeIter), + out int resultCount, + p, + t, + points.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - if (resultCount < count) - { - SafeNativeMethods.ZeroMemory((byte*)(checked((long)memoryPts + resultCount * size)), (ulong)((count - resultCount) * size)); - } - - points = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(memoryPts, count); - typesLocal.CopyTo(types, 0); - } - finally - { - Marshal.FreeHGlobal(memoryPts); + return resultCount; } - - return resultCount; } public unsafe int CopyData(ref PointF[] points, ref byte[] types, int startIndex, int endIndex) @@ -206,36 +195,24 @@ public unsafe int CopyData(ref PointF[] points, ref byte[] types, int startIndex if ((points.Length != types.Length) || (endIndex - startIndex + 1 > points.Length)) throw SafeNativeMethods.Gdip.StatusException(SafeNativeMethods.Gdip.InvalidParameter); - int resultCount = 0; - int size = Marshal.SizeOf(typeof(GPPOINTF)); - int count = points.Length; - byte[] typesLocal = new byte[count]; - - IntPtr memoryPts = Marshal.AllocHGlobal(checked(count * size)); - try + fixed (PointF* p = points) + fixed (byte* t = types) { - int status = SafeNativeMethods.Gdip.GdipPathIterCopyData(new HandleRef(this, nativeIter), out resultCount, - memoryPts, typesLocal, startIndex, endIndex); + int status = SafeNativeMethods.Gdip.GdipPathIterCopyData( + new HandleRef(this, nativeIter), + out int resultCount, + p, + t, + startIndex, + endIndex); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - if (resultCount < count) - { - SafeNativeMethods.ZeroMemory((byte*)(checked((long)memoryPts + resultCount * size)), (ulong)((count - resultCount) * size)); - } - - points = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(memoryPts, count); - typesLocal.CopyTo(types, 0); - } - finally - { - Marshal.FreeHGlobal(memoryPts); + return resultCount; } - - return resultCount; } // handle to native path iterator object diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs index 4bd39c992c89..9624a6993890 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs @@ -261,134 +261,80 @@ public void Invert() throw SafeNativeMethods.Gdip.StatusException(status); } - public void TransformPoints(PointF[] pts) + public unsafe void TransformPoints(PointF[] pts) { if (pts == null) - throw new ArgumentNullException("pts"); - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); + throw new ArgumentNullException(nameof(pts)); - try + fixed(PointF* p = pts) { - int status = SafeNativeMethods.Gdip.GdipTransformMatrixPoints(new HandleRef(this, nativeMatrix), - new HandleRef(null, buf), + int status = SafeNativeMethods.Gdip.GdipTransformMatrixPoints( + new HandleRef(this, nativeMatrix), + p, pts.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - - PointF[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(buf, pts.Length); - - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally - { - Marshal.FreeHGlobal(buf); } } - public void TransformPoints(Point[] pts) + public unsafe void TransformPoints(Point[] pts) { if (pts == null) - throw new ArgumentNullException("pts"); - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); + throw new ArgumentNullException(nameof(pts)); - try + fixed (Point* p = pts) { int status = SafeNativeMethods.Gdip.GdipTransformMatrixPointsI(new HandleRef(this, nativeMatrix), - new HandleRef(null, buf), + p, pts.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - - // must do an in-place copy because we only have a reference - Point[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTArray(buf, pts.Length); - - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally - { - Marshal.FreeHGlobal(buf); } } - public void TransformVectors(PointF[] pts) + public unsafe void TransformVectors(PointF[] pts) { if (pts == null) - { - throw new ArgumentNullException("pts"); - } - - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); + throw new ArgumentNullException(nameof(pts)); - try + fixed (PointF* p = pts) { - int status = SafeNativeMethods.Gdip.GdipVectorTransformMatrixPoints(new HandleRef(this, nativeMatrix), - new HandleRef(null, buf), + int status = SafeNativeMethods.Gdip.GdipVectorTransformMatrixPoints( + new HandleRef(this, nativeMatrix), + p, pts.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - - // must do an in-place copy because we only have a reference - PointF[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(buf, pts.Length); - - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally - { - Marshal.FreeHGlobal(buf); } } public void VectorTransformPoints(Point[] pts) => TransformVectors(pts); - public void TransformVectors(Point[] pts) + public unsafe void TransformVectors(Point[] pts) { if (pts == null) - { - throw new ArgumentNullException("pts"); - } + throw new ArgumentNullException(nameof(pts)); - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); - - try + fixed(Point* p = pts) { - int status = SafeNativeMethods.Gdip.GdipVectorTransformMatrixPointsI(new HandleRef(this, nativeMatrix), - new HandleRef(null, buf), + int status = SafeNativeMethods.Gdip.GdipVectorTransformMatrixPointsI( + new HandleRef(this, nativeMatrix), + p, pts.Length); if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } - - // must do an in-place copy because we only have a reference - Point[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTArray(buf, pts.Length); - - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally - { - Marshal.FreeHGlobal(buf); } } diff --git a/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs b/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs index 797ee7c994a1..1d42e5a2a383 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs @@ -2,21 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Internal; -using System.Text; using System.Collections; -using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Drawing.Internal; -using System.Drawing.Imaging; -using System.Drawing.Text; -using System.Drawing.Drawing2D; -using System.Threading; -using System.Security; -using System.Runtime.ConstrainedExecution; +using System.Internal; +using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; [assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope = "member", Target = "System.Drawing.SafeNativeMethods+BITMAP.bmBits")] [assembly: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope = "member", Target = "System.Drawing.SafeNativeMethods+DIBSECTION.dshSection")] @@ -325,53 +321,6 @@ internal static Exception StatusException(int status) return new ExternalException(SR.GdiplusUnknown, E_UNEXPECTED); } - //---------------------------------------------------------------------------------------- - // Helper function: Convert GpPointF* memory block to PointF[] - //---------------------------------------------------------------------------------------- - internal static PointF[] ConvertGPPOINTFArrayF(IntPtr memory, int count) - { - if (memory == IntPtr.Zero) - { - throw new ArgumentNullException(nameof(memory)); - } - - var points = new PointF[count]; - Type pointType = typeof(GPPOINTF); - int size = Marshal.SizeOf(pointType); - - for (int index = 0; index < count; index++) - { - var pt = (GPPOINTF)Marshal.PtrToStructure((IntPtr)((long)memory + index * size), pointType); - points[index] = new PointF(pt.X, pt.Y); - } - - return points; - } - - //---------------------------------------------------------------------------------------- - // Helper function: Convert GpPoint* memory block to Point[] - //---------------------------------------------------------------------------------------- - internal static Point[] ConvertGPPOINTArray(IntPtr memory, int count) - { - if (memory == IntPtr.Zero) - { - throw new ArgumentNullException(nameof(memory)); - } - - var points = new Point[count]; - Type pointType = typeof(GPPOINT); - - int size = Marshal.SizeOf(pointType); - - for (int index = 0; index < count; index++) - { - var pt = (GPPOINT)Marshal.PtrToStructure((IntPtr)((long)memory + index * size), pointType); - points[index] = new Point(pt.X, pt.Y); - } - - return points; - } - //---------------------------------------------------------------------------------------- // Helper function: Convert PointF[] to native memory block GpPointF* //---------------------------------------------------------------------------------------- diff --git a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index 2dd8b6cdde4b..f807496e1fa6 100644 --- a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -2,17 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Win32.SafeHandles; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Internal; using System.Drawing.Text; using System.Runtime.InteropServices; -using System.Text; +using Microsoft.Win32.SafeHandles; namespace System.Drawing { - internal partial class SafeNativeMethods + internal unsafe partial class SafeNativeMethods { internal partial class Gdip { @@ -305,9 +304,9 @@ private static void LoadFunctionPointers() private static FunctionWrapper GdipGetPathTypes_ptr; internal static int GdipGetPathTypes(HandleRef path, byte[] types, int count) => GdipGetPathTypes_ptr.Delegate(path, types, count); - private delegate int GdipGetPathPoints_delegate(HandleRef path, HandleRef points, int count); + private delegate int GdipGetPathPoints_delegate(HandleRef path, PointF* points, int count); private static FunctionWrapper GdipGetPathPoints_ptr; - internal static int GdipGetPathPoints(HandleRef path, HandleRef points, int count) => GdipGetPathPoints_ptr.Delegate(path, points, count); + internal static int GdipGetPathPoints(HandleRef path, PointF* points, int count) => GdipGetPathPoints_ptr.Delegate(path, points, count); private delegate int GdipGetPathFillMode_delegate(HandleRef path, out int fillmode); private static FunctionWrapper GdipGetPathFillMode_ptr; @@ -317,9 +316,9 @@ private static void LoadFunctionPointers() private static FunctionWrapper GdipSetPathFillMode_ptr; internal static int GdipSetPathFillMode(HandleRef path, int fillmode) => GdipSetPathFillMode_ptr.Delegate(path, fillmode); - private delegate int GdipGetPathData_delegate(HandleRef path, IntPtr pathData); + private delegate int GdipGetPathData_delegate(HandleRef path, GpPathData* pathData); private static FunctionWrapper GdipGetPathData_ptr; - internal static int GdipGetPathData(HandleRef path, IntPtr pathData) => GdipGetPathData_ptr.Delegate(path, pathData); + internal static int GdipGetPathData(HandleRef path, GpPathData* pathData) => GdipGetPathData_ptr.Delegate(path, pathData); private delegate int GdipStartPathFigure_delegate(HandleRef path); private static FunctionWrapper GdipStartPathFigure_ptr; @@ -745,13 +744,13 @@ private static void LoadFunctionPointers() private static FunctionWrapper GdipGetDpiY_ptr; internal static int GdipGetDpiY(HandleRef graphics, float[] dpi) => GdipGetDpiY_ptr.Delegate(graphics, dpi); - private delegate int GdipTransformPoints_delegate(HandleRef graphics, int destSpace, int srcSpace, IntPtr points, int count); + private delegate int GdipTransformPoints_delegate(HandleRef graphics, int destSpace, int srcSpace, PointF* points, int count); private static FunctionWrapper GdipTransformPoints_ptr; - internal static int GdipTransformPoints(HandleRef graphics, int destSpace, int srcSpace, IntPtr points, int count) => GdipTransformPoints_ptr.Delegate(graphics, destSpace, srcSpace, points, count); + internal static int GdipTransformPoints(HandleRef graphics, int destSpace, int srcSpace, PointF* points, int count) => GdipTransformPoints_ptr.Delegate(graphics, destSpace, srcSpace, points, count); - private delegate int GdipTransformPointsI_delegate(HandleRef graphics, int destSpace, int srcSpace, IntPtr points, int count); + private delegate int GdipTransformPointsI_delegate(HandleRef graphics, int destSpace, int srcSpace, Point* points, int count); private static FunctionWrapper GdipTransformPointsI_ptr; - internal static int GdipTransformPointsI(HandleRef graphics, int destSpace, int srcSpace, IntPtr points, int count) => GdipTransformPointsI_ptr.Delegate(graphics, destSpace, srcSpace, points, count); + internal static int GdipTransformPointsI(HandleRef graphics, int destSpace, int srcSpace, Point* points, int count) => GdipTransformPointsI_ptr.Delegate(graphics, destSpace, srcSpace, points, count); private delegate int GdipGetNearestColor_delegate(HandleRef graphics, ref int color); private static FunctionWrapper GdipGetNearestColor_ptr; @@ -769,13 +768,13 @@ private static void LoadFunctionPointers() private static FunctionWrapper GdipDrawLineI_ptr; internal static int GdipDrawLineI(HandleRef graphics, HandleRef pen, int x1, int y1, int x2, int y2) => GdipDrawLineI_ptr.Delegate(graphics, pen, x1, y1, x2, y2); - private delegate int GdipDrawLines_delegate(HandleRef graphics, HandleRef pen, HandleRef points, int count); + private delegate int GdipDrawLines_delegate(HandleRef graphics, HandleRef pen, PointF* points, int count); private static FunctionWrapper GdipDrawLines_ptr; - internal static int GdipDrawLines(HandleRef graphics, HandleRef pen, HandleRef points, int count) => GdipDrawLines_ptr.Delegate(graphics, pen, points, count); + internal static int GdipDrawLines(HandleRef graphics, HandleRef pen, PointF* points, int count) => GdipDrawLines_ptr.Delegate(graphics, pen, points, count); - private delegate int GdipDrawLinesI_delegate(HandleRef graphics, HandleRef pen, HandleRef points, int count); + private delegate int GdipDrawLinesI_delegate(HandleRef graphics, HandleRef pen, Point* points, int count); private static FunctionWrapper GdipDrawLinesI_ptr; - internal static int GdipDrawLinesI(HandleRef graphics, HandleRef pen, HandleRef points, int count) => GdipDrawLinesI_ptr.Delegate(graphics, pen, points, count); + internal static int GdipDrawLinesI(HandleRef graphics, HandleRef pen, Point* points, int count) => GdipDrawLinesI_ptr.Delegate(graphics, pen, points, count); private delegate int GdipDrawArc_delegate(HandleRef graphics, HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); private static FunctionWrapper GdipDrawArc_ptr; @@ -789,13 +788,13 @@ private static void LoadFunctionPointers() private static FunctionWrapper GdipDrawBezier_ptr; internal static int GdipDrawBezier(HandleRef graphics, HandleRef pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) => GdipDrawBezier_ptr.Delegate(graphics, pen, x1, y1, x2, y2, x3, y3, x4, y4); - private delegate int GdipDrawBeziers_delegate(HandleRef graphics, HandleRef pen, HandleRef points, int count); + private delegate int GdipDrawBeziers_delegate(HandleRef graphics, HandleRef pen, PointF* points, int count); private static FunctionWrapper GdipDrawBeziers_ptr; - internal static int GdipDrawBeziers(HandleRef graphics, HandleRef pen, HandleRef points, int count) => GdipDrawBeziers_ptr.Delegate(graphics, pen, points, count); + internal static int GdipDrawBeziers(HandleRef graphics, HandleRef pen, PointF* points, int count) => GdipDrawBeziers_ptr.Delegate(graphics, pen, points, count); - private delegate int GdipDrawBeziersI_delegate(HandleRef graphics, HandleRef pen, HandleRef points, int count); + private delegate int GdipDrawBeziersI_delegate(HandleRef graphics, HandleRef pen, Point* points, int count); private static FunctionWrapper GdipDrawBeziersI_ptr; - internal static int GdipDrawBeziersI(HandleRef graphics, HandleRef pen, HandleRef points, int count) => GdipDrawBeziersI_ptr.Delegate(graphics, pen, points, count); + internal static int GdipDrawBeziersI(HandleRef graphics, HandleRef pen, Point* points, int count) => GdipDrawBeziersI_ptr.Delegate(graphics, pen, points, count); private delegate int GdipDrawRectangle_delegate(HandleRef graphics, HandleRef pen, float x, float y, float width, float height); private static FunctionWrapper GdipDrawRectangle_ptr; diff --git a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs index b1ae6c187124..f20d0aa0b188 100644 --- a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs +++ b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs @@ -14,7 +14,7 @@ namespace System.Drawing { // Raw function imports for gdiplus // Functions are loaded manually in order to accomodate different shared library names on Unix. - internal partial class SafeNativeMethods + internal unsafe partial class SafeNativeMethods { internal partial class Gdip { @@ -509,13 +509,13 @@ private static void LoadSharedFunctionPointers() private static FunctionWrapper GdipPathIterRewind_ptr; internal static int GdipPathIterRewind(HandleRef pathIter) => GdipPathIterRewind_ptr.Delegate(pathIter); - private delegate int GdipPathIterEnumerate_delegate(HandleRef pathIter, out int resultCount, IntPtr memoryPts, [In] [Out] byte[] types, int count); + private delegate int GdipPathIterEnumerate_delegate(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int count); private static FunctionWrapper GdipPathIterEnumerate_ptr; - internal static int GdipPathIterEnumerate(HandleRef pathIter, out int resultCount, IntPtr memoryPts, [In] [Out] byte[] types, int count) => GdipPathIterEnumerate_ptr.Delegate(pathIter, out resultCount, memoryPts, types, count); + internal static int GdipPathIterEnumerate(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int count) => GdipPathIterEnumerate_ptr.Delegate(pathIter, out resultCount, points, types, count); - private delegate int GdipPathIterCopyData_delegate(HandleRef pathIter, out int resultCount, IntPtr memoryPts, [In] [Out] byte[] types, int startIndex, int endIndex); + private delegate int GdipPathIterCopyData_delegate(HandleRef pathIter, out int resultCount, PointF* memoryPts, byte* types, int startIndex, int endIndex); private static FunctionWrapper GdipPathIterCopyData_ptr; - internal static int GdipPathIterCopyData(HandleRef pathIter, out int resultCount, IntPtr memoryPts, [In] [Out] byte[] types, int startIndex, int endIndex) => GdipPathIterCopyData_ptr.Delegate(pathIter, out resultCount, memoryPts, types, startIndex, endIndex); + internal static int GdipPathIterCopyData(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int startIndex, int endIndex) => GdipPathIterCopyData_ptr.Delegate(pathIter, out resultCount, points, types, startIndex, endIndex); private delegate int GdipCreateHatchBrush_delegate(int hatchstyle, int forecol, int backcol, out IntPtr brush); private static FunctionWrapper GdipCreateHatchBrush_ptr; @@ -1259,21 +1259,21 @@ internal static unsafe int GdipGetFamilyName(HandleRef family, StringBuilder nam private static FunctionWrapper GdipInvertMatrix_ptr; internal static int GdipInvertMatrix(HandleRef matrix) => GdipInvertMatrix_ptr.Delegate(matrix); - private delegate int GdipTransformMatrixPoints_delegate(HandleRef matrix, HandleRef pts, int count); + private delegate int GdipTransformMatrixPoints_delegate(HandleRef matrix, PointF* pts, int count); private static FunctionWrapper GdipTransformMatrixPoints_ptr; - internal static int GdipTransformMatrixPoints(HandleRef matrix, HandleRef pts, int count) => GdipTransformMatrixPoints_ptr.Delegate(matrix, pts, count); + internal static int GdipTransformMatrixPoints(HandleRef matrix, PointF* pts, int count) => GdipTransformMatrixPoints_ptr.Delegate(matrix, pts, count); - private delegate int GdipTransformMatrixPointsI_delegate(HandleRef matrix, HandleRef pts, int count); + private delegate int GdipTransformMatrixPointsI_delegate(HandleRef matrix, Point* pts, int count); private static FunctionWrapper GdipTransformMatrixPointsI_ptr; - internal static int GdipTransformMatrixPointsI(HandleRef matrix, HandleRef pts, int count) => GdipTransformMatrixPointsI_ptr.Delegate(matrix, pts, count); + internal static int GdipTransformMatrixPointsI(HandleRef matrix, Point* pts, int count) => GdipTransformMatrixPointsI_ptr.Delegate(matrix, pts, count); - private delegate int GdipVectorTransformMatrixPoints_delegate(HandleRef matrix, HandleRef pts, int count); + private delegate int GdipVectorTransformMatrixPoints_delegate(HandleRef matrix, PointF* pts, int count); private static FunctionWrapper GdipVectorTransformMatrixPoints_ptr; - internal static int GdipVectorTransformMatrixPoints(HandleRef matrix, HandleRef pts, int count) => GdipVectorTransformMatrixPoints_ptr.Delegate(matrix, pts, count); + internal static int GdipVectorTransformMatrixPoints(HandleRef matrix, PointF* pts, int count) => GdipVectorTransformMatrixPoints_ptr.Delegate(matrix, pts, count); - private delegate int GdipVectorTransformMatrixPointsI_delegate(HandleRef matrix, HandleRef pts, int count); + private delegate int GdipVectorTransformMatrixPointsI_delegate(HandleRef matrix, Point* pts, int count); private static FunctionWrapper GdipVectorTransformMatrixPointsI_ptr; - internal static int GdipVectorTransformMatrixPointsI(HandleRef matrix, HandleRef pts, int count) => GdipVectorTransformMatrixPointsI_ptr.Delegate(matrix, pts, count); + internal static int GdipVectorTransformMatrixPointsI(HandleRef matrix, Point* pts, int count) => GdipVectorTransformMatrixPointsI_ptr.Delegate(matrix, pts, count); private delegate int GdipGetMatrixElements_delegate(HandleRef matrix, IntPtr m); private static FunctionWrapper GdipGetMatrixElements_ptr; diff --git a/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs b/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs index c2c3637228e3..d94a444c5146 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs @@ -583,57 +583,40 @@ public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int desti } } - public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] pts) + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] pts) { if (pts == null) { throw new ArgumentNullException(nameof(pts)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); - try - { - int status = SafeNativeMethods.Gdip.GdipTransformPoints(new HandleRef(this, NativeGraphics), unchecked((int)destSpace), - unchecked((int)srcSpace), buf, pts.Length); - SafeNativeMethods.Gdip.CheckStatus(status); - - // must do an in-place copy because we only have a reference - PointF[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTFArrayF(buf, pts.Length); - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally + fixed (PointF* p = pts) { - Marshal.FreeHGlobal(buf); + SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipTransformPoints( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); } } [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] pts) + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] pts) { if (pts == null) { throw new ArgumentNullException(nameof(pts)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(pts); - try - { - int status = SafeNativeMethods.Gdip.GdipTransformPointsI(new HandleRef(this, NativeGraphics), unchecked((int)destSpace), - unchecked((int)srcSpace), buf, pts.Length); - SafeNativeMethods.Gdip.CheckStatus(status); - - Point[] newPts = SafeNativeMethods.Gdip.ConvertGPPOINTArray(buf, pts.Length); - for (int i = 0; i < pts.Length; i++) - { - pts[i] = newPts[i]; - } - } - finally + fixed (Point* p = pts) { - Marshal.FreeHGlobal(buf); + SafeNativeMethods.Gdip.CheckStatus(SafeNativeMethods.Gdip.GdipTransformPointsI( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); } } @@ -671,7 +654,7 @@ public void DrawLine(Pen pen, PointF pt1, PointF pt2) /// /// Draws a series of line segments that connect an array of points. /// - public void DrawLines(Pen pen, PointF[] points) + public unsafe void DrawLines(Pen pen, PointF[] points) { if (pen == null) { @@ -683,16 +666,12 @@ public void DrawLines(Pen pen, PointF[] points) throw new ArgumentNullException(nameof(points)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(points); - try - { - int status = SafeNativeMethods.Gdip.GdipDrawLines(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - new HandleRef(this, buf), points.Length); - CheckErrorStatus(status); - } - finally + fixed(PointF* p = points) { - Marshal.FreeHGlobal(buf); + CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawLines( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); } } @@ -722,7 +701,7 @@ public void DrawLine(Pen pen, Point pt1, Point pt2) /// /// Draws a series of line segments that connect an array of points. /// - public void DrawLines(Pen pen, Point[] points) + public unsafe void DrawLines(Pen pen, Point[] points) { if (pen == null) { @@ -734,16 +713,13 @@ public void DrawLines(Pen pen, Point[] points) throw new ArgumentNullException(nameof(points)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(points); - try - { - int status = SafeNativeMethods.Gdip.GdipDrawLinesI(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - new HandleRef(this, buf), points.Length); - CheckErrorStatus(status); - } - finally + fixed (Point* p = points) { - Marshal.FreeHGlobal(buf); + CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawLinesI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); } } @@ -824,7 +800,7 @@ public void DrawBezier(Pen pen, PointF pt1, PointF pt2, PointF pt3, PointF pt4) /// /// Draws a series of cubic Bezier curves from an array of points. /// - public void DrawBeziers(Pen pen, PointF[] points) + public unsafe void DrawBeziers(Pen pen, PointF[] points) { if (pen == null) { @@ -836,16 +812,12 @@ public void DrawBeziers(Pen pen, PointF[] points) throw new ArgumentNullException(nameof(points)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(points); - try - { - int status = SafeNativeMethods.Gdip.GdipDrawBeziers(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - new HandleRef(this, buf), points.Length); - CheckErrorStatus(status); - } - finally + fixed(PointF* p = points) { - Marshal.FreeHGlobal(buf); + CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawBeziers( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); } } @@ -860,7 +832,7 @@ public void DrawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) /// /// Draws a series of cubic Bezier curves from an array of points. /// - public void DrawBeziers(Pen pen, Point[] points) + public unsafe void DrawBeziers(Pen pen, Point[] points) { if (pen == null) { @@ -872,16 +844,13 @@ public void DrawBeziers(Pen pen, Point[] points) throw new ArgumentNullException(nameof(points)); } - IntPtr buf = SafeNativeMethods.Gdip.ConvertPointToMemory(points); - try + fixed(Point* p = points) { - int status = SafeNativeMethods.Gdip.GdipDrawBeziersI(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), - new HandleRef(this, buf), points.Length); - CheckErrorStatus(status); - } - finally - { - Marshal.FreeHGlobal(buf); + CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawBeziersI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); } } diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs new file mode 100644 index 000000000000..ce97b0a42677 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct GpPathData + { + public int Count; + public PointF* Points; + public byte* Types; + } +} diff --git a/src/System.Drawing.Common/tests/DrawingTest.cs b/src/System.Drawing.Common/tests/DrawingTest.cs new file mode 100644 index 000000000000..f5496970691a --- /dev/null +++ b/src/System.Drawing.Common/tests/DrawingTest.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace System.Drawing.Tests +{ + public abstract class DrawingTest + { + private static Security.Cryptography.MD5 s_md5 = Security.Cryptography.MD5.Create(); + + protected void ValidateImageContent(Image image, byte[] expectedHash) + { + using (MemoryStream stream = new MemoryStream(4096)) + { + image.Save(stream, ImageFormat.Bmp); + stream.Seek(0, SeekOrigin.Begin); + byte[] hash = s_md5.ComputeHash(stream); + Assert.Equal(expectedHash, hash); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/GraphicsTests.cs b/src/System.Drawing.Common/tests/GraphicsTests.cs index dec60407bb80..e6afbbaa5019 100644 --- a/src/System.Drawing.Common/tests/GraphicsTests.cs +++ b/src/System.Drawing.Common/tests/GraphicsTests.cs @@ -2,13 +2,10 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.ComponentModel; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; -using System.Linq; using Xunit; -using Xunit.Sdk; namespace System.Drawing.Tests { @@ -2290,160 +2287,6 @@ public void GetNearestColor_Disposed_ThrowsArgumentException() } } - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLine_NullPen_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - AssertExtensions.Throws("pen", () => graphics.DrawLine(null, Point.Empty, Point.Empty)); - AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0, 0, 0, 0)); - AssertExtensions.Throws("pen", () => graphics.DrawLine(null, PointF.Empty, PointF.Empty)); - AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0f, 0f, 0f, 0f)); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLine_DisposedPen_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - var pen = new Pen(Color.Red); - pen.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLine_Busy_ThrowsInvalidOperationException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - IntPtr hdc = graphics.GetHdc(); - try - { - Assert.Throws(() => graphics.DrawLine(pen, Point.Empty, Point.Empty)); - Assert.Throws(() => graphics.DrawLine(pen, 0, 0, 0, 0)); - Assert.Throws(() => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); - Assert.Throws(() => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); - } - finally - { - graphics.ReleaseHdc(); - } - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLine_Disposed_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (var pen = new Pen(Color.Red)) - { - Graphics graphics = Graphics.FromImage(image); - graphics.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); - AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLines_NullPen_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new Point[2])); - AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new PointF[2])); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLines_DisposedPen_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - var pen = new Pen(Color.Red); - pen.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLines_NullPoints_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (Point[])null)); - AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (PointF[])null)); - } - } - - [ConditionalTheory(Helpers.GdiplusIsAvailable)] - [InlineData(0)] - [InlineData(1)] - public void DrawLines_InvalidPointsLength_ThrowsArgumentException(int length) - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[length])); - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[length])); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLines_Busy_ThrowsInvalidOperationException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - IntPtr hdc = graphics.GetHdc(); - try - { - Assert.Throws(() => graphics.DrawLines(pen, new Point[2])); - Assert.Throws(() => graphics.DrawLines(pen, new PointF[2])); - } - finally - { - graphics.ReleaseHdc(); - } - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawLines_Disposed_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (var pen = new Pen(Color.Red)) - { - Graphics graphics = Graphics.FromImage(image); - graphics.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); - AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); - } - } - [ActiveIssue(20884, TestPlatforms.AnyUnix)] [ConditionalFact(Helpers.GdiplusIsAvailable)] public void DrawArc_NullPen_ThrowsArgumentNullException() @@ -2545,159 +2388,6 @@ public void DrawArc_Disposed_ThrowsArgumentException() } } - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBezier_NullPen_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, 1, 2, 3, 4, 5, 6, 7, 8)); - AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); - AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBezier_DisposedPen_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - var pen = new Pen(Color.Red); - pen.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); - AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); - AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBezier_Busy_ThrowsInvalidOperationException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - IntPtr hdc = graphics.GetHdc(); - try - { - Assert.Throws(() => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); - Assert.Throws(() => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); - Assert.Throws(() => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); - } - finally - { - graphics.ReleaseHdc(); - } - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBezier_Disposed_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (var pen = new Pen(Color.Red)) - { - Graphics graphics = Graphics.FromImage(image); - graphics.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); - AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); - AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); - AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_NullPen_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new Point[2])); - AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new PointF[2])); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_DisposedPen_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - { - var pen = new Pen(Color.Red); - pen.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); - } - } - - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_NullPoints_ThrowsArgumentNullException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (Point[])null)); - AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (PointF[])null)); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_EmptyPoints_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[0])); - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[0])); - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_Busy_ThrowsInvalidOperationException() - { - using (var image = new Bitmap(10, 10)) - using (Graphics graphics = Graphics.FromImage(image)) - using (var pen = new Pen(Color.Red)) - { - IntPtr hdc = graphics.GetHdc(); - try - { - Assert.Throws(() => graphics.DrawBeziers(pen, new Point[2])); - Assert.Throws(() => graphics.DrawBeziers(pen, new PointF[2])); - } - finally - { - graphics.ReleaseHdc(); - } - } - } - - [ActiveIssue(20884, TestPlatforms.AnyUnix)] - [ConditionalFact(Helpers.GdiplusIsAvailable)] - public void DrawBeziers_Disposed_ThrowsArgumentException() - { - using (var image = new Bitmap(10, 10)) - using (var pen = new Pen(Color.Red)) - { - Graphics graphics = Graphics.FromImage(image); - graphics.Dispose(); - - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); - AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); - } - } - [ConditionalFact(Helpers.GdiplusIsAvailable)] public void DrawRectangle_NullPen_ThrowsArgumentNullException() { diff --git a/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs b/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs new file mode 100644 index 000000000000..48d97b427c74 --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class Graphics_DrawBezierTests : DrawingTest + { + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_Point() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawBezier(pen, new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10)); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xa4, 0xb9, 0x73, 0xb9, 0x6f, 0x3a, 0x85, 0x21, 0xd3, 0x65, 0x87, 0x24, 0xcf, 0x6d, 0x61, 0x94 } + : new byte[] { 0xcf, 0x92, 0xaa, 0xe2, 0x44, 0xd4, 0xdd, 0xae, 0xdd, 0x4c, 0x8a, 0xf5, 0xc3, 0x65, 0xac, 0xf2 }); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_Points() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.Red)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Point[] points = + { + new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10), + new Point(60, 15), new Point(65, 25), new Point(50, 30) + }; + + graphics.DrawBeziers(pen, points); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xd0, 0x00, 0x08, 0x21, 0x06, 0x29, 0xd8, 0xab, 0x19, 0xc5, 0xc9, 0xf6, 0xf2, 0x69, 0x30, 0x1f } + : new byte[] { 0x9d, 0x24, 0x9f, 0x91, 0xa3, 0xa5, 0x60, 0xde, 0x14, 0x69, 0x42, 0xa8, 0xe6, 0xc6, 0xbf, 0xc9 }); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_PointFs() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.Red)) + using (Graphics graphics = Graphics.FromImage(image)) + { + PointF[] points = + { + new PointF(10.0F, 10.0F), new PointF(20.0F, 1.0F), new PointF(35.0F, 5.0F), new PointF(50.0F, 10.0F), + new PointF(60.0F, 15.0F), new PointF(65.0F, 25.0F), new PointF(50.0F, 30.0F) + }; + + graphics.DrawBeziers(pen, points); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xd0, 0x00, 0x08, 0x21, 0x06, 0x29, 0xd8, 0xab, 0x19, 0xc5, 0xc9, 0xf6, 0xf2, 0x69, 0x30, 0x1f } + : new byte[] { 0x9d, 0x24, 0x9f, 0x91, 0xa3, 0xa5, 0x60, 0xde, 0x14, 0x69, 0x42, 0xa8, 0xe6, 0xc6, 0xbf, 0xc9 }); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, 1, 2, 3, 4, 5, 6, 7, 8)); + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); + Assert.Throws(() => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + Assert.Throws(() => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBezier_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new PointF[2])); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (PointF[])null)); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_EmptyPoints_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[0])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[0])); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawBeziers(pen, new Point[2])); + Assert.Throws(() => graphics.DrawBeziers(pen, new PointF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawBeziers_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs b/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs new file mode 100644 index 000000000000..8ee717c76d1b --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class Graphics_DrawLineTests : DrawingTest + { + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_Points() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawLines(pen, new Point[] { new Point(1, 1), new Point(1, 10), new Point(20, 5), new Point(25, 30) }); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0x8e, 0xc2, 0xfb, 0xb4, 0xde, 0x5d, 0xdc, 0xd2, 0x31, 0xbd, 0xd3, 0x9a, 0xcf, 0xc1, 0xd4, 0xad } + : new byte[] { 0x55, 0x40, 0xd8, 0xaa, 0xc7, 0x36, 0x06, 0x18, 0x1a, 0x57, 0x2b, 0xa9, 0x5a, 0xff, 0x2b, 0xb2 }); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_PointFs() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawLines(pen, new PointF[] { new PointF(1.0F, 1.0F), new PointF(1.0F, 10.0F), new PointF(20.0F, 5.0F), new PointF(25.0F, 30.0F) }); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0x8e, 0xc2, 0xfb, 0xb4, 0xde, 0x5d, 0xdc, 0xd2, 0x31, 0xbd, 0xd3, 0x9a, 0xcf, 0xc1, 0xd4, 0xad } + : new byte[] { 0x55, 0x40, 0xd8, 0xaa, 0xc7, 0x36, 0x06, 0x18, 0x1a, 0x57, 0x2b, 0xa9, 0x5a, 0xff, 0x2b, 0xb2 }); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLine_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, Point.Empty, Point.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0, 0, 0, 0)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0f, 0f, 0f, 0f)); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLine_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLine_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + Assert.Throws(() => graphics.DrawLine(pen, 0, 0, 0, 0)); + Assert.Throws(() => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + Assert.Throws(() => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLine_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new PointF[2])); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (PointF[])null)); + } + } + + [ConditionalTheory(Helpers.GdiplusIsAvailable)] + [InlineData(0)] + [InlineData(1)] + public void DrawLines_InvalidPointsLength_ThrowsArgumentException(int length) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[length])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[length])); + } + } + + [ActiveIssue(20884, TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawLines(pen, new Point[2])); + Assert.Throws(() => graphics.DrawLines(pen, new PointF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.GdiplusIsAvailable)] + public void DrawLines_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); + } + } + + } +} diff --git a/src/System.Drawing.Common/tests/Performance/Configurations.props b/src/System.Drawing.Common/tests/Performance/Configurations.props new file mode 100644 index 000000000000..78953dfc8851 --- /dev/null +++ b/src/System.Drawing.Common/tests/Performance/Configurations.props @@ -0,0 +1,8 @@ + + + + + netstandard; + + + diff --git a/src/System.Drawing.Common/tests/Performance/Perf_Graphics_DrawBeziers.cs b/src/System.Drawing.Common/tests/Performance/Perf_Graphics_DrawBeziers.cs new file mode 100644 index 000000000000..87ef22dfbce5 --- /dev/null +++ b/src/System.Drawing.Common/tests/Performance/Perf_Graphics_DrawBeziers.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Microsoft.Xunit.Performance; + +namespace System.Drawing.Tests +{ + public class Perf_Graphics_DrawBeziers : RemoteExecutorTestBase + { + [Benchmark(InnerIterationCount = 10000)] + public void DrawBezier_Point() + { + Random r = new Random(1942); + + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + graphics.DrawBezier(pen, new Point(r.Next(100), r.Next(100)), new Point(r.Next(100), r.Next(100)), new Point(r.Next(100), r.Next(100)), new Point(r.Next(100), r.Next(100))); + } + } + } + } + } + + [Benchmark(InnerIterationCount = 10000)] + public void DrawBezier_Points() + { + Point[] points = + { + new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10), + new Point(60, 15), new Point(65, 25), new Point(50, 30) + }; + + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.Blue)) + using (Graphics graphics = Graphics.FromImage(image)) + { + foreach (var iteration in Benchmark.Iterations) + { + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + graphics.DrawBeziers(pen, points); + } + } + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Performance/Perf_Graphics_Transforms.cs b/src/System.Drawing.Common/tests/Performance/Perf_Graphics_Transforms.cs new file mode 100644 index 000000000000..bc84d484425b --- /dev/null +++ b/src/System.Drawing.Common/tests/Performance/Perf_Graphics_Transforms.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Drawing.Drawing2D; +using Microsoft.Xunit.Performance; + +namespace System.Drawing.Tests +{ + public class Perf_Graphics_Transforms : RemoteExecutorTestBase + { + [Benchmark(InnerIterationCount = 10000)] + public void TransformPoints() + { + Point[] points = + { + new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10), + new Point(60, 15), new Point(65, 25), new Point(50, 30) + }; + + using (Bitmap image = new Bitmap(100, 100)) + using (Graphics graphics = Graphics.FromImage(image)) + { + foreach (var iteration in Benchmark.Iterations) + { + + using (iteration.StartMeasurement()) + { + for (int i = 0; i < Benchmark.InnerIterationCount; i++) + { + graphics.TransformPoints(CoordinateSpace.World, CoordinateSpace.Page, points); + graphics.TransformPoints(CoordinateSpace.Device, CoordinateSpace.World, points); + graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, points); + } + } + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Performance/System.Drawing.Common.Performance.Tests.csproj b/src/System.Drawing.Common/tests/Performance/System.Drawing.Common.Performance.Tests.csproj new file mode 100644 index 000000000000..d36e367eeaab --- /dev/null +++ b/src/System.Drawing.Common/tests/Performance/System.Drawing.Common.Performance.Tests.csproj @@ -0,0 +1,32 @@ + + + + + InnerLoop;OuterLoop + true + {E66FFA55-0975-4F0D-8A18-24B2687FEDEA} + + + + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + RemoteExecutorConsoleApp + + + Common\System\PerfUtils.cs + + + + + {69e46a6f-9966-45a5-8945-2559fe337827} + PerfRunner + + + + + + + \ No newline at end of file diff --git a/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj b/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj index cc2bf1d3a56f..982077e36e04 100644 --- a/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj +++ b/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj @@ -23,9 +23,12 @@ + + + From 43b2bd278d102f732a16b04377b499ccabea171a Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Mon, 4 Jun 2018 11:11:40 -0700 Subject: [PATCH 2/3] Address feedback --- src/System.Drawing.Common/src/System.Drawing.Common.csproj | 4 +--- .../src/System/Drawing/Drawing2D/Matrix.cs | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/System.Drawing.Common/src/System.Drawing.Common.csproj index 001a950815ed..7ad224fe1c62 100644 --- a/src/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -227,6 +227,7 @@ + @@ -378,8 +379,5 @@ - - - \ No newline at end of file diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs index 9624a6993890..c28ef9da9ab5 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs @@ -266,7 +266,7 @@ public unsafe void TransformPoints(PointF[] pts) if (pts == null) throw new ArgumentNullException(nameof(pts)); - fixed(PointF* p = pts) + fixed (PointF* p = pts) { int status = SafeNativeMethods.Gdip.GdipTransformMatrixPoints( new HandleRef(this, nativeMatrix), @@ -287,7 +287,8 @@ public unsafe void TransformPoints(Point[] pts) fixed (Point* p = pts) { - int status = SafeNativeMethods.Gdip.GdipTransformMatrixPointsI(new HandleRef(this, nativeMatrix), + int status = SafeNativeMethods.Gdip.GdipTransformMatrixPointsI( + new HandleRef(this, nativeMatrix), p, pts.Length); From a0b46899a11d267b9cdfb8d685cda9e2d3611be1 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Tue, 5 Jun 2018 21:44:15 -0700 Subject: [PATCH 3/3] Add space after fixed statements. --- .../src/System/Drawing/Drawing2D/Matrix.cs | 2 +- .../src/System/Drawing/Graphics.Windows.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs index c28ef9da9ab5..f9a26b610fa8 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs @@ -325,7 +325,7 @@ public unsafe void TransformVectors(Point[] pts) if (pts == null) throw new ArgumentNullException(nameof(pts)); - fixed(Point* p = pts) + fixed (Point* p = pts) { int status = SafeNativeMethods.Gdip.GdipVectorTransformMatrixPointsI( new HandleRef(this, nativeMatrix), diff --git a/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs b/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs index d94a444c5146..cedb5f5d4b42 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs @@ -666,7 +666,7 @@ public unsafe void DrawLines(Pen pen, PointF[] points) throw new ArgumentNullException(nameof(points)); } - fixed(PointF* p = points) + fixed (PointF* p = points) { CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawLines( new HandleRef(this, NativeGraphics), @@ -812,7 +812,7 @@ public unsafe void DrawBeziers(Pen pen, PointF[] points) throw new ArgumentNullException(nameof(points)); } - fixed(PointF* p = points) + fixed (PointF* p = points) { CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawBeziers( new HandleRef(this, NativeGraphics), @@ -844,7 +844,7 @@ public unsafe void DrawBeziers(Pen pen, Point[] points) throw new ArgumentNullException(nameof(points)); } - fixed(Point* p = points) + fixed (Point* p = points) { CheckErrorStatus(SafeNativeMethods.Gdip.GdipDrawBeziersI( new HandleRef(this, NativeGraphics),