Beispiel #1
0
void
gimp_channel_combine_mask (GimpChannel    *mask,
                           GimpChannel    *add_on,
                           GimpChannelOps  op,
                           gint            off_x,
                           gint            off_y)
{
  PixelRegion srcPR, destPR;
  gint        x, y, w, h;

  g_return_if_fail (GIMP_IS_CHANNEL (mask));
  g_return_if_fail (GIMP_IS_CHANNEL (add_on));

  if (! gimp_rectangle_intersect (off_x, off_y,
                                  gimp_item_get_width  (GIMP_ITEM (add_on)),
                                  gimp_item_get_height (GIMP_ITEM (add_on)),
                                  0, 0,
                                  gimp_item_get_width  (GIMP_ITEM (mask)),
                                  gimp_item_get_height (GIMP_ITEM (mask)),
                                  &x, &y, &w, &h))
    return;

  pixel_region_init (&srcPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (add_on)),
                     x - off_x, y - off_y, w, h, FALSE);
  pixel_region_init (&destPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                     x, y, w, h, TRUE);

  switch (op)
    {
    case GIMP_CHANNEL_OP_ADD:
    case GIMP_CHANNEL_OP_REPLACE:
      pixel_regions_process_parallel ((PixelProcessorFunc)
                                      gimp_channel_combine_sub_region_add,
                                      NULL, 2, &srcPR, &destPR);
      break;

    case GIMP_CHANNEL_OP_SUBTRACT:
      pixel_regions_process_parallel ((PixelProcessorFunc)
                                      gimp_channel_combine_sub_region_sub,
                                      NULL, 2, &srcPR, &destPR);
      break;

    case GIMP_CHANNEL_OP_INTERSECT:
      pixel_regions_process_parallel ((PixelProcessorFunc)
                                      gimp_channel_combine_sub_region_intersect,
                                      NULL, 2, &srcPR, &destPR);
      break;

    default:
      g_warning ("%s: unknown operation type", G_STRFUNC);
      break;
    }

  mask->bounds_known = FALSE;

  gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h);
}
void
gimp_paint_core_validate_undo_tiles (GimpPaintCore *core,
                                     GimpDrawable  *drawable,
                                     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_DRAWABLE (drawable));
  g_return_if_fail (core->undo_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->undo_tiles,
                                                   j, i, FALSE, FALSE);

          if (! tile_is_valid (dest_tile))
            {
              Tile *src_tile =
                tile_manager_get_tile (gimp_drawable_get_tiles (drawable),
                                       j, i, TRUE, FALSE);
              tile_manager_map_tile (core->undo_tiles, j, i, src_tile);
              tile_release (src_tile, FALSE);
            }
        }
    }
}
static TempBuf *
gimp_drawable_indexed_preview (GimpDrawable *drawable,
                               const guchar *cmap,
                               gint          src_x,
                               gint          src_y,
                               gint          src_width,
                               gint          src_height,
                               gint          dest_width,
                               gint          dest_height)
{
  TempBuf     *preview_buf;
  PixelRegion  srcPR;
  PixelRegion  destPR;
  gint         bytes     = gimp_drawable_preview_bytes (drawable);
  gint         subsample = 1;

  /*  calculate 'acceptable' subsample  */
  while ((dest_width  * (subsample + 1) * 2 < src_width) &&
         (dest_height * (subsample + 1) * 2 < src_width))
    subsample += 1;

  pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable),
                     src_x, src_y, src_width, src_height,
                     FALSE);

  preview_buf = temp_buf_new (dest_width, dest_height, bytes, 0, 0, NULL);

  pixel_region_init_temp_buf (&destPR, preview_buf,
                              0, 0, dest_width, dest_height);

  subsample_indexed_region (&srcPR, &destPR, cmap, subsample);

  return preview_buf;
}
Beispiel #4
0
static void
gimp_foreground_select_tool_stroke (GimpChannel    *mask,
                                    FgSelectStroke *stroke)
{
  GimpScanConvert *scan_convert = gimp_scan_convert_new ();

  if (stroke->num_points == 1)
    {
      GimpVector2 points[2];

      points[0] = points[1] = stroke->points[0];

      points[1].x += 0.01;
      points[1].y += 0.01;

      gimp_scan_convert_add_polyline (scan_convert, 2, points, FALSE);
    }
  else
    {
      gimp_scan_convert_add_polyline (scan_convert,
                                      stroke->num_points, stroke->points,
                                      FALSE);
    }

  gimp_scan_convert_stroke (scan_convert,
                            stroke->width,
                            GIMP_JOIN_ROUND, GIMP_CAP_ROUND, 10.0,
                            0.0, NULL);
  gimp_scan_convert_compose_value (scan_convert,
                             gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                             0, 0, stroke->background ? 0 : 255);
  gimp_scan_convert_free (scan_convert);
}
static GdkSegment *
gimp_region_select_tool_calculate (GimpRegionSelectTool *region_sel,
                                   GimpDisplay          *display,
                                   gint                 *num_segs)
{
  GimpTool                *tool    = GIMP_TOOL (region_sel);
  GimpRegionSelectOptions *options = GIMP_REGION_SELECT_TOOL_GET_OPTIONS (tool);
  GimpDisplayShell        *shell   = GIMP_DISPLAY_SHELL (display->shell);
  GimpDrawable            *drawable;
  GdkSegment              *segs;
  BoundSeg                *bsegs;
  PixelRegion              maskPR;

  drawable = gimp_image_get_active_drawable (display->image);

  gimp_display_shell_set_override_cursor (shell, GDK_WATCH);

  if (region_sel->region_mask)
    g_object_unref (region_sel->region_mask);

  region_sel->region_mask =
    GIMP_REGION_SELECT_TOOL_GET_CLASS (region_sel)->get_mask (region_sel,
                                                              display);

  if (! region_sel->region_mask)
    {
      gimp_display_shell_unset_override_cursor (shell);

      *num_segs = 0;
      return NULL;
    }

  /*  calculate and allocate a new segment array which represents the
   *  boundary of the contiguous region
   */
  pixel_region_init (&maskPR,
                     gimp_drawable_get_tiles (GIMP_DRAWABLE (region_sel->region_mask)),
                     0, 0,
                     gimp_item_width  (GIMP_ITEM (region_sel->region_mask)),
                     gimp_item_height (GIMP_ITEM (region_sel->region_mask)),
                     FALSE);

  bsegs = boundary_find (&maskPR, BOUNDARY_WITHIN_BOUNDS,
                         0, 0,
                         gimp_item_width  (GIMP_ITEM (region_sel->region_mask)),
                         gimp_item_height (GIMP_ITEM (region_sel->region_mask)),
                         BOUNDARY_HALF_WAY,
                         num_segs);

  segs = g_new (GdkSegment, *num_segs);

  gimp_display_shell_transform_segments (shell, bsegs, segs, *num_segs,
                                         ! options->sample_merged);
  g_free (bsegs);

  gimp_display_shell_unset_override_cursor (shell);

  return segs;
}
Beispiel #6
0
static void
gimp_drawable_apply_operation_private (GimpDrawable       *drawable,
                                       GimpProgress       *progress,
                                       const gchar        *undo_desc,
                                       GeglNode           *operation,
                                       gboolean            linear,
                                       TileManager         *dest_tiles,
                                       const GeglRectangle *rect)
{
  GeglNode      *gegl;
  GeglNode      *input;
  GeglNode      *output;
  GeglProcessor *processor;
  gdouble        value;

  gegl = gegl_node_new ();

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

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

  gegl_node_add_child (gegl, operation);

  gegl_node_link_many (input, operation, output, NULL);

  processor = gegl_node_new_processor (output, rect);

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

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

  g_object_unref (processor);

  g_object_unref (gegl);
}
void
floating_sel_restore (GimpLayer *layer,
                      gint       x,
                      gint       y,
                      gint       w,
                      gint       h)
{
  PixelRegion srcPR, destPR;
  gint        offx, offy;
  gint        x1, y1, x2, y2;

  g_return_if_fail (GIMP_IS_LAYER (layer));
  g_return_if_fail (gimp_layer_is_floating_sel (layer));

  /*  What this function does is "uncover" the specified area in the
   *  drawable that this floating selection obscures.  We do this so
   *  that either the floating selection can be removed or it can be
   *  translated
   */

  /*  Find the minimum area we need to uncover -- in image space  */
  gimp_item_offsets (GIMP_ITEM (layer->fs.drawable), &offx, &offy);

  x1 = MAX (GIMP_ITEM (layer)->offset_x, offx);
  y1 = MAX (GIMP_ITEM (layer)->offset_y, offy);
  x2 = MIN (GIMP_ITEM (layer)->offset_x + GIMP_ITEM (layer)->width,
            offx + gimp_item_width  (GIMP_ITEM (layer->fs.drawable)));
  y2 = MIN (GIMP_ITEM(layer)->offset_y + GIMP_ITEM (layer)->height,
            offy + gimp_item_height (GIMP_ITEM (layer->fs.drawable)));

  x1 = CLAMP (x, x1, x2);
  y1 = CLAMP (y, y1, y2);
  x2 = CLAMP (x + w, x1, x2);
  y2 = CLAMP (y + h, y1, y2);

  if ((x2 - x1) > 0 && (y2 - y1) > 0)
    {
      /*  Copy the area from the backing store to the drawable  */
      pixel_region_init (&srcPR, layer->fs.backing_store,
                         (x1 - GIMP_ITEM (layer)->offset_x),
                         (y1 - GIMP_ITEM (layer)->offset_y),
                         (x2 - x1), (y2 - y1), FALSE);
      pixel_region_init (&destPR, gimp_drawable_get_tiles (layer->fs.drawable),
                         (x1 - offx), (y1 - offy), (x2 - x1), (y2 - y1), TRUE);

      copy_region (&srcPR, &destPR);
    }
}
static BoundSeg *
gimp_region_select_tool_calculate (GimpRegionSelectTool *region_sel,
                                   GimpDisplay          *display,
                                   gint                 *n_segs)
{
  GimpDisplayShell *shell = gimp_display_get_shell (display);
  BoundSeg         *segs;
  PixelRegion       maskPR;

  gimp_display_shell_set_override_cursor (shell, GDK_WATCH);

  if (region_sel->region_mask)
    g_object_unref (region_sel->region_mask);

  region_sel->region_mask =
    GIMP_REGION_SELECT_TOOL_GET_CLASS (region_sel)->get_mask (region_sel,
                                                              display);

  if (! region_sel->region_mask)
    {
      gimp_display_shell_unset_override_cursor (shell);

      *n_segs = 0;
      return NULL;
    }

  /*  calculate and allocate a new segment array which represents the
   *  boundary of the contiguous region
   */
  pixel_region_init (&maskPR,
                     gimp_drawable_get_tiles (GIMP_DRAWABLE (region_sel->region_mask)),
                     0, 0,
                     gimp_item_get_width  (GIMP_ITEM (region_sel->region_mask)),
                     gimp_item_get_height (GIMP_ITEM (region_sel->region_mask)),
                     FALSE);

  segs = boundary_find (&maskPR, BOUNDARY_WITHIN_BOUNDS,
                        0, 0,
                        gimp_item_get_width  (GIMP_ITEM (region_sel->region_mask)),
                        gimp_item_get_height (GIMP_ITEM (region_sel->region_mask)),
                        BOUNDARY_HALF_WAY,
                        n_segs);

  gimp_display_shell_unset_override_cursor (shell);

  return segs;
}
Beispiel #9
0
void
gimp_channel_select_scan_convert (GimpChannel     *channel,
                                  const gchar     *undo_desc,
                                  GimpScanConvert *scan_convert,
                                  gint             offset_x,
                                  gint             offset_y,
                                  GimpChannelOps   op,
                                  gboolean         antialias,
                                  gboolean         feather,
                                  gdouble          feather_radius_x,
                                  gdouble          feather_radius_y,
                                  gboolean         push_undo)
{
  GimpItem    *item;
  GimpChannel *add_on;

  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
  g_return_if_fail (undo_desc != NULL);
  g_return_if_fail (scan_convert != NULL);

  if (push_undo)
    gimp_channel_push_undo (channel, undo_desc);

  /*  if applicable, replace the current selection  */
  if (op == GIMP_CHANNEL_OP_REPLACE)
    gimp_channel_clear (channel, NULL, FALSE);

  item = GIMP_ITEM (channel);

  add_on = gimp_channel_new_mask (gimp_item_get_image (item),
                                  gimp_item_width (item),
                                  gimp_item_height (item));
  gimp_scan_convert_render (scan_convert,
                            gimp_drawable_get_tiles (GIMP_DRAWABLE (add_on)),
                            offset_x, offset_y, antialias);

  if (feather)
    gimp_channel_feather (add_on,
                          feather_radius_x,
                          feather_radius_y,
                          FALSE /* no undo */);

  gimp_channel_combine_mask (channel, add_on, op, 0, 0);
  g_object_unref (add_on);
}
void
gimp_paint_core_cancel (GimpPaintCore *core,
                        GimpDrawable  *drawable)
{
  gint x, y;
  gint width, height;

  g_return_if_fail (GIMP_IS_PAINT_CORE (core));
  g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)));

  /*  Determine if any part of the image has been altered--
   *  if nothing has, then just return...
   */
  if ((core->x2 == core->x1) || (core->y2 == core->y1))
    return;

  if (gimp_rectangle_intersect (core->x1, core->y1,
                                core->x2 - core->x1,
                                core->y2 - core->y1,
                                0, 0,
                                gimp_item_get_width  (GIMP_ITEM (drawable)),
                                gimp_item_get_height (GIMP_ITEM (drawable)),
                                &x, &y, &width, &height))
    {
      gimp_paint_core_copy_valid_tiles (core->undo_tiles,
                                        gimp_drawable_get_tiles (drawable),
                                        x, y, width, height);
    }

  tile_manager_unref (core->undo_tiles);
  core->undo_tiles = NULL;

  if (core->saved_proj_tiles)
    {
      tile_manager_unref (core->saved_proj_tiles);
      core->saved_proj_tiles = NULL;
    }

  gimp_drawable_update (drawable, x, y, width, height);

  gimp_viewable_preview_thaw (GIMP_VIEWABLE (drawable));
}
Beispiel #11
0
TempBuf *
gimp_drawable_get_sub_preview (GimpDrawable *drawable,
                               gint          src_x,
                               gint          src_y,
                               gint          src_width,
                               gint          src_height,
                               gint          dest_width,
                               gint          dest_height)
{
  GimpItem    *item;
  GimpImage   *image;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (src_x >= 0, NULL);
  g_return_val_if_fail (src_y >= 0, NULL);
  g_return_val_if_fail (src_width  > 0, NULL);
  g_return_val_if_fail (src_height > 0, NULL);
  g_return_val_if_fail (dest_width  > 0, NULL);
  g_return_val_if_fail (dest_height > 0, NULL);

  item = GIMP_ITEM (drawable);

  g_return_val_if_fail ((src_x + src_width)  <= gimp_item_width  (item), NULL);
  g_return_val_if_fail ((src_y + src_height) <= gimp_item_height (item), NULL);

  image = gimp_item_get_image (item);

  if (! image->gimp->config->layer_previews)
    return NULL;

  if (GIMP_IMAGE_TYPE_BASE_TYPE (gimp_drawable_type (drawable)) == GIMP_INDEXED)
    return gimp_drawable_indexed_preview (drawable,
                                          gimp_image_get_colormap (image),
                                          src_x, src_y, src_width, src_height,
                                          dest_width, dest_height);

  return tile_manager_get_sub_preview (gimp_drawable_get_tiles (drawable),
                                       src_x, src_y, src_width, src_height,
                                       dest_width, dest_height);
}
Beispiel #12
0
/*  Similar to gimp_drawable_apply_region but works in "replace" mode (i.e.
 *  transparent pixels in src2 make the result transparent rather than
 *  opaque.
 *
 * Takes an additional mask pixel region as well.
 */
