Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#
Technical Blog

GraphicsPath Outline in C# (Alternative to GdipWindingModeOutline)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
19 Jul 2013CPOL 12.9K   5  
GraphicsPath outline in C# (alternative to GdipWindingModeOutline)

Introduction

I was recently working on an interesting exercise when I had to manipulate complex polygons. On the input, I had a collection of text blocks in different fonts and sizes manually positioned over a rectangular region. I had to produce an outline of all the chars, something that looks like this:

The first step was to convert text to paths, then try to get outline of the path. I initially used GdipWindingModeOutline function, but for some configurations (about 50% of the time actually), it failed with "GenericError". I was not able to detect so I had to come up with a custom solution.

There is a very nice open source polygon clipping library for C#. I came up with the following function (simplified):

C#
private GraphicsPath UnionAll(GraphicsPath gp)
{
    var m1 = new Matrix();
    m1.Scale(100, 100);

    gp.Flatten(m1, 0.01f);
    var bounds = gp.GetBounds();

    // add background rectangle

    var c = new Clipper();
    var rect = new List<IntPoint>
                    {
                        new IntPoint((int)bounds.Left - 10, (int)bounds.Top - 10),
                        new IntPoint((int)bounds.Right + 10, (int)bounds.Top - 10),
                        new IntPoint((int)bounds.Right + 10, (int)bounds.Bottom + 10),
                        new IntPoint((int)bounds.Left - 10, (int)bounds.Bottom + 10)
                    };

    c.AddPolygon(rect, PolyType.ptSubject);

    // add all polygons

    var iter = new GraphicsPathIterator(gp);

    while (true)
    {
        var subPath = new GraphicsPath();
        bool isClosed;

        if (iter.NextSubpath(subPath, out isClosed) == 0) break;

        var poly = subPath.PathPoints.Select
        (x => new IntPoint((int) x.X, (int) x.Y)).ToList();

        c.AddPolygon(poly, PolyType.ptClip);
    }

    // execute clipping

    var solution = new List<ExPolygon>();
    var result = c.Execute(ClipType.ctDifference, 
    solution, PolyFillType.pftPositive, PolyFillType.pftPositive);
    if (!result) throw new Exception("Clipper.Execute failed");

    // convert back to GraphicsPath

    var retval = new GraphicsPath();

    foreach (var s in solution)
    {
        retval.AddPolygon(s.outer.Select(x => new PointF(x.X, x.Y)).ToArray());

        foreach (var h in s.holes)
        {
            retval.AddPolygon(h.Select(x => new PointF(x.X, x.Y)).ToArray());
        }
    }

    var m2 = new Matrix();
    m2.Scale(0.01f, 0.01f);

    retval.Transform(m2);
    return retval;
}

As you can see, it:

  • scales path by 100 (to fix integer rounding)
  • adds base rectangle polygon
  • converts path into collection of polygons
  • executes the difference
  • converts polygons back into path
  • scales path back to original size

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --