gboolean
gimp_image_pick_color (GimpImage     *image,
                       GimpDrawable  *drawable,
                       gint           x,
                       gint           y,
                       gboolean       sample_merged,
                       gboolean       sample_average,
                       gdouble        average_radius,
                       const Babl   **sample_format,
                       gpointer       pixel,
                       GimpRGB       *color)
{
  GimpPickable *pickable;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
  g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), FALSE);
  g_return_val_if_fail (drawable == NULL ||
                        gimp_item_get_image (GIMP_ITEM (drawable)) == image,
                        FALSE);

  if (! sample_merged)
    {
      if (! drawable)
        drawable = gimp_image_get_active_drawable (image);

      if (! drawable)
        return FALSE;
    }

  if (sample_merged)
    {
      pickable = GIMP_PICKABLE (image);
    }
  else
    {
      gint off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
      x -= off_x;
      y -= off_y;

      pickable = GIMP_PICKABLE (drawable);
    }

  /* Do *not* call gimp_pickable_flush() here because it's too expensive
   * to call it unconditionally each time e.g. the cursor view is updated.
   * Instead, call gimp_pickable_flush() in the callers if needed.
   */

  if (sample_format)
    *sample_format = gimp_pickable_get_format (pickable);

  return gimp_pickable_pick_color (pickable, x, y,
                                   sample_average, average_radius,
                                   pixel, color);
}
void
gimp_channel_select_fuzzy (GimpChannel         *channel,
                           GimpDrawable        *drawable,
                           gboolean             sample_merged,
                           gint                 x,
                           gint                 y,
                           gfloat               threshold,
                           gboolean             select_transparent,
                           GimpSelectCriterion  select_criterion,
                           gboolean             diagonal_neighbors,
                           GimpChannelOps       op,
                           gboolean             antialias,
                           gboolean             feather,
                           gdouble              feather_radius_x,
                           gdouble              feather_radius_y)
{
  GimpPickable *pickable;
  GeglBuffer   *add_on;
  gint          add_on_x = 0;
  gint          add_on_y = 0;

  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
  g_return_if_fail (GIMP_IS_DRAWABLE (drawable));

  if (sample_merged)
    pickable = GIMP_PICKABLE (gimp_item_get_image (GIMP_ITEM (drawable)));
  else
    pickable = GIMP_PICKABLE (drawable);

  add_on = gimp_pickable_contiguous_region_by_seed (pickable,
                                                    antialias,
                                                    threshold,
                                                    select_transparent,
                                                    select_criterion,
                                                    diagonal_neighbors,
                                                    x, y);

  if (! sample_merged)
    gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y);

  gimp_channel_select_buffer (channel, C_("undo-type", "Fuzzy Select"),
                              add_on, add_on_x, add_on_y,
                              op,
                              feather,
                              feather_radius_x,
                              feather_radius_y);
  g_object_unref (add_on);
}
Exemple #3
0
static GValueArray *
selection_value_invoker (GimpProcedure      *procedure,
                         Gimp               *gimp,
                         GimpContext        *context,
                         GimpProgress       *progress,
                         const GValueArray  *args,
                         GError            **error)
{
  gboolean success = TRUE;
  GValueArray *return_vals;
  GimpImage *image;
  gint32 x;
  gint32 y;
  gint32 value = 0;

  image = gimp_value_get_image (&args->values[0], gimp);
  x = g_value_get_int (&args->values[1]);
  y = g_value_get_int (&args->values[2]);

  if (success)
    {
      value = gimp_pickable_get_opacity_at (GIMP_PICKABLE (gimp_image_get_mask (image)), x, y);
    }

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

  if (success)
    g_value_set_int (&return_vals->values[1], value);

  return return_vals;
}
Exemple #4
0
GimpTempBuf *
gimp_image_get_new_preview (GimpViewable *viewable,
                            GimpContext  *context,
                            gint          width,
                            gint          height)
{
  GimpImage   *image = GIMP_IMAGE (viewable);
  const Babl  *format;
  gboolean     linear;
  GimpTempBuf *buf;
  gdouble      scale_x;
  gdouble      scale_y;

  scale_x = (gdouble) width  / (gdouble) gimp_image_get_width  (image);
  scale_y = (gdouble) height / (gdouble) gimp_image_get_height (image);

  format = gimp_projectable_get_format (GIMP_PROJECTABLE (image));
  linear = gimp_babl_format_get_linear (format);

  format = gimp_babl_format (gimp_babl_format_get_base_type (format),
                             gimp_babl_precision (GIMP_COMPONENT_TYPE_U8,
                                                  linear),
                             babl_format_has_alpha (format));

  buf = gimp_temp_buf_new (width, height, format);

  gegl_buffer_get (gimp_pickable_get_buffer (GIMP_PICKABLE (image)),
                   GEGL_RECTANGLE (0, 0, width, height),
                   MIN (scale_x, scale_y),
                   gimp_temp_buf_get_format (buf),
                   gimp_temp_buf_get_data (buf),
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);

  return buf;
}
GimpLayer *
gimp_image_pick_layer (const GimpImage *image,
                       gint             x,
                       gint             y)
{
  GList *all_layers;
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      gint       off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);

      if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
                                        x - off_x, y - off_y) > 63)
        {
          g_list_free (all_layers);

          return layer;
        }
    }

  g_list_free (all_layers);

  return NULL;
}
Exemple #6
0
const GimpBuffer *
gimp_edit_copy (GimpImage     *image,
                GimpDrawable  *drawable,
                GimpContext   *context,
                GError       **error)
{
  GimpBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
                              context, FALSE, error);

  if (buffer)
    {
      gimp_set_global_buffer (image->gimp, buffer);
      g_object_unref (buffer);

      return image->gimp->global_buffer;
    }

  return NULL;
}
Exemple #7
0
static void
gimp_view_renderer_transform_create (GimpViewRenderer *renderer,
                                     GtkWidget        *widget,
                                     GeglBuffer       *src_buffer,
                                     GeglBuffer       *dest_buffer)
{
  if (GIMP_IS_COLOR_MANAGED (renderer->viewable))
    {
      GimpColorManaged *managed  = GIMP_COLOR_MANAGED (renderer->viewable);
      GimpPickable     *pickable = GIMP_PICKABLE (renderer->viewable);
      GimpColorProfile *profile;

      profile = gimp_color_managed_get_color_profile (managed);

      if (profile)
        {
          GimpImage       *image  = gimp_pickable_get_image (pickable);
          GimpColorConfig *config = image->gimp->config->color_management;

          renderer->profile_src_format  = gegl_buffer_get_format (src_buffer);
          renderer->profile_dest_format = gegl_buffer_get_format (dest_buffer);

          renderer->profile_transform =
            gimp_widget_get_color_transform (widget, config,
                                             profile,
                                             &renderer->profile_src_format,
                                             &renderer->profile_dest_format);
        }
    }
}
Exemple #8
0
const gchar *
gimp_edit_named_copy_visible (GimpImage    *image,
                              const gchar  *name,
                              GimpContext  *context,
                              GError      **error)
{
  GimpBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  buffer = gimp_edit_extract (image, GIMP_PICKABLE (image),
                              context, FALSE, error);

  if (buffer)
    {
      gimp_object_set_name (GIMP_OBJECT (buffer), name);
      gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer));
      g_object_unref (buffer);

      return gimp_object_get_name (buffer);
    }

  return NULL;
}
Exemple #9
0
GeglNode *
gimp_projection_get_sink_node (GimpProjection *proj)
{
  GeglNode   *graph;
  GeglBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL);

  if (proj->sink_node)
    return proj->sink_node;

  proj->graph = gegl_node_new ();

  g_object_set (proj->graph,
                "dont-cache", TRUE,
                NULL);

  graph = gimp_projectable_get_graph (proj->projectable);
  gegl_node_add_child (proj->graph, graph);

  buffer = gimp_projection_get_buffer (GIMP_PICKABLE (proj));

  proj->sink_node =
    gegl_node_new_child (proj->graph,
                         "operation", "gegl:write-buffer",
                         "buffer",    buffer,
                         NULL);

  gegl_node_connect_to (graph,           "output",
                        proj->sink_node, "input");

  return proj->sink_node;
}
Exemple #10
0
const gchar *
gimp_edit_named_cut (GimpImage     *image,
                     const gchar   *name,
                     GimpDrawable  *drawable,
                     GimpContext   *context,
                     GError       **error)
{
  GimpBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
                              context, TRUE, error);

  if (buffer)
    {
      gimp_object_set_name (GIMP_OBJECT (buffer), name);
      gimp_container_add (image->gimp->named_buffers, GIMP_OBJECT (buffer));
      g_object_unref (buffer);

      return gimp_object_get_name (buffer);
    }

  return NULL;
}
Exemple #11
0
TileManager *
gimp_projection_get_tiles_at_level (GimpProjection *proj,
                                    gint            level,
                                    gboolean       *is_premult)
{
  g_return_val_if_fail (GIMP_IS_PROJECTION (proj), NULL);

  if (! proj->pyramid)
    {
      const Babl *format;
      gint        bytes;
      gint        width;
      gint        height;

      format = gimp_projection_get_format (GIMP_PICKABLE (proj));

      bytes = babl_format_get_bytes_per_pixel (format);
      gimp_projectable_get_size (proj->projectable, &width, &height);

      proj->pyramid = tile_pyramid_new (bytes, width, height);

      tile_pyramid_set_validate_proc (proj->pyramid,
                                      (TileValidateProc) gimp_projection_validate_tile,
                                      proj);
    }

  return tile_pyramid_get_tiles (proj->pyramid, level, is_premult);
}
static GimpChannel *
gimp_by_color_select_tool_get_mask (GimpRegionSelectTool *region_select,
                                    GimpDisplay          *display)
{
  GimpTool              *tool    = GIMP_TOOL (region_select);
  GimpSelectionOptions  *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
  GimpDrawable          *drawable;
  GimpPickable          *pickable;
  GimpRGB                color;
  gint                   x, y;

  drawable = gimp_image_get_active_drawable (display->image);

  x = region_select->x;
  y = region_select->y;

  if (! options->sample_merged)
    {
      gint off_x, off_y;

      gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y);

      x -= off_x;
      y -= off_y;

      pickable = GIMP_PICKABLE (drawable);
    }
  else
    {
      pickable = GIMP_PICKABLE (display->image->projection);
    }

  gimp_pickable_flush (pickable);

  if (gimp_pickable_get_color_at (pickable, x, y, &color))
    return gimp_image_contiguous_region_by_color (display->image, drawable,
                                                  options->sample_merged,
                                                  options->antialias,
                                                  options->threshold,
                                                  options->select_transparent,
                                                  options->select_criterion,
                                                  &color);
  else
    return NULL;
}
/**
 * gimp_projection_initialize:
 * @proj: A #GimpProjection.
 * @x:
 * @y:
 * @w:
 * @h:
 *
 * This function determines whether a visible layer with combine mode
 * Normal provides complete coverage over the specified area.  If not,
 * the projection is initialized to transparent black.
 */
