コード例 #1
0
void
gimp_paint_core_validate_saved_proj_tiles (GimpPaintCore *core,
                                           GimpPickable  *pickable,
                                           gint           x,
                                           gint           y,
                                           gint           w,
                                           gint           h)
{
  gint i, j;

  g_return_if_fail (GIMP_IS_PAINT_CORE (core));
  g_return_if_fail (GIMP_IS_PICKABLE (pickable));
  g_return_if_fail (core->saved_proj_tiles != NULL);

  for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
    {
      for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
        {
          Tile *dest_tile = tile_manager_get_tile (core->saved_proj_tiles,
                                                   j, i, FALSE, FALSE);

          if (! tile_is_valid (dest_tile))
            {
              Tile *src_tile =
                tile_manager_get_tile (gimp_pickable_get_tiles (pickable),
                                       j, i, TRUE, FALSE);

              tile_manager_map_tile (core->saved_proj_tiles, j, i, src_tile);
              tile_release (src_tile, FALSE);
            }
        }
    }
}
コード例 #2
0
/**
 * 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);
    }
}
コード例 #3
0
void
gimp_projection_construct (GimpProjection *proj,
                           gint            x,
                           gint            y,
                           gint            w,
                           gint            h)
{
    g_return_if_fail (GIMP_IS_PROJECTION (proj));

#if 0
    GList *layers = gimp_projectable_get_layers (proj->projectable);

    if (layers && ! layers->next) /* a single layer */
    {
        GimpLayer    *layer    = layers->data;
        GimpDrawable *drawable = GIMP_DRAWABLE (layer);
        GimpItem     *item     = GIMP_ITEM (layer);
        gint          width, height;
        gint          off_x, off_y;

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

        gimp_item_get_offset (item, &off_x, &off_y);

        if (gimp_drawable_has_alpha (drawable)                    &&
                gimp_item_get_visible (item)                          &&
                gimp_item_get_width  (item) == width                  &&
                gimp_item_get_height (item) == height                 &&
                ! gimp_drawable_is_indexed (layer)                    &&
                gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE &&
                off_x == 0                                            &&
                off_y == 0                                            &&
                proj_offset_x == 0                                    &&
                proj_offset_y == 0)
        {
            PixelRegion srcPR, destPR;

            g_printerr ("cow-projection!");

            pixel_region_init (&srcPR,
                               gimp_drawable_get_tiles (layer),
                               x, y, w,h, FALSE);
            pixel_region_init (&destPR,
                               gimp_pickable_get_tiles (GIMP_PICKABLE (proj)),
                               x, y, w,h, TRUE);

            copy_region (&srcPR, &destPR);

            proj->construct_flag = TRUE;

            gimp_projection_construct_legacy (proj, FALSE, x, y, w, h);

            return;
        }
    }
