|
16 | 16 | using System.Drawing.Text; |
17 | 17 | using System.Globalization; |
18 | 18 | using System.IO; |
| 19 | +using System.Collections.Generic; |
| 20 | +using System.Linq; |
19 | 21 |
|
20 | 22 | namespace SvgNet.SvgGdi |
21 | 23 | { |
@@ -1965,9 +1967,95 @@ public void DrawLines(Pen pen, Point[] points) |
1965 | 1967 | } |
1966 | 1968 |
|
1967 | 1969 | /// <summary> |
1968 | | - /// Not implemented because GDI+ regions/paths are not emulated. |
| 1970 | + /// Implemented |
1969 | 1971 | /// </summary> |
1970 | | - public void DrawPath(Pen pen, GraphicsPath path) { throw new SvgGdiNotImpl("DrawPath (Pen pen, GraphicsPath path)"); } |
| 1972 | + /// <remarks> |
| 1973 | + /// Mainly based on the libgdi+ implementation: https://github.com/mono/libgdiplus/blob/master/src/graphics-cairo.c |
| 1974 | + /// and this SO question reply: https://stackoverflow.com/questions/1790862/how-to-determine-endpoints-of-arcs-in-graphicspath-pathpoints-and-pathtypes-arra |
| 1975 | + /// from SiiliconMind. |
| 1976 | + /// </remarks> |
| 1977 | + public void DrawPath(Pen pen, GraphicsPath path) |
| 1978 | + { |
| 1979 | + //Save the original pen dash style in case we need to change it |
| 1980 | + DashStyle originalPenDashStyle = pen.DashStyle; |
| 1981 | + |
| 1982 | + GraphicsPathIterator subpaths = new GraphicsPathIterator(path); |
| 1983 | + GraphicsPath subpath = new GraphicsPath(path.FillMode); |
| 1984 | + subpaths.Rewind(); |
| 1985 | + |
| 1986 | + //Iterate through all the subpaths in the path. Each subpath will contain either |
| 1987 | + //lines or Bezier curves |
| 1988 | + for (int s = 0; s < subpaths.SubpathCount; s++) |
| 1989 | + { |
| 1990 | + bool isClosed; |
| 1991 | + if (subpaths.NextSubpath(subpath, out isClosed) == 0) |
| 1992 | + { |
| 1993 | + continue; //go to next subpath if this one has zero points. |
| 1994 | + } |
| 1995 | + PointF start = new PointF(0, 0); |
| 1996 | + PointF origin = subpath.PathPoints[0]; |
| 1997 | + PointF last = subpath.PathPoints[subpath.PathPoints.Length - 1]; |
| 1998 | + int bezierCurvePointsIndex = 0; |
| 1999 | + PointF[] bezierCurvePoints = new PointF[4]; |
| 2000 | + for (int i = 0; i < subpath.PathPoints.Length; i++) |
| 2001 | + { |
| 2002 | + /* Each subpath point has a corresponding path point type which can be: |
| 2003 | + *The point starts the subpath |
| 2004 | + *The point is a line point |
| 2005 | + *The point is Bezier curve point |
| 2006 | + * Another point type like dash-mode |
| 2007 | + */ |
| 2008 | + switch ((PathPointType)subpath.PathTypes[i] & PathPointType.PathTypeMask) //Mask off non path-type types |
| 2009 | + { |
| 2010 | + case PathPointType.Start: |
| 2011 | + start = subpath.PathPoints[i]; |
| 2012 | + bezierCurvePoints[0] = subpath.PathPoints[i]; |
| 2013 | + bezierCurvePointsIndex = 1; |
| 2014 | + continue; |
| 2015 | + case PathPointType.Line: |
| 2016 | + DrawLine(pen, start, subpath.PathPoints[i]); //Draw a line segment ftom start point |
| 2017 | + start = subpath.PathPoints[i]; //Move start point here |
| 2018 | + bezierCurvePoints[0] = subpath.PathPoints[i]; //A line point can also be the start of a Bezier curve |
| 2019 | + bezierCurvePointsIndex = 1; |
| 2020 | + continue; |
| 2021 | + case PathPointType.Bezier3: |
| 2022 | + bezierCurvePoints[bezierCurvePointsIndex++] = subpath.PathPoints[i]; |
| 2023 | + if (bezierCurvePointsIndex == 4) //If 4 points including start have been found then draw the Bezier curve |
| 2024 | + { |
| 2025 | + DrawBezier(pen, bezierCurvePoints[0], bezierCurvePoints[1], bezierCurvePoints[2], bezierCurvePoints[3]); |
| 2026 | + bezierCurvePoints = new PointF[4]; |
| 2027 | + bezierCurvePoints[0] = subpath.PathPoints[i]; |
| 2028 | + bezierCurvePointsIndex = 1; |
| 2029 | + } |
| 2030 | + continue; |
| 2031 | + default: |
| 2032 | + |
| 2033 | + switch ((PathPointType)subpath.PathTypes[i]) |
| 2034 | + { |
| 2035 | + case PathPointType.DashMode: |
| 2036 | + pen.DashStyle = DashStyle.Dash; |
| 2037 | + continue; |
| 2038 | + default: |
| 2039 | + throw new SvgException("Unknown path type value: " + subpath.PathTypes[i]); |
| 2040 | + } |
| 2041 | + } |
| 2042 | + } |
| 2043 | + if (isClosed) //If the subpath is closed and it is a linear figure then draw the last connecting line segment |
| 2044 | + { |
| 2045 | + PathPointType originType = (PathPointType)subpath.PathTypes[0]; |
| 2046 | + PathPointType lastType = (PathPointType) subpath.PathTypes[subpath.PathPoints.Length - 1]; |
| 2047 | + |
| 2048 | + if (((lastType & PathPointType.PathTypeMask) == PathPointType.Line) && ((originType & PathPointType.PathTypeMask) == PathPointType.Line)) |
| 2049 | + { |
| 2050 | + DrawLine(pen, last, origin); |
| 2051 | + } |
| 2052 | + } |
| 2053 | + |
| 2054 | + } |
| 2055 | + subpath.Dispose(); |
| 2056 | + subpaths.Dispose(); |
| 2057 | + pen.DashStyle = originalPenDashStyle; |
| 2058 | + } |
1971 | 2059 |
|
1972 | 2060 | /// <summary> |
1973 | 2061 | /// Implemented. <c>DrawPie</c> functions work correctly and thus produce different output from GDI+ if the ellipse is not circular. |
@@ -2249,9 +2337,38 @@ public void FillEllipse(Brush brush, Int32 x, Int32 y, Int32 width, Int32 height |
2249 | 2337 | } |
2250 | 2338 |
|
2251 | 2339 | /// <summary> |
2252 | | - /// Not implemented, because GDI+ regions/paths are not emulated. |
| 2340 | + /// Implemented |
2253 | 2341 | /// </summary> |
2254 | | - public void FillPath(Brush brush, GraphicsPath path) { throw new SvgGdiNotImpl("FillPath (Brush brush, GraphicsPath path)"); } |
| 2342 | + public void FillPath(Brush brush, GraphicsPath path) |
| 2343 | + { |
| 2344 | + GraphicsPathIterator subpaths = new GraphicsPathIterator(path); |
| 2345 | + GraphicsPath subpath = new GraphicsPath(path.FillMode); |
| 2346 | + subpaths.Rewind(); |
| 2347 | + for (int s = 0; s < subpaths.SubpathCount; s++) |
| 2348 | + { |
| 2349 | + bool isClosed; |
| 2350 | + if (subpaths.NextSubpath(subpath, out isClosed) < 2) |
| 2351 | + { |
| 2352 | + continue; |
| 2353 | + } |
| 2354 | + if (!isClosed) |
| 2355 | + { |
| 2356 | + subpath.CloseAllFigures(); |
| 2357 | + } |
| 2358 | + PathPointType lastType = (PathPointType)subpath.PathTypes[subpath.PathPoints.Length - 1]; |
| 2359 | + if (subpath.PathTypes.Any(pt => ((PathPointType) pt & PathPointType.PathTypeMask) == PathPointType.Line)) |
| 2360 | + { |
| 2361 | + FillPolygon(brush, subpath.PathPoints, path.FillMode); |
| 2362 | + } |
| 2363 | + else |
| 2364 | + { |
| 2365 | + FillBeziers(brush, subpath.PathPoints, path.FillMode); |
| 2366 | + } |
| 2367 | + |
| 2368 | + } |
| 2369 | + subpath.Dispose(); |
| 2370 | + subpaths.Dispose(); |
| 2371 | + } |
2255 | 2372 |
|
2256 | 2373 | /// <summary> |
2257 | 2374 | /// Implemented <c>FillPie</c> functions work correctly and thus produce different output from GDI+ if the ellipse is not circular. |
|
0 commit comments