static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); WarpPrivate *priv = (WarpPrivate*) o->chant_data; gdouble dist; gdouble stamps; gdouble spacing = MAX (o->size * 0.01, 0.5); /*1% spacing for starters*/ GeglPathPoint prev, next, lerp; gulong i; GeglPathList *event; priv->buffer = gegl_buffer_dup (input); event = gegl_path_get_path (o->stroke); prev = *(event->d.point); while (event->next) { event = event->next; next = *(event->d.point); dist = gegl_path_point_dist (&next, &prev); stamps = dist / spacing; if (stamps < 1) { stamp (o, result, next.x, next.y); prev = next; } else { for (i = 0; i < stamps; i++) { gegl_path_point_lerp (&lerp, &prev, &next, (i * spacing) / dist); stamp (o, result, lerp.x, lerp.y); } prev = lerp; } } /* Affect the output buffer */ gegl_buffer_copy (priv->buffer, result, output, result); gegl_buffer_set_extent (output, gegl_buffer_get_extent (input)); g_object_unref (priv->buffer); /* prepare for the recomputing of the op */ priv->last_point_set = FALSE; return TRUE; }
static gboolean process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); WarpPrivate *priv = (WarpPrivate*) o->user_data; GeglBuffer *input; gdouble spacing = MAX (o->size * o->spacing, 0.5); gdouble dist; gint stamps; gint i; gdouble t; GeglPathPoint prev, next, lerp; GeglPathList *event; WarpPointList *processed_event; WarpPointList **processed_event_ptr; if (!o->stroke || strcmp (output_prop, "output")) return FALSE; /* if the previously processed stroke is valid, the cached buffer can be * passed as output right away. */ if (priv->processed_stroke_valid) { g_assert (priv->buffer != NULL); gegl_operation_context_set_object (context, "output", G_OBJECT (priv->buffer)); return TRUE; } /* ... otherwise, we need to check if the previously processed stroke is an * initial segment of the current stroke ... */ event = gegl_path_get_path (o->stroke); processed_event = priv->processed_stroke; processed_event_ptr = &priv->processed_stroke; while (event && processed_event) { if (event->d.point[0].x != processed_event->point.x || event->d.point[0].y != processed_event->point.y) { break; } processed_event_ptr = &processed_event->next; event = event->next; processed_event = processed_event->next; } /* if the loop stopped before we reached the last event of the processed * stroke, it's not an initial segment, and we need to clear the cache, and * process the entire stroke. */ if (processed_event) { clear_cache (o); event = gegl_path_get_path (o->stroke); processed_event_ptr = &priv->processed_stroke; } /* otherwise, we simply continue processing remaining stroke on top of the * previously processed buffer. */ /* intialize the cached buffer if we don't already have one. */ if (! priv->buffer) { input = GEGL_BUFFER (gegl_operation_context_get_object (context, "input")); priv->buffer = gegl_buffer_dup (input); /* we pass the buffer as output directly while keeping it cached, so mark * it as forked. */ gegl_object_set_has_forked (G_OBJECT (priv->buffer)); } if (event) { /* is this the first event of the stroke? */ if (! priv->processed_stroke) { prev = *(event->d.point); priv->last_x = prev.x; priv->last_y = prev.y; } else { prev.x = priv->last_x; prev.y = priv->last_y; } for (; event; event = event->next) { next = *(event->d.point); dist = gegl_path_point_dist (&next, &prev); stamps = floor (dist / spacing) + 1; /* stroke the current segment, such that there's always a stamp at * its final endpoint, and at positive integer multiples of * `spacing` away from it. */ if (stamps == 1) { stamp (o, next.x, next.y); } else { for (i = 0; i < stamps; i++) { t = 1.0 - ((stamps - i - 1) * spacing) / dist; gegl_path_point_lerp (&lerp, &prev, &next, t); stamp (o, lerp.x, lerp.y); } } prev = next; /* append the current event to the processed path. */ processed_event = g_slice_new (WarpPointList); processed_event->point = next; *processed_event_ptr = processed_event; processed_event_ptr = &processed_event->next; } *processed_event_ptr = NULL; } priv->processed_stroke_valid = TRUE; /* pass the processed buffer as output */ gegl_operation_context_set_object (context, "output", G_OBJECT (priv->buffer)); return TRUE; }