/* This file is part of GEGL * * GEGL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * GEGL 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright 2003 Calvin Williamson * 2005-2008 Øyvind Kolås */ #include "config.h" #include #include #include "gegl.h" #include "gegl-types-internal.h" #include "gegl-operation.h" #include "gegl-utils.h" #include "graph/gegl-node.h" #include "graph/gegl-connection.h" #include "graph/gegl-pad.h" #include "buffer/gegl-region.h" #include "buffer/gegl-buffer.h" #include "gegl-operations.h" static void attach (GeglOperation *self); static GeglRectangle get_bounding_box (GeglOperation *self); static GeglRectangle get_invalidated_by_change (GeglOperation *self, const gchar *input_pad, const GeglRectangle *input_region); static GeglRectangle get_required_for_output (GeglOperation *self, const gchar *input_pad, const GeglRectangle *region); static void gegl_operation_class_init (GeglOperationClass *klass); static void gegl_operation_base_init (GeglOperationClass *klass); static void gegl_operation_init (GeglOperation *self); GType gegl_operation_get_type (void) { static GType type = 0; if (! type) { const GTypeInfo info = { sizeof (GeglOperationClass), (GBaseInitFunc) gegl_operation_base_init, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gegl_operation_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GeglOperation), 0, /* n_preallocs */ (GInstanceInitFunc) gegl_operation_init, }; type = g_type_register_static (G_TYPE_OBJECT, "GeglOperation", &info, 0); } return type; } static void gegl_operation_class_init (GeglOperationClass *klass) { klass->name = NULL; /* an operation class with * name == NULL is not * included when doing * operation lookup by * name */ klass->compat_name = NULL; klass->attach = attach; klass->prepare = NULL; klass->no_cache = FALSE; klass->opencl_support = FALSE; klass->get_bounding_box = get_bounding_box; klass->get_invalidated_by_change = get_invalidated_by_change; klass->get_required_for_output = get_required_for_output; } static void gegl_operation_base_init (GeglOperationClass *klass) { /* XXX: leaked for now, should replace G_DEFINE_TYPE with the expanded one */ klass->keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void gegl_operation_init (GeglOperation *self) { } /** * gegl_operation_create_pad: * @self: a #GeglOperation. * @param_spec: * * Create a property. **/ void gegl_operation_create_pad (GeglOperation *self, GParamSpec *param_spec) { GeglPad *pad; g_return_if_fail (GEGL_IS_OPERATION (self)); g_return_if_fail (param_spec != NULL); if (!self->node) { g_warning ("%s: aborting, no associated node. " "This method should only be called after the operation is " "associated with a node.", G_STRFUNC); return; } pad = g_object_new (GEGL_TYPE_PAD, NULL); gegl_pad_set_param_spec (pad, param_spec); gegl_pad_set_node (pad, self->node); gegl_node_add_pad (self->node, pad); } void gegl_node_emit_computed (GeglNode *node, const GeglRectangle *rect); gboolean gegl_operation_process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_pad, const GeglRectangle *result, gint level) { GeglOperationClass *klass; g_return_val_if_fail (GEGL_IS_OPERATION (operation), FALSE); klass = GEGL_OPERATION_GET_CLASS (operation); if (!strcmp (output_pad, "output") && (result->width == 0 || result->height == 0)) { GeglBuffer *output = gegl_buffer_new (NULL, NULL); g_warning ("%s Eeek: processing 0px rectangle", G_STRLOC); /* when this case is hit.. we've done something bad.. */ gegl_operation_context_take_object (context, "output", G_OBJECT (output)); return TRUE; } return klass->process (operation, context, output_pad, result, level); } /* Calls an extending class' get_bound_box method if defined otherwise * just returns a zero-initiliased bouding box */ GeglRectangle gegl_operation_get_bounding_box (GeglOperation *self) { GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (self); GeglRectangle rect = { 0, 0, 0, 0 }; if (klass->get_bounding_box) return klass->get_bounding_box (self); return rect; } GeglRectangle gegl_operation_get_invalidated_by_change (GeglOperation *self, const gchar *input_pad, const GeglRectangle *input_region) { GeglOperationClass *klass; GeglRectangle retval = { 0, }; g_return_val_if_fail (GEGL_IS_OPERATION (self), retval); g_return_val_if_fail (input_pad != NULL, retval); g_return_val_if_fail (input_region != NULL, retval); klass = GEGL_OPERATION_GET_CLASS (self); if (input_region->width == 0 || input_region->height == 0) return *input_region; if (klass->get_invalidated_by_change) return klass->get_invalidated_by_change (self, input_pad, input_region); return *input_region; } static GeglRectangle get_required_for_output (GeglOperation *operation, const gchar *input_pad, const GeglRectangle *roi) { if (operation->node->is_graph) { return gegl_operation_get_required_for_output (operation, input_pad, roi); } return *roi; } GeglRectangle gegl_operation_get_required_for_output (GeglOperation *operation, const gchar *input_pad, const GeglRectangle *roi) { GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation); if (roi->width == 0 || roi->height == 0) return *roi; g_assert (klass->get_required_for_output); return klass->get_required_for_output (operation, input_pad, roi); } GeglRectangle gegl_operation_get_cached_region (GeglOperation *operation, const GeglRectangle *roi) { GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation); if (!klass->get_cached_region) { return *roi; } return klass->get_cached_region (operation, roi); } static void attach (GeglOperation *self) { g_warning ("kilroy was at What The Hack (%p, %s)\n", (void *) self, G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (self))); return; } void gegl_operation_attach (GeglOperation *self, GeglNode *node) { GeglOperationClass *klass; g_return_if_fail (GEGL_IS_OPERATION (self)); g_return_if_fail (GEGL_IS_NODE (node)); klass = GEGL_OPERATION_GET_CLASS (self); g_assert (klass->attach); self->node = node; klass->attach (self); } /* Calls the prepare function on the operation that extends this base class */ void gegl_operation_prepare (GeglOperation *self) { GeglOperationClass *klass; g_return_if_fail (GEGL_IS_OPERATION (self)); klass = GEGL_OPERATION_GET_CLASS (self); if (klass->prepare) klass->prepare (self); } 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); } GeglRectangle * gegl_operation_source_get_bounding_box (GeglOperation *operation, const gchar *input_pad_name) { GeglNode *node = gegl_operation_get_source_node (operation, input_pad_name); if (node) { GeglRectangle *ret; g_mutex_lock (node->mutex); ret = &node->have_rect; g_mutex_unlock (node->mutex); return ret; } return NULL; } static GeglRectangle get_bounding_box (GeglOperation *self) { GeglRectangle rect = { 0, 0, 0, 0 }; if (self->node->is_graph) { GeglOperation *operation; operation = gegl_node_get_output_proxy (self->node, "output")->operation; rect = gegl_operation_get_bounding_box (operation); } else { g_warning ("Operation '%s' has no get_bounding_box() method", G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (self))); } return rect; } static GeglRectangle get_invalidated_by_change (GeglOperation *self, const gchar *input_pad, const GeglRectangle *input_region) { #if 0 /* FIXME: this seems to sometimes go into an infinite loop, the * current workaround of passing the rectangle straight through * isn't even true for unsharp-mask/drop-shadow/difference of gaussians, * but it stops a crasher bug. * * This needs to be revisited as part of the core processing revisit * (perhaps together with a meta-op framework revwrite). */ if (self->node->is_graph) { return gegl_operation_get_invalidated_by_change ( gegl_node_get_output_proxy (self->node, "output")->operation, input_pad, input_region); } #endif return *input_region; } /* returns a freshly allocated list of the properties of the object, does not list * the regular gobject properties of GeglNode ('name' and 'operation') */ GParamSpec ** gegl_operation_list_properties (const gchar *operation_type, guint *n_properties_p) { GParamSpec **pspecs; GType type; GObjectClass *klass; type = gegl_operation_gtype_from_name (operation_type); if (!type) { if (n_properties_p) *n_properties_p = 0; return NULL; } klass = g_type_class_ref (type); pspecs = g_object_class_list_properties (klass, n_properties_p); g_type_class_unref (klass); return pspecs; } GeglNode * gegl_operation_detect (GeglOperation *operation, gint x, gint y) { GeglNode *node = NULL; GeglOperationClass *klass; if (!operation) return NULL; g_return_val_if_fail (GEGL_IS_OPERATION (operation), NULL); node = operation->node; klass = GEGL_OPERATION_GET_CLASS (operation); if (klass->detect) { return klass->detect (operation, x, y); } if (x >= node->have_rect.x && x < node->have_rect.x + node->have_rect.width && y >= node->have_rect.y && y < node->have_rect.y + node->have_rect.height) { return node; } return NULL; } void gegl_operation_set_format (GeglOperation *self, const gchar *pad_name, const Babl *format) { GeglPad *pad; g_return_if_fail (GEGL_IS_OPERATION (self)); g_return_if_fail (pad_name != NULL); pad = gegl_node_get_pad (self->node, pad_name); g_return_if_fail (pad != NULL); pad->format = format; } const Babl * gegl_operation_get_format (GeglOperation *self, const gchar *pad_name) { GeglPad *pad; g_return_val_if_fail (GEGL_IS_OPERATION (self), NULL); g_return_val_if_fail (pad_name != NULL, NULL); pad = gegl_node_get_pad (self->node, pad_name); if (pad == NULL || pad->format == NULL) { g_warning ("%s: returns NULL", G_STRFUNC); } g_return_val_if_fail (pad != NULL, NULL); return pad->format; } const gchar * gegl_operation_get_name (GeglOperation *operation) { GeglOperationClass *klass; g_return_val_if_fail (GEGL_IS_OPERATION (operation), NULL); klass = GEGL_OPERATION_GET_CLASS (operation); return klass->name; } void gegl_operation_invalidate (GeglOperation *operation, const GeglRectangle *roi, gboolean clear_cache) { GeglNode *node = NULL; if (!operation) return; g_return_if_fail (GEGL_IS_OPERATION (operation)); node = operation->node; gegl_node_invalidated (node, roi, TRUE); } gchar ** gegl_operation_list_keys (const gchar *operation_name, guint *n_keys) { GType type; GObjectClass *klass; GList *list, *l; gchar **ret; int count; int i; type = gegl_operation_gtype_from_name (operation_name); if (!type) { if (n_keys) *n_keys = 0; return NULL; } klass = g_type_class_ref (type); count = g_hash_table_size (GEGL_OPERATION_CLASS (klass)->keys); ret = g_malloc0 (sizeof (gpointer) * (count + 1)); list = g_hash_table_get_keys (GEGL_OPERATION_CLASS (klass)->keys); for (i = 0, l = list; l; l = l->next) { ret[i] = l->data; } g_list_free (list); if (n_keys) *n_keys = count; g_type_class_unref (klass); return ret; } void gegl_operation_class_set_key (GeglOperationClass *klass, const gchar *key_name, const gchar *key_value) { if (!key_value) { g_hash_table_remove (klass->keys, key_name); return; } else { key_value = g_strdup (key_value); g_hash_table_insert (klass->keys, g_strdup (key_name), (void*)key_value); } if (!strcmp (key_name, "name")) { klass->name = key_value; } } void gegl_operation_class_set_keys (GeglOperationClass *klass, const gchar *key_name, ...) { va_list var_args; va_start (var_args, key_name); while (key_name) { const char *value = va_arg (var_args, char *); gegl_operation_class_set_key (klass, key_name, value); key_name = va_arg (var_args, char *); } va_end (var_args); } void gegl_operation_set_key (const gchar *operation_name, const gchar *key_name, const gchar *key_value) { GType type; GObjectClass *klass; type = gegl_operation_gtype_from_name (operation_name); if (!type) return; klass = g_type_class_ref (type); gegl_operation_class_set_key (GEGL_OPERATION_CLASS (klass), key_name, key_value); g_type_class_unref (klass); } const gchar * gegl_operation_class_get_key (GeglOperationClass *operation_class, const gchar *key_name) { const gchar *ret = NULL; ret = g_hash_table_lookup (GEGL_OPERATION_CLASS (operation_class)->keys, key_name); return ret; } const gchar * gegl_operation_get_key (const gchar *operation_name, const gchar *key_name) { GType type; GObjectClass *klass; const gchar *ret = NULL; type = gegl_operation_gtype_from_name (operation_name); if (!type) { return NULL; } klass = g_type_class_ref (type); ret = gegl_operation_class_get_key (GEGL_OPERATION_CLASS (klass), key_name); g_type_class_unref (klass); return ret; }