PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/GammaJul.LgLcd/LcdGdiPage.cs

#
C# | 175 lines | 110 code | 17 blank | 48 comment | 25 complexity | a4471c2b21ccc0032c4b43f1d8db7bda MD5 | raw file
Possible License(s): LGPL-2.1
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. using System.Runtime.InteropServices;
  7. namespace GammaJul.LgLcd {
  8. /// <summary>
  9. /// Represents a <see cref="LcdPage"/> that use GDI+ to do its drawing.
  10. /// </summary>
  11. public class LcdGdiPage : LcdPage {
  12. private readonly List<LcdGdiObject> _children = new List<LcdGdiObject>();
  13. private readonly Bitmap _bitmap;
  14. private readonly Rectangle _rectangle;
  15. private readonly byte[] _32BppPixels;
  16. private readonly byte[] _8BppPixels;
  17. private Graphics _graphics;
  18. /// <summary>
  19. /// Gets the <see cref="Bitmap"/> used to draw this page.
  20. /// </summary>
  21. public Bitmap Bitmap {
  22. get { return _bitmap; }
  23. }
  24. /// <summary>
  25. /// Gets a list of <see cref="LcdGdiObject"/>s that are the children of this page.
  26. /// </summary>
  27. public List<LcdGdiObject> Children {
  28. get { return _children; }
  29. }
  30. /// <summary>
  31. /// Occurs just after the page have been cleared of all contents, but before the children are drawn.
  32. /// This event provides a <see cref="Graphics"/> object to use for custom drawing.
  33. /// </summary>
  34. public event EventHandler<GdiDrawingEventArgs> GdiDrawing;
  35. /// <summary>
  36. /// Raises the <see cref="GdiDrawing"/> event.
  37. /// </summary>
  38. /// <param name="graphics"><see cref="Graphics"/> to use for custom drawing.</param>
  39. protected void OnGdiDrawing(Graphics graphics) {
  40. EventHandler<GdiDrawingEventArgs> handler = GdiDrawing;
  41. if (handler != null)
  42. handler(this, new GdiDrawingEventArgs(graphics));
  43. }
  44. /// <summary>
  45. /// Prepares a <see cref="Graphics"/> object for drawing.
  46. /// </summary>
  47. /// <param name="graphics"><see cref="Graphics"/> to prepare for drawing.</param>
  48. protected static void PrepareGraphics(Graphics graphics) {
  49. if (graphics == null)
  50. throw new ArgumentNullException("graphics");
  51. graphics.PageUnit = GraphicsUnit.Pixel;
  52. graphics.PageScale = 1.0f;
  53. }
  54. /// <summary>
  55. /// Prepares a <see cref="Graphics"/> object for drawing a specified <see cref="LcdGdiObject"/>,
  56. /// by setting the appropriate properties (clip, interpolation, etc) on the <see cref="Graphics"/>.
  57. /// </summary>
  58. /// <param name="graphics"><see cref="Graphics"/> to prepare for drawing <paramref name="child"/>.</param>
  59. /// <param name="child"><see cref="LcdGdiObject"/> that will be drawn on <paramref name="graphics"/>.</param>
  60. internal protected static void PrepareGraphicsForChild(Graphics graphics, LcdGdiObject child) {
  61. if (graphics == null)
  62. throw new ArgumentNullException("graphics");
  63. if (child == null)
  64. throw new ArgumentNullException("child");
  65. graphics.Transform = child.Transform;
  66. if (child.Clip != null)
  67. graphics.Clip = child.Clip;
  68. graphics.InterpolationMode = child.InterpolationMode;
  69. graphics.PixelOffsetMode = child.PixelOffsetMode;
  70. graphics.RenderingOrigin = child.RenderingOrigin;
  71. graphics.SmoothingMode = child.SmoothingMode;
  72. }
  73. /// <summary>
  74. /// Derived classes override this method in order to update the page content.
  75. /// </summary>
  76. /// <param name="elapsedTotalTime">Time elapsed since the device creation.</param>
  77. /// <param name="elapsedTimeSinceLastFrame">Time elapsed since last frame update.</param>
  78. /// <returns><c>true</c> if the update has done something and a redraw is required.</returns>
  79. protected override bool UpdateCore(TimeSpan elapsedTotalTime, TimeSpan elapsedTimeSinceLastFrame) {
  80. Graphics graphics = Graphics.FromImage(_bitmap);
  81. PrepareGraphics(graphics);
  82. bool hasChanged = false;
  83. foreach (LcdGdiObject child in _children) {
  84. if (child.HasChanged) {
  85. hasChanged = true;
  86. PrepareGraphicsForChild(graphics, child);
  87. child.Update(elapsedTotalTime, elapsedTimeSinceLastFrame, this, graphics);
  88. child.HasChanged = false;
  89. graphics.ResetClip();
  90. }
  91. }
  92. if (hasChanged)
  93. _graphics = graphics;
  94. else {
  95. graphics.Dispose();
  96. _graphics = null;
  97. }
  98. return hasChanged;
  99. }
  100. /// <summary>
  101. /// Derived classes override this method in order to draw the page content visually.
  102. /// </summary>
  103. /// <returns>Implementors must return a pixel array conforming to
  104. /// <see cref="LcdPage.Device"/>'s <see cref="LcdDevice.DeviceType"/>.</returns>
  105. protected override byte[] DrawCore() {
  106. if (_graphics == null)
  107. _graphics = Graphics.FromImage(_bitmap);
  108. using (Graphics graphics = _graphics) {
  109. PrepareGraphics(graphics);
  110. graphics.FillRectangle(Brushes.White, _rectangle);
  111. foreach (LcdGdiObject child in _children) {
  112. if (child.IsVisible) {
  113. PrepareGraphicsForChild(graphics, child);
  114. child.Draw(this, graphics);
  115. graphics.ResetClip();
  116. }
  117. }
  118. OnGdiDrawing(graphics);
  119. }
  120. _graphics = null;
  121. return PixelsFromBitmap();
  122. }
  123. /// <summary>
  124. /// Copies the pixels from <see cref="Bitmap"/> and returns them.
  125. /// </summary>
  126. /// <returns>A copy of the pixels from <see cref="Bitmap"/>.</returns>
  127. protected byte[] PixelsFromBitmap() {
  128. BitmapData bitmapData = _bitmap.LockBits(_rectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  129. try {
  130. Marshal.Copy(bitmapData.Scan0, _32BppPixels, 0, _32BppPixels.Length);
  131. // 32bpp, simply returns
  132. if (Device.BitsPerPixel == 32)
  133. return _32BppPixels;
  134. // 8bpp, take the mean of each of the 4 8bit color components
  135. for (int i = 0; i < _8BppPixels.Length; ++i)
  136. _8BppPixels[i] = (byte) (255 - (_32BppPixels[i * 4] + _32BppPixels[i * 4 + 1] + _32BppPixels[i * 4 + 2] + _32BppPixels[i * 4 + 3]) / 4);
  137. return _8BppPixels;
  138. }
  139. finally {
  140. _bitmap.UnlockBits(bitmapData);
  141. }
  142. }
  143. /// <summary>
  144. /// Creates a new <see cref="LcdGdiPage"/> on the given device.
  145. /// </summary>
  146. /// <param name="device">Device where this page will be shown.</param>
  147. public LcdGdiPage(LcdDevice device)
  148. : base(device) {
  149. if (device.BitsPerPixel != 8 && device.BitsPerPixel != 32)
  150. throw new NotSupportedException("Only 8bpp and 32bpp devices are supported.");
  151. _bitmap = new Bitmap(device.PixelWidth, device.PixelHeight, PixelFormat.Format32bppArgb);
  152. _rectangle = new Rectangle(0, 0, device.PixelWidth, device.PixelHeight);
  153. _32BppPixels = new byte[device.PixelWidth * device.PixelHeight * 4];
  154. if (device.BitsPerPixel == 8)
  155. _8BppPixels = new byte[device.PixelWidth * device.PixelHeight];
  156. }
  157. }
  158. }