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 * 2005-2009,2011-2014 Øyvind Kolås
18 */
19
20 #include "config.h"
21
22 #include <glib-object.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "gegl.h"
27 #include "gegl-config.h"
28 #include "gegl-types-internal.h"
29 #include "gegl-parallel-private.h"
30 #include "gegl-operation.h"
31 #include "gegl-operation-private.h"
32 #include "gegl-operation-context.h"
33 #include "gegl-operation-context-private.h"
34 #include "gegl-operations-util.h"
35 #include "gegl-operation-meta.h"
36 #include "graph/gegl-node-private.h"
37 #include "graph/gegl-connection.h"
38 #include "graph/gegl-pad.h"
39 #include "gegl-operations.h"
40
41
42 #define GEGL_OPERATION_MIN_PIXELS_PER_PIXEL_TIME_UPDATE ( 32 * 32)
43 #define GEGL_OPERATION_DEFAULT_PIXELS_PER_THREAD ( 64 * 64)
44 #define GEGL_OPERATION_MAX_PIXELS_PER_THREAD (128 * 128)
45
46
47 struct _GeglOperationPrivate
48 {
49 gdouble pixel_time;
50 gboolean attached;
51 };
52
53
54 static void attach (GeglOperation *self);
55
56 static GeglRectangle get_bounding_box (GeglOperation *self);
57 static GeglRectangle get_invalidated_by_change (GeglOperation *self,
58 const gchar *input_pad,
59 const GeglRectangle *input_region);
60 static GeglRectangle get_required_for_output (GeglOperation *self,
61 const gchar *input_pad,
62 const GeglRectangle *region);
63
64 static void gegl_operation_update_pixel_time (GeglOperation *self,
65 const GeglRectangle *roi,
66 gdouble t);
67
68
G_DEFINE_TYPE_WITH_PRIVATE(GeglOperation,gegl_operation,G_TYPE_OBJECT)69 G_DEFINE_TYPE_WITH_PRIVATE (GeglOperation, gegl_operation, G_TYPE_OBJECT)
70
71
72 static void
73 gegl_operation_class_init (GeglOperationClass *klass)
74 {
75 klass->name = NULL; /* an operation class with
76 * name == NULL is not
77 * included when doing
78 * operation lookup by
79 * name
80 */
81 klass->compat_name = NULL;
82 klass->attach = attach;
83 klass->prepare = NULL;
84 klass->no_cache = FALSE;
85 klass->threaded = FALSE;
86 klass->cache_policy = GEGL_CACHE_POLICY_AUTO;
87 klass->get_bounding_box = get_bounding_box;
88 klass->get_invalidated_by_change = get_invalidated_by_change;
89 klass->get_required_for_output = get_required_for_output;
90 klass->cl_data = NULL;
91 }
92
93 static void
gegl_operation_init(GeglOperation * self)94 gegl_operation_init (GeglOperation *self)
95 {
96 GeglOperationPrivate *priv = gegl_operation_get_instance_private (self);
97
98 priv->pixel_time = -1.0;
99 }
100
101 /**
102 * gegl_operation_create_pad:
103 * @self: a #GeglOperation.
104 * @param_spec:
105 *
106 * Create a property.
107 **/
108 void
gegl_operation_create_pad(GeglOperation * self,GParamSpec * param_spec)109 gegl_operation_create_pad (GeglOperation *self,
110 GParamSpec *param_spec)
111 {
112 GeglPad *pad;
113
114 g_return_if_fail (GEGL_IS_OPERATION (self));
115 g_return_if_fail (param_spec != NULL);
116
117 if (!self->node)
118 {
119 g_warning ("%s: aborting, no associated node. "
120 "This method should only be called after the operation is "
121 "associated with a node.", G_STRFUNC);
122 return;
123 }
124
125 pad = g_object_new (GEGL_TYPE_PAD, NULL);
126 gegl_pad_set_param_spec (pad, param_spec);
127 gegl_pad_set_node (pad, self->node);
128 gegl_node_add_pad (self->node, pad);
129 }
130
131 gboolean
gegl_operation_process(GeglOperation * operation,GeglOperationContext * context,const gchar * output_pad,const GeglRectangle * result,gint level)132 gegl_operation_process (GeglOperation *operation,
133 GeglOperationContext *context,
134 const gchar *output_pad,
135 const GeglRectangle *result,
136 gint level)
137 {
138 GeglOperationClass *klass;
139 gint64 t;
140 gint64 n_pixels;
141 gboolean update_pixel_time;
142 gboolean success;
143
144 g_return_val_if_fail (GEGL_IS_OPERATION (operation), FALSE);
145 g_return_val_if_fail (result != NULL, FALSE);
146
147 klass = GEGL_OPERATION_GET_CLASS (operation);
148
149 if (!strcmp (output_pad, "output") &&
150 (result->width == 0 || result->height == 0))
151 {
152 GeglBuffer *output = gegl_buffer_new (NULL, NULL);
153 g_warning ("%s Eeek: processing 0px rectangle", G_STRLOC);
154 /* when this case is hit.. we've done something bad.. */
155 gegl_operation_context_take_object (context, "output", G_OBJECT (output));
156 return TRUE;
157 }
158
159 if (operation->node->passthrough)
160 {
161 GeglBuffer *input = GEGL_BUFFER (gegl_operation_context_get_object (context, "input"));
162 gegl_operation_context_take_object (context, output_pad, g_object_ref (G_OBJECT (input)));
163 return TRUE;
164 }
165
166 g_return_val_if_fail (klass->process, FALSE);
167
168 n_pixels = (gint64) result->width * (gint64) result->height;
169
170 update_pixel_time = n_pixels >=
171 GEGL_OPERATION_MIN_PIXELS_PER_PIXEL_TIME_UPDATE;
172
173 if (update_pixel_time)
174 t = g_get_monotonic_time ();
175
176 success = klass->process (operation, context, output_pad, result, level);
177
178 if (success && update_pixel_time)
179 {
180 t = g_get_monotonic_time () - t;
181
182 gegl_operation_update_pixel_time (operation, result,
183 (gdouble) t / G_TIME_SPAN_SECOND);
184 }
185
186 return success;
187 }
188
189
190 /* Calls an extending class' get_bound_box method if defined otherwise
191 * just returns a zero-initialised bounding box
192 */
193 GeglRectangle
gegl_operation_get_bounding_box(GeglOperation * self)194 gegl_operation_get_bounding_box (GeglOperation *self)
195 {
196 GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (self);
197 GeglRectangle rect = { 0, 0, 0, 0 };
198
199 g_return_val_if_fail (GEGL_IS_OPERATION (self), rect);
200 g_return_val_if_fail (GEGL_IS_NODE (self->node), rect);
201
202 if (self->node->passthrough)
203 {
204 GeglRectangle result = { 0, 0, 0, 0 };
205 GeglRectangle *in_rect;
206 in_rect = gegl_operation_source_get_bounding_box (self, "input");
207 if (in_rect)
208 return *in_rect;
209 return result;
210 }
211 else if (klass->get_bounding_box)
212 return klass->get_bounding_box (self);
213
214 return rect;
215 }
216
217 GeglRectangle
gegl_operation_get_invalidated_by_change(GeglOperation * self,const gchar * input_pad,const GeglRectangle * input_region)218 gegl_operation_get_invalidated_by_change (GeglOperation *self,
219 const gchar *input_pad,
220 const GeglRectangle *input_region)
221 {
222 GeglOperationClass *klass;
223 GeglRectangle retval = { 0, };
224
225 g_return_val_if_fail (GEGL_IS_OPERATION (self), retval);
226 g_return_val_if_fail (input_pad != NULL, retval);
227 g_return_val_if_fail (input_region != NULL, retval);
228
229 if (self->node && self->node->passthrough)
230 return *input_region;
231
232 klass = GEGL_OPERATION_GET_CLASS (self);
233
234 if (input_region->width == 0 ||
235 input_region->height == 0)
236 return *input_region;
237
238 if (klass->get_invalidated_by_change)
239 return klass->get_invalidated_by_change (self, input_pad, input_region);
240
241 return *input_region;
242 }
243
244 static GeglRectangle
get_required_for_output(GeglOperation * operation,const gchar * input_pad,const GeglRectangle * roi)245 get_required_for_output (GeglOperation *operation,
246 const gchar *input_pad,
247 const GeglRectangle *roi)
248 {
249 if (operation->node->passthrough)
250 return *roi;
251
252 if (operation->node->is_graph)
253 {
254 return gegl_operation_get_required_for_output (operation, input_pad, roi);
255 }
256
257 return *roi;
258 }
259
260 GeglRectangle
gegl_operation_get_required_for_output(GeglOperation * operation,const gchar * input_pad,const GeglRectangle * roi)261 gegl_operation_get_required_for_output (GeglOperation *operation,
262 const gchar *input_pad,
263 const GeglRectangle *roi)
264 {
265 GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation);
266
267 if (roi->width == 0 ||
268 roi->height == 0)
269 return *roi;
270
271 if (operation->node->passthrough)
272 return *roi;
273
274 g_assert (klass->get_required_for_output);
275
276 return klass->get_required_for_output (operation, input_pad, roi);
277 }
278
279
280
281 GeglRectangle
gegl_operation_get_cached_region(GeglOperation * operation,const GeglRectangle * roi)282 gegl_operation_get_cached_region (GeglOperation *operation,
283 const GeglRectangle *roi)
284 {
285 GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation);
286
287 if (operation->node->passthrough)
288 return *roi;
289
290 if (!klass->get_cached_region)
291 {
292 return *roi;
293 }
294
295 return klass->get_cached_region (operation, roi);
296 }
297
298 static void
attach(GeglOperation * self)299 attach (GeglOperation *self)
300 {
301 g_warning ("kilroy was at What The Hack (%p, %s)\n", (void *) self,
302 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (self)));
303 return;
304 }
305
306 void
gegl_operation_attach(GeglOperation * self,GeglNode * node)307 gegl_operation_attach (GeglOperation *self,
308 GeglNode *node)
309 {
310 GeglOperationClass *klass;
311 GeglOperationPrivate *priv;
312
313 g_return_if_fail (GEGL_IS_OPERATION (self));
314 g_return_if_fail (GEGL_IS_NODE (node));
315
316 klass = GEGL_OPERATION_GET_CLASS (self);
317 priv = gegl_operation_get_instance_private (self);
318
319 g_assert (klass->attach);
320 self->node = node;
321 klass->attach (self);
322 priv->attached = TRUE;
323
324 if (GEGL_IS_OPERATION_META (self))
325 {
326 GeglOperationMetaClass *meta_klass = GEGL_OPERATION_META_CLASS (klass);
327 if (meta_klass->update)
328 meta_klass->update (self);
329 }
330 }
331
332 gboolean
_gegl_operation_is_attached(GeglOperation * self)333 _gegl_operation_is_attached (GeglOperation *self)
334 {
335 GeglOperationPrivate *priv;
336 if (!self) return FALSE;
337 priv = gegl_operation_get_instance_private (self);
338 return priv->attached;
339 }
340
341 /* Calls the prepare function on the operation that extends this base class */
342 void
gegl_operation_prepare(GeglOperation * self)343 gegl_operation_prepare (GeglOperation *self)
344 {
345 GeglOperationClass *klass;
346
347 g_return_if_fail (GEGL_IS_OPERATION (self));
348
349 if (self->node->passthrough)
350 {
351 const Babl *format;
352
353 format = gegl_operation_get_source_format (self, "input");
354 gegl_operation_set_format (self, "output", format);
355 return;
356 }
357
358 klass = GEGL_OPERATION_GET_CLASS (self);
359
360 /* build OpenCL kernel */
361 if (!klass->cl_data)
362 {
363 const gchar *cl_source = gegl_operation_class_get_key (klass, "cl-source");
364 if (cl_source)
365 {
366 char *name = g_strdup (klass->name);
367 const char *kernel_name[] = {name, NULL};
368 char *k;
369 for (k=name; *k; k++)
370 switch (*k)
371 {
372 case ' ': case ':': case '-':
373 *k = '_';
374 break;
375 }
376 klass->cl_data = gegl_cl_compile_and_build (cl_source, kernel_name);
377 g_free (name);
378 }
379 }
380
381 if (klass->prepare)
382 klass->prepare (self);
383 }
384
385 GeglNode *
gegl_operation_get_source_node(GeglOperation * operation,const gchar * input_pad_name)386 gegl_operation_get_source_node (GeglOperation *operation,
387 const gchar *input_pad_name)
388 {
389 GeglNode *node;
390 GeglPad *pad;
391
392 g_return_val_if_fail (GEGL_IS_OPERATION (operation), NULL);
393 g_return_val_if_fail (GEGL_IS_NODE (operation->node), NULL);
394 g_return_val_if_fail (input_pad_name != NULL, NULL);
395
396 node = operation->node;
397 if (node->is_graph)
398 {
399 node = gegl_node_get_input_proxy (node, input_pad_name);
400 input_pad_name = "input";
401 }
402
403 pad = gegl_node_get_pad (node, input_pad_name);
404
405 if (!pad)
406 return NULL;
407
408 pad = gegl_pad_get_connected_to (pad);
409
410 if (!pad)
411 return NULL;
412
413 g_assert (gegl_pad_get_node (pad));
414
415 return gegl_pad_get_node (pad);
416 }
417
418 GeglRectangle *
gegl_operation_source_get_bounding_box(GeglOperation * operation,const gchar * input_pad_name)419 gegl_operation_source_get_bounding_box (GeglOperation *operation,
420 const gchar *input_pad_name)
421 {
422 GeglNode *node = gegl_operation_get_source_node (operation, input_pad_name);
423
424 if (node)
425 {
426 GeglRectangle *ret;
427 /* g_mutex_lock (&node->mutex); */
428 /* make sure node->have_rect is valid */
429 (void) gegl_node_get_bounding_box (node);
430 ret = &node->have_rect;
431 /* g_mutex_unlock (&node->mutex); */
432 return ret;
433 }
434
435 return NULL;
436 }
437
438 static GeglRectangle
get_bounding_box(GeglOperation * self)439 get_bounding_box (GeglOperation *self)
440 {
441 GeglRectangle rect = { 0, 0, 0, 0 };
442
443 if (self->node->is_graph)
444 {
445 GeglOperation *operation;
446
447 operation = gegl_node_get_output_proxy (self->node, "output")->operation;
448 rect = gegl_operation_get_bounding_box (operation);
449 }
450 else
451 {
452 g_warning ("Operation '%s' has no get_bounding_box() method",
453 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (self)));
454 }
455
456 return rect;
457 }
458
459 static GeglRectangle
get_invalidated_by_change(GeglOperation * self,const gchar * input_pad,const GeglRectangle * input_region)460 get_invalidated_by_change (GeglOperation *self,
461 const gchar *input_pad,
462 const GeglRectangle *input_region)
463 {
464 return *input_region;
465 }
466
467 /* returns a freshly allocated list of the properties of the object, does not list
468 * the regular gobject properties of GeglNode ('name' and 'operation') */
469 GParamSpec **
gegl_operation_list_properties(const gchar * operation_type,guint * n_properties_p)470 gegl_operation_list_properties (const gchar *operation_type,
471 guint *n_properties_p)
472 {
473 GParamSpec **pspecs;
474 GType type;
475 GObjectClass *klass;
476
477 type = gegl_operation_gtype_from_name (operation_type);
478 if (!type)
479 {
480 if (n_properties_p)
481 *n_properties_p = 0;
482 return NULL;
483 }
484 klass = g_type_class_ref (type);
485 pspecs = g_object_class_list_properties (klass, n_properties_p);
486 g_type_class_unref (klass);
487 return pspecs;
488 }
489
490 GParamSpec *
gegl_operation_find_property(const gchar * operation_type,const gchar * property_name)491 gegl_operation_find_property (const gchar *operation_type,
492 const gchar *property_name)
493 {
494 GParamSpec *ret = NULL;
495 GType type;
496 GObjectClass *klass;
497
498 type = gegl_operation_gtype_from_name (operation_type);
499 if (!type)
500 return NULL;
501
502 klass = g_type_class_ref (type);
503 ret = g_object_class_find_property (klass, property_name);
504 g_type_class_unref (klass);
505
506 return ret;
507 }
508
509 GeglNode *
gegl_operation_detect(GeglOperation * operation,gint x,gint y)510 gegl_operation_detect (GeglOperation *operation,
511 gint x,
512 gint y)
513 {
514 GeglNode *node = NULL;
515 GeglOperationClass *klass;
516
517 if (!operation)
518 return NULL;
519
520 g_return_val_if_fail (GEGL_IS_OPERATION (operation), NULL);
521 node = operation->node;
522
523 klass = GEGL_OPERATION_GET_CLASS (operation);
524
525 if (klass->detect)
526 {
527 return klass->detect (operation, x, y);
528 }
529
530 if (x >= node->have_rect.x &&
531 x < node->have_rect.x + node->have_rect.width &&
532 y >= node->have_rect.y &&
533 y < node->have_rect.y + node->have_rect.height)
534 {
535 return node;
536 }
537 return NULL;
538 }
539
540 void
gegl_operation_set_format(GeglOperation * self,const gchar * pad_name,const Babl * format)541 gegl_operation_set_format (GeglOperation *self,
542 const gchar *pad_name,
543 const Babl *format)
544 {
545 GeglPad *pad;
546
547 g_return_if_fail (GEGL_IS_OPERATION (self));
548 g_return_if_fail (pad_name != NULL);
549
550 pad = gegl_node_get_pad (self->node, pad_name);
551
552 g_return_if_fail (pad != NULL);
553
554 pad->format = format;
555 }
556
557 const Babl *
gegl_operation_get_format(GeglOperation * self,const gchar * pad_name)558 gegl_operation_get_format (GeglOperation *self,
559 const gchar *pad_name)
560 {
561 GeglPad *pad;
562
563 g_return_val_if_fail (GEGL_IS_OPERATION (self), NULL);
564 g_return_val_if_fail (pad_name != NULL, NULL);
565
566 pad = gegl_node_get_pad (self->node, pad_name);
567
568 g_return_val_if_fail (pad != NULL, NULL);
569
570 return pad->format;
571 }
572
573 const gchar *
gegl_operation_get_name(GeglOperation * operation)574 gegl_operation_get_name (GeglOperation *operation)
575 {
576 GeglOperationClass *klass;
577
578 g_return_val_if_fail (GEGL_IS_OPERATION (operation), NULL);
579
580 klass = GEGL_OPERATION_GET_CLASS (operation);
581
582 return klass->name;
583 }
584
585 void
gegl_operation_invalidate(GeglOperation * operation,const GeglRectangle * roi,gboolean clear_cache)586 gegl_operation_invalidate (GeglOperation *operation,
587 const GeglRectangle *roi,
588 gboolean clear_cache)
589 {
590 g_return_if_fail (GEGL_IS_OPERATION (operation));
591
592 if (operation->node)
593 gegl_node_invalidated (operation->node, roi, clear_cache);
594 }
595
596 gboolean
gegl_operation_cl_set_kernel_args(GeglOperation * operation,cl_kernel kernel,gint * p,cl_int * err)597 gegl_operation_cl_set_kernel_args (GeglOperation *operation,
598 cl_kernel kernel,
599 gint *p,
600 cl_int *err)
601 {
602 GParamSpec **self;
603 GParamSpec **parent;
604 guint n_self;
605 guint n_parent;
606 gint prop_no;
607
608 self = g_object_class_list_properties (
609 G_OBJECT_CLASS (g_type_class_ref (G_OBJECT_CLASS_TYPE (GEGL_OPERATION_GET_CLASS(operation)))),
610 &n_self);
611
612 parent = g_object_class_list_properties (
613 G_OBJECT_CLASS (g_type_class_ref (GEGL_TYPE_OPERATION)),
614 &n_parent);
615
616 for (prop_no=0;prop_no<n_self;prop_no++)
617 {
618 gint parent_no;
619 gboolean found=FALSE;
620 for (parent_no=0;parent_no<n_parent;parent_no++)
621 if (self[prop_no]==parent[parent_no])
622 found=TRUE;
623 /* only print properties if we are an addition compared to
624 * GeglOperation
625 */
626
627 /* Removing pads */
628 if (!strcmp(g_param_spec_get_name (self[prop_no]), "input") ||
629 !strcmp(g_param_spec_get_name (self[prop_no]), "output") ||
630 !strcmp(g_param_spec_get_name (self[prop_no]), "aux"))
631 continue;
632
633 if (!found)
634 {
635 if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (self[prop_no]), G_TYPE_DOUBLE))
636 {
637 gdouble value;
638 cl_float v;
639
640 g_object_get (G_OBJECT (operation), g_param_spec_get_name (self[prop_no]), &value, NULL);
641
642 v = value;
643 *err = gegl_clSetKernelArg(kernel, (*p)++, sizeof(cl_float), (void*)&v);
644 }
645 else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (self[prop_no]), G_TYPE_FLOAT))
646 {
647 gfloat value;
648 cl_float v;
649
650 g_object_get (G_OBJECT (operation), g_param_spec_get_name (self[prop_no]), &value, NULL);
651
652 v = value;
653 *err = gegl_clSetKernelArg(kernel, (*p)++, sizeof(cl_float), (void*)&v);
654 }
655 else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (self[prop_no]), G_TYPE_INT))
656 {
657 gint value;
658 cl_int v;
659
660 g_object_get (G_OBJECT (operation), g_param_spec_get_name (self[prop_no]), &value, NULL);
661
662 v = value;
663 *err = gegl_clSetKernelArg(kernel, (*p)++, sizeof(cl_int), (void*)&v);
664 }
665 else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (self[prop_no]), G_TYPE_BOOLEAN))
666 {
667 gboolean value;
668 cl_bool v;
669
670 g_object_get (G_OBJECT (operation), g_param_spec_get_name (self[prop_no]), &value, NULL);
671
672 v = value;
673 *err = gegl_clSetKernelArg(kernel, (*p)++, sizeof(cl_bool), (void*)&v);
674 }
675 else
676 {
677 g_error ("Unsupported OpenCL kernel argument");
678 return FALSE;
679 }
680 }
681 }
682
683 g_free (self);
684 g_free (parent);
685
686 return TRUE;
687 }
688
689 gchar **
gegl_operation_list_keys(const gchar * operation_name,guint * n_keys)690 gegl_operation_list_keys (const gchar *operation_name,
691 guint *n_keys)
692 {
693 GType type;
694 GeglOperationClass *klass;
695 GList *list, *l;
696 gchar **ret;
697 int count;
698 int i;
699 g_return_val_if_fail (operation_name != NULL, NULL);
700 type = gegl_operation_gtype_from_name (operation_name);
701 if (!type)
702 {
703 if (n_keys)
704 *n_keys = 0;
705 return NULL;
706 }
707 klass = g_type_class_ref (type);
708 if (! GEGL_IS_OPERATION_CLASS (klass))
709 {
710 g_type_class_unref (klass);
711
712 g_return_val_if_fail (GEGL_IS_OPERATION_CLASS (klass), NULL);
713 }
714 if (! klass->keys)
715 {
716 if (n_keys)
717 *n_keys = 0;
718 return NULL;
719 }
720 count = g_hash_table_size (klass->keys);
721 ret = g_malloc0 (sizeof (gpointer) * (count + 1));
722 list = g_hash_table_get_keys (klass->keys);
723 for (i = 0, l = list; l; l = l->next, i++)
724 {
725 ret[i] = l->data;
726 }
727 g_list_free (list);
728 if (n_keys)
729 *n_keys = count;
730 g_type_class_unref (klass);
731 return ret;
732 }
733
734 void
gegl_operation_class_set_key(GeglOperationClass * klass,const gchar * key_name,const gchar * key_value)735 gegl_operation_class_set_key (GeglOperationClass *klass,
736 const gchar *key_name,
737 const gchar *key_value)
738 {
739 gchar *key_value_dup;
740
741 g_return_if_fail (GEGL_IS_OPERATION_CLASS (klass));
742 g_return_if_fail (key_name != NULL);
743
744 if (!key_value)
745 {
746 if (klass->keys)
747 {
748 g_hash_table_remove (klass->keys, key_name);
749
750 if (g_hash_table_size (klass->keys) == 0)
751 g_clear_pointer (&klass->keys, g_hash_table_unref);
752 }
753
754 return;
755 }
756
757 key_value_dup = g_strdup (key_value);
758
759 if (!strcmp (key_name, "name"))
760 {
761 klass->name = key_value_dup;
762 gegl_operation_class_register_name (klass, key_value, FALSE);
763 }
764 else if (!strcmp (key_name, "compat-name"))
765 {
766 klass->compat_name = key_value_dup;
767 gegl_operation_class_register_name (klass, key_value, TRUE);
768 }
769
770 if (! klass->keys ||
771 /* avoid inheriting an existing hash table from the parent class */
772 g_hash_table_lookup (klass->keys, "operation-class") != klass)
773 {
774 /* XXX: leaked for now */
775 klass->keys = g_hash_table_new_full (g_str_hash, g_str_equal,
776 g_free, g_free);
777
778 /* ... so we don't actually have to worry about these values being
779 * freed ...
780 */
781 g_hash_table_insert (klass->keys, "operation-class", klass);
782 }
783
784 g_hash_table_insert (klass->keys, g_strdup (key_name),
785 (void*)key_value_dup);
786 }
787
788 void
gegl_operation_class_set_keys(GeglOperationClass * klass,const gchar * key_name,...)789 gegl_operation_class_set_keys (GeglOperationClass *klass,
790 const gchar *key_name,
791 ...)
792 {
793 va_list var_args;
794
795 g_return_if_fail (GEGL_IS_OPERATION_CLASS (klass));
796
797 va_start (var_args, key_name);
798 while (key_name)
799 {
800 const char *value = va_arg (var_args, char *);
801
802 gegl_operation_class_set_key (klass, key_name, value);
803
804 key_name = va_arg (var_args, char *);
805 }
806 va_end (var_args);
807 }
808
809 void
gegl_operation_set_key(const gchar * operation_name,const gchar * key_name,const gchar * key_value)810 gegl_operation_set_key (const gchar *operation_name,
811 const gchar *key_name,
812 const gchar *key_value)
813 {
814 GType type;
815 GObjectClass *klass;
816 type = gegl_operation_gtype_from_name (operation_name);
817 if (!type)
818 return;
819 klass = g_type_class_ref (type);
820 gegl_operation_class_set_key (GEGL_OPERATION_CLASS (klass), key_name, key_value);
821 g_type_class_unref (klass);
822 }
823
824 const gchar *
gegl_operation_class_get_key(GeglOperationClass * klass,const gchar * key_name)825 gegl_operation_class_get_key (GeglOperationClass *klass,
826 const gchar *key_name)
827 {
828 g_return_val_if_fail (GEGL_IS_OPERATION_CLASS (klass), NULL);
829 g_return_val_if_fail (key_name != NULL, NULL);
830
831 if (! klass->keys)
832 return NULL;
833
834 return g_hash_table_lookup (klass->keys, key_name);
835 }
836
837 const gchar *
gegl_operation_get_key(const gchar * operation_name,const gchar * key_name)838 gegl_operation_get_key (const gchar *operation_name,
839 const gchar *key_name)
840 {
841 GType type;
842 GObjectClass *klass;
843 const gchar *ret = NULL;
844 type = gegl_operation_gtype_from_name (operation_name);
845 if (!type)
846 {
847 return NULL;
848 }
849 klass = g_type_class_ref (type);
850 ret = gegl_operation_class_get_key (GEGL_OPERATION_CLASS (klass), key_name);
851 g_type_class_unref (klass);
852 return ret;
853 }
854
855 gboolean
gegl_operation_use_opencl(const GeglOperation * operation)856 gegl_operation_use_opencl (const GeglOperation *operation)
857 {
858 g_return_val_if_fail (operation->node, FALSE);
859 return operation->node->use_opencl && gegl_cl_is_accelerated ();
860 }
861
862 const Babl *
gegl_operation_get_source_format(GeglOperation * operation,const gchar * padname)863 gegl_operation_get_source_format (GeglOperation *operation,
864 const gchar *padname)
865 {
866 GeglNode *src_node = gegl_operation_get_source_node (operation, padname);
867
868 if (src_node)
869 {
870 GeglOperation *op = src_node->operation;
871 if (op)
872 return gegl_operation_get_format (op, "output");
873 /* XXX: could be a different pad than "output" */
874 }
875 return NULL;
876 }
877
878 gboolean
gegl_operation_use_threading(GeglOperation * operation,const GeglRectangle * roi)879 gegl_operation_use_threading (GeglOperation *operation,
880 const GeglRectangle *roi)
881 {
882 gint threads = gegl_config_threads ();
883 if (threads == 1)
884 return FALSE;
885
886 {
887 GeglOperationClass *op_class;
888 op_class = GEGL_OPERATION_GET_CLASS (operation);
889
890 if (op_class->opencl_support && gegl_cl_is_accelerated ())
891 return FALSE;
892
893 if (op_class->threaded &&
894 (gdouble) roi->width * (gdouble) roi->height >=
895 2 * gegl_operation_get_pixels_per_thread (operation))
896 return TRUE;
897 }
898 return FALSE;
899 }
900
901 static gboolean
gegl_operation_dynamic_thread_cost(void)902 gegl_operation_dynamic_thread_cost (void)
903 {
904 static gint dynamic_thread_cost = -1;
905
906 if (dynamic_thread_cost < 0)
907 {
908 if (g_getenv ("GEGL_DYNAMIC_THREAD_COST"))
909 {
910 dynamic_thread_cost = atoi (g_getenv ("GEGL_DYNAMIC_THREAD_COST")) ?
911 TRUE : FALSE;
912 }
913 else
914 {
915 dynamic_thread_cost = TRUE;
916 }
917 }
918
919 return dynamic_thread_cost;
920 }
921
922 gdouble
gegl_operation_get_pixels_per_thread(GeglOperation * operation)923 gegl_operation_get_pixels_per_thread (GeglOperation *operation)
924 {
925 GeglOperationPrivate *priv = gegl_operation_get_instance_private (operation);
926
927 if (priv->pixel_time < 0.0 || ! gegl_operation_dynamic_thread_cost ())
928 return GEGL_OPERATION_DEFAULT_PIXELS_PER_THREAD;
929 else if (priv->pixel_time == 0.0)
930 return GEGL_OPERATION_MAX_PIXELS_PER_THREAD;
931
932 return MIN (gegl_parallel_distribute_get_thread_time () / priv->pixel_time,
933 GEGL_OPERATION_MAX_PIXELS_PER_THREAD);
934 }
935
936 static void
gegl_operation_update_pixel_time(GeglOperation * self,const GeglRectangle * roi,gdouble t)937 gegl_operation_update_pixel_time (GeglOperation *self,
938 const GeglRectangle *roi,
939 gdouble t)
940 {
941 GeglOperationPrivate *priv = gegl_operation_get_instance_private (self);
942 gdouble n_pixels;
943 gint n_threads = 1;
944
945 n_pixels = (gdouble) roi->width * (gdouble) roi->height;
946
947 if (gegl_operation_use_threading (self, roi))
948 {
949 /* we're assuming the entire processing cost was distributed over the
950 * optimal number of threads, as per the op's thread cost, which might
951 * not always be the case, but should generally be about right.
952 */
953 n_threads = gegl_parallel_distribute_get_optimal_n_threads (
954 n_pixels,
955 gegl_operation_get_pixels_per_thread (self));
956 }
957
958 priv->pixel_time = (t - (n_threads - 1) *
959 gegl_parallel_distribute_get_thread_time ()) *
960 n_threads / n_pixels;
961 priv->pixel_time = MAX (priv->pixel_time, 0.0);
962 }
963
964 static guchar *gegl_temp_alloc[GEGL_MAX_THREADS * 4]={NULL,};
965 static gint gegl_temp_size[GEGL_MAX_THREADS * 4]={0,};
966
gegl_temp_buffer(int no,int size)967 guchar *gegl_temp_buffer (int no, int size)
968 {
969 if (!gegl_temp_alloc[no] || gegl_temp_size[no] < size)
970 {
971 if (gegl_temp_alloc[no])
972 gegl_free (gegl_temp_alloc[no]);
973 gegl_temp_alloc[no] = gegl_malloc (size);
974 gegl_temp_size[no] = size;
975 }
976 return gegl_temp_alloc[no];
977 }
978
979 void gegl_temp_buffer_free (void);
gegl_temp_buffer_free(void)980 void gegl_temp_buffer_free (void)
981 {
982 int no;
983 for (no = 0; no < GEGL_MAX_THREADS * 4; no++)
984 if (gegl_temp_alloc[no])
985 {
986 gegl_free (gegl_temp_alloc[no]);
987 gegl_temp_alloc[no] = NULL;
988 gegl_temp_size[no] = 0;
989 }
990 }
991
gegl_operation_progress(GeglOperation * operation,gdouble progress,gchar * message)992 void gegl_operation_progress (GeglOperation *operation,
993 gdouble progress,
994 gchar *message)
995 {
996 if (operation->node)
997 gegl_node_progress (operation->node, progress, message);
998 }
999
1000 const Babl *
gegl_operation_get_source_space(GeglOperation * operation,const char * in_pad)1001 gegl_operation_get_source_space (GeglOperation *operation, const char *in_pad)
1002 {
1003 const Babl *source_format = gegl_operation_get_source_format (operation, "input");
1004 if (source_format)
1005 return babl_format_get_space (source_format);
1006 return NULL;
1007 }
1008
1009 gboolean
gegl_operation_use_cache(GeglOperation * operation)1010 gegl_operation_use_cache (GeglOperation *operation)
1011 {
1012 GeglOperationClass *klass = GEGL_OPERATION_GET_CLASS (operation);
1013
1014 switch (klass->cache_policy)
1015 {
1016 case GEGL_CACHE_POLICY_AUTO:
1017 return ! klass->no_cache && klass->get_cached_region != NULL;
1018
1019 case GEGL_CACHE_POLICY_NEVER:
1020 return FALSE;
1021
1022 case GEGL_CACHE_POLICY_ALWAYS:
1023 return TRUE;
1024 }
1025
1026 g_return_val_if_reached (FALSE);
1027 }
1028