Exemplo n.º 1
0
static void
standard_output (const gchar *op_name)
{
  GeglNode *composition, *input, *aux, *operation, *crop, *output;
  gchar    *input_path  = g_build_path (G_DIR_SEPARATOR_S, data_dir,
                                        "standard-input.png", NULL);
  gchar    *aux_path    = g_build_path (G_DIR_SEPARATOR_S, data_dir,
                                        "standard-aux.png", NULL);
  gchar    *output_path = operation_to_path (op_name, FALSE);

  composition = gegl_node_new ();
  operation = gegl_node_create_child (composition, op_name);

  if (gegl_node_has_pad (operation, "output"))
    {
      input = gegl_node_new_child (composition,
                                   "operation", "gegl:load",
                                   "path", input_path,
                                   NULL);
      aux = gegl_node_new_child (composition,
                                 "operation", "gegl:load",
                                 "path", aux_path,
                                 NULL);
      crop = gegl_node_new_child (composition,
                                  "operation", "gegl:crop",
                                  "width", 200.0,
                                  "height", 200.0,
                                  NULL);
      output = gegl_node_new_child (composition,
                                    "operation", "gegl:png-save",
                                    "compression", 9,
                                    "path", output_path,
                                    NULL);

      gegl_node_link_many (operation, crop, output, NULL);

      if (gegl_node_has_pad (operation, "input"))
        gegl_node_link (input, operation);

      if (gegl_node_has_pad (operation, "aux"))
        gegl_node_connect_to (aux, "output", operation, "aux");

      gegl_node_process (output);
    }

  g_free (input_path);
  g_free (aux_path);
  g_free (output_path);
  g_object_unref (composition);
}
Exemplo n.º 2
0
static void
gimp_operation_tool_create_gui (GimpOperationTool *op_tool)
{
  GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (op_tool);
  GtkWidget      *options_gui;
  gint            off_x, off_y;
  GeglRectangle   area;
  gint            aux;

  gimp_filter_tool_get_drawable_area (filter_tool, &off_x, &off_y, &area);

  options_gui =
    gimp_prop_gui_new (G_OBJECT (filter_tool->config),
                       G_TYPE_FROM_INSTANCE (filter_tool->config), 0,
                       &area,
                       GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (op_tool)),
                       (GimpCreatePickerFunc) gimp_filter_tool_add_color_picker,
                       (GimpCreateControllerFunc) gimp_filter_tool_add_controller,
                       filter_tool);
  g_weak_ref_set (&op_tool->options_gui_ref, options_gui);

  for (aux = 1; ; aux++)
    {
      gchar pad[32];
      gchar label[32];

      if (aux == 1)
        {
          g_snprintf (pad,   sizeof (pad),   "aux");
          /* don't translate "Aux" */
          g_snprintf (label, sizeof (label), _("Aux Input"));
        }
      else
        {
          g_snprintf (pad,   sizeof (pad),   "aux%d", aux);
          /* don't translate "Aux" */
          g_snprintf (label, sizeof (label), _("Aux%d Input"), aux);
        }

      if (gegl_node_has_pad (filter_tool->operation, pad))
        {
          AuxInput *input;

          input = gimp_operation_tool_aux_input_new (op_tool,
                                                     filter_tool->operation, pad,
                                                     label);

          op_tool->aux_inputs = g_list_append (op_tool->aux_inputs, input);
        }
      else
        {
          break;
        }
    }
}
Exemplo n.º 3
0
void layer_set_graph(GeglEditorLayer* self, GeglNode* gegl)
{
  //properly dispose of old gegl graph
  self->gegl = gegl;
  gegl_editor_remove_all_nodes(self->editor);

  GSList *list = gegl_node_get_children(gegl);
  for(;list != NULL; list = list->next)
    {
      GeglNode* node = GEGL_NODE(list->data);
      g_print("Loading %s\n", gegl_node_get_operation(node));
      layer_add_gegl_node(self, node);
    }

  list = gegl_node_get_children(gegl);

  for(list = g_slist_reverse(list); list != NULL; list = list->next)
    {
      GeglNode* node = GEGL_NODE(list->data);
      gint from = get_editor_node_id(self, node);

      GeglNode** nodes;
      const gchar** pads;

      if(!gegl_node_has_pad(node, "output")) {
    	  break;}

      gint num = gegl_node_get_consumers(node, "output", &nodes, &pads);

      int i;
      g_print("%s: %d consumer(s)\n", gegl_node_get_operation(node), num);
      for(i = 0; i < num; i++)
	{
	  gint to = get_editor_node_id(self, nodes[i]);
	  g_print("Connecting to consumer (%s to %s): output->%s\n", gegl_node_get_operation(node), gegl_node_get_operation(nodes[0]), pads[0]);
	  gegl_editor_add_connection(self->editor, from, to, "output", pads[0]);
	}
    }
}
Exemplo n.º 4
0
void
gimp_operation_tool_set_operation (GimpOperationTool *tool,
                                   const gchar       *operation,
                                   const gchar       *undo_desc,
                                   const gchar       *icon_name)
{
    GimpImageMapTool *im_tool;
    GtkSizeGroup     *size_group = NULL;
    gint              aux;

    g_return_if_fail (GIMP_IS_OPERATION_TOOL (tool));
    g_return_if_fail (operation != NULL);

    im_tool = GIMP_IMAGE_MAP_TOOL (tool);

    if (tool->operation)
        g_free (tool->operation);

    if (tool->undo_desc)
        g_free (tool->undo_desc);

    if (tool->icon_name)
        g_free (tool->icon_name);

    tool->operation = g_strdup (operation);
    tool->undo_desc = g_strdup (undo_desc);
    tool->icon_name = g_strdup (icon_name);

    g_list_free_full (tool->aux_inputs,
                      (GDestroyNotify) gimp_operation_tool_aux_input_free);
    tool->aux_inputs = NULL;

    gimp_image_map_tool_get_operation (im_tool);

    if (undo_desc)
        GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = "yes"; /* XXX hack */
    else
        GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = NULL; /* XXX hack */

    if (tool->options_gui)
    {
        gtk_widget_destroy (tool->options_gui);
        tool->options_gui = NULL;

        if (im_tool->active_picker)
        {
            im_tool->active_picker = NULL;
            gimp_color_tool_disable (GIMP_COLOR_TOOL (tool));
        }
    }

    for (aux = 1; ; aux++)
    {
        gchar pad[32];
        gchar label[32];

        if (aux == 1)
        {
            g_snprintf (pad,   sizeof (pad),   "aux");
            g_snprintf (label, sizeof (label), _("Aux Input"));
        }
        else
        {
            g_snprintf (pad,   sizeof (pad),   "aux%d", aux);
            g_snprintf (label, sizeof (label), _("Aux%d Input"), aux);
        }

        if (gegl_node_has_pad (im_tool->operation, pad))
        {
            AuxInput  *input;
            GtkWidget *toggle;

            if (! size_group)
                size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);

            input = gimp_operation_tool_aux_input_new (tool,
                    im_tool->operation, pad,
                    label);

            tool->aux_inputs = g_list_append (tool->aux_inputs, input);

            toggle = gimp_buffer_source_box_get_toggle (GIMP_BUFFER_SOURCE_BOX (input->box));
            gtk_size_group_add_widget (size_group, toggle);

            if (tool->options_box)
            {
                gtk_box_pack_start (GTK_BOX (tool->options_box), input->box,
                                    FALSE, FALSE, 0);
                gtk_widget_show (input->box);
            }
        }
        else
        {
            break;
        }
    }

    if (size_group)
        g_object_unref (size_group);

    if (im_tool->config)
    {
        tool->options_gui =
            gimp_prop_gui_new (G_OBJECT (im_tool->config),
                               G_TYPE_FROM_INSTANCE (im_tool->config),
                               GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)),
                               (GimpCreatePickerFunc) gimp_image_map_tool_add_color_picker,
                               tool);

        if (tool->options_box)
        {
            gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui,
                                FALSE, FALSE, 0);
            gtk_widget_show (tool->options_gui);
        }
    }

    if (im_tool->gui)
    {
        if (undo_desc)
            gimp_tool_gui_set_description (im_tool->gui, undo_desc);

        if (icon_name)
            gimp_tool_gui_set_icon_name (im_tool->gui, icon_name);
    }

    if (GIMP_TOOL (tool)->drawable)
    {
        gimp_operation_tool_sync_op (tool, GIMP_TOOL (tool)->drawable);
        gimp_image_map_tool_preview (im_tool);
    }
}
Exemplo n.º 5
0
void
gimp_gegl_apply_operation (GeglBuffer          *src_buffer,
                           GimpProgress        *progress,
                           const gchar         *undo_desc,
                           GeglNode            *operation,
                           GeglBuffer          *dest_buffer,
                           const GeglRectangle *dest_rect)
{
  GeglNode      *gegl;
  GeglNode      *dest_node;
  GeglRectangle  rect = { 0, };
  gdouble        value;
  gboolean       progress_active = FALSE;

  g_return_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer));
  g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
  g_return_if_fail (GEGL_IS_NODE (operation));
  g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));

  if (dest_rect)
    {
      rect = *dest_rect;
    }
  else
    {
      rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width  (dest_buffer),
                                    gegl_buffer_get_height (dest_buffer));
    }

  gegl = gegl_node_new ();

  if (! gegl_node_get_parent (operation))
    gegl_node_add_child (gegl, operation);

  if (src_buffer && gegl_node_has_pad (operation, "input"))
    {
      GeglNode *src_node;

      /* dup() because reading and writing the same buffer doesn't
       * work with area ops when using a processor. See bug #701875.
       */
      if (progress && (src_buffer == dest_buffer))
        src_buffer = gegl_buffer_dup (src_buffer);
      else
        g_object_ref (src_buffer);

      src_node = gegl_node_new_child (gegl,
                                      "operation", "gegl:buffer-source",
                                      "buffer",    src_buffer,
                                      NULL);

      g_object_unref (src_buffer);

      gegl_node_connect_to (src_node,  "output",
                            operation, "input");
    }

  dest_node = gegl_node_new_child (gegl,
                                   "operation", "gegl:write-buffer",
                                   "buffer",    dest_buffer,
                                   NULL);


  gegl_node_connect_to (operation, "output",
                        dest_node, "input");

  if (progress)
    {
      GeglProcessor *processor;

      processor = gegl_node_new_processor (dest_node, &rect);

      progress_active = gimp_progress_is_active (progress);

      if (progress_active)
        {
          if (undo_desc)
            gimp_progress_set_text (progress, undo_desc);
        }
      else
        {
          gimp_progress_start (progress, undo_desc, FALSE);
        }

      while (gegl_processor_work (processor, &value))
        gimp_progress_set_value (progress, value);

      g_object_unref (processor);
    }
  else
    {
      gegl_node_blit (dest_node, 1.0, &rect,
                      NULL, NULL, 0, GEGL_BLIT_DEFAULT);
    }

  g_object_unref (gegl);

  if (progress && ! progress_active)
    gimp_progress_end (progress);
}
Exemplo n.º 6
0
static void
standard_output (const gchar *op_name)
{
  GeglNode *composition, *input, *aux, *operation, *crop, *output, *translate;
  GeglNode *background,  *over;
  gchar    *input_path  = g_build_path (G_DIR_SEPARATOR_S, data_dir,
                                        "standard-input.png", NULL);
  gchar    *aux_path    = g_build_path (G_DIR_SEPARATOR_S, data_dir,
                                        "standard-aux.png", NULL);
  gchar    *output_path = operation_to_path (op_name, FALSE);

  composition = gegl_node_new ();
  operation = gegl_node_create_child (composition, op_name);

  if (gegl_node_has_pad (operation, "output"))
    {
      input = gegl_node_new_child (composition,
                                   "operation", "gegl:load",
                                   "path", input_path,
                                   NULL);
      translate  = gegl_node_new_child (composition,
                                 "operation", "gegl:translate",
                                 "x", 0.0,
                                 "y", 80.0,
                                 NULL);
      aux = gegl_node_new_child (composition,
                                 "operation", "gegl:load",
                                 "path", aux_path,
                                 NULL);
      crop = gegl_node_new_child (composition,
                                  "operation", "gegl:crop",
                                  "width", 200.0,
                                  "height", 200.0,
                                  NULL);
      output = gegl_node_new_child (composition,
                                    "operation", "gegl:png-save",
                                    "compression", 9,
                                    "path", output_path,
                                    NULL);
      background = gegl_node_new_child (composition,
                                        "operation", "gegl:checkerboard",
                                        "color1", gegl_color_new ("rgb(0.75,0.75,0.75)"),
                                        "color2", gegl_color_new ("rgb(0.25,0.25,0.25)"),
                                        NULL);
      over = gegl_node_new_child (composition, "operation", "gegl:over", NULL);


      if (gegl_node_has_pad (operation, "input"))
        gegl_node_link (input, operation);

      if (gegl_node_has_pad (operation, "aux"))
      {
        gegl_node_connect_to (aux, "output", translate, "input");
        gegl_node_connect_to (translate, "output", operation, "aux");
      }

      gegl_node_connect_to (background, "output", over, "input");
      gegl_node_connect_to (operation,  "output", over, "aux");
      gegl_node_connect_to (over,       "output", crop, "input");
      gegl_node_connect_to (crop,       "output", output, "input");


      gegl_node_process (output);
    }

  g_free (input_path);
  g_free (aux_path);
  g_free (output_path);
  g_object_unref (composition);
}
Exemplo n.º 7
0
void
gimp_operation_tool_set_operation (GimpOperationTool *tool,
                                   const gchar       *operation,
                                   const gchar       *undo_desc,
                                   const gchar       *icon_name)
{
  GimpImageMapTool *im_tool;

  g_return_if_fail (GIMP_IS_OPERATION_TOOL (tool));
  g_return_if_fail (operation != NULL);

  im_tool = GIMP_IMAGE_MAP_TOOL (tool);

  if (tool->operation)
    g_free (tool->operation);

  if (tool->undo_desc)
    g_free (tool->undo_desc);

  if (tool->icon_name)
    g_free (tool->icon_name);

  tool->operation = g_strdup (operation);
  tool->undo_desc = g_strdup (undo_desc);
  tool->icon_name = g_strdup (icon_name);

  if (tool->aux_input)
    {
      g_object_unref (tool->aux_input);
      tool->aux_input = NULL;
    }

  if (tool->aux2_input)
    {
      g_object_unref (tool->aux2_input);
      tool->aux2_input = NULL;
    }

  gimp_image_map_tool_get_operation (im_tool);

  if (undo_desc)
    GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = "yes"; /* XXX hack */
  else
    GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = NULL; /* XXX hack */

  if (tool->aux_input_box)
    {
      gtk_widget_destroy (tool->aux_input_box);
      tool->aux_input_button = NULL;
      tool->aux_input_box    = NULL;
    }

  if (tool->aux2_input_box)
    {
      gtk_widget_destroy (tool->aux2_input_box);
      tool->aux2_input_button = NULL;
      tool->aux2_input_box    = NULL;
    }

  if (tool->options_gui)
    {
      gtk_widget_destroy (tool->options_gui);
      tool->options_gui = NULL;

      if (im_tool->active_picker)
        {
          im_tool->active_picker = NULL;
          gimp_color_tool_disable (GIMP_COLOR_TOOL (tool));
        }
    }

  if (gegl_node_has_pad (im_tool->operation, "aux"))
    {
      GimpContext *context;
      GtkWidget   *label;

      tool->aux_input = gegl_node_new_child (NULL,
                                             "operation", "gegl:buffer-source",
                                             NULL);

      gegl_node_connect_to (tool->aux_input,    "output",
                            im_tool->operation, "aux");

      context = GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool));

      tool->aux_input_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);

      label = gtk_label_new_with_mnemonic (_("_Aux Input"));
      gtk_box_pack_start (GTK_BOX (tool->aux_input_box), label,
                          FALSE, FALSE, 0);
      gtk_widget_show (label);

      tool->aux_input_button =
        gimp_pickable_button_new (context, GIMP_VIEW_SIZE_LARGE, 1);
      gtk_box_pack_start (GTK_BOX (tool->aux_input_box),
                          tool->aux_input_button, FALSE, FALSE, 0);
      gtk_widget_show (tool->aux_input_button);

      gtk_label_set_mnemonic_widget (GTK_LABEL (label),
                                     tool->aux_input_button);

      if (tool->options_box)
        {
          gtk_box_pack_start (GTK_BOX (tool->options_box), tool->aux_input_box,
                              FALSE, FALSE, 0);
          gtk_widget_show (tool->aux_input_box);
        }

      g_signal_connect_object (tool->aux_input_button, "notify::pickable",
                               G_CALLBACK (gimp_operation_tool_aux_notify),
                               tool->aux_input, 0);
    }

  if (gegl_node_has_pad (im_tool->operation, "aux2"))
    {
      GimpContext *context;
      GtkWidget   *label;

      tool->aux2_input = gegl_node_new_child (NULL,
                                             "operation", "gegl:buffer-source",
                                             NULL);

      gegl_node_connect_to (tool->aux2_input,   "output",
                            im_tool->operation, "aux2");

      context = GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool));

      tool->aux2_input_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);

      label = gtk_label_new_with_mnemonic (_("_Aux2 Input"));
      gtk_box_pack_start (GTK_BOX (tool->aux2_input_box), label,
                          FALSE, FALSE, 0);
      gtk_widget_show (label);

      tool->aux2_input_button =
        gimp_pickable_button_new (context, GIMP_VIEW_SIZE_LARGE, 1);
      gtk_box_pack_start (GTK_BOX (tool->aux2_input_box),
                          tool->aux2_input_button, FALSE, FALSE, 0);
      gtk_widget_show (tool->aux2_input_button);

      gtk_label_set_mnemonic_widget (GTK_LABEL (label),
                                     tool->aux2_input_button);

      if (tool->options_box)
        {
          gtk_box_pack_start (GTK_BOX (tool->options_box), tool->aux2_input_box,
                              FALSE, FALSE, 0);
          gtk_widget_show (tool->aux2_input_box);
        }

      g_signal_connect_object (tool->aux2_input_button, "notify::pickable",
                               G_CALLBACK (gimp_operation_tool_aux_notify),
                               tool->aux2_input, 0);
    }

  if (im_tool->config)
    {
      tool->options_gui =
        gimp_prop_gui_new (G_OBJECT (im_tool->config),
                           G_TYPE_FROM_INSTANCE (im_tool->config),
                           GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)),
                           (GimpCreatePickerFunc) gimp_image_map_tool_add_color_picker,
                           tool);

      if (tool->options_box)
        {
          gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui,
                              FALSE, FALSE, 0);
          gtk_widget_show (tool->options_gui);
        }
    }

  if (im_tool->gui)
    {
      if (undo_desc)
        gimp_tool_gui_set_description (im_tool->gui, undo_desc);

      if (icon_name)
        gimp_tool_gui_set_icon_name (im_tool->gui, icon_name);
    }

  if (GIMP_TOOL (tool)->drawable)
    {
      gimp_operation_tool_sync_op (tool, GIMP_TOOL (tool)->drawable);
      gimp_image_map_tool_preview (im_tool);
    }
}
Exemplo n.º 8
0
/**
 * gegl_graph_process:
 * @path: The traversal path
 *
 * Process the prepared request. This will return the
 * resulting buffer from the final node, or NULL if
 * that node is a sink.
 *
 * If gegl_graph_prepare_request has not been called
 * the behavior of this function is undefined.
 *
 * Return value: (transfer full): The result of the graph, or NULL if
 * there is no output pad.
 */
