1 /* This file is part of GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2003 Calvin Williamson
17  *           2006-2008 Øyvind Kolås
18  *           2013 Daniel Sabo
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 
27 #include <glib-object.h>
28 
29 #include "gegl.h"
30 #include "gegl-types-internal.h"
31 #include "gegl-operation-context.h"
32 #include "gegl-operation-context-private.h"
33 #include "gegl-node-private.h"
34 #include "gegl-buffer-private.h"
35 #include "gegl-tile-backend-buffer.h"
36 #include "gegl-config.h"
37 
38 #include "operation/gegl-operation.h"
39 
40 static GValue *
41 gegl_operation_context_add_value (GeglOperationContext *self,
42                                   const gchar          *property_name);
43 
44 
45 void
gegl_operation_context_set_need_rect(GeglOperationContext * self,const GeglRectangle * rect)46 gegl_operation_context_set_need_rect (GeglOperationContext *self,
47                                       const GeglRectangle  *rect)
48 {
49   g_assert (self);
50   self->need_rect = *rect;
51 }
52 
53 GeglRectangle *
gegl_operation_context_get_result_rect(GeglOperationContext * self)54 gegl_operation_context_get_result_rect (GeglOperationContext *self)
55 {
56   return &self->result_rect;
57 }
58 
59 void
gegl_operation_context_set_result_rect(GeglOperationContext * self,const GeglRectangle * rect)60 gegl_operation_context_set_result_rect (GeglOperationContext *self,
61                                         const GeglRectangle  *rect)
62 {
63   g_assert (self);
64   self->result_rect = *rect;
65 }
66 
67 GeglRectangle *
gegl_operation_context_get_need_rect(GeglOperationContext * self)68 gegl_operation_context_get_need_rect (GeglOperationContext *self)
69 {
70   return &self->need_rect;
71 }
72 
73 void
gegl_operation_context_set_property(GeglOperationContext * context,const gchar * property_name,const GValue * value)74 gegl_operation_context_set_property (GeglOperationContext *context,
75                                      const gchar          *property_name,
76                                      const GValue         *value)
77 {
78   GValue     *storage;
79 
80   g_return_if_fail (context != NULL);
81   g_return_if_fail (G_VALUE_TYPE (value) == GEGL_TYPE_BUFFER);
82 
83   /* if the value already exists in the context it will be reused */
84   storage = gegl_operation_context_add_value (context, property_name);
85 
86   g_value_copy (value, storage);
87 }
88 
89 void
gegl_operation_context_get_property(GeglOperationContext * context,const gchar * property_name,GValue * value)90 gegl_operation_context_get_property (GeglOperationContext *context,
91                                      const gchar          *property_name,
92                                      GValue               *value)
93 {
94   GValue     *storage;
95 
96   storage = gegl_operation_context_get_value (context, property_name);
97   if (storage != NULL)
98     {
99       g_value_copy (storage, value);
100     }
101 }
102 
103 typedef struct Property
104 {
105   gchar *name;
106   GValue value;
107 } Property;
108 
109 static Property *
property_new(const gchar * property_name)110 property_new (const gchar *property_name)
111 {
112   Property *property = g_slice_new0 (Property);
113 
114   property->name = g_strdup (property_name);
115   return property;
116 }
117 
118 static void
property_destroy(Property * property)119 property_destroy (Property *property)
120 {
121   g_free (property->name);
122   g_value_unset (&property->value); /* does an unref */
123   g_slice_free (Property, property);
124 }
125 
126 static gint
lookup_property(gconstpointer a,gconstpointer property_name)127 lookup_property (gconstpointer a,
128                  gconstpointer property_name)
129 {
130   Property *property = (void *) a;
131 
132   return strcmp (property->name, property_name);
133 }
134 
135 GValue *
gegl_operation_context_get_value(GeglOperationContext * self,const gchar * property_name)136 gegl_operation_context_get_value (GeglOperationContext *self,
137                                   const gchar          *property_name)
138 {
139   Property *property = NULL;
140 
141   {
142     GSList *found;
143     found = g_slist_find_custom (self->property, property_name, lookup_property);
144     if (found)
145       property = found->data;
146   }
147   if (!property)
148     {
149       return NULL;
150     }
151   return &property->value;
152 }
153 
154 void
gegl_operation_context_remove_property(GeglOperationContext * self,const gchar * property_name)155 gegl_operation_context_remove_property (GeglOperationContext *self,
156                                         const gchar          *property_name)
157 {
158   Property *property = NULL;
159 
160   GSList *found;
161   found = g_slist_find_custom (self->property, property_name, lookup_property);
162   if (found)
163     property = found->data;
164 
165   if (!property)
166     {
167       g_warning ("didn't find property %s for %s", property_name,
168                  GEGL_OPERATION_GET_CLASS (self->operation)->name);
169       return;
170     }
171   self->property = g_slist_remove (self->property, property);
172   property_destroy (property);
173 }
174 
175 static GValue *
gegl_operation_context_add_value(GeglOperationContext * self,const gchar * property_name)176 gegl_operation_context_add_value (GeglOperationContext *self,
177                                   const gchar          *property_name)
178 {
179   Property *property = NULL;
180   GSList   *found;
181 
182   found = g_slist_find_custom (self->property, property_name, lookup_property);
183 
184   if (found)
185     {
186       property = found->data;
187     }
188 
189   if (property)
190     {
191       g_value_reset (&property->value);
192       return &property->value;
193     }
194 
195   property = property_new (property_name);
196 
197   self->property = g_slist_prepend (self->property, property);
198   g_value_init (&property->value, GEGL_TYPE_BUFFER);
199 
200   return &property->value;
201 }
202 
203 GeglOperationContext *
gegl_operation_context_new(GeglOperation * operation,GHashTable * hashtable)204 gegl_operation_context_new (GeglOperation *operation,
205                             GHashTable    *hashtable)
206 {
207   GeglOperationContext *self = g_slice_new0 (GeglOperationContext);
208   self->operation = operation;
209   self->contexts = hashtable;
210   return self;
211 }
212 
213 void
gegl_operation_context_purge(GeglOperationContext * self)214 gegl_operation_context_purge (GeglOperationContext *self)
215 {
216   while (self->property)
217     {
218       Property *property = self->property->data;
219       self->property = g_slist_remove (self->property, property);
220       property_destroy (property);
221     }
222 }
223 
224 void
gegl_operation_context_destroy(GeglOperationContext * self)225 gegl_operation_context_destroy (GeglOperationContext *self)
226 {
227   gegl_operation_context_purge (self);
228   g_slice_free (GeglOperationContext, self);
229 }
230 
231 void
gegl_operation_context_set_object(GeglOperationContext * context,const gchar * padname,GObject * data)232 gegl_operation_context_set_object (GeglOperationContext *context,
233                                    const gchar          *padname,
234                                    GObject              *data)
235 {
236   g_return_if_fail (!data || GEGL_IS_BUFFER (data));
237 
238   /* Make it simple, just add an extra ref and then take the object */
239   if (data)
240     g_object_ref (data);
241   gegl_operation_context_take_object (context, padname, data);
242 }
243 
244 void
gegl_operation_context_take_object(GeglOperationContext * context,const gchar * padname,GObject * data)245 gegl_operation_context_take_object (GeglOperationContext *context,
246                                     const gchar          *padname,
247                                     GObject              *data)
248 {
249   GValue *storage;
250 
251   g_return_if_fail (context != NULL);
252   g_return_if_fail (!data || GEGL_IS_BUFFER (data));
253 
254   storage = gegl_operation_context_add_value (context, padname);
255   g_value_take_object (storage, data);
256 }
257 
258 GObject *
gegl_operation_context_dup_object(GeglOperationContext * context,const gchar * padname)259 gegl_operation_context_dup_object (GeglOperationContext *context,
260                                    const gchar          *padname)
261 {
262   GObject *ret;
263 
264   ret = gegl_operation_context_get_object (context, padname);
265   if (ret != NULL)
266     g_object_ref (ret);
267 
268   return ret;
269 }
270 
271 GObject *
gegl_operation_context_get_object(GeglOperationContext * context,const gchar * padname)272 gegl_operation_context_get_object (GeglOperationContext *context,
273                                    const gchar          *padname)
274 {
275   GObject     *ret;
276   GValue      *value;
277 
278   value = gegl_operation_context_get_value (context, padname);
279 
280   if (value != NULL)
281     {
282       ret = g_value_get_object (value);
283       if (ret != NULL)
284         {
285           return ret;
286         }
287     }
288 
289   return NULL;
290 }
291 
292 GeglBuffer *
gegl_operation_context_get_source(GeglOperationContext * context,const gchar * padname)293 gegl_operation_context_get_source (GeglOperationContext *context,
294                                    const gchar          *padname)
295 {
296   GeglBuffer *input;
297 
298   input = GEGL_BUFFER (gegl_operation_context_dup_object (context, padname));
299   return input;
300 }
301 
302 GeglBuffer *
gegl_operation_context_get_target(GeglOperationContext * context,const gchar * padname)303 gegl_operation_context_get_target (GeglOperationContext *context,
304                                    const gchar          *padname)
305 {
306   GeglBuffer          *output         = NULL;
307   const GeglRectangle *result;
308   const Babl          *format;
309   GeglNode            *node;
310   GeglOperation       *operation;
311   static gint          linear_buffers = -1;
312 
313 #if 0
314   g_return_val_if_fail (GEGL_IS_OPERATION_CONTEXT (context), NULL);
315 #endif
316 
317   g_return_val_if_fail (g_strcmp0 (padname, "output") == 0, NULL);
318 
319   if (linear_buffers == -1)
320     linear_buffers = g_getenv ("GEGL_LINEAR_BUFFERS")?1:0;
321 
322   operation = context->operation;
323   node = operation->node; /* <ick */
324   format = gegl_operation_get_format (operation, padname);
325 
326   if (format == NULL)
327     {
328       g_warning ("no format for %s presuming RGBA float\n",
329                  gegl_node_get_debug_name (node));
330       format = gegl_babl_rgba_linear_float ();
331     }
332   g_assert (format != NULL);
333 
334   result = &context->result_rect;
335 
336   if (result->width == 0 ||
337       result->height == 0)
338     {
339       if (linear_buffers)
340         output = gegl_buffer_linear_new (GEGL_RECTANGLE(0, 0, 0, 0), format);
341       else
342         output = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 0, 0), format);
343     }
344   else if (gegl_node_use_cache (node))
345     {
346       GeglBuffer    *cache;
347       cache = GEGL_BUFFER (gegl_node_get_cache (node));
348 
349       /* Only use the cache if the result is within the cache
350        * extent. This is certainly not optimal. My gut feeling is that
351        * the current caching mechanism needs to be redesigned
352        */
353       if (gegl_rectangle_contains (gegl_buffer_get_extent (cache), result))
354         output = g_object_ref (cache);
355     }
356 
357   if (! output)
358     {
359       if (linear_buffers)
360         {
361           output = gegl_buffer_linear_new (result, format);
362         }
363       else
364         {
365           output = g_object_new (
366             GEGL_TYPE_BUFFER,
367             "x",           result->x,
368             "y",           result->y,
369             "width",       result->width,
370             "height",      result->height,
371             "format",      format,
372             "initialized", gegl_operation_context_get_init_output (),
373             NULL);
374         }
375     }
376 
377   gegl_operation_context_take_object (context, padname, G_OBJECT (output));
378 
379   return output;
380 }
381 
382 gint
gegl_operation_context_get_level(GeglOperationContext * ctxt)383 gegl_operation_context_get_level (GeglOperationContext *ctxt)
384 {
385   return ctxt->level;
386 }
387 
388 
389 GeglBuffer *
gegl_operation_context_get_output_maybe_in_place(GeglOperation * operation,GeglOperationContext * context,GeglBuffer * input,const GeglRectangle * roi)390 gegl_operation_context_get_output_maybe_in_place (GeglOperation *operation,
391                                                   GeglOperationContext *context,
392                                                   GeglBuffer    *input,
393                                                   const GeglRectangle *roi)
394 {
395   GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation);
396   GeglBuffer *output;
397 
398   if (klass->want_in_place                    &&
399       ! gegl_node_use_cache (operation->node) &&
400       gegl_can_do_inplace_processing (operation, input, roi))
401     {
402       output = g_object_ref (input);
403       gegl_operation_context_take_object (context, "output", G_OBJECT (output));
404     }
405   else
406     {
407       output = gegl_operation_context_get_target (context, "output");
408     }
409   return output;
410 }
411 
412 GeglOperationContext *
gegl_operation_context_node_get_context(GeglOperationContext * context,GeglNode * node)413 gegl_operation_context_node_get_context (GeglOperationContext *context,
414                                          GeglNode             *node)
415 {
416   if (context->contexts)
417     return g_hash_table_lookup (context->contexts, node);
418   return NULL;
419 }
420 
421 
422 GeglBuffer *
gegl_operation_context_dup_input_maybe_copy(GeglOperationContext * context,const gchar * padname,const GeglRectangle * roi)423 gegl_operation_context_dup_input_maybe_copy (GeglOperationContext *context,
424                                              const gchar          *padname,
425                                              const GeglRectangle  *roi)
426 {
427   GeglBuffer *input;
428   GeglBuffer *output;
429   GeglBuffer *result;
430 
431   input = GEGL_BUFFER (gegl_operation_context_get_object (context, padname));
432 
433   if (! input)
434     return NULL;
435 
436   output = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));
437 
438   /* return input directly when processing in-place, otherwise, the copied
439    * input buffer will occupy space in the cache after the original is modified
440    */
441   if (input == output)
442     return g_object_ref (input);
443 
444 #if 1
445   {
446     GeglTileBackend *backend;
447 
448     backend = gegl_tile_backend_buffer_new (input);
449     gegl_tile_backend_set_flush_on_destroy (backend, FALSE);
450 
451     /* create new buffer with similar characteristics to the input buffer */
452     result = g_object_new (GEGL_TYPE_BUFFER,
453                            "format",       input->soft_format,
454                            "x",            input->extent.x,
455                            "y",            input->extent.y,
456                            "width",        input->extent.width,
457                            "height",       input->extent.height,
458                            "abyss-x",      input->abyss.x,
459                            "abyss-y",      input->abyss.y,
460                            "abyss-width",  input->abyss.width,
461                            "abyss-height", input->abyss.height,
462                            "shift-x",      input->shift_x,
463                            "shift-y",      input->shift_y,
464                            "tile-width",   input->tile_width,
465                            "tile-height",  input->tile_height,
466                            "backend",      backend,
467                            NULL);
468 
469     g_object_unref (backend);
470   }
471 #else
472   {
473     GeglRectangle required;
474     GeglRectangle temp;
475     gint          shift_x;
476     gint          shift_y;
477     gint          tile_width;
478     gint          tile_height;
479 
480     /* return input directly when processing a level greater than 0, since
481      * gegl_buffer_copy() only copies level-0 tiles
482      */
483     if (context->level > 0)
484       return g_object_ref (input);
485 
486     /* get required region to copy */
487     required = gegl_operation_get_required_for_output (context->operation,
488                                                        padname, roi);
489 
490     /* return input directly if the required rectangle is infinite, so that we
491      * don't attempt to copy an infinite region
492      */
493     if (gegl_rectangle_is_infinite_plane (&required))
494       return g_object_ref (input);
495 
496     /* align required region to the tile grid */
497     shift_x     = input->shift_x;
498     shift_y     = input->shift_y;
499     tile_width  = input->tile_width;
500     tile_height = input->tile_height;
501 
502     temp.x      = (gint) floor ((gdouble) (required.x                   + shift_x) / tile_width)  * tile_width;
503     temp.y      = (gint) floor ((gdouble) (required.y                   + shift_y) / tile_height) * tile_height;
504     temp.width  = (gint) ceil  ((gdouble) (required.x + required.width  + shift_x) / tile_width)  * tile_width  - temp.x;
505     temp.height = (gint) ceil  ((gdouble) (required.y + required.height + shift_y) / tile_height) * tile_height - temp.y;
506 
507     temp.x -= shift_x;
508     temp.y -= shift_y;
509 
510     required = temp;
511 
512     /* intersect required region with input abyss */
513     gegl_rectangle_intersect (&required, &required, &input->abyss);
514 
515     /* create new buffer with similar characteristics to the input buffer */
516     result = g_object_new (GEGL_TYPE_BUFFER,
517                            "format",       input->soft_format,
518                            "x",            input->extent.x,
519                            "y",            input->extent.y,
520                            "width",        input->extent.width,
521                            "height",       input->extent.height,
522                            "abyss-x",      input->abyss.x,
523                            "abyss-y",      input->abyss.y,
524                            "abyss-width",  input->abyss.width,
525                            "abyss-height", input->abyss.height,
526                            "shift-x",      shift_x,
527                            "shift-y",      shift_y,
528                            "tile-width",   tile_width,
529                            "tile-height",  tile_height,
530                            NULL);
531 
532     /* if the tile size doesn't match, bail */
533     if (result->tile_width != tile_width || result->tile_height != tile_height)
534       {
535         g_object_unref (result);
536 
537         return g_object_ref (input);
538       }
539 
540     /* copy required region from input to result -- tiles will generally be COWed */
541     gegl_buffer_copy (input,  &required, GEGL_ABYSS_NONE,
542                       result, &required);
543   }
544 #endif
545 
546   return result;
547 }
548 
549 gboolean
gegl_operation_context_get_init_output(void)550 gegl_operation_context_get_init_output (void)
551 {
552   static gint init_output = -1;
553 
554   if (init_output < 0)
555     {
556       if (g_getenv ("GEGL_OPERATION_INIT_OUTPUT"))
557         {
558           init_output = atoi (g_getenv ("GEGL_OPERATION_INIT_OUTPUT")) ?
559             TRUE : FALSE;
560         }
561       else
562         {
563           init_output = FALSE;
564         }
565     }
566 
567   return init_output;
568 }
569