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