aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Utilities/Tango.BugReporter/Program.cs
blob: 39d50c4a8fededc2e203ee8114480926b1f9f8c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
using PdfSharp.Drawing;
using PdfSharp.Pdf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.TFS;

namespace Tango.BugReporter
{
    class Program
    {
        private static List<Color> _colors = new List<Color>()
        {
             Colors.Crimson,
             Colors.Coral,
             Colors.Khaki,
             Colors.Wheat,
             Colors.PaleGreen,
             Colors.PaleTurquoise,
             Colors.CadetBlue,
             Colors.Lavender,
             Colors.SlateBlue,
             Colors.LightSteelBlue,
             Colors.PowderBlue,
             Colors.Silver,
             Colors.SlateGray,
        };

        static void Main(string[] args)
        {
            String startPath = AssemblyHelper.GetCurrentAssemblyFolder();

            ITeamFoundationServiceClient tfs = new TeamFoundationServiceClient("https://twinetfs.visualstudio.com/DefaultCollection", String.Empty, "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa");
            var project = tfs.GetProject("Tango").Result;

            var allItems = tfs.GetAllWorkItems(project).Result.OrderByDescending(x => x.Type).OrderBy(x => x.Severity).ToList();
            var activeItems = allItems.Where(x => x.State == State.Active || x.State == State.New).ToList();
            var resolvedItems = allItems.Where(x => x.State == State.Resolved).ToList();

            Document document = new Document();
            document.Info.Title = "Tango Bug Report";
            document.Info.Author = "Twine Solutions LTD";

            document.DefaultPageSetup.TopMargin = 5;
            document.DefaultPageSetup.RightMargin = 5;
            document.DefaultPageSetup.LeftMargin = 5;

            Style style = document.Styles["Normal"];
            style.Font.Size = 7;

            var section = document.AddSection();
            section.PageSetup.PageFormat = PageFormat.A4;



            var headParagraph = section.AddParagraph();
            headParagraph.Format.Alignment = ParagraphAlignment.Left;
            headParagraph.Format.Font.Size = 16;
            headParagraph.Format.Font.Bold = true;
            headParagraph.Format.SpaceBefore = "8mm";
            headParagraph.Format.SpaceAfter = "5mm";
            headParagraph.Format.RightIndent = "5mm";
            headParagraph.Format.Font.Color = Colors.DimGray;

            var img = headParagraph.AddImage(Path.Combine(startPath, "twine-logo.jpg"));
            headParagraph.AddSpace(5);
            var t = headParagraph.AddText("              Twine Solutions VSTS Report");

            var pp = section.AddParagraph(String.Format("This report was generated on {0}. Below you can find a daily summery of the Tango project, work items state and urgency.", DateTime.Now.ToShortDateString()));
            pp.Format.LeftIndent = "2cm";

            AddTable(section, "All Active Items", activeItems);

            section.AddPageBreak();

            AddTable(section, "Created Today", activeItems.Where(x => x.CreatedDate.Date == DateTime.Now.Date));

            section.AddPageBreak();

            AddTable(section, "Resolved Today", resolvedItems.Where(x => x.ResolvedDate.Date == DateTime.Now.Date));

            section.AddPageBreak();

            AddChart(section, ChartType.Column2D, "Items Per Member", activeItems.GroupBy(x => x.AssignedTo), nameof(TeamMember.DisplayName));

            section.AddPageBreak();

            AddChart(section, ChartType.Bar2D, "Items Per Area", activeItems.GroupBy(x => x.Area.Name), null);

            section.AddPageBreak();

            foreach (var g in activeItems.GroupBy(x => x.Area.Name))
            {
                AddTable(section, g.Key + " Items", g);

                section.AddPageBreak();
            }

            var group = activeItems.GroupBy(x => x.AssignedTo);

            var file = TemporaryManager.Default.CreateFile();

            PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(false, PdfFontEmbedding.None);
            pdfRenderer.Document = document;
            pdfRenderer.RenderDocument();
            pdfRenderer.PdfDocument.Save(file.Path);

            ITeamFoundationEmailClient emailClient = new TeamFoundationEmailClient(new System.Net.NetworkCredential("roy@twine-s.com", "Maya2018"));

            Email mail = new Email();
            mail.Attachments.Add(new Attachment() { FilePath = file.Path, Name = "VSTS-REPORT.pdf" });
            mail.From = project.Members.SingleOrDefault(x => x.DisplayName.Contains("Roy"));
            mail.To.Add(mail.From);
            mail.Subject = "VSTS Daily Report";
            mail.Body = "Attached is a daily report containing Tango project work items.";

            emailClient.Send(mail).Wait();

            file.Delete();
        }