static void
gimp_projection_initialize (GimpProjection *proj,
                            gint            x,
                            gint            y,
                            gint            w,
                            gint            h)
{
    GList    *list;
    gint      proj_off_x;
    gint      proj_off_y;
    gboolean  coverage = FALSE;

    gimp_projectable_get_offset (proj->projectable, &proj_off_x, &proj_off_y);

    for (list = gimp_projectable_get_layers (proj->projectable);
            list;
            list = g_list_next (list))
    {
        GimpLayer    *layer    = list->data;
        GimpDrawable *drawable = GIMP_DRAWABLE (layer);
        GimpItem     *item     = GIMP_ITEM (layer);
        gint          off_x, off_y;

        gimp_item_get_offset (item, &off_x, &off_y);

        /*  subtract the projectable's offsets because the list of
         *  update areas is in tile-pyramid coordinates, but our
         *  external API is always in terms of image coordinates.
         */
        off_x -= proj_off_x;
        off_y -= proj_off_y;

        if (gimp_item_get_visible (item)                          &&
                ! gimp_drawable_has_alpha (drawable)                  &&
                ! gimp_layer_get_mask (layer)                         &&
                gimp_layer_get_mode (layer) == GIMP_NORMAL_MODE       &&
                gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE &&
                off_x <= x                                            &&
                off_y <= y                                            &&
                (off_x + gimp_item_get_width  (item)) >= (x + w)      &&
                (off_y + gimp_item_get_height (item)) >= (y + h))
        {
            coverage = TRUE;
            break;
        }
    }

    if (! coverage)
    {
        PixelRegion region;

        pixel_region_init (&region,
                           gimp_pickable_get_tiles (GIMP_PICKABLE (proj)),
                           x, y, w, h, TRUE);
        clear_region (&region);
    }
}
Exemple #14
0
static GeglBuffer *
gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select,
                                 GimpDisplay          *display)
{
  GimpTool                *tool        = GIMP_TOOL (region_select);
  GimpSelectionOptions    *sel_options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
  GimpRegionSelectOptions *options     = GIMP_REGION_SELECT_TOOL_GET_OPTIONS (tool);
  GimpImage               *image       = gimp_display_get_image (display);
  GimpDrawable            *drawable    = gimp_image_get_active_drawable (image);
  GimpPickable            *pickable;
  gint                     x, y;

  x = region_select->x;
  y = region_select->y;

  if (! options->sample_merged)
    {
      gint off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      x -= off_x;
      y -= off_y;

      pickable = GIMP_PICKABLE (drawable);
    }
  else
    {
      pickable = GIMP_PICKABLE (image);
    }

  return gimp_pickable_contiguous_region_by_seed (pickable,
                                                  sel_options->antialias,
                                                  options->threshold / 255.0,
                                                  options->select_transparent,
                                                  options->select_criterion,
                                                  options->diagonal_neighbors,
                                                  x, y);
}
Exemple #15
0
GimpObject *
gimp_edit_cut (GimpImage     *image,
               GimpDrawable  *drawable,
               GimpContext   *context,
               GError       **error)
{
  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  if (GIMP_IS_LAYER (drawable) &&
      gimp_channel_is_empty (gimp_image_get_mask (image)))
    {
      GimpImage *clip_image;

      clip_image = gimp_image_new_from_drawable (image->gimp, drawable);
      gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image));
      gimp_set_clipboard_image (image->gimp, clip_image);
      g_object_unref (clip_image);

      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT,
                                   C_("undo-type", "Cut Layer"));

      gimp_image_remove_layer (image, GIMP_LAYER (drawable),
                               TRUE, NULL);

      gimp_image_undo_group_end (image);

      return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp));
    }
  else
    {
      GimpBuffer *buffer;

      buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable),
                                  context, TRUE, error);

      if (buffer)
        {
          gimp_set_clipboard_buffer (image->gimp, buffer);
          g_object_unref (buffer);

          return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp));
        }
    }

  return NULL;
}
GimpTextLayer *
gimp_image_pick_text_layer (const GimpImage *image,
                            gint             x,
                            gint             y)
{
  GList *all_layers;
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      gint       off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);

      if (GIMP_IS_TEXT_LAYER (layer) &&
          x >= off_x &&
          y >= off_y &&
          x <  off_x + gimp_item_get_width  (GIMP_ITEM (layer)) &&
          y <  off_y + gimp_item_get_height (GIMP_ITEM (layer)))
        {
          g_list_free (all_layers);

          return GIMP_TEXT_LAYER (layer);
        }
      else if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
                                             x - off_x, y - off_y) > 63)
        {
          /*  a normal layer covers any possible text layers below,
           *  bail out
           */

          break;
        }
    }

  g_list_free (all_layers);

  return NULL;
}
Exemple #17
0
static void
gimp_projection_invalidate (GimpProjection *proj,
                            guint           x,
                            guint           y,
                            guint           w,
                            guint           h)
{
  if (proj->pyramid)
    {
      if (proj->sink_node)
        {
          GeglBuffer *buffer;

          buffer = gimp_projection_get_buffer (GIMP_PICKABLE (proj));

          /* makes the buffer drop all GimpTiles */
          gimp_gegl_buffer_refetch_tiles (buffer);
        }

      tile_pyramid_invalidate_area (proj->pyramid, x, y, w, h);
    }
}
Exemple #18
0
static gboolean
gimp_image_map_tool_pick_color (GimpColorTool *color_tool,
                                gint           x,
                                gint           y,
                                GimpImageType *sample_type,
                                GimpRGB       *color,
                                gint          *color_index)
{
  GimpImageMapTool *tool = GIMP_IMAGE_MAP_TOOL (color_tool);
  gint              off_x, off_y;

  gimp_item_offsets (GIMP_ITEM (tool->drawable), &off_x, &off_y);

  *sample_type = gimp_drawable_type (tool->drawable);

  return gimp_pickable_pick_color (GIMP_PICKABLE (tool->image_map),
                                   x - off_x,
                                   y - off_y,
                                   color_tool->options->sample_average,
                                   color_tool->options->average_radius,
                                   color, color_index);
}
static gboolean
gimp_image_map_tool_pick_color (GimpColorTool  *color_tool,
                                gint            x,
                                gint            y,
                                const Babl    **sample_format,
                                gpointer        pixel,
                                GimpRGB        *color)
{
  GimpImageMapTool *im_tool = GIMP_IMAGE_MAP_TOOL (color_tool);
  gint              off_x, off_y;

  gimp_item_get_offset (GIMP_ITEM (im_tool->drawable), &off_x, &off_y);

  *sample_format = gimp_drawable_get_format (im_tool->drawable);

  return gimp_pickable_pick_color (GIMP_PICKABLE (im_tool->drawable),
                                   x - off_x,
                                   y - off_y,
                                   color_tool->options->sample_average,
                                   color_tool->options->average_radius,
                                   pixel, color);
}
Exemple #20
0
static GimpValueArray *
selection_value_invoker (GimpProcedure         *procedure,
                         Gimp                  *gimp,
                         GimpContext           *context,
                         GimpProgress          *progress,
                         const GimpValueArray  *args,
                         GError               **error)
{
  gboolean success = TRUE;
  GimpValueArray *return_vals;
  GimpImage *image;
  gint32 x;
  gint32 y;
  gint32 value = 0;

  image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp);
  x = g_value_get_int (gimp_value_array_index (args, 1));
  y = g_value_get_int (gimp_value_array_index (args, 2));

  if (success)
    {
      gdouble val;

      val= gimp_pickable_get_opacity_at (GIMP_PICKABLE (gimp_image_get_mask (image)),
                                         x, y);

      value = ROUND (CLAMP (val, 0.0, 1.0) * 255.0);
    }

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

  if (success)
    g_value_set_int (gimp_value_array_index (return_vals, 1), value);

  return return_vals;
}
Exemple #21
0
static GeglBuffer *
gimp_projection_get_buffer (GimpPickable *pickable)
{
  GimpProjection *proj = GIMP_PROJECTION (pickable);

  if (! proj->buffer)
    {
      GeglNode   *graph;
      const Babl *format;
      gint        width;
      gint        height;

      graph = gimp_projectable_get_graph (proj->projectable);
      format = gimp_projection_get_format (GIMP_PICKABLE (proj));
      gimp_projectable_get_size (proj->projectable, &width, &height);

      proj->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
                                      format);

      proj->validate_handler = gimp_tile_handler_projection_new (graph);
      gegl_buffer_add_handler (proj->buffer, proj->validate_handler);

      /*  This used to call gimp_tile_handler_projection_invalidate()
       *  which forced the entire projection to be constructed in one
       *  go for new images, causing a potentially huge delay. Now we
       *  initially validate stuff the normal way, which makes the
       *  image appear incrementally, but it keeps everything
       *  responsive.
       */
      gimp_projection_add_update_area (proj, 0, 0, width, height);
      proj->invalidate_preview = TRUE;
      gimp_projection_flush (proj);
    }

  return proj->buffer;
}
Exemple #22
0
const GimpBuffer *
gimp_edit_copy_visible (GimpImage    *image,
                        GimpContext  *context,
                        GError      **error)
{
  GimpBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  buffer = gimp_edit_extract (image, GIMP_PICKABLE (image),
                              context, FALSE, error);

  if (buffer)
    {
      gimp_set_global_buffer (image->gimp, buffer);
      g_object_unref (buffer);

      return image->gimp->global_buffer;
    }

  return NULL;
}
static void
gimp_perspective_clone_paint (GimpPaintCore    *paint_core,
                              GimpDrawable     *drawable,
                              GimpPaintOptions *paint_options,
                              const GimpCoords *coords,
                              GimpPaintState    paint_state,
                              guint32           time)
{
  GimpSourceCore       *source_core   = GIMP_SOURCE_CORE (paint_core);
  GimpPerspectiveClone *clone         = GIMP_PERSPECTIVE_CLONE (paint_core);
  GimpContext          *context       = GIMP_CONTEXT (paint_options);
  GimpCloneOptions     *clone_options = GIMP_CLONE_OPTIONS (paint_options);
  GimpSourceOptions    *options       = GIMP_SOURCE_OPTIONS (paint_options);

  switch (paint_state)
    {
    case GIMP_PAINT_STATE_INIT:
      if (source_core->set_source)
        {
          g_object_set (source_core, "src-drawable", drawable, NULL);

          source_core->src_x = coords->x;
          source_core->src_y = coords->y;

          /* get source coordinates in front view perspective */
          gimp_matrix3_transform_point (&clone->transform_inv,
                                        source_core->src_x,
                                        source_core->src_y,
                                        &clone->src_x_fv,
                                        &clone->src_y_fv);

          source_core->first_stroke = TRUE;
        }
      else
        {
          GeglBuffer *orig_buffer = NULL;
          GeglNode   *tile        = NULL;
          GeglNode   *src_node;

          if (options->align_mode == GIMP_SOURCE_ALIGN_NO)
            {
              source_core->orig_src_x = source_core->src_x;
              source_core->orig_src_y = source_core->src_y;

              source_core->first_stroke = TRUE;
            }

          clone->node = gegl_node_new ();

          g_object_set (clone->node,
                        "dont-cache", TRUE,
                        NULL);

          switch (clone_options->clone_type)
            {
            case GIMP_IMAGE_CLONE:
              {
                GimpPickable *src_pickable;
                GimpImage    *src_image;
                GimpImage    *dest_image;

                /*  If the source image is different from the
                 *  destination, then we should copy straight from the
                 *  source image to the canvas.
                 *  Otherwise, we need a call to get_orig_image to make sure
                 *  we get a copy of the unblemished (offset) image
                 */
                src_pickable = GIMP_PICKABLE (source_core->src_drawable);
                src_image    = gimp_pickable_get_image (src_pickable);

                if (options->sample_merged)
                  src_pickable = GIMP_PICKABLE (gimp_image_get_projection (src_image));

                dest_image = gimp_item_get_image (GIMP_ITEM (drawable));

                if ((options->sample_merged &&
                     (src_image != dest_image)) ||
                    (! options->sample_merged &&
                     (source_core->src_drawable != drawable)))
                  {
                    orig_buffer = gimp_pickable_get_buffer (src_pickable);
                  }
                else
                  {
                    if (options->sample_merged)
                      orig_buffer = gimp_paint_core_get_orig_proj (paint_core);
                    else
                      orig_buffer = gimp_paint_core_get_orig_image (paint_core);
                  }
              }
              break;

            case GIMP_PATTERN_CLONE:
              {
                GimpPattern *pattern = gimp_context_get_pattern (context);

                orig_buffer = gimp_pattern_create_buffer (pattern);

                tile = gegl_node_new_child (clone->node,
                                            "operation", "gegl:tile",
                                            NULL);
                clone->crop = gegl_node_new_child (clone->node,
                                                   "operation", "gegl:crop",
                                                   NULL);
              }
              break;
            }

          src_node = gegl_node_new_child (clone->node,
                                          "operation", "gegl:buffer-source",
                                          "buffer",    orig_buffer,
                                          NULL);

          clone->transform_node =
            gegl_node_new_child (clone->node,
                                 "operation", "gegl:transform",
                                 "sampler",    GIMP_INTERPOLATION_LINEAR,
                                 NULL);

          clone->dest_node =
            gegl_node_new_child (clone->node,
                                 "operation", "gegl:write-buffer",
                                 NULL);

          if (tile)
            {
              gegl_node_link_many (src_node,
                                   tile,
                                   clone->crop,
                                   clone->transform_node,
                                   clone->dest_node,
                                   NULL);

              g_object_unref (orig_buffer);
            }
          else
            {
              gegl_node_link_many (src_node,
                                   clone->transform_node,
                                   clone->dest_node,
                                   NULL);
            }
        }
      break;

    case GIMP_PAINT_STATE_MOTION:
      if (source_core->set_source)
        {
          /*  If the control key is down, move the src target and return */

          source_core->src_x = coords->x;
          source_core->src_y = coords->y;

          /* get source coordinates in front view perspective */
          gimp_matrix3_transform_point (&clone->transform_inv,
                                        source_core->src_x,
                                        source_core->src_y,
                                        &clone->src_x_fv,
                                        &clone->src_y_fv);

          source_core->first_stroke = TRUE;
        }
      else
        {
          /*  otherwise, update the target  */

          gint dest_x;
          gint dest_y;

          dest_x = coords->x;
          dest_y = coords->y;

          if (options->align_mode == GIMP_SOURCE_ALIGN_REGISTERED)
            {
              source_core->offset_x = 0;
              source_core->offset_y = 0;
            }
          else if (options->align_mode == GIMP_SOURCE_ALIGN_FIXED)
            {
              source_core->offset_x = source_core->src_x - dest_x;
              source_core->offset_y = source_core->src_y - dest_y;
            }
          else if (source_core->first_stroke)
            {
              source_core->offset_x = source_core->src_x - dest_x;
              source_core->offset_y = source_core->src_y - dest_y;

              /* get destination coordinates in front view perspective */
              gimp_matrix3_transform_point (&clone->transform_inv,
                                            dest_x,
                                            dest_y,
                                            &clone->dest_x_fv,
                                            &clone->dest_y_fv);

              source_core->first_stroke = FALSE;
            }

          gimp_source_core_motion (source_core, drawable, paint_options, coords);
        }
      break;

    case GIMP_PAINT_STATE_FINISH:
      if (clone->node)
        {
          g_object_unref (clone->node);
          clone->node           = NULL;
          clone->crop           = NULL;
          clone->transform_node = NULL;
          clone->dest_node      = NULL;
        }
      break;

    default:
      break;
    }

  g_object_notify (G_OBJECT (clone), "src-x");
  g_object_notify (G_OBJECT (clone), "src-y");
}
Exemple #24
0
gboolean
gimp_paint_core_start (GimpPaintCore     *core,
                       GimpDrawable      *drawable,
                       GimpPaintOptions  *paint_options,
                       const GimpCoords  *coords,
                       GError           **error)
{
  GimpImage   *image;
  GimpItem    *item;
  GimpChannel *mask;

  g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
  g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE);
  g_return_val_if_fail (coords != NULL, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  item  = GIMP_ITEM (drawable);
  image = gimp_item_get_image (item);

  if (core->stroke_buffer)
    {
      g_array_free (core->stroke_buffer, TRUE);
      core->stroke_buffer = NULL;
    }

  core->stroke_buffer = g_array_sized_new (TRUE, TRUE,
                                           sizeof (GimpCoords),
                                           STROKE_BUFFER_INIT_SIZE);

  /* remember the last stroke's endpoint for later undo */
  core->start_coords = core->last_coords;

  core->cur_coords = *coords;

  if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable,
                                                 paint_options,
                                                 coords, error))
    {
      return FALSE;
    }

  /*  Allocate the undo structure  */
  if (core->undo_buffer)
    g_object_unref (core->undo_buffer);

  core->undo_buffer = gegl_buffer_dup (gimp_drawable_get_buffer (drawable));

  /*  Allocate the saved proj structure  */
  if (core->saved_proj_buffer)
    {
      g_object_unref (core->saved_proj_buffer);
      core->saved_proj_buffer = NULL;
    }

  if (core->use_saved_proj)
    {
      GeglBuffer *buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));

      core->saved_proj_buffer = gegl_buffer_dup (buffer);
    }

  /*  Allocate the canvas blocks structure  */
  if (core->canvas_buffer)
    g_object_unref (core->canvas_buffer);

  core->canvas_buffer =
    gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                     gimp_item_get_width  (item),
                                     gimp_item_get_height (item)),
                     babl_format ("Y float"));

  /*  Get the initial undo extents  */

  core->x1 = core->x2 = core->cur_coords.x;
  core->y1 = core->y2 = core->cur_coords.y;

  core->last_paint.x = -1e6;
  core->last_paint.y = -1e6;

  mask = gimp_image_get_mask (image);

  /*  don't apply the mask to itself and don't apply an empty mask  */
  if (GIMP_DRAWABLE (mask) != drawable && ! gimp_channel_is_empty (mask))
    {
      GeglBuffer *mask_buffer;
      gint        offset_x;
      gint        offset_y;

      mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
      gimp_item_get_offset (item, &offset_x, &offset_y);

      core->mask_buffer   = g_object_ref (mask_buffer);
      core->mask_x_offset = -offset_x;
      core->mask_y_offset = -offset_y;
    }
  else
    {
      core->mask_buffer = NULL;
    }

  core->linear_mode = gimp_drawable_get_linear (drawable);

  if (paint_options->use_applicator)
    {
      core->applicator = gimp_applicator_new (NULL, core->linear_mode);

      if (core->mask_buffer)
        {
          gimp_applicator_set_mask_buffer (core->applicator,
                                           core->mask_buffer);
          gimp_applicator_set_mask_offset (core->applicator,
                                           core->mask_x_offset,
                                           core->mask_y_offset);
        }

      gimp_applicator_set_affect (core->applicator,
                                  gimp_drawable_get_active_mask (drawable));
      gimp_applicator_set_dest_buffer (core->applicator,
                                       gimp_drawable_get_buffer (drawable));
    }
  else
    {
      if (core->comp_buffer)
        {
          g_object_unref (core->comp_buffer);
          core->comp_buffer = NULL;
        }

      /* Allocate the scratch buffer if there's a component mask */
      if (gimp_drawable_get_active_mask (drawable) != GIMP_COMPONENT_ALL)
        {
          const Babl *format;

          if (core->linear_mode)
            format = babl_format ("RGBA float");
          else
            format = babl_format ("R'G'B'A float");

          core->comp_buffer =
            gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                             gimp_item_get_width  (item),
                                             gimp_item_get_height (item)),
                             format);
        }
    }

  /*  Freeze the drawable preview so that it isn't constantly updated.  */
  gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable));

  return TRUE;
}
void
gimp_source_core_motion (GimpSourceCore   *source_core,
                         GimpDrawable     *drawable,
                         GimpPaintOptions *paint_options,
                         GimpSymmetry     *sym)

