SvgGradientServer.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using Svg.Transforms;
  6. #pragma warning disable
  7. namespace Svg
  8. {
  9. /// <summary>
  10. /// Provides the base class for all paint servers that wish to render a gradient.
  11. /// </summary>
  12. public abstract class SvgGradientServer : SvgPaintServer, ISvgSupportsCoordinateUnits
  13. {
  14. private SvgCoordinateUnits _gradientUnits;
  15. private SvgGradientSpreadMethod _spreadMethod;
  16. private SvgPaintServer _inheritGradient;
  17. private List<SvgGradientStop> _stops;
  18. /// <summary>
  19. /// Initializes a new instance of the <see cref="SvgGradientServer"/> class.
  20. /// </summary>
  21. internal SvgGradientServer()
  22. {
  23. this.GradientUnits = SvgCoordinateUnits.ObjectBoundingBox;
  24. this.SpreadMethod = SvgGradientSpreadMethod.Pad;
  25. this._stops = new List<SvgGradientStop>();
  26. }
  27. /// <summary>
  28. /// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
  29. /// <see cref="Children"/> collection.
  30. /// </summary>
  31. /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
  32. /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
  33. protected override void AddElement(SvgElement child, int index)
  34. {
  35. if (child is SvgGradientStop)
  36. {
  37. this.Stops.Add((SvgGradientStop)child);
  38. }
  39. base.AddElement(child, index);
  40. }
  41. /// <summary>
  42. /// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
  43. /// <see cref="Children"/> collection.
  44. /// </summary>
  45. /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
  46. protected override void RemoveElement(SvgElement child)
  47. {
  48. if (child is SvgGradientStop)
  49. {
  50. this.Stops.Remove((SvgGradientStop)child);
  51. }
  52. base.RemoveElement(child);
  53. }
  54. /// <summary>
  55. /// Gets the ramp of colors to use on a gradient.
  56. /// </summary>
  57. public List<SvgGradientStop> Stops
  58. {
  59. get { return this._stops; }
  60. }
  61. /// <summary>
  62. /// Specifies what happens if the gradient starts or ends inside the bounds of the target rectangle.
  63. /// </summary>
  64. [SvgAttribute("spreadMethod")]
  65. public SvgGradientSpreadMethod SpreadMethod
  66. {
  67. get { return this._spreadMethod; }
  68. set { this._spreadMethod = value; }
  69. }
  70. /// <summary>
  71. /// Gets or sets the coordinate system of the gradient.
  72. /// </summary>
  73. [SvgAttribute("gradientUnits")]
  74. public SvgCoordinateUnits GradientUnits
  75. {
  76. get { return this._gradientUnits; }
  77. set { this._gradientUnits = value; }
  78. }
  79. /// <summary>
  80. /// Gets or sets another gradient fill from which to inherit the stops from.
  81. /// </summary>
  82. [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
  83. public SvgPaintServer InheritGradient
  84. {
  85. get { return this._inheritGradient; }
  86. set
  87. {
  88. this._inheritGradient = value;
  89. }
  90. }
  91. [SvgAttribute("gradientTransform")]
  92. public SvgTransformCollection GradientTransform
  93. {
  94. get { return (this.Attributes.GetAttribute<SvgTransformCollection>("gradientTransform")); }
  95. set { this.Attributes["gradientTransform"] = value; }
  96. }
  97. protected Matrix EffectiveGradientTransform
  98. {
  99. get
  100. {
  101. var transform = new Matrix();
  102. if (GradientTransform != null)
  103. {
  104. transform.Multiply(GradientTransform.GetMatrix());
  105. }
  106. return transform;
  107. }
  108. }
  109. /// <summary>
  110. /// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
  111. /// </summary>
  112. /// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
  113. /// <param name="opacity">The opacity of the colour blend.</param>
  114. protected ColorBlend GetColorBlend(ISvgRenderer renderer, float opacity, bool radial)
  115. {
  116. int colourBlends = this.Stops.Count;
  117. bool insertStart = false;
  118. bool insertEnd = false;
  119. //gradient.Transform = renderingElement.Transforms.Matrix;
  120. //stops should be processed in reverse order if it's a radial gradient
  121. // May need to increase the number of colour blends because the range *must* be from 0.0 to 1.0.
  122. // E.g. 0.5 - 0.8 isn't valid therefore the rest need to be calculated.
  123. // If the first stop doesn't start at zero
  124. if (this.Stops[0].Offset.Value > 0)
  125. {
  126. colourBlends++;
  127. if (radial)
  128. {
  129. insertEnd = true;
  130. }
  131. else
  132. {
  133. insertStart = true;
  134. }
  135. }
  136. // If the last stop doesn't end at 1 a stop
  137. float lastValue = this.Stops[this.Stops.Count - 1].Offset.Value;
  138. if (lastValue < 100 || lastValue < 1)
  139. {
  140. colourBlends++;
  141. if (radial)
  142. {
  143. insertStart = true;
  144. }
  145. else
  146. {
  147. insertEnd = true;
  148. }
  149. }
  150. ColorBlend blend = new ColorBlend(colourBlends);
  151. // Set positions and colour values
  152. int actualStops = 0;
  153. float mergedOpacity = 0.0f;
  154. float position = 0.0f;
  155. Color colour = System.Drawing.Color.Black;
  156. for (int i = 0; i < colourBlends; i++)
  157. {
  158. var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
  159. var boundWidth = renderer.GetBoundable().Bounds.Width;
  160. mergedOpacity = opacity * currentStop.Opacity;
  161. position =
  162. radial
  163. ? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth)
  164. : (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth);
  165. position = (float)Math.Round(position, 1, MidpointRounding.AwayFromZero);
  166. colour = System.Drawing.Color.FromArgb((int)Math.Round(mergedOpacity * 255), currentStop.GetColor(this));
  167. actualStops++;
  168. // Insert this colour before itself at position 0
  169. if (insertStart && i == 0)
  170. {
  171. blend.Positions[i] = 0.0f;
  172. blend.Colors[i] = colour;
  173. i++;
  174. }
  175. blend.Positions[i] = position;
  176. blend.Colors[i] = colour;
  177. // Insert this colour after itself at position 0
  178. if (insertEnd && i == colourBlends - 2)
  179. {
  180. i++;
  181. blend.Positions[i] = 1.0f;
  182. blend.Colors[i] = colour;
  183. }
  184. }
  185. return blend;
  186. }
  187. protected void LoadStops(SvgVisualElement parent)
  188. {
  189. var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient, parent);
  190. if (this.Stops.Count == 0 && core != null)
  191. {
  192. _stops.AddRange(core.Stops);
  193. }
  194. }
  195. protected static double CalculateDistance(PointF first, PointF second)
  196. {
  197. return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
  198. }
  199. protected static float CalculateLength(PointF vector)
  200. {
  201. return (float)Math.Sqrt(Math.Pow(vector.X, 2) + Math.Pow(vector.Y, 2));
  202. }
  203. public override SvgElement DeepCopy<T>()
  204. {
  205. var newObj = base.DeepCopy<T>() as SvgGradientServer;
  206. newObj.SpreadMethod = this.SpreadMethod;
  207. newObj.GradientUnits = this.GradientUnits;
  208. newObj.InheritGradient = this.InheritGradient;
  209. newObj.GradientTransform = this.GradientTransform;
  210. return newObj;
  211. }
  212. public SvgCoordinateUnits GetUnits()
  213. {
  214. return _gradientUnits;
  215. }
  216. }
  217. }
  218. #pragma warning restore