---
title: "Working with Paths"
canonical: https://www.graphicsmill.com/docs/gm/working-with-path.htm
product: "Graphics Mill"
---

# Working with Paths
 
A path is a set of geometric elements (curves, text, and graphics shapes) that can be used as a single object. There are two types of paths: closed, where the starting and ending points are at the same location, and open, where these points have different coordinates.
 
Graphics Mill allows using paths for the following major operations: to draw/fill a path on the [Graphics](https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Drawing_Graphics.htm.md), to draw a text string along a path, and to use a path as the clipping path for cutting out the image outside of it (to learn more about clipping path read [Clipping Path](https://www.graphicsmill.com/docs/gm/graphics.htm.md) paragraph). The latter requires a closed path. If you are not familiar with such aspects as **Graphics** or clipping path, read the [Graphics. Drawing Images and Geometric Shapes](https://www.graphicsmill.com/docs/gm/graphics.htm.md) article; to learn how to draw the text using the drawing engine, read the [Working with the Text Class](https://www.graphicsmill.com/docs/gm/working-with-text-class.htm.md) article.
 
There are two ways to create a path: copy an existing path or draw it from scratch.
 
## Copying Existing Paths
  
If you want to create a path from an existing one, you can use the static [Path.Create(GraphicsPath)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_Create_1_2bc0c98a.htm.md) method that allows initializing a path from [Drawing2D.GraphicsPath](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.graphicspath?view=netframework-4.8) or [ClippingPath.Path](https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Codecs_ClippingPath_Path.htm.md). The snippet below demonstrates how to extract a path from **ClippingPath.Path** stored in [Adobe Image Resource Blocks](https://www.graphicsmill.com/docs/gm/adobe-image-resource-blocks.htm.md) (to learn more about clipping path read the [Clipping Path](https://www.graphicsmill.com/docs/gm/clipping-path.htm.md) article). Notice that **ClippingPath.Path** is represented in relative coordinates, therefore you need to specify the height and width of the original:
 
```csharp
using (var reader = new JpegReader(@"Images\1.jpg"))
{
    var clippingPath = reader.ClippingPaths[0];
    var path = Path.Create(clippingPath, reader.Width, reader.Height);
}
```
 
## Creating New Paths
  
Creating a path means creating a number of path segments that can be defined as lines, curves, or shapes. Let us discuss how to work with each segment in more detail:
 
### Line Segment
 
A line segment is a line defined by two points. The first point is the last point that was added to the path, whereas the second point is the one passed to the [LineTo(PointF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_LineTo_1_ea862d5e.htm.md) method. Thus, if the path is empty, it does not contain any segment, and you need to use the [MoveTo(PointF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_MoveTo_1_ea862d5e.htm.md) method to add a starting point to the path. Also do not forget to call **MoveTo(PointF)** in case the last path segment was a shape (rectangle, ellipse, etc.). Using the **LineTo(PointF)** method you can add line segments one by one. The following snippet illustrates how to draw a line segment:
 
```csharp
var path = new Path();
path.MoveTo(10, 10);
path.LineTo(70, 60);
```
 
This snippet creates the line illustrated on the following graph:
 ![Line Curve.](https://www.graphicsmill.com/docs/gm/WorkingWithPath1.png) 
### Curve Segment
 
The API allows you to add either a quadratic or a cubic Bezier segment to a path. Both of the curves are created using the **CurveTo** Method. Before adding a curve to an empty path you need to add the starting point to the path as we did it for line segments.
 
***Quadratic Bezier***
 
The quadratic Bezier segment is a curve created via the [CurveTo(PointF, PointF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_CurveTo_2_e3d75561.htm.md) method. This method adds a curve starting at point P**0** and arriving to P**2** coming from the direction of P**1**. P**0** is the last point added to the path, while P**1** and P**2** are passed to **CurveTo**. The following snippet demonstrates how to create the quadratic Bezier segment:
 
```csharp
var path = new Path();
path.MoveTo(10, 60);
path.CurveTo(new System.Drawing.PointF(40, 20), new System.Drawing.PointF(70, 60));
```
 
This snippet creates the line illustrated on the following graph:
 ![Quadratic Bezier Curve.](https://www.graphicsmill.com/docs/gm/WorkingWithPath2.png) 
***Cubic Bezier***
 
The cubic Bezier segment is a curve created via the [CurveTo(PointF, PointF, PointF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_CurveTo_3_6669a9fe.htm.md) method. This method adds a curve starting at the point P**0**, arriving at P**3** coming from the direction of P**1** and P**2**. P**0** is the last point added to the path and P**1**, P**2**, and P**3** are passed to **CurveTo** as arguments. The following snippet demonstrates how to create the cubic Bezier segment:
 
```csharp
var path = new Path();
path.MoveTo(10, 60);
path.CurveTo(new System.Drawing.PointF(40, 20), new System.Drawing.PointF(70, 60), new System.Drawing.PointF(100, 20));
```
 
This snippet creates the line illustrated on the following graph:
 ![Cubic Bezier Curve.](https://www.graphicsmill.com/docs/gm/WorkingWithPath3.png) 
### Figure Segment
 
A figure is a closed path segment delineating an ellipse or rectangle. To create ellipse and rectangle segments use the [DrawEllipse(RectangleF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_DrawEllipse_1_a73b99d3.htm.md) and [DrawRectangle(RectangleF)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_DrawRectangle_1_a73b99d3.htm.md) methods, respectively. Unlike the Line and Curve segments, figure segments do not require adding a starting point to empty paths before creating a segment. Also, figure segments may have no links with other segments in the path. The following snippet demonstrates how to create the figure segments:
 
```csharp
using (var bitmap = new Bitmap(160, 120, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetGraphics())
using (var path = new Path())
{
    path.DrawEllipse(new System.Drawing.Rectangle(10, 10, 70, 70));
    path.DrawRectangle(new System.Drawing.RectangleF(75, 75, 75, 35));
    graphics.DrawPath(new Pen(RgbColor.Green), path);
    bitmap.Save(@"Images\Output\out.png");
}
```
 
This snippet utilizes the [Graphics.DrawPath(Pen, Path)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Graphics_DrawPath_2_77634c23.htm.md) method for drawing a path on a **Graphics** object. After launching this snippet you will see the following result:
 ![Figure Segment.](https://www.graphicsmill.com/docs/gm/WorkingWithPath4.png) 
You can also add a text outline to a path (for more information about text objects, see the [Working with the Text Class](https://www.graphicsmill.com/docs/gm/working-with-text-class.htm.md) article). The following snippet demonstrates how to create a path from the "Example" text:
 
```csharp
using (var bitmap = new Bitmap(160, 120, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetGraphics())
using (var path = new Path())
{
    path.DrawText(new PlainText("Example", FontRegistry.Installed.CreateFont("Arial", 26, 96, 96), 15, 75), FontRegistry.Installed, bitmap.DpiX, bitmap.DpiY);
    graphics.DrawPath(new Pen(RgbColor.Green), path);
    bitmap.Save(@"Images\Output\out.png");
}
```
 
As the result you will see the following path:
 ![Path Copied from Text.](https://www.graphicsmill.com/docs/gm/WorkingWithPath5.png) 
In addition, you can create a path segment from an existing [Drawing.Path](https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Drawing_Path.htm.md) using the [Path.DrawPath(Path)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_DrawPath_1_6209a3bf.htm.md) method:
 
```csharp
var path = new Path();
path.DrawPath(existedPath);
```
 
## Operations on Path
  
There are a number of operations that can be performed on paths: you can transform, close, convert to GraphicsPath, or serialize paths. You can also get a path from an image and change its outline.
 
### Transforming Path
 
The **Path** class provides the [ApplyTransform(Matrix)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_ApplyTransform_1_5bff9483.htm.md) method that changes a path according to a specified [Matrix](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.matrix?view=netframework-4.8) instance. **Matrix** is a transformation matrix representing a geometric transform such as rotating, scaling, translating, etc. The following snippet illustrates how to downsize the path twice as much as it originally was and then rotate it:
 
```csharp
using (var reader = new JpegReader(@"Images\1.jpg"))
using (var path = Path.Create(reader.ClippingPaths[0], reader.Width, reader.Height))
{
    var m = new System.Drawing.Drawing2D.Matrix();
    m.Scale(0.5f, 0.5f);
    m.Rotate(30);
    path.ApplyTransform(m);
}
```
 
### Closing Path
 
Earlier we discussed that a path can be open or closed. You can use the [Path.Close()](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_Close.htm.md) method to close a path. This method copies the starting point to the ending one. The following snippet demonstrates how to close the path:
 
```csharp
var path = new Path();
path.MoveTo(20, 20);
path.LineTo(140, 120);
path.LineTo(40, 100);
path.Close();
```
 
On the following picture you can see the difference between the opened and closed paths:
 ![Closing Path.](https://www.graphicsmill.com/docs/gm/WorkingWithPath6.png) 
### Converting to GraphicsPath
 
There can be times when you need to draw a **Path** instance on standard [System.Drawing.Graphics](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.graphics?view=netframework-4.8). You can easily do that using the [Path.ToGdiPlusGraphicsPath()](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_ToGdiPlusGraphicsPath.htm.md) method:
 
```csharp
using (var bitmap = new Bitmap(160, 160, PixelFormat.Format24bppRgb, RgbColor.White))
using (var path = new Path())
{
    path.MoveTo(20, 20);
    path.LineTo(140, 120);
    using (var graphics = bitmap.GetGraphics())
    {
        graphics.DrawPath(new Pen(RgbColor.Black), path);
    }
}
```
 
### Serializing Path
 
The **Path** class is marked with the **Serializable** attribute. This means that you can save an instance of the **Path** class to a file and then restore it for further editing. The following snippet demonstrates how to serialize the path:
 
```csharp
using (var path = new Path())
{
    path.MoveTo(10, 60);
    path.CurveTo(new System.Drawing.PointF(40, 20), new System.Drawing.PointF(70, 60));

    IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    System.IO.Stream stream = new System.IO.FileStream("MyFile.path",
        System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None);
    formatter.Serialize(stream, path);
    stream.Close();
}
```
 
To restore the path back to its former state use the following code:
 
```csharp
IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.Stream stream = new System.IO.FileStream("MyFile.path",
    System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
var path = (Path)formatter.Deserialize(stream);
stream.Close();
```
 
## Processing Outlines
  
You can get a path from the pixels of an image and then change its outline. For example, you can make a cut line for a sticker around a logo. The methods described below work with closed subpaths as outlines and with other subpaths as open lines. All distances and tolerances are in path units.
 
### Getting Contour from Image
 
The [Path.GetContour](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_GetContour.htm.md) method traces the region where a channel of an image reaches a level. Usually, this is the alpha channel. The contour has sub-pixel accuracy, so anti-aliased edges give smooth lines. The [Path.GetAlphaContour](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_GetAlphaContour.htm.md) method follows the pixel edges, so its contour has steps. The two contours are offset by half a pixel from each other, so do not mix them. You can also trace a color channel, for example, to get the outline of a dark logo on a white background. The following snippet traces a small image of a circle. The picture shows both contours scaled four times:
 
```csharp
// Trace the alpha channel at 50% with sub-pixel accuracy.
var contour = Path.GetContour(image, Channel.Alpha, 0.5f);
```
 ![Contour from Image.](https://www.graphicsmill.com/docs/gm/PathGetContour.png) 
### Inflating and Deflating Outline
 
The [Inflate](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_Inflate.htm.md) method grows every closed region by a distance. A negative distance shrinks the regions. Regions that are closer than twice the distance merge. You can choose the corner style: round, miter, or bevel. If you grow a path and then shrink it by the same distance, narrow gaps between its parts are closed.
 
```csharp
var path = new Path();
path.DrawEllipse(15, 15, 60, 60);
path.DrawRectangle(78, 65, 55, 65);

var original = new Path(path);

// Grow the shapes by 8 units. Shapes closer than 16 units merge.
path.Inflate(8f, LineJoin.Round);
```
 ![Inflated Path.](https://www.graphicsmill.com/docs/gm/PathInflate.png) 
### Widening Path
 
The [Widen](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_Widen.htm.md) method replaces the path with the outline of its stroke. It uses the width, line join, line cap, miter limit, and dash pattern of a pen. The stroke of a closed subpath is a ring. The ends of an open subpath get the line cap.
 
```csharp
var path = new Path();
path.MoveTo(20, 120);
path.LineTo(55, 30);
path.LineTo(95, 120);
path.LineTo(130, 30);

var original = new Path(path);

var pen = new Pen(RgbColor.Black, 14f)
{
    LineJoin = LineJoin.Round,
    LineCap = LineCap.Round
};

// Replace the line with the outline of its stroke.
path.Widen(pen);
```
 ![Widened Path.](https://www.graphicsmill.com/docs/gm/PathWiden.png) 
### Removing Holes
 
The [GetOuterContours](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_GetOuterContours.htm.md) method returns a new path with the outer boundaries only. It drops the holes and everything inside the holes. The [GetArea](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_GetArea.htm.md) method returns the area of the region filled by a path, with the holes subtracted.
 
```csharp
// A ring with a small circle inside its hole.
var ring = Path.Subtract(Path.Ellipse(15, 15, 120, 120), Path.Ellipse(40, 40, 70, 70));
var path = Path.Union(ring, Path.Ellipse(60, 60, 30, 30));

// Drop the hole and the circle inside it.
var outer = path.GetOuterContours();
```
 ![Outer Contours of Path.](https://www.graphicsmill.com/docs/gm/PathGetOuterContours.png) 
### Flattening Path
 
The [Flatten(Single)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_Flatten_1_7a94462e.htm.md) method replaces curves with polylines. The tolerance is the largest allowed distance between the polyline and the curve.
 
```csharp
var path = new Path();
path.MoveTo(15, 120);
path.CurveTo(new System.Drawing.PointF(40, -20), new System.Drawing.PointF(110, 170), new System.Drawing.PointF(135, 30));

var original = new Path(path);

// Replace the curve with a polyline that deviates from it by at most 2 units.
path.Flatten(2f);
```
 ![Flattened Path.](https://www.graphicsmill.com/docs/gm/PathFlatten.png) 
### Simplifying Path
 
The [Simplify(Single)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_Simplify_1_7a94462e.htm.md) method removes points that lie close to the line through their neighbors. Use it to reduce the number of points in a traced or hand-drawn path. The ends of open subpaths stay in place.
 
```csharp
// A noisy line, for example, a line drawn by hand.
var path = new Path();
for (int i = 0; i <= 40; i++)
{
    float x = 15 + i * 3;
    float y = 75 - 45 * (float)System.Math.Sin(i * System.Math.PI / 20) + ((i * 7) % 5 - 2);

    if (i == 0)
        path.MoveTo(x, y);
    else
        path.LineTo(x, y);
}

var original = new Path(path);

// Remove the points that are closer than 3 units to the line through their neighbors.
path.Simplify(3f);
```
 ![Simplified Path.](https://www.graphicsmill.com/docs/gm/PathSimplify.png) 
### Smoothing Path
 
The [Smooth(Single)](https://www.graphicsmill.com/docs/gm/M_Aurigma_GraphicsMill_Drawing_Path_Smooth_1_7a94462e.htm.md) method smooths every subpath along its length and rounds off the corners. A larger value gives a smoother result. A convex outline shrinks a little, and the ends of open subpaths stay in place.
 
```csharp
// A five-pointed star.
var path = new Path();
for (int i = 0; i < 10; i++)
{
    double angle = i * System.Math.PI / 5 - System.Math.PI / 2;
    float radius = i % 2 == 0 ? 65 : 28;
    float x = 75 + radius * (float)System.Math.Cos(angle);
    float y = 80 + radius * (float)System.Math.Sin(angle);

    if (i == 0)
        path.MoveTo(x, y);
    else
        path.LineTo(x, y);
}
path.Close();

var original = new Path(path);

// Round off the corners.
path.Smooth(6f);
```
 ![Smoothed Path.](https://www.graphicsmill.com/docs/gm/PathSmooth.png) 
### Fitting Curves
 
The [FitCurves](https://www.graphicsmill.com/docs/gm/O_T_Aurigma_GraphicsMill_Drawing_Path_FitCurves.htm.md) method replaces polylines with Bezier curves that stay within a tolerance of their points. A point where the direction turns by 60 degrees or more stays a sharp corner. You can change this angle. The curves pass within the tolerance of every point, so steps larger than the tolerance stay. For example, the steps of an outline from **GetAlphaContour** are not smoothed away. To get a smooth outline of a bitmap, trace it with **GetContour**.
 
```csharp
// A polyline: a straight edge and an arc made of short segments.
var path = new Path();
path.MoveTo(35, 15);
path.LineTo(35, 135);
for (int i = 1; i < 12; i++)
{
    double angle = System.Math.PI / 2 - i * System.Math.PI / 12;
    path.LineTo(35 + 85 * (float)System.Math.Cos(angle), 75 + 60 * (float)System.Math.Sin(angle));
}
path.Close();

var original = new Path(path);

// Replace the polyline with Bezier curves. The two corners of the straight edge stay sharp.
path.FitCurves(1f);
```
 ![Fitted Curves.](https://www.graphicsmill.com/docs/gm/PathFitCurves.png) 
### Example: Making Sticker
 
The following snippet makes a sticker from a logo with a transparent background. It traces the alpha channel, grows the outline, removes the holes, and smooths the result. Then it draws a white sticker, the logo, and the cut line:
 
```csharp
const float margin = 25f;

using (var logo = new Bitmap(@"Images\GraphicsMillLogo.png"))
using (var contour = Path.GetContour(logo, Channel.Alpha, 0.5f))
{
    // Grow the outline. Parts of the logo that are close to each other merge into one shape.
    contour.Inflate(15f);

    // Remove the holes, then make the outline smooth.
    using (var cutLine = contour.GetOuterContours())
    {
        cutLine.Smooth(4f);
        cutLine.FitCurves(0.5f);
        cutLine.Translate(margin, margin);

        using (var sticker = new Bitmap(logo.Width + (int)(2 * margin), logo.Height + (int)(2 * margin),
            PixelFormat.Format32bppArgb, RgbColor.Transparent))
        {
            // Use the resolution of the logo, so that the logo is drawn without scaling.
            sticker.DpiX = logo.DpiX;
            sticker.DpiY = logo.DpiY;

            using (var graphics = sticker.GetGraphics())
            {
                graphics.FillPath(new SolidBrush(RgbColor.White), cutLine);
                graphics.DrawImage(logo, margin, margin);
                graphics.DrawPath(new Pen(RgbColor.Magenta, 2f), cutLine);

                sticker.Save(@"Images\Output\LogoSticker.png");
            }
        }
    }
}
```
 
The picture shows the logo and the resulting sticker on a gray background:
 ![Sticker Made from Logo.](https://www.graphicsmill.com/docs/gm/PathSticker.png)
 
## See Also
  
### Reference
 
- [Aurigma.GraphicsMill.Drawing Namespace](https://www.graphicsmill.com/docs/gm/N_Aurigma_GraphicsMill_Drawing.htm.md)
- [Graphics Class](https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Drawing_Graphics.htm.md)
- [Path Class](https://www.graphicsmill.com/docs/gm/T_Aurigma_GraphicsMill_Drawing_Path.htm.md)

### Manual
 
- [Graphics. Drawing Images and Geometric Shapes](https://www.graphicsmill.com/docs/gm/graphics.htm.md)
- [Working with the Text Class](https://www.graphicsmill.com/docs/gm/working-with-text-class.htm.md)
- [Drawing Simple Text Objects](https://www.graphicsmill.com/docs/gm/advanced-drawing-text.htm.md)

### Other
 
- [Matrix Class](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.matrix?view=netframework-4.8)