{
  GimpPaintCore     *paint_core   = GIMP_PAINT_CORE (source_core);
  GimpSourceOptions *options      = GIMP_SOURCE_OPTIONS (paint_options);
  GimpDynamics      *dynamics     = GIMP_BRUSH_CORE (paint_core)->dynamics;
  GimpImage         *image        = gimp_item_get_image (GIMP_ITEM (drawable));
  GimpPickable      *src_pickable = NULL;
  GeglBuffer        *src_buffer   = NULL;
  GeglRectangle      src_rect;
  gint               base_src_offset_x;
  gint               base_src_offset_y;
  gint               src_offset_x;
  gint               src_offset_y;
  GeglBuffer        *paint_buffer;
  gint               paint_buffer_x;
  gint               paint_buffer_y;
  gint               paint_area_offset_x;
  gint               paint_area_offset_y;
  gint               paint_area_width;
  gint               paint_area_height;
  gdouble            fade_point;
  gdouble            opacity;
  GeglNode          *op;
  GimpCoords        *origin;
  GimpCoords        *coords;
  gint               n_strokes;
  gint               i;

  fade_point = gimp_paint_options_get_fade (paint_options, image,
                                            paint_core->pixel_dist);

  origin     = gimp_symmetry_get_origin (sym);
  /* Some settings are based on the original stroke. */
  opacity = gimp_dynamics_get_linear_value (dynamics,
                                            GIMP_DYNAMICS_OUTPUT_OPACITY,
                                            origin,
                                            paint_options,
                                            fade_point);
  if (opacity == 0.0)
    return;

  base_src_offset_x = source_core->offset_x;
  base_src_offset_y = source_core->offset_y;

  if (gimp_source_core_use_source (source_core, options))
    {
      src_pickable = GIMP_PICKABLE (source_core->src_drawable);

      if (options->sample_merged)
        {
          GimpImage *src_image = gimp_pickable_get_image (src_pickable);
          gint       off_x, off_y;

          src_pickable = GIMP_PICKABLE (src_image);

          gimp_item_get_offset (GIMP_ITEM (source_core->src_drawable),
                                &off_x, &off_y);

          base_src_offset_x += off_x;
          base_src_offset_y += off_y;
        }

      gimp_pickable_flush (src_pickable);
    }

  gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
                                           drawable,
                                           paint_options,
                                           origin);

  n_strokes  = gimp_symmetry_get_size (sym);
  for (i = 0; i < n_strokes; i++)
    {
      coords = gimp_symmetry_get_coords (sym, i);

      paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
                                                       paint_options, coords,
                                                       &paint_buffer_x,
                                                       &paint_buffer_y,
                                                       NULL, NULL);
      if (! paint_buffer)
        continue;

      paint_area_offset_x = 0;
      paint_area_offset_y = 0;
      paint_area_width    = gegl_buffer_get_width  (paint_buffer);
      paint_area_height   = gegl_buffer_get_height (paint_buffer);

      src_offset_x = base_src_offset_x;
      src_offset_y = base_src_offset_y;
      if (gimp_source_core_use_source (source_core, options))
        {
          /* When using a source, use the same for every stroke. */
          src_offset_x = src_offset_x - coords->x + origin->x;
          src_offset_y = src_offset_y - coords->y + origin->y;
          src_buffer =
            GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core,
                                                                  drawable,
                                                                  paint_options,
                                                                  src_pickable,
                                                                  src_offset_x,
                                                                  src_offset_y,
                                                                  paint_buffer,
                                                                  paint_buffer_x,
                                                                  paint_buffer_y,
                                                                  &paint_area_offset_x,
                                                                  &paint_area_offset_y,
                                                                  &paint_area_width,
                                                                  &paint_area_height,
                                                                  &src_rect);

          if (! src_buffer)
            continue;
        }

      /*  Set the paint buffer to transparent  */
      gegl_buffer_clear (paint_buffer, NULL);

      op = gimp_symmetry_get_operation (sym, i,
                                        gegl_buffer_get_width (paint_buffer),
                                        gegl_buffer_get_height (paint_buffer));
      GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core,
                                                        drawable,
                                                        paint_options,
                                                        coords,
                                                        op,
                                                        opacity,
                                                        src_pickable,
                                                        src_buffer,
                                                        &src_rect,
                                                        src_offset_x,
                                                        src_offset_y,
                                                        paint_buffer,
                                                        paint_buffer_x,
                                                        paint_buffer_y,
                                                        paint_area_offset_x,
                                                        paint_area_offset_y,
                                                        paint_area_width,
                                                        paint_area_height);

      if (src_buffer)
        g_object_unref (src_buffer);
    }
}
static void
gimp_drawable_bucket_fill_internal (GimpDrawable        *drawable,
                                    GimpFillType         fill_type,
                                    gint                 paint_mode,
                                    gdouble              opacity,
                                    gboolean             fill_transparent,
                                    GimpSelectCriterion  fill_criterion,
                                    gdouble              threshold,
                                    gboolean             sample_merged,
                                    gboolean             diagonal_neighbors,
                                    gdouble              x,
                                    gdouble              y,
                                    const GimpRGB       *color,
                                    GimpPattern         *pattern)
{
  GimpImage    *image;
  GimpPickable *pickable;
  GeglBuffer   *buffer;
  GeglBuffer   *mask_buffer;
  gint          x1, y1, x2, y2;
  gint          mask_offset_x = 0;
  gint          mask_offset_y = 0;
  gboolean      selection;

  g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)));
  g_return_if_fail (fill_type != GIMP_FILL_PATTERN ||
                    GIMP_IS_PATTERN (pattern));
  g_return_if_fail (fill_type == GIMP_FILL_PATTERN ||
                    color != NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  selection = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2);

  if ((x1 == x2) || (y1 == y2))
    return;

  gimp_set_busy (image->gimp);

  if (sample_merged)
    pickable = GIMP_PICKABLE (image);
  else
    pickable = GIMP_PICKABLE (drawable);

  /*  Do a seed bucket fill...To do this, calculate a new
   *  contiguous region. If there is a selection, calculate the
   *  intersection of this region with the existing selection.
   */
  mask_buffer = gimp_pickable_contiguous_region_by_seed (pickable,
                                                         TRUE,
                                                         threshold,
                                                         fill_transparent,
                                                         fill_criterion,
                                                         diagonal_neighbors,
                                                         (gint) x,
                                                         (gint) y);

  if (selection)
    {
      GimpDrawable *sel;
      gint          off_x = 0;
      gint          off_y = 0;

      if (! sample_merged)
        gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      sel = GIMP_DRAWABLE (gimp_image_get_mask (image));

      gimp_gegl_mask_combine_buffer (mask_buffer,
                                     gimp_drawable_get_buffer (sel),
                                     GIMP_CHANNEL_OP_INTERSECT,
                                     -off_x, -off_y);
    }

  gimp_gegl_mask_bounds (mask_buffer, &x1, &y1, &x2, &y2);

  /*  make sure we handle the mask correctly if it was sample-merged  */
  if (sample_merged)
    {
      GimpItem *item = GIMP_ITEM (drawable);
      gint      off_x, off_y;

      /*  Limit the channel bounds to the drawable's extents  */
      gimp_item_get_offset (item, &off_x, &off_y);

      x1 = CLAMP (x1, off_x, (off_x + gimp_item_get_width (item)));
      y1 = CLAMP (y1, off_y, (off_y + gimp_item_get_height (item)));
      x2 = CLAMP (x2, off_x, (off_x + gimp_item_get_width (item)));
      y2 = CLAMP (y2, off_y, (off_y + gimp_item_get_height (item)));

      mask_offset_x = x1;
      mask_offset_y = y1;

     /*  translate mask bounds to drawable coords  */
      x1 -= off_x;
      y1 -= off_y;
      x2 -= off_x;
      y2 -= off_y;
    }
  else
    {
      mask_offset_x = x1;
      mask_offset_y = y1;
    }

  buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1),
                            gimp_drawable_get_format_with_alpha (drawable));

  switch (fill_type)
    {
    case GIMP_FILL_FOREGROUND:
    case GIMP_FILL_BACKGROUND:
    case GIMP_FILL_WHITE:
    case GIMP_FILL_TRANSPARENT:
      {
        GeglColor *gegl_color = gimp_gegl_color_new (color);

        gegl_buffer_set_color (buffer, NULL, gegl_color);
        g_object_unref (gegl_color);
      }
      break;

    case GIMP_FILL_PATTERN:
      {
        GeglBuffer *pattern_buffer = gimp_pattern_create_buffer (pattern);

        gegl_buffer_set_pattern (buffer, NULL, pattern_buffer, -x1, -y1);
        g_object_unref (pattern_buffer);
      }
      break;
    }

  gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer,
                           mask_buffer,
                           -mask_offset_x,
                           -mask_offset_y,
                           1.0);
  g_object_unref (mask_buffer);

  /*  Apply it to the image  */
  gimp_drawable_apply_buffer (drawable, buffer,
                              GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1),
                              TRUE, C_("undo-type", "Bucket Fill"),
                              opacity, paint_mode,
                              NULL, x1, y1);

  g_object_unref (buffer);

  gimp_drawable_update (drawable, x1, y1, x2 - x1, y2 - y1);

  gimp_unset_busy (image->gimp);
}
Exemple #27
0
void
gimp_source_core_motion (GimpSourceCore   *source_core,
                         GimpDrawable     *drawable,
                         GimpPaintOptions *paint_options,
                         const GimpCoords *coords)

