using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace SmartCoalApplication.SystemLayer { public static class PdnGraphics { public static PropertyItem CreatePropertyItem() { PropertyItem2 pi2 = new PropertyItem2(0, 0, 0, new byte[0]); return pi2.ToPropertyItem(); } public static PropertyItem ClonePropertyItem(PropertyItem pi) { byte[] valueClone; if (pi.Value == null) { valueClone = new byte[0]; } else { valueClone = (byte[])pi.Value.Clone(); } PropertyItem2 pi2 = new PropertyItem2(pi.Id, pi.Len, pi.Type, valueClone); return pi2.ToPropertyItem(); } public static string SerializePropertyItem(PropertyItem pi) { PropertyItem2 pi2 = PropertyItem2.FromPropertyItem(pi); return pi2.ToBlob(); } public static PropertyItem DeserializePropertyItem(string piBlob) { PropertyItem2 pi2 = PropertyItem2.FromBlob(piBlob); return pi2.ToPropertyItem(); } internal unsafe static void GetRegionScans(IntPtr hRgn, out Rectangle[] scans, out int area) { uint bytes = 0; int countdown = 100; int error = 0; while (countdown > 0) { bytes = SafeNativeMethods.GetRegionData(hRgn, 0, (NativeStructs.RGNDATA*)IntPtr.Zero); error = Marshal.GetLastWin32Error(); if (bytes == 0) { --countdown; System.Threading.Thread.Sleep(5); } else { break; } } // But if we retry several times and it still messes up then we will finally give up. if (bytes == 0) { throw new Win32Exception(error, "GetRegionData returned " + bytes.ToString() + ", GetLastError() = " + error.ToString()); } byte* data; // Up to 512 bytes, allocate on the stack. Otherwise allocate from the heap. if (bytes <= 512) { byte* data1 = stackalloc byte[(int)bytes]; data = data1; } else { data = (byte*)Memory.Allocate(bytes).ToPointer(); } try { NativeStructs.RGNDATA* pRgnData = (NativeStructs.RGNDATA*)data; uint result = SafeNativeMethods.GetRegionData(hRgn, bytes, pRgnData); if (result != bytes) { throw new OutOfMemoryException("SafeNativeMethods.GetRegionData returned 0"); } NativeStructs.RECT* pRects = NativeStructs.RGNDATA.GetRectsPointer(pRgnData); scans = new Rectangle[pRgnData->rdh.nCount]; area = 0; for (int i = 0; i < scans.Length; ++i) { scans[i] = Rectangle.FromLTRB(pRects[i].left, pRects[i].top, pRects[i].right, pRects[i].bottom); area += scans[i].Width * scans[i].Height; } pRects = null; pRgnData = null; } finally { if (bytes > 512) { Memory.Free(new IntPtr(data)); } } } /// /// Retrieves an array of rectangles that approximates a region, and computes the /// pixel area of it. This method is necessary to work around some bugs in .NET /// and to increase performance for the way in which we typically use this data. /// /// The Region to retrieve data from. /// An array of Rectangle to put the scans into. /// An integer to write the computed area of the region into. /// /// Note to implementors: Simple implementations may simple call region.GetRegionScans() /// and process the data for the 'out' variables. public static void GetRegionScans(Region region, out Rectangle[] scans, out int area) { using (NullGraphics nullGraphics = new NullGraphics()) { IntPtr hRgn = IntPtr.Zero; try { hRgn = region.GetHrgn(nullGraphics.Graphics); GetRegionScans(hRgn, out scans, out area); } finally { if (hRgn != IntPtr.Zero) { SafeNativeMethods.DeleteObject(hRgn); hRgn = IntPtr.Zero; } } } GC.KeepAlive(region); } } }