PageRenderTime 15ms CodeModel.GetById 7ms app.highlight 5ms RepoModel.GetById 1ms 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
  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}