{
  GimpPaintCore     *paint_core   = GIMP_PAINT_CORE (source_core);
  GimpSourceOptions *options      = GIMP_SOURCE_OPTIONS (paint_options);
  GimpDynamics      *dynamics     = GIMP_BRUSH_CORE (paint_core)->dynamics;
  GimpImage         *image        = gimp_item_get_image (GIMP_ITEM (drawable));
  GimpPickable      *src_pickable = NULL;
  GeglBuffer        *src_buffer   = NULL;
  GeglRectangle      src_rect;
  gint               src_offset_x;
  gint               src_offset_y;
  GeglBuffer        *paint_buffer;
  gint               paint_buffer_x;
  gint               paint_buffer_y;
  gint               paint_area_offset_x;
  gint               paint_area_offset_y;
  gint               paint_area_width;
  gint               paint_area_height;
  gdouble            fade_point;
  gdouble            opacity;

  fade_point = gimp_paint_options_get_fade (paint_options, image,
                                            paint_core->pixel_dist);

  opacity = gimp_dynamics_get_linear_value (dynamics,
                                            GIMP_DYNAMICS_OUTPUT_OPACITY,
                                            coords,
                                            paint_options,
                                            fade_point);
  if (opacity == 0.0)
    return;

  src_offset_x = source_core->offset_x;
  src_offset_y = source_core->offset_y;

  if (gimp_source_core_use_source (source_core, options))
    {
      src_pickable = GIMP_PICKABLE (source_core->src_drawable);

      if (options->sample_merged)
        {
          GimpImage *src_image = gimp_pickable_get_image (src_pickable);
          gint       off_x, off_y;

          src_pickable = GIMP_PICKABLE (src_image);

          gimp_item_get_offset (GIMP_ITEM (source_core->src_drawable),
                                &off_x, &off_y);

          src_offset_x += off_x;
          src_offset_y += off_y;
        }

      gimp_pickable_flush (src_pickable);
    }

  paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
                                                   paint_options, coords,
                                                   &paint_buffer_x,
                                                   &paint_buffer_y);
  if (! paint_buffer)
    return;

  paint_area_offset_x = 0;
  paint_area_offset_y = 0;
  paint_area_width    = gegl_buffer_get_width  (paint_buffer);
  paint_area_height   = gegl_buffer_get_height (paint_buffer);

  if (gimp_source_core_use_source (source_core, options))
    {
      src_buffer =
        GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core,
                                                              drawable,
                                                              paint_options,
                                                              src_pickable,
                                                              src_offset_x,
                                                              src_offset_y,
                                                              paint_buffer,
                                                              paint_buffer_x,
                                                              paint_buffer_y,
                                                              &paint_area_offset_x,
                                                              &paint_area_offset_y,
                                                              &paint_area_width,
                                                              &paint_area_height,
                                                              &src_rect);
      if (! src_buffer)
        return;
    }

  /*  Set the paint buffer to transparent  */
  gegl_buffer_clear (paint_buffer, NULL);

  GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core,
                                                    drawable,
                                                    paint_options,
                                                    coords,
                                                    opacity,
                                                    src_pickable,
                                                    src_buffer,
                                                    &src_rect,
                                                    src_offset_x,
                                                    src_offset_y,
                                                    paint_buffer,
                                                    paint_buffer_x,
                                                    paint_buffer_y,
                                                    paint_area_offset_x,
                                                    paint_area_offset_y,
                                                    paint_area_width,
                                                    paint_area_height);

  if (src_buffer)
    g_object_unref (src_buffer);
}
GeglBuffer *
gimp_drawable_transform_cut (GimpDrawable *drawable,
                             GimpContext  *context,
                             gint         *offset_x,
                             gint         *offset_y,
                             gboolean     *new_layer)
{
  GimpImage  *image;
  GeglBuffer *buffer;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (offset_x != NULL, NULL);
  g_return_val_if_fail (offset_y != NULL, NULL);
  g_return_val_if_fail (new_layer != NULL, NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  /*  extract the selected mask if there is a selection  */
  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
    {
      gint x, y, w, h;

      /* set the keep_indexed flag to FALSE here, since we use
       * gimp_layer_new_from_buffer() later which assumes that the buffer
       * are either RGB or GRAY.  Eeek!!!              (Sven)
       */
      if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h))
        {
          buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
                                           GIMP_PICKABLE (drawable),
                                           context,
                                           TRUE, FALSE, TRUE,
                                           offset_x, offset_y,
                                           NULL);
          /*  clear the selection  */
          gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);

          *new_layer = TRUE;
        }
      else
        {
          buffer = NULL;
          *new_layer = FALSE;
        }
    }
  else  /*  otherwise, just copy the layer  */
    {
      buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
                                       GIMP_PICKABLE (drawable),
                                       context,
                                       FALSE, TRUE, GIMP_IS_LAYER (drawable),
                                       offset_x, offset_y,
                                       NULL);

      *new_layer = FALSE;
    }

  return buffer;
}
/*
 * This function is called if the user clicks and releases the left
 * button without moving it.  There are the things we might want
 * to do here:
 * 1) If there is an existing rectangle and we are inside it, we
 *    convert it into a selection.
 * 2) If there is an existing rectangle and we are outside it, we
 *    clear it.
 * 3) If there is no rectangle and there is a floating selection,
 *    we anchor it.
 * 4) If there is no rectangle and we are inside the selection, we
 *    create a rectangle from the selection bounds.
 * 5) If there is no rectangle and we are outside the selection,
 *    we clear the selection.
 */
