static void _gegl_graph_do_build (GeglGraphTraversal *path, GeglNode *node) { GeglPad *pad = NULL; GeglListVisitor *list_visitor = g_object_new (GEGL_TYPE_LIST_VISITOR, NULL); /* We need to check the real node of the output/input pad in case this is a proxy node */ pad = gegl_node_get_pad (node, "output"); if (pad) { node = gegl_pad_get_node (pad); } else { pad = gegl_node_get_pad (node, "input"); if (pad) node = gegl_pad_get_node (pad); } path->dfs_path = gegl_list_visitor_get_dfs_path (list_visitor, GEGL_VISITABLE (node)); path->bfs_path = gegl_list_visitor_get_bfs_path (list_visitor, GEGL_VISITABLE (node)); path->contexts = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)gegl_operation_context_destroy); path->rects_dirty = FALSE; g_object_unref (list_visitor); }
GeglNode * gegl_operation_get_source_node (GeglOperation *operation, const gchar *input_pad_name) { GeglPad *pad; g_assert (operation && operation->node && input_pad_name); pad = gegl_node_get_pad (operation->node, input_pad_name); if (!pad) return NULL; pad = gegl_pad_get_connected_to (pad); if (!pad) return NULL; g_assert (gegl_pad_get_node (pad)); return gegl_pad_get_node (pad); }
/* 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); } } } }
void gegl_graph_prepare_request (GeglGraphTraversal *path, const GeglRectangle *request_roi, gint level) { GList *list_iter = NULL; static const GeglRectangle empty_rect = {0, 0, 0, 0}; g_return_if_fail (path->bfs_path); if (path->rects_dirty) { /* Zero all the needs rects so we can intersect with them below */ for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next) { GeglNode *node = GEGL_NODE (list_iter->data); GeglOperationContext *context = g_hash_table_lookup (path->contexts, node); /* We only need to reset the need rect, result will always get overwritten */ gegl_operation_context_set_need_rect (context, &empty_rect); /* Reset cached status, because the rect we need may have changed */ context->cached = FALSE; } } path->rects_dirty = TRUE; { /* Prep the first node */ GeglNode *node = GEGL_NODE (path->bfs_path->data); GeglOperationContext *context = g_hash_table_lookup (path->contexts, node); GeglRectangle new_need; g_return_if_fail (context); gegl_rectangle_intersect (&new_need, &node->have_rect, request_roi); gegl_operation_context_set_need_rect (context, &new_need); gegl_operation_context_set_result_rect (context, &new_need); } /* Iterate over all the nodes and propagate the requested rectangle */ for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next) { GeglNode *node = GEGL_NODE (list_iter->data); GeglOperation *operation = node->operation; GeglOperationContext *context; GeglRectangle *request; GSList *input_pads; context = g_hash_table_lookup (path->contexts, node); g_return_if_fail (context); request = gegl_operation_context_get_need_rect (context); if (request->width == 0 || request->height == 0) { gegl_operation_context_set_result_rect (context, &empty_rect); continue; } if (node->cache) { gint i; for (i = level; i >=0 && !context->cached; i--) { if (gegl_region_rect_in (node->cache->valid_region[level], request) == GEGL_OVERLAP_RECTANGLE_IN) { /* This node is cached and the cache fulfills our need rect */ context->cached = TRUE; gegl_operation_context_set_result_rect (context, &empty_rect); } } if (context->cached) continue; } { /* Expand request if the operation has a minimum processing requirement */ GeglRectangle full_request = gegl_operation_get_cached_region (operation, request); gegl_operation_context_set_need_rect (context, &full_request); /* FIXME: We could trim this down based on the cache, instead of being all or nothing */ gegl_operation_context_set_result_rect (context, request); for (input_pads = node->input_pads; input_pads; input_pads = input_pads->next) { GeglPad *source_pad = gegl_pad_get_connected_to (input_pads->data); if (source_pad) { GeglNode *source_node = gegl_pad_get_node (source_pad); GeglOperationContext *source_context = g_hash_table_lookup (path->contexts, source_node); const gchar *pad_name = gegl_pad_get_name (input_pads->data); GeglRectangle rect, current_need, new_need; /* Combine this need rect with any existing request */ rect = gegl_operation_get_required_for_output (operation, pad_name, &full_request); current_need = *gegl_operation_context_get_need_rect (source_context); gegl_rectangle_bounding_box (&new_need, &rect, ¤t_need); /* Limit request to the nodes output */ gegl_rectangle_intersect (&new_need, &source_node->have_rect, &new_need); gegl_operation_context_set_need_rect (source_context, &new_need); } } } } }