        private static Chart AddChart<T>(Section section, ChartType chartType, String header, IEnumerable<IGrouping<T, WorkItem>> workItemGroups, String propName)
        {
            var headParagraph = section.AddParagraph(header);
            headParagraph.Format.Alignment = ParagraphAlignment.Left;
            headParagraph.Format.Font.Size = 16;
            headParagraph.Format.Font.Bold = true;
            headParagraph.Format.SpaceBefore = "8mm";
            headParagraph.Format.SpaceAfter = "5mm";

            Chart chart = section.AddChart(chartType);
            chart.Width = section.Document.DefaultPageSetup.PageWidth - 5;
            chart.Height = "10cm";

            workItemGroups = workItemGroups.OrderByDescending(x => x.Count());

            Series series = chart.SeriesCollection.AddSeries();
            series.Add(workItemGroups.Select(x => (double)x.Count()).ToArray());

            XSeries xseries = chart.XValues.AddXSeries();

            if (propName != null)
            {
                xseries.Add(workItemGroups.Select(x => x.Key.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.Instance).GetValue(x.Key).ToString()).ToArray());
            }
            else
            {
                xseries.Add(workItemGroups.Select(x => x.Key.ToString()).ToArray());
            }

            var elements = series.Elements.Cast<Point>().ToArray();

            for (int i = 0; i < elements.Length; i++)
            {
                elements[i].FillFormat.Color = _colors[i];
            }

            chart.XAxis.MajorTickMark = TickMarkType.Outside;
            chart.XAxis.Title.Caption = propName;
            chart.YAxis.MajorTickMark = TickMarkType.Outside;
            chart.YAxis.HasMajorGridlines = true;
            chart.PlotArea.LineFormat.Color = Colors.White;
            chart.PlotArea.LineFormat.Width = 1;
            chart.PlotArea.LineFormat.Visible = true;
            return chart;
        }

