Sehr schnell Grid zeichnen ins Control

    • C#
    • .NET (FX) 4.5–4.8

    Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von Bluespide.

      Sehr schnell Grid zeichnen ins Control

      Hallo,

      ich möchte gern mein Programm-Code vorstellen zum sehr schnellen Zeichen eines Grid in einem eigenen Control, für Koordinaten-Systeme oder ähnliches.
      Verbesserungen sind immer gewünscht ;D

      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Drawing;
      3. using System.Drawing.Drawing2D;
      4. using System.Drawing.Imaging;
      5. using System.Runtime.CompilerServices;
      6. using System.Threading;
      7. using System.Threading.Tasks;
      8. using System.Windows.Forms;
      9. //public unsafe class GridBlock : Control { //old
      10. public unsafe class GridBlock : Panel {
      11. private Graphics _graphics;
      12. //private int _halfWidth; //Vertikale mitte //old
      13. //private int _halfHeight; //horizontale mitte //old
      14. private int _offsetX; //verschiebung der Maus vom _mouseDownPosition aus in x-Achse
      15. private int _offsetY; //verschiebung der Maus vom _mouseDownPosition aus in Y-Achse
      16. private bool _leftMouseButtonHold; //linke Maustaste zum verschieben des Grid
      17. private Point _mouseDownPosition; //allgemeine klick Position der Maustasten
      18. private Point _mouseLeftDownPosition; //click position der linken Maustaste
      19. #region LoopSetting
      20. private ParallelOptions _parallelOptions;
      21. #endregion
      22. #region Grid
      23. ///Settings
      24. #region Settings
      25. private int _gridIndex = 0; //Index vom zu zeichnenden Bitmap
      26. private Bitmap[] _grid; //Bitmaps auf den gezeichnet wird
      27. private int*[] _gridPtrs; //Pointer zu den Bitmap-Array
      28. private int _gridPixelCount; //Anzahl der gesamt Pixel vom Bitmap
      29. //private Rectangle _gridRectangle; //
      30. private Color _gridColor; //Farbe der Grid-Linen
      31. private int _gridIntColor; //
      32. private Color _gridBackground; //Hintergrund Farbe es Grid
      33. private int _gridIntBackground; //
      34. private Pen _gridPen;
      35. private int _gridStroke; //Breite der Grid-Linien
      36. private int _gridWidth; //Breite eines Kästchens im Grid
      37. private int _gridHeight; //Höheeines Kästchens im Grid
      38. private bool _gridShow; //Sollte logisch sein vom Namen her
      39. private int _gridOffsetX; //verschiebung des Grid in x-achse
      40. private int _gridOffsetY; //verschiebung des Grid in y-achse
      41. #endregion
      42. ///Properties
      43. #region Properties
      44. /// <summary>
      45. /// Grid-Line color
      46. /// </summary>
      47. public Color GridColor {
      48. get => _gridColor;
      49. set {
      50. if (_gridColor != value) {
      51. _gridColor = value;
      52. _gridIntColor = value.ToArgb();
      53. UpdateGridPen();
      54. UpdateGrid();
      55. }
      56. }
      57. }
      58. /// <summary>
      59. /// Grid background color
      60. /// </summary>
      61. public Color GridBackground {
      62. get => _gridBackground;
      63. set {
      64. if (_gridBackground != value) {
      65. _gridBackground = value;
      66. _gridIntBackground = value.ToArgb();
      67. UpdateGrid();
      68. }
      69. }
      70. }
      71. /// <summary>
      72. /// Grid-Line stroke
      73. /// </summary>
      74. public int GridStroke {
      75. get => _gridStroke;
      76. set {
      77. if (_gridStroke != value) {
      78. _gridStroke = value;
      79. UpdateGridPen();
      80. UpdateGrid();
      81. }
      82. }
      83. }
      84. /// <summary>
      85. /// Grid-Rectangle width
      86. /// </summary>
      87. public int GridWidth {
      88. get => _gridWidth;
      89. set {
      90. if (_gridWidth != value) {
      91. _gridWidth = value;
      92. UpdateGrid();
      93. }
      94. }
      95. }
      96. /// <summary>
      97. /// Grid-Rectangle height
      98. /// </summary>
      99. public int GridHeight {
      100. get => _gridHeight;
      101. set {
      102. if (_gridHeight != value) {
      103. _gridHeight = value;
      104. UpdateGrid();
      105. }
      106. }
      107. }
      108. /// <summary>
      109. /// True, the grid-lines are shown. False, the grid-lines aren't shown
      110. /// </summary>
      111. public bool GridShow {
      112. get => _gridShow;
      113. set {
      114. if (_gridShow != value) {
      115. _gridShow = value;
      116. UpdateGrid();
      117. }
      118. }
      119. }
      120. #endregion
      121. ///Methoden
      122. #region Methoden zu den Properties
      123. [MethodImpl(MethodImplOptions.AggressiveInlining)]
      124. private void UpdateGridPen() {
      125. _gridPen = new Pen(_gridColor, _gridStroke);
      126. }
      127. /// <summary>
      128. /// redraw the invisibil Bitmap and send them do show
      129. /// </summary>
      130. private unsafe void UpdateGrid() {
      131. int bufferIndex = (_gridIndex + 1) % 2;
      132. if (_grid[bufferIndex] != null) {
      133. int* vptr = _gridPtrs[bufferIndex];
      134. //Parallel.For für maximale zeichen geschwindigkeit (bei mir waren es ~1.7e6 Pixel/ms und das entsprichte bei mir halbe CPU geschwindigkeit)
      135. //3,4 GHZ
      136. Parallel.For(0, _gridPixelCount, _parallelOptions, pos => {
      137. int x = pos % Width;
      138. int y = pos / Width;
      139. ///n - _gridOffsetn := verschiebung determinieren
      140. *(vptr + pos) = ((x - _gridOffsetX) % _gridWidth == 0 || (y - _gridOffsetY) % _gridHeight == 0)
      141. ? _gridIntColor
      142. : _gridIntBackground;
      143. });
      144. Interlocked.Exchange(ref _gridIndex, (_gridIndex + 1) % 2);
      145. Invalidate();
      146. }
      147. }
      148. #endregion
      149. #endregion
      150. public GridBlock() {
      151. ControlStyles styles = ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint;
      152. SetStyle(styles, true);
      153. _parallelOptions = new ParallelOptions() {
      154. MaxDegreeOfParallelism = Environment.ProcessorCount //zum zeichnen genutze Prozessoren
      155. };
      156. _grid = new Bitmap[2]; //Warum zwei bitmaps? wärend das eine gezeichnet wird, wird das andere dargestellt
      157. _gridPtrs = new int*[2]; //zwei pointer jeweils auf die Bitmaps
      158. _gridBackground = Color.FromArgb(64, 64, 64);
      159. _gridColor = Color.FromArgb(60, 60, 60);
      160. _gridIntBackground = _gridBackground.ToArgb();
      161. _gridIntColor = _gridColor.ToArgb();
      162. _gridHeight = 30;
      163. _gridWidth = 30;
      164. _gridStroke = 1;
      165. _gridShow = true;
      166. _gridPen = new Pen(_gridColor, _gridStroke);
      167. _offsetX = 0; //Bewegung des Grids
      168. _offsetY = 0;
      169. }
      170. #region Events
      171. protected override void OnPaint(PaintEventArgs e) {
      172. base.OnPaint(e);
      173. if (_graphics != e.Graphics) {
      174. _graphics = e.Graphics;
      175. _graphics.CompositingQuality = CompositingQuality.HighQuality;
      176. _graphics.SmoothingMode = SmoothingMode.HighQuality;
      177. _graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
      178. _graphics.InterpolationMode = InterpolationMode.High;
      179. }
      180. if (_grid[_gridIndex] != null) {
      181. _graphics.DrawImage(_grid[_gridIndex], Point.Empty); //Zeichen des BitMaps
      182. }
      183. }
      184. protected override void OnSizeChanged(EventArgs e) {
      185. base.OnSizeChanged(e);
      186. if (Width != 0 && Height != 0) {
      187. //_halfHeight = (int)(Height * 0.5); //old
      188. //_halfWidth = (int)(Width * 0.5); //old
      189. // Erstellet neue Bitmaps mit der Größe dieses Controls
      190. Interlocked.Exchange(ref _grid[0], new Bitmap(Width, Height))?.Dispose();
      191. Interlocked.Exchange(ref _grid[1], new Bitmap(Width, Height))?.Dispose();
      192. //create
      193. //_gridRectangle = new Rectangle(0, 0, Width, Height); //old
      194. Rectangle gridRect = ClientRectangle;
      195. _gridPixelCount = Width * Height;
      196. _gridPtrs[0] = GetBitmapPointer(_grid[0], ref gridRect);
      197. _gridPtrs[1] = GetBitmapPointer(_grid[1], ref gridRect);
      198. UpdateGrid();
      199. } else {
      200. Interlocked.Exchange(ref _grid[0], null)?.Dispose();
      201. Interlocked.Exchange(ref _grid[0], null)?.Dispose();
      202. }
      203. }
      204. [MethodImpl(MethodImplOptions.AggressiveInlining)]
      205. //private unsafe int* GetBitmapPointer(Bitmap bmp) { //old
      206. private unsafe int* GetBitmapPointer(Bitmap bmp, ref Rectangle gridRect) {
      207. BitmapData bitmapData = bmp.LockBits(gridRect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
      208. IntPtr intPtr = bitmapData.Scan0;
      209. bmp.UnlockBits(bitmapData);
      210. return (int*)intPtr.ToPointer();
      211. }
      212. protected override void OnMouseDown(MouseEventArgs e) {
      213. base.OnMouseDown(e);
      214. _mouseDownPosition = e.Location;
      215. if (e.Button == MouseButtons.Left) {
      216. _leftMouseButtonHold = true;
      217. _mouseLeftDownPosition = _mouseDownPosition;
      218. }
      219. }
      220. protected override void OnMouseUp(MouseEventArgs e) {
      221. base.OnMouseUp(e);
      222. _leftMouseButtonHold = !(e.Button == MouseButtons.Left);
      223. }
      224. protected override void OnMouseMove(MouseEventArgs e) {
      225. base.OnMouseMove(e);
      226. if (_leftMouseButtonHold) {
      227. _offsetX = e.X - _mouseLeftDownPosition.X;
      228. _offsetY = e.Y - _mouseLeftDownPosition.Y;
      229. _gridOffsetX = _offsetX % _gridWidth;
      230. _gridOffsetY = _offsetY % _gridHeight;
      231. UpdateGrid();
      232. }
      233. }
      234. #endregion
      235. }

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Facebamm“ ()

      Wäre vielleicht ganz gut, wenn Du den Code noch dokumentieren könntest. Dann kann man das noch besser verstehen, was passiert.

      Grüße
      #define for for(int z=0;z<2;++z)for // Have fun!
      Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

      Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

      Facebamm schrieb:

      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      private unsafe int* GetBitmapPointer(Bitmap bmp) {
      BitmapData bitmapData = bmp.LockBits(_gridRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
      IntPtr intPtr = bitmapData.Scan0;
      bmp.UnlockBits(bitmapData);
      return (int*)intPtr.ToPointer();
      }


      Du darfst eigentlich nur den Pointer benutzen solange die Bits gelocked sind. Du gibt den Bereich aber mit ​UnlockBits direkt wieder Frei und benutzt den Pointer danach weiterhin. Das Funktioniert eine Zeit lang, ist aber nur Glück und kann jederzeit aufhören: Read and write directly to Unlocked Bitmap unmanaged memory (Scan0)
      Ich den Code noch einmal überarbeitet und ein paar kleine dinge verändert (mit //oldgekennzeichnet)
      @Trade, ich hab noch nicht alles auskommentiert und beschrieben, wird aber noch folgen :D


      @Bluespide ich bin mir nicht sicher bei der Aussage, aber daher das ich das Bitmap in der Classspeicher, sollte sich die Speicher-Addr. nicht ändern von dem Byte-Array des Bitmaps im RAM.
      oder seh ich das falsch Oo?
      Schau dir einfach den StackOverflow Artikel an in dem steht ganz klar: ​it is not safe to read/write an unlocked Bitmap Scan0, aber ich will dich jetzt nicht zwingen das umzustellen. Wenn es bei dir bis jetzt immer Funktioniert hat ist es ja auch ok, aber ich will nur sagen: Irgendwann kannst du mal ne Exception bekommen oder das Bild aktualisiert sich nicht mehr und dann kannst du nichts dagegen machen, weil das kein Bug ist, sondern du das Bitmap nicht richtig benutzt hast. Ist halt ein gewisses Risiko.