SvgRectangle.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. using System;
  2. using System.Drawing;
  3. using System.Drawing.Drawing2D;
  4. #pragma warning disable
  5. namespace Svg
  6. {
  7. /// <summary>
  8. /// Represents an SVG rectangle that could also have rounded edges.
  9. /// </summary>
  10. [SvgElement("rect")]
  11. public class SvgRectangle : SvgPathBasedElement
  12. {
  13. private SvgUnit _cornerRadiusX;
  14. private SvgUnit _cornerRadiusY;
  15. private SvgUnit _height;
  16. private GraphicsPath _path;
  17. private SvgUnit _width;
  18. private SvgUnit _x;
  19. private SvgUnit _y;
  20. /// <summary>
  21. /// Initializes a new instance of the <see cref="SvgRectangle"/> class.
  22. /// </summary>
  23. public SvgRectangle()
  24. {
  25. _width = new SvgUnit(0.0f);
  26. _height = new SvgUnit(0.0f);
  27. _cornerRadiusX = new SvgUnit(0.0f);
  28. _cornerRadiusY = new SvgUnit(0.0f);
  29. _x = new SvgUnit(0.0f);
  30. _y = new SvgUnit(0.0f);
  31. }
  32. /// <summary>
  33. /// Gets an <see cref="SvgPoint"/> representing the top left point of the rectangle.
  34. /// </summary>
  35. public SvgPoint Location
  36. {
  37. get { return new SvgPoint(X, Y); }
  38. }
  39. /// <summary>
  40. /// Gets or sets the position where the left point of the rectangle should start.
  41. /// </summary>
  42. [SvgAttribute("x")]
  43. public SvgUnit X
  44. {
  45. get { return _x; }
  46. set
  47. {
  48. if (_x != value)
  49. {
  50. _x = value;
  51. OnAttributeChanged(new AttributeEventArgs { Attribute = "x", Value = value });
  52. IsPathDirty = true;
  53. }
  54. }
  55. }
  56. /// <summary>
  57. /// Gets or sets the position where the top point of the rectangle should start.
  58. /// </summary>
  59. [SvgAttribute("y")]
  60. public SvgUnit Y
  61. {
  62. get { return _y; }
  63. set
  64. {
  65. if (_y != value)
  66. {
  67. _y = value;
  68. OnAttributeChanged(new AttributeEventArgs { Attribute = "y", Value = value });
  69. IsPathDirty = true;
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Gets or sets the width of the rectangle.
  75. /// </summary>
  76. [SvgAttribute("width")]
  77. public SvgUnit Width
  78. {
  79. get { return _width; }
  80. set
  81. {
  82. if (_width != value)
  83. {
  84. _width = value;
  85. OnAttributeChanged(new AttributeEventArgs { Attribute = "width", Value = value });
  86. IsPathDirty = true;
  87. }
  88. }
  89. }
  90. /// <summary>
  91. /// Gets or sets the height of the rectangle.
  92. /// </summary>
  93. [SvgAttribute("height")]
  94. public SvgUnit Height
  95. {
  96. get { return _height; }
  97. set
  98. {
  99. if (_height != value)
  100. {
  101. _height = value;
  102. OnAttributeChanged(new AttributeEventArgs { Attribute = "height", Value = value });
  103. IsPathDirty = true;
  104. }
  105. }
  106. }
  107. /// <summary>
  108. /// Gets or sets the X-radius of the rounded edges of this rectangle.
  109. /// </summary>
  110. [SvgAttribute("rx")]
  111. public SvgUnit CornerRadiusX
  112. {
  113. get
  114. {
  115. // If ry has been set and rx hasn't, use it's value
  116. if (_cornerRadiusX.Value == 0.0f && _cornerRadiusY.Value > 0.0f)
  117. return _cornerRadiusY;
  118. return _cornerRadiusX;
  119. }
  120. set
  121. {
  122. _cornerRadiusX = value;
  123. IsPathDirty = true;
  124. }
  125. }
  126. /// <summary>
  127. /// Gets or sets the Y-radius of the rounded edges of this rectangle.
  128. /// </summary>
  129. [SvgAttribute("ry")]
  130. public SvgUnit CornerRadiusY
  131. {
  132. get
  133. {
  134. // If rx has been set and ry hasn't, use it's value
  135. if (_cornerRadiusY.Value == 0.0f && _cornerRadiusX.Value > 0.0f)
  136. return _cornerRadiusX;
  137. return _cornerRadiusY;
  138. }
  139. set
  140. {
  141. _cornerRadiusY = value;
  142. IsPathDirty = true;
  143. }
  144. }
  145. /// <summary>
  146. /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
  147. /// </summary>
  148. protected override bool RequiresSmoothRendering
  149. {
  150. get
  151. {
  152. if (base.RequiresSmoothRendering)
  153. return (CornerRadiusX.Value > 0 || CornerRadiusY.Value > 0);
  154. else
  155. return false;
  156. }
  157. }
  158. /// <summary>
  159. /// Gets the <see cref="GraphicsPath"/> for this element.
  160. /// </summary>
  161. public override GraphicsPath Path(ISvgRenderer renderer)
  162. {
  163. if (_path == null || IsPathDirty)
  164. {
  165. var halfStrokeWidth = new SvgUnit(base.StrokeWidth / 2);
  166. // If it is to render, don't need to consider stroke
  167. if (renderer != null)
  168. {
  169. halfStrokeWidth = 0;
  170. this.IsPathDirty = false;
  171. }
  172. // If the corners aren't to be rounded just create a rectangle
  173. if (CornerRadiusX.Value == 0.0f && CornerRadiusY.Value == 0.0f)
  174. {
  175. // Starting location which take consideration of stroke width
  176. SvgPoint strokedLocation = new SvgPoint(Location.X - halfStrokeWidth, Location.Y - halfStrokeWidth);
  177. var width = this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) + halfStrokeWidth;
  178. var height = this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) + halfStrokeWidth;
  179. var rectangle = new RectangleF(strokedLocation.ToDeviceValue(renderer, this), new SizeF(width, height));
  180. _path = new GraphicsPath();
  181. _path.StartFigure();
  182. _path.AddRectangle(rectangle);
  183. _path.CloseFigure();
  184. }
  185. else
  186. {
  187. _path = new GraphicsPath();
  188. var arcBounds = new RectangleF();
  189. var lineStart = new PointF();
  190. var lineEnd = new PointF();
  191. var width = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
  192. var height = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
  193. var rx = Math.Min(CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2, width);
  194. var ry = Math.Min(CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2, height);
  195. var location = Location.ToDeviceValue(renderer, this);
  196. // Start
  197. _path.StartFigure();
  198. // Add first arc
  199. arcBounds.Location = location;
  200. arcBounds.Width = rx;
  201. arcBounds.Height = ry;
  202. _path.AddArc(arcBounds, 180, 90);
  203. // Add first line
  204. lineStart.X = Math.Min(location.X + rx, location.X + width * 0.5f);
  205. lineStart.Y = location.Y;
  206. lineEnd.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
  207. lineEnd.Y = lineStart.Y;
  208. _path.AddLine(lineStart, lineEnd);
  209. // Add second arc
  210. arcBounds.Location = new PointF(location.X + width - rx, location.Y);
  211. _path.AddArc(arcBounds, 270, 90);
  212. // Add second line
  213. lineStart.X = location.X + width;
  214. lineStart.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
  215. lineEnd.X = lineStart.X;
  216. lineEnd.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
  217. _path.AddLine(lineStart, lineEnd);
  218. // Add third arc
  219. arcBounds.Location = new PointF(location.X + width - rx, location.Y + height - ry);
  220. _path.AddArc(arcBounds, 0, 90);
  221. // Add third line
  222. lineStart.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
  223. lineStart.Y = location.Y + height;
  224. lineEnd.X = Math.Min(location.X + rx, location.X + width * 0.5f);
  225. lineEnd.Y = lineStart.Y;
  226. _path.AddLine(lineStart, lineEnd);
  227. // Add third arc
  228. arcBounds.Location = new PointF(location.X, location.Y + height - ry);
  229. _path.AddArc(arcBounds, 90, 90);
  230. // Add fourth line
  231. lineStart.X = location.X;
  232. lineStart.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
  233. lineEnd.X = lineStart.X;
  234. lineEnd.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
  235. _path.AddLine(lineStart, lineEnd);
  236. // Close
  237. _path.CloseFigure();
  238. }
  239. }
  240. return _path;
  241. }
  242. /// <summary>
  243. /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
  244. /// </summary>
  245. protected override void Render(ISvgRenderer renderer)
  246. {
  247. if (Width.Value > 0.0f && Height.Value > 0.0f)
  248. {
  249. base.Render(renderer);
  250. }
  251. }
  252. public override SvgElement DeepCopy()
  253. {
  254. return DeepCopy<SvgRectangle>();
  255. }
  256. public override SvgElement DeepCopy<T>()
  257. {
  258. var newObj = base.DeepCopy<T>() as SvgRectangle;
  259. newObj.CornerRadiusX = this.CornerRadiusX;
  260. newObj.CornerRadiusY = this.CornerRadiusY;
  261. newObj.Height = this.Height;
  262. newObj.Width = this.Width;
  263. newObj.X = this.X;
  264. newObj.Y = this.Y;
  265. return newObj;
  266. }
  267. }
  268. }
  269. #pragma warning restore