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