コード例 #1
0
ファイル: gimp-gegl-utils.c プロジェクト: feedbackex/gimp
static void
gimp_gegl_progress_callback (GObject      *object,
                             gdouble       value,
                             GimpProgress *progress)
{
    const gchar *text;

    text = g_object_get_data (object, "gimp-progress-text");

    if (text)
    {
        if (value == 0.0)
        {
            if (gimp_progress_is_active (progress))
                gimp_progress_set_text (progress, "%s", text);
            else
                gimp_progress_start (progress, FALSE, "%s", text);

            return;
        }
        else if (value == 1.0)
        {
            gimp_progress_end (progress);

            return;
        }
    }

    gimp_progress_set_value (progress, value);
}
コード例 #2
0
ファイル: gimpfiledialog.c プロジェクト: Amerekanets/gimp
static void
gimp_file_dialog_progress_set_text (GimpProgress *progress,
                                    const gchar  *message)
{
  GimpFileDialog *dialog = GIMP_FILE_DIALOG (progress);

  gimp_progress_set_text (GIMP_PROGRESS (dialog->progress), message);
}
コード例 #3
0
static void
gimp_display_shell_progress_set_text (GimpProgress *progress,
                                      const gchar  *message)
{
  GimpDisplayShell *shell     = GIMP_DISPLAY_SHELL (progress);
  GimpStatusbar    *statusbar = gimp_display_shell_get_statusbar (shell);

  gimp_progress_set_text (GIMP_PROGRESS (statusbar), message);
}
コード例 #4
0
ファイル: gimpdisplay.c プロジェクト: Amerekanets/gimp
static void
gimp_display_progress_set_text (GimpProgress *progress,
                                const gchar  *message)
{
  GimpDisplay *display = GIMP_DISPLAY (progress);

  if (display->shell)
    gimp_progress_set_text (GIMP_PROGRESS (display->shell), message);
}
コード例 #5
0
ファイル: gimpprogressdialog.c プロジェクト: Amerekanets/gimp
static void
gimp_progress_dialog_progress_set_text (GimpProgress *progress,
                                        const gchar  *message)
{
  GimpProgressDialog *dialog = GIMP_PROGRESS_DIALOG (progress);

  if (! dialog->box)
    return;

  gimp_progress_set_text (GIMP_PROGRESS (dialog->box), message);
}
コード例 #6
0
ファイル: gimpplugin-progress.c プロジェクト: DevMaggio/gimp
void
gimp_plug_in_progress_set_text (GimpPlugIn  *plug_in,
                                const gchar *message)
{
  GimpPlugInProcFrame *proc_frame;

  g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));

  proc_frame = gimp_plug_in_get_proc_frame (plug_in);

  if (proc_frame->progress)
    gimp_progress_set_text (proc_frame->progress, message);
}
コード例 #7
0
ファイル: gimpplugin-progress.c プロジェクト: DevMaggio/gimp
void
gimp_plug_in_progress_start (GimpPlugIn  *plug_in,
                             const gchar *message,
                             GimpObject  *display)
{
  GimpPlugInProcFrame *proc_frame;

  g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
  g_return_if_fail (display == NULL || GIMP_IS_OBJECT (display));

  proc_frame = gimp_plug_in_get_proc_frame (plug_in);

  if (! proc_frame->progress)
    {
      proc_frame->progress = gimp_new_progress (plug_in->manager->gimp,
                                                display);

      if (proc_frame->progress)
        {
          proc_frame->progress_created = TRUE;

          g_object_ref (proc_frame->progress);

          gimp_plug_in_progress_attach (proc_frame->progress);
        }
    }

  if (proc_frame->progress)
    {
      if (! proc_frame->progress_cancel_id)
        proc_frame->progress_cancel_id =
          g_signal_connect (proc_frame->progress, "cancel",
                            G_CALLBACK (gimp_plug_in_progress_cancel_callback),
                            plug_in);

      if (gimp_progress_is_active (proc_frame->progress))
        {
          if (message)
            gimp_progress_set_text (proc_frame->progress, message);

          if (gimp_progress_get_value (proc_frame->progress) > 0.0)
            gimp_progress_set_value (proc_frame->progress, 0.0);
        }
      else
        {
          gimp_progress_start (proc_frame->progress,
                               message ? message : "",
                               TRUE);
        }
    }
}
コード例 #8
0
ファイル: gimptool-progress.c プロジェクト: frne/gimp
static void
gimp_tool_progress_set_text (GimpProgress *progress,
                             const gchar  *message)
{
  GimpTool *tool = GIMP_TOOL (progress);

  if (tool->progress)
    {
      GimpDisplayShell *shell = gimp_display_get_shell (tool->progress_display);

      gimp_progress_set_text (GIMP_PROGRESS (tool->progress), message);
      gimp_widget_flush_expose (shell->canvas);
    }
}
コード例 #9
0
/**
 * gimp_progress_set_text_printf:
 * @format: a standard printf() format string
 * @Varargs: arguments for @format
 *
 * Changes the text in the progress bar for the current plug-in.
 *
 * This function allows to change the text in the progress bar for the
 * current plug-in. Unlike gimp_progress_init() it does not change the
 * displayed value.
 *
 * Returns: %TRUE on success.
 *
 * Since: GIMP 2.4
 **/
