/* 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 <http://www.gnu.org/licenses/>.
*
* Copyright 2003 Calvin Williamson
* 2006-2008 Øyvind Kolås
*/
#include "config.h"
#include <string.h>
#include <glib-object.h>
#include "gegl.h"
#include "gegl/gegl-utils.h"
#include "gegl-types-internal.h"
#include "gegl-operation-context.h"
#include "gegl/graph/gegl-node.h"
#include "gegl-config.h"
#include "operation/gegl-operation.h"
static GValue *
gegl_operation_context_add_value (GeglOperationContext *self,
const gchar *property_name,
GType proptype);
void
gegl_operation_context_set_need_rect (GeglOperationContext *self,
const GeglRectangle *rect)
{
g_assert (self);
self->need_rect = *rect;
}
GeglRectangle *
gegl_operation_context_get_result_rect (GeglOperationContext *self)
{
return &self->result_rect;
}
void
gegl_operation_context_set_result_rect (GeglOperationContext *self,
const GeglRectangle *rect)
{
g_assert (self);
self->result_rect = *rect;
}
GeglRectangle *
gegl_operation_context_get_need_rect (GeglOperationContext *self)
{
return &self->need_rect;
}
void
gegl_operation_context_set_property (GeglOperationContext *context,
const gchar *property_name,
const GValue *value)
{
GParamSpec *pspec;
GValue *storage;
g_return_if_fail (context != NULL);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (context->operation)), property_name);
if (!pspec)
{
g_warning ("%s: node %s has no pad|property named '%s'",
G_STRFUNC,
GEGL_OPERATION_GET_CLASS (context->operation)->name,
property_name);
}
/* if the value already exists in the context it will be reused */
storage = gegl_operation_context_add_value (context, property_name, G_PARAM_SPEC_VALUE_TYPE(pspec));
/* storage needs to have the correct type */
g_value_copy (value, storage);
}
void
gegl_operation_context_get_property (GeglOperationContext *context,
const gchar *property_name,
GValue *value)
{
GParamSpec *pspec;
GValue *storage;
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (context->operation)), property_name);
if (!pspec)
{
g_warning ("%s: node %s has no pad|property named '%s'",
G_STRFUNC,
GEGL_OPERATION_GET_CLASS (context->operation)->name,
property_name);
}
storage = gegl_operation_context_get_value (context, property_name);
if (storage != NULL)
{
g_value_copy (storage, value);
}
}
typedef struct Property
{
gchar *name;
GValue value;
} Property;
static Property *
property_new (const gchar *property_name)
{
Property *property = g_slice_new0 (Property);
property->name = g_strdup (property_name);
return property;
}
static void
property_destroy (Property *property)
{
g_free (property->name);
g_value_unset (&property->value); /* does an unref */
g_slice_free (Property, property);
}
static gint
lookup_property (gconstpointer a,
gconstpointer property_name)
{
Property *property = (void *) a;
return strcmp (property->name, property_name);
}
GValue *
gegl_operation_context_get_value (GeglOperationContext *self,
const gchar *property_name)
{
Property *property = NULL;
{
GSList *found;
found = g_slist_find_custom (self->property, property_name, lookup_property);
if (found)
property = found->data;
}
if (!property)
{
return NULL;
}
return &property->value;
}
void
gegl_operation_context_remove_property (GeglOperationContext *self,
const gchar *property_name)
{
Property *property = NULL;
GSList *found;
found = g_slist_find_custom (self->property, property_name, lookup_property);
if (found)
property = found->data;
if (!property)
{
g_warning ("didn't find property %s for %s", property_name,
GEGL_OPERATION_GET_CLASS (self->operation)->name);
return;
}
self->property = g_slist_remove (self->property, property);
property_destroy (property);
}
static GValue *
gegl_operation_context_add_value (GeglOperationContext *self,
const gchar *property_name,
GType proptype)
{
Property *property = NULL;
GSList *found;
found = g_slist_find_custom (self->property, property_name, lookup_property);
if (found)
{
property = found->data;
}
if (property)
{
/* XXX: check that the existing one was of the right type */
g_value_reset (&property->value);
return &property->value;
}
property = property_new (property_name);
self->property = g_slist_prepend (self->property, property);
g_value_init (&property->value, proptype);
return &property->value;
}
GeglOperationContext *gegl_operation_context_new (void)
{
GeglOperationContext *self = g_slice_new0 (GeglOperationContext);
return self;
}
void gegl_operation_context_destroy (GeglOperationContext *self)
{
while (self->property)
{
Property *property = self->property->data;
self->property = g_slist_remove (self->property, property);
property_destroy (property);
}
g_slice_free (GeglOperationContext, self);
}
void
gegl_operation_context_set_object (GeglOperationContext *context,
const gchar *padname,
GObject *data)
{
/* Make it simple, just add an extra ref and then take the object */
if (data)
g_object_ref (data);
gegl_operation_context_take_object (context, padname, data);
}
void
gegl_operation_context_take_object (GeglOperationContext *context,
const gchar *padname,
GObject *data)
{
GParamSpec *pspec;
/* FIXME: check that there isn't already an existing
* output object/value set?
*/
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (context->operation)), padname);
if (pspec)
{
GValue value = {0, };
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_take_object (&value, data);
gegl_operation_context_set_property (context, padname, &value);
g_value_unset (&value);
}
else
{
g_warning ("%s: No paramspec found for pad '%s' on \"%s\"\n",
G_STRFUNC,
padname,
gegl_operation_get_name (context->operation));
}
}
GObject *
gegl_operation_context_get_object (GeglOperationContext *context,
const gchar *padname)
{
GObject *ret;
GParamSpec *pspec;
GValue value = { 0, };
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (context->operation)), padname);
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
gegl_operation_context_get_property (context, padname, &value);
/* FIXME: handle other things than gobjects as well? */
ret = g_value_get_object (&value);
if (!ret)
{/*
g_warning ("some important data was not found on %s.%s",
gegl_node_get_debug_name (node), property_name);
*/
}
g_value_unset (&value);
return ret;
}
GeglBuffer *
gegl_operation_context_get_source (GeglOperationContext *context,
const gchar *padname)
{
GeglBuffer *real_input;
GeglBuffer *input;
real_input = GEGL_BUFFER (gegl_operation_context_get_object (context, padname));
if (!real_input)
return NULL;
input = g_object_ref (real_input);
return input;
}
static GeglBuffer *emptybuf (void)
{
static GeglBuffer *empty = NULL; /* we leak this single empty buffer,
avoiding having to create it weighs
up the penalty.
*/
if (!empty)
{
GeglRectangle rect={0,0,0,0};
empty = gegl_buffer_new (&rect, babl_format ("RGBA float"));
}
return empty;
}
GeglBuffer *
gegl_operation_context_get_target (GeglOperationContext *context,
const gchar *padname)
{
GeglBuffer *output;
const GeglRectangle *result;
const Babl *format;
GeglNode *node;
GeglOperation *operation;
#if 0
g_return_val_if_fail (GEGL_IS_OPERATION_CONTEXT (context), NULL);
#endif
operation = context->operation;
node = operation->node; /* <ick */
format = gegl_operation_get_format (operation, padname);
if (format == NULL)
{
g_warning ("no format for %s presuming RGBA float\n",
gegl_node_get_debug_name (node));
format = babl_format ("RGBA float");
}
g_assert (format != NULL);
g_assert (!strcmp (padname, "output"));
result = &context->result_rect;
if (result->width == 0 ||
result->height == 0)
{
output = g_object_ref (emptybuf());
}
else if (node->dont_cache == FALSE &&
! GEGL_OPERATION_CLASS (G_OBJECT_GET_CLASS (operation))->no_cache)
{
GeglBuffer *cache;
cache = GEGL_BUFFER (gegl_node_get_cache (node));
/* Only use the cache if the result is within the cache
* extent. This is certainly not optimal. My gut feeling is that
* the current caching mechanism needs to be redesigned
*/
if (gegl_rectangle_contains (gegl_buffer_get_extent (cache), result))
{
output = g_object_ref (cache);
}
else
{
output = gegl_buffer_new_ram (result, format);
}
}
else
{
output = gegl_buffer_new_ram (result, format);
}
gegl_operation_context_take_object (context, padname, G_OBJECT (output));
return output;
}
gint gegl_operation_context_get_level (GeglOperationContext *ctxt)
{
return ctxt->level;
}