context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.clearcanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project is free software: you can
  // redistribute it and/or modify it under the terms of the GNU General Public
  // License as published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project is distributed in the hope that it
  // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
  // Public License for more details.
  //
  // You should have received a copy of the GNU General Public License along with
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Drawing.Imaging;
  using ClearCanvas.Common.Utilities;
  using ClearCanvas.ImageViewer.Annotations;
  using ClearCanvas.ImageViewer.Graphics;
  using ClearCanvas.ImageViewer.Mathematics;
  
  namespace ClearCanvas.ImageViewer.Rendering.GDI
  {
  	/// <summary>
  	/// A 2D Renderer that uses GDI+.
  	/// </summary>
  	public class GdiRenderer : RendererBase
      {
          #region GdiObjectFactory Decorators
  
          //For unifying old and new API for DrawAnnotationBox, mostly, but others as well.
  	    private class LegacyGdiObjectFactory : IGdiObjectFactory
  	    {
              private readonly IFontFactory _fontFactory;
  	        private readonly SolidBrush _brush;
  
              public LegacyGdiObjectFactory(IFontFactory fontFactory, SolidBrush brush)
  	        {
  	            _fontFactory = fontFactory;
  	            _brush = brush;
  	        }
  
  	        public Font CreateFont(CreateFontArgs args)
  	        {
  	            return _fontFactory.CreateFont(args);
  	        }
  
  	        public StringFormat CreateStringFormat(CreateStringFormatArgs args)
  	        {
  	            return new StringFormat
  	            {
  	                Trimming = args.Trimming,
  	                Alignment = args.Alignment,
  	                LineAlignment = args.LineAlignment,
  	                FormatFlags = args.Flags
  	            };
  	        }
  
  	        public Brush CreateBrush(CreateBrushArgs args)
  	        {
  	            _brush.Color = args.GetColor();
  	            return _brush;
  	        }
  
  	        public void Dispose()
  	        {
  	        }
  	    }
  
  	    private class UnifiedFactory : IGdiObjectFactory, IFontFactory
  	    {
  	        private GdiObjectFactory _gdiObjectFactory;
  
  	        public UnifiedFactory(GdiObjectFactory gdiObjectFactory)
  	        {
  	            _gdiObjectFactory = gdiObjectFactory;
  	        }
  
  	        public Font GetFont(string fontName, float fontSize, FontStyle fontStyle = FontStyle.Regular, GraphicsUnit graphicsUnit = GraphicsUnit.Point, string defaultFontName = null)
  	        {
  	            var args = new CreateFontArgs(fontName, fontSize, fontStyle, graphicsUnit){DefaultFontName = defaultFontName};
  	            return _gdiObjectFactory.CreateFont(args);
  	        }
  
              public Font CreateFont(CreateFontArgs args)
              {
                  return _gdiObjectFactory.CreateFont(args);
              }
  
  	        public StringFormat CreateStringFormat(CreateStringFormatArgs args)
  	        {
  	            return _gdiObjectFactory.CreateStringFormat(args);
  	        }
  
  	        public Brush CreateBrush(CreateBrushArgs args)
  	        {
                  return _gdiObjectFactory.CreateBrush(args);
  	        }
  
  	        public void Dispose()
  	        {
  	            if (_gdiObjectFactory == null) return;
  	            _gdiObjectFactory.Dispose();
  	            _gdiObjectFactory = null;
  	        }
  	    }
  
          #endregion
  
          /// <summary>
  		/// The minimum font size for rendered text. If the specified font size is less than this value, the text will not be rendered at all.
  		/// </summary>
  		protected static readonly ushort MinimumFontSizeInPixels = 4;
  
  		private const float _nominalScreenDpi = 96;
  
  		private Pen _pen;
          private SolidBrush _brush;
          private UnifiedFactory _gdiObjectFactory;
  
  		/// <summary>
  		/// Default constructor.
  		/// </summary>
  		public GdiRenderer()
  		{
  			_pen = new Pen(Color.White);
              _brush = new SolidBrush(Color.Black);
  
  		    _gdiObjectFactory = new UnifiedFactory(new GdiObjectFactory());
  		}
  
  		private new GdiRenderingSurface Surface
  		{
  			get { return (GdiRenderingSurface) base.Surface; }
  		}
  
  		#region Disposal
  
  		/// <summary>
  		/// Dispose method.  Inheritors should override this method to do any additional cleanup.
  		/// </summary>
  		protected override void Dispose(bool disposing)
  		{
  			base.Dispose(disposing);
  
  			if (disposing)
  			{
  				if (_pen != null)
  				{
  					_pen.Dispose();
  					_pen = null;
  				}
  
                  if (_brush != null)
                  {
                      _brush.Dispose();
                      _brush = null;
                  }
  
  				if (_gdiObjectFactory != null)
  				{
                      _gdiObjectFactory.Dispose();
                      _gdiObjectFactory = null;
  				}
  			}
  		}
  
  		#endregion
  
  		/// <summary>
  		/// Traverses and renders the scene graph.  
  		/// </summary>
  		protected override void Render()
  		{
  #if DEBUG
              var clock = new CodeClock();
  			clock.Start();
  #endif
  			Surface.FinalBuffer.Graphics.Clear(Color.Black);
  			base.Render();
  #if DEBUG
  			clock.Stop();
  			PerformanceReportBroker.PublishReport("GDIRenderer", "Render", clock.Seconds);
  #endif
  		}
  
  		/// <summary>
  		/// Called when <see cref="DrawArgs.DrawMode"/> is equal to <b>DrawMode.Refresh</b>.
  		/// </summary>
  		protected override void Refresh()
  		{
  #if DEBUG
              var clock = new CodeClock();
              clock.Start();
  #endif
  			if (Surface.FinalBuffer != null)
  				Surface.FinalBuffer.RenderToScreen();
  #if DEBUG
  			clock.Stop();
  			PerformanceReportBroker.PublishReport("GDIRenderer", "Refresh", clock.Seconds);
  #endif
          }
  
  		/// <summary>
  		/// Factory method for an <see cref="IRenderingSurface"/>.
  		/// </summary>
  		public override sealed IRenderingSurface GetRenderingSurface(IntPtr windowId, int width, int height)
  		{
  			return new GdiRenderingSurface(windowId, width, height);
  		}
  
  		/// <summary>
  		/// Draws an <see cref="ImageGraphic"/>.
  		/// </summary>
  		protected override void DrawImageGraphic(ImageGraphic imageGraphic)
  		{
  #if DEBUG
              var clock = new CodeClock();
  			clock.Start();
  #endif
  			Surface.ImageBuffer.Graphics.Clear(Color.FromArgb(0x0, 0xFF, 0xFF, 0xFF));
  
  			DrawImageGraphic(Surface.ImageBuffer, imageGraphic);
  
  			Surface.FinalBuffer.RenderImage(Surface.ImageBuffer);
  #if DEBUG
  			clock.Stop();
  			PerformanceReportBroker.PublishReport("GDIRenderer", "DrawImageGraphic", clock.Seconds);
  #endif
  		}
  
  		/// <summary>
  		/// Draws a <see cref="LinePrimitive"/>.
  		/// </summary>
  		protected override void DrawLinePrimitive(LinePrimitive line)
  		{
  			DrawLinePrimitive(Surface.FinalBuffer, _pen, line, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="InvariantLinePrimitive"/>.
  		/// </summary>
  		protected override void DrawInvariantLinePrimitive(InvariantLinePrimitive line)
  		{
  			DrawLinePrimitive(Surface.FinalBuffer, _pen, line, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="SplinePrimitive"/>.
  		/// </summary>
  		protected override void DrawSplinePrimitive(SplinePrimitive spline)
  		{
  			DrawSplinePrimitive(Surface.FinalBuffer, _pen, spline, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="RectanglePrimitive"/>.
  		/// </summary>
  		protected override void DrawRectanglePrimitive(RectanglePrimitive rect)
  		{
  			DrawRectanglePrimitive(Surface.FinalBuffer, _pen, rect, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="InvariantRectanglePrimitive"/>.
  		/// </summary>
  		protected override void DrawInvariantRectanglePrimitive(InvariantRectanglePrimitive rect)
  		{
  			DrawRectanglePrimitive(Surface.FinalBuffer, _pen, rect, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="EllipsePrimitive"/>.
  		/// </summary>
  		protected override void DrawEllipsePrimitive(EllipsePrimitive ellipse)
  		{
  			DrawEllipsePrimitive(Surface.FinalBuffer, _pen, ellipse, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="InvariantEllipsePrimitive"/>.
  		/// </summary>
  		protected override void DrawInvariantEllipsePrimitive(InvariantEllipsePrimitive ellipse)
  		{
  			DrawEllipsePrimitive(Surface.FinalBuffer, _pen, ellipse, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="ArcPrimitive"/>.
  		/// </summary>
  		protected override void DrawArcPrimitive(IArcGraphic arc)
  		{
  			DrawArcPrimitive(Surface.FinalBuffer, _pen, arc, Dpi);
  		}
  
  		/// <summary>
  		/// Draws a <see cref="PointPrimitive"/>.
  		/// </summary>
  		protected override void DrawPointPrimitive(PointPrimitive pointPrimitive)
  		{
  			DrawPointPrimitive(Surface.FinalBuffer, _brush, pointPrimitive, Dpi);
  		}
  
  		/// <summary>
  		/// Draws an <see cref="InvariantTextPrimitive"/>.
  		/// </summary>
  		protected override void DrawTextPrimitive(InvariantTextPrimitive textPrimitive)
  		{
  			DrawTextPrimitive(Surface.FinalBuffer, _brush, _gdiObjectFactory, textPrimitive, Dpi);
  		}
  
  		/// <summary>
  		/// Draws an <see cref="AnnotationBox"/>.
  		/// </summary>
  		protected override void DrawAnnotationBox(string annotationText, AnnotationBox annotationBox)
  		{
  			DrawAnnotationBox(Surface.FinalBuffer, _gdiObjectFactory, annotationText, annotationBox, Dpi);
  		}
  
  		#region Static Draw Helpers
  
  		/// <summary>
  		/// Draws an image graphic to the specified destination buffer.
  		/// </summary>
  		/// <param name="buffer">The destination buffer.</param>
  		/// <param name="imageGraphic">The image graphic to be drawn.</param>
  		public static void DrawImageGraphic(BitmapBuffer buffer, ImageGraphic imageGraphic)
  		{
  			const int bytesPerPixel = 4;
  
  			var bounds = buffer.Bounds;
  			var bitmapData = buffer.Bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
  			try
  			{
  				ImageRenderer.Render(imageGraphic, bitmapData.Scan0, bitmapData.Width, bytesPerPixel, bounds);
  			}
  			finally
  			{
  				buffer.Bitmap.UnlockBits(bitmapData);
  			}
  		}
  
          /// <summary>
          /// Draws an annotation box to the specified destination buffer.
          /// </summary>
          /// <param name="buffer">The destination buffer.</param>
          /// <param name="brush">A GDI brush to use for drawing.</param>
          /// <param name="fontFactory">A GDI font factory to use for drawing.</param>
          /// <param name="annotationText">The annotation text to be drawn.</param>
          /// <param name="annotationBox">The annotation box to be drawn.</param>
          /// <param name="dpi">The intended output DPI.</param>
          [Obsolete("Use the overload that takes an IGdiObjectFactory instead.")]
          public static void DrawAnnotationBox(IGdiBuffer buffer, SolidBrush brush, IFontFactory fontFactory,
  	        string annotationText, AnnotationBox annotationBox, float dpi = _nominalScreenDpi)
          {
              var fakeFactory = new LegacyGdiObjectFactory(fontFactory, brush);
              DrawAnnotationBox(buffer, fakeFactory, annotationText, annotationBox, dpi);
  	    }
  
  	    /// <summary>
  	    /// Draws an annotation box to the specified destination buffer.
  	    /// </summary>
  	    /// <param name="buffer">The destination buffer.</param>
          /// <param name="gdiObjectFactory">A factory for GDI objects.</param>
          /// <param name="annotationText">The annotation text to be drawn.</param>
  	    /// <param name="annotationBox">The annotation box to be drawn.</param>
  	    /// <param name="dpi">The intended output DPI.</param>
  	    public static void DrawAnnotationBox(IGdiBuffer buffer, IGdiObjectFactory gdiObjectFactory, 
              string annotationText, AnnotationBox annotationBox, float dpi = _nominalScreenDpi)
  	    {
  	        // if there's nothing to draw, there's nothing to do. go figure.
  	        if (string.IsNullOrWhiteSpace(annotationText))
  	            return;
  
  	        var clientRectangle = RectangleUtilities.CalculateSubRectangle(buffer.Bounds, annotationBox.NormalizedRectangle);
  
  	        //Deflate the client rectangle by 4 pixels to allow some space 
  	        //between neighbouring rectangles whose borders coincide.
  	        Rectangle.Inflate(clientRectangle, -4, -4);
  
  	        var fontSize = (clientRectangle.Height/annotationBox.NumberOfLines) - 1;
  
  	        //don't draw it if it's too small to read, anyway.
  	        if (fontSize < MinimumFontSizeInPixels)
  	            return;
  
  	        var style = FontStyle.Regular;
  	        if (annotationBox.Bold)
  	            style |= FontStyle.Bold;
  	        if (annotationBox.Italics)
  	            style |= FontStyle.Italic;
  
  	        //don't draw it if it's too small to read, anyway.
  	        if (fontSize < MinimumFontSizeInPixels)
  	            return;
  
  	        var fontArgs = new CreateFontArgs(annotationBox.Font, fontSize, style, GraphicsUnit.Pixel) { DefaultFontName = AnnotationBox.DefaultFont };
              var font = gdiObjectFactory.CreateFont(fontArgs);
              var format = gdiObjectFactory.CreateStringFormat(new CreateStringFormatArgs(annotationBox));
  
  	        var layoutArea = new SizeF(clientRectangle.Width, clientRectangle.Height);
  	        var size = buffer.Graphics.MeasureString(annotationText, font, layoutArea, format);
  	        if (annotationBox.FitWidth && size.Width > clientRectangle.Width)
  	        {
  	            fontSize = (int) (Math.Round(fontSize*clientRectangle.Width/(double) size.Width - 0.5));
  	            //don't draw it if it's too small to read, anyway.
  	            if (fontSize < MinimumFontSizeInPixels)
  	                return;
  
                  font = gdiObjectFactory.CreateFont(fontArgs);
  	        }
  
  	        // Draw drop shadow
  			var brush = gdiObjectFactory.CreateBrush(new CreateBrushArgs(Color.Black));
  	        clientRectangle.Offset(1, 1);
  
  	        buffer.Graphics.DrawString(
  	            annotationText,
  	            font,
  	            brush,
  	            clientRectangle,
  	            format);
  
  	        brush = gdiObjectFactory.CreateBrush(new CreateBrushArgs(annotationBox.Color));
  
  	        clientRectangle.Offset(-1, -1);
  
  	        buffer.Graphics.DrawString(
  	            annotationText,
  	            font,
  	            brush,
  	            clientRectangle,
  	            format);
  	    }
  
          /// <summary>
          /// Draws a text primitive to the specified destination buffer.
          /// </summary>
          /// <param name="buffer">The destination buffer.</param>
          /// <param name="brush">A GDI brush to use for drawing.</param>
          /// <param name="fontFactory">A GDI font factory to use for drawing.</param>
          /// <param name="text">The text primitive to be drawn.</param>
          /// <param name="dpi">The intended output DPI.</param>
          [Obsolete("Use the overload that takes an IGdiObjectFactory instead.")]
          public static void DrawTextPrimitive(IGdiBuffer buffer, SolidBrush brush, IFontFactory fontFactory, InvariantTextPrimitive text, float dpi = _nominalScreenDpi)
  	    {
              var fakeFactory = new LegacyGdiObjectFactory(fontFactory, brush);
  
gt:             DrawTextPrimitive(buffer, fakeFactory, text, dpi);