static gboolean process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_pad, const GeglRectangle *result, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); if (o->buffer) { g_object_ref (o->buffer); /* Add an extra reference, since * gegl_operation_set_data is * stealing one. */ /* override core behaviour, by resetting the buffer in the operation_context */ gegl_operation_context_take_object (context, "output", G_OBJECT (o->buffer)); /* mark that this buffer should not be used for in-place * processing. */ gegl_object_set_has_forked (G_OBJECT (o->buffer)); } return TRUE; }
GeglBuffer * gegl_graph_get_shared_empty (GeglGraphTraversal *path) { if (!path->shared_empty) { path->shared_empty = gegl_buffer_new_ram (GEGL_RECTANGLE (0, 0, 0, 0), gegl_babl_rgba_linear_float ()); gegl_object_set_has_forked (G_OBJECT (path->shared_empty)); } return path->shared_empty; }
static gboolean gegl_crop_process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); GeglBuffer *input; gboolean success = FALSE; GeglRectangle extent; extent.x = o->x; extent.y = o->y; extent.width = o->width; extent.height = o->height; input = gegl_operation_context_get_source (context, "input"); if (input) { GeglBuffer *output = gegl_buffer_create_sub_buffer (input, &extent); if (gegl_object_get_has_forked (G_OBJECT (input))) gegl_object_set_has_forked (G_OBJECT (output)); gegl_operation_context_take_object (context, "output", G_OBJECT (output)); g_object_unref (input); success = TRUE; } else { g_warning ("%s got NULL input pad", gegl_node_get_operation (operation->node)); } return success; }
/* this is the visitor that does the real computations for GEGL */ static void gegl_eval_visitor_visit_pad (GeglVisitor *self, GeglPad *pad) { GeglNode *node = gegl_pad_get_node (pad); gpointer context_id = self->context_id; GeglOperationContext *context = gegl_node_get_context (node, context_id); GeglOperation *operation = node->operation; GEGL_VISITOR_CLASS (gegl_eval_visitor_parent_class)->visit_pad (self, pad); if (gegl_pad_is_output (pad)) { /* processing only really happens for output pads */ if (context->cached) { GEGL_NOTE (GEGL_DEBUG_PROCESS, "Using cache for pad '%s' on \"%s\"", gegl_pad_get_name (pad), gegl_node_get_debug_name (node)); gegl_operation_context_set_object (context, gegl_pad_get_name (pad), G_OBJECT (node->cache)); } else { glong time = gegl_ticks (); /* Make the operation do it's actual processing */ GEGL_NOTE (GEGL_DEBUG_PROCESS, "For \"%s\" processing pad '%s' result_rect = %d, %d %d×%d", gegl_pad_get_name (pad), gegl_node_get_debug_name (node), context->result_rect.x, context->result_rect.y, context->result_rect.width, context->result_rect.height); gegl_operation_process (operation, context, gegl_pad_get_name (pad), &context->result_rect); time = gegl_ticks () - time; gegl_instrument ("process", gegl_node_get_operation (node), time); if (gegl_pad_get_num_connections (pad) > 1) { /* Mark buffers that have been consumed by different parts of the * graph so that in-place processing can be avoided on them. */ GValue *value; GeglBuffer *buffer; value = gegl_operation_context_get_value (context, gegl_pad_get_name (pad)); if (value) { buffer = g_value_get_object (value); if (buffer) gegl_object_set_has_forked (buffer); } } } } else if (gegl_pad_is_input (pad)) { GeglPad *source_pad = gegl_pad_get_connected_to (pad); /* the work needed to be done on input pads is to set the * data from the corresponding output pad it is connected to */ if (source_pad) { GValue value = { 0 }; GParamSpec *prop_spec = gegl_pad_get_param_spec (pad); GeglNode *source_node = gegl_pad_get_node (source_pad); GeglOperationContext *source_context = gegl_node_get_context (source_node, context_id); g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec)); gegl_operation_context_get_property (source_context, gegl_pad_get_name (source_pad), &value); if (!g_value_get_object (&value) && !g_object_get_data (G_OBJECT (source_node), "graph")) g_warning ("eval-visitor encountered a NULL buffer passed from: %s.%s-[%p]", gegl_node_get_debug_name (source_node), gegl_pad_get_name (source_pad), g_value_get_object (&value)); gegl_operation_context_set_property (context, gegl_pad_get_name (pad), &value); /* reference counting for this source dropped to zero, freeing up */ if (-- gegl_node_get_context ( gegl_pad_get_node (source_pad), context_id)->refs == 0 && g_value_get_object (&value)) { gegl_operation_context_remove_property ( gegl_node_get_context ( gegl_pad_get_node (source_pad), context_id), gegl_pad_get_name (source_pad)); } g_value_unset (&value); /* processing for sink operations that accepts partial consumption and thus probably are being processed by the processor from the this very operation. */ if (GEGL_IS_OPERATION_SINK (operation) && !gegl_operation_sink_needs_full (operation)) { GEGL_NOTE (GEGL_DEBUG_PROCESS, "Processing pad '%s' on \"%s\"", gegl_pad_get_name (pad), gegl_node_get_debug_name (node)); gegl_operation_process (operation, context, "output", &context->result_rect); } } } }
static gboolean gegl_affine_process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result) { GeglBuffer *input; GeglBuffer *output; GeglMatrix3 matrix; OpAffine *affine = (OpAffine *) operation; gegl_affine_create_composite_matrix (affine, &matrix); if (gegl_affine_is_intermediate_node (affine) || gegl_matrix3_is_identity (&matrix)) { /* passing straight through (like gegl:nop) */ input = gegl_operation_context_get_source (context, "input"); if (!input) { g_warning ("transform received NULL input"); return FALSE; } gegl_operation_context_take_object (context, "output", G_OBJECT (input)); } else if (gegl_affine_matrix3_allow_fast_translate (&matrix) || (gegl_matrix3_is_translate (&matrix) && ! strcmp (affine->filter, "nearest"))) { /* doing a buffer shifting trick, (enhanced nop) */ input = gegl_operation_context_get_source (context, "input"); output = g_object_new (GEGL_TYPE_BUFFER, "source", input, "shift-x", (int)-matrix.coeff[0][2], "shift-y", (int)-matrix.coeff[1][2], "abyss-width", -1, /* turn of abyss (relying on abyss of source) */ NULL); if (gegl_object_get_has_forked (input)) gegl_object_set_has_forked (output); gegl_operation_context_take_object (context, "output", G_OBJECT (output)); if (input != NULL) g_object_unref (input); } else if (gegl_affine_matrix3_allow_fast_reflect_x (&matrix)) { GeglRectangle src_rect; GeglSampler *sampler; GeglRectangle context_rect; input = gegl_operation_context_get_source (context, "input"); if (!input) { g_warning ("transform received NULL input"); return FALSE; } output = gegl_operation_context_get_target (context, "output"); src_rect = gegl_operation_get_required_for_output (operation, "output", result); src_rect.y += 1; sampler = gegl_buffer_sampler_new (input, babl_format("RaGaBaA float"), gegl_sampler_type_from_string (affine->filter)); context_rect = *gegl_sampler_get_context_rect (sampler); src_rect.width -= context_rect.width; src_rect.height -= context_rect.height; gegl_affine_fast_reflect_x (output, input, result, &src_rect); g_object_unref (sampler); if (input != NULL) g_object_unref (input); } else if (gegl_affine_matrix3_allow_fast_reflect_y (&matrix)) { GeglRectangle src_rect; GeglSampler *sampler; GeglRectangle context_rect; input = gegl_operation_context_get_source (context, "input"); if (!input) { g_warning ("transform received NULL input"); return FALSE; } output = gegl_operation_context_get_target (context, "output"); src_rect = gegl_operation_get_required_for_output (operation, "output", result); src_rect.x += 1; sampler = gegl_buffer_sampler_new (input, babl_format("RaGaBaA float"), gegl_sampler_type_from_string (affine->filter)); context_rect = *gegl_sampler_get_context_rect (sampler); src_rect.width -= context_rect.width; src_rect.height -= context_rect.height; gegl_affine_fast_reflect_y (output, input, result, &src_rect); g_object_unref (sampler); if (input != NULL) g_object_unref (input); } else { /* for all other cases, do a proper resampling */ GeglSampler *sampler; input = gegl_operation_context_get_source (context, "input"); output = gegl_operation_context_get_target (context, "output"); sampler = gegl_buffer_sampler_new (input, babl_format("RaGaBaA float"), gegl_sampler_type_from_string (affine->filter)); affine_generic (output, input, &matrix, sampler); g_object_unref (sampler); if (input != NULL) g_object_unref (input); } return TRUE; }
/** * gegl_graph_process: * @path: The traversal path * * Process the prepared request. This will return the * resulting buffer from the final node, or NULL if * that node is a sink. * * If gegl_graph_prepare_request has not been called * the behavior of this function is undefined. * * Return value: (transfer full): The result of the graph, or NULL if * there is no output pad. */ GeglBuffer * gegl_graph_process (GeglGraphTraversal *path, gint level) { GList *list_iter = NULL; GeglBuffer *result = NULL; GeglOperationContext *context = NULL; GeglOperationContext *last_context = NULL; GeglBuffer *operation_result = NULL; for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next) { GeglNode *node = GEGL_NODE (list_iter->data); GeglOperation *operation = node->operation; g_return_val_if_fail (node, NULL); g_return_val_if_fail (operation, NULL); GEGL_INSTRUMENT_START(); operation_result = NULL; if (last_context) gegl_operation_context_purge (last_context); context = g_hash_table_lookup (path->contexts, node); g_return_val_if_fail (context, NULL); GEGL_NOTE (GEGL_DEBUG_PROCESS, "Will process %s result_rect = %d, %d %d×%d", gegl_node_get_debug_name (node), context->result_rect.x, context->result_rect.y, context->result_rect.width, context->result_rect.height); if (context->need_rect.width > 0 && context->need_rect.height > 0) { if (context->cached) { GEGL_NOTE (GEGL_DEBUG_PROCESS, "Using cached result for %s", gegl_node_get_debug_name (node)); operation_result = GEGL_BUFFER (node->cache); } else { /* provide something on input pad, always - this makes having behavior depending on it not being set.. not work, is sacrifising that worth it? */ if (gegl_node_has_pad (node, "input") && !gegl_operation_context_get_object (context, "input")) { gegl_operation_context_set_object (context, "input", G_OBJECT (gegl_graph_get_shared_empty(path))); } context->level = level; /* note: this hard-coding of "output" makes some more custom * graph topologies harder than neccesary. */ gegl_operation_process (operation, context, "output", &context->need_rect, context->level); operation_result = GEGL_BUFFER (gegl_operation_context_get_object (context, "output")); if (operation_result && operation_result == (GeglBuffer *)operation->node->cache) gegl_cache_computed (operation->node->cache, &context->need_rect, level); } } else { operation_result = NULL; } if (operation_result) { GeglPad *output_pad = gegl_node_get_pad (node, "output"); GList *targets = gegl_graph_get_connected_output_contexts (path, output_pad); GList *targets_iter; GEGL_NOTE (GEGL_DEBUG_PROCESS, "Will deliver the results of %s:%s to %d targets", gegl_node_get_debug_name (node), "output", g_list_length (targets)); if (g_list_length (targets) > 1) gegl_object_set_has_forked (G_OBJECT (operation_result)); for (targets_iter = targets; targets_iter; targets_iter = g_list_next (targets_iter)) { ContextConnection *target_con = targets_iter->data; gegl_operation_context_set_object (target_con->context, target_con->name, G_OBJECT (operation_result)); } g_list_free_full (targets, free_context_connection); } last_context = context; GEGL_INSTRUMENT_END ("process", gegl_node_get_operation (node)); } if (last_context) { if (operation_result) result = g_object_ref (operation_result); else if (gegl_node_has_pad (last_context->operation->node, "output")) result = g_object_ref (gegl_graph_get_shared_empty (path)); gegl_operation_context_purge (last_context); } return result; }
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; }