void
gimp_drawable_real_replace_region (GimpDrawable *drawable,
                                   PixelRegion  *src2PR,
                                   gboolean      push_undo,
                                   const gchar  *undo_desc,
                                   gdouble       opacity,
                                   PixelRegion  *maskPR,
                                   gint          x,
                                   gint          y)
{
  GimpItem        *item  = GIMP_ITEM (drawable);
  GimpImage       *image = gimp_item_get_image (item);
  GimpChannel     *mask  = gimp_image_get_mask (image);
  gint             x1, y1, x2, y2;
  gint             offset_x, offset_y;
  PixelRegion      src1PR, destPR;
  CombinationMode  operation;
  gboolean         active_components[MAX_CHANNELS];

  /*  don't apply the mask to itself and don't apply an empty mask  */
  if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask))
    mask = NULL;

  /*  configure the active channel array  */
  gimp_drawable_get_active_components (drawable, active_components);

  /*  determine what sort of operation is being attempted and
   *  if it's actually legal...
   */
  operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable),
                                               src2PR->bytes);
  if (operation == -1)
    {
      g_warning ("%s: illegal parameters.", G_STRFUNC);
      return;
    }

  /*  get the layer offsets  */
  gimp_item_get_offset (item, &offset_x, &offset_y);

  /*  make sure the image application coordinates are within drawable bounds  */
  x1 = CLAMP (x, 0,             gimp_item_get_width  (item));
  y1 = CLAMP (y, 0,             gimp_item_get_height (item));
  x2 = CLAMP (x + src2PR->w, 0, gimp_item_get_width  (item));
  y2 = CLAMP (y + src2PR->h, 0, gimp_item_get_height (item));

  if (mask)
    {
      GimpItem *mask_item = GIMP_ITEM (mask);

      /*  make sure coordinates are in mask bounds ...
       *  we need to add the layer offset to transform coords
       *  into the mask coordinate system
       */
      x1 = CLAMP (x1, -offset_x, gimp_item_get_width  (mask_item) - offset_x);
      y1 = CLAMP (y1, -offset_y, gimp_item_get_height (mask_item) - offset_y);
      x2 = CLAMP (x2, -offset_x, gimp_item_get_width  (mask_item) - offset_x);
      y2 = CLAMP (y2, -offset_y, gimp_item_get_height (mask_item) - offset_y);
    }

  /*  If the calling procedure specified an undo step...  */
  if (push_undo)
    gimp_drawable_push_undo (drawable, undo_desc,
                             x1, y1,
                             x2 - x1, y2 - y1,
                             NULL, FALSE);

  /* configure the pixel regions */
  pixel_region_init (&src1PR, gimp_drawable_get_tiles (drawable),
                     x1, y1, x2 - x1, y2 - y1,
                     FALSE);
  pixel_region_init (&destPR, gimp_drawable_get_tiles (drawable),
                     x1, y1, x2 - x1, y2 - y1,
                     TRUE);
  pixel_region_resize (src2PR,
                       src2PR->x + (x1 - x), src2PR->y + (y1 - y),
                       x2 - x1, y2 - y1);

  if (mask)
    {
      PixelRegion  mask2PR, tempPR;
      guchar      *temp_data;

      pixel_region_init (&mask2PR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                         x1 + offset_x,
                         y1 + offset_y,
                         x2 - x1, y2 - y1,
                         FALSE);

      temp_data = g_malloc ((y2 - y1) * (x2 - x1));

      pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1,
                              0, 0, x2 - x1, y2 - y1);

      copy_region (&mask2PR, &tempPR);

      pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1,
                              0, 0, x2 - x1, y2 - y1);

      apply_mask_to_region (&tempPR, maskPR, OPAQUE_OPACITY);

      pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1,
                              0, 0, x2 - x1, y2 - y1);

      combine_regions_replace (&src1PR, src2PR, &destPR, &tempPR, NULL,
                               opacity * 255.999,
                               active_components,
                               operation);

      g_free (temp_data);
    }
  else
    {
      combine_regions_replace (&src1PR, src2PR, &destPR, maskPR, NULL,
                               opacity * 255.999,
                               active_components,
                               operation);
    }
}
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);
    }
}
Beispiel #14
0
void
gimp_drawable_bucket_fill_full (GimpDrawable        *drawable,
                                GimpBucketFillMode   fill_mode,
                                gint                 paint_mode,
                                gdouble              opacity,
                                gboolean             do_seed_fill,
                                gboolean             fill_transparent,
                                GimpSelectCriterion  fill_criterion,
                                gdouble              threshold,
                                gboolean             sample_merged,
                                gdouble              x,
                                gdouble              y,
                                const GimpRGB       *color,
                                GimpPattern         *pattern)
{
  GimpImage   *image;
  TileManager *buf_tiles;
  PixelRegion  bufPR, maskPR;
  GimpChannel *mask = NULL;
  gint         bytes;
  gint         x1, y1, x2, y2;
  guchar       col[MAX_CHANNELS];
  TempBuf     *pat_buf = NULL;
  gboolean     new_buf = FALSE;
  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_mode != GIMP_PATTERN_BUCKET_FILL ||
                    GIMP_IS_PATTERN (pattern));
  g_return_if_fail (fill_mode == GIMP_PATTERN_BUCKET_FILL ||
                    color != NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

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

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

  if (fill_mode == GIMP_FG_BUCKET_FILL ||
      fill_mode == GIMP_BG_BUCKET_FILL)
    {
      guchar tmp_col[MAX_CHANNELS];

      gimp_rgb_get_uchar (color,
                          &tmp_col[RED],
                          &tmp_col[GREEN],
                          &tmp_col[BLUE]);

      gimp_image_transform_color (image, gimp_drawable_type (drawable), col,
                                  GIMP_RGB, tmp_col);
      col[gimp_drawable_bytes_with_alpha (drawable) - 1] = OPAQUE_OPACITY;
    }
  else if (fill_mode == GIMP_PATTERN_BUCKET_FILL)
    {
      pat_buf = gimp_image_transform_temp_buf (image,
                                               gimp_drawable_type (drawable),
                                               pattern->mask, &new_buf);
    }
  else
    {
      g_warning ("%s: invalid fill_mode passed", G_STRFUNC);
      return;
    }

  gimp_set_busy (image->gimp);

  /*  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.
   */
  if (do_seed_fill)
    {
      mask = gimp_image_contiguous_region_by_seed (image, drawable,
                                                   sample_merged,
                                                   TRUE,
                                                   (gint) threshold,
                                                   fill_transparent,
                                                   fill_criterion,
                                                   (gint) x,
                                                   (gint) y);

      if (selection)
        {
          gint off_x = 0;
          gint off_y = 0;

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

          gimp_channel_combine_mask (mask, gimp_image_get_mask (image),
                                     GIMP_CHANNEL_OP_INTERSECT,
                                     -off_x, -off_y);
        }

      gimp_channel_bounds (mask, &x1, &y1, &x2, &y2);

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

          item = GIMP_ITEM (drawable);

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

          pixel_region_init (&maskPR,
                             gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                             x1, y1, (x2 - x1), (y2 - y1), TRUE);

          /*  translate mask bounds to drawable coords  */
          x1 -= off_x;
          y1 -= off_y;
          x2 -= off_x;
          y2 -= off_y;
        }
      else
        {
          pixel_region_init (&maskPR,
                             gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                             x1, y1, (x2 - x1), (y2 - y1), TRUE);
        }

      /*  if the image doesn't have an alpha channel, make sure that
       *  the buf_tiles have.  We need the alpha channel to fill with
       *  the region calculated above
       */
      if (! gimp_drawable_has_alpha (drawable))
        bytes++;
    }
  else if (fill_mode == GIMP_PATTERN_BUCKET_FILL &&
           (pat_buf->bytes == 2 || pat_buf->bytes == 4))
    {
      /* If pattern being applied has an alpha channel, add one to the
       * buf_tiles.
       */
      if (! gimp_drawable_has_alpha (drawable))
        bytes++;
    }

  buf_tiles = tile_manager_new ((x2 - x1), (y2 - y1), bytes);
  pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE);

  switch (fill_mode)
    {
    case GIMP_FG_BUCKET_FILL:
    case GIMP_BG_BUCKET_FILL:
      if (mask)
        color_region_mask (&bufPR, &maskPR, col);
      else
        color_region (&bufPR, col);
      break;

    case GIMP_PATTERN_BUCKET_FILL:
      if (mask)
        pattern_region (&bufPR, &maskPR, pat_buf, x1, y1);
      else
        pattern_region (&bufPR, NULL, pat_buf, x1, y1);
      break;
    }

  /*  Apply it to the image  */
  pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), FALSE);
  gimp_drawable_apply_region (drawable, &bufPR,
                              TRUE, C_("undo-type", "Bucket Fill"),
                              opacity, paint_mode,
                              NULL, NULL, x1, y1);
  tile_manager_unref (buf_tiles);

  /*  update the image  */
  gimp_drawable_update (drawable, x1, y1, x2 - x1, y2 - y1);

  /*  free the mask  */
  if (mask)
    g_object_unref (mask);

  if (new_buf)
    temp_buf_free (pat_buf);

  gimp_unset_busy (image->gimp);
}
Beispiel #15
0
static void
gimp_convolve_motion (GimpPaintCore    *paint_core,
                      GimpDrawable     *drawable,
                      GimpPaintOptions *paint_options)
{
  GimpConvolve        *convolve   = GIMP_CONVOLVE (paint_core);
  GimpBrushCore       *brush_core = GIMP_BRUSH_CORE (paint_core);
  GimpConvolveOptions *options    = GIMP_CONVOLVE_OPTIONS (paint_options);
  GimpContext         *context    = GIMP_CONTEXT (paint_options);
  GimpImage           *image;
  TempBuf             *area;
  PixelRegion          srcPR;
  PixelRegion          destPR;
  PixelRegion          tempPR;
  guchar              *buffer;
  gdouble              opacity;
  gdouble              rate;
  gint                 bytes;

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (gimp_drawable_is_indexed (drawable))
    return;

  opacity = gimp_paint_options_get_fade (paint_options, image,
                                         paint_core->pixel_dist);
  if (opacity == 0.0)
    return;

  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
  if (! area)
    return;

  rate = options->rate;

  rate *= gimp_paint_options_get_dynamic_rate (paint_options,
                                               &paint_core->cur_coords);

  gimp_convolve_calculate_matrix (convolve, options->type,
                                  brush_core->brush->mask->width / 2,
                                  brush_core->brush->mask->height / 2,
                                  rate);

  /*  configure the source pixel region  */
  pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable),
                     area->x, area->y, area->width, area->height, FALSE);

  if (gimp_drawable_has_alpha (drawable))
    {
      bytes = srcPR.bytes;

      buffer = g_malloc (area->height * bytes * area->width);

      pixel_region_init_data (&tempPR, buffer,
                              bytes, bytes * area->width,
                              0, 0, area->width, area->height);

      copy_region (&srcPR, &tempPR);
    }
  else
    {
      /* note: this particular approach needlessly convolves the totally-
         opaque alpha channel. A faster approach would be to keep
         tempPR the same number of bytes as srcPR, and extend the
         paint_core_replace_canvas API to handle non-alpha images. */

      bytes = srcPR.bytes + 1;

      buffer = g_malloc (area->height * bytes * area->width);

      pixel_region_init_data (&tempPR, buffer,
                              bytes, bytes * area->width,
                              0, 0, area->width, area->height);

      add_alpha_region (&srcPR, &tempPR);
    }

  /*  Convolve the region  */
  pixel_region_init_data (&tempPR, buffer,
                          bytes, bytes * area->width,
                          0, 0, area->width, area->height);

  pixel_region_init_temp_buf (&destPR, area,
                              0, 0, area->width, area->height);

  convolve_region (&tempPR, &destPR,
                   convolve->matrix, 3, convolve->matrix_divisor,
                   GIMP_NORMAL_CONVOL, TRUE);

  g_free (buffer);

  gimp_brush_core_replace_canvas (brush_core, drawable,
                                  MIN (opacity, GIMP_OPACITY_OPAQUE),
                                  gimp_context_get_opacity (context),
                                  gimp_paint_options_get_brush_mode (paint_options),
                                  1.0,
                                  GIMP_PAINT_INCREMENTAL);
}
const BoundSeg *
floating_sel_boundary (GimpLayer *layer,
                       gint      *n_segs)
{
  PixelRegion bPR;
  gint        i;

  g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL);
  g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL);
  g_return_val_if_fail (n_segs != NULL, NULL);

  if (layer->fs.boundary_known == FALSE)
    {
      gint width, height;
      gint off_x, off_y;

      width  = gimp_item_width  (GIMP_ITEM (layer));
      height = gimp_item_height (GIMP_ITEM (layer));
      gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y);

      if (layer->fs.segs)
        g_free (layer->fs.segs);

      if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
        {
          /*  find the segments  */
          pixel_region_init (&bPR,
                             gimp_drawable_get_tiles (GIMP_DRAWABLE (layer)),
                             0, 0, width, height, FALSE);
          layer->fs.segs = boundary_find (&bPR, BOUNDARY_WITHIN_BOUNDS,
                                          0, 0, width, height,
                                          BOUNDARY_HALF_WAY,
                                          &layer->fs.num_segs);

          /*  offset the segments  */
          for (i = 0; i < layer->fs.num_segs; i++)
            {
              layer->fs.segs[i].x1 += off_x;
              layer->fs.segs[i].y1 += off_y;
              layer->fs.segs[i].x2 += off_x;
              layer->fs.segs[i].y2 += off_y;
            }
        }
      else
        {
          layer->fs.num_segs = 4;
          layer->fs.segs     = g_new0 (BoundSeg, 4);

          /* top */
          layer->fs.segs[0].x1 = off_x;
          layer->fs.segs[0].y1 = off_y;
          layer->fs.segs[0].x2 = off_x + width;
          layer->fs.segs[0].y2 = off_y;

          /* left */
          layer->fs.segs[1].x1 = off_x;
          layer->fs.segs[1].y1 = off_y;
          layer->fs.segs[1].x2 = off_x;
          layer->fs.segs[1].y2 = off_y + height;

          /* right */
          layer->fs.segs[2].x1 = off_x + width;
          layer->fs.segs[2].y1 = off_y;
          layer->fs.segs[2].x2 = off_x + width;
          layer->fs.segs[2].y2 = off_y + height;

          /* bottom */
          layer->fs.segs[3].x1 = off_x;
          layer->fs.segs[3].y1 = off_y + height;
          layer->fs.segs[3].x2 = off_x + width;
          layer->fs.segs[3].y2 = off_y + height;
        }

      layer->fs.boundary_known = TRUE;
    }

  *n_segs = layer->fs.num_segs;

  return layer->fs.segs;
}
Beispiel #17
0
GimpImage *
gimp_image_duplicate (GimpImage *image)
{
  GimpImage    *new_image;
  GimpLayer    *floating_layer;
  GList        *list;
  GimpLayer    *active_layer              = NULL;
  GimpChannel  *active_channel            = NULL;
  GimpVectors  *active_vectors            = NULL;
  GimpDrawable *new_floating_sel_drawable = NULL;
  GimpDrawable *floating_sel_drawable     = NULL;
  gchar        *filename;
  gint          count;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  gimp_set_busy_until_idle (image->gimp);

  /*  Create a new image  */
  new_image = gimp_create_image (image->gimp,
                                 image->width, image->height,
                                 image->base_type,
                                 FALSE);
  gimp_image_undo_disable (new_image);

  /*  Store the folder to be used by the save dialog  */
  filename = gimp_image_get_filename (image);
  if (filename)
    {
      g_object_set_data_full (G_OBJECT (new_image), "gimp-image-dirname",
                              g_path_get_dirname (filename),
                              (GDestroyNotify) g_free);
      g_free (filename);
    }

  /*  Copy the colormap if necessary  */
  if (new_image->base_type == GIMP_INDEXED)
    gimp_image_set_colormap (new_image,
                             gimp_image_get_colormap (image),
                             gimp_image_get_colormap_size (image),
                             FALSE);

  /*  Copy resolution information  */
  new_image->xresolution     = image->xresolution;
  new_image->yresolution     = image->yresolution;
  new_image->resolution_unit = image->resolution_unit;

  /*  Copy floating layer  */
  floating_layer = gimp_image_floating_sel (image);
  if (floating_layer)
    {
      floating_sel_relax (floating_layer, FALSE);

      floating_sel_drawable = floating_layer->fs.drawable;
      floating_layer = NULL;
    }

  /*  Copy the layers  */
  for (list = GIMP_LIST (image->layers)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      GimpLayer *new_layer;

      new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (layer),
                                                 new_image,
                                                 G_TYPE_FROM_INSTANCE (layer),
                                                 FALSE));

      /*  Make sure the copied layer doesn't say: "<old layer> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_layer),
                            gimp_object_get_name (GIMP_OBJECT (layer)));

      /*  Make sure that if the layer has a layer mask,
       *  its name isn't screwed up
       */
      if (new_layer->mask)
        gimp_object_set_name (GIMP_OBJECT (new_layer->mask),
                              gimp_object_get_name (GIMP_OBJECT (layer->mask)));

      if (gimp_image_get_active_layer (image) == layer)
        active_layer = new_layer;

      if (image->floating_sel == layer)
        floating_layer = new_layer;

      if (floating_sel_drawable == GIMP_DRAWABLE (layer))
        new_floating_sel_drawable = GIMP_DRAWABLE (new_layer);

      if (floating_layer != new_layer)
        gimp_image_add_layer (new_image, new_layer, count++);
    }

  /*  Copy the channels  */
  for (list = GIMP_LIST (image->channels)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpChannel *channel = list->data;
      GimpChannel *new_channel;

      new_channel =
        GIMP_CHANNEL (gimp_item_convert (GIMP_ITEM (channel),
                                         new_image,
                                         G_TYPE_FROM_INSTANCE (channel),
                                         FALSE));

      /*  Make sure the copied channel doesn't say: "<old channel> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_channel),
                            gimp_object_get_name (GIMP_OBJECT (channel)));

      if (gimp_image_get_active_channel (image) == channel)
        active_channel = (new_channel);

      if (floating_sel_drawable == GIMP_DRAWABLE (channel))
        new_floating_sel_drawable = GIMP_DRAWABLE (new_channel);

      gimp_image_add_channel (new_image, new_channel, count++);
    }

  /*  Copy any vectors  */
  for (list = GIMP_LIST (image->vectors)->list, count = 0;
       list;
       list = g_list_next (list))
    {
      GimpVectors *vectors = list->data;
      GimpVectors *new_vectors;

      new_vectors =
        GIMP_VECTORS (gimp_item_convert (GIMP_ITEM (vectors),
                                         new_image,
                                         G_TYPE_FROM_INSTANCE (vectors),
                                         FALSE));

      /*  Make sure the copied vectors doesn't say: "<old vectors> copy"  */
      gimp_object_set_name (GIMP_OBJECT (new_vectors),
                            gimp_object_get_name (GIMP_OBJECT (vectors)));

      if (gimp_image_get_active_vectors (image) == vectors)
        active_vectors = new_vectors;

      gimp_image_add_vectors (new_image, new_vectors, count++);
    }

  /*  Copy the selection mask  */
  {
    TileManager *src_tiles;
    TileManager *dest_tiles;
    PixelRegion  srcPR, destPR;

    src_tiles  =
      gimp_drawable_get_tiles (GIMP_DRAWABLE (image->selection_mask));
    dest_tiles =
      gimp_drawable_get_tiles (GIMP_DRAWABLE (new_image->selection_mask));

    pixel_region_init (&srcPR, src_tiles,
                       0, 0, image->width, image->height, FALSE);
    pixel_region_init (&destPR, dest_tiles,
                       0, 0, image->width, image->height, TRUE);

    copy_region (&srcPR, &destPR);

    new_image->selection_mask->bounds_known   = FALSE;
    new_image->selection_mask->boundary_known = FALSE;
  }

  if (floating_layer)
    floating_sel_attach (floating_layer, new_floating_sel_drawable);

  /*  Set active layer, active channel, active vectors  */
  if (active_layer)
    gimp_image_set_active_layer (new_image, active_layer);

  if (active_channel)
    gimp_image_set_active_channel (new_image, active_channel);

  if (active_vectors)
    gimp_image_set_active_vectors (new_image, active_vectors);

  /*  Copy state of all color channels  */
  for (count = 0; count < MAX_CHANNELS; count++)
    {
      new_image->visible[count] = image->visible[count];
      new_image->active[count]  = image->active[count];
    }

  /*  Copy any guides  */
  for (list = image->guides; list; list = g_list_next (list))
    {
      GimpGuide *guide    = list->data;
      gint       position = gimp_guide_get_position (guide);

      switch (gimp_guide_get_orientation (guide))
        {
        case GIMP_ORIENTATION_HORIZONTAL:
          gimp_image_add_hguide (new_image, position, FALSE);
          break;

        case GIMP_ORIENTATION_VERTICAL:
          gimp_image_add_vguide (new_image, position, FALSE);
          break;

        default:
          g_error ("Unknown guide orientation.\n");
        }
    }

  /*  Copy any sample points  */
  for (list = image->sample_points; list; list = g_list_next (list))
    {
      GimpSamplePoint *sample_point = list->data;

      gimp_image_add_sample_point_at_pos (new_image,
                                          sample_point->x,
                                          sample_point->y,
                                          FALSE);
    }

  /*  Copy the grid  */
  if (image->grid)
    gimp_image_set_grid (new_image, image->grid, FALSE);

  /*  Copy the quick mask info  */
  new_image->quick_mask_state    = image->quick_mask_state;
  new_image->quick_mask_inverted = image->quick_mask_inverted;
  new_image->quick_mask_color    = image->quick_mask_color;

  /*  Copy parasites  */
  if (image->parasites)
    {
      g_object_unref (new_image->parasites);
      new_image->parasites = gimp_parasite_list_copy (image->parasites);
    }

  gimp_image_undo_enable (new_image);

  return new_image;
}
static void
gimp_text_layer_render_layout (GimpTextLayer  *layer,
                               GimpTextLayout *layout)
{
  GimpDrawable    *drawable = GIMP_DRAWABLE (layer);
  GimpItem        *item     = GIMP_ITEM (layer);
  GimpImage       *image    = gimp_item_get_image (item);
  cairo_t         *cr;
  cairo_surface_t *surface;
  PixelRegion      layerPR;
  const guchar    *data;
  GimpImageType    layer_type;
  gint             layer_alpha_byte;
  gint             rowstride;
  gint             width;
  gint             height;
  gpointer         pr;

  g_return_if_fail (gimp_drawable_has_alpha (drawable));

  width  = gimp_item_get_width  (item);
  height = gimp_item_get_height (item);

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);

  cr = cairo_create (surface);
  gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE);
  cairo_destroy (cr);

  pixel_region_init (&layerPR, gimp_drawable_get_tiles (drawable),
                     0, 0, width, height, TRUE);

  layer_type = gimp_drawable_type (drawable);
  layer_alpha_byte = layerPR.bytes - 1;

  cairo_surface_flush (surface);
  data      = cairo_image_surface_get_data (surface);
  rowstride = cairo_image_surface_get_stride (surface);

  for (pr = pixel_regions_register (1, &layerPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
    {
      const guchar *src  = data + layerPR.y * rowstride + layerPR.x * 4;
      guchar       *dest = layerPR.data;
      gint          rows = layerPR.h;

      while (rows--)
        {
          const guchar *s = src;
          guchar       *d = dest;
          gint          w = layerPR.w;

          while (w--)
            {
              guchar color[4];

              GIMP_CAIRO_ARGB32_GET_PIXEL (s,
                                           color[0],
                                           color[1],
                                           color[2],
                                           color[3]);

              gimp_image_transform_color (image,
                                          layer_type, d, GIMP_RGB, color);
              d[layer_alpha_byte] = color[3];

              s += 4;
              d += layerPR.bytes;
            }

          src  += rowstride;
          dest += layerPR.rowstride;
        }
    }

  cairo_surface_destroy (surface);

  gimp_drawable_update (drawable, 0, 0, width, height);
}
void
floating_sel_store (GimpLayer *layer,
                    gint       x,
                    gint       y,
                    gint       w,
                    gint       h)
{
  PixelRegion srcPR, destPR;
  gint        offx, offy;
  gint        x1, y1, x2, y2;

  g_return_if_fail (GIMP_IS_LAYER (layer));
  g_return_if_fail (gimp_layer_is_floating_sel (layer));

  /*  Check the backing store & make sure it has the correct dimensions  */
  if ((tile_manager_width  (layer->fs.backing_store) !=
       gimp_item_width (GIMP_ITEM(layer)))  ||
      (tile_manager_height (layer->fs.backing_store) !=
       gimp_item_height (GIMP_ITEM(layer))) ||
      (tile_manager_bpp    (layer->fs.backing_store) !=
       gimp_drawable_bytes (layer->fs.drawable)))
    {
      /*  free the backing store and allocate anew  */
      tile_manager_unref (layer->fs.backing_store);

      layer->fs.backing_store =
        tile_manager_new (GIMP_ITEM (layer)->width,
                          GIMP_ITEM (layer)->height,
                          gimp_drawable_bytes (layer->fs.drawable));
    }

  /*  What this function does is save the specified area of the
   *  drawable that this floating selection obscures.  We do this so
   *  that it will be possible to subsequently restore the drawable's area
   */
  gimp_item_offsets (GIMP_ITEM (layer->fs.drawable), &offx, &offy);

  /*  Find the minimum area we need to uncover -- in image space  */
  x1 = MAX (GIMP_ITEM (layer)->offset_x, offx);
  y1 = MAX (GIMP_ITEM (layer)->offset_y, offy);
  x2 = MIN (GIMP_ITEM (layer)->offset_x + GIMP_ITEM (layer)->width,
            offx + gimp_item_width (GIMP_ITEM (layer->fs.drawable)));
  y2 = MIN (GIMP_ITEM (layer)->offset_y + GIMP_ITEM (layer)->height,
            offy + gimp_item_height (GIMP_ITEM (layer->fs.drawable)));

  x1 = CLAMP (x, x1, x2);
  y1 = CLAMP (y, y1, y2);
  x2 = CLAMP (x + w, x1, x2);
  y2 = CLAMP (y + h, y1, y2);

  if ((x2 - x1) > 0 && (y2 - y1) > 0)
    {
      /*  Copy the area from the drawable to the backing store  */
      pixel_region_init (&srcPR, gimp_drawable_get_tiles (layer->fs.drawable),
                         (x1 - offx), (y1 - offy), (x2 - x1), (y2 - y1), FALSE);
      pixel_region_init (&destPR, layer->fs.backing_store,
                         (x1 - GIMP_ITEM (layer)->offset_x),
                         (y1 - GIMP_ITEM (layer)->offset_y),
                         (x2 - x1), (y2 - y1), TRUE);

      copy_region (&srcPR, &destPR);
    }
}
Beispiel #20
0
static void
gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel,
                                    GimpDisplay        *display)
{
  GimpForegroundSelectTool    *fg_select;
  GimpForegroundSelectOptions *options;
  GimpImage                   *image = gimp_display_get_image (display);
  GimpDrawable                *drawable;
  GimpScanConvert             *scan_convert;
  GimpChannel                 *mask;
  const GimpVector2           *points;
  gint                         n_points;

  drawable  = gimp_image_get_active_drawable (image);
  fg_select = GIMP_FOREGROUND_SELECT_TOOL (free_sel);
  options   = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (free_sel);

  if (fg_select->idle_id)
    {
      g_source_remove (fg_select->idle_id);
      fg_select->idle_id = 0;
    }

  if (! drawable)
    return;

  scan_convert = gimp_scan_convert_new ();

  gimp_free_select_tool_get_points (free_sel,
                                    &points,
                                    &n_points);

  gimp_scan_convert_add_polyline (scan_convert,
                                  n_points,
                                  points,
                                  TRUE);

  mask = gimp_channel_new (image,
                           gimp_image_get_width (image),
                           gimp_image_get_height (image),
                           "foreground-extraction", NULL);

  gimp_scan_convert_render_value (scan_convert,
                                  gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                                  0, 0, 128);
  gimp_scan_convert_free (scan_convert);

  if (fg_select->strokes)
    {
      GList *list;

      gimp_set_busy (image->gimp);

      /*  apply foreground and background markers  */
      for (list = fg_select->strokes; list; list = list->next)
        gimp_foreground_select_tool_stroke (mask, list->data);

      if (fg_select->state)
        gimp_drawable_foreground_extract_siox (GIMP_DRAWABLE (mask),
                                               fg_select->state,
                                               fg_select->refinement,
                                               options->smoothness,
                                               options->sensitivity,
                                               ! options->contiguous,
                                               GIMP_PROGRESS (fg_select));

      fg_select->refinement = SIOX_REFINEMENT_NO_CHANGE;

      gimp_unset_busy (image->gimp);
    }
  else
    {
      gint x1, y1;
      gint x2, y2;

      g_object_set (options, "background", FALSE, NULL);

      gimp_foreground_select_tool_get_area (mask, &x1, &y1, &x2, &y2);

      if (fg_select->state)
        g_warning ("state should be NULL here");

      fg_select->state =
        gimp_drawable_foreground_extract_siox_init (drawable,
                                                    x1, y1, x2 - x1, y2 - y1);
    }

  gimp_foreground_select_tool_set_mask (fg_select, display, mask);

  g_object_unref (mask);
}
Beispiel #21
0
void
gimp_drawable_real_apply_region (GimpDrawable         *drawable,
                                 PixelRegion          *src2PR,
                                 gboolean              push_undo,
                                 const gchar          *undo_desc,
                                 gdouble               opacity,
                                 GimpLayerModeEffects  mode,
                                 TileManager          *src1_tiles,
                                 PixelRegion          *destPR,
                                 gint                  x,
                                 gint                  y)
{
  GimpItem        *item  = GIMP_ITEM (drawable);
  GimpImage       *image = gimp_item_get_image (item);
  GimpChannel     *mask  = gimp_image_get_mask (image);
  gint             x1, y1, x2, y2;
  gint             offset_x, offset_y;
  PixelRegion      src1PR, my_destPR;
  CombinationMode  operation;
  gboolean         active_components[MAX_CHANNELS];

  /*  don't apply the mask to itself and don't apply an empty mask  */
  if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask))
    mask = NULL;

  /*  configure the active channel array  */
  gimp_drawable_get_active_components (drawable, active_components);

  /*  determine what sort of operation is being attempted and
   *  if it's actually legal...
   */
  operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable),
                                               src2PR->bytes);
  if (operation == -1)
    {
      g_warning ("%s: illegal parameters.", G_STRFUNC);
      return;
    }

  /*  get the layer offsets  */
  gimp_item_get_offset (item, &offset_x, &offset_y);

  /*  make sure the image application coordinates are within drawable bounds  */
  x1 = CLAMP (x,             0, gimp_item_get_width  (item));
  y1 = CLAMP (y,             0, gimp_item_get_height (item));
  x2 = CLAMP (x + src2PR->w, 0, gimp_item_get_width  (item));
  y2 = CLAMP (y + src2PR->h, 0, gimp_item_get_height (item));

  if (mask)
    {
      GimpItem *mask_item = GIMP_ITEM (mask);

      /*  make sure coordinates are in mask bounds ...
       *  we need to add the layer offset to transform coords
       *  into the mask coordinate system
       */
      x1 = CLAMP (x1, -offset_x, gimp_item_get_width  (mask_item) - offset_x);
      y1 = CLAMP (y1, -offset_y, gimp_item_get_height (mask_item) - offset_y);
      x2 = CLAMP (x2, -offset_x, gimp_item_get_width  (mask_item) - offset_x);
      y2 = CLAMP (y2, -offset_y, gimp_item_get_height (mask_item) - offset_y);
    }

  /*  If the calling procedure specified an undo step...  */
  if (push_undo)
    {
      GimpDrawableUndo *undo;

      gimp_drawable_push_undo (drawable, undo_desc,
                               x1, y1,
                               x2 - x1, y2 - y1,
                               NULL, FALSE);

      undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image));

      if (undo)
        {
          PixelRegion tmp_srcPR;
          PixelRegion tmp_destPR;

          undo->paint_mode = mode;
          undo->opacity    = opacity;
          undo->src2_tiles = tile_manager_new (x2 - x1, y2 - y1,
                                               src2PR->bytes);

          tmp_srcPR = *src2PR;
          pixel_region_resize (&tmp_srcPR,
                               src2PR->x + (x1 - x), src2PR->y + (y1 - y),
                               x2 - x1, y2 - y1);
          pixel_region_init (&tmp_destPR, undo->src2_tiles,
                             0, 0,
                             x2 - x1, y2 - y1, TRUE);

          copy_region (&tmp_srcPR, &tmp_destPR);
        }
    }

  /* configure the pixel regions */

  /* check if an alternative to using the drawable's data as src1 was
   * provided...
   */
  if (src1_tiles)
    {
      pixel_region_init (&src1PR, src1_tiles,
                         x1, y1, x2 - x1, y2 - y1,
                         FALSE);
    }
  else
    {
      pixel_region_init (&src1PR, gimp_drawable_get_tiles (drawable),
                         x1, y1, x2 - x1, y2 - y1,
                         FALSE);
    }

  /* check if an alternative to using the drawable's data as dest was
   * provided...
   */
  if (!destPR)
    {
      pixel_region_init (&my_destPR, gimp_drawable_get_tiles (drawable),
                         x1, y1, x2 - x1, y2 - y1,
                         TRUE);
      destPR = &my_destPR;
    }

  pixel_region_resize (src2PR,
                       src2PR->x + (x1 - x), src2PR->y + (y1 - y),
                       x2 - x1, y2 - y1);

  if (mask)
    {
      PixelRegion maskPR;

      pixel_region_init (&maskPR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                         x1 + offset_x,
                         y1 + offset_y,
                         x2 - x1, y2 - y1,
                         FALSE);

      combine_regions (&src1PR, src2PR, destPR, &maskPR, NULL,
                       opacity * 255.999,
                       mode,
                       active_components,
                       operation);
    }
  else
    {
      combine_regions (&src1PR, src2PR, destPR, NULL, NULL,
                       opacity * 255.999,
                       mode,
                       active_components,
                       operation);
    }
}
TempBuf *
gimp_paint_core_get_orig_image (GimpPaintCore *core,
                                GimpDrawable  *drawable,
                                gint           x,
                                gint           y,
                                gint           width,
                                gint           height)
{
  PixelRegion   srcPR;
  PixelRegion   destPR;
  Tile         *undo_tile;
  gboolean      release_tile;
  gint          h;
  gint          pixelwidth;
  gint          drawable_width;
  gint          drawable_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_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (core->undo_tiles != NULL, NULL);

  core->orig_buf = temp_buf_resize (core->orig_buf,
                                    gimp_drawable_bytes (drawable),
                                    x, y, width, height);

  drawable_width  = gimp_item_get_width  (GIMP_ITEM (drawable));
  drawable_height = gimp_item_get_height (GIMP_ITEM (drawable));

  gimp_rectangle_intersect (x, y,
                            width, height,
                            0, 0,
                            drawable_width, drawable_height,
                            &x, &y,
                            &width, &height);

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

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

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

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

          undo_tile = tile_manager_get_tile (core->undo_tiles,
                                             srcPR.x, srcPR.y,
                                             TRUE, FALSE);
          s = tile_data_pointer (undo_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 (undo_tile, FALSE);
    }

  return core->orig_buf;
}
Beispiel #23
0
void
gimp_channel_combine_rect (GimpChannel    *mask,
                           GimpChannelOps  op,
                           gint            x,
                           gint            y,
                           gint            w,
                           gint            h)
{
  PixelRegion maskPR;
  guchar      color;

  g_return_if_fail (GIMP_IS_CHANNEL (mask));

  if (! gimp_rectangle_intersect (x, y, w, h,
                                  0, 0,
                                  gimp_item_get_width  (GIMP_ITEM (mask)),
                                  gimp_item_get_height (GIMP_ITEM (mask)),
                                  &x, &y, &w, &h))
    return;

  pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                     x, y, w, h, TRUE);

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
    color = OPAQUE_OPACITY;
  else
    color = TRANSPARENT_OPACITY;

  color_region (&maskPR, &color);

  /*  Determine new boundary  */
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty)
    {
      if (x < mask->x1)
        mask->x1 = x;
      if (y < mask->y1)
        mask->y1 = y;
      if ((x + w) > mask->x2)
        mask->x2 = (x + w);
      if ((y + h) > mask->y2)
        mask->y2 = (y + h);
    }
  else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty)
    {
      mask->empty = FALSE;
      mask->x1    = x;
      mask->y1    = y;
      mask->x2    = x + w;
      mask->y2    = y + h;
    }
  else
    {
      mask->bounds_known = FALSE;
    }

  mask->x1 = CLAMP (mask->x1, 0, gimp_item_get_width  (GIMP_ITEM (mask)));
  mask->y1 = CLAMP (mask->y1, 0, gimp_item_get_height (GIMP_ITEM (mask)));
  mask->x2 = CLAMP (mask->x2, 0, gimp_item_get_width  (GIMP_ITEM (mask)));
  mask->y2 = CLAMP (mask->y2, 0, gimp_item_get_height (GIMP_ITEM (mask)));

  gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h);
}
Beispiel #24
0
/**
 * gimp_channel_combine_ellipse_rect:
 * @mask:      the channel with which to combine the elliptic rect
 * @op:        whether to replace, add to, or subtract from the current
 *             contents
 * @x:         x coordinate of upper left corner of bounding rect
 * @y:         y coordinate of upper left corner of bounding rect
 * @w:         width of bounding rect
 * @h:         height of bounding rect
 * @a:         elliptic a-constant applied to corners
 * @b:         elliptic b-constant applied to corners
 * @antialias: if %TRUE, antialias the elliptic corners
 *
 * Used for rounded cornered rectangles and ellipses.  If @op is
 * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels
 * within the ellipse to 255.  If @op is %GIMP_CHANNEL_OP_SUBTRACT,
 * sets pixels within to zero.  If @antialias is %TRUE, pixels that
 * impinge on the edge of the ellipse are set to intermediate values,
 * depending on how much they overlap.
 **/