#endif

    /*  First, determine if the projection image needs to be
     *  initialized--this is the case when there are no visible
     *  layers that cover the entire canvas--either because layers
     *  are offset or only a floating selection is visible
     */
    gimp_projection_initialize (proj, x, y, w, h);

    /*  call functions which process the list of layers and
     *  the list of channels
     */
    if (proj->use_gegl)
    {
        gimp_projection_construct_gegl (proj, x, y, w, h);
    }
    else
    {
        proj->construct_flag = FALSE;

        gimp_projection_construct_legacy (proj, TRUE, x, y, w, h);
    }
}
コード例 #4
0
static void
gimp_projection_construct_legacy (GimpProjection *proj,
                                  gboolean        with_layers,
                                  gint            x,
                                  gint            y,
                                  gint            w,
                                  gint            h)
{
    GList *list;
    GList *reverse_list = NULL;
    gint   proj_off_x;
    gint   proj_off_y;

    for (list = gimp_projectable_get_channels (proj->projectable);
            list;
            list = g_list_next (list))
    {
        if (gimp_item_get_visible (GIMP_ITEM (list->data)))
        {
            reverse_list = g_list_prepend (reverse_list, list->data);
        }
    }

    if (with_layers)
    {
        for (list = gimp_projectable_get_layers (proj->projectable);
                list;
                list = g_list_next (list))
        {
            GimpLayer *layer = list->data;

            if (! gimp_layer_is_floating_sel (layer) &&
                    gimp_item_get_visible (GIMP_ITEM (layer)))
            {
                /*  only add layers that are visible and not floating selections
                 *  to the list
                 */
                reverse_list = g_list_prepend (reverse_list, layer);
            }
        }
    }

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

    for (list = reverse_list; list; list = g_list_next (list))
    {
        GimpItem    *item = list->data;
        PixelRegion  projPR;
        gint         x1, y1;
        gint         x2, y2;
        gint         off_x;
        gint         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;

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

        pixel_region_init (&projPR,
                           gimp_pickable_get_tiles (GIMP_PICKABLE (proj)),
                           x1, y1, x2 - x1, y2 - y1,
                           TRUE);

        gimp_drawable_project_region (GIMP_DRAWABLE (item),
                                      x1 - off_x, y1 - off_y,
                                      x2 - x1,    y2 - y1,
                                      &projPR,
                                      proj->construct_flag);

        proj->construct_flag = TRUE;  /*  something was projected  */
    }

    g_list_free (reverse_list);
}
コード例 #5
0
ファイル: gimpsourcecore.c プロジェクト: jdburton/gimp-osx
static gboolean
gimp_source_core_real_get_source (GimpSourceCore   *source_core,
                                  GimpDrawable     *drawable,
                                  GimpPaintOptions *paint_options,
                                  GimpPickable     *src_pickable,
                                  gint              src_offset_x,
                                  gint              src_offset_y,
                                  TempBuf          *paint_area,
                                  gint             *paint_area_offset_x,
                                  gint             *paint_area_offset_y,
                                  gint             *paint_area_width,
                                  gint             *paint_area_height,
                                  PixelRegion      *srcPR)
{
  GimpSourceOptions *options   = GIMP_SOURCE_OPTIONS (paint_options);
  GimpImage         *image     = gimp_item_get_image (GIMP_ITEM (drawable));
  GimpImage         *src_image = gimp_pickable_get_image (src_pickable);
  TileManager       *src_tiles = gimp_pickable_get_tiles (src_pickable);
  gint               x1, y1;
  gint               x2, y2;

  x1 = CLAMP (paint_area->x + src_offset_x,
              0, tile_manager_width  (src_tiles));
  y1 = CLAMP (paint_area->y + src_offset_y,
              0, tile_manager_height (src_tiles));
  x2 = CLAMP (paint_area->x + src_offset_x + paint_area->width,
              0, tile_manager_width  (src_tiles));
  y2 = CLAMP (paint_area->y + src_offset_y + paint_area->height,
              0, tile_manager_height (src_tiles));

  if (!(x2 - x1) || !(y2 - y1))
    return FALSE;

  /*  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
   */
  if ((  options->sample_merged && (src_image                 != image)) ||
      (! options->sample_merged && (source_core->src_drawable != drawable)))
    {
      pixel_region_init (srcPR, src_tiles,
                         x1, y1, x2 - x1, y2 - y1, FALSE);
    }
  else
    {
      TempBuf *orig;

      /*  get the original image  */
      if (options->sample_merged)
        orig = gimp_paint_core_get_orig_proj (GIMP_PAINT_CORE (source_core),
                                              src_pickable,
                                              x1, y1, x2, y2);
      else
        orig = gimp_paint_core_get_orig_image (GIMP_PAINT_CORE (source_core),
                                               GIMP_DRAWABLE (src_pickable),
                                               x1, y1, x2, y2);

      pixel_region_init_temp_buf (srcPR, orig,
                                  0, 0, x2 - x1, y2 - y1);
    }

  *paint_area_offset_x = x1 - (paint_area->x + src_offset_x);
  *paint_area_offset_y = y1 - (paint_area->y + src_offset_y);
  *paint_area_width    = x2 - x1;
  *paint_area_height   = y2 - y1;

  return TRUE;
}
コード例 #6
0
ファイル: gimpimage-crop.c プロジェクト: jdburton/gimp-osx
gboolean
gimp_image_crop_auto_shrink (GimpImage *image,
                             gint       x1,
                             gint       y1,
                             gint       x2,
                             gint       y2,
                             gboolean   active_drawable_only,
                             gint      *shrunk_x1,
                             gint      *shrunk_y1,
                             gint      *shrunk_x2,
                             gint      *shrunk_y2)
{
  GimpDrawable    *active_drawable = NULL;
  GimpPickable    *pickable;
  ColorsEqualFunc  colors_equal_func;
  guchar           bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 };
  gboolean         has_alpha;
  PixelRegion      PR;
  guchar          *buffer = NULL;
  gint             width, height;
  GimpImageType    type;
  gint             bytes;
  gint             x, y, abort;
  gboolean         retval = FALSE;

  g_return_val_if_fail (image != NULL, FALSE);
  g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
  g_return_val_if_fail (shrunk_x1 != NULL, FALSE);
  g_return_val_if_fail (shrunk_y1 != NULL, FALSE);
  g_return_val_if_fail (shrunk_x2 != NULL, FALSE);
  g_return_val_if_fail (shrunk_y2 != NULL, FALSE);

  gimp_set_busy (image->gimp);

  /* You should always keep in mind that crop->tx2 and crop->ty2 are
     the NOT the coordinates of the bottomright corner of the area to
     be cropped. They point at the pixel located one to the right and
     one to the bottom.
   */

  if (active_drawable_only)
    {
      active_drawable = gimp_image_get_active_drawable (image);

      if (! active_drawable)
        goto FINISH;

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

  gimp_pickable_flush (pickable);

  type      = gimp_pickable_get_image_type (pickable);
  bytes     = GIMP_IMAGE_TYPE_BYTES (type);
  has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (type);

  switch (gimp_image_crop_guess_bgcolor (pickable,
                                         bytes, has_alpha, bgcolor,
                                         x1, x2-1, y1, y2-1))
    {
    case AUTO_CROP_ALPHA:
      colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_alpha;
      break;
    case AUTO_CROP_COLOR:
      colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_equal;
      break;
    default:
      goto FINISH;
      break;
    }

  width  = x2 - x1;
  height = y2 - y1;

  pixel_region_init (&PR, gimp_pickable_get_tiles (pickable),
                     x1, y1, width, height, FALSE);

  /* The following could be optimized further by processing
   * the smaller side first instead of defaulting to width    --Sven
   */

  buffer = g_malloc ((width > height ? width : height) * bytes);

  /* Check how many of the top lines are uniform/transparent. */
  abort = FALSE;
  for (y = y1; y < y2 && !abort; y++)
    {
      pixel_region_get_row (&PR, x1, y, width, buffer, 1);
      for (x = 0; x < width && !abort; x++)
        abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes);
    }
  if (y == y2 && !abort)
    goto FINISH;
  y1 = y - 1;

  /* Check how many of the bottom lines are uniform/transparent. */
  abort = FALSE;
  for (y = y2; y > y1 && !abort; y--)
    {
      pixel_region_get_row (&PR, x1, y-1 , width, buffer, 1);
      for (x = 0; x < width && !abort; x++)
        abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes);
    }
  y2 = y + 1;

  /* compute a new height for the next operations */
  height = y2 - y1;

  /* Check how many of the left lines are uniform/transparent. */
  abort = FALSE;
  for (x = x1; x < x2 && !abort; x++)
    {
      pixel_region_get_col (&PR, x, y1, height, buffer, 1);
      for (y = 0; y < height && !abort; y++)
        abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes);
    }
  x1 = x - 1;

  /* Check how many of the right lines are uniform/transparent. */
  abort = FALSE;
  for (x = x2; x > x1 && !abort; x--)
    {
      pixel_region_get_col (&PR, x-1, y1, height, buffer, 1);
      for (y = 0; y < height && !abort; y++)
        abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes);
    }
  x2 = x + 1;

  *shrunk_x1 = x1;
  *shrunk_y1 = y1;
  *shrunk_x2 = x2;
  *shrunk_y2 = y2;

  retval = TRUE;

 FINISH:
  g_free (buffer);
  gimp_unset_busy (image->gimp);

  return retval;
}
コード例 #7
0
TempBuf *
gimp_paint_core_get_orig_proj (GimpPaintCore *core,
                               GimpPickable  *pickable,
                               gint           x,
                               gint           y,
                               gint           width,
                               gint           height)
{
  TileManager  *src_tiles;
  PixelRegion   srcPR;
  PixelRegion   destPR;
  Tile         *saved_tile;
  gboolean      release_tile;
  gint          h;
  gint          pixelwidth;
  gint          pickable_width;
  gint          pickable_height;
  const guchar *s;
  guchar       *d;
  gpointer      pr;

  g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), NULL);
  g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
  g_return_val_if_fail (core->saved_proj_tiles != NULL, NULL);

  core->orig_proj_buf = temp_buf_resize (core->orig_proj_buf,
                                         gimp_pickable_get_bytes (pickable),
                                         x, y, width, height);

  src_tiles = gimp_pickable_get_tiles (pickable);

  pickable_width  = tile_manager_width  (src_tiles);
  pickable_height = tile_manager_height (src_tiles);

  gimp_rectangle_intersect (x, y,
                            width, height,
                            0, 0,
                            pickable_width, pickable_height,
                            &x, &y,
                            &width, &height);

  /*  configure the pixel regions  */
  pixel_region_init (&srcPR, src_tiles,
                     x, y, width, height,
                     FALSE);

  pixel_region_init_temp_buf (&destPR, core->orig_proj_buf,
                              x - core->orig_proj_buf->x,
                              y - core->orig_proj_buf->y,
                              width, height);

  for (pr = pixel_regions_register (2, &srcPR, &destPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
    {
      /*  If the saved tile corresponding to this location is valid, use it  */
      saved_tile = tile_manager_get_tile (core->saved_proj_tiles,
                                          srcPR.x, srcPR.y,
                                          FALSE, FALSE);

      if (tile_is_valid (saved_tile))
        {
          release_tile = TRUE;

          saved_tile = tile_manager_get_tile (core->saved_proj_tiles,
                                              srcPR.x, srcPR.y,
                                              TRUE, FALSE);
          s = tile_data_pointer (saved_tile, srcPR.x, srcPR.y);
        }
      else
        {
          release_tile = FALSE;

          s = srcPR.data;
        }

      d = destPR.data;

      pixelwidth = srcPR.w * srcPR.bytes;

      h = srcPR.h;
      while (h --)
        {
          memcpy (d, s, pixelwidth);

          s += srcPR.rowstride;
          d += destPR.rowstride;
        }

      if (release_tile)
        tile_release (saved_tile, FALSE);
    }

  return core->orig_proj_buf;
}
コード例 #8
0
gboolean
gimp_paint_core_start (GimpPaintCore     *core,
                       GimpDrawable      *drawable,
                       GimpPaintOptions  *paint_options,
                       const GimpCoords  *coords,
                       GError           **error)
{
  GimpItem *item;

  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);

  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);

  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_tiles)
    tile_manager_unref (core->undo_tiles);

  core->undo_tiles = tile_manager_new (gimp_item_get_width  (item),
                                       gimp_item_get_height (item),
                                       gimp_drawable_bytes (drawable));

  /*  Allocate the saved proj structure  */
  if (core->saved_proj_tiles)
    tile_manager_unref (core->saved_proj_tiles);

  core->saved_proj_tiles = NULL;

  if (core->use_saved_proj)
    {
      GimpImage    *image    = gimp_item_get_image (item);
      GimpPickable *pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
      TileManager  *tiles    = gimp_pickable_get_tiles (pickable);

      core->saved_proj_tiles = tile_manager_new (tile_manager_width (tiles),
                                                 tile_manager_height (tiles),
                                                 tile_manager_bpp (tiles));
    }

  /*  Allocate the canvas blocks structure  */
  if (core->canvas_tiles)
    tile_manager_unref (core->canvas_tiles);

  core->canvas_tiles = tile_manager_new (gimp_item_get_width  (item),
                                         gimp_item_get_height (item),
                                         1);

  /*  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;

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

  return TRUE;
}
コード例 #9
0
static gboolean
gimp_perspective_clone_get_source (GimpSourceCore   *source_core,
                                   GimpDrawable     *drawable,
                                   GimpPaintOptions *paint_options,
                                   GimpPickable     *src_pickable,
                                   gint              src_offset_x,
                                   gint              src_offset_y,
                                   TempBuf          *paint_area,
                                   gint             *paint_area_offset_x,
                                   gint             *paint_area_offset_y,
                                   gint             *paint_area_width,
                                   gint             *paint_area_height,
                                   PixelRegion      *srcPR)
{
  GimpPerspectiveClone *clone      = GIMP_PERSPECTIVE_CLONE (source_core);
  GimpPaintCore        *paint_core = GIMP_PAINT_CORE (source_core);
  GimpSourceOptions    *options    = GIMP_SOURCE_OPTIONS (paint_options);
  GimpImage            *src_image;
  GimpImage            *image;
  GimpImageType         src_type;
  gint                  x1d, y1d, x2d, y2d;
  gdouble               x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s;
  gint                  xmin, ymin, xmax, ymax;
  TileManager          *src_tiles;
  TileManager          *orig_tiles;
  PixelRegion           origPR;
  PixelRegion           destPR;
  GimpMatrix3           matrix;
  gint                  bytes;

  src_image = gimp_pickable_get_image (src_pickable);
  image     = gimp_item_get_image (GIMP_ITEM (drawable));

  src_type  = gimp_pickable_get_image_type (src_pickable);
  src_tiles = gimp_pickable_get_tiles (src_pickable);

  /* Destination coordinates that will be painted */
  x1d = paint_area->x;
  y1d = paint_area->y;
  x2d = paint_area->x + paint_area->width;
  y2d = paint_area->y + paint_area->height;

  /* Boundary box for source pixels to copy: Convert all the vertex of
   * the box to paint in destination area to its correspondent in
   * source area bearing in mind perspective
   */
  gimp_perspective_clone_get_source_point (clone, x1d, y1d, &x1s, &y1s);
  gimp_perspective_clone_get_source_point (clone, x1d, y2d, &x2s, &y2s);
  gimp_perspective_clone_get_source_point (clone, x2d, y1d, &x3s, &y3s);
  gimp_perspective_clone_get_source_point (clone, x2d, y2d, &x4s, &y4s);

  xmin = floor (MIN4 (x1s, x2s, x3s, x4s));
  ymin = floor (MIN4 (y1s, y2s, y3s, y4s));
  xmax = ceil  (MAX4 (x1s, x2s, x3s, x4s));
  ymax = ceil  (MAX4 (y1s, y2s, y3s, y4s));

  xmin = CLAMP (xmin - 1, 0, tile_manager_width  (src_tiles));
  ymin = CLAMP (ymin - 1, 0, tile_manager_height (src_tiles));
  xmax = CLAMP (xmax + 1, 0, tile_manager_width  (src_tiles));
  ymax = CLAMP (ymax + 1, 0, tile_manager_height (src_tiles));

  /* if the source area is completely out of the image */
  if (!(xmax - xmin) || !(ymax - ymin))
    return FALSE;

  /*  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
   */
  if ((  options->sample_merged && (src_image                 != image)) ||
      (! options->sample_merged && (source_core->src_drawable != drawable)))
    {
      pixel_region_init (&origPR, src_tiles,
                         xmin, ymin, xmax - xmin, ymax - ymin, FALSE);
    }
  else
    {
      TempBuf *orig;

      /*  get the original image  */
      if (options->sample_merged)
        orig = gimp_paint_core_get_orig_proj (paint_core,
                                              src_pickable,
                                              xmin, ymin, xmax, ymax);
      else
        orig = gimp_paint_core_get_orig_image (paint_core,
                                               GIMP_DRAWABLE (src_pickable),
                                               xmin, ymin, xmax, ymax);

      pixel_region_init_temp_buf (&origPR, orig,
                                  0, 0, xmax - xmin, ymax - ymin);
    }

  /*  copy the original image to a tile manager, adding alpha if needed  */

  bytes = GIMP_IMAGE_TYPE_BYTES (GIMP_IMAGE_TYPE_WITH_ALPHA (src_type));

  orig_tiles = tile_manager_new (xmax - xmin, ymax - ymin, bytes);

  tile_manager_set_offsets (orig_tiles, xmin, ymin);

  pixel_region_init (&destPR, orig_tiles,
                     0, 0, xmax - xmin, ymax - ymin,
                     TRUE);

  if (bytes > origPR.bytes)
    add_alpha_region (&origPR, &destPR);
  else
    copy_region (&origPR, &destPR);

  clone->src_area = temp_buf_resize (clone->src_area,
                                     tile_manager_bpp (orig_tiles),
                                     0, 0,
                                     x2d - x1d, y2d - y1d);

  pixel_region_init_temp_buf (&destPR, clone->src_area,
                              0, 0,
                              x2d - x1d, y2d - y1d);

  gimp_perspective_clone_get_matrix (clone, &matrix);

  gimp_transform_region (src_pickable,
                         GIMP_CONTEXT (paint_options),
                         orig_tiles,
                         &destPR,
                         x1d, y1d, x2d, y2d,
                         &matrix,
                         GIMP_INTERPOLATION_LINEAR, 0, NULL);

  tile_manager_unref (orig_tiles);

  pixel_region_init_temp_buf (srcPR, clone->src_area,
                              0, 0, x2d - x1d, y2d - y1d);

  return TRUE;
}