gboolean
gimp_progress_set_text_printf (const gchar *format,
                               ...)
{
  gchar    *text;
  gboolean  retval;
  va_list   args;

  g_return_val_if_fail (format != NULL, FALSE);

  va_start (args, format);
  text = g_strdup_vprintf (format, args);
  va_end (args);

  retval = gimp_progress_set_text (text);

  g_free (text);

  return retval;
}
コード例 #10
0
ファイル: file-remote.c プロジェクト: terminateden/gimp
static void
file_remote_progress_callback (goffset  current_num_bytes,
                               goffset  total_num_bytes,
                               gpointer user_data)
{
  RemoteProgress *progress = user_data;
  GTimeVal        now;

  /*  update the progress only up to 10 times a second  */
  g_get_current_time (&now);

  if (progress->last_time.tv_sec &&
      ((now.tv_sec - progress->last_time.tv_sec) * 1000 +
       (now.tv_usec - progress->last_time.tv_usec) / 1000) < 100)
    return;

  progress->last_time = now;

  if (total_num_bytes > 0)
    {
      const gchar *format;
      gchar       *done  = g_format_size (current_num_bytes);
      gchar       *total = g_format_size (total_num_bytes);

      switch (progress->mode)
        {
        case DOWNLOAD:
          format = _("Downloading image (%s of %s)");
          break;

        case UPLOAD:
          format = _("Uploading image (%s of %s)");
          break;

        default:
          g_assert_not_reached ();
        }

      gimp_progress_set_text (progress->progress, format, done, total);
      g_free (total);
      g_free (done);

      gimp_progress_set_value (progress->progress,
                               (gdouble) current_num_bytes /
                               (gdouble) total_num_bytes);
    }
  else
    {
      const gchar *format;
      gchar       *done = g_format_size (current_num_bytes);

      switch (progress->mode)
        {
        case DOWNLOAD:
          format = _("Downloaded %s of image data");
          break;

        case UPLOAD:
          format = _("Uploaded %s of image data");
          break;

        default:
          g_assert_not_reached ();
        }

      gimp_progress_set_text (progress->progress, format, done);
      g_free (done);

      gimp_progress_pulse (progress->progress);
    }

  while (! progress->cancel && g_main_context_pending (NULL))
    g_main_context_iteration (NULL, FALSE);
}
コード例 #11
0
static GeglBuffer *
gradient_precalc_shapeburst (GimpImage           *image,
                             GimpDrawable        *drawable,
                             const GeglRectangle *region,
                             gdouble              dist,
                             GimpProgress        *progress)
{
  GimpChannel *mask;
  GeglBuffer  *dist_buffer;
  GeglBuffer  *temp_buffer;
  GeglNode    *shapeburst;
  gdouble      max;
  gfloat       max_iteration;

  gimp_progress_set_text (progress, _("Calculating distance map"));

  /*  allocate the distance map  */
  dist_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                                 region->width, region->height),
                                 babl_format ("Y float"));

  /*  allocate the selection mask copy
   *  XXX: its format should be the same of gimp:shapeburst input buffer
   *       porting the op to 'float' should be reflected here as well
   */
  temp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                                 region->width, region->height),
                                 babl_format ("Y u8"));

  mask = gimp_image_get_mask (image);

  /*  If the image mask is not empty, use it as the shape burst source  */
  if (! gimp_channel_is_empty (mask))
    {
      gint x, y, width, height;
      gint off_x, off_y;

      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      /*  copy the mask to the temp mask  */
      gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)),
                        GEGL_RECTANGLE (x + off_x, y + off_y, width, height),
                        temp_buffer,
                        GEGL_RECTANGLE (0, 0, 0, 0));
    }
  else
    {
      /*  If the intended drawable has an alpha channel, use that  */
      if (gimp_drawable_has_alpha (drawable))
        {
          const Babl *component_format;

          component_format = babl_format ("A u8");

          /*  extract the aplha into the temp mask  */
          gegl_buffer_set_format (temp_buffer, component_format);
          gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
                            GEGL_RECTANGLE (region->x, region->y,
                                            region->width, region->height),
                            temp_buffer,
                            GEGL_RECTANGLE (0, 0, 0, 0));
          gegl_buffer_set_format (temp_buffer, NULL);
        }
      else
        {
          GeglColor *white = gegl_color_new ("white");

          /*  Otherwise, just fill the shapeburst to white  */
          gegl_buffer_set_color (temp_buffer, NULL, white);
          g_object_unref (white);
        }
    }

  shapeburst = gegl_node_new_child (NULL,
                                    "operation", "gimp:shapeburst",
                                    NULL);

  gimp_gegl_progress_connect (shapeburst, progress, NULL);

  gimp_gegl_apply_operation (temp_buffer, NULL, NULL,
                             shapeburst,
                             dist_buffer, NULL);

  gegl_node_get (shapeburst, "max-iterations", &max, NULL);

  g_object_unref (shapeburst);

  max_iteration = max;

  g_object_unref (temp_buffer);

  /*  normalize the shapeburst with the max iteration  */
  if (max_iteration > 0)
    {
      GeglBufferIterator *iter;

      iter = gegl_buffer_iterator_new (dist_buffer, NULL, 0, NULL,
                                       GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);

      while (gegl_buffer_iterator_next (iter))
        {
          gint    count = iter->length;
          gfloat *data  = iter->data[0];

          while (count--)
            *data++ /= max_iteration;
        }
    }

  return dist_buffer;
}
コード例 #12
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);
}
コード例 #13
0
ファイル: xcf.c プロジェクト: AdamGrzonkowski/gimp-1
static GimpValueArray *
xcf_save_invoker (GimpProcedure         *procedure,
                  Gimp                  *gimp,
                  GimpContext           *context,
                  GimpProgress          *progress,
                  const GimpValueArray  *args,
                  GError               **error)
{
  XcfInfo         info = { 0, };
  GimpValueArray *return_vals;
  GimpImage      *image;
  const gchar    *uri;
  GFile          *file;
  gboolean        success  = FALSE;
  GError         *my_error = NULL;

  gimp_set_busy (gimp);

  image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp);
  uri   = g_value_get_string (gimp_value_array_index (args, 3));
  file  = g_file_new_for_uri (uri);

  info.output = G_OUTPUT_STREAM (g_file_replace (file,
                                                 NULL, FALSE, G_FILE_CREATE_NONE,
                                                 NULL, &my_error));

  if (info.output)
    {
      gboolean compat_mode = gimp_image_get_xcf_compat_mode (image);

      info.gimp     = gimp;
      info.seekable = G_SEEKABLE (info.output);
      info.progress = progress;
      info.file     = file;

      if (compat_mode)
        info.compression = COMPRESS_RLE;
      else
        info.compression = COMPRESS_ZLIB;

      info.file_version = gimp_image_get_xcf_version (image,
                                                      info.compression ==
                                                      COMPRESS_ZLIB,
                                                      NULL, NULL);

      if (progress)
        gimp_progress_start (progress, FALSE, _("Saving '%s'"),
                             gimp_file_get_utf8_name (file));

      success = xcf_save_image (&info, image, &my_error);

      if (success)
        {
          if (progress)
            gimp_progress_set_text (progress, _("Closing '%s'"),
                                    gimp_file_get_utf8_name (file));

          success = g_output_stream_close (info.output, NULL, &my_error);
        }

      if (! success)
        g_propagate_prefixed_error (error, my_error,
                                    _("Error writing '%s': "),
                                    gimp_file_get_utf8_name (file));

      g_object_unref (info.output);

      if (progress)
        gimp_progress_end (progress);
    }
  else
    {
      g_propagate_prefixed_error (error, my_error,
                                  _("Error creating '%s': "),
                                  gimp_file_get_utf8_name (file));
    }

  g_object_unref (file);

  return_vals = gimp_procedure_get_return_values (procedure, success,
                                                  error ? *error : NULL);

  gimp_unset_busy (gimp);

  return return_vals;
}
コード例 #14
0
ファイル: gimpdrawable-blend.c プロジェクト: alfanak/gimp
static GeglBuffer *
gradient_precalc_shapeburst (GimpImage           *image,
                             GimpDrawable        *drawable,
                             const GeglRectangle *region,
                             gdouble              dist,
                             GimpProgress        *progress)
{
  GimpChannel *mask;
  GeglBuffer  *dist_buffer;
  GeglBuffer  *temp_buffer;
  GeglNode    *shapeburst;

  gimp_progress_set_text (progress, _("Calculating distance map"));

  /*  allocate the distance map  */
  dist_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                                 region->width, region->height),
                                 babl_format ("Y float"));

  /*  allocate the selection mask copy
   */
  temp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                                 region->width, region->height),
                                 babl_format ("Y float"));

  mask = gimp_image_get_mask (image);

  /*  If the image mask is not empty, use it as the shape burst source  */
  if (! gimp_channel_is_empty (mask))
    {
      gint x, y, width, height;
      gint off_x, off_y;

      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      /*  copy the mask to the temp mask  */
      gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)),
                        GEGL_RECTANGLE (x + off_x, y + off_y, width, height),
                        temp_buffer,
                        GEGL_RECTANGLE (0, 0, 0, 0));
    }
  else
    {
      /*  If the intended drawable has an alpha channel, use that  */
      if (gimp_drawable_has_alpha (drawable))
        {
          const Babl *component_format;

          component_format = babl_format ("A float");

          /*  extract the aplha into the temp mask  */
          gegl_buffer_set_format (temp_buffer, component_format);
          gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
                            GEGL_RECTANGLE (region->x, region->y,
                                            region->width, region->height),
                            temp_buffer,
                            GEGL_RECTANGLE (0, 0, 0, 0));
          gegl_buffer_set_format (temp_buffer, NULL);
        }
      else
        {
          GeglColor *white = gegl_color_new ("white");

          /*  Otherwise, just fill the shapeburst to white  */
          gegl_buffer_set_color (temp_buffer, NULL, white);
          g_object_unref (white);
        }
    }

  shapeburst = gegl_node_new_child (NULL,
                                    "operation", "gimp:shapeburst",
                                    "normalize", TRUE,
                                    NULL);

  gimp_gegl_progress_connect (shapeburst, progress, NULL);

  gimp_gegl_apply_operation (temp_buffer, NULL, NULL,
                             shapeburst,
                             dist_buffer, NULL);

  g_object_unref (shapeburst);

  g_object_unref (temp_buffer);

  return dist_buffer;
}
コード例 #15
0
ファイル: jpeg_compress.c プロジェクト: possiblyphilip/Koi
void * jpeg_highlighter_algorithm(JOB_ARG *job)
{
	GimpRunMode mode = GIMP_RUN_NONINTERACTIVE;
	int num_return_vals;
	gint32 layer, temp_layer;
	char temp_file_name[256];
	int ii;

	printf("inside %s thread %d\n", jpeg_plugin.name, job->thread);

// i really only want to run this once and the plugin doesnt know how many threads will get kicked off because its dynamic
	if(job->thread == 0)
	{

		sleep(1);


		sprintf(temp_file_name,"%stemp.jpg",job->file_name);


//		mkstemp(file_name);
		printf("using filename %s\n", temp_file_name);


		printf("saving jpeg at %f compression\n", jpeg_compress);
		gimp_progress_set_text("waiting for jpeg save\n");

		gimp_run_procedure("file-jpeg-save",&num_return_vals, GIMP_PDB_INT32, mode, GIMP_PDB_IMAGE, job->image_id , GIMP_PDB_DRAWABLE, job->drawable->drawable_id, GIMP_PDB_STRING, temp_file_name, GIMP_PDB_STRING, "temp", GIMP_PDB_FLOAT, jpeg_compress, GIMP_PDB_FLOAT, 0.0, GIMP_PDB_INT32, 0, GIMP_PDB_INT32, 0, GIMP_PDB_STRING,"created with Koi", GIMP_PDB_INT32, 0, GIMP_PDB_INT32, 1, GIMP_PDB_INT32, 0, GIMP_PDB_INT32, 1, GIMP_PDB_END);
//		for(ii = 0; ii < SLEEP_TIME; ii++)
//		{
//			job->progress = ((float)ii/SLEEP_TIME) * 4;
			sleep(1);
//		}



		printf("saved jpeg\n");
//		sleep(1);
		// reload our saved image and suck a layer off of it to subtract against or original image
		temp_layer = gimp_file_load_layer(mode, job->image_id, temp_file_name);

		printf("loaded new layer %d in image %d\n", temp_layer, job->image_id);

		//gimp_layer_add_alpha(temp_layer);

		gimp_layer_set_mode(temp_layer, 8);

		printf("set layer mode %d\n", temp_layer);

		/* Add the new layer to this image as the top layer */
		if (gimp_image_add_layer(job->image_id, temp_layer, -1) != TRUE)
		{
			printf("failed to create layer\n");
			return;
		}


		printf("set layer as top\n");

		layer = gimp_image_get_active_layer(job->image_id);
		if (layer == -1)
		{
			printf("failed to get active layer\n");
			return;
		}

		gimp_image_merge_down(job->image_id, layer, 2);

		printf("merged layers\n");
		job->drawable->drawable_id = gimp_image_get_active_drawable(job->image_id);
//		printf("get active drawable\n");
//		gimp_brightness_contrast(job->drawable->drawable_id, 126, 125);
//		printf("adjust contrast\n");
//
//		printf("Jpeg threshold: %d\n",jpeg_threshold);
//
//		//I should have this subtract against an edge detection layer and then threshold it
//
//		gimp_threshold(job->drawable->drawable_id, jpeg_threshold,255 );
//		printf("threshold\n");

//			if(! gimp_drawable_has_alpha (job->drawable->drawable_id))
//			{
//				 /* some filtermacros do not work with layer that do not have an alpha channel
//				 * and cause gimp to fail on attempt to call gimp_pixel_rgn_init
//				  * with both dirty and shadow flag set to TRUE
//				  * in this situation GIMP displays the error message
//				  *    "expected tile ack and received: 5"
//				  *    and causes the called plug-in to exit immediate without success
//				  * Therfore always add an alpha channel before calling a filtermacro.
//				  */
//				  gimp_layer_add_alpha(layer);
//				  printf("adding alpha channel\n");
//		   }

		remove(temp_file_name);

		sleep(1);

//		job->progress = 4;

		pthread_cond_broadcast(&jpeg_cond);
		pthread_mutex_lock(&jpeg_mutex);
		printf("got lock\n");
		jpeg_wait = 0;
		pthread_mutex_unlock(&jpeg_mutex);

		printf("drawable ID after jpeg %d\n",gimp_image_get_active_drawable(job->image_id));

	}
	else
	{
		printf("thread %d waiting\n", job->thread);
		pthread_mutex_lock(&jpeg_mutex);
		while (jpeg_wait)
		{
			pthread_cond_wait(&jpeg_cond, &jpeg_mutex);
		}
		pthread_mutex_unlock(&jpeg_mutex);
	}


	job->progress = 1;

	return NULL;
}
コード例 #16
0
ファイル: gimpwarptool.c プロジェクト: Distrotech/gimp
static void
gimp_warp_tool_animate (GimpWarpTool *wt)
{
  GimpTool        *tool    = GIMP_TOOL (wt);
  GimpWarpOptions *options = GIMP_WARP_TOOL_GET_OPTIONS (wt);
  GimpImage       *orig_image;
  GimpImage       *image;
  GimpLayer       *layer;
  GimpLayer       *first_layer;
  GeglNode        *scale_node;
  GimpProgress    *progress;
  GtkWidget       *widget;
  gint             i;

  if (! gimp_warp_tool_get_undo_desc (tool, tool->display))
    {
      gimp_tool_message_literal (tool, tool->display,
                                 _("Please add some warp strokes first."));
      return;
    }

  /*  get rid of the image map so we can use wt->graph  */
  if (wt->image_map)
    {
      gimp_image_map_abort (wt->image_map);
      g_object_unref (wt->image_map);
      wt->image_map = NULL;
    }

  gimp_progress_start (GIMP_PROGRESS (tool), FALSE,
                       _("Rendering Frame %d"), 1);

  orig_image = gimp_item_get_image (GIMP_ITEM (tool->drawable));

  image = gimp_create_image (orig_image->gimp,
                             gimp_item_get_width  (GIMP_ITEM (tool->drawable)),
                             gimp_item_get_height (GIMP_ITEM (tool->drawable)),
                             gimp_drawable_get_base_type (tool->drawable),
                             gimp_drawable_get_precision (tool->drawable),
                             TRUE);

  /*  the first frame is always the unwarped image  */
  layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (tool->drawable), image,
                                         GIMP_TYPE_LAYER));
  gimp_object_take_name (GIMP_OBJECT (layer),
                         g_strdup_printf (_("Frame %d"), 1));

  gimp_item_set_offset (GIMP_ITEM (layer), 0, 0);
  gimp_item_set_visible (GIMP_ITEM (layer), TRUE, FALSE);
  gimp_layer_set_mode (layer, GIMP_NORMAL_MODE, FALSE);
  gimp_layer_set_opacity (layer, GIMP_OPACITY_OPAQUE, FALSE);
  gimp_image_add_layer (image, layer, NULL, 0, FALSE);

  first_layer = layer;

  scale_node = gegl_node_new_child (NULL,
                                    "operation",    "gimp:scalar-multiply",
                                    "n-components", 2,
                                    NULL);
  gimp_warp_tool_add_op (wt, scale_node);

  progress = gimp_sub_progress_new (GIMP_PROGRESS (tool));

  for (i = 1; i < options->n_animation_frames; i++)
    {
      gimp_progress_set_text (GIMP_PROGRESS (tool),
                              _("Rendering Frame %d"), i + 1);

      gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (progress),
                                  i, options->n_animation_frames);

      layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (first_layer),
                                               GIMP_TYPE_LAYER));
      gimp_object_take_name (GIMP_OBJECT (layer),
                             g_strdup_printf (_("Frame %d"), i + 1));

      gegl_node_set (scale_node,
                     "factor", (gdouble) i /
                               (gdouble) (options->n_animation_frames - 1),
                     NULL);

      gimp_gegl_apply_operation (gimp_drawable_get_buffer (GIMP_DRAWABLE (first_layer)),
                                 progress,
                                 _("Frame"),
                                 wt->graph,
                                 gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
                                 NULL);

      gimp_image_add_layer (image, layer, NULL, 0, FALSE);
    }

  g_object_unref (progress);

  gimp_warp_tool_remove_op (wt, scale_node);

  gimp_progress_end (GIMP_PROGRESS (tool));

  /*  recreate the image map  */
  gimp_warp_tool_create_image_map (wt, tool->drawable);
  gimp_image_map_apply (wt->image_map, NULL);

  widget = GTK_WIDGET (gimp_display_get_shell (tool->display));
  gimp_create_display (orig_image->gimp, image, GIMP_UNIT_PIXEL, 1.0,
                       G_OBJECT (gtk_widget_get_screen (widget)),
                       gimp_widget_get_monitor (widget));
  g_object_unref (image);
}