Search code examples
c#.netgdi+gdigraphicspath

Envelope Distortion with .Net Graphicspath


I'm trying to figure out the best way to accomplish warping a GraphicsPath in .net to a specific shape. The result I'm trying to achieve is warping text to curve up, down, fan left, right, and things like a wave. All of these can be achieved in Adobe Illustrator using the envelope distort feature.

I have considered rolling my own method of doing this using some of predefined grid and point mapping system to transform points. However, it seems like there would be some algorithms or samples as to how to do this already available?


Solution

  • Here's a couple of methods from a captcha generator we use that might steer you in the right direction:

        internal static void DrawPhrase(
            this Graphics graphics, 
            int width, 
            int height, 
            string phrase)
        {
            graphics.FillRectangle(
                Brushes.White,
                0,
                0,
                width,
                height);
    
            using (var gp = new GraphicsPath())
            {
                gp.AddString(phrase,
                             FontFamily.GenericMonospace,
                             (int)FontStyle.Bold,
                             33f,
                             new Point(0,
                                       0),
                             StringFormat.GenericTypographic);
    
                using (var gpp = gp.Deform(width, height))
                {
                    var bounds = gpp.GetBounds();
                    var matrix = new Matrix();
                    var x = (width - bounds.Width) / 2 - bounds.Left;
                    var y = (height - bounds.Height) / 2 - bounds.Top;
                    matrix.Translate(x,
                                     y);
                    gpp.Transform(matrix);
                    graphics.FillPath(Brushes.Black,
                                      gpp);
                }
            }
    
            graphics.Flush();
        }
        internal static GraphicsPath Deform(
            this GraphicsPath path, 
            int width, 
            int height)
        {
            var WarpFactor = 4;
            var xAmp = WarpFactor * width / 300d;
            var yAmp = WarpFactor * height / 50d;
            var xFreq = 2d * Math.PI / width;
            var yFreq = 2d * Math.PI / height;
            var deformed = new PointF[path.PathPoints.Length];
            var xSeed = rng.NextDouble() * 2 * Math.PI;
            var ySeed = rng.NextDouble() * 2 * Math.PI;
            var i = 0;
            foreach (var original in path.PathPoints)
            {
                var val = xFreq * original.X + yFreq * original.Y;
                var xOffset = (int)(xAmp * Math.Sin(val + xSeed));
                var yOffset = (int)(yAmp * Math.Sin(val + ySeed));
                deformed[i++] = new PointF(original.X + xOffset,
                                         original.Y + yOffset);
            }
    
            return new GraphicsPath(deformed,
                                    path.PathTypes);
        }