[RESOLVED] GDI warp path question
Hi,
I am rendering text by using the Paint event of the form and creating a path and filling it (i.e. in the normal way). I need to create this text effect:
http://i72.photobucket.com/albums/i1...y/textbend.png
Does anyone know how to warp the path to make it look like this? I've used warp + matrices in the past to create perspective style effects. I don't know how to do curvy warps though.
thanks
Paul
Re: GDI warp path question
I can't come up with a ready solution, but I'm interested in this topic and have some ideas about how it might be done. What I have in mind is to divide the rectangular box containing the original text path into narrow vertical slices and warp each rectangle to fit the upper and lower elliptical curves. I'll see if I can work something out, assuming nobody else replies with something more useful.
I have also noted several references on the web about fitting text to a Bezier curves which you might find interesting, although I think fitting the text to elliptical curves could be easier:
http://www.codeguru.com/cpp/g-m/gdi/...le.php/c10595/(in Csharp)
http://msdn.microsoft.com/en-us/magazine/dd263097.aspx(Petzold, in WPF)
http://www.planetclegg.com/projects/...ToSplines.html(in Java, I think).
BB
Re: GDI warp path question
Great thanks. I was thinking of splitting the path into vertical sections too.
There's a version of Warp (for paths) which takes an array of points. I was thinking of calculating the array using a sine function. Going to investigate exactly how that version of warp works next.
Just can't help thinking there's a much easier way!
Re: GDI warp path question
On thinking about it, it might be better to split up the path letter by letter instead of into regular slices. It depends on the font you are using. It would be easy for a monospaced font, but otherwise you would have to measure the width of each letter (or maybe letter pair, to get the spacing right?). I would think of doing that with GraphicsPath.GetBounds for each letter (or pair), as it gives more accurate results than Graphics.MeasureString.
OK, suppose you get the rectangle bounding each letter (or each slice if you are doing it that way). You need to warp the corners to corresponding points along the elliptical arcs. Unfortunaly GDI+ doesn't help at with getting regularly spaced points along a curve. But it's not too difficult to calculate the position of points on an ellipse or elliptical arc. Basically:
Code:
x = a * Cos(theta)
y = b * Sin(theta)
where a and b are the axis lengths of the ellipse and theta is the angle on the surrounding circle. (At least, it's a lot easier than finding points along a Bezier curve!) You could map each pixel on the original rectanglar box to (say) 0.5 degrees in the formulas above and get a good perspective result. That's because an ellipse is basically a circle in perspective.
If you choose to divide the text into regular slices, it's very difficult to get a GraphicsPath corresponding to each slice. It would be easiest to convert the whole path to a Region and then slice it up, and transform each slice [EDIT: not sure how]; but the results may not look as good, among other reasons because GDI+ can't anti-alias the edges of a Region. I would look at the possibility of treating each slice as a clipping region, and use that somehow for drawing/filling slices of the GraphicsPath.
BB
1 Attachment(s)
Re: GDI warp path question
Sorry, after all that, "calculating the array using a sine function" IS the right way to go. I tried transforming the PathPoints array of a text path with trig functions and after much tinkering got the following results:
Attachment 82107
It's the result of lots of scribbling diagrams on paper, half-remembered geometry and blind guesswork. So don't take the details too seriously, especially the trig code, as it may not work for text paths of different sizes. But it shows it can be done and it shouldn't take too much code. It's all on this form:
vb.net Code:
Imports System.Drawing.Drawing2D
Public Class OldCurvedTextForm
Private textPath As New GraphicsPath
Private newPath As GraphicsPath
Private textFont As Font = New Font("Georgia", 100, FontStyle.Regular, GraphicsUnit.Pixel)
Private textString As String = "THIS IS MY STRING"
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.SmoothingMode = SmoothingMode.HighQuality
e.Graphics.DrawPath(Pens.Gray, textPath)
e.Graphics.FillPath(Brushes.Yellow, newPath)
e.Graphics.DrawPath(Pens.Black, newPath)
End Sub
Private Sub OldCurvedTextForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
textPath.AddString(textString, textFont.FontFamily, 0, 80, Point.Empty, Nothing)
newPath = CurvePath(textPath)
End Sub
Private Function CurvePath(ByVal inputPath As GraphicsPath) As GraphicsPath
Dim newPath As GraphicsPath
Dim w = inputPath.GetBounds.Width / 2
Dim h = inputPath.GetBounds.Height / 2
Using mtx As New Matrix
mtx.Translate(-w, -h)
inputPath.Transform(mtx)
End Using
Dim newPathPoints(inputPath.PathPoints.Count - 1) As PointF
For i As Integer = 0 To inputPath.PathPoints.Count - 1
Dim pf As PointF = inputPath.PathPoints(i)
Dim newX = pf.X * Math.Cos(pf.X / w / 2)
Dim newY = 2 * pf.Y / Math.Cos(pf.X / w)
newPathPoints(i) = New PointF(newX, newY)
Next
newPath = New GraphicsPath(newPathPoints, inputPath.PathTypes)
Using mtx As New Matrix
mtx.Translate(w, h)
inputPath.Transform(mtx)
mtx.Translate(0, 5 * h)
newPath.Transform(mtx)
End Using
Return newPath
End Function
End Class
BB
Re: GDI warp path question
This is brilliant thanks!! I'd actually started to do exactly the same kind of function as you've done (transform path points with a sine function). You beat me to it though and ironed out a few of the issues I was having too, so thank you!!