📄 cairotwisted.c
字号:
/* Example code to show how to use pangocairo to render text * projected on a path. * * * Written by Behdad Esfahbod, 2006..2007 * * Permission to use, copy, modify, distribute, and sell this example * for any purpose is hereby granted without fee. * It is provided "as is" without express or implied warranty. */#include <math.h>#include <stdlib.h>#include <pango/pangocairo.h>void fancy_cairo_stroke (cairo_t *cr);void fancy_cairo_stroke_preserve (cairo_t *cr);/* A fancy cairo_stroke[_preserve]() that draws points and control * points, and connects them together. */static void_fancy_cairo_stroke (cairo_t *cr, cairo_bool_t preserve){ int i; double line_width; cairo_path_t *path; cairo_path_data_t *data; const double dash[] = {10, 10}; cairo_save (cr); cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); line_width = cairo_get_line_width (cr); path = cairo_copy_path (cr); cairo_new_path (cr); cairo_save (cr); cairo_set_line_width (cr, line_width / 3); cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0); for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: case CAIRO_PATH_LINE_TO: cairo_move_to (cr, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: cairo_line_to (cr, data[1].point.x, data[1].point.y); cairo_move_to (cr, data[2].point.x, data[2].point.y); cairo_line_to (cr, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: break; default: g_assert_not_reached (); } } cairo_stroke (cr); cairo_restore (cr); cairo_save (cr); cairo_set_line_width (cr, line_width * 4); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: cairo_move_to (cr, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_LINE_TO: cairo_rel_line_to (cr, 0, 0); cairo_move_to (cr, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: cairo_rel_line_to (cr, 0, 0); cairo_move_to (cr, data[1].point.x, data[1].point.y); cairo_rel_line_to (cr, 0, 0); cairo_move_to (cr, data[2].point.x, data[2].point.y); cairo_rel_line_to (cr, 0, 0); cairo_move_to (cr, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: cairo_rel_line_to (cr, 0, 0); break; default: g_assert_not_reached (); } } cairo_rel_line_to (cr, 0, 0); cairo_stroke (cr); cairo_restore (cr); for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: cairo_move_to (cr, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_LINE_TO: cairo_line_to (cr, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: cairo_curve_to (cr, data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: cairo_close_path (cr); break; default: g_assert_not_reached (); } } cairo_stroke (cr); if (preserve) cairo_append_path (cr, path); cairo_path_destroy (path); cairo_restore (cr);}/* A fancy cairo_stroke() that draws points and control points, and * connects them together. */voidfancy_cairo_stroke (cairo_t *cr){ _fancy_cairo_stroke (cr, FALSE);}/* A fancy cairo_stroke_preserve() that draws points and control * points, and connects them together. */voidfancy_cairo_stroke_preserve (cairo_t *cr){ _fancy_cairo_stroke (cr, TRUE);}/* Returns Euclidean distance between two points */static doubletwo_points_distance (cairo_path_data_t *a, cairo_path_data_t *b){ double dx, dy; dx = b->point.x - a->point.x; dy = b->point.y - a->point.y; return sqrt (dx * dx + dy * dy);}/* Returns length of a Bezier curve. * Seems like computing that analytically is not easy. The * code just flattens the curve using cairo and adds the length * of segments. */static doublecurve_length (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3){ cairo_surface_t *surface; cairo_t *cr; cairo_path_t *path; cairo_path_data_t *data, current_point; int i; double length; surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0); cr = cairo_create (surface); cairo_surface_destroy (surface); cairo_move_to (cr, x0, y0); cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); length = 0; path = cairo_copy_path_flat (cr); for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: current_point = data[1]; break; case CAIRO_PATH_LINE_TO: length += two_points_distance (¤t_point, &data[1]); current_point = data[1]; break; default: case CAIRO_PATH_CURVE_TO: case CAIRO_PATH_CLOSE_PATH: g_assert_not_reached (); } } cairo_path_destroy (path); cairo_destroy (cr); return length;}typedef double parametrization_t;/* Compute parametrization info. That is, for each part of the * cairo path, tags it with its length. */static parametrization_t *parametrize_path (cairo_path_t *path){ int i; cairo_path_data_t *data, current_point; parametrization_t *parametrization; parametrization = malloc (path->num_data * sizeof (parametrization[0])); for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; parametrization[i] = 0.0; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: current_point = data[1]; break; case CAIRO_PATH_LINE_TO: parametrization[i] = two_points_distance (¤t_point, &data[1]); current_point = data[1]; break; case CAIRO_PATH_CURVE_TO: /* naive curve-length, treating bezier as three line segments: parametrization[i] = two_points_distance (¤t_point, &data[1]) + two_points_distance (&data[1], &data[2]) + two_points_distance (&data[2], &data[3]); */ parametrization[i] = curve_length (current_point.point.x, current_point.point.x, data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y); current_point = data[3]; break; case CAIRO_PATH_CLOSE_PATH: break; default: g_assert_not_reached (); } } return parametrization;}typedef void (*transform_point_func_t) (void *closure, double *x, double *y);/* Project a path using a function. Each point of the path (including * Bezier control points) is passed to the function for transformation. */static voidtransform_path (cairo_path_t *path, transform_point_func_t f, void *closure){ int i; cairo_path_data_t *data; for (i=0; i < path->num_data; i += path->data[i].header.length) { data = &path->data[i]; switch (data->header.type) { case CAIRO_PATH_CURVE_TO: f (closure, &data[3].point.x, &data[3].point.y); f (closure, &data[2].point.x, &data[2].point.y); case CAIRO_PATH_MOVE_TO: case CAIRO_PATH_LINE_TO: f (closure, &data[1].point.x, &data[1].point.y); break; case CAIRO_PATH_CLOSE_PATH: break; default: g_assert_not_reached (); } }}/* Simple struct to hold a path and its parametrization */typedef struct { cairo_path_t *path; parametrization_t *parametrization;} parametrized_path_t;/* Project a point X,Y onto a parameterized path. The final point is * where you get if you walk on the path forward from the beginning for X * units, then stop there and walk another Y units perpendicular to the * path at that point. In more detail: * * There's three pieces of math involved: * * - The parametric form of the Line equation * http://en.wikipedia.org/wiki/Line * * - The parametric form of the Cubic Bézier curve equation
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -