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