static gboolean
gimp_rectangle_select_tool_execute (GimpRectangleTool *rectangle,
                                    gint               x,
                                    gint               y,
                                    gint               w,
                                    gint               h)
{
  GimpTool                       *tool = GIMP_TOOL (rectangle);
  GimpRectangleSelectTool        *rect_sel_tool;
  GimpRectangleSelectToolPrivate *priv;

  rect_sel_tool = GIMP_RECTANGLE_SELECT_TOOL (rectangle);
  priv          = GIMP_RECTANGLE_SELECT_TOOL_GET_PRIVATE (rect_sel_tool);

  if (w == 0 && h == 0 && tool->display != NULL)
    {
      GimpImage   *image     = gimp_display_get_image (tool->display);
      GimpChannel *selection = gimp_image_get_mask (image);
      gint         pressx;
      gint         pressy;

      if (gimp_image_get_floating_selection (image))
        {
          floating_sel_anchor (gimp_image_get_floating_selection (image));
          gimp_image_flush (image);
          return TRUE;
        }

      pressx = ROUND (priv->press_x);
      pressy = ROUND (priv->press_y);

      /*  if the click was inside the marching ants  */
      if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection),
                                        pressx, pressy) > 0.5)
        {
          gint x1, y1, x2, y2;

          if (gimp_channel_bounds (selection, &x1, &y1, &x2, &y2))
            {
              g_object_set (rectangle,
                            "x1", x1,
                            "y1", y1,
                            "x2", x2,
                            "y2", y2,
                            NULL);
            }

          gimp_rectangle_tool_set_function (rectangle,
                                            GIMP_RECTANGLE_TOOL_MOVING);

          return FALSE;
        }
      else
        {
          GimpTool       *tool = GIMP_TOOL (rectangle);
          GimpChannelOps  operation;

          /* prevent this change from halting the tool */
          gimp_tool_control_push_preserve (tool->control, TRUE);

          /* We can conceptually think of a click outside of the
           * selection as adding a 0px selection. Behave intuitivly
           * for the current selection mode
           */
          operation = gimp_rectangle_select_tool_get_operation (rect_sel_tool);
          switch (operation)
            {
            case GIMP_CHANNEL_OP_REPLACE:
            case GIMP_CHANNEL_OP_INTERSECT:
              gimp_channel_clear (selection, NULL, TRUE);
              gimp_image_flush (image);
              break;

            case GIMP_CHANNEL_OP_ADD:
            case GIMP_CHANNEL_OP_SUBTRACT:
            default:
              /* Do nothing */
              break;
            }

          gimp_tool_control_pop_preserve (tool->control);
        }
    }

  gimp_rectangle_select_tool_update_option_defaults (rect_sel_tool, FALSE);

  /* Reset the automatic undo/redo mechanism */
  priv->undo = NULL;
  priv->redo = NULL;

  return TRUE;
}
/**
 * gimp_drawable_get_bucket_fill_buffer:
 * @drawable: the #GimpDrawable to edit.
 * @options:
 * @fill_transparent:
 * @fill_criterion:
 * @threshold:
 * @sample_merged:
 * @diagonal_neighbors:
 * @seed_x: X coordinate to start the fill.
 * @seed_y: Y coordinate to start the fill.
 * @mask_buffer: mask of the fill in-progress when in an interactive
 *               filling process. Set to NULL if you need a one-time
 *               fill.
 * @mask_x: returned x bound of @mask_buffer.
 * @mask_y: returned x bound of @mask_buffer.
 * @mask_width: returned width bound of @mask_buffer.
 * @mask_height: returned height bound of @mask_buffer.
 *
 * Creates the fill buffer for a bucket fill operation on @drawable,
 * without actually applying it (if you want to apply it directly as a
 * one-time operation, use gimp_drawable_bucket_fill() instead). If
 * @mask_buffer is not NULL, the intermediate fill mask will also be
 * returned. This fill mask can later be reused in successive calls to
 * gimp_drawable_get_bucket_fill_buffer() for interactive filling.
 *
 * Returns: a fill buffer which can be directly applied to @drawable, or
 *          used in a drawable filter as preview.
 */
