最近手头工作比较轻松了一点就继续研究和完善之前的录屏软件,使用AForge最大的问题在于:最原始的只能够录全屏,而自定义的录屏需要更改非常多的细节:like follows:
1、需要支持区域化录屏;
2、需要支持麦克风录音,并且混音在视频中,同步;
3、需要支持系统声音录取、并且需要混音在视频中,同步;
4、需要支持捕获光标,并且自定义颜色、描边,最重要的是你需要在区域录屏的时候支持坐标位置更新(相对比较难);
前面3个已经在前面的文章介绍了,这里不再赘述。着重列出第4点的内容以及如何解决。如果正在研究录屏这块的朋友们,千万别去copy那什么网上有限制时间录制和收费的录制,特别是有些很恶心的还发表长篇大论写的如何如何实现(的确技术不可否认是实现了),其实最后还是要你付费才能完全使用,就问你恶不恶心!
好了,废话不多说,我们来一一解决;
首先获取系统光标有两种方式,第一种是直接通过系统API进行获取光标,这个是完全记录系统光标在做什么。随着系统光标变化而变化的。这边有用到的是几个类:
第一种方式:【CursorHelper.cs】、【GDIStuff.cs】、【Win32Stuff.cs】相对复杂一些;我就在代码中直接显示就好了,不需要引用任何其他的东西;
/// <summary> /// The rt global cursor. /// </summary> public class CursorHelper { #region Constants /// <summary> /// The curso r_ showing. /// </summary> private const int CURSOR_SHOWING = 1; #endregion #region Public Methods and Operators /// <summary> /// The capture cursor. /// </summary> /// <param name="x"> /// The x. /// </param> /// <param name="y"> /// The y. /// </param> /// <returns> /// The <see cref="Bitmap"/>. /// </returns> public static Bitmap CaptureCursor(ref int x, ref int y) { Win32Stuff.CURSORINFO cursorInfo = new Win32Stuff.CURSORINFO(); cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); if (!Win32Stuff.GetCursorInfo(out cursorInfo)) { return null; } if (cursorInfo.flags != Win32Stuff.CURSOR_SHOWING) { return null; } IntPtr hicon = Win32Stuff.CopyIcon(cursorInfo.hCursor); if (hicon == IntPtr.Zero) { return null; } Win32Stuff.ICONINFO iconInfo; if (!Win32Stuff.GetIconInfo(hicon, out iconInfo)) { return null; } x = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot); y = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot); using (Bitmap maskBitmap = Bitmap.FromHbitmap(iconInfo.hbmMask)) { // Is this a monochrome cursor? if (maskBitmap.Height == maskBitmap.Width * 2) { Bitmap resultBitmap = new Bitmap(maskBitmap.Width, maskBitmap.Width); using (Graphics desktopGraphics = Graphics.FromHwnd(Win32Stuff.GetDesktopWindow())) { IntPtr desktopHdc = desktopGraphics.GetHdc(); IntPtr maskHdc = GDIStuff.CreateCompatibleDC(desktopHdc); IntPtr oldPtr = GDIStuff.SelectObject(maskHdc, maskBitmap.GetHbitmap()); using (Graphics resultGraphics = Graphics.FromImage(resultBitmap)) { IntPtr resultHdc = resultGraphics.GetHdc(); // These two operation will result in a black cursor over a white background. // Later in the code, a call to MakeTransparent() will get rid of the white background. GDIStuff.BitBlt( resultHdc, 0, 0, 32, 32, maskHdc, 0, 32, (int)GDIStuff.TernaryRasterOperations.SRCCOPY); GDIStuff.BitBlt( resultHdc, 0, 0, 32, 32, maskHdc, 0, 0, (int)GDIStuff.TernaryRasterOperations.SRCINVERT); resultGraphics.ReleaseHdc(resultHdc); GDIStuff.DeleteDC(resultHdc); GDIStuff.DeleteObject(resultHdc); } IntPtr newPtr = GDIStuff.SelectObject(maskHdc, oldPtr); GDIStuff.DeleteObject(oldPtr); GDIStuff.DeleteObject(newPtr); GDIStuff.DeleteDC(maskHdc); desktopGraphics.ReleaseHdc(desktopHdc); GDIStuff.DeleteDC(desktopHdc); } // Remove the white background from the BitBlt calls, // resulting in a black cursor over a transparent background. resultBitmap.MakeTransparent(Color.White); return resultBitmap; } } //// Delete the mask, if present. // if (iconInfo.hbmMask != IntPtr.Zero) // { // DeleteObject(iconInfo.hbmMask); // } //// Delete the color bitmap, if present. // if (iconInfo.hbmColor != IntPtr.Zero) // { // DeleteObject(iconInfo.hbmColor); // } using (Icon icon = Icon.FromHandle(hicon)) { return icon.ToBitmap(); } } #endregion #region Methods /// <summary> /// The copy icon. /// </summary> /// <param name="hIcon"> /// The h icon. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("user32.dll")] private static extern IntPtr CopyIcon(IntPtr hIcon); /// <summary> /// The delete object. /// </summary> /// <param name="hDc"> /// The h dc. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll")] private static extern IntPtr DeleteObject(IntPtr hDc); /// <summary> /// The destroy icon. /// </summary> /// <param name="hIcon"> /// The h icon. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("user32.dll")] private static extern bool DestroyIcon(IntPtr hIcon); /// <summary> /// The get cursor info. /// </summary> /// <param name="pci"> /// The pci. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("user32.dll")] private static extern bool GetCursorInfo(out CURSORINFO pci); /// <summary> /// The get gdi handle count. /// </summary> /// <returns> /// The <see cref="int" />. /// </returns> private static int GetGDIHandleCount() { return GetGuiResources(Process.GetCurrentProcess().Handle, 0); } /// <summary> /// The get gui resources. /// </summary> /// <param name="hProcess"> /// The h process. /// </param> /// <param name="uiFlags"> /// The ui flags. /// </param> /// <returns> /// The <see cref="int"/>. /// </returns> [DllImport("user32.dll")] private static extern int GetGuiResources(IntPtr hProcess, int uiFlags); /// <summary> /// The get icon info. /// </summary> /// <param name="hIcon"> /// The h icon. /// </param> /// <param name="piconinfo"> /// The piconinfo. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("user32.dll")] private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo); /// <summary> /// The get user handle count. /// </summary> /// <returns> /// The <see cref="int" />. /// </returns> private static int GetUserHandleCount() { return GetGuiResources(Process.GetCurrentProcess().Handle, 1); } /// <summary> /// The handle message. /// </summary> /// <param name="message"> /// The message. /// </param> private static void HandleMessage(string message) { Debug.WriteLine("HC: " + message + ": GDI: " + GetGDIHandleCount() + ": User: " + GetUserHandleCount()); } #endregion /// <summary> /// The cursorinfo. /// </summary> [StructLayout(LayoutKind.Sequential)] private struct CURSORINFO { // Fields /// <summary> /// The cb size. /// </summary> [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")] public int cbSize; /// <summary> /// The flags. /// </summary> [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")] public int flags; /// <summary> /// The h cursor. /// </summary> [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")] public IntPtr hCursor; /// <summary> /// The pt screen pos. /// </summary> [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here."), SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Reviewed. Suppression is OK here.")] public POINT ptScreenPos; } /// <summary> /// The iconinfo. /// </summary> [StructLayout(LayoutKind.Sequential)] private struct ICONINFO { // Fields /// <summary> /// The f icon. /// </summary> public bool fIcon; /// <summary> /// The x hotspot. /// </summary> public int xHotspot; /// <summary> /// The y hotspot. /// </summary> public int yHotspot; // Handle of the icon’s bitmask bitmap. /// <summary> /// The hbm mask. /// </summary> public IntPtr hbmMask; // Handle of the icon’s color bitmap. Optional for monochrome icons. /// <summary> /// The hbm color. /// </summary> public IntPtr hbmColor; } /// <summary> /// The point. /// </summary> [StructLayout(LayoutKind.Sequential)] private struct POINT { // Fields /// <summary> /// The x. /// </summary> public int x; /// <summary> /// The y. /// </summary> public int y; } ///// <summary> ///// The capture cursor. ///// </summary> ///// <param name="x"> ///// The x. ///// </param> ///// <param name="y"> ///// The y. ///// </param> ///// <returns> ///// The <see cref="Bitmap"/>. ///// </returns> // public static Bitmap CaptureCursor(ref int x, ref int y) // { // try // { // // Return value initially nothing // Bitmap bmp = null; // CURSORINFO curInfo = new CURSORINFO(); // curInfo.cbSize = Marshal.SizeOf(curInfo); // // HandleMessage("Start") // if (GetCursorInfo(ref curInfo)) // { // if (curInfo.flags == CURSOR_SHOWING) // { // IntPtr hicon = CopyIcon(curInfo.hCursor); // if (hicon != IntPtr.Zero) // { // ICONINFO icoInfo = default(ICONINFO); // if (GetIconInfo(hicon, ref icoInfo)) // { // // Delete the mask, if present. // if (icoInfo.hbmMask != IntPtr.Zero) // { // DeleteObject(icoInfo.hbmMask); // } // // Delete the color bitmap, if present. // if (icoInfo.hbmColor != IntPtr.Zero) // { // DeleteObject(icoInfo.hbmColor); // } // x = curInfo.ptScreenPos.x - icoInfo.xHotspot; // y = curInfo.ptScreenPos.y - icoInfo.yHotspot; // } // Icon ic = Icon.FromHandle(hicon); // bmp = ic.ToBitmap(); // // Must destroy the icon object we got from CopyIcon // DestroyIcon(hicon); // } // } // } // // HandleMessage("End") // return bmp; // } // catch // { // return null; // } // } }
/// <summary> /// The gdi stuff. /// </summary> internal class GDIStuff { #region Constants /// <summary> /// The srccopy. /// </summary> public const int SRCCOPY = 13369376; #endregion #region Enums /// <summary> /// Specifies a raster-operation code. These codes define how the color data for the /// source rectangle is to be combined with the color data for the destination /// rectangle to achieve the final color. /// </summary> public enum TernaryRasterOperations { /// <summary>dest = source</summary> SRCCOPY = 0x00CC0020, /// <summary>dest = source OR dest</summary> SRCPAINT = 0x00EE0086, /// <summary>dest = source AND dest</summary> SRCAND = 0x008800C6, /// <summary>dest = source XOR dest</summary> SRCINVERT = 0x00660046, /// <summary>dest = source AND (NOT dest)</summary> SRCERASE = 0x00440328, /// <summary>dest = (NOT source)</summary> NOTSRCCOPY = 0x00330008, /// <summary>dest = (NOT src) AND (NOT dest)</summary> NOTSRCERASE = 0x001100A6, /// <summary>dest = (source AND pattern)</summary> MERGECOPY = 0x00C000CA, /// <summary>dest = (NOT source) OR dest</summary> MERGEPAINT = 0x00BB0226, /// <summary>dest = pattern</summary> PATCOPY = 0x00F00021, /// <summary>dest = DPSnoo</summary> PATPAINT = 0x00FB0A09, /// <summary>dest = pattern XOR dest</summary> PATINVERT = 0x005A0049, /// <summary>dest = (NOT dest)</summary> DSTINVERT = 0x00550009, /// <summary>dest = BLACK</summary> BLACKNESS = 0x00000042, /// <summary>dest = WHITE</summary> WHITENESS = 0x00FF0062, /// <summary> /// Capture window as seen on screen. This includes layered windows /// such as WPF windows with AllowsTransparency="true" /// </summary> CAPTUREBLT = 0x40000000 } #endregion #region Public Methods and Operators /// <summary> /// The bit blt. /// </summary> /// <param name="hdcDest"> /// The hdc dest. /// </param> /// <param name="xDest"> /// The x dest. /// </param> /// <param name="yDest"> /// The y dest. /// </param> /// <param name="wDest"> /// The w dest. /// </param> /// <param name="hDest"> /// The h dest. /// </param> /// <param name="hdcSource"> /// The hdc source. /// </param> /// <param name="xSrc"> /// The x src. /// </param> /// <param name="ySrc"> /// The y src. /// </param> /// <param name="RasterOp"> /// The raster op. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "BitBlt")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "Reviewed. Suppression is OK here.")] public static extern bool BitBlt( IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp); /// <summary> /// The create compatible bitmap. /// </summary> /// <param name="hdc"> /// The hdc. /// </param> /// <param name="nWidth"> /// The n width. /// </param> /// <param name="nHeight"> /// The n height. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")] public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); /// <summary> /// The create compatible dc. /// </summary> /// <param name="hdc"> /// The hdc. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")] public static extern IntPtr CreateCompatibleDC(IntPtr hdc); /// <summary> /// The create dc. /// </summary> /// <param name="lpszDriver"> /// The lpsz driver. /// </param> /// <param name="lpszDevice"> /// The lpsz device. /// </param> /// <param name="lpszOutput"> /// The lpsz output. /// </param> /// <param name="lpInitData"> /// The lp init data. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "CreateDC")] public static extern IntPtr CreateDC(IntPtr lpszDriver, string lpszDevice, IntPtr lpszOutput, IntPtr lpInitData); /// <summary> /// The delete dc. /// </summary> /// <param name="hDc"> /// The h dc. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "DeleteDC")] public static extern IntPtr DeleteDC(IntPtr hDc); /// <summary> /// The delete object. /// </summary> /// <param name="hDc"> /// The h dc. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] public static extern IntPtr DeleteObject(IntPtr hDc); /// <summary> /// The select object. /// </summary> /// <param name="hdc"> /// The hdc. /// </param> /// <param name="bmp"> /// The bmp. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("gdi32.dll", EntryPoint = "SelectObject")] public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp); #endregion }
/// <summary> /// The win 32 stuff. /// </summary> internal class Win32Stuff { #region Constants /// <summary> /// The curso r_ showing. /// </summary> public const int CURSOR_SHOWING = 0x00000001; /// <summary> /// The s m_ cxscreen. /// </summary> public const int SM_CXSCREEN = 0; /// <summary> /// The s m_ cyscreen. /// </summary> public const int SM_CYSCREEN = 1; #endregion #region Public Methods and Operators /// <summary> /// The copy icon. /// </summary> /// <param name="hIcon"> /// The h icon. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "CopyIcon")] public static extern IntPtr CopyIcon(IntPtr hIcon); /// <summary> /// The get cursor info. /// </summary> /// <param name="pci"> /// The pci. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "GetCursorInfo")] public static extern bool GetCursorInfo(out CURSORINFO pci); /// <summary> /// The get dc. /// </summary> /// <param name="ptr"> /// The ptr. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "GetDC")] public static extern IntPtr GetDC(IntPtr ptr); /// <summary> /// The get desktop window. /// </summary> /// <returns> /// The <see cref="IntPtr" />. /// </returns> [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] public static extern IntPtr GetDesktopWindow(); /// <summary> /// The get icon info. /// </summary> /// <param name="hIcon"> /// The h icon. /// </param> /// <param name="piconinfo"> /// The piconinfo. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "GetIconInfo")] public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo); /// <summary> /// The get system metrics. /// </summary> /// <param name="abc"> /// The abc. /// </param> /// <returns> /// The <see cref="int"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")] public static extern int GetSystemMetrics(int abc); /// <summary> /// The get window dc. /// </summary> /// <param name="ptr"> /// The ptr. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "GetWindowDC")] public static extern IntPtr GetWindowDC(int ptr); /// <summary> /// The release dc. /// </summary> /// <param name="hWnd"> /// The h wnd. /// </param> /// <param name="hDc"> /// The h dc. /// </param> /// <returns> /// The <see cref="IntPtr"/>. /// </returns> [DllImport("user32.dll", EntryPoint = "ReleaseDC")] public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); #endregion /// <summary> /// The cursorinfo. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct CURSORINFO { /// <summary> /// The cb size. /// </summary> public int cbSize; // Specifies the size, in bytes, of the structure. /// <summary> /// The flags. /// </summary> public int flags; // Specifies the cursor state. This parameter can be one of the following values: /// <summary> /// The h cursor. /// </summary> public IntPtr hCursor; // Handle to the cursor. /// <summary> /// The pt screen pos. /// </summary> public POINT ptScreenPos; // A POINT structure that receives the screen coordinates of the cursor. } /// <summary> /// The iconinfo. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct ICONINFO { /// <summary> /// The f icon. /// </summary> public bool fIcon; // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies /// <summary> /// The x hotspot. /// </summary> public int xHotspot; // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot /// <summary> /// The y hotspot. /// </summary> public int yHotspot; // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot /// <summary> /// The hbm mask. /// </summary> public IntPtr hbmMask; // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, /// <summary> /// The hbm color. /// </summary> public IntPtr hbmColor; // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this } /// <summary> /// The point. /// </summary> [StructLayout(LayoutKind.Sequential)] public struct POINT { /// <summary> /// The x. /// </summary> public int x; /// <summary> /// The y. /// </summary> public int y; } }
OK,类已经铺垫好了,接下来就在你的视频捕获方法中放入:关键方法--CursorHelper.CaptureCursor(ref x,ref y);
1 Graphics g = Graphics.FromImage(bitmap);//编辑原始视频帧 2 g.SmoothingMode = SmoothingMode.AntiAlias;//设置鼠标质量 3 g.InterpolationMode = InterpolationMode.HighQualityBicubic; 4 g.PixelOffsetMode = PixelOffsetMode.HighQuality; 5 var x = _currentPoint.X; 6 var y = _currentPoint.Y; 7 var cursorBmp = CursorHelper.CaptureCursor(ref x, ref y); 8 if (cursorBmp != null) 9 { 10 g.DrawImage(cursorBmp, _currentPoint); 11 } 12 cursorBmp.Dispose();
**注释说明:其中_currentPoint 是相对于屏幕的坐标Point** 获取方法是--
_currentPoint = System.Windows.Forms.Cursor.Position;//(大屏坐标)
**注释说明:其中bitmap是当前获取的最原始的视频帧(不包含任何的例如光标-声音-什么锤子之类的哈哈哈)**,此类方法就是把原始视频帧重新编辑!
第二种方式:相对简单一点,获取光标_currentPoint还是使用上面的方法,但是不同的地方是我要自定义光标icon,这个又有一点难点就是如何画怎么画;---项目中采用的是外圈描边,内边填充方式;
1 SolidBrush myBrush = new SolidBrush(System.Drawing.Color.FromArgb(50, ColorTranslator.FromHtml("#你的填充颜色")));//设置透明度跟填充颜色 2 System.Drawing.Pen p = new System.Drawing.Pen(ColorTranslator.FromHtml("#你的描边颜色"));//设置透明度跟描边颜色 3 Graphics g = Graphics.FromImage(bitmap);//编辑原始视频帧 4 g.SmoothingMode = SmoothingMode.AntiAlias;//设置鼠标质量 5 g.InterpolationMode = InterpolationMode.HighQualityBicubic; 6 g.PixelOffsetMode = PixelOffsetMode.HighQuality; 7 g.DrawEllipse(p, new Rectangle(_currentPoint.X - this.screenArea.Left, _currentPoint.Y - this.screenArea.Top, 25, 25));//描边 8 g.FillEllipse(myBrush, new Rectangle(_currentPoint.X - this.screenArea.Left, _currentPoint.Y - this.screenArea.Top, 25, 25));//填充圆形区域 9 myBrush.Dispose(); 10 p.Dispose(); 11 g.Flush();
**注释:在上述这种方式中特别注意,原始的方法比如你是全屏录制则采用以下方式即可,还有自定义笔刷的画法,我想做完给大家分享。**
1 g.DrawEllipse(p, new Rectangle(_currentPoint.X , _currentPoint.Y, 25, 25));//描边 2 g.FillEllipse(myBrush, new Rectangle(_currentPoint.X , _currentPoint.Y, 25, 25));//填充圆形区域
**注释:如果你的录屏方式也存在区域模式,那么就采用 当前光标位置X轴减去你录屏区域的左坐标,当前光标位置Y轴减去你录屏的顶坐标即可获取,这种方式自适应任何区域**
以上是个人在完善时候研究的成果,在此希望把它们分享给更多正在研究的伙伴们,因为研究的时候的确遇到了非常多的问题,我希望这些文章能够给你们一些方向研究,加快你们的开发进度。