static void gimp_mask_undo_constructed (GObject *object) { GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); GimpChannel *channel; GimpDrawable *drawable; gint x1, y1, x2, y2; if (G_OBJECT_CLASS (parent_class)->constructed) G_OBJECT_CLASS (parent_class)->constructed (object); g_assert (GIMP_IS_CHANNEL (GIMP_ITEM_UNDO (object)->item)); channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (object)->item); drawable = GIMP_DRAWABLE (channel); if (gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) { mask_undo->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gimp_drawable_get_format (drawable)); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), mask_undo->buffer, GEGL_RECTANGLE (0, 0, 0, 0)); mask_undo->x = x1; mask_undo->y = y1; } mask_undo->format = gimp_drawable_get_format (drawable); }
void gimp_drawable_foreground_extract_siox (GimpDrawable *mask, SioxState *state, SioxRefinementType refinement, gint smoothness, const gdouble sensitivity[3], gboolean multiblob, GimpProgress *progress) { GeglBuffer *buffer; gint x1, y1; gint x2, y2; g_return_if_fail (GIMP_IS_DRAWABLE (mask)); g_return_if_fail (babl_format_get_bytes_per_pixel (gimp_drawable_get_format (mask)) == 1); g_return_if_fail (state != NULL); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); if (progress) gimp_progress_start (progress, _("Foreground Extraction"), FALSE); if (GIMP_IS_CHANNEL (mask)) { gimp_channel_bounds (GIMP_CHANNEL (mask), &x1, &y1, &x2, &y2); } else { x1 = 0; y1 = 0; x2 = gimp_item_get_width (GIMP_ITEM (mask)); y2 = gimp_item_get_height (GIMP_ITEM (mask)); } buffer = gimp_drawable_get_buffer (mask); siox_foreground_extract (state, refinement, gimp_gegl_buffer_get_tiles (buffer), x1, y1, x2, y2, smoothness, sensitivity, multiblob, (SioxProgressFunc) gimp_progress_set_value, progress); if (progress) gimp_progress_end (progress); gimp_drawable_update (mask, x1, y1, x2, y2); }
void gimp_image_resize_to_selection (GimpImage *image, GimpContext *context, GimpProgress *progress) { GimpChannel *selection = gimp_image_get_mask (image); gint x1, y1; gint x2, y2; if (gimp_channel_is_empty (selection)) return; gimp_channel_bounds (selection, &x1, &y1, &x2, &y2); gimp_image_resize (image, context, x2 - x1, y2 - y1, - x1, - y1, progress); }
static void gimp_foreground_select_tool_get_area (GimpChannel *mask, gint *x1, gint *y1, gint *x2, gint *y2) { gint width; gint height; gimp_channel_bounds (mask, x1, y1, x2, y2); width = *x2 - *x1; height = *y2 - *y1; *x1 = MAX (*x1 - width / 2, 0); *y1 = MAX (*y1 - height / 2, 0); *x2 = MIN (*x2 + width / 2, gimp_item_get_width (GIMP_ITEM (mask))); *y2 = MIN (*y2 + height / 2, gimp_item_get_height (GIMP_ITEM (mask))); }
void image_crop_cmd_callback (GtkAction *action, gpointer data) { GimpImage *image; GtkWidget *widget; gint x1, y1, x2, y2; return_if_no_image (image, data); return_if_no_widget (widget, data); if (! gimp_channel_bounds (gimp_image_get_mask (image), &x1, &y1, &x2, &y2)) { gimp_message (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_WARNING, _("Cannot crop because the current selection is empty.")); return; } gimp_image_crop (image, action_data_get_context (data), x1, y1, x2, y2, FALSE, TRUE); gimp_image_flush (image); }
static GValueArray * selection_bounds_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GValueArray *args, GError **error) { gboolean success = TRUE; GValueArray *return_vals; GimpImage *image; gboolean non_empty = FALSE; gint32 x1 = 0; gint32 y1 = 0; gint32 x2 = 0; gint32 y2 = 0; image = gimp_value_get_image (&args->values[0], gimp); if (success) { non_empty = gimp_channel_bounds (gimp_image_get_mask (image), &x1, &y1, &x2, &y2); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) { g_value_set_boolean (&return_vals->values[1], non_empty); g_value_set_int (&return_vals->values[2], x1); g_value_set_int (&return_vals->values[3], y1); g_value_set_int (&return_vals->values[4], x2); g_value_set_int (&return_vals->values[5], y2); } return return_vals; }
/* * This function is called if the user clicks and releases the left * button without moving it. There are the things we might want * to do here: * 1) If there is an existing rectangle and we are inside it, we * convert it into a selection. * 2) If there is an existing rectangle and we are outside it, we * clear it. * 3) If there is no rectangle and there is a floating selection, * we anchor it. * 4) If there is no rectangle and we are inside the selection, we * create a rectangle from the selection bounds. * 5) If there is no rectangle and we are outside the selection, * we clear the selection. */ static gboolean gimp_rectangle_select_tool_execute (GimpRectangleTool *rectangle, gint x, gint y, gint w, gint h) { GimpTool *tool = GIMP_TOOL (rectangle); GimpRectangleSelectTool *rect_sel_tool; GimpRectangleSelectToolPrivate *priv; rect_sel_tool = GIMP_RECTANGLE_SELECT_TOOL (rectangle); priv = GIMP_RECTANGLE_SELECT_TOOL_GET_PRIVATE (rect_sel_tool); if (w == 0 && h == 0 && tool->display != NULL) { GimpImage *image = gimp_display_get_image (tool->display); GimpChannel *selection = gimp_image_get_mask (image); gint pressx; gint pressy; if (gimp_image_get_floating_selection (image)) { floating_sel_anchor (gimp_image_get_floating_selection (image)); gimp_image_flush (image); return TRUE; } pressx = ROUND (priv->press_x); pressy = ROUND (priv->press_y); /* if the click was inside the marching ants */ if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection), pressx, pressy) > 0.5) { gint x1, y1, x2, y2; if (gimp_channel_bounds (selection, &x1, &y1, &x2, &y2)) { g_object_set (rectangle, "x1", x1, "y1", y1, "x2", x2, "y2", y2, NULL); } gimp_rectangle_tool_set_function (rectangle, GIMP_RECTANGLE_TOOL_MOVING); return FALSE; } else { GimpTool *tool = GIMP_TOOL (rectangle); GimpChannelOps operation; /* prevent this change from halting the tool */ gimp_tool_control_push_preserve (tool->control, TRUE); /* We can conceptually think of a click outside of the * selection as adding a 0px selection. Behave intuitivly * for the current selection mode */ operation = gimp_rectangle_select_tool_get_operation (rect_sel_tool); switch (operation) { case GIMP_CHANNEL_OP_REPLACE: case GIMP_CHANNEL_OP_INTERSECT: gimp_channel_clear (selection, NULL, TRUE); gimp_image_flush (image); break; case GIMP_CHANNEL_OP_ADD: case GIMP_CHANNEL_OP_SUBTRACT: default: /* Do nothing */ break; } gimp_tool_control_pop_preserve (tool->control); } } gimp_rectangle_select_tool_update_option_defaults (rect_sel_tool, FALSE); /* Reset the automatic undo/redo mechanism */ priv->undo = NULL; priv->redo = NULL; return TRUE; }
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); }
static void compute_offset (GObject *object, GimpAlignmentType alignment) { gint object_offset_x = 0; gint object_offset_y = 0; gint object_height = 0; gint object_width = 0; gint offset = 0; if (GIMP_IS_IMAGE (object)) { GimpImage *image = GIMP_IMAGE (object); object_offset_x = 0; object_offset_y = 0; object_height = gimp_image_get_height (image); object_width = gimp_image_get_width (image); } else if (GIMP_IS_CHANNEL (object)) { /* for channels, we use the bounds of the visible area, not the layer bounds. This includes the selection channel */ GimpChannel *channel = GIMP_CHANNEL (object); if (gimp_channel_is_empty (channel)) { /* fall back on using the offsets instead */ GimpItem *item = GIMP_ITEM (object); gimp_item_get_offset (item, &object_offset_x, &object_offset_y); object_width = gimp_item_get_width (item); object_height = gimp_item_get_height (item); } else { gint x1, x2, y1, y2; gimp_channel_bounds (channel, &x1, &y1, &x2, &y2); object_offset_x = x1; object_offset_y = y1; object_width = x2 - x1; object_height = y2 - y1; } } else if (GIMP_IS_ITEM (object)) { GimpItem *item = GIMP_ITEM (object); if (GIMP_IS_VECTORS (object)) { gdouble x1_f, y1_f, x2_f, y2_f; gimp_vectors_bounds (GIMP_VECTORS (item), &x1_f, &y1_f, &x2_f, &y2_f); object_offset_x = ROUND (x1_f); object_offset_y = ROUND (y1_f); object_height = ROUND (y2_f - y1_f); object_width = ROUND (x2_f - x1_f); } else { gimp_item_get_offset (item, &object_offset_x, &object_offset_y); object_width = gimp_item_get_width (item); object_height = gimp_item_get_height (item); } } else if (GIMP_IS_GUIDE (object)) { GimpGuide *guide = GIMP_GUIDE (object); switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_VERTICAL: object_offset_x = gimp_guide_get_position (guide); object_width = 0; break; case GIMP_ORIENTATION_HORIZONTAL: object_offset_y = gimp_guide_get_position (guide); object_height = 0; break; default: break; } } else { g_printerr ("Alignment object is not an image, item or guide.\n"); } switch (alignment) { case GIMP_ALIGN_LEFT: case GIMP_ARRANGE_LEFT: offset = object_offset_x; break; case GIMP_ALIGN_HCENTER: case GIMP_ARRANGE_HCENTER: offset = object_offset_x + object_width/2; break; case GIMP_ALIGN_RIGHT: case GIMP_ARRANGE_RIGHT: offset = object_offset_x + object_width; break; case GIMP_ALIGN_TOP: case GIMP_ARRANGE_TOP: offset = object_offset_y; break; case GIMP_ALIGN_VCENTER: case GIMP_ARRANGE_VCENTER: offset = object_offset_y + object_height/2; break; case GIMP_ALIGN_BOTTOM: case GIMP_ARRANGE_BOTTOM: offset = object_offset_y + object_height; break; default: g_assert_not_reached (); } g_object_set_data (object, "align-offset", GINT_TO_POINTER (offset)); }
static void gimp_mask_undo_pop (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (undo); GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); GimpDrawable *drawable = GIMP_DRAWABLE (channel); GeglBuffer *new_buffer; const Babl *format; gint x1, y1, x2, y2; gint width = 0; gint height = 0; GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); if (gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) { new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gimp_drawable_get_format (drawable)); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), new_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gegl_buffer_clear (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1)); } else { new_buffer = NULL; } format = gimp_drawable_get_format (drawable); if (mask_undo->convert_format) { GeglBuffer *buffer; gint width = gimp_item_get_width (GIMP_ITEM (channel)); gint height = gimp_item_get_height (GIMP_ITEM (channel)); buffer = gimp_gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), mask_undo->format); gegl_buffer_clear (buffer, NULL); gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); g_object_unref (buffer); } if (mask_undo->buffer) { width = gegl_buffer_get_width (mask_undo->buffer); height = gegl_buffer_get_height (mask_undo->buffer); gegl_buffer_copy (mask_undo->buffer, NULL, gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (mask_undo->x, mask_undo->y, 0, 0)); g_object_unref (mask_undo->buffer); } /* invalidate the current bounds and boundary of the mask */ gimp_drawable_invalidate_boundary (drawable); if (mask_undo->buffer) { channel->empty = FALSE; channel->x1 = mask_undo->x; channel->y1 = mask_undo->y; channel->x2 = mask_undo->x + width; channel->y2 = mask_undo->y + height; } else { channel->empty = TRUE; channel->x1 = 0; channel->y1 = 0; channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); } /* we know the bounds */ channel->bounds_known = TRUE; /* set the new mask undo parameters */ mask_undo->buffer = new_buffer; mask_undo->x = x1; mask_undo->y = y1; mask_undo->format = format; gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, gimp_item_get_width (GIMP_ITEM (channel)), gimp_item_get_height (GIMP_ITEM (channel))); }