GeglBuffer *
gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
                                      GimpFillOptions      *options,
                                      gboolean              fill_transparent,
                                      GimpSelectCriterion   fill_criterion,
                                      gdouble               threshold,
                                      gboolean              sample_merged,
                                      gboolean              diagonal_neighbors,
                                      gdouble               seed_x,
                                      gdouble               seed_y,
                                      GeglBuffer          **mask_buffer,
                                      gdouble              *mask_x,
                                      gdouble              *mask_y,
                                      gint                 *mask_width,
                                      gint                 *mask_height)
{
  GimpImage    *image;
  GimpPickable *pickable;
  GeglBuffer   *buffer;
  GeglBuffer   *new_mask;
  gboolean      antialias;
  gint          x, y, width, height;
  gint          mask_offset_x = 0;
  gint          mask_offset_y = 0;
  gint          sel_x, sel_y, sel_width, sel_height;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
                                  &sel_x, &sel_y, &sel_width, &sel_height))
    return NULL;

  if (mask_buffer && *mask_buffer && threshold == 0.0)
    {
      gfloat pixel;

      gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel,
                          babl_format ("Y float"),
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      if (pixel != 0.0)
        /* Already selected. This seed won't change the selection. */
        return NULL;
    }

  gimp_set_busy (image->gimp);
  if (sample_merged)
    pickable = GIMP_PICKABLE (image);
  else
    pickable = GIMP_PICKABLE (drawable);

  antialias = gimp_fill_options_get_antialias (options);

  /*  Do a seed bucket fill...To do this, calculate a new
   *  contiguous region.
   */
  new_mask = gimp_pickable_contiguous_region_by_seed (pickable,
                                                      antialias,
                                                      threshold,
                                                      fill_transparent,
                                                      fill_criterion,
                                                      diagonal_neighbors,
                                                      (gint) seed_x,
                                                      (gint) seed_y);
  if (mask_buffer && *mask_buffer)
    {
      gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer,
                                     GIMP_CHANNEL_OP_ADD, 0, 0);
      g_object_unref (*mask_buffer);
    }
  if (mask_buffer)
    *mask_buffer = new_mask;

  gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height);
  width  -= x;
  height -= y;

  /*  If there is a selection, intersect the region bounds
   *  with the selection bounds, to avoid processing areas
   *  that are going to be masked out anyway.  The actual
   *  intersection of the fill region with the mask data
   *  happens when combining the fill buffer, in
   *  gimp_drawable_apply_buffer().
   */
  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
    {
      gint off_x = 0;
      gint off_y = 0;

      if (sample_merged)
        gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      if (! gimp_rectangle_intersect (x, y, width, height,

                                      sel_x + off_x, sel_y + off_y,
                                      sel_width,     sel_height,

                                      &x, &y, &width, &height))
        {
          if (! mask_buffer)
            g_object_unref (new_mask);
          /*  The fill region and the selection are disjoint; bail.  */
          gimp_unset_busy (image->gimp);

          return NULL;
        }
    }

  /*  make sure we handle the mask correctly if it was sample-merged  */
  if (sample_merged)
    {
      GimpItem *item = GIMP_ITEM (drawable);
      gint      off_x, off_y;

      /*  Limit the channel bounds to the drawable's extents  */
      gimp_item_get_offset (item, &off_x, &off_y);

      gimp_rectangle_intersect (x, y, width, height,

                                off_x, off_y,
                                gimp_item_get_width (item),
                                gimp_item_get_height (item),

                                &x, &y, &width, &height);

      mask_offset_x = x;
      mask_offset_y = y;

     /*  translate mask bounds to drawable coords  */
      x -= off_x;
      y -= off_y;
    }
  else
    {
      mask_offset_x = x;
      mask_offset_y = y;
    }

  buffer = gimp_fill_options_create_buffer (options, drawable,
                                            GEGL_RECTANGLE (0, 0,
                                                            width, height),
                                            -x, -y);

  gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask,
                           -mask_offset_x, -mask_offset_y, 1.0);

  if (mask_x)
    *mask_x = x;
  if (mask_y)
    *mask_y = y;
  if (mask_width)
    *mask_width = width;
  if (mask_height)
    *mask_height = height;

  if (! mask_buffer)
    g_object_unref (new_mask);

  gimp_unset_busy (image->gimp);

  return buffer;
}