Stroking is a common task of every graphics library. When I started writing Fog-Framework, I used algorithms from antigrain to work with vector geometry. However, the antigrain can only stroke polygons, so quadratic and cubic Bézier curves must be first approximated into the line segments. When a graphics path is stroked, the information about curves is definitely lost, and very acute angles are not stroked properly unless very large value is used for property called approximation-scale (it's called flatness in other graphics libraries).
This inaccuracy can lead into problems. For example to stroke a path twice the curve must be approximated into many line segments. More line segments means more vertices, which means more cpu power to transform them, for example.
The purpose of this article is to demonstrate the work to replace the old polygon stroker with a new curve-to-curve stroker. The actual limitation is that current version (2011-08-08) can stroke quadratic Bézier curves only.
Background and Implementation Notes
The new curve-to-curve stroker is highly inspired by the work of Mike "Pomax" Kamermans, see his excellent resource A primer on Bézier curves, section How to scale curves. Please try the interactive example provided in the linked article. The quadratic Bézier curve scaling is definitely the way to go. This method is precise for "safe" curves, which are defined as curves where the angle between the two lines which define the curve is not acute. I found that this condition is not the best one and I adjusted the angle to 100 degrees.
If the "safe" curve criteria is not met, then the curve must be subdivided into smaller ones. There are several ways how the subdivision criteria are created. The author of Bézier primer used subdivision based on curve extremas (curve is first rotated so extrema is always found, if curve is not degenerate), but I noticed that the offset of commonly used curves is not precise, so I adjusted the mechanism how the curve is subdivided and I added some conditions for subdivision. This means that Fog-Framework generates more curves, but I found it more precise (for me it's perfect).
The results of new curve-to-curve stroker (left) vs the antigrain stroker (right). The number of resulting curve segments generated by new curve-to-curve stroker depends on the curvature of the original curve, the number of line segments generated by antigrain stroker depends on the curve length and the flatness which was set to 0.25 (the default value).
As can be seen, the degenerate cases are handled more precisely by curve-to-curve stroker, because the corners are approximated using curves, instead of lines. I think that the curve-to-curve stroker generates a lot of curves in this case, but the resulting shape looks better than shape approximated by using line segments only.
This is only the beginning of my stroker implementation. I'd like to post another article related to the stroking of cubic Bézier curves after finished. The final implementation will be part of Fog-Framework, so everyone can use it without restrictions.