VectSharp  2.2.1
A light library for C# vector graphics
Graphics.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using System;
19 using System.Collections.Generic;
20 using VectSharp.Filters;
21 
22 namespace VectSharp
23 {
24  internal static class Utils
25  {
26  public static Point RotatePoint(Point point, double angle)
27  {
28  return new Point(point.X * Math.Cos(angle) - point.Y * Math.Sin(angle), point.X * Math.Sin(angle) + point.Y * Math.Cos(angle));
29  }
30  }
31 
32  /// <summary>
33  /// This interface should be implemented by classes intended to provide graphics output capability to a <see cref="Graphics"/> object.
34  /// </summary>
35  public interface IGraphicsContext
36  {
37  /// <summary>
38  /// Width of the graphic surface.
39  /// </summary>
40  double Width { get; }
41 
42  /// <summary>
43  /// Height of the graphic surface.
44  /// </summary>
45  double Height { get; }
46 
47  /// <summary>
48  /// Save the current transform state (rotation, translation, scale). This should be implemented as a LIFO stack.
49  /// </summary>
50  void Save();
51 
52  /// <summary>
53  /// Restore the previous transform state (rotation, translation, scale). This should be implemented as a LIFO stack.
54  /// </summary>
55  void Restore();
56 
57  /// <summary>
58  /// Translate the coordinate system origin.
59  /// </summary>
60  /// <param name="x">The horizontal translation.</param>
61  /// <param name="y">The vertical translation.</param>
62  void Translate(double x, double y);
63 
64  /// <summary>
65  /// Rotate the coordinate system around the origin.
66  /// </summary>
67  /// <param name="angle">The angle (in radians) by which to rotate the coordinate system.</param>
68  void Rotate(double angle);
69 
70  /// <summary>
71  /// Scale the coordinate system with respect to the origin.
72  /// </summary>
73  /// <param name="scaleX">The horizontal scale.</param>
74  /// <param name="scaleY">The vertical scale.</param>
75  void Scale(double scaleX, double scaleY);
76 
77  /// <summary>
78  /// Transform the coordinate system with the specified transformation matrix [ [a, c, e], [b, d, f], [0, 0, 1] ].
79  /// </summary>
80  /// <param name="a">The first element of the first column.</param>
81  /// <param name="b">The second element of the first column.</param>
82  /// <param name="c">The first element of the second column.</param>
83  /// <param name="d">The second element of the second column.</param>
84  /// <param name="e">The first element of the third column.</param>
85  /// <param name="f">The second element of the third column.</param>
86  void Transform(double a, double b, double c, double d, double e, double f);
87 
88  /// <summary>
89  /// The current font.
90  /// </summary>
91  Font Font { get; set; }
92 
93  /// <summary>
94  /// The current text baseline.
95  /// </summary>
97 
98  /// <summary>
99  /// Fill a text string using the current <see cref="Font"/> and <see cref="TextBaseline"/>.
100  /// </summary>
101  /// <param name="text">The string to draw.</param>
102  /// <param name="x">The horizontal coordinate of the text origin.</param>
103  /// <param name="y">The vertical coordinate of the text origin.</param>
104  void FillText(string text, double x, double y);
105 
106  /// <summary>
107  /// Stroke the outline of a text string using the current <see cref="Font"/> and <see cref="TextBaseline"/>.
108  /// </summary>
109  /// <param name="text">The string to draw.</param>
110  /// <param name="x">The horizontal coordinate of the text origin.</param>
111  /// <param name="y">The vertical coordinate of the text origin.</param>
112  void StrokeText(string text, double x, double y);
113 
114  /// <summary>
115  /// Change the current point without drawing a line from the previous point. If necessary, start a new figure.
116  /// </summary>
117  /// <param name="x">The horizontal coordinate of the point.</param>
118  /// <param name="y">The vertical coordinate of the point.</param>
119  void MoveTo(double x, double y);
120 
121  /// <summary>
122  /// Draw a line from the previous point to the specified point.
123  /// </summary>
124  /// <param name="x">The horizontal coordinate of the point.</param>
125  /// <param name="y">The vertical coordinate of the point.</param>
126  void LineTo(double x, double y);
127 
128  /// <summary>
129  /// Close the current figure.
130  /// </summary>
131  void Close();
132 
133  /// <summary>
134  /// Stroke the current path using the current <see cref="StrokeStyle"/>, <see cref="LineWidth"/>, <see cref="LineCap"/>, <see cref="LineJoin"/> and <see cref="LineDash"/>.
135  /// </summary>
136  void Stroke();
137 
138  /// <summary>
139  /// Set the current clipping path as the intersection of the previous clipping path and the current path.
140  /// </summary>
142 
143  /// <summary>
144  /// Current brush used to fill paths.
145  /// </summary>
146  Brush FillStyle { get; }
147 
148  /// <summary>
149  /// Set the current <see cref="FillStyle"/>.
150  /// </summary>
151  /// <param name="style">A <see cref="ValueTuple{Int32, Int32, Int32, Double}"/> containing component information for the colour. For r, g, and b, range: [0, 255]; for a, range: [0, 1].</param>
152  void SetFillStyle((int r, int g, int b, double a) style);
153 
154  /// <summary>
155  /// Set the current <see cref="FillStyle"/>.
156  /// </summary>
157  /// <param name="style">The new fill style.</param>
158  void SetFillStyle(Brush style);
159 
160  /// <summary>
161  /// Current brush used to stroke paths.
162  /// </summary>
164 
165  /// <summary>
166  /// Set the current <see cref="StrokeStyle"/>.
167  /// </summary>
168  /// <param name="style">A <see cref="ValueTuple{Int32, Int32, Int32, Double}"/> containing component information for the colour. For r, g, and b, range: [0, 255]; for a, range: [0, 1].</param>
169  void SetStrokeStyle((int r, int g, int b, double a) style);
170 
171  /// <summary>
172  /// Set the current <see cref="StrokeStyle"/>.
173  /// </summary>
174  /// <param name="style">The new stroke style.</param>
175  void SetStrokeStyle(Brush style);
176 
177  /// <summary>
178  /// Add to the current figure a cubic Bezier from the current point to a destination point, with two control points.
179  /// </summary>
180  /// <param name="p1X">The horizontal coordinate of the first control point.</param>
181  /// <param name="p1Y">The vertical coordinate of the first control point.</param>
182  /// <param name="p2X">The horizontal coordinate of the second control point.</param>
183  /// <param name="p2Y">The vertical coordinate of the second control point.</param>
184  /// <param name="p3X">The horizontal coordinate of the destination point.</param>
185  /// <param name="p3Y">The vertical coordinate of the destination point.</param>
186  void CubicBezierTo(double p1X, double p1Y, double p2X, double p2Y, double p3X, double p3Y);
187 
188  /// <summary>
189  /// Add a rectangle figure to the current path.
190  /// </summary>
191  /// <param name="x0">The horizontal coordinate of the top-left corner of the rectangle.</param>
192  /// <param name="y0">The vertical coordinate of the top-left corner of the rectangle.</param>
193  /// <param name="width">The width of corner of the rectangle.</param>
194  /// <param name="height">The height of corner of the rectangle.</param>
195  void Rectangle(double x0, double y0, double width, double height);
196 
197  /// <summary>
198  /// Fill the current path using the current <see cref="FillStyle"/>.
199  /// </summary>
200  void Fill();
201 
202  /// <summary>
203  /// Current line width used to stroke paths.
204  /// </summary>
205  double LineWidth { get; set; }
206 
207  /// <summary>
208  /// Current line cap used to stroke paths.
209  /// </summary>
210  LineCaps LineCap { set; }
211 
212  /// <summary>
213  /// Current line join used to stroke paths.
214  /// </summary>
216 
217  /// <summary>
218  /// Set the current line dash pattern.
219  /// </summary>
220  /// <param name="dash">The line dash pattern.</param>
221  void SetLineDash(LineDash dash);
222 
223  /// <summary>
224  /// The current tag. How this can be used depends on each implementation.
225  /// </summary>
226  string Tag { get; set; }
227 
228  /// <summary>
229  /// Draw a raster image.
230  /// </summary>
231  /// <param name="sourceX">The horizontal coordinate of the top-left corner of the rectangle delimiting the source area of the image.</param>
232  /// <param name="sourceY">The vertical coordinate of the top-left corner of the rectangle delimiting the source area of the image.</param>
233  /// <param name="sourceWidth">The width of the rectangle delimiting the source area of the image.</param>
234  /// <param name="sourceHeight">The height of the rectangle delimiting the source area of the image.</param>
235  /// <param name="destinationX">The horizontal coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
236  /// <param name="destinationY">The vertical coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
237  /// <param name="destinationWidth">The width of the rectangle delimiting the destination area of the image.</param>
238  /// <param name="destinationHeight">The height of the rectangle delimiting the destination area of the image.</param>
239  /// <param name="image">The image to draw.</param>
240  void DrawRasterImage(int sourceX, int sourceY, int sourceWidth, int sourceHeight, double destinationX, double destinationY, double destinationWidth, double destinationHeight, RasterImage image);
241 
242  /// <summary>
243  /// Draws a <see cref="Graphics"/> object, applying the specified <paramref name="filter"/>.
244  /// </summary>
245  /// <param name="graphics">The <see cref="Graphics"/> object to draw on the current <see cref="Graphics"/> object.</param>
246  /// <param name="filter">An <see cref="IFilter"/> object, representing the filter to apply to the <paramref name="graphics"/> object.</param>
247  void DrawFilteredGraphics(Graphics graphics, IFilter filter);
248  }
249 
250  /// <summary>
251  /// The exception that is thrown when an unbalanced graphics state stack occurs.
252  /// </summary>
253  public class UnbalancedStackException : Exception
254  {
255  internal UnbalancedStackException(int excessSave, int excessRestore) : base("The graphics state stack is unbalanced!\nThere are " + excessSave + " calls to Graphics.Save() and " + excessRestore + " calls to Graphics.Restore() in excess!") { }
256  }
257 
258  /// <summary>
259  /// Represents an abstract drawing surface.
260  /// </summary>
261  public partial class Graphics
262  {
263  /// <summary>
264  /// Determines how an unbalanced graphics state stack (which occurs if the number of calls to <see cref="Save"/> and <see cref="Restore"/> is not equal) will be treated. The default is <see cref="UnbalancedStackActions.Throw"/>.
265  /// </summary>
267 
268  internal List<IGraphicsAction> Actions = new List<IGraphicsAction>();
269 
270  /// <summary>
271  /// Fill a <see cref="GraphicsPath"/>.
272  /// </summary>
273  /// <param name="path">The <see cref="GraphicsPath"/> to fill.</param>
274  /// <param name="fillColour">The <see cref="Brush"/> with which to fill the <see cref="GraphicsPath"/>.</param>
275  /// <param name="tag">A tag to identify the filled path.</param>
276  public void FillPath(GraphicsPath path, Brush fillColour, string tag = null)
277  {
278  Actions.Add(new PathAction(path, fillColour, null, 0, LineCaps.Butt, LineJoins.Miter, LineDash.SolidLine, tag, false));
279  }
280 
281 
282  /// <summary>
283  /// Stroke a <see cref="GraphicsPath"/>.
284  /// </summary>
285  /// <param name="path">The <see cref="GraphicsPath"/> to stroke.</param>
286  /// <param name="strokeColour">The <see cref="Brush"/> with which to stroke the <see cref="GraphicsPath"/>.</param>
287  /// <param name="lineWidth">The width of the line with which the path is stroked.</param>
288  /// <param name="lineCap">The line cap to use to stroke the path.</param>
289  /// <param name="lineJoin">The line join to use to stroke the path.</param>
290  /// <param name="lineDash">The line dash to use to stroke the path.</param>
291  /// <param name="tag">A tag to identify the stroked path.</param>
292  public void StrokePath(GraphicsPath path, Brush strokeColour, double lineWidth = 1, LineCaps lineCap = LineCaps.Butt, LineJoins lineJoin = LineJoins.Miter, LineDash? lineDash = null, string tag = null)
293  {
294  Actions.Add(new PathAction(path, null, strokeColour, lineWidth, lineCap, lineJoin, lineDash ?? LineDash.SolidLine, tag, false));
295  }
296 
297  /// <summary>
298  /// Intersect the current clipping path with the specified <see cref="GraphicsPath"/>.
299  /// </summary>
300  /// <param name="path">The <see cref="GraphicsPath"/> to intersect with the current clipping path.</param>
301  public void SetClippingPath(GraphicsPath path)
302  {
303  Actions.Add(new PathAction(path, null, null, 0, LineCaps.Butt, LineJoins.Miter, LineDash.SolidLine, null, true));
304  }
305 
306  /// <summary>
307  /// Intersect the current clipping path with the specified rectangle.
308  /// </summary>
309  /// <param name="leftX">The horizontal coordinate of the top-left corner of the rectangle.</param>
310  /// <param name="topY">The vertical coordinate of the top-left corner of the rectangle.</param>
311  /// <param name="width">The width of the rectangle.</param>
312  /// <param name="height">The height of the rectangle.</param>
313  public void SetClippingPath(double leftX, double topY, double width, double height)
314  {
315  SetClippingPath(new Point(leftX, topY), new Size(width, height));
316  }
317 
318  /// <summary>
319  /// Intersect the current clipping path with the specified rectangle.
320  /// </summary>
321  /// <param name="topLeft">The top-left corner of the rectangle.</param>
322  /// <param name="size">The size of the rectangle.</param>
323  public void SetClippingPath(Point topLeft, Size size)
324  {
325  Actions.Add(new PathAction(new GraphicsPath().MoveTo(topLeft).LineTo(topLeft.X + size.Width, topLeft.Y).LineTo(topLeft.X + size.Width, topLeft.Y + size.Height).LineTo(topLeft.X, topLeft.Y + size.Height).Close(), null, null, 0, LineCaps.Butt, LineJoins.Miter, LineDash.SolidLine, null, true));
326  }
327 
328  /// <summary>
329  /// Rotate the coordinate system around the origin.
330  /// </summary>
331  /// <param name="angle">The angle (in radians) by which to rotate the coordinate system.</param>
332  public void Rotate(double angle)
333  {
334  Actions.Add(new TransformAction(angle));
335  }
336 
337  /// <summary>
338  /// Rotate the coordinate system around a pivot point.
339  /// </summary>
340  /// <param name="angle">The angle (in radians) by which to rotate the coordinate system.</param>
341  /// <param name="pivot">The pivot around which the coordinate system is to be rotated.</param>
342  public void RotateAt(double angle, Point pivot)
343  {
344  Actions.Add(new TransformAction(pivot));
345  Actions.Add(new TransformAction(angle));
346  Actions.Add(new TransformAction(new Point(-pivot.X, -pivot.Y)));
347  }
348 
349 
350  /// <summary>
351  /// Transform the coordinate system with the specified transformation matrix [ [a, c, e], [b, d, f], [0, 0, 1] ].
352  /// </summary>
353  /// <param name="a">The first element of the first column.</param>
354  /// <param name="b">The second element of the first column.</param>
355  /// <param name="c">The first element of the second column.</param>
356  /// <param name="d">The second element of the second column.</param>
357  /// <param name="e">The first element of the third column.</param>
358  /// <param name="f">The second element of the third column.</param>
359  public void Transform(double a, double b, double c, double d, double e, double f)
360  {
361  double[,] matrix = new double[,] { { a, c, e }, { b, d, f }, { 0, 0, 1 } };
362  Actions.Add(new TransformAction(matrix));
363  }
364 
365  /// <summary>
366  /// Translate the coordinate system origin.
367  /// </summary>
368  /// <param name="x">The horizontal translation.</param>
369  /// <param name="y">The vertical translation.</param>
370  public void Translate(double x, double y)
371  {
372  Actions.Add(new TransformAction(new Point(x, y)));
373  }
374 
375  /// <summary>
376  /// Translate the coordinate system origin.
377  /// </summary>
378  /// <param name="delta">The new origin point.</param>
379  public void Translate(Point delta)
380  {
381  Actions.Add(new TransformAction(delta));
382  }
383 
384  /// <summary>
385  /// Scale the coordinate system with respect to the origin.
386  /// </summary>
387  /// <param name="scaleX">The horizontal scale.</param>
388  /// <param name="scaleY">The vertical scale.</param>
389  public void Scale(double scaleX, double scaleY)
390  {
391  Actions.Add(new TransformAction(new Size(scaleX, scaleY)));
392  }
393 
394  /// <summary>
395  /// Fill a rectangle.
396  /// </summary>
397  /// <param name="topLeft">The top-left corner of the rectangle.</param>
398  /// <param name="size">The size of the rectangle.</param>
399  /// <param name="fillColour">The colour with which to fill the rectangle.</param>
400  /// <param name="tag">A tag to identify the filled rectangle.</param>
401  public void FillRectangle(Point topLeft, Size size, Brush fillColour, string tag = null)
402  {
403  Actions.Add(new RectangleAction(topLeft, size, fillColour, null, 0, LineCaps.Butt, LineJoins.Miter, LineDash.SolidLine, tag));
404  }
405 
406  /// <summary>
407  /// Fill a rectangle.
408  /// </summary>
409  /// <param name="leftX">The horizontal coordinate of the top-left corner of the rectangle.</param>
410  /// <param name="topY">The vertical coordinate of the top-left corner of the rectangle.</param>
411  /// <param name="width">The width of the rectangle.</param>
412  /// <param name="height">The height of the rectangle.</param>
413  /// <param name="fillColour">The colour with which to fill the rectangle.</param>
414  /// <param name="tag">A tag to identify the filled rectangle.</param>
415  public void FillRectangle(double leftX, double topY, double width, double height, Brush fillColour, string tag = null)
416  {
417  Actions.Add(new RectangleAction(new Point(leftX, topY), new Size(width, height), fillColour, null, 0, LineCaps.Butt, LineJoins.Miter, LineDash.SolidLine, tag));
418  }
419 
420  /// <summary>
421  /// Stroke a rectangle.
422  /// </summary>
423  /// <param name="topLeft">The top-left corner of the rectangle.</param>
424  /// <param name="size">The size of the rectangle.</param>
425  /// <param name="strokeColour">The colour with which to stroke the rectangle.</param>
426  /// <param name="lineWidth">The width of the line with which the rectangle is stroked.</param>
427  /// <param name="lineCap">The line cap to use to stroke the rectangle.</param>
428  /// <param name="lineJoin">The line join to use to stroke the rectangle.</param>
429  /// <param name="lineDash">The line dash to use to stroke the rectangle.</param>
430  /// <param name="tag">A tag to identify the filled rectangle.</param>
431  public void StrokeRectangle(Point topLeft, Size size, Brush strokeColour, double lineWidth = 1, LineCaps lineCap = LineCaps.Butt, LineJoins lineJoin = LineJoins.Miter, LineDash? lineDash = null, string tag = null)
432  {
433  Actions.Add(new RectangleAction(topLeft, size, null, strokeColour, lineWidth, lineCap, lineJoin, lineDash ?? LineDash.SolidLine, tag));
434  }
435 
436  /// <summary>
437  /// Stroke a rectangle.
438  /// </summary>
439  /// <param name="leftX">The horizontal coordinate of the top-left corner of the rectangle.</param>
440  /// <param name="topY">The vertical coordinate of the top-left corner of the rectangle.</param>
441  /// <param name="width">The width of the rectangle.</param>
442  /// <param name="height">The height of the rectangle.</param>
443  /// <param name="strokeColour">The colour with which to stroke the rectangle.</param>
444  /// <param name="lineWidth">The width of the line with which the rectangle is stroked.</param>
445  /// <param name="lineCap">The line cap to use to stroke the rectangle.</param>
446  /// <param name="lineJoin">The line join to use to stroke the rectangle.</param>
447  /// <param name="lineDash">The line dash to use to stroke the rectangle.</param>
448  /// <param name="tag">A tag to identify the filled rectangle.</param>
449  public void StrokeRectangle(double leftX, double topY, double width, double height, Brush strokeColour, double lineWidth = 1, LineCaps lineCap = LineCaps.Butt, LineJoins lineJoin = LineJoins.Miter, LineDash? lineDash = null, string tag = null)
450  {
451  Actions.Add(new RectangleAction(new Point(leftX, topY), new Size(width, height), null, strokeColour, lineWidth, lineCap, lineJoin, lineDash ?? LineDash.SolidLine, tag));
452  }
453 
454  /// <summary>
455  /// Draw a raster image.
456  /// </summary>
457  /// <param name="sourceX">The horizontal coordinate of the top-left corner of the rectangle delimiting the source area of the image.</param>
458  /// <param name="sourceY">The vertical coordinate of the top-left corner of the rectangle delimiting the source area of the image.</param>
459  /// <param name="sourceWidth">The width of the rectangle delimiting the source area of the image.</param>
460  /// <param name="sourceHeight">The height of the rectangle delimiting the source area of the image.</param>
461  /// <param name="destinationX">The horizontal coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
462  /// <param name="destinationY">The vertical coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
463  /// <param name="destinationWidth">The width of the rectangle delimiting the destination area of the image.</param>
464  /// <param name="destinationHeight">The height of the rectangle delimiting the destination area of the image.</param>
465  /// <param name="image">The image to draw.</param>
466  /// <param name="tag">A tag to identify the drawn image.</param>
467  public void DrawRasterImage(int sourceX, int sourceY, int sourceWidth, int sourceHeight, double destinationX, double destinationY, double destinationWidth, double destinationHeight, RasterImage image, string tag = null)
468  {
469  Actions.Add(new RasterImageAction(sourceX, sourceY, sourceWidth, sourceHeight, destinationX, destinationY, destinationWidth, destinationHeight, image, tag));
470  }
471 
472  /// <summary>
473  /// Draw a raster image.
474  /// </summary>
475  /// <param name="x">The horizontal coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
476  /// <param name="y">The vertical coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
477  /// <param name="image">The image to draw.</param>
478  /// <param name="tag">A tag to identify the drawn image.</param>
479  public void DrawRasterImage(double x, double y, RasterImage image, string tag = null)
480  {
481  DrawRasterImage(0, 0, image.Width, image.Height, x, y, image.Width, image.Height, image, tag);
482  }
483 
484  /// <summary>
485  /// Draw a raster image.
486  /// </summary>
487  /// <param name="position">The the top-left corner of the rectangle delimiting the destination area of the image.</param>
488  /// <param name="image">The image to draw.</param>
489  /// <param name="tag">A tag to identify the drawn image.</param>
490  public void DrawRasterImage(Point position, RasterImage image, string tag = null)
491  {
492  DrawRasterImage(0, 0, image.Width, image.Height, position.X, position.Y, image.Width, image.Height, image, tag);
493  }
494 
495  /// <summary>
496  /// Draw a raster image.
497  /// </summary>
498  /// <param name="x">The horizontal coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
499  /// <param name="y">The vertical coordinate of the top-left corner of the rectangle delimiting the destination area of the image.</param>
500  /// <param name="width">The width of the rectangle delimiting the destination area of the image.</param>
501  /// <param name="height">The height of the rectangle delimiting the destination area of the image.</param>
502  /// <param name="image">The image to draw.</param>
503  /// <param name="tag">A tag to identify the drawn image.</param>
504  public void DrawRasterImage(double x, double y, double width, double height, RasterImage image, string tag = null)
505  {
506  DrawRasterImage(0, 0, image.Width, image.Height, x, y, width, height, image, tag);
507  }
508 
509  /// <summary>
510  /// Draw a raster image.
511  /// </summary>
512  /// <param name="position">The the top-left corner of the rectangle delimiting the destination area of the image.</param>
513  /// <param name="size">The size of the rectangle delimiting the destination area of the image.</param>
514  /// <param name="image">The image to draw.</param>
515  /// <param name="tag">A tag to identify the drawn image.</param>
516  public void DrawRasterImage(Point position, Size size, RasterImage image, string tag = null)
517  {
518  DrawRasterImage(0, 0, image.Width, image.Height, position.X, position.Y, size.Width, size.Height, image, tag);
519  }
520 
521  /// <summary>
522  /// Save the current transform state (rotation, translation, scale).
523  /// </summary>
524  public void Save()
525  {
526  Actions.Add(new StateAction(StateAction.StateActionTypes.Save));
527  }
528 
529  /// <summary>
530  /// Restore the previous transform state (rotation, translation scale).
531  /// </summary>
532  public void Restore()
533  {
534  Actions.Add(new StateAction(StateAction.StateActionTypes.Restore));
535  }
536 
537  private void FixGraphicsStateStack()
538  {
540  {
541  return;
542  }
543 
544  int count = 0;
545  List<int> toBeRemoved = new List<int>();
546 
547  for (int i = 0; i < this.Actions.Count; i++)
548  {
549  if (this.Actions[i] is StateAction st)
550  {
551  if (st.StateActionType == StateAction.StateActionTypes.Save)
552  {
553  count++;
554  }
555  else if (st.StateActionType == StateAction.StateActionTypes.Restore)
556  {
557  if (count == 0)
558  {
559  toBeRemoved.Add(i);
560  }
561  else
562  {
563  count--;
564  }
565  }
566  }
567  }
568 
570  {
571  if (count > 0 || toBeRemoved.Count > 0)
572  {
573  throw new UnbalancedStackException(count, toBeRemoved.Count);
574  }
575  }
576  else if (UnbalancedStackAction == UnbalancedStackActions.SilentlyFix)
577  {
578  if (count > 0 || toBeRemoved.Count > 0)
579  {
580  for (int i = toBeRemoved.Count - 1; i >= 0; i--)
581  {
582  this.Actions.RemoveAt(toBeRemoved[i]);
583  }
584 
585  for (int i = 0; i < count; i++)
586  {
587  this.Restore();
588  }
589 
590  this.FixGraphicsStateStack();
591  }
592  }
593  }
594 
595  /// <summary>
596  /// Copy the current graphics to an instance of a class implementing <see cref="IGraphicsContext"/>.
597  /// </summary>
598  /// <param name="destinationContext">The <see cref="IGraphicsContext"/> on which the graphics are to be copied.</param>
599  public void CopyToIGraphicsContext(IGraphicsContext destinationContext)
600  {
601  this.FixGraphicsStateStack();
602 
603  for (int i = 0; i < this.Actions.Count; i++)
604  {
605  if (this.Actions[i] is RectangleAction)
606  {
607  RectangleAction rec = this.Actions[i] as RectangleAction;
608 
609  destinationContext.Tag = rec.Tag;
610  destinationContext.Rectangle(rec.TopLeft.X, rec.TopLeft.Y, rec.Size.Width, rec.Size.Height);
611 
612  if (rec.Fill != null)
613  {
614  if (destinationContext.FillStyle != rec.Fill)
615  {
616  destinationContext.SetFillStyle(rec.Fill);
617  }
618  destinationContext.Fill();
619  }
620  else if (rec.Stroke != null)
621  {
622  if (destinationContext.StrokeStyle != rec.Stroke)
623  {
624  destinationContext.SetStrokeStyle(rec.Stroke);
625  }
626  if (destinationContext.LineWidth != rec.LineWidth)
627  {
628  destinationContext.LineWidth = rec.LineWidth;
629  }
630  destinationContext.SetLineDash(rec.LineDash);
631  destinationContext.LineCap = rec.LineCap;
632  destinationContext.LineJoin = rec.LineJoin;
633 
634  destinationContext.Stroke();
635  }
636  }
637  else if (this.Actions[i] is PathAction)
638  {
639  PathAction pth = this.Actions[i] as PathAction;
640 
641  destinationContext.Tag = pth.Tag;
642 
643  for (int j = 0; j < pth.Path.Segments.Count; j++)
644  {
645  switch (pth.Path.Segments[j].Type)
646  {
647  case SegmentType.Move:
648  destinationContext.MoveTo(pth.Path.Segments[j].Point.X, pth.Path.Segments[j].Point.Y);
649  break;
650  case SegmentType.Line:
651  destinationContext.LineTo(pth.Path.Segments[j].Point.X, pth.Path.Segments[j].Point.Y);
652  break;
653  case SegmentType.CubicBezier:
654  destinationContext.CubicBezierTo(pth.Path.Segments[j].Points[0].X, pth.Path.Segments[j].Points[0].Y, pth.Path.Segments[j].Points[1].X, pth.Path.Segments[j].Points[1].Y, pth.Path.Segments[j].Points[2].X, pth.Path.Segments[j].Points[2].Y);
655  break;
656  case SegmentType.Arc:
657  {
658  ArcSegment seg = pth.Path.Segments[j] as ArcSegment;
659  Segment[] segs = seg.ToBezierSegments();
660  for (int k = 0; k < segs.Length; k++)
661  {
662  switch (segs[k].Type)
663  {
664  case SegmentType.Move:
665  destinationContext.MoveTo(segs[k].Point.X, segs[k].Point.Y);
666  break;
667  case SegmentType.Line:
668  destinationContext.LineTo(segs[k].Point.X, segs[k].Point.Y);
669  break;
670  case SegmentType.CubicBezier:
671  destinationContext.CubicBezierTo(segs[k].Points[0].X, segs[k].Points[0].Y, segs[k].Points[1].X, segs[k].Points[1].Y, segs[k].Points[2].X, segs[k].Points[2].Y);
672  break;
673  }
674  }
675  }
676  break;
677  case SegmentType.Close:
678  destinationContext.Close();
679  break;
680  }
681  }
682 
683  if (pth.IsClipping)
684  {
685  destinationContext.SetClippingPath();
686  }
687  else
688  {
689  if (pth.Fill != null)
690  {
691  if (destinationContext.FillStyle != pth.Fill)
692  {
693  destinationContext.SetFillStyle(pth.Fill);
694  }
695  destinationContext.Fill();
696  }
697  else if (pth.Stroke != null)
698  {
699  if (destinationContext.StrokeStyle != pth.Stroke)
700  {
701  destinationContext.SetStrokeStyle(pth.Stroke);
702  }
703  if (destinationContext.LineWidth != pth.LineWidth)
704  {
705  destinationContext.LineWidth = pth.LineWidth;
706  }
707  destinationContext.SetLineDash(pth.LineDash);
708  destinationContext.LineCap = pth.LineCap;
709  destinationContext.LineJoin = pth.LineJoin;
710 
711  destinationContext.Stroke();
712  }
713  }
714  }
715  else if (this.Actions[i] is TextAction)
716  {
717  TextAction txt = this.Actions[i] as TextAction;
718 
719  destinationContext.Tag = txt.Tag;
720  if (destinationContext.TextBaseline != txt.TextBaseline)
721  {
722  destinationContext.TextBaseline = txt.TextBaseline;
723  }
724  destinationContext.Font = txt.Font;
725 
726  if (txt.Fill != null)
727  {
728  if (destinationContext.FillStyle != txt.Fill)
729  {
730  destinationContext.SetFillStyle(txt.Fill);
731  }
732  destinationContext.FillText(txt.Text, txt.Origin.X, txt.Origin.Y);
733  }
734  else if (txt.Stroke != null)
735  {
736  if (destinationContext.StrokeStyle != txt.Stroke)
737  {
738  destinationContext.SetStrokeStyle(txt.Stroke);
739  }
740  if (destinationContext.LineWidth != txt.LineWidth)
741  {
742  destinationContext.LineWidth = txt.LineWidth;
743  }
744  destinationContext.SetLineDash(txt.LineDash);
745  destinationContext.LineCap = txt.LineCap;
746  destinationContext.LineJoin = txt.LineJoin;
747 
748  destinationContext.StrokeText(txt.Text, txt.Origin.X, txt.Origin.Y);
749  }
750  }
751  else if (this.Actions[i] is TransformAction)
752  {
753  TransformAction trf = this.Actions[i] as TransformAction;
754 
755  if (trf.Delta != null)
756  {
757  destinationContext.Translate(((Point)trf.Delta).X, ((Point)trf.Delta).Y);
758  }
759  else if (trf.Angle != null)
760  {
761  destinationContext.Rotate((double)trf.Angle);
762  }
763  else if (trf.Scale != null)
764  {
765  destinationContext.Scale(((Size)trf.Scale).Width, ((Size)trf.Scale).Height);
766  }
767  else if (trf.Matrix != null)
768  {
769  destinationContext.Transform(trf.Matrix[0, 0], trf.Matrix[1, 0], trf.Matrix[0, 1], trf.Matrix[1, 1], trf.Matrix[0, 2], trf.Matrix[1, 2]);
770  }
771  }
772  else if (this.Actions[i] is StateAction)
773  {
774  if (((StateAction)this.Actions[i]).StateActionType == StateAction.StateActionTypes.Save)
775  {
776  destinationContext.Save();
777  }
778  else
779  {
780  destinationContext.Restore();
781  }
782  }
783  else if (this.Actions[i] is RasterImageAction)
784  {
785  RasterImageAction img = this.Actions[i] as RasterImageAction;
786  destinationContext.Tag = img.Tag;
787  destinationContext.DrawRasterImage(img.SourceX, img.SourceY, img.SourceWidth, img.SourceHeight, img.DestinationX, img.DestinationY, img.DestinationWidth, img.DestinationHeight, img.Image);
788  }
789  else if (this.Actions[i] is FilteredGraphicsAction)
790  {
791  FilteredGraphicsAction fil = this.Actions[i] as FilteredGraphicsAction;
792  destinationContext.DrawFilteredGraphics(fil.Content, fil.Filter);
793  }
794  }
795  }
796 
797  /// <summary>
798  /// Draws a <see cref="Graphics"/> object on the current <see cref="Graphics"/> object.
799  /// </summary>
800  /// <param name="origin">The point at which to place the origin of <paramref name="graphics"/>.</param>
801  /// <param name="graphics">The <see cref="Graphics"/> object to draw on the current <see cref="Graphics"/> object.</param>
802  public void DrawGraphics(Point origin, Graphics graphics)
803  {
804  this.Save();
805  this.Translate(origin);
806 
807  graphics.FixGraphicsStateStack();
808 
809  this.Actions.AddRange(graphics.Actions);
810 
811  this.Restore();
812  }
813 
814  /// <summary>
815  /// Draws a <see cref="Graphics"/> object on the current <see cref="Graphics"/> object.
816  /// </summary>
817  /// <param name="originX">The horizontal coordinate at which to place the origin of <paramref name="graphics"/>.</param>
818  /// <param name="originY">The vertical coordinate at which to place the origin of <paramref name="graphics"/>.</param>
819  /// <param name="graphics">The <see cref="Graphics"/> object to draw on the current <see cref="Graphics"/> object.</param>
820  public void DrawGraphics(double originX, double originY, Graphics graphics)
821  {
822  this.DrawGraphics(new Point(originX, originY), graphics);
823  }
824 
825  /// <summary>
826  /// Draws a <see cref="Graphics"/> object on the current <see cref="Graphics"/> object, applying the specified <paramref name="filter"/>.
827  /// </summary>
828  /// <param name="origin">The point at which to place the origin of <paramref name="graphics"/>.</param>
829  /// <param name="graphics">The <see cref="Graphics"/> object to draw on the current <see cref="Graphics"/> object.</param>
830  /// <param name="filter">An <see cref="IFilter"/> object, representing the filter to apply to the <paramref name="graphics"/> object.</param>
831  public void DrawGraphics(Point origin, Graphics graphics, IFilter filter)
832  {
833  this.Save();
834  this.Translate(origin);
835 
836  Graphics clone = new Graphics();
837  clone.Actions.AddRange(graphics.Actions);
838  clone.FixGraphicsStateStack();
839 
840  this.Actions.Add(new FilteredGraphicsAction(clone, filter));
841 
842  this.Restore();
843  }
844 
845  /// <summary>
846  /// Draws a <see cref="Graphics"/> object on the current <see cref="Graphics"/> object, applying the specified <paramref name="filter"/>.
847  /// </summary>
848  /// <param name="originX">The horizontal coordinate at which to place the origin of <paramref name="graphics"/>.</param>
849  /// <param name="originY">The vertical coordinate at which to place the origin of <paramref name="graphics"/>.</param>
850  /// <param name="graphics">The <see cref="Graphics"/> object to draw on the current <see cref="Graphics"/> object.</param>
851  /// <param name="filter">An <see cref="IFilter"/> object, representing the filter to apply to the <paramref name="graphics"/> object.</param>
852  public void DrawGraphics(double originX, double originY, Graphics graphics, IFilter filter)
853  {
854  this.DrawGraphics(new Point(originX, originY), graphics, filter);
855  }
856 
857 
858  internal static Point Multiply(double[,] matrix, Point pt)
859  {
860  double x = matrix[0, 0] * pt.X + matrix[0, 1] * pt.Y + matrix[0, 2];
861  double y = matrix[1, 0] * pt.X + matrix[1, 1] * pt.Y + matrix[1, 2];
862  double z = matrix[2, 0] * pt.X + matrix[2, 1] * pt.Y + matrix[2, 2];
863 
864  return new Point(x / z, y / z);
865  }
866 
867  internal static double[,] Multiply(double[,] m1, double[,] m2)
868  {
869  return new double[3, 3]
870  {
871  { m1[0,0] * m2[0,0] + m1[0,1] * m2[1,0] + m1[0,2] *m2[2,0], m1[0,0] * m2[0,1] + m1[0,1] * m2[1,1] + m1[0,2] * m2[2,1], m1[0,0] * m2[0,2] + m1[0,1] * m2[1,2] + m1[0,2] * m2[2,2] },
872  { m1[1,0] * m2[0,0] + m1[1,1] * m2[1,0] + m1[1,2] *m2[2,0], m1[1,0] * m2[0,1] + m1[1,1] * m2[1,1] + m1[1,2] * m2[2,1], m1[1,0] * m2[0,2] + m1[1,1] * m2[1,2] + m1[1,2] * m2[2,2] },
873  { m1[2,0] * m2[0,0] + m1[2,1] * m2[1,0] + m1[2,2] *m2[2,0], m1[2,0] * m2[0,1] + m1[2,1] * m2[1,1] + m1[2,2] * m2[2,1], m1[2,0] * m2[0,2] + m1[2,1] * m2[1,2] + m1[2,2] * m2[2,2] }
874  };
875  }
876 
877  internal static double[,] TranslationMatrix(double dx, double dy)
878  {
879  return new double[3, 3]
880  {
881  {1, 0, dx },
882  {0, 1, dy },
883  {0, 0, 1 }
884  };
885  }
886 
887  internal static double[,] ScaleMatrix(double sx, double sy)
888  {
889  return new double[3, 3]
890  {
891  {sx, 0, 0 },
892  {0, sy, 0 },
893  {0, 0, 1 }
894  };
895  }
896 
897  internal static double[,] RotationMatrix(double theta)
898  {
899  return new double[3, 3]
900  {
901  {Math.Cos(theta), -Math.Sin(theta), 0 },
902  {Math.Sin(theta), Math.Cos(theta), 0 },
903  {0, 0, 1 }
904  };
905  }
906 
907  internal static double[,] Invert(double[,] m)
908  {
909  double[,] tbr = new double[3, 3];
910 
911  tbr[0, 0] = (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
912  tbr[0, 1] = -(m[0, 1] * m[2, 2] - m[0, 2] * m[2, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
913  tbr[0, 2] = (m[0, 1] * m[1, 2] - m[0, 2] * m[1, 1]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
914  tbr[1, 0] = -(m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
915  tbr[1, 1] = (m[0, 0] * m[2, 2] - m[0, 2] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
916  tbr[1, 2] = -(m[0, 0] * m[1, 2] - m[0, 2] * m[1, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
917  tbr[2, 0] = (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
918  tbr[2, 1] = -(m[0, 0] * m[2, 1] - m[0, 1] * m[2, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
919  tbr[2, 2] = (m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0]) / (m[0, 0] * m[1, 1] * m[2, 2] - m[0, 0] * m[1, 2] * m[2, 1] - m[1, 0] * m[0, 1] * m[2, 2] + m[2, 0] * m[0, 1] * m[1, 2] + m[1, 0] * m[0, 2] * m[2, 1] - m[2, 0] * m[0, 2] * m[1, 1]);
920 
921  return tbr;
922  }
923 
924  /// <summary>
925  /// Creates a new <see cref="Graphics"/> object in which all the graphics actions have been transformed using an arbitrary transformation function. Raster images are replaced by grey rectangles.
926  /// </summary>
927  /// <param name="transformationFunction">An arbitrary transformation function.</param>
928  /// <param name="linearisationResolution">The resolution that will be used to linearise curve segments.</param>
929  /// <returns>A new <see cref="Graphics"/> object in which all graphics actions have been linearised and transformed using the <paramref name="transformationFunction"/>.</returns>
930  public Graphics Transform(Func<Point, Point> transformationFunction, double linearisationResolution)
931  {
932  Graphics destinationGraphics = new Graphics();
933 
934  Stack<double[,]> transformMatrix = new Stack<double[,]>();
935  double[,] currMatrix = new double[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
936 
937  for (int i = 0; i < this.Actions.Count; i++)
938  {
939  if (this.Actions[i] is RectangleAction)
940  {
941  RectangleAction rec = this.Actions[i] as RectangleAction;
942 
943  GraphicsPath rectanglePath = new GraphicsPath();
944 
945  Point pt1 = transformationFunction(Multiply(currMatrix, rec.TopLeft));
946  Point pt2 = transformationFunction(Multiply(currMatrix, new Point(rec.TopLeft.X + rec.Size.Width, rec.TopLeft.Y)));
947  Point pt3 = transformationFunction(Multiply(currMatrix, new Point(rec.TopLeft.X + rec.Size.Width, rec.TopLeft.Y + rec.Size.Height)));
948  Point pt4 = transformationFunction(Multiply(currMatrix, new Point(rec.TopLeft.X, rec.TopLeft.Y + rec.Size.Height)));
949 
950  rectanglePath.MoveTo(pt1).LineTo(pt2).LineTo(pt3).LineTo(pt4).Close();
951 
952  if (rec.Fill != null)
953  {
954  destinationGraphics.FillPath(rectanglePath, rec.Fill, rec.Tag);
955  }
956  else if (rec.Stroke != null)
957  {
958  destinationGraphics.StrokePath(rectanglePath, rec.Stroke, rec.LineWidth, rec.LineCap, rec.LineJoin, rec.LineDash, rec.Tag);
959  }
960  }
961  else if (this.Actions[i] is PathAction)
962  {
963  PathAction pth = this.Actions[i] as PathAction;
964 
965  GraphicsPath newPath = pth.Path.Linearise(linearisationResolution).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
966 
967  if (pth.IsClipping)
968  {
969  destinationGraphics.SetClippingPath(newPath);
970  }
971  else
972  {
973  if (pth.Fill != null)
974  {
975  destinationGraphics.FillPath(newPath, pth.Fill, pth.Tag);
976  }
977  else if (pth.Stroke != null)
978  {
979  destinationGraphics.StrokePath(newPath, pth.Stroke, pth.LineWidth, pth.LineCap, pth.LineJoin, pth.LineDash, pth.Tag);
980  }
981  }
982  }
983  else if (this.Actions[i] is TextAction)
984  {
985  TextAction txt = this.Actions[i] as TextAction;
986 
987  GraphicsPath textPath = new GraphicsPath().AddText(txt.Origin, txt.Text, txt.Font, txt.TextBaseline).Linearise(linearisationResolution).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
988 
989  if (txt.Fill != null)
990  {
991  destinationGraphics.FillPath(textPath, txt.Fill, txt.Tag);
992  }
993  else if (txt.Stroke != null)
994  {
995  destinationGraphics.StrokePath(textPath, txt.Stroke, txt.LineWidth, txt.LineCap, txt.LineJoin, txt.LineDash, txt.Tag);
996  }
997  }
998  else if (this.Actions[i] is TransformAction)
999  {
1000  TransformAction trf = this.Actions[i] as TransformAction;
1001 
1002  if (trf.Delta != null)
1003  {
1004  currMatrix = Multiply(currMatrix, TranslationMatrix(trf.Delta.Value.X, trf.Delta.Value.Y));
1005  }
1006  else if (trf.Angle != null)
1007  {
1008  currMatrix = Multiply(currMatrix, RotationMatrix(trf.Angle.Value));
1009  }
1010  else if (trf.Scale != null)
1011  {
1012  currMatrix = Multiply(currMatrix, ScaleMatrix(trf.Scale.Value.Width, trf.Scale.Value.Height));
1013  }
1014  else if (trf.Matrix != null)
1015  {
1016  currMatrix = Multiply(currMatrix, trf.Matrix);
1017  }
1018  }
1019  else if (this.Actions[i] is StateAction)
1020  {
1021  if (((StateAction)this.Actions[i]).StateActionType == StateAction.StateActionTypes.Save)
1022  {
1023  transformMatrix.Push(currMatrix);
1024  }
1025  else
1026  {
1027  currMatrix = transformMatrix.Pop();
1028  }
1029  }
1030  else if (this.Actions[i] is RasterImageAction)
1031  {
1032  RasterImageAction img = this.Actions[i] as RasterImageAction;
1033 
1034  GraphicsPath rectanglePath = new GraphicsPath();
1035 
1036  Point pt1 = transformationFunction(Multiply(currMatrix, new Point(img.DestinationX, img.DestinationY)));
1037  Point pt2 = transformationFunction(Multiply(currMatrix, new Point(img.DestinationX + img.DestinationWidth, img.DestinationY)));
1038  Point pt3 = transformationFunction(Multiply(currMatrix, new Point(img.DestinationX + img.DestinationWidth, img.DestinationY + img.DestinationHeight)));
1039  Point pt4 = transformationFunction(Multiply(currMatrix, new Point(img.DestinationX, img.DestinationY + img.DestinationHeight)));
1040 
1041  rectanglePath.MoveTo(pt1).LineTo(pt2).LineTo(pt3).LineTo(pt4).Close();
1042 
1043  destinationGraphics.FillPath(rectanglePath, Colour.FromRgb(220, 220, 220), img.Tag);
1044  }
1045  }
1046 
1047  return destinationGraphics;
1048  }
1049 
1050  private GraphicsPath ReduceMaximumLength(GraphicsPath path, double maxLength)
1051  {
1052  GraphicsPath shortLinearisedPath = new GraphicsPath();
1053 
1054  // Square of the max length - so that we avoid the square roots.
1055  double maxLengthSq = maxLength * maxLength;
1056 
1057  Point currPoint = new Point();
1058  Point startFigurePoint = new Point();
1059 
1060  foreach (Segment seg in path.Segments)
1061  {
1062  if (seg is MoveSegment mov)
1063  {
1064  shortLinearisedPath.MoveTo(mov.Point);
1065  startFigurePoint = mov.Point;
1066  currPoint = mov.Point;
1067  }
1068  else if (seg is LineSegment line)
1069  {
1070  double lengthSq = (line.Point.X - currPoint.X) * (line.Point.X - currPoint.X) +
1071  (line.Point.Y - currPoint.Y) * (line.Point.Y - currPoint.Y);
1072 
1073  if (lengthSq < maxLengthSq)
1074  {
1075  shortLinearisedPath.LineTo(line.Point);
1076  }
1077  else
1078  {
1079  int segmentCount = (int)Math.Ceiling(Math.Sqrt(lengthSq / maxLengthSq));
1080 
1081  for (int j = 0; j < segmentCount - 1; j++)
1082  {
1083  Point endPoint = new Point(currPoint.X + (line.Point.X - currPoint.X) * (j + 1) / segmentCount,
1084  currPoint.Y + (line.Point.Y - currPoint.Y) * (j + 1) / segmentCount);
1085  shortLinearisedPath.LineTo(endPoint);
1086  }
1087 
1088  shortLinearisedPath.LineTo(line.Point);
1089  }
1090  currPoint = line.Point;
1091  }
1092  else if (seg is CloseSegment close)
1093  {
1094  double lengthSq = (startFigurePoint.X - currPoint.X) * (startFigurePoint.X - currPoint.X) +
1095  (startFigurePoint.Y - currPoint.Y) * (startFigurePoint.Y - currPoint.Y);
1096 
1097  if (lengthSq < maxLengthSq)
1098  {
1099  shortLinearisedPath.Close();
1100  }
1101  else
1102  {
1103  int segmentCount = (int)Math.Ceiling(Math.Sqrt(lengthSq / maxLengthSq));
1104 
1105  for (int j = 0; j < segmentCount - 1; j++)
1106  {
1107  Point endPoint = new Point(currPoint.X + (startFigurePoint.X - currPoint.X) * (j + 1) / segmentCount,
1108  currPoint.Y + (startFigurePoint.Y - currPoint.Y) * (j + 1) / segmentCount);
1109  shortLinearisedPath.LineTo(endPoint);
1110  }
1111 
1112  shortLinearisedPath.Close();
1113  }
1114  currPoint = startFigurePoint;
1115  }
1116  }
1117 
1118  return shortLinearisedPath;
1119  }
1120 
1121  /// <summary>
1122  /// Creates a new <see cref="Graphics"/> object in which all the graphics actions have been transformed using an arbitrary transformation function. Raster images are replaced by grey rectangles.
1123  /// </summary>
1124  /// <param name="transformationFunction">An arbitrary transformation function.</param>
1125  /// <param name="linearisationResolution">The resolution that will be used to linearise curve segments.</param>
1126  /// <param name="maxSegmentLength">The maximum length of line segments.</param>
1127  /// <returns>A new <see cref="Graphics"/> object in which all graphics actions have been linearised and transformed using the <paramref name="transformationFunction"/>.</returns>
1128  public Graphics Transform(Func<Point, Point> transformationFunction, double linearisationResolution, double maxSegmentLength)
1129  {
1130  Graphics destinationGraphics = new Graphics();
1131 
1132  Stack<double[,]> transformMatrix = new Stack<double[,]>();
1133  double[,] currMatrix = new double[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
1134 
1135  for (int i = 0; i < this.Actions.Count; i++)
1136  {
1137  if (this.Actions[i] is RectangleAction)
1138  {
1139  RectangleAction rec = this.Actions[i] as RectangleAction;
1140 
1141  GraphicsPath rectanglePath = new GraphicsPath();
1142 
1143  Point pt1 = rec.TopLeft;
1144  Point pt2 = new Point(rec.TopLeft.X + rec.Size.Width, rec.TopLeft.Y);
1145  Point pt3 = new Point(rec.TopLeft.X + rec.Size.Width, rec.TopLeft.Y + rec.Size.Height);
1146  Point pt4 = new Point(rec.TopLeft.X, rec.TopLeft.Y + rec.Size.Height);
1147 
1148  rectanglePath.MoveTo(pt1).LineTo(pt2).LineTo(pt3).LineTo(pt4).Close();
1149 
1150  rectanglePath = ReduceMaximumLength(rectanglePath, maxSegmentLength).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
1151 
1152  if (rec.Fill != null)
1153  {
1154  destinationGraphics.FillPath(rectanglePath, rec.Fill, rec.Tag);
1155  }
1156  else if (rec.Stroke != null)
1157  {
1158  destinationGraphics.StrokePath(rectanglePath, rec.Stroke, rec.LineWidth, rec.LineCap, rec.LineJoin, rec.LineDash, rec.Tag);
1159  }
1160  }
1161  else if (this.Actions[i] is PathAction)
1162  {
1163  PathAction pth = this.Actions[i] as PathAction;
1164 
1165  GraphicsPath newPath = ReduceMaximumLength(pth.Path.Linearise(linearisationResolution), maxSegmentLength).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
1166 
1167  if (pth.IsClipping)
1168  {
1169  destinationGraphics.SetClippingPath(newPath);
1170  }
1171  else
1172  {
1173  if (pth.Fill != null)
1174  {
1175  destinationGraphics.FillPath(newPath, pth.Fill, pth.Tag);
1176  }
1177  else if (pth.Stroke != null)
1178  {
1179  destinationGraphics.StrokePath(newPath, pth.Stroke, pth.LineWidth, pth.LineCap, pth.LineJoin, pth.LineDash, pth.Tag);
1180  }
1181  }
1182  }
1183  else if (this.Actions[i] is TextAction)
1184  {
1185  TextAction txt = this.Actions[i] as TextAction;
1186 
1187  GraphicsPath textPath = ReduceMaximumLength(new GraphicsPath().AddText(txt.Origin, txt.Text, txt.Font, txt.TextBaseline).Linearise(linearisationResolution), maxSegmentLength).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
1188 
1189  if (txt.Fill != null)
1190  {
1191  destinationGraphics.FillPath(textPath, txt.Fill, txt.Tag);
1192  }
1193  else if (txt.Stroke != null)
1194  {
1195  destinationGraphics.StrokePath(textPath, txt.Stroke, txt.LineWidth, txt.LineCap, txt.LineJoin, txt.LineDash, txt.Tag);
1196  }
1197  }
1198  else if (this.Actions[i] is TransformAction)
1199  {
1200  TransformAction trf = this.Actions[i] as TransformAction;
1201 
1202  if (trf.Delta != null)
1203  {
1204  currMatrix = Multiply(currMatrix, TranslationMatrix(trf.Delta.Value.X, trf.Delta.Value.Y));
1205  }
1206  else if (trf.Angle != null)
1207  {
1208  currMatrix = Multiply(currMatrix, RotationMatrix(trf.Angle.Value));
1209  }
1210  else if (trf.Scale != null)
1211  {
1212  currMatrix = Multiply(currMatrix, ScaleMatrix(trf.Scale.Value.Width, trf.Scale.Value.Height));
1213  }
1214  else if (trf.Matrix != null)
1215  {
1216  currMatrix = Multiply(currMatrix, trf.Matrix);
1217  }
1218  }
1219  else if (this.Actions[i] is StateAction)
1220  {
1221  if (((StateAction)this.Actions[i]).StateActionType == StateAction.StateActionTypes.Save)
1222  {
1223  transformMatrix.Push(currMatrix);
1224  }
1225  else
1226  {
1227  currMatrix = transformMatrix.Pop();
1228  }
1229  }
1230  else if (this.Actions[i] is RasterImageAction)
1231  {
1232  RasterImageAction img = this.Actions[i] as RasterImageAction;
1233 
1234  GraphicsPath rectanglePath = new GraphicsPath();
1235 
1236  Point pt1 = new Point(img.DestinationX, img.DestinationY);
1237  Point pt2 = new Point(img.DestinationX + img.DestinationWidth, img.DestinationY);
1238  Point pt3 = new Point(img.DestinationX + img.DestinationWidth, img.DestinationY + img.DestinationHeight);
1239  Point pt4 = new Point(img.DestinationX, img.DestinationY + img.DestinationHeight);
1240 
1241  rectanglePath.MoveTo(pt1).LineTo(pt2).LineTo(pt3).LineTo(pt4).Close();
1242 
1243  rectanglePath = ReduceMaximumLength(rectanglePath, maxSegmentLength).Transform(pt => transformationFunction(Multiply(currMatrix, pt)));
1244 
1245  destinationGraphics.FillPath(rectanglePath, Colour.FromRgb(220, 220, 220), img.Tag);
1246  }
1247  }
1248 
1249  return destinationGraphics;
1250  }
1251 
1252  /// <summary>
1253  /// Creates a new <see cref="Graphics"/> object by linearising all of the elements of the current instance, i.e. replacing curve segments with series of line segments that approximate them. Raster images are left unchanged.
1254  /// </summary>
1255  /// <param name="resolution">The resolution that will be used to linearise curve segments.</param>
1256  /// <returns>A new <see cref="Graphics"/> object containing the linearised elements.</returns>
1257  public Graphics Linearise(double resolution)
1258  {
1259  Graphics destinationGraphics = new Graphics();
1260 
1261  for (int i = 0; i < this.Actions.Count; i++)
1262  {
1263  if (this.Actions[i] is RectangleAction || this.Actions[i] is TransformAction || this.Actions[i] is StateAction || this.Actions[i] is RasterImageAction)
1264  {
1265  destinationGraphics.Actions.Add(this.Actions[i]);
1266  }
1267  else if (this.Actions[i] is PathAction)
1268  {
1269  PathAction pth = this.Actions[i] as PathAction;
1270 
1271  GraphicsPath newPath = pth.Path.Linearise(resolution);
1272 
1273  if (pth.IsClipping)
1274  {
1275  destinationGraphics.SetClippingPath(newPath);
1276  }
1277  else
1278  {
1279  if (pth.Fill != null)
1280  {
1281  destinationGraphics.FillPath(newPath, pth.Fill, pth.Tag);
1282  }
1283  else if (pth.Stroke != null)
1284  {
1285  destinationGraphics.StrokePath(newPath, pth.Stroke, pth.LineWidth, pth.LineCap, pth.LineJoin, pth.LineDash, pth.Tag);
1286  }
1287  }
1288  }
1289  else if (this.Actions[i] is TextAction)
1290  {
1291  TextAction txt = this.Actions[i] as TextAction;
1292 
1293  GraphicsPath textPath = new GraphicsPath().AddText(txt.Origin, txt.Text, txt.Font, txt.TextBaseline).Linearise(resolution);
1294 
1295  if (txt.Fill != null)
1296  {
1297  destinationGraphics.FillPath(textPath, txt.Fill, txt.Tag);
1298  }
1299  else if (txt.Stroke != null)
1300  {
1301  destinationGraphics.StrokePath(textPath, txt.Stroke, txt.LineWidth, txt.LineCap, txt.LineJoin, txt.LineDash, txt.Tag);
1302  }
1303  }
1304  }
1305 
1306  return destinationGraphics;
1307  }
1308 
1309  /// <summary>
1310  /// Computes the rectangular bounds of the region affected by the drawing operations performed on the <see cref="Graphics"/> object.
1311  /// </summary>
1312  /// <returns>The smallest rectangle that contains all the elements drawn on the <see cref="Graphics"/>.</returns>
1314  {
1315  Rectangle tbr = Rectangle.NaN;
1316  bool initialised = false;
1317 
1318  Stack<double[,]> transformMatrix = new Stack<double[,]>();
1319  double[,] currMatrix = new double[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
1320 
1321  for (int i = 0; i < this.Actions.Count; i++)
1322  {
1323  if (this.Actions[i] is IPrintableAction act)
1324  {
1325  Rectangle bounds = act.GetBounds();
1326 
1327  double lineThickness = double.IsNaN(act.LineWidth) ? 0 : act.LineWidth;
1328 
1329  Point pt1 = Multiply(currMatrix, new Point(bounds.Location.X - lineThickness * 0.5, bounds.Location.Y - lineThickness * 0.5));
1330  Point pt2 = Multiply(currMatrix, new Point(bounds.Location.X + bounds.Size.Width + lineThickness * 0.5, bounds.Location.Y - lineThickness * 0.5));
1331  Point pt3 = Multiply(currMatrix, new Point(bounds.Location.X + bounds.Size.Width + lineThickness * 0.5, bounds.Location.Y + bounds.Size.Height + lineThickness * 0.5));
1332  Point pt4 = Multiply(currMatrix, new Point(bounds.Location.X - lineThickness * 0.5, bounds.Location.Y + bounds.Size.Height + lineThickness * 0.5));
1333 
1334  bounds = Point.Bounds(pt1, pt2, pt3, pt4);
1335 
1336  if (!double.IsNaN(bounds.Location.X) && !double.IsNaN(bounds.Location.Y) && !double.IsNaN(bounds.Size.Width) && !double.IsNaN(bounds.Size.Height))
1337  {
1338  if (!initialised)
1339  {
1340  tbr = bounds;
1341  initialised = true;
1342  }
1343  else
1344  {
1345  tbr = Rectangle.Union(tbr, bounds);
1346  }
1347  }
1348  }
1349  else if (this.Actions[i] is TransformAction)
1350  {
1351  TransformAction trf = this.Actions[i] as TransformAction;
1352 
1353  if (trf.Delta != null)
1354  {
1355  currMatrix = Multiply(currMatrix, TranslationMatrix(trf.Delta.Value.X, trf.Delta.Value.Y));
1356  }
1357  else if (trf.Angle != null)
1358  {
1359  currMatrix = Multiply(currMatrix, RotationMatrix(trf.Angle.Value));
1360  }
1361  else if (trf.Scale != null)
1362  {
1363  currMatrix = Multiply(currMatrix, ScaleMatrix(trf.Scale.Value.Width, trf.Scale.Value.Height));
1364  }
1365  else if (trf.Matrix != null)
1366  {
1367  currMatrix = Multiply(currMatrix, trf.Matrix);
1368  }
1369  }
1370  else if (this.Actions[i] is StateAction)
1371  {
1372  if (((StateAction)this.Actions[i]).StateActionType == StateAction.StateActionTypes.Save)
1373  {
1374  transformMatrix.Push(currMatrix);
1375  }
1376  else
1377  {
1378  currMatrix = transformMatrix.Pop();
1379  }
1380  }
1381  }
1382 
1383  return tbr;
1384  }
1385 
1386  /// <summary>
1387  /// A method that is used to rasterise a region of a <see cref="Graphics"/> object. Set this to <see langword="null"/> if you wish to use the default
1388  /// rasterisation methods (implemented by either VectSharp.Raster, or VectSharp.Raster.ImageSharp). You will have to provide your own implementation
1389  /// of this method if neither VectSharp.Raster nor VectSharp.Raster.ImageSharp are referenced by your project. The first argument of this method is
1390  /// the <see cref="Graphics"/> to be rasterised, the second is a <see cref="Rectangle"/> representing the region to rasterise, the third is a
1391  /// <see cref="double"/> representing the scale, and the third is a boolean value indicating whether the resulting <see cref="RasterImage"/> should
1392  /// be interpolated.
1393  /// </summary>
1394  public static Func<Graphics, Rectangle, double, bool, RasterImage> RasterisationMethod = null;
1395 
1396  /// <summary>
1397  /// Tries to rasterise specified region of this <see cref="Graphics"/> object using the default rasterisation method.
1398  /// </summary>
1399  /// <param name="region">The region of the <see cref="Graphics"/> to rasterise.</param>
1400  /// <param name="scale">The scale at which the image is rasterised.</param>
1401  /// <param name="interpolate">Determines whether the resulting <see cref="RasterImage"/> should be interpolated or not.</param>
1402  /// <param name="output">When this method returns, this will contain the rasterised image (or <see langword="null"/> if the image could not be rasterised.</param>
1403  /// <returns><see langword="true"/> if the image could be rasterised; <see langword="false"/> if it could not be rasterised.</returns>
1404  public bool TryRasterise(Rectangle region, double scale, bool interpolate, out RasterImage output)
1405  {
1406  if (RasterisationMethod != null)
1407  {
1408  output = RasterisationMethod(this, region, scale, interpolate);
1409  return true;
1410  }
1411  else
1412  {
1413  System.Reflection.Assembly raster;
1414 
1415  try
1416  {
1417  raster = System.Reflection.Assembly.Load("VectSharp.Raster");
1418  if (raster != null)
1419  {
1420  System.Reflection.MethodInfo rasteriser = raster.GetType("VectSharp.Raster.Raster").GetMethod("Rasterise", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
1421  output = (RasterImage)rasteriser.Invoke(null, new object[] { this, region, scale, interpolate });
1422  return true;
1423  }
1424  }
1425  catch { }
1426 
1427  try
1428  {
1429  raster = System.Reflection.Assembly.Load("VectSharp.Raster.ImageSharp");
1430  if (raster != null)
1431  {
1432  System.Reflection.MethodInfo rasteriser = raster.GetType("VectSharp.Raster.ImageSharp.ImageSharpContextInterpreter").GetMethod("Rasterise", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
1433  output = (RasterImage)rasteriser.Invoke(null, new object[] { this, region, scale, interpolate });
1434  return true;
1435  }
1436  }
1437  catch { }
1438 
1439 
1440  output = null;
1441  return false;
1442  }
1443  }
1444  }
1445 }
VectSharp.Graphics.StrokeRectangle
void StrokeRectangle(double leftX, double topY, double width, double height, Brush strokeColour, double lineWidth=1, LineCaps lineCap=LineCaps.Butt, LineJoins lineJoin=LineJoins.Miter, LineDash? lineDash=null, string tag=null)
Stroke a rectangle.
Definition: Graphics.cs:449
VectSharp.Rectangle
Represents a rectangle.
Definition: Point.cs:173
VectSharp.Graphics.Linearise
Graphics Linearise(double resolution)
Creates a new Graphics object by linearising all of the elements of the current instance,...
Definition: Graphics.cs:1257
VectSharp.Graphics.Translate
void Translate(double x, double y)
Translate the coordinate system origin.
Definition: Graphics.cs:370
VectSharp.Graphics.Transform
Graphics Transform(Func< Point, Point > transformationFunction, double linearisationResolution)
Creates a new Graphics object in which all the graphics actions have been transformed using an arbitr...
Definition: Graphics.cs:930
VectSharp.Graphics.FillPath
void FillPath(GraphicsPath path, Brush fillColour, string tag=null)
Fill a GraphicsPath.
Definition: Graphics.cs:276
VectSharp.Graphics.DrawGraphics
void DrawGraphics(Point origin, Graphics graphics)
Draws a Graphics object on the current Graphics object.
Definition: Graphics.cs:802
VectSharp.Graphics.StrokePath
void StrokePath(GraphicsPath path, Brush strokeColour, double lineWidth=1, LineCaps lineCap=LineCaps.Butt, LineJoins lineJoin=LineJoins.Miter, LineDash? lineDash=null, string tag=null)
Stroke a GraphicsPath.
Definition: Graphics.cs:292
VectSharp.IGraphicsContext.LineTo
void LineTo(double x, double y)
Draw a line from the previous point to the specified point.
VectSharp.Graphics.DrawGraphics
void DrawGraphics(double originX, double originY, Graphics graphics, IFilter filter)
Draws a Graphics object on the current Graphics object, applying the specified filter .
Definition: Graphics.cs:852
VectSharp.Graphics.UnbalancedStackAction
static UnbalancedStackActions UnbalancedStackAction
Determines how an unbalanced graphics state stack (which occurs if the number of calls to Save and Re...
Definition: Graphics.cs:266
VectSharp.Colour
Represents an RGB colour.
Definition: Colour.cs:26
VectSharp.GraphicsPath.Linearise
GraphicsPath Linearise(double resolution)
Linearises a GraphicsPath, replacing curve segments with series of line segments that approximate the...
Definition: GraphicsPath.cs:1844
VectSharp.Font.Font
Font(FontFamily fontFamily, double fontSize)
Create a new Font object, given the base typeface and the font size.
Definition: Font.cs:165
VectSharp.IGraphicsContext.Stroke
void Stroke()
Stroke the current path using the current StrokeStyle, LineWidth, LineCap, LineJoin and LineDash.
VectSharp.RasterImage
Represents a raster image, created from raw pixel data. Consider using the derived classes included i...
Definition: RasterImage.cs:99
VectSharp.GraphicsPath
Represents a graphics path that can be filled or stroked.
Definition: GraphicsPath.cs:29
VectSharp.IGraphicsContext.CubicBezierTo
void CubicBezierTo(double p1X, double p1Y, double p2X, double p2Y, double p3X, double p3Y)
Add to the current figure a cubic Bezier from the current point to a destination point,...
VectSharp.IGraphicsContext.FillStyle
Brush FillStyle
Current brush used to fill paths.
Definition: Graphics.cs:146
VectSharp
Definition: Brush.cs:26
VectSharp.Graphics.SetClippingPath
void SetClippingPath(double leftX, double topY, double width, double height)
Intersect the current clipping path with the specified rectangle.
Definition: Graphics.cs:313
VectSharp.Filters.IFilter
Represents a filter. Do not implement this interface directly; instead, implement ILocationInvariantF...
Definition: Filters.cs:26
VectSharp.Graphics.SetClippingPath
void SetClippingPath(Point topLeft, Size size)
Intersect the current clipping path with the specified rectangle.
Definition: Graphics.cs:323
VectSharp.UnbalancedStackActions
UnbalancedStackActions
Represents ways to deal with unbalanced graphics state stacks.
Definition: Enums.cs:183
VectSharp.Graphics.CopyToIGraphicsContext
void CopyToIGraphicsContext(IGraphicsContext destinationContext)
Copy the current graphics to an instance of a class implementing IGraphicsContext.
Definition: Graphics.cs:599
VectSharp.Graphics.DrawRasterImage
void DrawRasterImage(int sourceX, int sourceY, int sourceWidth, int sourceHeight, double destinationX, double destinationY, double destinationWidth, double destinationHeight, RasterImage image, string tag=null)
Draw a raster image.
Definition: Graphics.cs:467
VectSharp.IGraphicsContext.Rectangle
void Rectangle(double x0, double y0, double width, double height)
Add a rectangle figure to the current path.
VectSharp.RasterImage.Width
int Width
The width in pixels of the image.
Definition: RasterImage.cs:123
VectSharp.Brush
Represents a brush used to fill or stroke graphics elements. This could be a solid colour,...
Definition: Brush.cs:31
VectSharp.IGraphicsContext.StrokeStyle
Brush StrokeStyle
Current brush used to stroke paths.
Definition: Graphics.cs:163
VectSharp.Size.Height
double Height
Height of the object.
Definition: Point.cs:155
VectSharp.IGraphicsContext.Font
Font Font
The current font.
Definition: Graphics.cs:91
VectSharp.LineCaps
LineCaps
Represents line caps.
Definition: Enums.cs:71
VectSharp.IGraphicsContext.Save
void Save()
Save the current transform state (rotation, translation, scale). This should be implemented as a LIFO...
VectSharp.Graphics.Save
void Save()
Save the current transform state (rotation, translation, scale).
Definition: Graphics.cs:524
VectSharp.UnbalancedStackException
The exception that is thrown when an unbalanced graphics state stack occurs.
Definition: Graphics.cs:254
VectSharp.Graphics.FillRectangle
void FillRectangle(Point topLeft, Size size, Brush fillColour, string tag=null)
Fill a rectangle.
Definition: Graphics.cs:401
VectSharp.Graphics.DrawRasterImage
void DrawRasterImage(Point position, RasterImage image, string tag=null)
Draw a raster image.
Definition: Graphics.cs:490
VectSharp.Font
Represents a typeface with a specific size.
Definition: Font.cs:29
VectSharp.IGraphicsContext.Restore
void Restore()
Restore the previous transform state (rotation, translation, scale). This should be implemented as a ...
VectSharp.TextBaselines
TextBaselines
Represent text baselines.
Definition: Enums.cs:24
VectSharp.IGraphicsContext.Rotate
void Rotate(double angle)
Rotate the coordinate system around the origin.
VectSharp.Graphics
Represents an abstract drawing surface.
Definition: Graphics.cs:262
VectSharp.Graphics.RotateAt
void RotateAt(double angle, Point pivot)
Rotate the coordinate system around a pivot point.
Definition: Graphics.cs:342
VectSharp.IGraphicsContext.MoveTo
void MoveTo(double x, double y)
Change the current point without drawing a line from the previous point. If necessary,...
VectSharp.Rectangle.Size
Size Size
The size of the rectangle.
Definition: Point.cs:187
VectSharp.IGraphicsContext.LineWidth
double LineWidth
Current line width used to stroke paths.
Definition: Graphics.cs:205
VectSharp.IGraphicsContext.SetFillStyle
void SetFillStyle((int r, int g, int b, double a) style)
Set the current FillStyle.
VectSharp.IGraphicsContext.Scale
void Scale(double scaleX, double scaleY)
Scale the coordinate system with respect to the origin.
VectSharp.Graphics.RasterisationMethod
static Func< Graphics, Rectangle, double, bool, RasterImage > RasterisationMethod
A method that is used to rasterise a region of a Graphics object. Set this to null if you wish to use...
Definition: Graphics.cs:1394
VectSharp.GraphicsPath.AddText
GraphicsPath AddText(double originX, double originY, string text, Font font, TextBaselines textBaseline=TextBaselines.Top)
Add the contour of a text string to the current path.
Definition: GraphicsPath.cs:312
VectSharp.IGraphicsContext.SetStrokeStyle
void SetStrokeStyle((int r, int g, int b, double a) style)
Set the current StrokeStyle.
VectSharp.LineJoins
LineJoins
Represents line joining options.
Definition: Enums.cs:92
VectSharp.IGraphicsContext.SetStrokeStyle
void SetStrokeStyle(Brush style)
Set the current StrokeStyle.
VectSharp.Graphics.TryRasterise
bool TryRasterise(Rectangle region, double scale, bool interpolate, out RasterImage output)
Tries to rasterise specified region of this Graphics object using the default rasterisation method.
Definition: Graphics.cs:1404
VectSharp.Rectangle.NaN
static readonly Rectangle NaN
A rectangle whose dimensions are all double.NaN.
Definition: Point.cs:177
VectSharp.Segment
Represents a segment as part of a GraphicsPath.
Definition: Segment.cs:29
VectSharp.IGraphicsContext.DrawFilteredGraphics
void DrawFilteredGraphics(Graphics graphics, IFilter filter)
Draws a Graphics object, applying the specified filter .
VectSharp.Graphics.DrawGraphics
void DrawGraphics(Point origin, Graphics graphics, IFilter filter)
Draws a Graphics object on the current Graphics object, applying the specified filter .
Definition: Graphics.cs:831
VectSharp.Graphics.Rotate
void Rotate(double angle)
Rotate the coordinate system around the origin.
Definition: Graphics.cs:332
VectSharp.IGraphicsContext.SetLineDash
void SetLineDash(LineDash dash)
Set the current line dash pattern.
VectSharp.GraphicsPath.Transform
GraphicsPath Transform(Func< Point, Point > transformationFunction)
Transforms all of the Points in the GraphicsPath with an arbitrary transformation function.
Definition: GraphicsPath.cs:2910
VectSharp.Point.Point
Point(double x, double y)
Create a new Point.
Definition: Point.cs:44
VectSharp.Graphics.DrawRasterImage
void DrawRasterImage(Point position, Size size, RasterImage image, string tag=null)
Draw a raster image.
Definition: Graphics.cs:516
VectSharp.SegmentType
SegmentType
Types of Segment.
Definition: Enums.cs:152
VectSharp.GraphicsPath.LineTo
GraphicsPath LineTo(Point p)
Move the current point and trace a segment from the previous point.
Definition: GraphicsPath.cs:66
VectSharp.Point.X
double X
Horizontal (x) coordinate, measured to the right of the origin.
Definition: Point.cs:32
VectSharp.RasterImage.Height
int Height
The height in pixels of the image.
Definition: RasterImage.cs:128
VectSharp.GraphicsPath.MoveTo
GraphicsPath MoveTo(Point p)
Move the current point without tracing a segment from the previous point.
Definition: GraphicsPath.cs:41
VectSharp.Graphics.Scale
void Scale(double scaleX, double scaleY)
Scale the coordinate system with respect to the origin.
Definition: Graphics.cs:389
VectSharp.IGraphicsContext.TextBaseline
TextBaselines TextBaseline
The current text baseline.
Definition: Graphics.cs:96
VectSharp.Graphics.SetClippingPath
void SetClippingPath(GraphicsPath path)
Intersect the current clipping path with the specified GraphicsPath.
Definition: Graphics.cs:301
VectSharp.IGraphicsContext.LineJoin
LineJoins LineJoin
Current line join used to stroke paths.
Definition: Graphics.cs:215
VectSharp.Filters
Definition: BoxBlurFilter.cs:22
VectSharp.IGraphicsContext.Tag
string Tag
The current tag. How this can be used depends on each implementation.
Definition: Graphics.cs:226
VectSharp.Size
Represents the size of an object.
Definition: Point.cs:146
VectSharp.IGraphicsContext.Fill
void Fill()
Fill the current path using the current FillStyle.
VectSharp.LineDash
Represents instructions on how to paint a dashed line.
Definition: Enums.cs:113
VectSharp.Rectangle.Location
Point Location
The top-left corner of the rectangle.
Definition: Point.cs:182
VectSharp.Size.Width
double Width
Width of the object.
Definition: Point.cs:150
VectSharp.IGraphicsContext.DrawRasterImage
void DrawRasterImage(int sourceX, int sourceY, int sourceWidth, int sourceHeight, double destinationX, double destinationY, double destinationWidth, double destinationHeight, RasterImage image)
Draw a raster image.
VectSharp.IGraphicsContext.SetFillStyle
void SetFillStyle(Brush style)
Set the current FillStyle.
VectSharp.Graphics.DrawRasterImage
void DrawRasterImage(double x, double y, RasterImage image, string tag=null)
Draw a raster image.
Definition: Graphics.cs:479
VectSharp.GraphicsPath.Close
GraphicsPath Close()
Trace a segment from the current point to the start point of the figure and flag the figure as closed...
Definition: GraphicsPath.cs:295
VectSharp.Graphics.Restore
void Restore()
Restore the previous transform state (rotation, translation scale).
Definition: Graphics.cs:532
VectSharp.IGraphicsContext.Height
double Height
Height of the graphic surface.
Definition: Graphics.cs:45
VectSharp.IGraphicsContext.Close
void Close()
Close the current figure.
VectSharp.LineDash.SolidLine
static LineDash SolidLine
A solid (not dashed) line
Definition: Enums.cs:117
VectSharp.IGraphicsContext
This interface should be implemented by classes intended to provide graphics output capability to a G...
Definition: Graphics.cs:36
VectSharp.Graphics.Transform
void Transform(double a, double b, double c, double d, double e, double f)
Transform the coordinate system with the specified transformation matrix [ [a, c, e],...
Definition: Graphics.cs:359
VectSharp.Point
Represents a point relative to an origin in the top-left corner.
Definition: Point.cs:28
VectSharp.Graphics.GetBounds
Rectangle GetBounds()
Computes the rectangular bounds of the region affected by the drawing operations performed on the Gra...
Definition: Graphics.cs:1313
VectSharp.IGraphicsContext.StrokeText
void StrokeText(string text, double x, double y)
Stroke the outline of a text string using the current Font and TextBaseline.
VectSharp.IGraphicsContext.FillText
void FillText(string text, double x, double y)
Fill a text string using the current Font and TextBaseline.
VectSharp.Graphics.Transform
Graphics Transform(Func< Point, Point > transformationFunction, double linearisationResolution, double maxSegmentLength)
Creates a new Graphics object in which all the graphics actions have been transformed using an arbitr...
Definition: Graphics.cs:1128
VectSharp.Graphics.Translate
void Translate(Point delta)
Translate the coordinate system origin.
Definition: Graphics.cs:379
VectSharp.Segment.Point
virtual Point Point
The end point of the Segment.
Definition: Segment.cs:45
VectSharp.Graphics.StrokeRectangle
void StrokeRectangle(Point topLeft, Size size, Brush strokeColour, double lineWidth=1, LineCaps lineCap=LineCaps.Butt, LineJoins lineJoin=LineJoins.Miter, LineDash? lineDash=null, string tag=null)
Stroke a rectangle.
Definition: Graphics.cs:431
VectSharp.Graphics.DrawRasterImage
void DrawRasterImage(double x, double y, double width, double height, RasterImage image, string tag=null)
Draw a raster image.
Definition: Graphics.cs:504
VectSharp.IGraphicsContext.Translate
void Translate(double x, double y)
Translate the coordinate system origin.
VectSharp.Colour.FromRgb
static Colour FromRgb(double r, double g, double b)
Create a new colour from RGB (red, green and blue) values.
Definition: Colour.cs:62
VectSharp.IGraphicsContext.SetClippingPath
void SetClippingPath()
Set the current clipping path as the intersection of the previous clipping path and the current path.
VectSharp.IGraphicsContext.Transform
void Transform(double a, double b, double c, double d, double e, double f)
Transform the coordinate system with the specified transformation matrix [ [a, c, e],...
VectSharp.Rectangle.Union
static Rectangle Union(Rectangle rectangle1, Rectangle rectangle2)
Computes the rectangular bounds of the union of two Rectangles.
Definition: Point.cs:230
VectSharp.Point.Bounds
static Rectangle Bounds(IEnumerable< Point > points)
Computes the smallest Rectangle that contains all the specified points.
Definition: Point.cs:107
VectSharp.Point.Y
double Y
Vertical (y) coordinate, measured to the bottom of the origin.
Definition: Point.cs:37
VectSharp.IGraphicsContext.LineCap
LineCaps LineCap
Current line cap used to stroke paths.
Definition: Graphics.cs:210
VectSharp.IGraphicsContext.Width
double Width
Width of the graphic surface.
Definition: Graphics.cs:40
VectSharp.GraphicsPath.Segments
List< Segment > Segments
The segments that make up the path.
Definition: GraphicsPath.cs:33
VectSharp.Graphics.FillRectangle
void FillRectangle(double leftX, double topY, double width, double height, Brush fillColour, string tag=null)
Fill a rectangle.
Definition: Graphics.cs:415
VectSharp.Graphics.DrawGraphics
void DrawGraphics(double originX, double originY, Graphics graphics)
Draws a Graphics object on the current Graphics object.
Definition: Graphics.cs:820