Optimize Matrix.Elements (#47932)
authorJeremy Kuhne <jkuhne@microsoft.com>
Tue, 9 Feb 2021 15:56:14 +0000 (07:56 -0800)
committerGitHub <noreply@github.com>
Tue, 9 Feb 2021 15:56:14 +0000 (10:56 -0500)
* Optimize Matrix.Elements

Just pin the array. There is no need to allocate native memory and copy.

* Move more element usage to the stack.

* Remove DeleteObject call

src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs
src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs
src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs
src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs
src/libraries/System.Drawing.Common/src/misc/GDI/WindowsGraphics.cs

index 6416a58..bc729e4 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
 using Gdip = System.Drawing.SafeNativeMethods.Gdip;
@@ -86,25 +87,35 @@ namespace System.Drawing.Drawing2D
         {
             get
             {
-                IntPtr buf = Marshal.AllocHGlobal(6 * 8); // 6 elements x 8 bytes (float)
+                float[] elements = new float[6];
+                GetElements(elements);
+                return elements;
+            }
+        }
 
-                try
-                {
-                    Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(this, NativeMatrix), buf));
+        internal unsafe void GetElements(Span<float> elements)
+        {
+            Debug.Assert(elements.Length >= 6);
 
-                    float[] m = new float[6];
-                    Marshal.Copy(buf, m, 0, 6);
-                    return m;
-                }
-                finally
-                {
-                    Marshal.FreeHGlobal(buf);
-                }
+            fixed (float* m = elements)
+            {
+                Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(this, NativeMatrix), m));
             }
         }
 
-        public float OffsetX => Elements[4];
-        public float OffsetY => Elements[5];
+        public unsafe float OffsetX => Offset.X;
+
+        public unsafe float OffsetY => Offset.Y;
+
+        internal unsafe PointF Offset
+        {
+            get
+            {
+                Span<float> elements = stackalloc float[6];
+                GetElements(elements);
+                return new PointF(elements[4], elements[5]);
+            }
+        }
 
         public void Reset()
         {
@@ -263,6 +274,7 @@ namespace System.Drawing.Drawing2D
                 return isIdentity != 0;
             }
         }
+
         public override bool Equals([NotNullWhen(true)] object? obj)
         {
             if (!(obj is Matrix matrix2))
index dd5d77b..4071574 100644 (file)
@@ -800,7 +800,7 @@ namespace System.Drawing
             internal static extern int GdipVectorTransformMatrixPointsI(HandleRef matrix, Point* pts, int count);
 
             [DllImport(LibraryName, ExactSpelling = true)]
-            internal static extern int GdipGetMatrixElements(HandleRef matrix, IntPtr m);
+            internal static extern unsafe int GdipGetMatrixElements(HandleRef matrix, float* m);
 
             [DllImport(LibraryName, ExactSpelling = true)]
             internal static extern int GdipIsMatrixInvertible(HandleRef matrix, out int boolean);
index fef8416..e78929e 100644 (file)
@@ -691,9 +691,7 @@ namespace System.Drawing
 
             if (!cumulTransform.IsIdentity)
             {
-                float[] elements = cumulTransform.Elements;
-                currentOffset.X = elements[4];
-                currentOffset.Y = elements[5];
+                currentOffset = cumulTransform.Offset;
             }
 
             GraphicsContext? context = _previousContext;
index beb7ec2..fcffae0 100644 (file)
@@ -49,9 +49,7 @@ namespace System.Drawing
             Matrix transform = g.Transform;
             if (!transform.IsIdentity)
             {
-                float[] elements = transform.Elements;
-                _transformOffset.X = elements[4];
-                _transformOffset.Y = elements[5];
+                _transformOffset = transform.Offset;
             }
             transform.Dispose();
 
index c6d2b84..2b38e96 100644 (file)
@@ -4,6 +4,7 @@
 using System.Buffers;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.Drawing.Internal;
 using System.IO;
@@ -418,8 +419,11 @@ namespace System.Drawing
         internal void Draw(Graphics graphics, Rectangle targetRect)
         {
             Rectangle copy = targetRect;
-            copy.X += (int)graphics.Transform.OffsetX;
-            copy.Y += (int)graphics.Transform.OffsetY;
+
+            using Matrix transform = graphics.Transform;
+            PointF offset = transform.Offset;
+            copy.X += (int)offset.X;
+            copy.Y += (int)offset.Y;
 
             using (WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping))
             {
@@ -435,8 +439,10 @@ namespace System.Drawing
         internal void DrawUnstretched(Graphics graphics, Rectangle targetRect)
         {
             Rectangle copy = targetRect;
-            copy.X += (int)graphics.Transform.OffsetX;
-            copy.Y += (int)graphics.Transform.OffsetY;
+            using Matrix transform = graphics.Transform;
+            PointF offset = transform.Offset;
+            copy.X += (int)offset.X;
+            copy.Y += (int)offset.Y;
 
             using (WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping))
             {
index 8774e9c..d6f876c 100644 (file)
@@ -54,7 +54,8 @@ namespace System.Drawing.Internal
             Debug.Assert(g != null, "null Graphics object.");
 
             WindowsRegion? wr = null;
-            float[]? elements = null;
+
+            PointF offset = default;
 
             Region? clipRgn = null;
             Matrix? worldTransf = null;
@@ -71,7 +72,7 @@ namespace System.Drawing.Internal
                 {
                     if ((properties & ApplyGraphicsProperties.TranslateTransform) != 0)
                     {
-                        elements = worldTransf.Elements;
+                        offset = worldTransf.Offset;
                     }
 
                     worldTransf.Dispose();
@@ -110,10 +111,10 @@ namespace System.Drawing.Internal
                 }
             }
 
-            if (elements != null)
+            if (offset != default)
             {
                 // elements (XFORM) = [eM11, eM12, eM21, eM22, eDx, eDy], eDx/eDy specify the translation offset.
-                wg.DeviceContext.TranslateTransform((int)elements[4], (int)elements[5]);
+                wg.DeviceContext.TranslateTransform((int)offset.X, (int)offset.Y);
             }
 
             return wg;