示例#1
0
文件: gimpcagetool.c 项目: STRNG/gimp
static void
gimp_cage_tool_compute_coef (GimpCageTool *ct)
{
  GimpCageConfig *config = ct->config;
  GimpProgress   *progress;
  const Babl     *format;
  GeglNode       *gegl;
  GeglNode       *input;
  GeglNode       *output;
  GeglProcessor  *processor;
  GeglBuffer     *buffer;
  gdouble         value;

  progress = gimp_progress_start (GIMP_PROGRESS (ct),
                                  _("Computing Cage Coefficients"), FALSE);

  if (ct->coef)
    {
      g_object_unref (ct->coef);
      ct->coef = NULL;
    }

  format = babl_format_n (babl_type ("float"),
                          gimp_cage_config_get_n_points (config) * 2);


  gegl = gegl_node_new ();

  input = gegl_node_new_child (gegl,
                               "operation", "gimp:cage-coef-calc",
                               "config",    ct->config,
                               NULL);

  output = gegl_node_new_child (gegl,
                                "operation", "gegl:buffer-sink",
                                "buffer",    &buffer,
                                "format",    format,
                                NULL);

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

  processor = gegl_node_new_processor (output, NULL);

  while (gegl_processor_work (processor, &value))
    {
      if (progress)
        gimp_progress_set_value (progress, value);
    }

  if (progress)
    gimp_progress_end (progress);

  g_object_unref (processor);

  ct->coef = buffer;
  g_object_unref (gegl);

  ct->dirty_coef = FALSE;
}
static void
gimp_projection_construct_gegl (GimpProjection *proj,
                                gint            x,
                                gint            y,
                                gint            w,
                                gint            h)
{
    GeglNode      *sink;
    GeglRectangle  rect;

    sink = gimp_projection_get_sink_node (proj);

    rect.x      = x;
    rect.y      = y;
    rect.width  = w;
    rect.height = h;

    if (! proj->processor)
        proj->processor = gegl_node_new_processor (sink, &rect);
    else
        gegl_processor_set_rectangle (proj->processor, &rect);

    while (gegl_processor_work (proj->processor, NULL));

    /* FIXME: Reuse it when it can handle dirty rects when painting
     * properly
     */
    g_object_unref (proj->processor);
    proj->processor = NULL;
}
示例#3
0
int dt_dev_pixelpipe_process(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev, int x, int y, int width, int height, float scale)
{
  pipe->processing = 1;
  printf("pixelpipe process start\n");

  // have backbuf in right size:
  if(pipe->backbuf_size < width*height*4*sizeof(uint8_t))
  {
    pthread_mutex_lock(&pipe->backbuf_mutex);
    pipe->backbuf_size = width*height*4*sizeof(uint8_t);
    free(pipe->backbuf);
    pipe->backbuf = (uint8_t *)dt_alloc_align(16, pipe->backbuf_size);
    pthread_mutex_unlock(&pipe->backbuf_mutex);
  }

  // scale node (is slow):
  // scale *= 2;
  // FIXME: this seems to be a bug in gegl. need to manually adjust updated roi here.
  GeglRectangle roi  = (GeglRectangle)
  {
    x, y, width, height
  };
  GeglRectangle roio = (GeglRectangle)
  {
    roi.x/scale, roi.y/scale, roi.width/scale, roi.height/scale
  };
  roio.x      = MAX(0, roio.x);
  roio.y      = MAX(0, roio.y);
  roio.width  = MIN(pipe->iwidth -roio.x-1, roio.width);
  roio.height = MIN(pipe->iheight-roio.y-1, roio.height);
  GeglProcessor *processor = gegl_node_new_processor (pipe->output, &roio);
  // gegl_node_set(pipe->scale, "x", scale, "y", scale, NULL);
  // GeglProcessor *processor = gegl_node_new_processor (pipe->output, roi);
  double         progress;

  // TODO: insert constant scale node at beginning, maintain lo-res branch of pipeline (shadowed).
  // TODO: decide on scale param, which one to use.

  while (gegl_processor_work (processor, &progress))
  {
    // if history changed, abort processing?
    if(pipe->changed != DT_DEV_PIPE_UNCHANGED || dev->gui_leaving) return 1;
  }
  gegl_processor_destroy (processor);

  // gegl scale node turned out to be even slower :(
  gegl_node_blit (pipe->output, scale, &roi, babl_format("RGBA u8"), pipe->backbuf, GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_CACHE);
  // gegl_node_blit (pipe->output, 1.0, roi, babl_format("RGBA u8"), output, GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_CACHE);

  // TODO: update histograms here with this data?
  printf("pixelpipe process end\n");
  pipe->processing = 0;
  return 0;
}
示例#4
0
void
gimp_applicator_apply (GimpApplicator       *applicator,
                       GeglBuffer           *src_buffer,
                       GeglBuffer           *apply_buffer,
                       gint                  apply_buffer_x,
                       gint                  apply_buffer_y,
                       gdouble               opacity,
                       GimpLayerModeEffects  paint_mode)
{
    gint width  = gegl_buffer_get_width  (apply_buffer);
    gint height = gegl_buffer_get_height (apply_buffer);

    if (applicator->src_buffer != src_buffer)
    {
        applicator->src_buffer = src_buffer;

        gegl_node_set (applicator->src_node,
                       "buffer", src_buffer,
                       NULL);
    }

    if (applicator->apply_buffer != apply_buffer)
    {
        applicator->apply_buffer = apply_buffer;

        gegl_node_set (applicator->apply_src_node,
                       "buffer", apply_buffer,
                       NULL);
    }


    gegl_node_set (applicator->apply_offset_node,
                   "x", (gdouble) apply_buffer_x,
                   "y", (gdouble) apply_buffer_y,
                   NULL);

    if ((applicator->opacity    != opacity) ||
            (applicator->paint_mode != paint_mode))
    {
        applicator->opacity    = opacity;
        applicator->paint_mode = paint_mode;

        gimp_gegl_mode_node_set (applicator->mode_node,
                                 paint_mode, opacity, FALSE);
    }

    gegl_processor_set_rectangle (applicator->processor,
                                  GEGL_RECTANGLE (apply_buffer_x,
                                          apply_buffer_y,
                                          width, height));

    while (gegl_processor_work (applicator->processor, NULL));
}
示例#5
0
static void
gimp_drawable_apply_operation_private (GimpDrawable       *drawable,
                                       GimpProgress       *progress,
                                       const gchar        *undo_desc,
                                       GeglNode           *operation,
                                       gboolean            linear,
                                       TileManager         *dest_tiles,
                                       const GeglRectangle *rect)
{
  GeglNode      *gegl;
  GeglNode      *input;
  GeglNode      *output;
  GeglProcessor *processor;
  gdouble        value;

  gegl = gegl_node_new ();

  /* Disable caching on all children of the node unless explicitly re-enabled.
   */
  g_object_set (gegl,
                "dont-cache", TRUE,
                NULL);

  input  = gegl_node_new_child (gegl,
                                "operation",    "gimp:tilemanager-source",
                                "tile-manager", gimp_drawable_get_tiles (drawable),
                                "linear",       linear,
                                NULL);
  output = gegl_node_new_child (gegl,
                                "operation",    "gimp:tilemanager-sink",
                                "tile-manager", dest_tiles,
                                "linear",       linear,
                                NULL);

  gegl_node_add_child (gegl, operation);

  gegl_node_link_many (input, operation, output, NULL);

  processor = gegl_node_new_processor (output, rect);

  if (progress)
    gimp_progress_start (progress, undo_desc, FALSE);

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

  g_object_unref (processor);

  g_object_unref (gegl);
}
示例#6
0
static gboolean paint_release (GtkWidget      *widget,
                               GdkEventButton *event)
{
  if (event->button == 1)
    {
      gdouble        x0, x1, y0, y1;
      GeglProcessor *processor;
      GeglNode      *writebuf;
      GeglRectangle  roi;

      gegl_path_get_bounds (vector, &x0, &x1, &y0, &y1);

      roi.x = x0 - LINEWIDTH;
      roi.y = y0 - LINEWIDTH;
      roi.width = x1 - x0 + LINEWIDTH * 2;
      roi.height = y1 - y0 + LINEWIDTH * 2;

      writebuf = gegl_node_new_child (gegl,
                                      "operation", "gegl:write-buffer",
                                      "buffer",    buffer,
                                      NULL);
      gegl_node_link_many (over, writebuf, NULL);

      processor = gegl_node_new_processor (writebuf, &roi);
      while (gegl_processor_work (processor, NULL)) ;

      gegl_processor_destroy (processor);
      g_object_unref (writebuf);

      gegl_node_link_many (top, out, NULL);
      g_object_unref (over);
      g_object_unref (stroke);

      over     = NULL;
      stroke   = NULL;
      pen_down = FALSE;

      return TRUE;
    }
  return FALSE;
}
static gboolean
test_change_processor_rect_do_test (GeglProcessor       *processor,
                                    const GeglRectangle *rect,
                                    GeglNode            *sink)
{
  gint        i                         = 0;
  gboolean    result                    = TRUE;
  float       expected_result_buffer[4] = { 1.0, 1.0, 1.0, 1.0 };
  float       result_buffer[4]          = { 0, };
  GeglBuffer *buffer                    = NULL;

  gegl_node_set (sink,
                 "buffer", &buffer,
                 NULL);
  
  gegl_processor_set_rectangle (processor, rect);

  while (gegl_processor_work (processor, NULL));

  gegl_buffer_get (buffer,
                   rect,
                   1.0,
                   babl_format ("RGBA float"),
                   result_buffer,
                   GEGL_AUTO_ROWSTRIDE,
                   GEGL_ABYSS_NONE);

  /* Compare with a small epsilon to account for accumulated error */
  for(i = 0; i < G_N_ELEMENTS (expected_result_buffer); i++)
    result = result && FLOATS_EQUAL (expected_result_buffer[i], result_buffer[i]);

  gegl_node_set (sink,
                 "buffer", NULL,
                 NULL);

  g_object_unref (buffer);

  return result;
}
static gboolean
task_monitor(ViewHelper *self)
{
    if (!self->processor || !self->node) {
        return FALSE;
    }

    // PERFORMANCE: combine all the rects added to the queue during a single
    // iteration of the main loop somehow

    if (!self->currently_processed_rect) {

        if (g_queue_is_empty(self->processing_queue)) {
            // Unregister worker
            self->monitor_id = 0;
            return FALSE;
        }
        else {
            // Fetch next rect to process
            self->currently_processed_rect = (GeglRectangle *)g_queue_pop_tail(self->processing_queue);
            g_assert(self->currently_processed_rect);
            gegl_processor_set_rectangle(self->processor, self->currently_processed_rect);
        }
    }

    gboolean processing_done = !gegl_processor_work(self->processor, NULL);

    if (processing_done) {
        // Go to next region
        if (self->currently_processed_rect) {
            g_free(self->currently_processed_rect);
        }
        self->currently_processed_rect = NULL;
    }

    return TRUE;
}
示例#9
0
static void
gimp_seamless_clone_tool_filter_update (GimpSeamlessCloneTool *sc)
{
  GimpTool         *tool  = GIMP_TOOL (sc);
  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
  GimpItem         *item  = GIMP_ITEM (tool->drawable);
  gint              x, y;
  gint              w, h;
  gint              off_x, off_y;
  GeglRectangle     visible;
  GeglOperation    *op = NULL;

  GimpProgress     *progress;
  GeglNode         *output;
  GeglProcessor    *processor;
  gdouble           value;

  progress = gimp_progress_start (GIMP_PROGRESS (sc), FALSE,
                                  _("Cloning the foreground object"));

  /* Find out at which x,y is the top left corner of the currently
   * displayed part */
  gimp_display_shell_untransform_viewport (shell, &x, &y, &w, &h);

  /* Find out where is our drawable positioned */
  gimp_item_get_offset (item, &off_x, &off_y);

  /* Create a rectangle from the intersection of the currently displayed
   * part with the drawable */
  gimp_rectangle_intersect (x, y, w, h,
                            off_x,
                            off_y,
                            gimp_item_get_width  (item),
                            gimp_item_get_height (item),
                            &visible.x,
                            &visible.y,
                            &visible.width,
                            &visible.height);

  /* Since the filter_apply function receives a rectangle describing
   * where it should update the preview, and since that rectangle should
   * be relative to the drawable's location, we now offset back by the
   * drawable's offsetts. */
  visible.x -= off_x;
  visible.y -= off_y;

  g_object_get (sc->sc_node, "gegl-operation", &op, NULL);
  /* If any cache of the visible area was present, clear it!
   * We need to clear the cache in the sc_node, since that is
   * where the previous paste was located
   */
  gegl_operation_invalidate (op, &visible, TRUE);
  g_object_unref (op);

  /* Now update the image map and show this area */
  gimp_drawable_filter_apply (sc->filter, NULL);

  /* Show update progress. */
  output = gegl_node_get_output_proxy (sc->render_node, "output");
  processor = gegl_node_new_processor (output, NULL);

  while (gegl_processor_work (processor, &value))
    {
      if (progress)
        gimp_progress_set_value (progress, value);
    }

  if (progress)
    gimp_progress_end (progress);

  g_object_unref (processor);
}
示例#10
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);
}
static void
gimp_foreground_select_tool_preview (GimpForegroundSelectTool *fg_select,
                                     GimpDisplay              *display)
{
  GimpTool                    *tool     = GIMP_TOOL (fg_select);
  GimpForegroundSelectOptions *options  = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
  GimpImage                   *image    = gimp_display_get_image (display);
  GimpDrawable                *drawable = gimp_image_get_active_drawable (image);
  GeglBuffer                  *trimap_buffer;
  GeglBuffer                  *drawable_buffer;
  GeglNode                    *gegl;
  GeglNode                    *matting_node;
  GeglNode                    *input_image;
  GeglNode                    *input_trimap;
  GeglNode                    *output_mask;
  GeglBuffer                  *buffer;
  GimpProgress                *progress;
  GeglProcessor               *processor;
  gdouble                     value;

  if (fg_select->mask)
    {
      g_object_unref (fg_select->mask);
      fg_select->mask = NULL;
    }

  progress = gimp_progress_start (GIMP_PROGRESS (fg_select),
                                  _("Computing alpha of unknown pixels"),
                                  FALSE);

  trimap_buffer   = fg_select->trimap;
  drawable_buffer = gimp_drawable_get_buffer (drawable);

  gegl = gegl_node_new ();

  input_trimap = gegl_node_new_child (gegl,
                                      "operation", "gegl:buffer-source",
                                      "buffer",    trimap_buffer,
                                      NULL);
  input_image = gegl_node_new_child (gegl,
                                     "operation", "gegl:buffer-source",
                                     "buffer",    drawable_buffer,
                                     NULL);
  output_mask = gegl_node_new_child (gegl,
                                     "operation", "gegl:buffer-sink",
                                     "buffer",    &buffer,
                                     "format",    NULL,
                                     NULL);

  if (options->engine == GIMP_MATTING_ENGINE_GLOBAL)
    {
      matting_node = gegl_node_new_child (gegl,
                                          "operation",  "gegl:matting-global",
                                          "iterations", options->iterations,
                                          NULL);
    }
  else
    {
      matting_node = gegl_node_new_child (gegl,
                                          "operation",     "gegl:matting-levin",
                                          "levels",        options->levels,
                                          "active_levels", options->active_levels,
                                          NULL);
    }

  gegl_node_connect_to (input_image,  "output",
                        matting_node, "input");
  gegl_node_connect_to (input_trimap, "output",
                        matting_node, "aux");
  gegl_node_connect_to (matting_node, "output",
                        output_mask,  "input");

  processor = gegl_node_new_processor (output_mask, NULL);

  while (gegl_processor_work (processor, &value))
    {
      if (progress)
        gimp_progress_set_value (progress, value);
    }

  if (progress)
    gimp_progress_end (progress);

  g_object_unref (processor);

  fg_select->mask = buffer;

  gimp_foreground_select_tool_set_preview (fg_select, display);

  g_object_unref (gegl);
}
GeglBuffer *
gimp_drawable_foreground_extract (GimpDrawable      *drawable,
                                  GimpMattingEngine  engine,
                                  gint               global_iterations,
                                  gint               levin_levels,
                                  gint               levin_active_levels,
                                  GeglBuffer        *trimap,
                                  GimpProgress      *progress)
{
  GeglBuffer    *drawable_buffer;
  GeglNode      *gegl;
  GeglNode      *input_node;
  GeglNode      *trimap_node;
  GeglNode      *matting_node;
  GeglNode      *output_node;
  GeglBuffer    *buffer;
  GeglProcessor *processor;
  gdouble        value;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (GEGL_IS_BUFFER (trimap), NULL);
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);

  progress = gimp_progress_start (progress, FALSE,
                                  _("Computing alpha of unknown pixels"));

  drawable_buffer = gimp_drawable_get_buffer (drawable);

  gegl = gegl_node_new ();

  trimap_node = gegl_node_new_child (gegl,
                                     "operation", "gegl:buffer-source",
                                     "buffer",    trimap,
                                     NULL);
  input_node = gegl_node_new_child (gegl,
                                    "operation", "gegl:buffer-source",
                                    "buffer",    drawable_buffer,
                                    NULL);
  output_node = gegl_node_new_child (gegl,
                                     "operation", "gegl:buffer-sink",
                                     "buffer",    &buffer,
                                     "format",    NULL,
                                     NULL);

  if (engine == GIMP_MATTING_ENGINE_GLOBAL)
    {
      matting_node = gegl_node_new_child (gegl,
                                          "operation",  "gegl:matting-global",
                                          "iterations", global_iterations,
                                          NULL);
    }
  else
    {
      matting_node = gegl_node_new_child (gegl,
                                          "operation",     "gegl:matting-levin",
                                          "levels",        levin_levels,
                                          "active_levels", levin_active_levels,
                                          NULL);
    }

  gegl_node_connect_to (input_node,   "output",
                        matting_node, "input");
  gegl_node_connect_to (trimap_node,  "output",
                        matting_node, "aux");
  gegl_node_connect_to (matting_node, "output",
                        output_node,  "input");

  processor = gegl_node_new_processor (output_node, NULL);

  while (gegl_processor_work (processor, &value))
    {
      if (progress)
        gimp_progress_set_value (progress, value);
    }

  if (progress)
    gimp_progress_end (progress);

  g_object_unref (processor);

  g_object_unref (gegl);

  return buffer;
}
示例#13
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;
}