/* 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
* 2006-2008 Øyvind Kolås
* 2013 Daniel Sabo
*/
#include "config.h"
#include
#include
#include
#include
#include "gegl.h"
#include "gegl-types-internal.h"
#include "gegl-operation-context.h"
#include "gegl-operation-context-private.h"
#include "gegl-node-private.h"
#include "gegl-buffer-private.h"
#include "gegl-tile-backend-buffer.h"
#include "gegl-config.h"
#include "operation/gegl-operation.h"
static GValue *
gegl_operation_context_add_value (GeglOperationContext *self,
const gchar *property_name);
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)
{
GValue *storage;
g_return_if_fail (context != NULL);
g_return_if_fail (G_VALUE_TYPE (value) == GEGL_TYPE_BUFFER);
/* if the value already exists in the context it will be reused */
storage = gegl_operation_context_add_value (context, property_name);
g_value_copy (value, storage);
}
void
gegl_operation_context_get_property (GeglOperationContext *context,
const gchar *property_name,
GValue *value)
{
GValue *storage;
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)
{
Property *property = NULL;
GSList *found;
found = g_slist_find_custom (self->property, property_name, lookup_property);
if (found)
{
property = found->data;
}
if (property)
{
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, GEGL_TYPE_BUFFER);
return &property->value;
}
GeglOperationContext *
gegl_operation_context_new (GeglOperation *operation,
GHashTable *hashtable)
{
GeglOperationContext *self = g_slice_new0 (GeglOperationContext);
self->operation = operation;
self->contexts = hashtable;
return self;
}
void
gegl_operation_context_purge (GeglOperationContext *self)
{
while (self->property)
{
Property *property = self->property->data;
self->property = g_slist_remove (self->property, property);
property_destroy (property);
}
}
void
gegl_operation_context_destroy (GeglOperationContext *self)
{
gegl_operation_context_purge (self);
g_slice_free (GeglOperationContext, self);
}
void
gegl_operation_context_set_object (GeglOperationContext *context,
const gchar *padname,
GObject *data)
{
g_return_if_fail (!data || GEGL_IS_BUFFER (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)
{
GValue *storage;
g_return_if_fail (context != NULL);
g_return_if_fail (!data || GEGL_IS_BUFFER (data));
storage = gegl_operation_context_add_value (context, padname);
g_value_take_object (storage, data);
}
GObject *
gegl_operation_context_dup_object (GeglOperationContext *context,
const gchar *padname)
{
GObject *ret;
ret = gegl_operation_context_get_object (context, padname);
if (ret != NULL)
g_object_ref (ret);
return ret;
}
GObject *
gegl_operation_context_get_object (GeglOperationContext *context,
const gchar *padname)
{
GObject *ret;
GValue *value;
value = gegl_operation_context_get_value (context, padname);
if (value != NULL)
{
ret = g_value_get_object (value);
if (ret != NULL)
{
return ret;
}
}
return NULL;
}
GeglBuffer *
gegl_operation_context_get_source (GeglOperationContext *context,
const gchar *padname)
{
GeglBuffer *input;
input = GEGL_BUFFER (gegl_operation_context_dup_object (context, padname));
return input;
}
GeglBuffer *
gegl_operation_context_get_target (GeglOperationContext *context,
const gchar *padname)
{
GeglBuffer *output = NULL;
const GeglRectangle *result;
const Babl *format;
GeglNode *node;
GeglOperation *operation;
static gint linear_buffers = -1;
#if 0
g_return_val_if_fail (GEGL_IS_OPERATION_CONTEXT (context), NULL);
#endif
g_return_val_if_fail (g_strcmp0 (padname, "output") == 0, NULL);
if (linear_buffers == -1)
linear_buffers = g_getenv ("GEGL_LINEAR_BUFFERS")?1:0;
operation = context->operation;
node = operation->node; /* result_rect;
if (result->width == 0 ||
result->height == 0)
{
if (linear_buffers)
output = gegl_buffer_linear_new (GEGL_RECTANGLE(0, 0, 0, 0), format);
else
output = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 0, 0), format);
}
else if (gegl_node_use_cache (node))
{
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);
}
if (! output)
{
if (linear_buffers)
{
output = gegl_buffer_linear_new (result, format);
}
else
{
output = g_object_new (
GEGL_TYPE_BUFFER,
"x", result->x,
"y", result->y,
"width", result->width,
"height", result->height,
"format", format,
"initialized", gegl_operation_context_get_init_output (),
NULL);
}
}
gegl_operation_context_take_object (context, padname, G_OBJECT (output));
return output;
}
gint
gegl_operation_context_get_level (GeglOperationContext *ctxt)
{
return ctxt->level;
}
GeglBuffer *
gegl_operation_context_get_output_maybe_in_place (GeglOperation *operation,
GeglOperationContext *context,
GeglBuffer *input,
const GeglRectangle *roi)
{
GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation);
GeglBuffer *output;
if (klass->want_in_place &&
! gegl_node_use_cache (operation->node) &&
gegl_can_do_inplace_processing (operation, input, roi))
{
output = g_object_ref (input);
gegl_operation_context_take_object (context, "output", G_OBJECT (output));
}
else
{
output = gegl_operation_context_get_target (context, "output");
}
return output;
}
GeglOperationContext *
gegl_operation_context_node_get_context (GeglOperationContext *context,
GeglNode *node)
{
if (context->contexts)
return g_hash_table_lookup (context->contexts, node);
return NULL;
}
GeglBuffer *
gegl_operation_context_dup_input_maybe_copy (GeglOperationContext *context,
const gchar *padname,
const GeglRectangle *roi)
{
GeglBuffer *input;
GeglBuffer *output;
GeglBuffer *result;
input = GEGL_BUFFER (gegl_operation_context_get_object (context, padname));
if (! input)
return NULL;
output = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));
/* return input directly when processing in-place, otherwise, the copied
* input buffer will occupy space in the cache after the original is modified
*/
if (input == output)
return g_object_ref (input);
#if 1
{
GeglTileBackend *backend;
backend = gegl_tile_backend_buffer_new (input);
gegl_tile_backend_set_flush_on_destroy (backend, FALSE);
/* create new buffer with similar characteristics to the input buffer */
result = g_object_new (GEGL_TYPE_BUFFER,
"format", input->soft_format,
"x", input->extent.x,
"y", input->extent.y,
"width", input->extent.width,
"height", input->extent.height,
"abyss-x", input->abyss.x,
"abyss-y", input->abyss.y,
"abyss-width", input->abyss.width,
"abyss-height", input->abyss.height,
"shift-x", input->shift_x,
"shift-y", input->shift_y,
"tile-width", input->tile_width,
"tile-height", input->tile_height,
"backend", backend,
NULL);
g_object_unref (backend);
}
#else
{
GeglRectangle required;
GeglRectangle temp;
gint shift_x;
gint shift_y;
gint tile_width;
gint tile_height;
/* return input directly when processing a level greater than 0, since
* gegl_buffer_copy() only copies level-0 tiles
*/
if (context->level > 0)
return g_object_ref (input);
/* get required region to copy */
required = gegl_operation_get_required_for_output (context->operation,
padname, roi);
/* return input directly if the required rectangle is infinite, so that we
* don't attempt to copy an infinite region
*/
if (gegl_rectangle_is_infinite_plane (&required))
return g_object_ref (input);
/* align required region to the tile grid */
shift_x = input->shift_x;
shift_y = input->shift_y;
tile_width = input->tile_width;
tile_height = input->tile_height;
temp.x = (gint) floor ((gdouble) (required.x + shift_x) / tile_width) * tile_width;
temp.y = (gint) floor ((gdouble) (required.y + shift_y) / tile_height) * tile_height;
temp.width = (gint) ceil ((gdouble) (required.x + required.width + shift_x) / tile_width) * tile_width - temp.x;
temp.height = (gint) ceil ((gdouble) (required.y + required.height + shift_y) / tile_height) * tile_height - temp.y;
temp.x -= shift_x;
temp.y -= shift_y;
required = temp;
/* intersect required region with input abyss */
gegl_rectangle_intersect (&required, &required, &input->abyss);
/* create new buffer with similar characteristics to the input buffer */
result = g_object_new (GEGL_TYPE_BUFFER,
"format", input->soft_format,
"x", input->extent.x,
"y", input->extent.y,
"width", input->extent.width,
"height", input->extent.height,
"abyss-x", input->abyss.x,
"abyss-y", input->abyss.y,
"abyss-width", input->abyss.width,
"abyss-height", input->abyss.height,
"shift-x", shift_x,
"shift-y", shift_y,
"tile-width", tile_width,
"tile-height", tile_height,
NULL);
/* if the tile size doesn't match, bail */
if (result->tile_width != tile_width || result->tile_height != tile_height)
{
g_object_unref (result);
return g_object_ref (input);
}
/* copy required region from input to result -- tiles will generally be COWed */
gegl_buffer_copy (input, &required, GEGL_ABYSS_NONE,
result, &required);
}
#endif
return result;
}
gboolean
gegl_operation_context_get_init_output (void)
{
static gint init_output = -1;
if (init_output < 0)
{
if (g_getenv ("GEGL_OPERATION_INIT_OUTPUT"))
{
init_output = atoi (g_getenv ("GEGL_OPERATION_INIT_OUTPUT")) ?
TRUE : FALSE;
}
else
{
init_output = FALSE;
}
}
return init_output;
}