/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * Wilber Cairo rendering * Copyright (C) 2008 Sven Neumann * * Some code here is based on code from librsvg that was originally * written by Raph Levien for Gill. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include "libgimpmath/gimpmath.h" #include /* This string is a path description as found in SVG files. You can * use Inkscape to create the SVG file, then copy the path from it. * It works best if you combine all paths into one. Inkscape has a * function to do that. */ static const gchar wilber_path[] = "M 509.72445,438.68864 C 501.47706,469.77945 464.95038,491.54566 431.85915,497.74874 C 438.5216,503.01688 442.87782,511.227 442.87782,520.37375 C 442.87783,536.24746 429.95607,549.0223 414.08235,549.0223 C 398.20863,549.0223 385.28688,536.24746 385.28688,520.37375 C 385.28688,511.52403 389.27666,503.61286 395.57098,498.3364 C 359.36952,495.90384 343.70976,463.95812 343.70975,463.95814 L 342.68134,509.64891 C 342.68134,514.35021 342.08391,519.96098 340.18378,528.3072 C 339.84664,527.80364 339.51399,527.33515 339.15537,526.83804 C 330.25511,514.5011 317.25269,507.81431 306.39317,508.76741 C 302.77334,509.08511 299.47017,510.33348 296.54982,512.4403 C 284.86847,520.86757 284.97665,540.94721 296.84366,557.3965 C 306.96274,571.42287 322.32232,578.25612 333.8664,574.73254 C 391.94635,615.17624 532.16931,642.41915 509.72445,438.68864 z M 363.24953,501.1278 C 373.83202,501.12778 382.49549,509.79127 382.49549,520.37375 C 382.49549,530.95624 373.83201,539.47279 363.24953,539.47279 C 352.66706,539.47279 344.1505,530.95624 344.1505,520.37375 C 344.15049,509.79129 352.66706,501.1278 363.24953,501.1278 z M 305.80551,516.1132 C 311.68466,516.11318 316.38344,521.83985 316.38344,528.89486 C 316.38345,535.94982 311.68467,541.67652 305.80551,541.67652 C 299.92636,541.67652 295.08067,535.94987 295.08067,528.89486 C 295.08065,521.83985 299.92636,516.1132 305.80551,516.1132 z M 440.821,552.54828 C 440.821,552.54828 448.7504,554.02388 453.8965,559.45332 C 457.41881,563.16951 457.75208,569.15506 456.98172,577.37703 C 456.21143,573.8833 454.89571,571.76659 453.8965,569.29666 C 443.01388,582.47662 413.42981,583.08929 376.0312,569.88433 C 416.63248,578.00493 437.38806,570.56014 449.48903,561.2163 C 446.29383,557.08917 440.821,552.54828 440.821,552.54828 z M 434.64723,524.59684 C 434.64723,532.23974 428.44429,538.44268 420.80139,538.44268 C 413.15849,538.44268 406.95555,532.23974 406.95555,524.59684 C 406.95555,516.95394 413.15849,510.751 420.80139,510.751 C 428.44429,510.751 434.64723,516.95394 434.64723,524.59684 z M 378.00043,522.99931 C 378.00043,527.70264 374.18324,531.51984 369.47991,531.51984 C 364.77658,531.51984 360.95939,527.70264 360.95939,522.99931 C 360.95939,518.29599 364.77658,514.47879 369.47991,514.47879 C 374.18324,514.47879 378.00043,518.29599 378.00043,522.99931 z "; static gboolean wilber_extents_known = FALSE; static gdouble wilber_x1, wilber_y1; static gdouble wilber_x2, wilber_y2; static void wilber_parse_path_data (cairo_t *cr, const gchar *data); static void wilber_get_extents (cairo_t *cr); /** * gimp_cairo_wilber: * @cr: Cairo context * @x: x position * @y: y position * * Draw a Wilber path at position @x, @y. */ void gimp_cairo_wilber (cairo_t *cr, gdouble x, gdouble y) { wilber_get_extents (cr); cairo_save (cr); cairo_translate (cr, x - wilber_x1, y - wilber_y1); wilber_parse_path_data (cr, wilber_path); cairo_restore (cr); } void gimp_cairo_wilber_get_size (cairo_t *cr, gdouble *width, gdouble *height) { wilber_get_extents (cr); *width = wilber_x2 - wilber_x1; *height = wilber_y2 - wilber_y1; } static void wilber_get_extents (cairo_t *cr) { if (! wilber_extents_known) { cairo_save (cr); wilber_parse_path_data (cr, wilber_path); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_fill_extents (cr, &wilber_x1, &wilber_y1, &wilber_x2, &wilber_y2); cairo_restore (cr); wilber_extents_known = TRUE; } } /**********************************************************/ /* Below is the code that parses the actual path data. */ /* */ /* This code is taken from librsvg and was originally */ /* written by Raph Levien for Gill. */ /**********************************************************/ typedef struct { cairo_t *cr; gdouble cpx, cpy; /* current point */ gdouble rpx, rpy; /* reflection point (for 's' and 't' commands) */ gchar cmd; /* current command (lowercase) */ gint param; /* number of parameters */ gboolean rel; /* true if relative coords */ gdouble params[7]; /* parameters that have been parsed */ } ParsePathContext; static void parse_path_default_xy (ParsePathContext *ctx, gint n_params); static void parse_path_do_cmd (ParsePathContext *ctx, gboolean final); static void wilber_parse_path_data (cairo_t *cr, const gchar *data) { ParsePathContext ctx; gboolean in_num = FALSE; gboolean in_frac = FALSE; gboolean in_exp = FALSE; gboolean exp_wait_sign = FALSE; gdouble val = 0.0; gchar c = 0; gint sign = 0; gint exp = 0; gint exp_sign = 0; gdouble frac = 0.0; gint i; memset (&ctx, 0, sizeof (ParsePathContext)); ctx.cr = cr; for (i = 0; ; i++) { c = data[i]; if (c >= '0' && c <= '9') { /* digit */ if (in_num) { if (in_exp) { exp = (exp * 10) + c - '0'; exp_wait_sign = FALSE; } else if (in_frac) val += (frac *= 0.1) * (c - '0'); else val = (val * 10) + c - '0'; } else { in_num = TRUE; in_frac = FALSE; in_exp = FALSE; exp = 0; exp_sign = 1; exp_wait_sign = FALSE; val = c - '0'; sign = 1; } } else if (c == '.') { if (!in_num) { in_num = TRUE; val = 0; } in_frac = TRUE; frac = 1; } else if ((c == 'E' || c == 'e') && in_num) { in_exp = TRUE; exp_wait_sign = TRUE; exp = 0; exp_sign = 1; } else if ((c == '+' || c == '-') && in_exp) { exp_sign = c == '+' ? 1 : -1; } else if (in_num) { /* end of number */ val *= sign * pow (10, exp_sign * exp); if (ctx.rel) { /* Handle relative coordinates. This switch statement attempts to determine _what_ the coords are relative to. This is underspecified in the 12 Apr working draft. */ switch (ctx.cmd) { case 'l': case 'm': case 'c': case 's': case 'q': case 't': /* rule: even-numbered params are x-relative, odd-numbered are y-relative */ if ((ctx.param & 1) == 0) val += ctx.cpx; else if ((ctx.param & 1) == 1) val += ctx.cpy; break; case 'a': /* rule: sixth and seventh are x and y, rest are not relative */ if (ctx.param == 5) val += ctx.cpx; else if (ctx.param == 6) val += ctx.cpy; break; case 'h': /* rule: x-relative */ val += ctx.cpx; break; case 'v': /* rule: y-relative */ val += ctx.cpy; break; } } ctx.params[ctx.param++] = val; parse_path_do_cmd (&ctx, FALSE); in_num = FALSE; } if (c == '\0') break; else if ((c == '+' || c == '-') && !exp_wait_sign) { sign = c == '+' ? 1 : -1; val = 0; in_num = TRUE; in_frac = FALSE; in_exp = FALSE; exp = 0; exp_sign = 1; exp_wait_sign = FALSE; } else if (c == 'z' || c == 'Z') { if (ctx.param) parse_path_do_cmd (&ctx, TRUE); cairo_close_path (ctx.cr); } else if (c >= 'A' && c <= 'Z' && c != 'E') { if (ctx.param) parse_path_do_cmd (&ctx, TRUE); ctx.cmd = c + 'a' - 'A'; ctx.rel = FALSE; } else if (c >= 'a' && c <= 'z' && c != 'e') { if (ctx.param) parse_path_do_cmd (&ctx, TRUE); ctx.cmd = c; ctx.rel = TRUE; } /* else c _should_ be whitespace or , */ } } /* supply defaults for missing parameters, assuming relative coordinates are to be interpreted as x,y */ static void parse_path_default_xy (ParsePathContext *ctx, gint n_params) { gint i; if (ctx->rel) { for (i = ctx->param; i < n_params; i++) { if (i > 2) ctx->params[i] = ctx->params[i - 2]; else if (i == 1) ctx->params[i] = ctx->cpy; else if (i == 0) /* we shouldn't get here (ctx->param > 0 as precondition) */ ctx->params[i] = ctx->cpx; } } else { for (i = ctx->param; i < n_params; i++) ctx->params[i] = 0.0; } } static void parse_path_do_cmd (ParsePathContext *ctx, gboolean final) { switch (ctx->cmd) { case 'm': /* moveto */ if (ctx->param == 2 || final) { parse_path_default_xy (ctx, 2); ctx->cpx = ctx->rpx = ctx->params[0]; ctx->cpy = ctx->rpy = ctx->params[1]; cairo_move_to (ctx->cr, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 'l': /* lineto */ if (ctx->param == 2 || final) { parse_path_default_xy (ctx, 2); ctx->cpx = ctx->rpx = ctx->params[0]; ctx->cpy = ctx->rpy = ctx->params[1]; cairo_line_to (ctx->cr, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 'c': /* curveto */ if (ctx->param == 6 || final) { gdouble x, y; parse_path_default_xy (ctx, 6); x = ctx->params[0]; y = ctx->params[1]; ctx->rpx = ctx->params[2]; ctx->rpy = ctx->params[3]; ctx->cpx = ctx->params[4]; ctx->cpy = ctx->params[5]; cairo_curve_to (ctx->cr, x, y, ctx->rpx, ctx->rpy, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 's': /* smooth curveto */ if (ctx->param == 4 || final) { gdouble x, y; parse_path_default_xy (ctx, 4); x = 2 * ctx->cpx - ctx->rpx; y = 2 * ctx->cpy - ctx->rpy; ctx->rpx = ctx->params[0]; ctx->rpy = ctx->params[1]; ctx->cpx = ctx->params[2]; ctx->cpy = ctx->params[3]; cairo_curve_to (ctx->cr, x, y, ctx->rpx, ctx->rpy, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 'h': /* horizontal lineto */ if (ctx->param == 1) { ctx->cpx = ctx->rpx = ctx->params[0]; cairo_line_to (ctx->cr, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 'v': /* vertical lineto */ if (ctx->param == 1) { ctx->cpy = ctx->rpy = ctx->params[0]; cairo_line_to (ctx->cr, ctx->cpx, ctx->cpy); ctx->param = 0; } break; case 'q': /* quadratic bezier curveto */ if (ctx->param == 4 || final) { parse_path_default_xy (ctx, 4); ctx->rpx = ctx->params[0]; ctx->rpy = ctx->params[1]; ctx->cpx = ctx->params[2]; ctx->cpy = ctx->params[3]; g_warning ("quadratic bezier curveto not implemented"); ctx->param = 0; } break; case 't': /* truetype quadratic bezier curveto */ if (ctx->param == 2 || final) { parse_path_default_xy (ctx, 2); ctx->rpx = 2 * ctx->cpx - ctx->rpx; ctx->rpy = 2 * ctx->cpy - ctx->rpy; ctx->cpx = ctx->params[0]; ctx->cpy = ctx->params[1]; g_warning ("truetype quadratic bezier curveto not implemented"); ctx->param = 0; } else if (final) { if (ctx->param > 2) { parse_path_default_xy (ctx, 4); ctx->rpx = ctx->params[0]; ctx->rpy = ctx->params[1]; ctx->cpx = ctx->params[2]; ctx->cpy = ctx->params[3]; g_warning ("conicto not implemented"); } else { parse_path_default_xy (ctx, 2); ctx->cpx = ctx->rpx = ctx->params[0]; ctx->cpy = ctx->rpy = ctx->params[1]; cairo_line_to (ctx->cr, ctx->cpx, ctx->cpy); } ctx->param = 0; } break; case 'a': if (ctx->param == 7 || final) { ctx->cpx = ctx->rpx = ctx->params[5]; ctx->cpy = ctx->rpy = ctx->params[6]; g_warning ("arcto not implemented"); ctx->param = 0; } break; default: ctx->param = 0; break; } }