void
gimp_channel_combine_ellipse_rect (GimpChannel    *mask,
                                   GimpChannelOps  op,
                                   gint            x,
                                   gint            y,
                                   gint            w,
                                   gint            h,
                                   gdouble         a,
                                   gdouble         b,
                                   gboolean        antialias)
{
  PixelRegion  maskPR;
  gdouble      a_sqr;
  gdouble      b_sqr;
  gdouble      ellipse_center_x;
  gint         x0, y0;
  gint         width, height;
  gpointer     pr;

  g_return_if_fail (GIMP_IS_CHANNEL (mask));
  g_return_if_fail (a >= 0.0 && b >= 0.0);
  g_return_if_fail (op != GIMP_CHANNEL_OP_INTERSECT);

  /* Make sure the elliptic corners fit into the rect */
  a = MIN (a, w / 2.0);
  b = MIN (b, h / 2.0);

  a_sqr = SQR (a);
  b_sqr = SQR (b);

  if (! gimp_rectangle_intersect (x, y, w, h,
                                  0, 0,
                                  gimp_item_get_width  (GIMP_ITEM (mask)),
                                  gimp_item_get_height (GIMP_ITEM (mask)),
                                  &x0, &y0, &width, &height))
    return;

  ellipse_center_x = x + a;

  pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)),
                     x0, y0, width, height, TRUE);

  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
    {
      guchar *data = maskPR.data;
      gint    py;

      for (py = maskPR.y;
           py < maskPR.y + maskPR.h;
           py++, data += maskPR.rowstride)
        {
          const gint px = maskPR.x;
          gdouble    ellipse_center_y;

          if (py >= y + b && py < y + h - b)
            {
              /*  we are on a row without rounded corners  */
              gimp_channel_combine_span (data, op, 0, maskPR.w, 255);
              continue;
            }

          /* Match the ellipse center y with our current y */
          if (py < y + b)
            {
              ellipse_center_y = y + b;
            }
          else
            {
              ellipse_center_y = y + h - b;
            }

          /* For a non-antialiased ellipse, use the normal equation
           * for an ellipse with an arbitrary center
           * (ellipse_center_x, ellipse_center_y).
           */
          if (! antialias)
            {
              gdouble     half_ellipse_width_at_y;
              gint        x_start;
              gint        x_end;

              half_ellipse_width_at_y =
                sqrt (a_sqr -
                      a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr);

              x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y);
              x_end   = ROUND (ellipse_center_x + w - 2 * a +
                               half_ellipse_width_at_y);

              gimp_channel_combine_span (data, op,
                                         MAX (x_start - px, 0),
                                         MIN (x_end   - px, maskPR.w), 255);
            }
          else  /* use antialiasing */
            {
              /* algorithm changed 7-18-04, because the previous one
               * did not work well for eccentric ellipses.  The new
               * algorithm measures the distance to the ellipse in the
               * X and Y directions, and uses trigonometry to
               * approximate the distance to the ellipse as the
               * distance to the hypotenuse of a right triangle whose
               * legs are the X and Y distances.  (WES)
               */
              const gfloat yi       = ABS (py + 0.5 - ellipse_center_y);
              gint         last_val = -1;
              gint         x_start  = px;
              gint         cur_x;

              for (cur_x = px; cur_x < (px + maskPR.w); cur_x++)
                {
                  gfloat  xj;
                  gfloat  xdist;
                  gfloat  ydist;
                  gfloat  r;
                  gfloat  dist;
                  gint    val;

                  if (cur_x < x + w / 2)
                    {
                      ellipse_center_x = x + a;
                    }
                  else
                    {
                      ellipse_center_x = x + w - a;
                    }

                  xj = ABS (cur_x + 0.5 - ellipse_center_x);

                  if (yi < b)
                    xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr);
                  else
                    xdist = 1000.0;  /* anything large will work */

                  if (xj < a)
                    ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr);
                  else
                    ydist = 1000.0;  /* anything large will work */

                  r = hypot (xdist, ydist);

                  if (r < 0.001)
                    dist = 0.;
                  else
                    dist = xdist * ydist / r; /* trig formula for distance to
                                               * hypotenuse
                                               */

                  if (xdist < 0.0)
                    dist *= -1;

                  if (dist < -0.5)
                    val = 255;
                  else if (dist < 0.5)
                    val = (gint) (255 * (1 - (dist + 0.5)));
                  else
                    val = 0;

                  if (last_val != val)
                    {
                      if (last_val != -1)
                        gimp_channel_combine_span (data, op,
                                                   MAX (x_start - px, 0),
                                                   MIN (cur_x   - px, maskPR.w),
                                                   last_val);

                      x_start = cur_x;
                      last_val = val;
                    }

                  /*  skip ahead if we are on the straight segment
                   *  between rounded corners
                   */
                  if (cur_x >= x + a && cur_x < x + w - a)
                    {
                      gimp_channel_combine_span (data, op,
                                                 MAX (x_start - px, 0),
                                                 MIN (cur_x   - px, maskPR.w),
                                                 last_val);

                      x_start = cur_x;
                      cur_x = x + w - a;
                      last_val = val = 255;
                    }

                  /* Time to change center? */
                  if (cur_x >= x + w / 2)
                    {
                      ellipse_center_x = x + w - a;
                    }
                }

              gimp_channel_combine_span (data, op,
                                         MAX (x_start - px, 0),
                                         MIN (cur_x   - px, maskPR.w),
                                         last_val);
            }
        }
    }

  /*  use the intersected values for the boundary calculation  */
  x = x0;
  y = y0;
  w = width;
  h = height;

  /*  determine new boundary  */
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty)
    {
      if (x < mask->x1)
        mask->x1 = x;
      if (y < mask->y1)
        mask->y1 = y;
      if ((x + w) > mask->x2)
        mask->x2 = (x + w);
      if ((y + h) > mask->y2)
        mask->y2 = (y + h);
    }
  else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty)
    {
      mask->empty = FALSE;
      mask->x1    = x;
      mask->y1    = y;
      mask->x2    = x + w;
      mask->y2    = y + h;
    }
  else
    {
      mask->bounds_known = FALSE;
    }

  gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h);
}
Beispiel #25
0
static GimpLayer *
gimp_image_merge_layers (GimpImage     *image,
                         GSList        *merge_list,
                         GimpContext   *context,
                         GimpMergeType  merge_type,
                         const gchar   *undo_desc)
{
  GList           *list;
  GSList          *reverse_list = NULL;
  PixelRegion      src1PR, src2PR, maskPR;
  PixelRegion     *mask;
  GimpLayer       *merge_layer;
  GimpLayer       *layer;
  GimpLayer       *bottom_layer;
  GimpImageType    type;
  gint             count;
  gint             x1, y1, x2, y2;
  gint             x3, y3, x4, y4;
  CombinationMode  operation;
  gint             position;
  gboolean         active[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE };
  gint             off_x, off_y;
  gchar           *name;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);

  layer        = NULL;
  type         = GIMP_RGBA_IMAGE;
  x1 = y1      = 0;
  x2 = y2      = 0;
  bottom_layer = NULL;

  /*  Get the layer extents  */
  count = 0;
  while (merge_list)
    {
      layer = merge_list->data;

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

      switch (merge_type)
        {
        case GIMP_EXPAND_AS_NECESSARY:
        case GIMP_CLIP_TO_IMAGE:
          if (! count)
            {
              x1 = off_x;
              y1 = off_y;
              x2 = off_x + gimp_item_width  (GIMP_ITEM (layer));
              y2 = off_y + gimp_item_height (GIMP_ITEM (layer));
            }
          else
            {
              if (off_x < x1)
                x1 = off_x;
              if (off_y < y1)
                y1 = off_y;
              if ((off_x + gimp_item_width (GIMP_ITEM (layer))) > x2)
                x2 = (off_x + gimp_item_width (GIMP_ITEM (layer)));
              if ((off_y + gimp_item_height (GIMP_ITEM (layer))) > y2)
                y2 = (off_y + gimp_item_height (GIMP_ITEM (layer)));
            }

          if (merge_type == GIMP_CLIP_TO_IMAGE)
            {
              x1 = CLAMP (x1, 0, gimp_image_get_width  (image));
              y1 = CLAMP (y1, 0, gimp_image_get_height (image));
              x2 = CLAMP (x2, 0, gimp_image_get_width  (image));
              y2 = CLAMP (y2, 0, gimp_image_get_height (image));
            }
          break;

        case GIMP_CLIP_TO_BOTTOM_LAYER:
          if (merge_list->next == NULL)
            {
              x1 = off_x;
              y1 = off_y;
              x2 = off_x + gimp_item_width (GIMP_ITEM (layer));
              y2 = off_y + gimp_item_height (GIMP_ITEM (layer));
            }
          break;

        case GIMP_FLATTEN_IMAGE:
          if (merge_list->next == NULL)
            {
              x1 = 0;
              y1 = 0;
              x2 = gimp_image_get_width  (image);
              y2 = gimp_image_get_height (image);
            }
          break;
        }

      count ++;
      reverse_list = g_slist_prepend (reverse_list, layer);
      merge_list = g_slist_next (merge_list);
    }

  if ((x2 - x1) == 0 || (y2 - y1) == 0)
    return NULL;

  /*  Start a merge undo group. */

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE,
                               undo_desc);

  name = g_strdup (gimp_object_get_name (GIMP_OBJECT (layer)));

  if (merge_type == GIMP_FLATTEN_IMAGE ||
      gimp_drawable_type (GIMP_DRAWABLE (layer)) == GIMP_INDEXED_IMAGE)
    {
      guchar bg[4] = { 0, 0, 0, 0 };

      type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (gimp_image_base_type (image));

      merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1),
                                    type,
                                    gimp_object_get_name (GIMP_OBJECT (layer)),
                                    GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
      if (! merge_layer)
        {
          g_warning ("%s: could not allocate merge layer.", G_STRFUNC);
          return NULL;
        }

      GIMP_ITEM (merge_layer)->offset_x = x1;
      GIMP_ITEM (merge_layer)->offset_y = y1;

      /*  get the background for compositing  */
      gimp_image_get_background (image, context,
                                 gimp_drawable_type (GIMP_DRAWABLE (merge_layer)),
                                 bg);

      /*  init the pixel region  */
      pixel_region_init (&src1PR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)),
                         0, 0, (x2 - x1), (y2 - y1),
                         TRUE);

      /*  set the region to the background color  */
      color_region (&src1PR, bg);

      position = 0;
    }
  else
    {
      /*  The final merged layer inherits the name of the bottom most layer
       *  and the resulting layer has an alpha channel whether or not the
       *  original did. Opacity is set to 100% and the MODE is set to normal.
       */

      merge_layer =
        gimp_layer_new (image, (x2 - x1), (y2 - y1),
                        gimp_drawable_type_with_alpha (GIMP_DRAWABLE (layer)),
                        "merged layer",
                        GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);

      if (!merge_layer)
        {
          g_warning ("%s: could not allocate merge layer", G_STRFUNC);
          return NULL;
        }

      GIMP_ITEM (merge_layer)->offset_x = x1;
      GIMP_ITEM (merge_layer)->offset_y = y1;

      /*  clear the layer  */
      pixel_region_init (&src1PR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)),
                         0, 0,
                         (x2 - x1), (y2 - y1),
                         TRUE);
      clear_region (&src1PR);

      /*  Find the index in the layer list of the bottom layer--we need this
       *  in order to add the final, merged layer to the layer list correctly
       */
      layer = reverse_list->data;
      position =
        gimp_container_num_children (image->layers) -
        gimp_container_get_child_index (image->layers, GIMP_OBJECT (layer));
    }

  bottom_layer = layer;

  /* Copy the tattoo and parasites of the bottom layer to the new layer */
  gimp_item_set_tattoo (GIMP_ITEM (merge_layer),
                        gimp_item_get_tattoo (GIMP_ITEM (bottom_layer)));

  g_object_unref (GIMP_ITEM (merge_layer)->parasites);
  GIMP_ITEM (merge_layer)->parasites =
    gimp_parasite_list_copy (GIMP_ITEM (bottom_layer)->parasites);

  while (reverse_list)
    {
      GimpLayerModeEffects  mode;

      layer = reverse_list->data;

      /*  determine what sort of operation is being attempted and
       *  if it's actually legal...
       */
      operation = gimp_image_merge_layers_get_operation (merge_layer, layer);

      if (operation == -1)
        {
          gimp_layer_add_alpha (layer);

          /*  try again ...  */
          operation = gimp_image_merge_layers_get_operation (merge_layer,
                                                             layer);
        }

      if (operation == -1)
        {
          g_warning ("%s: attempting to merge incompatible layers.", G_STRFUNC);
          return NULL;
        }

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

      x3 = CLAMP (off_x, x1, x2);
      y3 = CLAMP (off_y, y1, y2);
      x4 = CLAMP (off_x + gimp_item_width  (GIMP_ITEM (layer)), x1, x2);
      y4 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), y1, y2);

      /* configure the pixel regions  */
      pixel_region_init (&src1PR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)),
                         (x3 - x1), (y3 - y1), (x4 - x3), (y4 - y3),
                         TRUE);
      pixel_region_init (&src2PR,
                         gimp_drawable_get_tiles (GIMP_DRAWABLE (layer)),
                         (x3 - off_x), (y3 - off_y),
                         (x4 - x3), (y4 - y3),
                         FALSE);

      if (gimp_layer_get_mask (layer) &&
          gimp_layer_mask_get_apply (layer->mask))
        {
          TileManager *tiles;

          tiles = gimp_drawable_get_tiles (GIMP_DRAWABLE (layer->mask));

          pixel_region_init (&maskPR, tiles,
                             (x3 - off_x), (y3 - off_y), (x4 - x3), (y4 - y3),
                             FALSE);
          mask = &maskPR;
        }
      else
        {
          mask = NULL;
        }

      /* DISSOLVE_MODE is special since it is the only mode that does not
       *  work on the projection with the lower layer, but only locally on
       *  the layers alpha channel.
       */
      mode = gimp_layer_get_mode (layer);
      if (layer == bottom_layer && mode != GIMP_DISSOLVE_MODE)
        mode = GIMP_NORMAL_MODE;

      combine_regions (&src1PR, &src2PR, &src1PR, mask, NULL,
                       gimp_layer_get_opacity (layer) * 255.999,
                       mode,
                       active,
                       operation);

      gimp_image_remove_layer (image, layer);

      reverse_list = g_slist_next (reverse_list);
    }

  g_slist_free (reverse_list);

  /*  if the type is flatten, remove all the remaining layers  */
  if (merge_type == GIMP_FLATTEN_IMAGE)
    {
      list = GIMP_LIST (image->layers)->list;
      while (list)
        {
          layer = list->data;

          list = g_list_next (list);
          gimp_image_remove_layer (image, layer);
        }

      gimp_image_add_layer (image, merge_layer, position);
    }
  else
    {
      /*  Add the layer to the image  */
      gimp_image_add_layer (image, merge_layer,
         gimp_container_num_children (image->layers) - position + 1);
    }

  /* set the name after the original layers have been removed so we
   * don't end up with #2 appended to the name
   */
  gimp_object_take_name (GIMP_OBJECT (merge_layer), name);

  gimp_item_set_visible (GIMP_ITEM (merge_layer), TRUE, TRUE);

  /*  End the merge undo group  */
  gimp_image_undo_group_end (image);

  gimp_drawable_update (GIMP_DRAWABLE (merge_layer),
                        0, 0,
                        gimp_item_width  (GIMP_ITEM (merge_layer)),
                        gimp_item_height (GIMP_ITEM (merge_layer)));

  return merge_layer;
}
Beispiel #26
0
static gboolean
gimp_smudge_start (GimpPaintCore    *paint_core,
                   GimpDrawable     *drawable,
                   GimpPaintOptions *paint_options)
{
  GimpSmudge  *smudge = GIMP_SMUDGE (paint_core);
  GimpImage   *image;
  TempBuf     *area;
  PixelRegion  srcPR;
  gint         bytes;
  gint         x, y, w, h;

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (gimp_drawable_is_indexed (drawable))
    return FALSE;

  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
  if (! area)
    return FALSE;

  /*  adjust the x and y coordinates to the upper left corner of the brush  */
  gimp_smudge_brush_coords (paint_core, &x, &y, &w, &h);

  /*  Allocate the accumulation buffer */
  bytes = gimp_drawable_bytes (drawable);
  smudge->accum_data = g_malloc (w * h * bytes);

  /*  If clipped, prefill the smudge buffer with the color at the
   *  brush position.
   */
  if (x != area->x || y != area->y || w != area->width || h != area->height)
    {
      guchar fill[4];

      gimp_pickable_get_pixel_at (GIMP_PICKABLE (drawable),
                                  CLAMP ((gint) paint_core->cur_coords.x,
                                         0,
                                         gimp_item_width (GIMP_ITEM (drawable)) - 1),
                                  CLAMP ((gint) paint_core->cur_coords.y,
                                         0,
                                         gimp_item_height (GIMP_ITEM (drawable)) - 1),
                                  fill);

      pixel_region_init_data (&srcPR, smudge->accum_data,
                              bytes, bytes * w,
                              0, 0, w, h);

      color_region (&srcPR, fill);
    }

  pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable),
                     area->x, area->y, area->width, area->height, FALSE);

  pixel_region_init_data (&smudge->accumPR, smudge->accum_data,
                          bytes, bytes * w,
                          area->x - x,
                          area->y - y,
                          area->width,
                          area->height);

  /* copy the region under the original painthit. */
  copy_region (&srcPR, &smudge->accumPR);

  pixel_region_init_data (&smudge->accumPR, smudge->accum_data,
                          bytes, bytes * w,
                          area->x - x,
                          area->y - y,
                          area->width,
                          area->height);

  return TRUE;
}
Beispiel #27
0
static void
gimp_smudge_motion (GimpPaintCore    *paint_core,
                    GimpDrawable     *drawable,
                    GimpPaintOptions *paint_options)
{
  GimpSmudge          *smudge           = GIMP_SMUDGE (paint_core);
  GimpSmudgeOptions   *options          = GIMP_SMUDGE_OPTIONS (paint_options);
  GimpContext         *context          = GIMP_CONTEXT (paint_options);
  GimpPressureOptions *pressure_options = paint_options->pressure_options;
  GimpImage           *image;
  TempBuf             *area;
  PixelRegion          srcPR, destPR, tempPR;
  gdouble              rate;
  gdouble              opacity;
  gint                 x, y, w, h;

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (gimp_drawable_is_indexed (drawable))
    return;

  opacity = gimp_paint_options_get_fade (paint_options, image,
                                         paint_core->pixel_dist);
  if (opacity == 0.0)
    return;

  /*  Get the unclipped brush coordinates  */
  gimp_smudge_brush_coords (paint_core, &x, &y, &w, &h);

  /*  Get the paint area (Smudge won't scale!)  */
  area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options);
  if (! area)
    return;

  /* srcPR will be the pixels under the current painthit from the drawable */
  pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable),
                     area->x, area->y, area->width, area->height, FALSE);

  /* Enable pressure sensitive rate */
  if (pressure_options->rate)
    rate = MIN (options->rate / 100.0 * PRESSURE_SCALE *
                paint_core->cur_coords.pressure, 1.0);
  else
    rate = options->rate / 100.0;

  /* The tempPR will be the built up buffer (for smudge) */
  pixel_region_init_data (&tempPR, smudge->accum_data,
                          smudge->accumPR.bytes,
                          smudge->accumPR.rowstride,
                          area->x - x,
                          area->y - y,
                          area->width,
                          area->height);

  /* The dest will be the paint area we got above (= canvas_buf) */
  pixel_region_init_temp_buf (&destPR, area,
                              0, 0, area->width, area->height);

  /*  Smudge uses the buffer Accum.
   *  For each successive painthit Accum is built like this
   *    Accum =  rate*Accum  + (1-rate)*I.
   *  where I is the pixels under the current painthit.
   *  Then the paint area (canvas_buf) is built as
   *    (Accum,1) (if no alpha),
   */

  blend_region (&srcPR, &tempPR, &tempPR, ROUND (rate * 255.0));

  /* re-init the tempPR */
  pixel_region_init_data (&tempPR, smudge->accum_data,
                          smudge->accumPR.bytes,
                          smudge->accumPR.rowstride,
                          area->x - x,
                          area->y - y,
                          area->width,
                          area->height);

  if (! gimp_drawable_has_alpha (drawable))
    add_alpha_region (&tempPR, &destPR);
  else
    copy_region (&tempPR, &destPR);

  if (pressure_options->opacity)
    opacity *= PRESSURE_SCALE * paint_core->cur_coords.pressure;

  gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
                                  MIN (opacity, GIMP_OPACITY_OPAQUE),
                                  gimp_context_get_opacity (context),
                                  gimp_paint_options_get_brush_mode (paint_options),
                                  GIMP_PAINT_INCREMENTAL);
}
Beispiel #28
0
void
gimp_display_shell_render (GimpDisplayShell *shell,
                           gint              x,
                           gint              y,
                           gint              w,
                           gint              h,
                           GdkRectangle     *highlight)
{
    GimpProjection *projection;
    GimpImage      *image;
    RenderInfo      info;
    GimpImageType   type;

    g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
    g_return_if_fail (w > 0 && h > 0);

    image = shell->display->image;
    projection = image->projection;

    /* Initialize RenderInfo with values that don't change during the
     * call of this function.
     */
    info.shell      = shell;

    info.x          = x + shell->offset_x;
    info.y          = y + shell->offset_y;
    info.w          = w;
    info.h          = h;

    info.dest_bpp   = 3;
    info.dest_bpl   = info.dest_bpp * GIMP_RENDER_BUF_WIDTH;
    info.dest_width = info.dest_bpp * info.w;

    switch (GIMP_DISPLAY_CONFIG (image->gimp->config)->zoom_quality)
    {
    case GIMP_ZOOM_QUALITY_LOW:
        info.zoom_quality = GIMP_DISPLAY_ZOOM_FAST;
        break;

    case GIMP_ZOOM_QUALITY_HIGH:
        info.zoom_quality = GIMP_DISPLAY_ZOOM_PIXEL_AA;
        break;
    }

    if (GIMP_IMAGE_TYPE_HAS_ALPHA (gimp_projection_get_image_type (projection)))
    {
        gdouble opacity = gimp_projection_get_opacity (projection);

        info.alpha = render_image_init_alpha (opacity * 255.999);
    }

    /* Setup RenderInfo for rendering a GimpProjection level. */
    {
        TileManager *src_tiles;
        gint         level;

        level = gimp_projection_get_level (projection,
                                           shell->scale_x,
                                           shell->scale_y);

        src_tiles = gimp_projection_get_tiles_at_level (projection, level);

        gimp_display_shell_render_info_scale (&info, shell, src_tiles, level);
    }

    /* Currently, only RGBA and GRAYA projection types are used. */
    type = gimp_projection_get_image_type (projection);

    switch (type)
    {
    case GIMP_RGBA_IMAGE:
        render_image_rgb_a (&info);
        break;
    case GIMP_GRAYA_IMAGE:
        render_image_gray_a (&info);
        break;
    default:
        g_warning ("%s: unsupported projection type (%d)", G_STRFUNC, type);
        g_assert_not_reached ();
    }

    /*  apply filters to the rendered projection  */
    if (shell->filter_stack)
        gimp_color_display_stack_convert (shell->filter_stack,
                                          shell->render_buf,
                                          w, h,
                                          3,
                                          3 * GIMP_RENDER_BUF_WIDTH);

    /*  dim pixels outside the highlighted rectangle  */
    if (highlight)
    {
        gimp_display_shell_render_highlight (shell, x, y, w, h, highlight);
    }
    else if (shell->mask)
    {
        TileManager *src_tiles = gimp_drawable_get_tiles (shell->mask);

        /* The mask does not (yet) have an image pyramid, use 0 as level, */
        gimp_display_shell_render_info_scale (&info, shell, src_tiles, 0);

        gimp_display_shell_render_mask (shell, &info);
    }

    /*  put it to the screen  */
    gimp_canvas_draw_rgb (GIMP_CANVAS (shell->canvas), GIMP_CANVAS_STYLE_RENDER,
                          x + shell->disp_xoffset, y + shell->disp_yoffset,
                          w, h,
                          shell->render_buf,
                          3 * GIMP_RENDER_BUF_WIDTH,
                          shell->offset_x, shell->offset_y);
}