diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /app/widgets/gimpcairo-wilber.c | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/widgets/gimpcairo-wilber.c')
-rw-r--r-- | app/widgets/gimpcairo-wilber.c | 1010 |
1 files changed, 1010 insertions, 0 deletions
diff --git a/app/widgets/gimpcairo-wilber.c b/app/widgets/gimpcairo-wilber.c new file mode 100644 index 0000000..faf75bf --- /dev/null +++ b/app/widgets/gimpcairo-wilber.c @@ -0,0 +1,1010 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Wilber Cairo rendering + * Copyright (C) 2008 Sven Neumann <sven@gimp.org> + * + * Some code here is based on code from librsvg that was originally + * written by Raph Levien <raph@artofcode.com> 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> + +#include <gtk/gtk.h> + +#include "libgimpmath/gimpmath.h" + +#include "widgets-types.h" + +#include "gimpcairo-wilber.h" + + +static void gimp_cairo_wilber_internal (GtkWidget *widget, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble factor, + gdouble max_eye_angle); +static void gimp_cairo_eyes (GtkWidget *widget, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble factor, + gdouble max_eye_angle); + + +static gboolean pointer_eyes = FALSE; +static GSList *cairo_wilber_widgets = NULL; + + +void +gimp_cairo_wilber_toggle_pointer_eyes (void) +{ + GSList *iter; + + pointer_eyes = ! pointer_eyes; + + for (iter = cairo_wilber_widgets; iter; iter = g_slist_next (iter)) + { + if (pointer_eyes) + g_object_set_data (G_OBJECT (iter->data), "wilber-eyes-state", NULL); + + gtk_widget_queue_draw (GTK_WIDGET (iter->data)); + } +} + +void +gimp_cairo_draw_toolbox_wilber (GtkWidget *widget, + cairo_t *cr) +{ + GtkStyle *style; + GtkStateType state; + GtkAllocation allocation; + gdouble wilber_width; + gdouble wilber_height; + gdouble factor; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cr != NULL); + + style = gtk_widget_get_style (widget); + state = gtk_widget_get_state (widget); + + gtk_widget_get_allocation (widget, &allocation); + + gimp_cairo_wilber_get_size (cr, &wilber_width, &wilber_height); + + factor = allocation.width / wilber_width * 0.9; + + if (! gtk_widget_get_has_window (widget)) + cairo_translate (cr, allocation.x, allocation.y); + + cairo_scale (cr, factor, factor); + + gimp_cairo_wilber_internal (widget, cr, + (allocation.width / factor - wilber_width) / 2.0, + (allocation.height / factor - wilber_height) / 2.0, + factor, 30.0 * G_PI / 180.0); + + cairo_set_source_rgba (cr, + style->fg[state].red / 65535.0, + style->fg[state].green / 65535.0, + style->fg[state].blue / 65535.0, + 0.10); + cairo_fill (cr); +} + +void +gimp_cairo_draw_drop_wilber (GtkWidget *widget, + cairo_t *cr, + gboolean blink) +{ + GtkStyle *style; + GtkStateType state; + GtkAllocation allocation; + gdouble wilber_width; + gdouble wilber_height; + gdouble width; + gdouble height; + gdouble side; + gdouble factor; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (cr != NULL); + + style = gtk_widget_get_style (widget); + state = gtk_widget_get_state (widget); + + gtk_widget_get_allocation (widget, &allocation); + + gimp_cairo_wilber_get_size (cr, &wilber_width, &wilber_height); + + wilber_width /= 2; + wilber_height /= 2; + + side = MIN (MIN (allocation.width, allocation.height), + MAX (allocation.width, allocation.height) / 2); + + width = MAX (wilber_width, side); + height = MAX (wilber_height, side); + + factor = MIN (width / wilber_width, height / wilber_height); + + if (! gtk_widget_get_has_window (widget)) + cairo_translate (cr, allocation.x, allocation.y); + + cairo_scale (cr, factor, factor); + + /* magic factors depend on the image used, everything else is generic + */ + gimp_cairo_wilber_internal (widget, cr, + - wilber_width * 0.6, + allocation.height / factor - wilber_height * 1.1, + factor, 50.0 * G_PI / 180.0); + + cairo_set_source_rgba (cr, + style->fg[state].red / 65535.0, + style->fg[state].green / 65535.0, + style->fg[state].blue / 65535.0, + 0.15); + cairo_fill (cr); + + if (blink) + { + gimp_cairo_eyes (widget, cr, + - wilber_width * 0.6, + allocation.height / factor - wilber_height * 1.1, + factor, 50.0 * G_PI / 180.0); + + cairo_set_source_rgba (cr, + style->fg[state].red / 65535.0, + 0, + 0, + 1.0); + cairo_fill (cr); + } +} + + +/* 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 "; + +static const gchar eyes_path[] = + "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 cairo_path_t *wilber_cairo_path = NULL; +static gdouble wilber_x1, wilber_y1; +static gdouble wilber_x2, wilber_y2; + +static cairo_path_t *eyes_cairo_path = NULL; +static gdouble eyes_x1, eyes_y1; +static gdouble eyes_x2, eyes_y2; + + +static void parse_path_data (cairo_t *cr, + const gchar *data); +static void wilber_get_extents (cairo_t *cr); +static void eyes_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) +{ + gimp_cairo_wilber_internal (NULL, cr, x, y, 1.0, 0.0); +} + +static void +gimp_cairo_wilber_weak_notify (gpointer data, + GObject *widget) +{ + cairo_wilber_widgets = g_slist_remove (cairo_wilber_widgets, widget); +} + +static void +gimp_cairo_wilber_internal (GtkWidget *widget, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble factor, + gdouble max_eye_angle) +{ + wilber_get_extents (cr); + + cairo_save (cr); + + cairo_translate (cr, x - wilber_x1, y - wilber_y1); + cairo_append_path (cr, wilber_cairo_path); + + cairo_restore (cr); + + gimp_cairo_eyes (widget, cr, x, y, factor, max_eye_angle); + + if (widget && ! g_slist_find (cairo_wilber_widgets, widget)) + { + cairo_wilber_widgets = g_slist_prepend (cairo_wilber_widgets, widget); + + g_object_weak_ref (G_OBJECT (widget), + gimp_cairo_wilber_weak_notify, NULL); + } +} + +typedef struct +{ + gdouble x; + gdouble y; + gdouble radius; + + gdouble a; + gdouble b; + gdouble r; +} Eye; + +static const Eye eyes[2] = +{ + { .x = (344.151 + 382.496) / 2.0, + .y = (501.128 + 539.473) / 2.0, + .radius = (382.496 - 344.151) / 2.0, + + .a = 25.0 * G_PI / 180.0, + .b = 24.0 * G_PI / 180.0, + .r = 0.475 + }, + + { .x = (385.287 + 442.878) / 2.0, + .y = (491.431 + 549.022) / 2.0, + .radius = (442.878 - 385.287) / 2.0, + + .a = 34.0 * G_PI / 180.0, + .b = 19.0 * G_PI / 180.0, + .r = 0.5 + } +}; + +typedef struct +{ + gdouble a; + gdouble b; +} EyeState; + +typedef struct +{ + EyeState eyes[2]; + gdouble x; + gdouble y; + gdouble factor; + gdouble max_eye_angle; + gdouble t; + gint timeout_id; +} EyesState; + +static EyesState * +eyes_state_new (void) +{ + EyesState *state = g_slice_new0 (EyesState); + gint i; + + for (i = 0; i < 2; i++) + { + state->eyes[i].a = eyes[i].a; + state->eyes[i].b = eyes[i].b; + } + + state->t = (gdouble) g_get_monotonic_time () / G_TIME_SPAN_SECOND; + + return state; +} + +static void +eyes_state_free (EyesState *state) +{ + if (state->timeout_id) + g_source_remove (state->timeout_id); + + g_slice_free (EyesState, state); +} + +static gboolean +gimp_cairo_pointer_eyes_timeout (GtkWidget *widget) +{ + EyesState *state; + gdouble t; + gint pointer_x; + gint pointer_y; + GtkAllocation allocation; + GdkWindow *window; + gint window_x; + gint window_y; + gint redraw = 2; + gint i; + + state = g_object_get_data (G_OBJECT (widget), "wilber-eyes-state"); + + t = (gdouble) g_get_monotonic_time () / G_TIME_SPAN_SECOND; + + gdk_display_get_pointer (gtk_widget_get_display (widget), + NULL, &pointer_x, &pointer_y, NULL); + + gtk_widget_get_allocation (widget, &allocation); + + window = gtk_widget_get_window (widget); + + if (window) + gdk_window_get_origin (window, &window_x, &window_y); + + for (i = 0; i < 2; i++) + { + const Eye *eye = &eyes[i]; + gdouble a; + gdouble b; + gdouble c; + GimpVector3 u; + GimpVector3 v; + GimpVector3 w; + + if (pointer_eyes) + { + gdouble screen_x; + gdouble screen_y; + gdouble z = 220.0 * state->factor; + gdouble d; + + screen_x = (eye->x + state->x - wilber_x1) * state->factor; + screen_y = (eye->y + state->y - wilber_y1) * state->factor; + + if (! gtk_widget_get_has_window (widget)) + { + screen_x += allocation.x; + screen_y += allocation.y; + } + + if (window) + { + screen_x += window_x; + screen_y += window_y; + } + + d = sqrt (SQR (pointer_x - screen_x) + SQR (pointer_y - screen_y)); + a = atan2 (pointer_y - screen_y, pointer_x - screen_x); + b = atan (d / z); + b = MIN (b, state->max_eye_angle); + } + else + { + a = eyes[i].a; + b = eyes[i].b; + } + + if (a == state->eyes[i].a && b == state->eyes[i].b) + { + redraw--; + + continue; + } + + u.x = sin (state->eyes[i].b) * cos (state->eyes[i].a); + u.y = sin (state->eyes[i].b) * sin (state->eyes[i].a); + u.z = cos (state->eyes[i].b); + + v.x = sin (b) * cos (a); + v.y = sin (b) * sin (a); + v.z = cos (b); + + c = acos (gimp_vector3_inner_product (&u, &v)); + + if (c < 1e-2) + { + state->eyes[i].a = a; + state->eyes[i].b = b; + + continue; + } + + c *= 1.0 - exp (-(t - state->t) * 15.0); + + w = gimp_vector3_cross_product (&u, &v); + w = gimp_vector3_cross_product (&w, &u); + gimp_vector3_normalize (&w); + + v.x = u.x * cos (c) + w.x * sin (c); + v.y = u.y * cos (c) + w.y * sin (c); + v.z = u.z * cos (c) + w.z * sin (c); + + a = atan2 (v.y, v.x); + b = acos (v.z); + + state->eyes[i].a = a; + state->eyes[i].b = b; + } + + state->t = t; + + if (redraw) + { + state->timeout_id = 0; + + gtk_widget_queue_draw (widget); + + return G_SOURCE_REMOVE; + } + else if (! pointer_eyes) + { + state->timeout_id = 0; + + g_object_set_data (G_OBJECT (widget), "wilber-eyes-state", NULL); + gtk_widget_queue_draw (widget); + + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +static void +gimp_cairo_pointer_eyes (GtkWidget *widget, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble factor, + gdouble max_eye_angle) +{ + EyesState *state; + gint i; + + state = g_object_get_data (G_OBJECT (widget), "wilber-eyes-state"); + + if (! state) + { + state = eyes_state_new (); + + g_object_set_data_full (G_OBJECT (widget), "wilber-eyes-state", state, + (GDestroyNotify) eyes_state_free); + } + + for (i = 0; i < 2; i++) + { + const Eye *eye = &eyes[i]; + gdouble R = eye->radius; + gdouble r = eye->r * eye->radius; + gint j; + + cairo_save (cr); + + cairo_translate (cr, eye->x, eye->y); + cairo_rotate (cr, state->eyes[i].a); + + for (j = 0; j < 32; j++) + { + gdouble a = -2.0 * G_PI * j / 32.0; + gdouble u = r * cos (a); + gdouble v = r * sin (a); + gdouble w = sqrt (SQR (R) - SQR (v)); + gdouble b = asin (u / w); + + b = CLAMP (b + state->eyes[i].b, -G_PI / 2.0, +G_PI / 2.0); + u = w * sin (b); + + if (j == 0) + cairo_move_to (cr, u, v); + else + cairo_line_to (cr, u, v); + } + + cairo_close_path (cr); + + cairo_restore (cr); + } + + state->x = x; + state->y = y; + state->factor = factor; + state->max_eye_angle = max_eye_angle; + + if (! state->timeout_id) + { + state->timeout_id = + g_timeout_add (17, + (GSourceFunc) gimp_cairo_pointer_eyes_timeout, + widget); + } +} + +static void +gimp_cairo_eyes (GtkWidget *widget, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble factor, + gdouble max_eye_angle) +{ + wilber_get_extents (cr); + eyes_get_extents (cr); + + cairo_save (cr); + + cairo_translate (cr, x - wilber_x1, y - wilber_y1); + if (widget && + (pointer_eyes || + g_object_get_data (G_OBJECT (widget), "wilber-eyes-state"))) + { + gimp_cairo_pointer_eyes (widget, cr, x, y, factor, max_eye_angle); + } + else + { + cairo_append_path (cr, eyes_cairo_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 *unused) +{ + if (! wilber_cairo_path) + { + cairo_surface_t *s = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1); + cairo_t *cr = cairo_create (s); + + parse_path_data (cr, wilber_path); + cairo_fill_extents (cr, &wilber_x1, &wilber_y1, &wilber_x2, &wilber_y2); + + wilber_cairo_path = cairo_copy_path (cr); + + cairo_destroy (cr); + cairo_surface_destroy (s); + } +} + +static void +eyes_get_extents (cairo_t *unused) +{ + if (! eyes_cairo_path) + { + cairo_surface_t *s = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1); + cairo_t *cr = cairo_create (s); + + parse_path_data (cr, eyes_path); + cairo_fill_extents (cr, &eyes_x1, &eyes_y1, &eyes_x2, &eyes_y2); + + eyes_cairo_path = cairo_copy_path (cr); + + cairo_destroy (cr); + cairo_surface_destroy (s); + } +} + +/**********************************************************/ +/* Below is the code that parses the actual path data. */ +/* */ +/* This code is taken from librsvg and was originally */ +/* written by Raph Levien <raph@artofcode.com> 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 +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; + } +} |