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: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908: 909: 910: 911: 912: 913: 914: 915: 916:
| private WebCamControler _cam1; private WebCamControler _cam2;
_cam1 = new WebCamControler(panelWebCam1, this); _cam2 = new WebCamControler(panelWebCam2, this);
public void StartWebCam(int i) { if (i == 1) _cam1.StartWebCam(VideoQuality1); if (i == 2) if (_webCam2IsVisible) _cam2.StartWebCam(VideoQuality2); }
public void CloseWebCam(int i) { if (i == 1) _cam1.CloseInterfaces(); if (i == 2) if(_webCam2IsVisible) _cam2.CloseInterfaces(); }
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } if (_cam1 != null) _cam1.CloseInterfaces(); if (_cam2 != null) _cam2.CloseInterfaces(); base.Dispose(disposing); _cam1.bitmapDone -= _cam1_bitmapDone; _cam2.bitmapDone -= _cam2_bitmapDone; _cam1.startCamera -= _cam1_startCamera; _cam2.startCamera -= _cam2_startCamera; _cam1.stopCamera -= _cam1_stopCamera; _cam2.stopCamera -= _cam2_stopCamera; _cam1.Dispose(); _cam2.Dispose(); _cam1 = null; _cam2 = null; }
Klasse ------------------------------------------------------------------
using System; using System.Collections; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Drawing; using System.Windows.Forms; using System.Diagnostics; using DShowNET; using DShowNET.Device; using SampleGrabberNET;
namespace WebCam { class WebCamControler : ISampleGrabberCB, IDisposable { #region constructor public WebCamControler(Panel panel, UserControl userControl) { _panel = panel; _userControl = userControl; } #endregion
#region DllImport [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern int OleCreatePropertyFrame( IntPtr hwndOwner, int x, int y, [MarshalAs(UnmanagedType.LPWStr)] string lpszCaption, int cObjects, [MarshalAs(UnmanagedType.Interface, ArraySubType = UnmanagedType.IUnknown)] ref object ppUnk, int cPages, IntPtr lpPageClsID, int lcid, int dwReserved, IntPtr lpvReserved); #endregion
#region private members
private Panel _panel = new Panel();
private UserControl _userControl = new UserControl();
private IBaseFilter capFilter;
private IGraphBuilder graphBuilder;
private ICaptureGraphBuilder2 capGraph;
private ISampleGrabber sampGrabber;
private IMediaControl mediaCtrl;
private IMediaEventEx mediaEvt;
private IVideoWindow videoWin;
private IBaseFilter baseGrabFlt;
private VideoInfoHeader videoInfoHeader;
private Bitmap _bitmap;
private DsDevice dev = null; private bool _disposed; private bool captured = true;
private bool _start = false;
private int bufferedSize;
private delegate void CaptureDone();
private byte[] savedArray;
private const int WM_GRAPHNOTIFY = 0x00008001; private const int WS_CHILD = 0x40000000; private const int WS_CLIPCHILDREN = 0x02000000; private const int WS_CLIPSIBLINGS = 0x04000000;
public int pWM_GRAPHNOTIFY { get { return WM_GRAPHNOTIFY; } }
public IMediaEventEx pmediaEvt { get { return mediaEvt; } }
private ArrayList capDevices; #endregion
#region internal member internal enum PlayState { Init, Stopped, Paused, Running } #endregion
#region internal functions int ISampleGrabberCB.SampleCB(double SampleTime, IMediaSample pSample) { return 0; }
int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) { if (captured || (savedArray == null)) {
return 0; } captured = true; bufferedSize = BufferLen;
if ((pBuffer != IntPtr.Zero) && (BufferLen > 1000) && (BufferLen <= savedArray.Length)) Marshal.Copy(pBuffer, savedArray, 0, BufferLen); else MessageBox.Show("Grab Fehler");
_userControl.BeginInvoke(new CaptureDone(this.OnCaptureDone));
return 0; } #endregion
#region public functions public int SampleCB(double SampleTime, IMediaSample pSample) { return 0; }
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) { return 0; }
public void StartWebCam(Size _size) { if (!_start) { try { _start = true; startCamera(false); DsDev.GetDevicesOfCat(FilterCategory.VideoInputDevice, out capDevices);
if (capDevices == null) { MessageBox.Show("Es wurde keine WebCam gefunden!"); return; }
if (_panel != null && _panel.Name == "panelWebCam1") { foreach (DsDevice webCam in capDevices) { if (webCam.Name == WebCam.WebCam1) {
dev = webCam; if (!StartupVideo(dev.Mon, _size)) break; }
} }
if (_panel != null && _panel.Name == "panelWebCam2") { foreach (DsDevice webCam in capDevices) { if (webCam.Name == WebCam.WebCam2) {
dev = webCam; if (!StartupVideo(dev.Mon, _size)) break; } } } startCamera(true); } catch (Exception ex) { throw new Exception(string.Format("{0} {1}", ex, ex.GetBaseException())); } } else { return; } }
public void ShowCams() { try { if (DsDev.GetDevicesOfCat(FilterCategory.VideoInputDevice, out capDevices)) { DeviceSelector selector = new DeviceSelector(capDevices); selector.ShowDialog(); dev = selector.SelectedDevice; } } catch(Exception ex) { Debug.WriteLine(ex.ToString()); } }
public string CheckFalseDirectXVersion() { string message = string.Empty; try { message = !DsUtils.IsCorrectDirectXVersion() ? string.Empty : "DirectX 8.1 oder höher ist installiert!"; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}", ex, ex.GetBaseException())); } return message; }
public void CloseInterfaces() { int hr; try { stopCamera(false); #if DEBUG if (rotCookie != 0) DsROT.RemoveGraphFromRot(ref rotCookie); #endif
if (mediaCtrl != null) { hr = mediaCtrl.Stop(); mediaCtrl = null; }
if (mediaEvt != null) { hr = mediaEvt.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero); mediaEvt = null; }
if (videoWin != null) { hr = videoWin.put_Visible(DsHlp.OAFALSE); hr = videoWin.put_Owner(IntPtr.Zero); videoWin = null; }
baseGrabFlt = null; if (sampGrabber != null) Marshal.ReleaseComObject(sampGrabber); sampGrabber = null;
if (capGraph != null) Marshal.ReleaseComObject(capGraph); capGraph = null;
if (graphBuilder != null) Marshal.ReleaseComObject(graphBuilder); graphBuilder = null;
if (capFilter != null) Marshal.ReleaseComObject(capFilter); capFilter = null;
if (capDevices != null) { foreach (DsDevice d in capDevices) d.Dispose(); capDevices = null; } stopCamera(true); _start = false; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
public void SnapShot() { if (_start) { int hr; try { if (sampGrabber == null) { MessageBox.Show("sampGrabber = null"); return; }
if (savedArray == null) { int size = videoInfoHeader.BmiHeader.ImageSize; savedArray = new byte[size + 64000]; }
captured = false; hr = sampGrabber.SetCallback(this, 1); } catch (Exception ex) { throw new Exception(ex.ToString()); } } }
public void OnGraphNotify() { try { DsEvCode code; int p1, p2, hr1 = 0; do { hr1 = mediaEvt.GetEvent(out code, out p1, out p2, 0); if (hr1 < 0) break; hr1 = mediaEvt.FreeEventParams(code, p1, p2); } while (hr1 == 0); } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
public void DisplayCameraPropeties() { try { if (capFilter != null) DisplayPropertyPage(capFilter); } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
protected virtual void Dispose(bool disposing) { if (disposing) { if (_panel != null) _panel.Dispose(); if (_bitmap != null) _bitmap.Dispose(); if (_panel != null) _panel.Dispose(); } _disposed = true; }
public void Dispose() { _disposed = true; Dispose(_disposed); GC.SuppressFinalize(this); }
#endregion
#region public events public delegate void BitmapDone(Bitmap _bitmap);
public event BitmapDone bitmapDone;
public delegate void StartCamera(bool start);
public event StartCamera startCamera;
public delegate void StopCamera(bool stop);
public event StopCamera stopCamera;
#endregion
#region private functions private bool StartupVideo(UCOMIMoniker mon, Size _size) { int hr; try { if (!CreateCaptureDevice(mon)) return false;
if (!GetInterfaces()) return false;
if (!SetupGraph(_size)) return false;
if (!SetupVideoWindow()) return false;
hr = mediaCtrl.Run(); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
return true; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
private bool SetupVideoWindow() { int hr; try { hr = videoWin.put_Owner(_panel.Handle); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
hr = videoWin.put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
ResizeVideoWindow();
hr = videoWin.put_Visible(DsHlp.OATRUE); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
hr = mediaEvt.SetNotifyWindow(_userControl.Handle, WM_GRAPHNOTIFY, IntPtr.Zero); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
return true; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex, ex.GetBaseException())); } }
private bool CreateCaptureDevice(UCOMIMoniker mon) { object capObj = null; try { Guid gbf = typeof(IBaseFilter).GUID; mon.BindToObject(null, null, ref gbf, out capObj); capFilter = (IBaseFilter)capObj; capObj = null; return true; } catch (Exception ee) { MessageBox.Show("Could not create capture device\r\n" + ee.Message, "DirectShow.NET", MessageBoxButtons.OK, MessageBoxIcon.Stop); return false; } finally { if (capObj != null) Marshal.ReleaseComObject(capObj); capObj = null; } }
private bool GetInterfaces() { Type comType = null; object comObj = null; try { comType = Type.GetTypeFromCLSID(Clsid.FilterGraph); if (comType == null) throw new NotImplementedException(@"DirectShow FilterGraph not installed/registered!"); comObj = Activator.CreateInstance(comType); graphBuilder = (IGraphBuilder)comObj; comObj = null;
Guid clsid = Clsid.CaptureGraphBuilder2; Guid riid = typeof(ICaptureGraphBuilder2).GUID; comObj = DsBugWO.CreateDsInstance(ref clsid, ref riid); capGraph = (ICaptureGraphBuilder2)comObj; comObj = null;
comType = Type.GetTypeFromCLSID(Clsid.SampleGrabber); if (comType == null) throw new NotImplementedException(@"DirectShow SampleGrabber not installed/registered!"); comObj = Activator.CreateInstance(comType); sampGrabber = (ISampleGrabber)comObj; comObj = null;
mediaCtrl = (IMediaControl)graphBuilder; videoWin = (IVideoWindow)graphBuilder; mediaEvt = (IMediaEventEx)graphBuilder; baseGrabFlt = (IBaseFilter)sampGrabber;
return true; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } finally { if (comObj != null) Marshal.ReleaseComObject(comObj); comObj = null; } }
private bool SetupGraph(Size _size) { int hr;
try { hr = capGraph.SetFiltergraph(graphBuilder); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
hr = graphBuilder.AddFilter(capFilter, "Ds.NET Video Capture Device");
if (hr < 0) Marshal.ThrowExceptionForHR(hr);
if (((WebCam)_userControl).VideoSettingsDialog) DsUtils.ShowCapPinDialog(capGraph, capFilter, _panel.Handle);
AMMediaType media = new AMMediaType(); media.majorType = MediaType.Video; media.subType = MediaSubType.RGB24; media.formatType = FormatType.VideoInfo; hr = sampGrabber.SetMediaType(media);
if (!((WebCam)_userControl).VideoSettingsDialog) { SetCaptureResolution(_size); }
if (hr < 0) Marshal.ThrowExceptionForHR(hr);
hr = graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber"); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
Guid cat = PinCategory.Preview; Guid med = MediaType.Video; hr = capGraph.RenderStream(ref cat, ref med, capFilter, null, null); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
cat = PinCategory.Capture; med = MediaType.Video; hr = capGraph.RenderStream(ref cat, ref med, capFilter, null, baseGrabFlt); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
media = new AMMediaType(); hr = sampGrabber.GetConnectedMediaType(media); if (hr < 0) Marshal.ThrowExceptionForHR(hr); if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero)) throw new NotSupportedException("Unknown Grabber Media Format");
videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader)); Marshal.FreeCoTaskMem(media.formatPtr); media.formatPtr = IntPtr.Zero;
hr = sampGrabber.SetBufferSamples(false); if (hr == 0) hr = sampGrabber.SetOneShot(false); if (hr == 0) hr = sampGrabber.SetCallback(null, 0); if (hr < 0) Marshal.ThrowExceptionForHR(hr);
return true; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
private void SetCaptureResolution(Size size) { Guid streamConfigId = typeof(IAMStreamConfig).GUID; Guid ycat = PinCategory.Capture; Guid ymed = MediaType.Video; object videoConfigObj; int hr = capGraph.FindInterface(ref ycat, ref ymed, capFilter, ref streamConfigId, out videoConfigObj);
Marshal.ThrowExceptionForHR(hr);
IAMStreamConfig videoConfig = videoConfigObj as IAMStreamConfig;
if (null == videoConfig)
throw new COMException("Cannot obtain IAMStreamConfig");
int capsCount, capSize;
hr = videoConfig.GetNumberOfCapabilities(out capsCount, out capSize); Marshal.ThrowExceptionForHR(hr);
VideoInfoHeader vih = new VideoInfoHeader(); IntPtr pSC = Marshal.AllocHGlobal(capSize);
AMMediaType mt = null;
int videoFormatIndex = -1; long frameInterval = 10000000 / 25; for (int i = 0; i < capsCount; ++i) {
hr = videoConfig.GetStreamCaps(i, out mt, pSC); Marshal.ThrowExceptionForHR(hr);
Marshal.PtrToStructure(mt.formatPtr, vih);
string capline = String.Format("width:{0}, height:{1}, colorDepth: {2}", vih.BmiHeader.Width, vih.BmiHeader.Height, vih.BmiHeader.BitCount); Debug.WriteLine(capline);
if (vih.BmiHeader.Width == size.Width && vih.BmiHeader.Height == size.Height && vih.BmiHeader.BitCount == 24) { vih.BmiHeader.Width = size.Width; vih.BmiHeader.Height = size.Height; videoFormatIndex = i; } }
if (videoFormatIndex != -1) { hr = videoConfig.GetStreamCaps(videoFormatIndex, out mt, pSC); Marshal.ThrowExceptionForHR(hr); Marshal.PtrToStructure(mt.formatPtr, vih); vih.AvgTimePerFrame = frameInterval; Marshal.StructureToPtr(vih, mt.formatPtr, false);
hr = videoConfig.SetFormat(mt); Marshal.ThrowExceptionForHR(hr); }
Marshal.FreeHGlobal(pSC); }
private void ResizeVideoWindow() { if (videoWin != null) { Rectangle rc = _panel.ClientRectangle; videoWin.SetWindowPosition(0, 0, rc.Right, rc.Bottom); } }
private void OnCaptureDone() { Bitmap b = null; try { int hr; hr = sampGrabber.SetCallback(null, 0);
int w = videoInfoHeader.BmiHeader.Width; int h = videoInfoHeader.BmiHeader.Height; if (((w & 0x03) != 0) || (w < 32) || (w > 4096) || (h < 32) || (h > 4096)) MessageBox.Show("Fehler bei der Bildverarbeitung"); int stride = w * 3;
GCHandle handle = GCHandle.Alloc(savedArray, GCHandleType.Pinned); int scan0 = (int)handle.AddrOfPinnedObject(); scan0 += (h - 1) * stride; b = new Bitmap(w, h, -stride, PixelFormat.Format24bppRgb, (IntPtr)scan0); _bitmap = new Bitmap(b); bitmapDone(b); handle.Free(); savedArray = null; } catch (Exception ex) { throw new Exception(string.Format("{0}{1}",ex,ex.GetBaseException())); } }
private void DisplayPropertyPage(IBaseFilter dev) { ISpecifyPropertyPages pProp = dev as ISpecifyPropertyPages; int hr = 0;
DsCAUUID caGUID; hr = pProp.GetPages(out caGUID); object oDevice = (object)dev; hr = OleCreatePropertyFrame(_userControl.Handle, 0, 0, "", 1, ref oDevice, caGUID.cElems, caGUID.pElems, 0, 0, IntPtr.Zero);
Marshal.ReleaseComObject(oDevice);
Marshal.FreeCoTaskMem(caGUID.pElems); } #endregion
#region public properties public Bitmap Bitmap { get { return _bitmap; } set { _bitmap = value; } } #endregion
#region debug #if DEBUG private int rotCookie = 0; #endif #endregion } } |