void layer_add_gegl_node(GeglEditorLayer* layer, GeglNode* node) { //get input pads //gegl_pad_is_output GSList *pads = gegl_node_get_input_pads(node); guint num_inputs = g_slist_length(pads); gchar** inputs = malloc(sizeof(gchar*)*num_inputs); int i; for(i = 0; pads != NULL; pads = pads->next, i++) { inputs[i] = (gchar*)gegl_pad_get_name(pads->data); } gint id; if(gegl_node_get_pad(node, "output") == NULL) { id = gegl_editor_add_node(layer->editor, gegl_node_get_operation(node), num_inputs, inputs, 0, NULL); } else { gchar* output = "output"; gchar* outputs[] = {output}; id = gegl_editor_add_node(layer->editor, gegl_node_get_operation(node), num_inputs, inputs, 1, outputs); } node_id_pair* new_pair = malloc(sizeof(node_id_pair)); new_pair->node = node; new_pair->id = id; layer->pairs = g_slist_append(layer->pairs, new_pair); }
gint layer_connected_pads (gpointer host, GeglEditor* editor, gint from, const gchar* output, gint to, const gchar* input) { GeglEditorLayer* self = (GeglEditorLayer*)host; GeglNode* from_node = NULL; GeglNode* to_node = NULL; GSList* pair = self->pairs; for(;pair != NULL; pair = pair->next) { node_id_pair* data = pair->data; if(data->id == from) from_node = data->node; if(data->id == to) to_node = data->node; if(from_node != NULL && to_node != NULL) break; } g_assert(from_node != NULL && to_node != NULL); g_assert(from_node != to_node); gboolean success = gegl_node_connect_to(from_node, output, to_node, input); g_print("connected (%d): %s(%s) to %s(%s), %i\n", success, gegl_node_get_operation(from_node), output, gegl_node_get_operation(to_node), input, success); refresh_images(self); }
static void prepare_cl (GeglOperation *operation) { GeglOperationAreaFilter* op_area = GEGL_OPERATION_AREA_FILTER (operation); GeglChantO* o = GEGL_CHANT_PROPERTIES (operation); gdouble theta = o->angle * G_PI / 180.0; gdouble offset_x = fabs(o->length * cos(theta)); gdouble offset_y = fabs(o->length * sin(theta)); op_area->left = op_area->right = (gint)ceil(0.5 * offset_x); op_area->top = op_area->bottom = (gint)ceil(0.5 * offset_y); GeglNode * self; GeglPad *pad; Babl * format=babl_format ("RaGaBaA float"); self=gegl_operation_get_source_node(operation,"input"); while(self) { if(strcmp(gegl_node_get_operation(self),"gimp:tilemanager-source")==0) { format=gegl_operation_get_format(self->operation,"output"); break; } self=gegl_operation_get_source_node(self->operation,"input"); } gegl_operation_set_format (operation, "output", format); }
gboolean gimp_warp_tool_undo (GimpTool *tool, GimpDisplay *display) { GimpWarpTool *wt = GIMP_WARP_TOOL (tool); GeglNode *to_delete; GeglNode *previous; const gchar *type; if (! wt->render_node) return FALSE; to_delete = gegl_node_get_producer (wt->render_node, "aux", NULL); type = gegl_node_get_operation (to_delete); if (strcmp (type, "gegl:warp")) return FALSE; previous = gegl_node_get_producer (to_delete, "input", NULL); gegl_node_disconnect (to_delete, "input"); gegl_node_connect_to (previous, "output", wt->render_node, "aux"); wt->redo_stack = g_list_prepend (wt->redo_stack, g_object_ref (to_delete)); gegl_node_remove_child (wt->graph, to_delete); gimp_warp_tool_update_stroke (wt, to_delete); return TRUE; }
static void print_info(GeglNode* gegl) { GSList *list = gegl_node_get_children(gegl); for(;list != NULL; list = list->next) { GeglNode* node = GEGL_NODE(list->data); g_print("Node %s\n", gegl_node_get_operation(node)); if(gegl_node_get_pad(node, "output") == NULL) { g_print("Output pad is NULL\n"); } /* GeglNode** nodes; const gchar** pads; gint num = gegl_node_get_consumers(node, "output", &nodes, &pads); g_print("%s: %d consumer(s)\n", gegl_node_get_operation(node), num); int i; for(i = 0; i < num; i++) { g_print("Connection: (%s to %s)\n", gegl_node_get_operation(node), gegl_node_get_operation(nodes[0]), pads[0]); } g_print("\n");*/ } }
/* we replicate the process function from GeglOperationComposer3 to be * able to bail out earlier for some common processing time pitfalls */ static gboolean gegl_operation_composer3_process2 (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result, gint level) { GeglOperationComposer3Class *klass = GEGL_OPERATION_COMPOSER3_GET_CLASS (operation); GeglBuffer *input; GeglBuffer *aux; GeglBuffer *aux2; GeglBuffer *output; gboolean success = FALSE; if (strcmp (output_prop, "output")) { g_warning ("requested processing of %s pad on a composer", output_prop); return FALSE; } input = gegl_operation_context_get_source (context, "input"); aux = gegl_operation_context_get_source (context, "aux"); aux2 = gegl_operation_context_get_source (context, "aux2"); /* we could be even faster by not alway writing to this buffer, that * would potentially break other assumptions we want to make from the * GEGL core so we avoid doing that */ output = gegl_operation_context_get_target (context, "output"); if (input != NULL || aux != NULL || aux2 != NULL) { if (result->width == 0 || result->height == 0) success = TRUE; else success = klass->process (operation, input, aux, aux2, output, result, level); if (input) g_object_unref (input); if (aux) g_object_unref (aux); if (aux2) g_object_unref (aux2); } else { g_warning ("%s received NULL input, aux, and aux2", gegl_node_get_operation (operation->node)); } return success; }
void layer_set_graph(GeglEditorLayer* self, GeglNode* gegl) { //properly dispose of old gegl graph self->gegl = gegl; gegl_editor_remove_all_nodes(self->editor); GSList *list = gegl_node_get_children(gegl); for(;list != NULL; list = list->next) { GeglNode* node = GEGL_NODE(list->data); g_print("Loading %s\n", gegl_node_get_operation(node)); layer_add_gegl_node(self, node); } list = gegl_node_get_children(gegl); for(list = g_slist_reverse(list); list != NULL; list = list->next) { GeglNode* node = GEGL_NODE(list->data); gint from = get_editor_node_id(self, node); GeglNode** nodes; const gchar** pads; if(!gegl_node_has_pad(node, "output")) { break;} gint num = gegl_node_get_consumers(node, "output", &nodes, &pads); int i; g_print("%s: %d consumer(s)\n", gegl_node_get_operation(node), num); for(i = 0; i < num; i++) { gint to = get_editor_node_id(self, nodes[i]); g_print("Connecting to consumer (%s to %s): output->%s\n", gegl_node_get_operation(node), gegl_node_get_operation(nodes[0]), pads[0]); gegl_editor_add_connection(self->editor, from, to, "output", pads[0]); } } }
/* sets up the node's bounding box */ static void gegl_have_visitor_visit_node (GeglVisitor *self, GeglNode *node) { GeglOperation *operation; glong time = gegl_ticks (); GEGL_VISITOR_CLASS (gegl_have_visitor_parent_class)->visit_node (self, node); if (!node) return; operation = node->operation; g_mutex_lock (node->mutex); node->have_rect = gegl_operation_get_bounding_box (operation); GEGL_NOTE (GEGL_DEBUG_PROCESS, "For \"%s\" have_rect = %d,%d %d×%d", gegl_node_get_debug_name (node), node->have_rect.x, node->have_rect.y, node->have_rect.width, node->have_rect.height); g_mutex_unlock (node->mutex); time = gegl_ticks () - time; gegl_instrument ("process", gegl_node_get_operation (node), time); gegl_instrument (gegl_node_get_operation (node), "defined-region", time); }
void refresh_images(GeglEditorLayer* self) { return; GSList* pair = self->pairs; for(;pair != NULL; pair = pair->next) { node_id_pair *data = pair->data; /* if(node->image != NULL) cairo_surface_destroy(node->image); //TODO: only destory if it has changed*/ const Babl *cairo_argb32 = babl_format("cairo-ARGB32"); const GeglRectangle roi = gegl_node_get_bounding_box(GEGL_NODE(data->node)); g_print("Rect: %dx%d\n", roi.x, roi.y); if(roi.width == 0 || roi.height == 0) { g_print("Empty rectangle: %s\n", gegl_node_get_operation(GEGL_NODE(data->node))); continue; //skip } gegl_editor_show_node_image(self->editor, data->id); gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, roi.width); guchar* buf = malloc(stride*roi.height); //make buffer in memory gegl_node_blit(GEGL_NODE(data->node), 1.0, &roi, cairo_argb32, buf, GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_CACHE); cairo_surface_t* image = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, roi.width, roi.height, stride); // free(buf); gegl_editor_set_node_image(self->editor, data->id, image); } }
const gchar * gimp_warp_tool_get_undo_desc (GimpTool *tool, GimpDisplay *display) { GimpWarpTool *wt = GIMP_WARP_TOOL (tool); GeglNode *to_delete; const gchar *type; if (! wt->render_node) return NULL; to_delete = gegl_node_get_producer (wt->render_node, "aux", NULL); type = gegl_node_get_operation (to_delete); if (strcmp (type, "gegl:warp")) return NULL; return _("Warp Tool Stroke"); }
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_operation_composer3_process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result, gint level) { GeglOperationComposer3Class *klass = GEGL_OPERATION_COMPOSER3_GET_CLASS (operation); GeglBuffer *input; GeglBuffer *aux; GeglBuffer *aux2; GeglBuffer *output; gboolean success = FALSE; if (strcmp (output_prop, "output")) { g_warning ("requested processing of %s pad on a composer", output_prop); return FALSE; } if (result->width == 0 || result->height == 0) { output = gegl_operation_context_get_target (context, "output"); return TRUE; } input = gegl_operation_context_get_source (context, "input"); output = gegl_operation_context_get_output_maybe_in_place (operation, context, input, result); aux = gegl_operation_context_get_source (context, "aux"); aux2 = gegl_operation_context_get_source (context, "aux2"); /* A composer with a NULL aux, can still be valid, the * subclass has to handle it. */ if (input != NULL || aux != NULL || aux2 != NULL) { if (gegl_operation_use_threading (operation, result)) { gint threads = gegl_config_threads (); GThreadPool *pool = thread_pool (); ThreadData thread_data[GEGL_MAX_THREADS]; gint pending = threads; if (result->width > result->height) { gint bit = result->width / threads; for (gint j = 0; j < threads; j++) { thread_data[j].roi.y = result->y; thread_data[j].roi.height = result->height; thread_data[j].roi.x = result->x + bit * j; thread_data[j].roi.width = bit; } thread_data[threads-1].roi.width = result->width - (bit * (threads-1)); } else { gint bit = result->height / threads; for (gint j = 0; j < threads; j++) { thread_data[j].roi.x = result->x; thread_data[j].roi.width = result->width; thread_data[j].roi.y = result->y + bit * j; thread_data[j].roi.height = bit; } thread_data[threads-1].roi.height = result->height - (bit * (threads-1)); } for (gint i = 0; i < threads; i++) { thread_data[i].klass = klass; thread_data[i].operation = operation; thread_data[i].input = input; thread_data[i].aux = aux; thread_data[i].aux2 = aux2; thread_data[i].output = output; thread_data[i].pending = &pending; thread_data[i].level = level; thread_data[i].success = TRUE; } for (gint i = 1; i < threads; i++) g_thread_pool_push (pool, &thread_data[i], NULL); thread_process (&thread_data[0], NULL); while (g_atomic_int_get (&pending)) {}; success = thread_data[0].success; } else { success = klass->process (operation, input, aux, aux2, output, result, level); } g_clear_object (&input); g_clear_object (&aux); g_clear_object (&aux2); } else { g_warning ("%s received NULL input, aux, and aux2", gegl_node_get_operation (operation->node)); } return success; }
void gegl_dot_util_add_node (GString *string, GeglNode *node) { g_string_append_printf (string, "op_%p [fontsize=\"10\" label=\"", node); /* We build the record from top to bottom */ g_string_append_printf (string, "{"); /* The first row is a list of output pads */ { GSList *pads = gegl_node_get_pads (node); GSList *entry = pads; gboolean got_output = FALSE; g_string_append_printf (string, "{"); while (entry) { GeglPad *pad = entry->data; if (gegl_pad_is_output (pad)) { if (got_output) { g_string_append (string, "|"); } got_output = TRUE; g_string_append_printf (string, "<%s>%s", gegl_pad_get_name (pad), gegl_pad_get_name (pad)); } entry = g_slist_next (entry); } g_string_append_printf (string, "}|"); } /* The second row is the operation name such as gegl:translate */ g_string_append_printf (string, "%s |", gegl_node_get_debug_name (node)); /* The next rows are property names and their values */ if (1) { guint n_properties; GParamSpec **properties = gegl_operation_list_properties (gegl_node_get_operation (node), &n_properties); guint i; for (i = 0; i < n_properties; i++) { const gchar *name = properties[i]->name; GValue tvalue = { 0, }; GValue svalue = { 0, }; if (properties[i]->value_type == GEGL_TYPE_BUFFER) continue; g_value_init (&svalue, G_TYPE_STRING); g_value_init (&tvalue, properties[i]->value_type); gegl_node_get_property (node, name, &tvalue); if (g_value_transform (&tvalue, &svalue)) { gchar *sval = g_value_dup_string (&svalue); if (sval && strlen (sval) > 30) { sval[28] = '.'; sval[29] = '.'; sval[30] = '\0'; } if (sval) { g_string_append_printf (string, "%s=%s | ", name, sval); g_free (sval); } g_value_unset (&svalue); } g_value_unset (&tvalue); } g_free (properties); } /* The last row is input pads */ { GSList *pads = gegl_node_get_pads (node); GSList *entry = pads; gboolean got_input = FALSE; g_string_append_printf (string, "{"); while (entry) { GeglPad *pad = entry->data; if (gegl_pad_is_input (pad)) { if (got_input) { g_string_append (string, "|"); } got_input = TRUE; g_string_append_printf (string, "<%s>%s", gegl_pad_get_name (pad), gegl_pad_get_name (pad)); } entry = g_slist_next (entry); } g_string_append_printf (string, "}"); } g_string_append_printf (string, "}\""); g_string_append_printf (string, "shape=\"record\"];\n"); }
/** * 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; }
gint layer_node_selected (gpointer host, GeglEditor* editor, gint node_id) { GeglEditorLayer* self = (GeglEditorLayer*)host; GeglNode* node = NULL; GSList* pair = self->pairs; for(;pair != NULL; pair = pair->next) { node_id_pair* data = pair->data; if(data->id == node_id) { node = data->node; break; } } g_assert(node != NULL); GeglNode** nodes; const gchar** pads; gint num = gegl_node_get_consumers(node, "output", &nodes, &pads); int i; g_print("%s: %d consumer(s)\n", gegl_node_get_operation(node), num); for(i = 0; i < num; i++) { g_print("Connection: (%s to %s)\n", gegl_node_get_operation(node), gegl_node_get_operation(nodes[0]), pads[0]); } g_print("Input from: %s\n", gegl_node_get_operation(gegl_node_get_producer(node, "input", NULL))); // g_print("selected: %s\n", gegl_node_get_operation(node)); guint n_props; GParamSpec** properties = gegl_operation_list_properties(gegl_node_get_operation(node), &n_props); //TODO: only create enough columns for the properties which will actually be included (i.e. ignoring GeglBuffer props) GtkTable *prop_table = GTK_TABLE(gtk_table_new(2, n_props, FALSE)); int d; for(d = 0, i = 0; i < n_props; i++, d++) { GParamSpec* prop = properties[i]; GType type = prop->value_type; const gchar* name = prop->name; GtkWidget* name_label = gtk_label_new(name); gtk_misc_set_alignment(GTK_MISC(name_label), 0, 0.5); GtkWidget* value_entry = gtk_entry_new(); gchar buf[256] = "*"; //can probably be smaller; In fact, can probably do this without sprintf and a buffer. TODO: look at g_string gint i_value; gdouble d_value; gchar* str_value; gboolean skip = FALSE; switch(type) { case G_TYPE_INT: gegl_node_get(node, name, &i_value, NULL); sprintf(buf, "%d", i_value); break; case G_TYPE_DOUBLE: gegl_node_get(node, name, &d_value, NULL); sprintf(buf, "%.3f", d_value); break; case G_TYPE_STRING: gegl_node_get(node, name, &str_value, NULL); sprintf(buf, "%s", str_value); break; } if(type == GEGL_TYPE_BUFFER) { skip = TRUE; d--; } else if( type == GEGL_TYPE_COLOR) { skip = TRUE; GtkWidget *color_button = gtk_button_new_with_label("Select"); select_color_info* info = malloc(sizeof(select_color_info)); info->node = node; info->property = name; info->layer = self; g_signal_connect(color_button, "clicked", (GCallback)select_color, info); gtk_table_attach(prop_table, name_label, 0, 1, d, d+1, GTK_FILL, GTK_FILL, 1, 1); gtk_table_attach(prop_table, color_button, 1, 2, d, d+1, GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 1, 1); } if(!skip) { gtk_entry_set_text(GTK_ENTRY(value_entry), buf); gtk_entry_set_width_chars(GTK_ENTRY(value_entry), 2); struct text_prop_data *data = malloc(sizeof(struct text_prop_data)); //TODO store this in a list and free it when the node is deselected data->node = node; data->property = name; data->prop_type = type; data->layer = self; g_signal_connect(value_entry, "activate", G_CALLBACK(text_property_changed), data); gtk_table_attach(prop_table, name_label, 0, 1, d, d+1, GTK_FILL, GTK_FILL, 1, 1); gtk_table_attach(prop_table, value_entry, 1, 2, d, d+1, GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 1, 1); } } // gegl_node_process(node); GeglGtkView *gtk_view = gegl_gtk_view_new_for_node(node); GeglRectangle rect = gegl_node_get_bounding_box(node); if(gegl_rectangle_is_infinite_plane(&rect)) { gegl_gtk_view_set_autoscale_policy(gtk_view, GEGL_GTK_VIEW_AUTOSCALE_DISABLED); gegl_gtk_view_set_scale(gtk_view, 1.0); g_print("Disable autoscale: scale=%f, x=%f, y=%f\n", gegl_gtk_view_get_scale(gtk_view), gegl_gtk_view_get_x(gtk_view), gegl_gtk_view_get_y(gtk_view)); } gtk_widget_show(GTK_WIDGET(gtk_view)); //TODO: draw checkerboard under preview to indicate transparency gtk_box_pack_start(GTK_BOX(self->prop_box), GTK_WIDGET(prop_table), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->prop_box), GTK_WIDGET(gtk_view), TRUE, TRUE, 10); GtkWidget* label = gtk_label_new("Click the image\nto open in a\nnew window"); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); gtk_box_pack_start(GTK_BOX(self->prop_box), label, FALSE, TRUE, 10); gtk_widget_show_all(self->prop_box); }