GeglBuffer *
gegl_graph_process (GeglGraphTraversal *path,
                    gint                level)
{
  GList *list_iter = NULL;
  GeglBuffer *result = NULL;
  GeglOperationContext *context = NULL;
  GeglOperationContext *last_context = NULL;
  GeglBuffer *operation_result = NULL;

  for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
    {
      GeglNode *node = GEGL_NODE (list_iter->data);
      GeglOperation *operation = node->operation;
      g_return_val_if_fail (node, NULL);
      g_return_val_if_fail (operation, NULL);
      
      GEGL_INSTRUMENT_START();

      operation_result = NULL;

      if (last_context)
        gegl_operation_context_purge (last_context);
      
      context = g_hash_table_lookup (path->contexts, node);
      g_return_val_if_fail (context, NULL);

      GEGL_NOTE (GEGL_DEBUG_PROCESS,
                 "Will process %s result_rect = %d, %d %d×%d",
                 gegl_node_get_debug_name (node),
                 context->result_rect.x, context->result_rect.y, context->result_rect.width, context->result_rect.height);
      
      if (context->need_rect.width > 0 && context->need_rect.height > 0)
        {
          if (context->cached)
            {
              GEGL_NOTE (GEGL_DEBUG_PROCESS,
                         "Using cached result for %s",
                         gegl_node_get_debug_name (node));
              operation_result = GEGL_BUFFER (node->cache);
            }
          else
            {
              /* provide something on input pad, always - this makes having
                 behavior depending on it not being set.. not work, is
                 sacrifising that worth it?
               */
              if (gegl_node_has_pad (node, "input") &&
                  !gegl_operation_context_get_object (context, "input"))
                {
                  gegl_operation_context_set_object (context, "input", G_OBJECT (gegl_graph_get_shared_empty(path)));
                }

              context->level = level;

              /* note: this hard-coding of "output" makes some more custom
               * graph topologies harder than neccesary.
               */
              gegl_operation_process (operation, context, "output", &context->need_rect, context->level);
              operation_result = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));

              if (operation_result && operation_result == (GeglBuffer *)operation->node->cache)
                gegl_cache_computed (operation->node->cache, &context->need_rect, level);
            }
        }
      else
        {
          operation_result = NULL;
        }

      if (operation_result)
        {
          GeglPad *output_pad = gegl_node_get_pad (node, "output");
          GList   *targets = gegl_graph_get_connected_output_contexts (path, output_pad);
          GList   *targets_iter;

          GEGL_NOTE (GEGL_DEBUG_PROCESS,
                     "Will deliver the results of %s:%s to %d targets",
                     gegl_node_get_debug_name (node),
                     "output",
                     g_list_length (targets));

          if (g_list_length (targets) > 1)
            gegl_object_set_has_forked (G_OBJECT (operation_result));

          for (targets_iter = targets; targets_iter; targets_iter = g_list_next (targets_iter))
            {
              ContextConnection *target_con = targets_iter->data;
              gegl_operation_context_set_object (target_con->context, target_con->name, G_OBJECT (operation_result));
            }
          g_list_free_full (targets, free_context_connection);
        }
      last_context = context;

      GEGL_INSTRUMENT_END ("process", gegl_node_get_operation (node));
    }
  if (last_context)
    {
      if (operation_result)
        result = g_object_ref (operation_result);
      else if (gegl_node_has_pad (last_context->operation->node, "output"))
        result = g_object_ref (gegl_graph_get_shared_empty (path));
      gegl_operation_context_purge (last_context);
    }

  return result;
}
Exemplo n.º 9
0
gboolean
gimp_gegl_apply_cached_operation (GeglBuffer          *src_buffer,
                                  GimpProgress        *progress,
                                  const gchar         *undo_desc,
                                  GeglNode            *operation,
                                  GeglBuffer          *dest_buffer,
                                  const GeglRectangle *dest_rect,
                                  GeglBuffer          *cache,
                                  const GeglRectangle *valid_rects,
                                  gint                 n_valid_rects,
                                  gboolean             cancellable)
{
  GeglNode      *gegl;
  GeglNode      *dest_node;
  GeglRectangle  rect = { 0, };
  GeglProcessor *processor        = NULL;
  gboolean       progress_started = FALSE;
  gdouble        value;
  gboolean       cancel           = FALSE;

  g_return_val_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer), FALSE);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
  g_return_val_if_fail (GEGL_IS_NODE (operation), FALSE);
  g_return_val_if_fail (GEGL_IS_BUFFER (dest_buffer), FALSE);
  g_return_val_if_fail (cache == NULL || GEGL_IS_BUFFER (cache), FALSE);
  g_return_val_if_fail (valid_rects == NULL || cache != NULL, FALSE);
  g_return_val_if_fail (valid_rects == NULL || n_valid_rects != 0, FALSE);

  if (dest_rect)
    {
      rect = *dest_rect;
    }
  else
    {
      rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width  (dest_buffer),
                                    gegl_buffer_get_height (dest_buffer));
    }

  gegl = gegl_node_new ();

  if (! gegl_node_get_parent (operation))
    gegl_node_add_child (gegl, operation);

  if (src_buffer && gegl_node_has_pad (operation, "input"))
    {
      GeglNode *src_node;

      /* dup() because reading and writing the same buffer doesn't
       * work with area ops when using a processor. See bug #701875.
       */
      if (progress && (src_buffer == dest_buffer))
        src_buffer = gegl_buffer_dup (src_buffer);
      else
        g_object_ref (src_buffer);

      src_node = gegl_node_new_child (gegl,
                                      "operation", "gegl:buffer-source",
                                      "buffer",    src_buffer,
                                      NULL);

      g_object_unref (src_buffer);

      gegl_node_connect_to (src_node,  "output",
                            operation, "input");
    }

  dest_node = gegl_node_new_child (gegl,
                                   "operation", "gegl:write-buffer",
                                   "buffer",    dest_buffer,
                                   NULL);

  gegl_node_connect_to (operation, "output",
                        dest_node, "input");

  if (progress)
    {
      processor = gegl_node_new_processor (dest_node, &rect);

      if (gimp_progress_is_active (progress))
        {
          if (undo_desc)
            gimp_progress_set_text_literal (progress, undo_desc);

          progress_started = FALSE;
          cancellable      = FALSE;
        }
      else
        {
          gimp_progress_start (progress, cancellable, "%s", undo_desc);

          if (cancellable)
            g_signal_connect (progress, "cancel",
                              G_CALLBACK (gimp_gegl_apply_operation_cancel),
                              &cancel);

          progress_started = TRUE;
        }
    }

  if (cache)
    {
      cairo_region_t *region;
      gint            all_pixels;
      gint            done_pixels = 0;
      gint            n_rects;
      gint            i;

      region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);

      all_pixels = rect.width * rect.height;

      for (i = 0; i < n_valid_rects; i++)
        {
          gegl_buffer_copy (cache,       valid_rects + i, GEGL_ABYSS_NONE,
                            dest_buffer, valid_rects + i);

          cairo_region_subtract_rectangle (region,
                                           (cairo_rectangle_int_t *)
                                           valid_rects + i);

          done_pixels += valid_rects[i].width * valid_rects[i].height;

          if (progress)
            gimp_progress_set_value (progress,
                                     (gdouble) done_pixels /
                                     (gdouble) all_pixels);
        }

      n_rects = cairo_region_num_rectangles (region);

      for (i = 0; ! cancel && (i < n_rects); i++)
        {
          cairo_rectangle_int_t render_rect;

          cairo_region_get_rectangle (region, i, &render_rect);

          if (progress)
            {
              gint rect_pixels = render_rect.width * render_rect.height;

#ifdef REUSE_PROCESSOR
              gegl_processor_set_rectangle (processor,
                                            (GeglRectangle *) &render_rect);
#else
              g_object_unref (processor);
              processor = gegl_node_new_processor (dest_node,
                                                   (GeglRectangle *) &render_rect);
#endif

              while (! cancel && gegl_processor_work (processor, &value))
                {
                  gimp_progress_set_value (progress,
                                           ((gdouble) done_pixels +
                                            value * rect_pixels) /
                                           (gdouble) all_pixels);

                  if (cancellable)
                    while (! cancel && g_main_context_pending (NULL))
                      g_main_context_iteration (NULL, FALSE);
                }

              done_pixels += rect_pixels;
            }
          else
            {
              gegl_node_blit (dest_node, 1.0, (GeglRectangle *) &render_rect,
                              NULL, NULL, 0, GEGL_BLIT_DEFAULT);
            }
        }

      cairo_region_destroy (region);
    }
  else
    {
      if (progress)
        {
          while (! cancel && gegl_processor_work (processor, &value))
            {
              gimp_progress_set_value (progress, value);

              if (cancellable)
                while (! cancel && g_main_context_pending (NULL))
                  g_main_context_iteration (NULL, FALSE);
            }
        }
      else
        {
          gegl_node_blit (dest_node, 1.0, &rect,
                          NULL, NULL, 0, GEGL_BLIT_DEFAULT);
        }
    }

  if (processor)
    g_object_unref (processor);

  g_object_unref (gegl);

  if (progress_started)
    {
      gimp_progress_end (progress);

      if (cancellable)
        g_signal_handlers_disconnect_by_func (progress,
                                              gimp_gegl_apply_operation_cancel,
                                              &cancel);
    }

  return ! cancel;
}