|
Packit |
0ec9dd |
/* Example code to show how to use pangocairo to render text
|
|
Packit |
0ec9dd |
* projected on a path.
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* Written by Behdad Esfahbod, 2006..2007
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* Permission to use, copy, modify, distribute, and sell this example
|
|
Packit |
0ec9dd |
* for any purpose is hereby granted without fee.
|
|
Packit |
0ec9dd |
* It is provided "as is" without express or implied warranty.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
#include <math.h>
|
|
Packit |
0ec9dd |
#include <stdlib.h>
|
|
Packit |
0ec9dd |
#include <pango/pangocairo.h>
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
void fancy_cairo_stroke (cairo_t *cr);
|
|
Packit |
0ec9dd |
void fancy_cairo_stroke_preserve (cairo_t *cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* A fancy cairo_stroke[_preserve]() that draws points and control
|
|
Packit |
0ec9dd |
* points, and connects them together.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
_fancy_cairo_stroke (cairo_t *cr, cairo_bool_t preserve)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
int i;
|
|
Packit |
0ec9dd |
double line_width;
|
|
Packit |
0ec9dd |
cairo_path_t *path;
|
|
Packit |
0ec9dd |
cairo_path_data_t *data;
|
|
Packit |
0ec9dd |
const double dash[] = {10, 10};
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_save (cr);
|
|
Packit |
0ec9dd |
cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
line_width = cairo_get_line_width (cr);
|
|
Packit |
0ec9dd |
path = cairo_copy_path (cr);
|
|
Packit |
0ec9dd |
cairo_new_path (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_save (cr);
|
|
Packit |
0ec9dd |
cairo_set_line_width (cr, line_width / 3);
|
|
Packit |
0ec9dd |
cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0);
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
cairo_line_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[2].point.x, data[2].point.y);
|
|
Packit |
0ec9dd |
cairo_line_to (cr, data[3].point.x, data[3].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
cairo_stroke (cr);
|
|
Packit |
0ec9dd |
cairo_restore (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_save (cr);
|
|
Packit |
0ec9dd |
cairo_set_line_width (cr, line_width * 4);
|
|
Packit |
0ec9dd |
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[2].point.x, data[2].point.y);
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[3].point.x, data[3].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 0, 0);
|
|
Packit |
0ec9dd |
cairo_stroke (cr);
|
|
Packit |
0ec9dd |
cairo_restore (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
cairo_move_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
cairo_line_to (cr, data[1].point.x, data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
cairo_curve_to (cr, data[1].point.x, data[1].point.y,
|
|
Packit |
0ec9dd |
data[2].point.x, data[2].point.y,
|
|
Packit |
0ec9dd |
data[3].point.x, data[3].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
cairo_close_path (cr);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
cairo_stroke (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
if (preserve)
|
|
Packit |
0ec9dd |
cairo_append_path (cr, path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_path_destroy (path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_restore (cr);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* A fancy cairo_stroke() that draws points and control points, and
|
|
Packit |
0ec9dd |
* connects them together.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
void
|
|
Packit |
0ec9dd |
fancy_cairo_stroke (cairo_t *cr)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
_fancy_cairo_stroke (cr, FALSE);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* A fancy cairo_stroke_preserve() that draws points and control
|
|
Packit |
0ec9dd |
* points, and connects them together.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
void
|
|
Packit |
0ec9dd |
fancy_cairo_stroke_preserve (cairo_t *cr)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
_fancy_cairo_stroke (cr, TRUE);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Returns Euclidean distance between two points */
|
|
Packit |
0ec9dd |
static double
|
|
Packit |
0ec9dd |
two_points_distance (cairo_path_data_t *a, cairo_path_data_t *b)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
double dx, dy;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
dx = b->point.x - a->point.x;
|
|
Packit |
0ec9dd |
dy = b->point.y - a->point.y;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
return sqrt (dx * dx + dy * dy);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Returns length of a Bezier curve.
|
|
Packit |
0ec9dd |
* Seems like computing that analytically is not easy. The
|
|
Packit |
0ec9dd |
* code just flattens the curve using cairo and adds the length
|
|
Packit |
0ec9dd |
* of segments.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
static double
|
|
Packit |
0ec9dd |
curve_length (double x0, double y0,
|
|
Packit |
0ec9dd |
double x1, double y1,
|
|
Packit |
0ec9dd |
double x2, double y2,
|
|
Packit |
0ec9dd |
double x3, double y3)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_surface_t *surface;
|
|
Packit |
0ec9dd |
cairo_t *cr;
|
|
Packit |
0ec9dd |
cairo_path_t *path;
|
|
Packit |
0ec9dd |
cairo_path_data_t *data, current_point = {0,};
|
|
Packit |
0ec9dd |
int i;
|
|
Packit |
0ec9dd |
double length;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
|
|
Packit |
0ec9dd |
cr = cairo_create (surface);
|
|
Packit |
0ec9dd |
cairo_surface_destroy (surface);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_move_to (cr, x0, y0);
|
|
Packit |
0ec9dd |
cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
length = 0;
|
|
Packit |
0ec9dd |
path = cairo_copy_path_flat (cr);
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
length += two_points_distance (¤t_point, &data[1]);
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
cairo_path_destroy (path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_destroy (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
return length;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
typedef double parametrization_t;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Compute parametrization info. That is, for each part of the
|
|
Packit |
0ec9dd |
* cairo path, tags it with its length.
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* Free returned value with g_free().
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
static parametrization_t *
|
|
Packit |
0ec9dd |
parametrize_path (cairo_path_t *path)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
int i;
|
|
Packit |
0ec9dd |
cairo_path_data_t *data, last_move_to = {0,}, current_point = {0,};
|
|
Packit |
0ec9dd |
parametrization_t *parametrization;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
parametrization = g_malloc (path->num_data * sizeof (parametrization[0]));
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
parametrization[i] = 0.0;
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
last_move_to = data[1];
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
/* Make it look like it's a line_to to last_move_to */
|
|
Packit |
0ec9dd |
data = (&last_move_to) - 1;
|
|
Packit |
0ec9dd |
/* fall through */
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
parametrization[i] = two_points_distance (¤t_point, &data[1]);
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
/* naive curve-length, treating bezier as three line segments:
|
|
Packit |
0ec9dd |
parametrization[i] = two_points_distance (¤t_point, &data[1])
|
|
Packit |
0ec9dd |
+ two_points_distance (&data[1], &data[2])
|
|
Packit |
0ec9dd |
+ two_points_distance (&data[2], &data[3]);
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
parametrization[i] = curve_length (current_point.point.x, current_point.point.y,
|
|
Packit |
0ec9dd |
data[1].point.x, data[1].point.y,
|
|
Packit |
0ec9dd |
data[2].point.x, data[2].point.y,
|
|
Packit |
0ec9dd |
data[3].point.x, data[3].point.y);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
current_point = data[3];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
return parametrization;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
typedef void (*transform_point_func_t) (void *closure, double *x, double *y);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Project a path using a function. Each point of the path (including
|
|
Packit |
0ec9dd |
* Bezier control points) is passed to the function for transformation.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
transform_path (cairo_path_t *path, transform_point_func_t f, void *closure)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
int i;
|
|
Packit |
0ec9dd |
cairo_path_data_t *data;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
for (i=0; i < path->num_data; i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
f (closure, &data[3].point.x, &data[3].point.y);
|
|
Packit |
0ec9dd |
f (closure, &data[2].point.x, &data[2].point.y);
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
f (closure, &data[1].point.x, &data[1].point.y);
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Simple struct to hold a path and its parametrization */
|
|
Packit |
0ec9dd |
typedef struct {
|
|
Packit |
0ec9dd |
cairo_path_t *path;
|
|
Packit |
0ec9dd |
parametrization_t *parametrization;
|
|
Packit |
0ec9dd |
} parametrized_path_t;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Project a point X,Y onto a parameterized path. The final point is
|
|
Packit |
0ec9dd |
* where you get if you walk on the path forward from the beginning for X
|
|
Packit |
0ec9dd |
* units, then stop there and walk another Y units perpendicular to the
|
|
Packit |
0ec9dd |
* path at that point. In more detail:
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* There's three pieces of math involved:
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* - The parametric form of the Line equation
|
|
Packit |
0ec9dd |
* http://en.wikipedia.org/wiki/Line
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* - The parametric form of the Cubic Bézier curve equation
|
|
Packit |
0ec9dd |
* http://en.wikipedia.org/wiki/B%C3%A9zier_curve
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* - The Gradient (aka multi-dimensional derivative) of the above
|
|
Packit |
0ec9dd |
* http://en.wikipedia.org/wiki/Gradient
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* The parametric forms are used to answer the question of "where will I be
|
|
Packit |
0ec9dd |
* if I walk a distance of X on this path". The Gradient is used to answer
|
|
Packit |
0ec9dd |
* the question of "where will I be if then I stop, rotate left for 90
|
|
Packit |
0ec9dd |
* degrees and walk straight for a distance of Y".
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
point_on_path (parametrized_path_t *param,
|
|
Packit |
0ec9dd |
double *x, double *y)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
int i;
|
|
Packit |
0ec9dd |
double ratio, the_y = *y, the_x = *x, dx, dy;
|
|
Packit |
0ec9dd |
cairo_path_data_t *data, last_move_to = {0,}, current_point = {0,};
|
|
Packit |
0ec9dd |
cairo_path_t *path = param->path;
|
|
Packit |
0ec9dd |
parametrization_t *parametrization = param->parametrization;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
for (i=0; i + path->data[i].header.length < path->num_data &&
|
|
Packit |
0ec9dd |
(the_x > parametrization[i] ||
|
|
Packit |
0ec9dd |
path->data[i].header.type == CAIRO_PATH_MOVE_TO);
|
|
Packit |
0ec9dd |
i += path->data[i].header.length) {
|
|
Packit |
0ec9dd |
the_x -= parametrization[i];
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
last_move_to = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
current_point = data[1];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
current_point = data[3];
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
data = &path->data[i];
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
switch (data->header.type) {
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
case CAIRO_PATH_MOVE_TO:
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CLOSE_PATH:
|
|
Packit |
0ec9dd |
/* Make it look like it's a line_to to last_move_to */
|
|
Packit |
0ec9dd |
data = (&last_move_to) - 1;
|
|
Packit |
0ec9dd |
/* fall through */
|
|
Packit |
0ec9dd |
case CAIRO_PATH_LINE_TO:
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
ratio = the_x / parametrization[i];
|
|
Packit |
0ec9dd |
/* Line polynomial */
|
|
Packit |
0ec9dd |
*x = current_point.point.x * (1 - ratio) + data[1].point.x * ratio;
|
|
Packit |
0ec9dd |
*y = current_point.point.y * (1 - ratio) + data[1].point.y * ratio;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Line gradient */
|
|
Packit |
0ec9dd |
dx = -(current_point.point.x - data[1].point.x);
|
|
Packit |
0ec9dd |
dy = -(current_point.point.y - data[1].point.y);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/*optimization for: ratio = the_y / sqrt (dx * dx + dy * dy);*/
|
|
Packit |
0ec9dd |
ratio = the_y / parametrization[i];
|
|
Packit |
0ec9dd |
*x += -dy * ratio;
|
|
Packit |
0ec9dd |
*y += dx * ratio;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
case CAIRO_PATH_CURVE_TO:
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
/* FIXME the formulas here are not exactly what we want, because the
|
|
Packit |
0ec9dd |
* Bezier parametrization is not uniform. But I don't know how to do
|
|
Packit |
0ec9dd |
* better. The caller can do slightly better though, by flattening the
|
|
Packit |
0ec9dd |
* Bezier and avoiding this branch completely. That has its own cost
|
|
Packit |
0ec9dd |
* though, as large y values magnify the flattening error drastically.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
double ratio_1_0, ratio_0_1;
|
|
Packit |
0ec9dd |
double ratio_2_0, ratio_0_2;
|
|
Packit |
0ec9dd |
double ratio_3_0, ratio_2_1, ratio_1_2, ratio_0_3;
|
|
Packit |
0ec9dd |
double _1__4ratio_1_0_3ratio_2_0, _2ratio_1_0_3ratio_2_0;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
ratio = the_x / parametrization[i];
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
ratio_1_0 = ratio;
|
|
Packit |
0ec9dd |
ratio_0_1 = 1 - ratio;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
ratio_2_0 = ratio_1_0 * ratio_1_0; /* ratio * ratio */
|
|
Packit |
0ec9dd |
ratio_0_2 = ratio_0_1 * ratio_0_1; /* (1 - ratio) * (1 - ratio) */
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
ratio_3_0 = ratio_2_0 * ratio_1_0; /* ratio * ratio * ratio */
|
|
Packit |
0ec9dd |
ratio_2_1 = ratio_2_0 * ratio_0_1; /* ratio * ratio * (1 - ratio) */
|
|
Packit |
0ec9dd |
ratio_1_2 = ratio_1_0 * ratio_0_2; /* ratio * (1 - ratio) * (1 - ratio) */
|
|
Packit |
0ec9dd |
ratio_0_3 = ratio_0_1 * ratio_0_2; /* (1 - ratio) * (1 - ratio) * (1 - ratio) */
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
_1__4ratio_1_0_3ratio_2_0 = 1 - 4 * ratio_1_0 + 3 * ratio_2_0;
|
|
Packit |
0ec9dd |
_2ratio_1_0_3ratio_2_0 = 2 * ratio_1_0 - 3 * ratio_2_0;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Bezier polynomial */
|
|
Packit |
0ec9dd |
*x = current_point.point.x * ratio_0_3
|
|
Packit |
0ec9dd |
+ 3 * data[1].point.x * ratio_1_2
|
|
Packit |
0ec9dd |
+ 3 * data[2].point.x * ratio_2_1
|
|
Packit |
0ec9dd |
+ data[3].point.x * ratio_3_0;
|
|
Packit |
0ec9dd |
*y = current_point.point.y * ratio_0_3
|
|
Packit |
0ec9dd |
+ 3 * data[1].point.y * ratio_1_2
|
|
Packit |
0ec9dd |
+ 3 * data[2].point.y * ratio_2_1
|
|
Packit |
0ec9dd |
+ data[3].point.y * ratio_3_0;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Bezier gradient */
|
|
Packit |
0ec9dd |
dx =-3 * current_point.point.x * ratio_0_2
|
|
Packit |
0ec9dd |
+ 3 * data[1].point.x * _1__4ratio_1_0_3ratio_2_0
|
|
Packit |
0ec9dd |
+ 3 * data[2].point.x * _2ratio_1_0_3ratio_2_0
|
|
Packit |
0ec9dd |
+ 3 * data[3].point.x * ratio_2_0;
|
|
Packit |
0ec9dd |
dy =-3 * current_point.point.y * ratio_0_2
|
|
Packit |
0ec9dd |
+ 3 * data[1].point.y * _1__4ratio_1_0_3ratio_2_0
|
|
Packit |
0ec9dd |
+ 3 * data[2].point.y * _2ratio_1_0_3ratio_2_0
|
|
Packit |
0ec9dd |
+ 3 * data[3].point.y * ratio_2_0;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
ratio = the_y / sqrt (dx * dx + dy * dy);
|
|
Packit |
0ec9dd |
*x += -dy * ratio;
|
|
Packit |
0ec9dd |
*y += dx * ratio;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
break;
|
|
Packit |
0ec9dd |
default:
|
|
Packit |
0ec9dd |
g_assert_not_reached ();
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Projects the current path of cr onto the provided path. */
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
map_path_onto (cairo_t *cr, cairo_path_t *path)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_path_t *current_path;
|
|
Packit |
0ec9dd |
parametrized_path_t param;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
param.path = path;
|
|
Packit |
0ec9dd |
param.parametrization = parametrize_path (path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
current_path = cairo_copy_path (cr);
|
|
Packit |
0ec9dd |
cairo_new_path (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
transform_path (current_path,
|
|
Packit |
0ec9dd |
(transform_point_func_t) point_on_path, ¶m;;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_append_path (cr, current_path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_path_destroy (current_path);
|
|
Packit |
0ec9dd |
g_free (param.parametrization);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
typedef void (*draw_path_func_t) (cairo_t *cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
draw_text (cairo_t *cr,
|
|
Packit |
0ec9dd |
double x,
|
|
Packit |
0ec9dd |
double y,
|
|
Packit |
0ec9dd |
const char *font,
|
|
Packit |
0ec9dd |
const char *text)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
PangoLayout *layout;
|
|
Packit |
0ec9dd |
PangoLayoutLine *line;
|
|
Packit |
0ec9dd |
PangoFontDescription *desc;
|
|
Packit |
0ec9dd |
cairo_font_options_t *font_options;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
font_options = cairo_font_options_create ();
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
|
|
Packit |
0ec9dd |
cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_set_font_options (cr, font_options);
|
|
Packit |
0ec9dd |
cairo_font_options_destroy (font_options);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
layout = pango_cairo_create_layout (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
desc = pango_font_description_from_string (font);
|
|
Packit |
0ec9dd |
pango_layout_set_font_description (layout, desc);
|
|
Packit |
0ec9dd |
pango_font_description_free (desc);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
pango_layout_set_text (layout, text, -1);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Use pango_layout_get_line() instead of pango_layout_get_line_readonly()
|
|
Packit |
0ec9dd |
* for older versions of pango
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
line = pango_layout_get_line_readonly (layout, 0);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_move_to (cr, x, y);
|
|
Packit |
0ec9dd |
pango_cairo_layout_line_path (cr, line);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
g_object_unref (layout);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
draw_twisted (cairo_t *cr,
|
|
Packit |
0ec9dd |
double x,
|
|
Packit |
0ec9dd |
double y,
|
|
Packit |
0ec9dd |
const char *font,
|
|
Packit |
0ec9dd |
const char *text)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_path_t *path;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_save (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Decrease tolerance a bit, since it's going to be magnified */
|
|
Packit |
0ec9dd |
cairo_set_tolerance (cr, 0.01);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
/* Using cairo_copy_path() here shows our deficiency in handling
|
|
Packit |
0ec9dd |
* Bezier curves, specially around sharper curves.
|
|
Packit |
0ec9dd |
*
|
|
Packit |
0ec9dd |
* Using cairo_copy_path_flat() on the other hand, magnifies the
|
|
Packit |
0ec9dd |
* flattening error with large off-path values. We decreased
|
|
Packit |
0ec9dd |
* tolerance for that reason. Increase tolerance to see that
|
|
Packit |
0ec9dd |
* artifact.
|
|
Packit |
0ec9dd |
*/
|
|
Packit |
0ec9dd |
path = cairo_copy_path_flat (cr);
|
|
Packit |
0ec9dd |
/*path = cairo_copy_path (cr);*/
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_new_path (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
draw_text (cr, x, y, font, text);
|
|
Packit |
0ec9dd |
map_path_onto (cr, path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_path_destroy (path);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_fill_preserve (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_save (cr);
|
|
Packit |
0ec9dd |
cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
|
|
Packit |
0ec9dd |
cairo_stroke (cr);
|
|
Packit |
0ec9dd |
cairo_restore (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_restore (cr);
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
draw_dream (cairo_t *cr)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_move_to (cr, 50, 650);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_rel_line_to (cr, 250, 50);
|
|
Packit |
0ec9dd |
cairo_rel_curve_to (cr, 250, 50, 600, -50, 600, -250);
|
|
Packit |
0ec9dd |
cairo_rel_curve_to (cr, 0, -400, -300, -100, -800, -300);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_set_line_width (cr, 1.5);
|
|
Packit |
0ec9dd |
cairo_set_source_rgba (cr, 0.3, 0.3, 1.0, 0.3);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
fancy_cairo_stroke_preserve (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
draw_twisted (cr,
|
|
Packit |
0ec9dd |
0, 0,
|
|
Packit |
0ec9dd |
"Serif 72",
|
|
Packit |
0ec9dd |
"It was a dream... Oh Just a dream...");
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
static void
|
|
Packit |
0ec9dd |
draw_wow (cairo_t *cr)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_move_to (cr, 400, 780);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_rel_curve_to (cr, 50, -50, 150, -50, 200, 0);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_scale (cr, 1.0, 2.0);
|
|
Packit |
0ec9dd |
cairo_set_line_width (cr, 2.0);
|
|
Packit |
0ec9dd |
cairo_set_source_rgba (cr, 0.3, 1.0, 0.3, 1.0);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
fancy_cairo_stroke_preserve (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
draw_twisted (cr,
|
|
Packit |
0ec9dd |
-20, -150,
|
|
Packit |
0ec9dd |
"Serif 60",
|
|
Packit |
0ec9dd |
"WOW!");
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
int main (int argc, char **argv)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
cairo_t *cr;
|
|
Packit |
0ec9dd |
char *filename;
|
|
Packit |
0ec9dd |
cairo_status_t status;
|
|
Packit |
0ec9dd |
cairo_surface_t *surface;
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
if (argc != 2)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
g_printerr ("Usage: cairotwisted OUTPUT_FILENAME\n");
|
|
Packit |
0ec9dd |
return 1;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
filename = argv[1];
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
Packit |
0ec9dd |
1000, 800);
|
|
Packit |
0ec9dd |
cr = cairo_create (surface);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
|
|
Packit |
0ec9dd |
cairo_paint (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
draw_dream (cr);
|
|
Packit |
0ec9dd |
draw_wow (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
cairo_destroy (cr);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
status = cairo_surface_write_to_png (surface, filename);
|
|
Packit |
0ec9dd |
cairo_surface_destroy (surface);
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
if (status != CAIRO_STATUS_SUCCESS)
|
|
Packit |
0ec9dd |
{
|
|
Packit |
0ec9dd |
g_printerr ("Could not save png to '%s'\n", filename);
|
|
Packit |
0ec9dd |
return 1;
|
|
Packit |
0ec9dd |
}
|
|
Packit |
0ec9dd |
|
|
Packit |
0ec9dd |
return 0;
|
|
Packit |
0ec9dd |
}
|