        private static Table AddTable(Section section, String header, IEnumerable<WorkItem> workItems)
        {
            String startPath = AssemblyHelper.GetCurrentAssemblyFolder();

            var headParagraph = section.AddParagraph(header);
            headParagraph.Format.Alignment = ParagraphAlignment.Left;
            headParagraph.Format.Font.Size = 16;
            headParagraph.Format.Font.Bold = true;
            headParagraph.Format.SpaceBefore = "8mm";
            headParagraph.Format.SpaceAfter = "5mm";


            var table = section.AddTable();

            table.Borders.Color = Colors.Black;
            table.Borders.Width = 0.25;
            table.Borders.Left.Width = 0.5;
            table.Borders.Right.Width = 0.5;
            table.Rows.LeftIndent = 0;

            table.BottomPadding = "2mm";
            table.TopPadding = "2mm";
            table.LeftPadding = "2mm";
            table.RightPadding = "2mm";


            // Before you can add a row, you must define the columns
            table.AddColumn("1cm");
            table.AddColumn();
            table.AddColumn("4cm");
            table.AddColumn();
            table.AddColumn();
            table.AddColumn();
            table.AddColumn("1.5cm");
            table.AddColumn("1.5cm");
            table.AddColumn("1.5cm");

            // Create the header of the table
            Row row = table.AddRow();
            row.HeadingFormat = true;
            row.Shading.Color = Colors.Gainsboro;
            row.Cells[0].AddParagraph("Work Item");
            row.Cells[1].AddParagraph("Created Date");
            row.Cells[2].AddParagraph("Title");
            row.Cells[3].AddParagraph("Area");
            row.Cells[4].AddParagraph("Created By");
            row.Cells[5].AddParagraph("Assigned To");
            row.Cells[6].AddParagraph("Severity");
            row.Cells[7].AddParagraph("Priority");
            row.Cells[8].AddParagraph("State");

            foreach (var item in workItems)
            {
                Row r = table.AddRow();
                var image = r.Cells[0].AddImage(Path.Combine(startPath, item.Type == WorkItemType.Bug ? "bug.png" : "task.png"));
                image.Width = 15;
                image.Height = 15;
                r.Cells[1].AddParagraph(item.CreatedDate.ToShortDateString());

                var linkParagraph = r.Cells[2].AddParagraph();
                var link = linkParagraph.AddHyperlink(String.Format("https://twinetfs.visualstudio.com/DefaultCollection/Tango/_queries?id={0}&_a=edit", item.ID), HyperlinkType.Web);
                link.AddFormattedText(item.Title);

                r.Cells[3].AddParagraph(item.Area.Name);
                r.Cells[4].AddParagraph(item.CreatedBy.DisplayName);
                r.Cells[5].AddParagraph(item.AssignedTo.DisplayName);
                var p = r.Cells[6].AddParagraph(item.Severity.ToString());

                if (item.Severity == Severity.Critical || item.Severity == Severity.High)
                {
                    p.Format.Font.Color = Colors.Red;
                }

                r.Cells[7].AddParagraph(item.Priority.ToDescription());
                r.Cells[8].AddParagraph(item.State.ToString());
            }

            return table;
        }
    }
}
pan> /// <param name="x">The X coordinate of the pixel to get</param> /// <param name="y">The Y coordinate of the pixel to get</param> /// <exception cref="InvalidOperationException">The fast bitmap is not locked</exception> /// <exception cref="ArgumentOutOfRangeException">The provided coordinates are out of bounds of the bitmap</exception> public int GetPixelInt(int x, int y) { if (!Locked) { throw new InvalidOperationException("The FastBitmap must be locked before any pixel operations are made"); } if (x < 0 || x >= Width) { throw new ArgumentOutOfRangeException(nameof(x), @"The X component must be >= 0 and < width"); } if (y < 0 || y >= Height) { throw new ArgumentOutOfRangeException(nameof(y), @"The Y component must be >= 0 and < height"); } return *(_scan0 + x + y * Stride); } /// <summary> /// Gets the pixel color at the given coordinates as an unsigned integer value. /// If the bitmap was not locked beforehands, an exception is thrown /// </summary> /// <param name="x">The X coordinate of the pixel to get</param> /// <param name="y">The Y coordinate of the pixel to get</param> /// <exception cref="InvalidOperationException">The fast bitmap is not locked</exception> /// <exception cref="ArgumentOutOfRangeException">The provided coordinates are out of bounds of the bitmap</exception> public uint GetPixelUInt(int x, int y) { if (!Locked) { throw new InvalidOperationException("The FastBitmap must be locked before any pixel operations are made"); } if (x < 0 || x >= Width) { throw new ArgumentOutOfRangeException(nameof(x), @"The X component must be >= 0 and < width"); } if (y < 0 || y >= Height) { throw new ArgumentOutOfRangeException(nameof(y), @"The Y component must be >= 0 and < height"); } return *((uint*)_scan0 + x + y * Stride); } /// <summary> /// Copies the contents of the given array of colors into this FastBitmap. /// Throws an ArgumentException if the count of colors on the array mismatches the pixel count from this FastBitmap /// </summary> /// <param name="colors">The array of colors to copy</param> /// <param name="ignoreZeroes">Whether to ignore zeroes when copying the data</param> public void CopyFromArray(int[] colors, bool ignoreZeroes = false) { if (colors.Length != Width * Height) { throw new ArgumentException(@"The number of colors of the given array mismatch the pixel count of the bitmap", nameof(colors)); } // Simply copy the argb values array // ReSharper disable once InconsistentNaming int* s0t = _scan0; fixed (int* source = colors) { // ReSharper disable once InconsistentNaming int* s0s = source; int count = Width * Height; if (!ignoreZeroes) { // Unfold the loop const int sizeBlock = 8; int rem = count % sizeBlock; count /= sizeBlock; while (count-- > 0) { *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); *(s0t++) = *(s0s++); } while (rem-- > 0) { *(s0t++) = *(s0s++); } } else { while (count-- > 0) { if (*(s0s) == 0) { s0t++; s0s++; continue; } *(s0t++) = *(s0s++); } } } } /// <summary> /// Clears the bitmap with the given color /// </summary> /// <param name="color">The color to clear the bitmap with</param> public void Clear(Color color) { Clear(color.ToArgb()); } /// <summary> /// Clears the bitmap with the given color /// </summary> /// <param name="color">The color to clear the bitmap with</param> public void Clear(int color) { bool unlockAfter = false; if (!Locked) { Lock(); unlockAfter = true; } // Clear all the pixels int count = Width * Height; int* curScan = _scan0; // Uniform color pixel values can be mem-set straight away int component = (color & 0xFF); if (component == ((color >> 8) & 0xFF) && component == ((color >> 16) & 0xFF) && component == ((color >> 24) & 0xFF)) { memset(_scan0, component, (ulong)(Height * Stride * BytesPerPixel)); } else { // Defines the ammount of assignments that the main while() loop is performing per loop. // The value specified here must match the number of assignment statements inside that loop const int assignsPerLoop = 8; int rem = count % assignsPerLoop; count /= assignsPerLoop; while (count-- > 0) { *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; *(curScan++) = color; } while (rem-- > 0) { *(curScan++) = color; } if (unlockAfter) { Unlock(); } } } /// <summary> /// Clears a square region of this image w/ a given color /// </summary> /// <param name="region"></param> /// <param name="color"></param> public void ClearRegion(Rectangle region, Color color) { ClearRegion(region, color.ToArgb()); } /// <summary> /// Clears a square region of this image w/ a given color /// </summary> /// <param name="region"></param> /// <param name="color"></param> public void ClearRegion(Rectangle region, int color) { var thisReg = new Rectangle(0, 0, Width, Height); if (!region.IntersectsWith(thisReg)) return; // If the region covers the entire image, use faster Clear(). if (region == thisReg) { Clear(color); return; } int minX = region.X; int maxX = region.X + region.Width; int minY = region.Y; int maxY = region.Y + region.Height; // Bail out of optimization if there's too few rows to make this worth it if (maxY - minY < 16) { for (int y = minY; y < maxY; y++) { for (int x = minX; x < maxX; x++) { *(_scan0 + x + y * Stride) = color; } } return; } ulong strideWidth = (ulong)region.Width * BytesPerPixel; // Uniform color pixel values can be mem-set straight away int component = (color & 0xFF); if (component == ((color >> 8) & 0xFF) && component == ((color >> 16) & 0xFF) && component == ((color >> 24) & 0xFF)) { for (int y = minY; y < maxY; y++) { memset(_scan0 + minX + y * Stride, component, strideWidth); } } else { // Prepare a horizontal slice of pixels that will be copied over each horizontal row down. var row = new int[region.Width]; fixed (int* pRow = row) { int count = region.Width; int rem = count % 8; count /= 8; int* pSrc = pRow; while (count-- > 0) { *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; *pSrc++ = color; } while (rem-- > 0) { *pSrc++ = color; } var sx = _scan0 + minX; for (int y = minY; y < maxY; y++) { memcpy(sx + y * Stride, pRow, strideWidth); } } } } /// <summary> /// Copies a region of the source bitmap into this fast bitmap /// </summary> /// <param name="source">The source image to copy</param> /// <param name="srcRect">The region on the source bitmap that will be copied over</param> /// <param name="destRect">The region on this fast bitmap that will be changed</param> /// <exception cref="ArgumentException">The provided source bitmap is the same bitmap locked in this FastBitmap</exception> public void CopyRegion(Bitmap source, Rectangle srcRect, Rectangle destRect) { // Throw exception when trying to copy same bitmap over if (source == _bitmap) { throw new ArgumentException(@"Copying regions across the same bitmap is not supported", nameof(source)); } var srcBitmapRect = new Rectangle(0, 0, source.Width, source.Height); var destBitmapRect = new Rectangle(0, 0, Width, Height); // Check if the rectangle configuration doesn't generate invalid states or does not affect the target image if (srcRect.Width <= 0 || srcRect.Height <= 0 || destRect.Width <= 0 || destRect.Height <= 0 || !srcBitmapRect.IntersectsWith(srcRect) || !destRect.IntersectsWith(destBitmapRect)) return; // Find the areas of the first and second bitmaps that are going to be affected srcBitmapRect = Rectangle.Intersect(srcRect, srcBitmapRect); // Clip the source rectangle on top of the destination rectangle in a way that clips out the regions of the original bitmap // that will not be drawn on the destination bitmap for being out of bounds srcBitmapRect = Rectangle.Intersect(srcBitmapRect, new Rectangle(srcRect.X, srcRect.Y, destRect.Width, destRect.Height)); destBitmapRect = Rectangle.Intersect(destRect, destBitmapRect); // Clip the source bitmap region yet again here srcBitmapRect = Rectangle.Intersect(srcBitmapRect, new Rectangle(-destRect.X + srcRect.X, -destRect.Y + srcRect.Y, Width, Height)); // Calculate the rectangle containing the maximum possible area that is supposed to be affected by the copy region operation int copyWidth = Math.Min(srcBitmapRect.Width, destBitmapRect.Width); int copyHeight = Math.Min(srcBitmapRect.Height, destBitmapRect.Height); if (copyWidth == 0 || copyHeight == 0) return; int srcStartX = srcBitmapRect.Left; int srcStartY = srcBitmapRect.Top; int destStartX = destBitmapRect.Left; int destStartY = destBitmapRect.Top; using (var fastSource = source.FastLock()) { ulong strideWidth = (ulong)copyWidth * BytesPerPixel; // Perform copies of whole pixel rows for (int y = 0; y < copyHeight; y++) { int destX = destStartX; int destY = destStartY + y; int srcX = srcStartX; int srcY = srcStartY + y; long offsetSrc = (srcX + srcY * fastSource.Stride); long offsetDest = (destX + destY * Stride); memcpy(_scan0 + offsetDest, fastSource._scan0 + offsetSrc, strideWidth); } } } /// <summary> /// Performs a copy operation of the pixels from the Source bitmap to the Target bitmap. /// If the dimensions or pixel depths of both images don't match, the copy is not performed /// </summary> /// <param name="source">The bitmap to copy the pixels from</param> /// <param name="target">The bitmap to copy the pixels to</param> /// <returns>Whether the copy proceedure was successful</returns> /// <exception cref="ArgumentException">The provided source and target bitmaps are the same</exception> public static bool CopyPixels(Bitmap source, Bitmap target) { if (source == target) { throw new ArgumentException(@"Copying pixels across the same bitmap is not supported", nameof(source)); } if (source.Width != target.Width || source.Height != target.Height || source.PixelFormat != target.PixelFormat) return false; using (FastBitmap fastSource = source.FastLock(), fastTarget = target.FastLock()) { memcpy(fastTarget.Scan0, fastSource.Scan0, (ulong)(fastSource.Height * fastSource.Stride * BytesPerPixel)); } return true; } /// <summary> /// Clears the given bitmap with the given color /// </summary> /// <param name="bitmap">The bitmap to clear</param> /// <param name="color">The color to clear the bitmap with</param> public static void ClearBitmap(Bitmap bitmap, Color color) { ClearBitmap(bitmap, color.ToArgb()); } /// <summary> /// Clears the given bitmap with the given color /// </summary> /// <param name="bitmap">The bitmap to clear</param> /// <param name="color">The color to clear the bitmap with</param> public static void ClearBitmap(Bitmap bitmap, int color) { using (var fb = bitmap.FastLock()) { fb.Clear(color); } } /// <summary> /// Copies a region of the source bitmap to a target bitmap /// </summary> /// <param name="source">The source image to copy</param> /// <param name="target">The target image to be altered</param> /// <param name="srcRect">The region on the source bitmap that will be copied over</param> /// <param name="destRect">The region on the target bitmap that will be changed</param> /// <exception cref="ArgumentException">The provided source and target bitmaps are the same bitmap</exception> public static void CopyRegion(Bitmap source, Bitmap target, Rectangle srcRect, Rectangle destRect) { var srcBitmapRect = new Rectangle(0, 0, source.Width, source.Height); var destBitmapRect = new Rectangle(0, 0, target.Width, target.Height); // If the copy operation results in an entire copy, use CopyPixels instead if (srcBitmapRect == srcRect && destBitmapRect == destRect && srcBitmapRect == destBitmapRect) { CopyPixels(source, target); return; } using (var fastTarget = target.FastLock()) { fastTarget.CopyRegion(source, srcRect, destRect); } } /// <summary> /// Returns a bitmap that is a slice of the original provided 32bpp Bitmap. /// The region must have a width and a height > 0, and must lie inside the source bitmap's area /// </summary> /// <param name="source">The source bitmap to slice</param> /// <param name="region">The region of the source bitmap to slice</param> /// <returns>A Bitmap that represents the rectangle region slice of the source bitmap</returns> /// <exception cref="ArgumentException">The provided bimap is not 32bpp</exception> /// <exception cref="ArgumentException">The provided region is invalid</exception> public static Bitmap SliceBitmap(Bitmap source, Rectangle region) { if (region.Width <= 0 || region.Height <= 0) { throw new ArgumentException(@"The provided region must have a width and a height > 0", nameof(region)); } var sliceRectangle = Rectangle.Intersect(new Rectangle(Point.Empty, source.Size), region); if (sliceRectangle.IsEmpty) { throw new ArgumentException(@"The provided region must not lie outside of the bitmap's region completely", nameof(region)); } var slicedBitmap = new Bitmap(sliceRectangle.Width, sliceRectangle.Height); CopyRegion(source, slicedBitmap, sliceRectangle, new Rectangle(0, 0, sliceRectangle.Width, sliceRectangle.Height)); return slicedBitmap; } #if NETSTANDARD public static void memcpy(IntPtr dest, IntPtr src, ulong count) { Buffer.MemoryCopy(src.ToPointer(), dest.ToPointer(), count, count); } public static void memcpy(void* dest, void* src, ulong count) { Buffer.MemoryCopy(src, dest, count, count); } public static void memset(void* dest, int value, ulong count) { Unsafe.InitBlock(dest, (byte)value, (uint)count); } #else /// <summary> /// .NET wrapper to native call of 'memcpy'. Requires Microsoft Visual C++ Runtime installed /// </summary> [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static extern IntPtr memcpy(IntPtr dest, IntPtr src, ulong count); /// <summary> /// .NET wrapper to native call of 'memcpy'. Requires Microsoft Visual C++ Runtime installed /// </summary> [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static extern IntPtr memcpy(void* dest, void* src, ulong count); /// <summary> /// .NET wrapper to native call of 'memset'. Requires Microsoft Visual C++ Runtime installed /// </summary> [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static extern IntPtr memset(void* dest, int value, ulong count); #endif /// <summary> /// Represents a disposable structure that is returned during Lock() calls, and unlocks the bitmap on Dispose calls /// </summary> public struct FastBitmapLocker : IDisposable { /// <summary> /// Gets the fast bitmap instance attached to this locker /// </summary> public FastBitmap FastBitmap { get; } /// <summary> /// Initializes a new instance of the FastBitmapLocker struct with an initial fast bitmap object. /// The fast bitmap object passed will be unlocked after calling Dispose() on this struct /// </summary> /// <param name="fastBitmap">A fast bitmap to attach to this locker which will be released after a call to Dispose</param> public FastBitmapLocker(FastBitmap fastBitmap) { FastBitmap = fastBitmap; } /// <summary> /// Disposes of this FastBitmapLocker, essentially unlocking the underlying fast bitmap /// </summary> public void Dispose() { if (FastBitmap.Locked) FastBitmap.Unlock(); } } } /// <summary> /// Describes a pixel format to use when locking a bitmap using <see cref="FastBitmap"/>. /// </summary> public enum FastBitmapLockFormat { /// <summary>Specifies that the format is 32 bits per pixel; 8 bits each are used for the red, green, and blue components. The remaining 8 bits are not used.</summary> Format32bppRgb = 139273, /// <summary>Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied, according to the alpha component.</summary> Format32bppPArgb = 925707, /// <summary>Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components.</summary> Format32bppArgb = 2498570, } /// <summary> /// Static class that contains fast bitmap extension methdos for the Bitmap class /// </summary> public static class FastBitmapExtensions { /// <summary> /// Locks this bitmap into memory and returns a FastBitmap that can be used to manipulate its pixels /// </summary> /// <param name="bitmap">The bitmap to lock</param> /// <returns>A locked FastBitmap</returns> public static FastBitmap FastLock(this Bitmap bitmap) { var fast = new FastBitmap(bitmap); fast.Lock(); return fast; } /// <summary> /// Locks this bitmap into memory and returns a FastBitmap that can be used to manipulate its pixels /// </summary> /// <param name="bitmap">The bitmap to lock</param> /// <param name="lockFormat">The underlying pixel format to use when locking the bitmap</param> /// <returns>A locked FastBitmap</returns> public static FastBitmap FastLock(this Bitmap bitmap, FastBitmapLockFormat lockFormat) { var fast = new FastBitmap(bitmap); fast.Lock(lockFormat); return fast; } /// <summary> /// Returns a deep clone of this Bitmap object, with all the data copied over. /// After a deep clone, the new bitmap is completely independent from the original /// </summary> /// <param name="bitmap">The bitmap to clone</param> /// <returns>A deep clone of this Bitmap object, with all the data copied over</returns> public static Bitmap DeepClone(this Bitmap bitmap) { return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat); } } }