Recently, I tried implementing "Text on path" prototype. Even it's not a new concept nor a striking function, still it's an interesting feature to understand UI & graphics. Actually, many drawing tools (ie, MS Photoshop, Inkscape, gimp, etc) have provided it since a long time ago.

For those people who don't know about "Text on path", it is a feature that displays a text on an arbitrary virtual path like the next figure.


Even though this kind of text effects does nothing in point of functionality view, this may work for users to get interested in your application UI or to understand your application context easier. Here is a perfect use-case that this feature is effectively used in a real application (Samsung Android Milk app).


Then how could you implement it? If you are an app developer, probably you could use the convenient easy API set which is provided from your UIFW system. But my question is not for the situation. Sometimes you may need to implement by your hand beyond the framework. This page is for it.

Most of all, you need to understand two points for this. First is a path information and second is a drawing method. Most UIFWs have their own Path data interface. A traditional path interface might look like this. (just in C style)

Path *path = create_path();
move_to(path, pos_x, pos_y);
line_to(path, to_x, to_y);
circle_to(path, center_x, center_y, radius);
curve_to(path, start_pt, ctrl_pt1, ctrl_pt2, end_pt);
...

This path data keeps the path information internally. You can move the start position using move_to() and then link following path points using line_to(), circle_to() and curve_to(). Probably there would be more functions for path but skip others here now. I don't think this path will have a nice visual but point here is, you can give the path information using sort of those interfaces.

Once the path data is set, all path coordinates per *pixels* could be generated by some interpolation algorithm during a ready stage of your program. Normally, Linear Interpolation provides good quality for this.


Or you can apply other interpolation formulas such as polynomial and spline.

I believe you can generate line, circle and some rectangular shapes' path coordinates easily. It doesn't require complex formulas for them. But how about curves? How can you generate pixels for curves? One generic method for representing a curve is a using Bezier Curve. This algorithm has been universally used in graphics and even it works for this text on path. Basically, this requires 4 points - start point, end point and two control points. Start point is the start position and end point is end position for your path. Two control points actually decide the path curve style.


The curve path can be completed with this formula.


Please refer Bezier Curve Wikipedia page for more details.

//progress: A normalized value (0 ~ 1) in bezier curve. 0 indicates the position of start point and 1 indicates the position of end point.
get_bezier_curve_pos(progress)
{
    v[0];    //1st control point x coordinate
    v[1];    //1st control point y coordinate
    v[2];    //2nd control point x coordinate
    v[3];    //2nd control point y coordinate
    ...
    tx = (pow((1 - progress), 3) * 0) + (3 * progress * pow((1 - progress), 2) * v[0]) + (3 * pow(progress, 2) * (1 - progress) * v[2]) + (pow(progress, 3) * 1);
    ty = (pow((1 - progress), 3) * 0) + (3 * progress * pow((1 - progress), 2) * v[1]) + (3 * pow(progress, 2) * (1 - progress) * v[3]) + (pow(progress, 3) * 1);
    ...
    return tx, ty;
}

This logic performs to get x, y position on a Bezier curve. tx, ty are a pair of xy coordinates. You can get coordinates by giving progress value from 0 to 1. 0 is the start point of the curve and 1 is the end point of the curve. 0.5 is the center point of the curve. If you connect one more than Bezier curves by giving several points set(start, end and 2 control points), you can make a long complex arbitrary curving path.

Now, you can generate pixel coordinates of a curving path. Next step is an actual drawing.

(For your information, you can refer the next website to simulate a Bezier curve with your control points.)

Firstly, you need a text to draw. But you don't need to implement text rendering feature in your program. Instead, use the existing functions as possible. For instance, you can use one text rendering library such as Freetype. Or, probably your platform may support its own text rendering feature. Since those texts rendering requires a huge amount of pages for understanding, I'd rather skip it here. You can google it by yourself.

If you use a text drawing library, definitely those libraries provide an API that returns the generated bitmap of a text. In this case, you can get a bitmap of a glyph or a bitmap of a text(sentence). Rather than glyph, text may be much better because that some languages such as Hindi, its glyph connected each other. In that case, you can make the completed curved text only with a text.


Now, let's suppose you have a text bitmap. Next work is to bend the text bitmap. If your graphics system has a method to draw textures based on the polygons, that's the most the easiest way for this. Or you can use a 3D rendering system such as OpenGL/Direct3D directly. I'm sorry, I don't take cover 3D graphics topics such as polygon and texture mapping here. If you are unfamiliar with that, please go to revise your 3d graphics lesson first.

Suppose you use the polygon based drawing. You need to construct a mesh for curved text area. You can achieve that with some linear algebra idea. Let's compute polygon vertices. Since you have a Bezier curve, you can get vectors along with a curving path by progress.


p1 = get_bezier_curve_pos(0.0);    //Point 1 at 0.0
p2 = get_bezier_curve_pos(0.01);    //Point 2 at 0.01
v = p2 - p1;    //A vector
normalize(v);

You got the first segment vector. Compute it's upper vertex. Transform the vector by -90 degree then increase the vector length by half of text height.

rad = degree_to_radian(90);
matrix2 = { cos(rad), -sin(rad), sin(rad), cos(rad) };    //Euler angle
v *= matrix2;
normalize(v);
v *= (text_height * 0.5);

v1 = v + p1;    //Don't forget the origin of the vector.


Compute the lower vertex. Simply, you can invert the upper vector here.

v2 = -v + p1;


Obviously, you got the 2 vertices. For one polygon, you need next 2 vertices more. Repeat the above sequence with the line segment between 0.01 ~ 0.02.

p1 = get_bezier_curve_pos(0.01);    //Point 1 at 0.01
p2 = get_bezier_curve_pos(0.02);    //Point 2 at 0.02
v = p2 - p1;    //A vector
normalize(v);

v *= matrix2;
normalize(v);
v *= (text_height * 0.5);

v3 = v + p1;
v4 = -v + p1;



Repeat this to the end of the curve. (~ 1.0)


Pretty easy. Now you can see how does "Text on path" mesh accomplished! Segments count in the screenshots might not be matched exactly. As far as decrease the segment distance, you will have a fine-grained result. Last step is texture mapping. Since you already have a text bitmap, you can use the bitmap as a texture.


Next video shows my prototype.