c# – 如何绘制矩形集合的轮廓?
作为我正在研究的项目的一部分,我必须从图像中存储和恢复魔杖区域.为了获得存储数据,我正在使用
GetRegionData方法.根据规范指定,此方法:
我将RegionData.Data属性中保存的byte []存储在base64字符串中,所以我可以稍后通过一种非常规的方法检索RegionData: // This "instantiates" a RegionData object by simply initiating an object and setting the object type pointer to the specified type. // Constructors don't run,but in this very specific case - they don't have to. The RegionData's only member is a "data" byte array,which we set right after. var regionData = (RegionData)FormatterServices.GetUninitializedObject(typeof(RegionData)); regionData.Data = bytes; 然后我创建一个Region并在构造函数中传递上面的RegionData对象,并调用GetRegionScans来获取构成该区域的矩形对象: var region = new Region(regionData); RectangleF[] rectangles = region.GetRegionScans(new Matrix()); 这样我最终得到了一组用于绘制和重建区域的矩形.我已将整个绘图过程隔离到WinForms应用程序,并且我使用以下代码在图像控件上绘制此矩形集合: using (var g = Graphics.FromImage(picBox.Image)) { var p = new Pen(Color.Black,1f); var alternatePen = new Pen(Color.BlueViolet,1f); var b = new SolidBrush(picBox.BackColor); var niceBrush = new SolidBrush(Color.Orange); foreach (var r in rectangles) { g.DrawRectangle(p,new Rectangle(new Point((int)r.Location.X,(int)r.Location.Y),new Size((int)r.Width,(int)r.Height))); } } 上面的代码导致在我的图片控件中呈现以下内容: 这里的大纲是正确的 – 这正是我最初使用魔棒工具标记的内容.然而,当我绘制矩形时,我也最终得到了水平线,这些线不是原始魔棒选择的一部分.因此,我无法再查看实际图像,而我的魔棒工具现在使图像变得无用. 我想我只会在屏幕上绘制每个矩形的左右边缘,所以我最终会在屏幕上显示一堆点,为我勾勒出图像的轮廓.为此,我尝试了以下代码: var pointPair = new[] { new Point((int) r.Left,(int) r.Y),new Point((int) r.Right,(int) r.Y) }; g.DrawRectangle(p,pointPair[0].X,pointPair[0].Y,1,1); g.DrawRectangle(p,pointPair[1].X,pointPair[1].Y,1); 结果可以在下面观察到: 虽然距离较近,但它仍然没有雪茄. 我正在寻找一种连接点的方法,以便在我的区域中创建矩形的轮廓.对人类而言微不足道的事情,但我无法弄清楚如何指导计算机为我做这件事. 我已经尝试通过计算每个矩形的左和右点上的最邻近点,并将它们渲染为单独的矩形,从每个矩形创建新点,但无济于事. 任何帮助将不胜感激,因为我真的在这里不知所措. 谢谢! 解 感谢Peter Duniho的回答,我设法解决了这个问题.为了完整起见,我在下面包含了SafeHandle包装类和我用来完成这项工作的代码. 绘图代码 下面的代码是我用来绘制区域轮廓的代码. private void DrawRegionOutline(Graphics graphics,Color color,Region region) { var regionHandle = new SafeRegionHandle(region,region.GetHrgn(graphics)); var deviceContext = new SafeDeviceContextHandle(graphics); var brushHandle = new SafeBrushHandle(color); using (regionHandle) using (deviceContext) using (brushHandle) FrameRgn(deviceContext.DangerousGetHandle(),regionHandle.DangerousGetHandle(),brushHandle.DangerousGetHandle(),1); } SafeHandleNative SafeHandleZeroOrMinusOneIsInvalid周围的小包装,以确保在完成句柄后进行清理. [HostProtection(MayLeakOnAbort = true)] [SuppressUnmanagedCodeSecurity] public abstract class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid { #region Platform Invoke [DllImport("kernel32.dll",SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] protected internal static extern bool CloseHandle(IntPtr hObject); #endregion /// <summary> /// Initializes a new instance of the <see cref="SafeNativeHandle"/> class. /// </summary> protected SafeNativeHandle() : base(true) {} /// <summary> /// Initializes a new instance of the <see cref="SafeNativeHandle"/> class. /// </summary> /// <param name="handle">The handle.</param> protected SafeNativeHandle(IntPtr handle) : base(true) { SetHandle(handle); } } 我创建了三个其他包装器,一个用于Region对象,一个用于Brush,另一个用于设备上下文句柄.这些都继承自SafeNativeHandle,但为了不垃圾邮件,我只提供我用于下面区域的那个.其他两个包装器实际上是相同的,但使用相应的Win32 API来清理自己的资源. public class SafeRegionHandle : SafeNativeHandle { private readonly Region _region; /// <summary> /// Initializes a new instance of the <see cref="SafeRegionHandle" /> class. /// </summary> /// <param name="region">The region.</param> /// <param name="handle">The handle.</param> public SafeRegionHandle(Region region,IntPtr handle) { _region = region; base.handle = handle; } /// <summary> /// When overridden in a derived class,executes the code required to free the handle. /// </summary> /// <returns> /// true if the handle is released successfully; otherwise,in the event of a catastrophic failure,false. In this case,it generates a releaseHandleFailed MDA Managed Debugging Assistant. /// </returns> [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)] protected override bool ReleaseHandle() { try { _region.ReleaseHrgn(handle); } catch { return false; } return true; } } 解决方法
我还不完全确定我理解这个问题.然而,听起来好像你只是想通过概述它来绘制给定区域,而不是填充它.
不幸的是,据我所知,.NET API不支持这一点.但是,本机Windows API可以.这是一些应该做你想做的代码: [DllImport("gdi32")] static extern bool FrameRgn(System.IntPtr hDC,System.IntPtr hRgn,IntPtr hBrush,int nWidth,int nHeight); [DllImport("gdi32")] static extern IntPtr CreateSolidBrush(uint colorref); [DllImport("gdi32.dll")] static extern bool DeleteObject([In] IntPtr hObject); [StructLayout(LayoutKind.Explicit)] struct COLORREF { [FieldOffset(0)] public uint colorref; [FieldOffset(0)] public byte red; [FieldOffset(1)] public byte green; [FieldOffset(2)] public byte blue; public COLORREF(Color color) : this() { red = color.R; green = color.G; blue = color.B; } } void DrawRegion(Graphics graphics,Region region) { COLORREF colorref = new COLORREF(color); IntPtr hdc = IntPtr.Zero,hbrush = IntPtr.Zero,hrgn = IntPtr.Zero; try { hrgn = region.GetHrgn(graphics); hdc = graphics.GetHdc(); hbrush = CreateSolidBrush(colorref.colorref); FrameRgn(hdc,hrgn,hbrush,1); } finally { if (hrgn != IntPtr.Zero) { region.ReleaseHrgn(hrgn); } if (hbrush != IntPtr.Zero) { DeleteObject(hbrush); } if (hdc != IntPtr.Zero) { graphics.ReleaseHdc(hdc); } } } 从Paint事件处理程序或其他具有Graphics实例的适当上下文中调用DrawRegion()方法,例如在示例中绘??制到Image对象中. 显然,你可以使这个扩展方法更方便.另外,虽然在这个例子中我直接处理句柄的初始化和释放,但是更好的实现会将句柄包装在适当的SafeHandle子类中,这样你就可以方便地使用而不是try / finally,并获得备份完成(如果您忘记处置). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |