static GimpValueArray * selection_is_empty_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { gboolean success = TRUE; GimpValueArray *return_vals; GimpImage *image; gboolean is_empty = FALSE; image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp); if (success) { is_empty = gimp_channel_is_empty (gimp_image_get_mask (image)); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) g_value_set_boolean (gimp_value_array_index (return_vals, 1), is_empty); return return_vals; }
GimpTransformResize gimp_drawable_transform_get_effective_clip (GimpDrawable *drawable, GeglBuffer *orig_buffer, GimpTransformResize clip_result) { g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), clip_result); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), clip_result); g_return_val_if_fail (orig_buffer == NULL || GEGL_IS_BUFFER (orig_buffer), clip_result); /* Always clip unfloated buffers since they must keep their size */ if (GIMP_IS_CHANNEL (drawable)) { if (orig_buffer) { if (! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; } else { GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); if (gimp_channel_is_empty (gimp_image_get_mask (image))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; } } return clip_result; }
static GimpValueArray * flip_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { gboolean success = TRUE; GimpValueArray *return_vals; GimpDrawable *drawable; gint32 flip_type; drawable = gimp_value_get_drawable (gimp_value_array_index (args, 0), gimp); flip_type = g_value_get_enum (gimp_value_array_index (args, 1)); if (success) { gint x, y, width, height; success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, TRUE, error); if (success && gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) { gdouble axis; gimp_transform_get_flip_axis (x, y, width, height, flip_type, TRUE, &axis); if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) && ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable))))) { if (! gimp_drawable_transform_flip (drawable, context, flip_type, axis, FALSE)) { success = FALSE; } } else { gimp_item_flip (GIMP_ITEM (drawable), context, flip_type, axis, FALSE); } } } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) gimp_value_set_drawable (gimp_value_array_index (return_vals, 1), drawable); return return_vals; }
GimpObject * gimp_edit_cut (GimpImage *image, GimpDrawable *drawable, GimpContext *context, GError **error) { g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (GIMP_IS_LAYER (drawable) && gimp_channel_is_empty (gimp_image_get_mask (image))) { GimpImage *clip_image; clip_image = gimp_image_new_from_drawable (image->gimp, drawable); gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image)); gimp_set_clipboard_image (image->gimp, clip_image); g_object_unref (clip_image); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_CUT, C_("undo-type", "Cut Layer")); gimp_image_remove_layer (image, GIMP_LAYER (drawable), TRUE, NULL); gimp_image_undo_group_end (image); return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp)); } else { GimpBuffer *buffer; buffer = gimp_edit_extract (image, GIMP_PICKABLE (drawable), context, TRUE, error); if (buffer) { gimp_set_clipboard_buffer (image->gimp, buffer); g_object_unref (buffer); return GIMP_OBJECT (gimp_get_clipboard_buffer (image->gimp)); } } return NULL; }
static gboolean gimp_drawable_edit_can_fill_direct (GimpDrawable *drawable, GimpFillOptions *options) { GimpImage *image; GimpContext *context; gdouble opacity; GimpComponentMask affect; GimpLayerMode mode; GimpLayerCompositeMode composite_mode; GimpLayerCompositeRegion composite_region; image = gimp_item_get_image (GIMP_ITEM (drawable)); context = GIMP_CONTEXT (options); opacity = gimp_context_get_opacity (context); affect = gimp_drawable_get_active_mask (drawable); mode = gimp_context_get_paint_mode (context); composite_mode = gimp_layer_mode_get_paint_composite_mode (mode); composite_region = gimp_layer_mode_get_included_region (mode, composite_mode); if (gimp_channel_is_empty (gimp_image_get_mask (image)) && opacity == GIMP_OPACITY_OPAQUE && affect == GIMP_COMPONENT_MASK_ALL && gimp_layer_mode_is_trivial (mode) && (! gimp_layer_mode_is_subtractive (mode) ^ ! (composite_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE))) { switch (gimp_fill_options_get_style (options)) { case GIMP_FILL_STYLE_SOLID: return TRUE; case GIMP_FILL_STYLE_PATTERN: { GimpPattern *pattern; GimpTempBuf *mask; const Babl *format; pattern = gimp_context_get_pattern (context); mask = gimp_pattern_get_mask (pattern); format = gimp_temp_buf_get_format (mask); return ! babl_format_has_alpha (format); } } } return FALSE; }
void select_actions_update (GimpActionGroup *group, gpointer data) { GimpImage *image = action_data_get_image (data); GimpDrawable *drawable = NULL; gboolean fs = FALSE; gboolean sel = FALSE; gboolean writable = FALSE; gboolean children = FALSE; if (image) { drawable = gimp_image_get_active_drawable (image); if (drawable) { writable = ! gimp_item_is_content_locked (GIMP_ITEM (drawable)); if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) children = TRUE; } fs = (gimp_image_get_floating_selection (image) != NULL); sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); } #define SET_SENSITIVE(action,condition) \ gimp_action_group_set_action_sensitive (group, action, (condition) != 0) SET_SENSITIVE ("select-all", drawable); SET_SENSITIVE ("select-none", drawable && sel); SET_SENSITIVE ("select-invert", drawable); SET_SENSITIVE ("select-float", writable && !children && sel); SET_SENSITIVE ("select-feather", drawable && sel); SET_SENSITIVE ("select-sharpen", drawable && sel); SET_SENSITIVE ("select-shrink", drawable && sel); SET_SENSITIVE ("select-grow", drawable && sel); SET_SENSITIVE ("select-border", drawable && sel); SET_SENSITIVE ("select-flood", drawable && sel); SET_SENSITIVE ("select-save", drawable && !fs); SET_SENSITIVE ("select-stroke", writable && !children && sel); SET_SENSITIVE ("select-stroke-last-values", writable && !children && sel); #undef SET_SENSITIVE }
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); }
gboolean gimp_paint_core_start (GimpPaintCore *core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, GError **error) { GimpImage *image; GimpItem *item; GimpChannel *mask; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (coords != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); item = GIMP_ITEM (drawable); image = gimp_item_get_image (item); if (core->stroke_buffer) { g_array_free (core->stroke_buffer, TRUE); core->stroke_buffer = NULL; } core->stroke_buffer = g_array_sized_new (TRUE, TRUE, sizeof (GimpCoords), STROKE_BUFFER_INIT_SIZE); /* remember the last stroke's endpoint for later undo */ core->start_coords = core->last_coords; core->cur_coords = *coords; if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable, paint_options, coords, error)) { return FALSE; } /* Allocate the undo structure */ if (core->undo_buffer) g_object_unref (core->undo_buffer); core->undo_buffer = gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); /* Allocate the saved proj structure */ if (core->saved_proj_buffer) { g_object_unref (core->saved_proj_buffer); core->saved_proj_buffer = NULL; } if (core->use_saved_proj) { GeglBuffer *buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image)); core->saved_proj_buffer = gegl_buffer_dup (buffer); } /* Allocate the canvas blocks structure */ if (core->canvas_buffer) g_object_unref (core->canvas_buffer); core->canvas_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), babl_format ("Y float")); /* Get the initial undo extents */ core->x1 = core->x2 = core->cur_coords.x; core->y1 = core->y2 = core->cur_coords.y; core->last_paint.x = -1e6; core->last_paint.y = -1e6; mask = gimp_image_get_mask (image); /* don't apply the mask to itself and don't apply an empty mask */ if (GIMP_DRAWABLE (mask) != drawable && ! gimp_channel_is_empty (mask)) { GeglBuffer *mask_buffer; gint offset_x; gint offset_y; mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); gimp_item_get_offset (item, &offset_x, &offset_y); core->mask_buffer = g_object_ref (mask_buffer); core->mask_x_offset = -offset_x; core->mask_y_offset = -offset_y; } else { core->mask_buffer = NULL; } core->linear_mode = gimp_drawable_get_linear (drawable); if (paint_options->use_applicator) { core->applicator = gimp_applicator_new (NULL, core->linear_mode); if (core->mask_buffer) { gimp_applicator_set_mask_buffer (core->applicator, core->mask_buffer); gimp_applicator_set_mask_offset (core->applicator, core->mask_x_offset, core->mask_y_offset); } gimp_applicator_set_affect (core->applicator, gimp_drawable_get_active_mask (drawable)); gimp_applicator_set_dest_buffer (core->applicator, gimp_drawable_get_buffer (drawable)); } else { if (core->comp_buffer) { g_object_unref (core->comp_buffer); core->comp_buffer = NULL; } /* Allocate the scratch buffer if there's a component mask */ if (gimp_drawable_get_active_mask (drawable) != GIMP_COMPONENT_ALL) { const Babl *format; if (core->linear_mode) format = babl_format ("RGBA float"); else format = babl_format ("R'G'B'A float"); core->comp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), format); } } /* Freeze the drawable preview so that it isn't constantly updated. */ gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable)); return TRUE; }
void gimp_image_set_quick_mask_state (GimpImage *image, gboolean active) { GimpChannel *selection; GimpChannel *mask; gboolean channel_was_active; g_return_if_fail (GIMP_IS_IMAGE (image)); if (active == gimp_image_get_quick_mask_state (image)) return; /* Keep track of the state so that we can make the right drawable * active again when deactiviting quick mask (see bug #134371). */ if (image->quick_mask_state) channel_was_active = (image->quick_mask_state & CHANNEL_WAS_ACTIVE) != 0; else channel_was_active = gimp_image_get_active_channel (image) != NULL; /* Set image->quick_mask_state early so we can return early when * being called recursively. */ image->quick_mask_state = (active ? TRUE | (channel_was_active ? CHANNEL_WAS_ACTIVE : 0) : FALSE); selection = gimp_image_get_mask (image); mask = gimp_image_get_quick_mask (image); if (active) { if (! mask) { gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, _("Enable Quick Mask")); if (gimp_channel_is_empty (selection)) { /* if no selection */ GimpLayer *floating_sel = gimp_image_floating_sel (image); if (floating_sel) floating_sel_to_layer (floating_sel); mask = gimp_channel_new (image, image->width, image->height, GIMP_IMAGE_QUICK_MASK_NAME, &image->quick_mask_color); /* Clear the mask */ gimp_channel_clear (mask, NULL, FALSE); } else { /* if selection */ mask = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (selection), GIMP_TYPE_CHANNEL, FALSE)); /* Clear the selection */ gimp_channel_clear (selection, NULL, TRUE); gimp_channel_set_color (mask, &image->quick_mask_color, FALSE); gimp_item_rename (GIMP_ITEM (mask), GIMP_IMAGE_QUICK_MASK_NAME); } if (image->quick_mask_inverted) gimp_channel_invert (mask, FALSE); gimp_image_add_channel (image, mask, 0); gimp_image_undo_group_end (image); } } else { if (mask) { GimpLayer *floating_sel = gimp_image_floating_sel (image); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, _("Disable Quick Mask")); if (image->quick_mask_inverted) gimp_channel_invert (mask, TRUE); if (floating_sel && floating_sel->fs.drawable == GIMP_DRAWABLE (mask)) floating_sel_anchor (floating_sel); gimp_selection_load (gimp_image_get_mask (image), mask); gimp_image_remove_channel (image, mask); if (! channel_was_active) gimp_image_unset_active_channel (image); gimp_image_undo_group_end (image); } } gimp_image_quick_mask_changed (image); }
static void gimp_move_tool_button_press (GimpTool *tool, const GimpCoords *coords, guint32 time, GdkModifierType state, GimpButtonPressType press_type, GimpDisplay *display) { GimpMoveTool *move = GIMP_MOVE_TOOL (tool); GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool); GimpDisplayShell *shell = gimp_display_get_shell (display); GimpImage *image = gimp_display_get_image (display); tool->display = display; move->floating_layer = NULL; move->guide = NULL; move->moving_guide = FALSE; move->old_active_layer = NULL; move->old_active_vectors = NULL; if (! options->move_current) { if (options->move_type == GIMP_TRANSFORM_TYPE_PATH) { GimpVectors *vectors; if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display, coords, 7, 7, NULL, NULL, NULL, NULL, NULL, &vectors)) { move->old_active_vectors = gimp_image_get_active_vectors (image); gimp_image_set_active_vectors (image, vectors); } else { /* no path picked */ return; } } else if (options->move_type == GIMP_TRANSFORM_TYPE_LAYER) { GimpGuide *guide; GimpLayer *layer; const gint snap_distance = display->config->snap_distance; if (gimp_display_shell_get_show_guides (shell) && (guide = gimp_image_find_guide (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance)))) { move->guide = guide; move->moving_guide = TRUE; move->guide_position = gimp_guide_get_position (guide); move->guide_orientation = gimp_guide_get_orientation (guide); gimp_tool_control_set_scroll_lock (tool->control, TRUE); gimp_tool_control_set_precision (tool->control, GIMP_CURSOR_PRECISION_PIXEL_BORDER); gimp_tool_control_activate (tool->control); gimp_display_shell_selection_pause (shell); gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display); gimp_tool_push_status_length (tool, display, _("Move Guide: "), SWAP_ORIENT (move->guide_orientation), move->guide_position, NULL); return; } else if ((layer = gimp_image_pick_layer (image, coords->x, coords->y))) { if (gimp_image_get_floating_selection (image) && ! gimp_layer_is_floating_sel (layer)) { /* If there is a floating selection, and this aint it, * use the move tool to anchor it. */ move->floating_layer = gimp_image_get_floating_selection (image); gimp_tool_control_activate (tool->control); return; } else { move->old_active_layer = gimp_image_get_active_layer (image); gimp_image_set_active_layer (image, layer); } } else { /* no guide and no layer picked */ return; } } } switch (options->move_type) { case GIMP_TRANSFORM_TYPE_PATH: if (gimp_image_get_active_vectors (image)) gimp_edit_selection_tool_start (tool, display, coords, GIMP_TRANSLATE_MODE_VECTORS, TRUE); break; case GIMP_TRANSFORM_TYPE_SELECTION: if (! gimp_channel_is_empty (gimp_image_get_mask (image))) gimp_edit_selection_tool_start (tool, display, coords, GIMP_TRANSLATE_MODE_MASK, TRUE); break; case GIMP_TRANSFORM_TYPE_LAYER: { GimpDrawable *drawable = gimp_image_get_active_drawable (image); if (GIMP_IS_LAYER_MASK (drawable)) gimp_edit_selection_tool_start (tool, display, coords, GIMP_TRANSLATE_MODE_LAYER_MASK, TRUE); else if (GIMP_IS_CHANNEL (drawable)) gimp_edit_selection_tool_start (tool, display, coords, GIMP_TRANSLATE_MODE_CHANNEL, TRUE); else if (GIMP_IS_LAYER (drawable)) gimp_edit_selection_tool_start (tool, display, coords, GIMP_TRANSLATE_MODE_LAYER, TRUE); } break; } }
GimpDrawable * gimp_drawable_transform_rotate (GimpDrawable *drawable, GimpContext *context, GimpRotationType rotate_type, gdouble center_x, gdouble center_y, gboolean clip_result) { GimpImage *image; GeglBuffer *orig_buffer; gint orig_offset_x; gint orig_offset_y; gboolean new_layer; GimpDrawable *result = NULL; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); /* Start a transform undo group */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, C_("undo-type", "Rotate")); /* Cut/Copy from the specified drawable */ orig_buffer = gimp_drawable_transform_cut (drawable, context, &orig_offset_x, &orig_offset_y, &new_layer); if (orig_buffer) { GeglBuffer *new_buffer; gint new_offset_x; gint new_offset_y; /* always clip unfloated buffers so they keep their size */ if (GIMP_IS_CHANNEL (drawable) && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = TRUE; /* also transform the mask if we are transforming an entire layer */ if (GIMP_IS_LAYER (drawable) && gimp_layer_get_mask (GIMP_LAYER (drawable)) && gimp_channel_is_empty (gimp_image_get_mask (image))) { GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); gimp_item_rotate (GIMP_ITEM (mask), context, rotate_type, center_x, center_y, clip_result); } /* transform the buffer */ new_buffer = gimp_drawable_transform_buffer_rotate (drawable, context, orig_buffer, orig_offset_x, orig_offset_y, rotate_type, center_x, center_y, clip_result, &new_offset_x, &new_offset_y); /* Free the cut/copied buffer */ g_object_unref (orig_buffer); if (new_buffer) { result = gimp_drawable_transform_paste (drawable, new_buffer, new_offset_x, new_offset_y, new_layer); g_object_unref (new_buffer); } } /* push the undo group end */ gimp_image_undo_group_end (image); return result; }
static void gimp_selection_tool_oper_update (GimpTool *tool, const GimpCoords *coords, GdkModifierType state, gboolean proximity, GimpDisplay *display) { GimpSelectionTool *selection_tool = GIMP_SELECTION_TOOL (tool); GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool); GimpImage *image; GimpChannel *selection; GimpDrawable *drawable; GimpLayer *layer; GimpLayer *floating_sel; GdkModifierType extend_mask; GdkModifierType modify_mask; gboolean move_layer = FALSE; gboolean move_floating_sel = FALSE; gboolean selection_empty; image = gimp_display_get_image (display); selection = gimp_image_get_mask (image); drawable = gimp_image_get_active_drawable (image); layer = gimp_image_pick_layer (image, coords->x, coords->y); floating_sel = gimp_image_get_floating_selection (image); extend_mask = gimp_get_extend_selection_mask (); modify_mask = gimp_get_modify_selection_mask (); if (drawable) { if (floating_sel) { if (layer == floating_sel) move_floating_sel = TRUE; } else if (gimp_item_mask_intersect (GIMP_ITEM (drawable), NULL, NULL, NULL, NULL)) { move_layer = TRUE; } } selection_empty = gimp_channel_is_empty (selection); selection_tool->function = SELECTION_SELECT; if (selection_tool->allow_move && (state & GDK_MOD1_MASK) && (state & modify_mask) && move_layer) { /* move the selection */ selection_tool->function = SELECTION_MOVE; } else if (selection_tool->allow_move && (state & GDK_MOD1_MASK) && (state & extend_mask) && move_layer) { /* move a copy of the selection */ selection_tool->function = SELECTION_MOVE_COPY; } else if (selection_tool->allow_move && (state & GDK_MOD1_MASK) && ! selection_empty) { /* move the selection mask */ selection_tool->function = SELECTION_MOVE_MASK; } else if (selection_tool->allow_move && ! (state & (extend_mask | modify_mask)) && move_floating_sel) { /* move the selection */ selection_tool->function = SELECTION_MOVE; } else if ((state & modify_mask) || (state & extend_mask)) { /* select */ selection_tool->function = SELECTION_SELECT; } else if (floating_sel) { /* anchor the selection */ selection_tool->function = SELECTION_ANCHOR; } gimp_tool_pop_status (tool, display); if (proximity) { const gchar *status = NULL; gboolean free_status = FALSE; GdkModifierType modifiers = (extend_mask | modify_mask); if (! selection_empty) modifiers |= GDK_MOD1_MASK; switch (selection_tool->function) { case SELECTION_SELECT: switch (options->operation) { case GIMP_CHANNEL_OP_REPLACE: if (! selection_empty) { status = gimp_suggest_modifiers (_("Click-Drag to replace the " "current selection"), modifiers & ~state, NULL, NULL, NULL); free_status = TRUE; } else { status = _("Click-Drag to create a new selection"); } break; case GIMP_CHANNEL_OP_ADD: status = gimp_suggest_modifiers (_("Click-Drag to add to the " "current selection"), modifiers & ~(state | extend_mask), NULL, NULL, NULL); free_status = TRUE; break; case GIMP_CHANNEL_OP_SUBTRACT: status = gimp_suggest_modifiers (_("Click-Drag to subtract from the " "current selection"), modifiers & ~(state | modify_mask), NULL, NULL, NULL); free_status = TRUE; break; case GIMP_CHANNEL_OP_INTERSECT: status = gimp_suggest_modifiers (_("Click-Drag to intersect with " "the current selection"), modifiers & ~state, NULL, NULL, NULL); free_status = TRUE; break; } break; case SELECTION_MOVE_MASK: status = gimp_suggest_modifiers (_("Click-Drag to move the " "selection mask"), modifiers & ~state, NULL, NULL, NULL); free_status = TRUE; break; case SELECTION_MOVE: status = _("Click-Drag to move the selected pixels"); break; case SELECTION_MOVE_COPY: status = _("Click-Drag to move a copy of the selected pixels"); break; case SELECTION_ANCHOR: status = _("Click to anchor the floating selection"); break; default: g_return_if_reached (); } if (status) gimp_tool_push_status (tool, display, "%s", status); if (free_status) g_free ((gchar *) status); } }
/** * gimp_drawable_get_bucket_fill_buffer: * @drawable: the #GimpDrawable to edit. * @options: * @fill_transparent: * @fill_criterion: * @threshold: * @sample_merged: * @diagonal_neighbors: * @seed_x: X coordinate to start the fill. * @seed_y: Y coordinate to start the fill. * @mask_buffer: mask of the fill in-progress when in an interactive * filling process. Set to NULL if you need a one-time * fill. * @mask_x: returned x bound of @mask_buffer. * @mask_y: returned x bound of @mask_buffer. * @mask_width: returned width bound of @mask_buffer. * @mask_height: returned height bound of @mask_buffer. * * Creates the fill buffer for a bucket fill operation on @drawable, * without actually applying it (if you want to apply it directly as a * one-time operation, use gimp_drawable_bucket_fill() instead). If * @mask_buffer is not NULL, the intermediate fill mask will also be * returned. This fill mask can later be reused in successive calls to * gimp_drawable_get_bucket_fill_buffer() for interactive filling. * * Returns: a fill buffer which can be directly applied to @drawable, or * used in a drawable filter as preview. */ GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable, GimpFillOptions *options, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gboolean diagonal_neighbors, gdouble seed_x, gdouble seed_y, GeglBuffer **mask_buffer, gdouble *mask_x, gdouble *mask_y, gint *mask_width, gint *mask_height) { GimpImage *image; GimpPickable *pickable; GeglBuffer *buffer; GeglBuffer *new_mask; gboolean antialias; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_height; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return NULL; if (mask_buffer && *mask_buffer && threshold == 0.0) { gfloat pixel; gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (pixel != 0.0) /* Already selected. This seed won't change the selection. */ return NULL; } gimp_set_busy (image->gimp); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); antialias = gimp_fill_options_get_antialias (options); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ new_mask = gimp_pickable_contiguous_region_by_seed (pickable, antialias, threshold, fill_transparent, fill_criterion, diagonal_neighbors, (gint) seed_x, (gint) seed_y); if (mask_buffer && *mask_buffer) { gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, GIMP_CHANNEL_OP_ADD, 0, 0); g_object_unref (*mask_buffer); } if (mask_buffer) *mask_buffer = new_mask; gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, intersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { if (! mask_buffer) g_object_unref (new_mask); /* The fill region and the selection are disjoint; bail. */ gimp_unset_busy (image->gimp); return NULL; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height), -x, -y); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, -mask_offset_x, -mask_offset_y, 1.0); if (mask_x) *mask_x = x; if (mask_y) *mask_y = y; if (mask_width) *mask_width = width; if (mask_height) *mask_height = height; if (! mask_buffer) g_object_unref (new_mask); gimp_unset_busy (image->gimp); return buffer; }
void gimp_drawable_real_apply_buffer (GimpDrawable *drawable, GeglBuffer *buffer, const GeglRectangle *buffer_region, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GimpLayerModeEffects mode, GeglBuffer *base_buffer, gint base_x, gint base_y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); GeglBuffer *mask_buffer = NULL; GeglNode *apply; GimpComponentMask affect; gint x, y, width, height; gint offset_x, offset_y; /* 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; if (! base_buffer) base_buffer = gimp_drawable_get_buffer (drawable); /* get the layer offsets */ gimp_item_get_offset (item, &offset_x, &offset_y); /* make sure the image application coordinates are within drawable bounds */ gimp_rectangle_intersect (base_x, base_y, buffer_region->width, buffer_region->height, 0, 0, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); 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 */ gimp_rectangle_intersect (x, y, width, height, -offset_x, -offset_y, gimp_item_get_width (mask_item), gimp_item_get_height (mask_item), &x, &y, &width, &height); } if (push_undo) { GimpDrawableUndo *undo; gimp_drawable_push_undo (drawable, undo_desc, NULL, x, y, width, height); undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); if (undo) { undo->paint_mode = mode; undo->opacity = opacity; undo->applied_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gegl_buffer_get_format (buffer)); gegl_buffer_copy (buffer, GEGL_RECTANGLE (buffer_region->x + (x - base_x), buffer_region->y + (y - base_y), width, height), undo->applied_buffer, GEGL_RECTANGLE (0, 0, width, height)); } } if (mask) mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); affect = gimp_drawable_get_active_mask (drawable); apply = gimp_gegl_create_apply_buffer_node (buffer, base_x - buffer_region->x, base_y - buffer_region->y, 0, 0, 0, 0, mask_buffer, -offset_x, -offset_y, opacity, mode, affect); gimp_gegl_apply_operation (base_buffer, NULL, NULL, apply, gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x, y, width, height)); g_object_unref (apply); }
/* 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_buffer (GimpDrawable *drawable, GeglBuffer *buffer, const GeglRectangle *buffer_region, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GeglBuffer *mask_buffer, const GeglRectangle *mask_buffer_region, gint dest_x, gint dest_y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); GeglBuffer *drawable_buffer; gint x, y, width, height; gint offset_x, offset_y; 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); /* get the layer offsets */ gimp_item_get_offset (item, &offset_x, &offset_y); /* make sure the image application coordinates are within drawable bounds */ gimp_rectangle_intersect (dest_x, dest_y, buffer_region->width, buffer_region->height, 0, 0, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); 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 */ gimp_rectangle_intersect (x, y, width, height, -offset_x, -offset_y, gimp_item_get_width (mask_item), gimp_item_get_height (mask_item), &x, &y, &width, &height); } /* If the calling procedure specified an undo step... */ if (push_undo) gimp_drawable_push_undo (drawable, undo_desc, NULL, x, y, width, height); drawable_buffer = gimp_drawable_get_buffer (drawable); if (mask) { GeglBuffer *src_buffer; GeglBuffer *dest_buffer; src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); dest_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gegl_buffer_get_format (src_buffer)); gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (x + offset_x, y + offset_y, width, height), dest_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gimp_gegl_combine_mask (mask_buffer, mask_buffer_region, dest_buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0); gimp_gegl_replace (buffer, buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), dest_buffer, GEGL_RECTANGLE (0, 0, width, height), drawable_buffer, GEGL_RECTANGLE (x, y, width, height), opacity, active_components); g_object_unref (dest_buffer); } else { gimp_gegl_replace (buffer, buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), mask_buffer, mask_buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), opacity, active_components); } }
void gimp_drawable_bucket_fill (GimpDrawable *drawable, GimpFillOptions *options, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gboolean diagonal_neighbors, gdouble seed_x, gdouble seed_y) { GimpImage *image; GimpPickable *pickable; GeglBuffer *buffer; GeglBuffer *mask_buffer; gboolean antialias; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_height; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return; gimp_set_busy (image->gimp); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); antialias = gimp_fill_options_get_antialias (options); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ mask_buffer = gimp_pickable_contiguous_region_by_seed (pickable, antialias, threshold, fill_transparent, fill_criterion, diagonal_neighbors, (gint) seed_x, (gint) seed_y); gimp_gegl_mask_bounds (mask_buffer, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, inersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { /* The fill region and the selection are disjoint; bail. */ g_object_unref (mask_buffer); gimp_unset_busy (image->gimp); return; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height)); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, mask_buffer, -mask_offset_x, -mask_offset_y, 1.0); g_object_unref (mask_buffer); /* Apply it to the image */ gimp_drawable_apply_buffer (drawable, buffer, GEGL_RECTANGLE (0, 0, width, height), TRUE, C_("undo-type", "Bucket Fill"), gimp_context_get_opacity (GIMP_CONTEXT (options)), gimp_context_get_paint_mode (GIMP_CONTEXT (options)), NULL, x, y); g_object_unref (buffer); gimp_drawable_update (drawable, x, y, width, height); gimp_unset_busy (image->gimp); }
GimpLayer * gimp_edit_paste (GimpImage *image, GimpDrawable *drawable, GimpBuffer *paste, gboolean paste_into, gint viewport_x, gint viewport_y, gint viewport_width, gint viewport_height) { GimpLayer *layer; const Babl *format; gint image_width; gint image_height; gint width; gint height; gint offset_x; gint offset_y; gboolean clamp_to_image = TRUE; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (drawable == NULL || gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_BUFFER (paste), NULL); /* Make a new layer: if drawable == NULL, * user is pasting into an empty image. */ if (drawable) format = gimp_drawable_get_format_with_alpha (drawable); else format = gimp_image_get_layer_format (image, TRUE); layer = gimp_layer_new_from_buffer (paste, image, format, _("Pasted Layer"), GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); if (! layer) return NULL; image_width = gimp_image_get_width (image); image_height = gimp_image_get_height (image); width = gimp_item_get_width (GIMP_ITEM (layer)); height = gimp_item_get_height (GIMP_ITEM (layer)); if (viewport_width == image_width && viewport_height == image_height) { /* if the whole image is visible, act as if there was no viewport */ viewport_x = 0; viewport_y = 0; viewport_width = 0; viewport_height = 0; } if (drawable) { /* if pasting to a drawable */ gint off_x, off_y; gint x1, y1, x2, y2; gint paste_x, paste_y; gint paste_width, paste_height; gboolean have_mask; gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); have_mask = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2); if (! have_mask && /* if we have no mask */ viewport_width > 0 && /* and we have a viewport */ viewport_height > 0 && (width < (x2 - x1) || /* and the paste is smaller than the target */ height < (y2 - y1)) && /* and the viewport intersects with the target */ gimp_rectangle_intersect (viewport_x, viewport_y, viewport_width, viewport_height, off_x, off_y, x2 - x1, y2 - y1, &paste_x, &paste_y, &paste_width, &paste_height)) { /* center on the viewport */ offset_x = paste_x + (paste_width - width) / 2; offset_y = paste_y + (paste_height- height) / 2; } else { /* otherwise center on the target */ offset_x = off_x + ((x1 + x2) - width) / 2; offset_y = off_y + ((y1 + y2) - height) / 2; /* and keep it that way */ clamp_to_image = FALSE; } } else if (viewport_width > 0 && /* if we have a viewport */ viewport_height > 0 && (width < image_width || /* and the paste is */ height < image_height)) /* smaller than the image */ { /* center on the viewport */ offset_x = viewport_x + (viewport_width - width) / 2; offset_y = viewport_y + (viewport_height - height) / 2; } else { /* otherwise center on the image */ offset_x = (image_width - width) / 2; offset_y = (image_height - height) / 2; /* and keep it that way */ clamp_to_image = FALSE; } if (clamp_to_image) { /* Ensure that the pasted layer is always within the image, if it * fits and aligned at top left if it doesn't. (See bug #142944). */ offset_x = MIN (offset_x, image_width - width); offset_y = MIN (offset_y, image_height - height); offset_x = MAX (offset_x, 0); offset_y = MAX (offset_y, 0); } gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y); /* Start a group undo */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, C_("undo-type", "Paste")); /* If there is a selection mask clear it-- * this might not always be desired, but in general, * it seems like the correct behavior. */ if (! gimp_channel_is_empty (gimp_image_get_mask (image)) && ! paste_into) gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); /* if there's a drawable, add a new floating selection */ if (drawable) floating_sel_attach (layer, drawable); else gimp_image_add_layer (image, layer, NULL, 0, TRUE); /* end the group undo */ gimp_image_undo_group_end (image); return layer; }
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)); }
/** * gimp_drawable_get_line_art_fill_buffer: * @drawable: the #GimpDrawable to edit. * @line_art: the #GimpLineArt computed as fill source. * @options: the #GimpFillOptions. * @sample_merged: * @seed_x: X coordinate to start the fill. * @seed_y: Y coordinate to start the fill. * @mask_buffer: mask of the fill in-progress when in an interactive * filling process. Set to NULL if you need a one-time * fill. * @mask_x: returned x bound of @mask_buffer. * @mask_y: returned x bound of @mask_buffer. * @mask_width: returned width bound of @mask_buffer. * @mask_height: returned height bound of @mask_buffer. * * Creates the fill buffer for a bucket fill operation on @drawable * based on @line_art and @options, without actually applying it. * If @mask_buffer is not NULL, the intermediate fill mask will also be * returned. This fill mask can later be reused in successive calls to * gimp_drawable_get_bucket_fill_buffer() for interactive filling. * * Returns: a fill buffer which can be directly applied to @drawable, or * used in a drawable filter as preview. */ GeglBuffer * gimp_drawable_get_line_art_fill_buffer (GimpDrawable *drawable, GimpLineArt *line_art, GimpFillOptions *options, gboolean sample_merged, gdouble seed_x, gdouble seed_y, GeglBuffer **mask_buffer, gdouble *mask_x, gdouble *mask_y, gint *mask_width, gint *mask_height) { GimpImage *image; GeglBuffer *buffer; GeglBuffer *new_mask; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_height; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return NULL; if (mask_buffer && *mask_buffer) { gfloat pixel; gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (pixel != 0.0) /* Already selected. This seed won't change the selection. */ return NULL; } gimp_set_busy (image->gimp); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ new_mask = gimp_pickable_contiguous_region_by_line_art (NULL, line_art, (gint) seed_x, (gint) seed_y); if (mask_buffer && *mask_buffer) { gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, GIMP_CHANNEL_OP_ADD, 0, 0); g_object_unref (*mask_buffer); } if (mask_buffer) *mask_buffer = new_mask; gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, intersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { if (! mask_buffer) g_object_unref (new_mask); /* The fill region and the selection are disjoint; bail. */ gimp_unset_busy (image->gimp); return NULL; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height), -x, -y); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, -mask_offset_x, -mask_offset_y, 1.0); if (gimp_fill_options_get_antialias (options)) { /* Antialias for the line art algorithm is not applied during mask * creation because it is not based on individual pixel colors. * Instead we just want to apply it on the borders of the mask at * the end (since the mask can evolve, we don't want to actually * touch it, but only the intermediate results). */ GeglNode *graph; GeglNode *input; GeglNode *op; graph = gegl_node_new (); input = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", buffer, NULL); op = gegl_node_new_child (graph, "operation", "gegl:gaussian-blur", "std-dev-x", 0.5, "std-dev-y", 0.5, NULL); gegl_node_connect_to (input, "output", op, "input"); gegl_node_blit_buffer (op, buffer, NULL, 0, GEGL_ABYSS_NONE); g_object_unref (graph); } if (mask_x) *mask_x = x; if (mask_y) *mask_y = y; if (mask_width) *mask_width = width; if (mask_height) *mask_height = height; if (! mask_buffer) g_object_unref (new_mask); gimp_unset_busy (image->gimp); return buffer; }
void layers_actions_update (GimpActionGroup *group, gpointer data) { GimpImage *image = action_data_get_image (data); GimpLayer *layer = NULL; GimpLayerMask *mask = NULL; /* layer mask */ gboolean fs = FALSE; /* floating sel */ gboolean ac = FALSE; /* active channel */ gboolean sel = FALSE; gboolean alpha = FALSE; /* alpha channel present */ gboolean indexed = FALSE; /* is indexed */ gboolean lock_alpha = FALSE; gboolean can_lock_alpha = FALSE; gboolean text_layer = FALSE; gboolean writable = FALSE; gboolean children = FALSE; GList *next = NULL; GList *next_visible = NULL; GList *prev = NULL; if (image) { fs = (gimp_image_get_floating_selection (image) != NULL); ac = (gimp_image_get_active_channel (image) != NULL); sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); indexed = (gimp_image_base_type (image) == GIMP_INDEXED); layer = gimp_image_get_active_layer (image); if (layer) { GList *layer_list; GList *list; mask = gimp_layer_get_mask (layer); lock_alpha = gimp_layer_get_lock_alpha (layer); can_lock_alpha = gimp_layer_can_lock_alpha (layer); alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)); writable = ! gimp_item_is_content_locked (GIMP_ITEM (layer)); if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) children = TRUE; layer_list = gimp_item_get_container_iter (GIMP_ITEM (layer)); list = g_list_find (layer_list, layer); if (list) { prev = g_list_previous (list); next = g_list_next (list); for (next_visible = next; next_visible; next_visible = g_list_next (next_visible)) { if (gimp_item_get_visible (next_visible->data)) { /* "next_visible" is actually "next_visible" and * "writable" and "not group" */ if (gimp_item_is_content_locked (next_visible->data) || gimp_viewable_get_children (next_visible->data)) next_visible = NULL; break; } } } text_layer = gimp_item_is_text_layer (GIMP_ITEM (layer)); } } #define SET_VISIBLE(action,condition) \ gimp_action_group_set_action_visible (group, action, (condition) != 0) #define SET_SENSITIVE(action,condition) \ gimp_action_group_set_action_sensitive (group, action, (condition) != 0) #define SET_ACTIVE(action,condition) \ gimp_action_group_set_action_active (group, action, (condition) != 0) #define SET_LABEL(action,label) \ gimp_action_group_set_action_label (group, action, label) SET_VISIBLE ("layers-text-tool", text_layer && !ac); SET_SENSITIVE ("layers-edit-attributes", layer && !fs && !ac); if (layer && gimp_layer_is_floating_sel (layer)) { SET_LABEL ("layers-new", C_("layers-action", "To _New Layer")); SET_LABEL ("layers-new-last-values", C_("layers-action", "To _New Layer")); } else { SET_LABEL ("layers-new", C_("layers-action", "_New Layer...")); SET_LABEL ("layers-new-last-values", C_("layers-action", "_New Layer")); } SET_SENSITIVE ("layers-new", image); SET_SENSITIVE ("layers-new-last-values", image); SET_SENSITIVE ("layers-new-from-visible", image); SET_SENSITIVE ("layers-new-group", image && !indexed); SET_SENSITIVE ("layers-duplicate", layer && !fs && !ac); SET_SENSITIVE ("layers-delete", layer && !ac); SET_SENSITIVE ("layers-select-top", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-select-bottom", layer && !fs && !ac && next); SET_SENSITIVE ("layers-select-previous", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-select-next", layer && !fs && !ac && next); SET_SENSITIVE ("layers-raise", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-raise-to-top", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-lower", layer && !fs && !ac && next); SET_SENSITIVE ("layers-lower-to-bottom", layer && !fs && !ac && next); SET_SENSITIVE ("layers-anchor", layer && fs && !ac); SET_SENSITIVE ("layers-merge-down", layer && !fs && !ac && next_visible); SET_VISIBLE ("layers-merge-group", children); SET_SENSITIVE ("layers-merge-group", layer && !fs && !ac && children); SET_SENSITIVE ("layers-merge-layers", layer && !fs && !ac); SET_SENSITIVE ("layers-flatten-image", layer && !fs && !ac); SET_VISIBLE ("layers-text-discard", text_layer && !ac); SET_VISIBLE ("layers-text-to-vectors", text_layer && !ac); SET_VISIBLE ("layers-text-along-vectors", text_layer && !ac); SET_SENSITIVE ("layers-resize", writable && !ac); SET_SENSITIVE ("layers-resize-to-image", writable && !ac); SET_SENSITIVE ("layers-scale", writable && !ac); SET_SENSITIVE ("layers-crop", writable && sel); SET_SENSITIVE ("layers-alpha-add", writable && !children && !fs && !alpha); SET_SENSITIVE ("layers-alpha-remove", writable && !children && !fs && alpha); SET_SENSITIVE ("layers-lock-alpha", can_lock_alpha); SET_ACTIVE ("layers-lock-alpha", lock_alpha); SET_SENSITIVE ("layers-mask-add", layer && !fs && !ac && !mask && !children); SET_SENSITIVE ("layers-mask-apply", writable && !fs && !ac && mask && !children); SET_SENSITIVE ("layers-mask-delete", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-edit", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-show", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-disable", layer && !fs && !ac && mask); SET_ACTIVE ("layers-mask-edit", mask && gimp_layer_mask_get_edit (mask)); SET_ACTIVE ("layers-mask-show", mask && gimp_layer_mask_get_show (mask)); SET_ACTIVE ("layers-mask-disable", mask && !gimp_layer_mask_get_apply (mask)); SET_SENSITIVE ("layers-mask-selection-replace", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-add", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-subtract", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-intersect", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-alpha-selection-replace", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-add", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-subtract", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-intersect", layer && !fs && !ac); #undef SET_VISIBLE #undef SET_SENSITIVE #undef SET_ACTIVE #undef SET_LABEL }
GimpDrawable * gimp_drawable_transform_affine (GimpDrawable *drawable, GimpContext *context, const GimpMatrix3 *matrix, GimpTransformDirection direction, GimpInterpolationType interpolation_type, gint recursion_level, GimpTransformResize clip_result, GimpProgress *progress) { GimpImage *image; GeglBuffer *orig_buffer; gint orig_offset_x; gint orig_offset_y; gboolean new_layer; GimpDrawable *result = NULL; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (matrix != NULL, NULL); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); /* Start a transform undo group */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, C_("undo-type", "Transform")); /* Cut/Copy from the specified drawable */ orig_buffer = gimp_drawable_transform_cut (drawable, context, &orig_offset_x, &orig_offset_y, &new_layer); if (orig_buffer) { GeglBuffer *new_buffer; gint new_offset_x; gint new_offset_y; /* always clip unfloated buffers so they keep their size */ if (GIMP_IS_CHANNEL (drawable) && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; /* also transform the mask if we are transforming an entire layer */ if (GIMP_IS_LAYER (drawable) && gimp_layer_get_mask (GIMP_LAYER (drawable)) && gimp_channel_is_empty (gimp_image_get_mask (image))) { GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); gimp_item_transform (GIMP_ITEM (mask), context, matrix, direction, interpolation_type, recursion_level, clip_result, progress); } /* transform the buffer */ new_buffer = gimp_drawable_transform_buffer_affine (drawable, context, orig_buffer, orig_offset_x, orig_offset_y, matrix, direction, interpolation_type, recursion_level, clip_result, &new_offset_x, &new_offset_y, progress); /* Free the cut/copied buffer */ g_object_unref (orig_buffer); if (new_buffer) { result = gimp_drawable_transform_paste (drawable, new_buffer, new_offset_x, new_offset_y, new_layer); g_object_unref (new_buffer); } } /* push the undo group end */ gimp_image_undo_group_end (image); return result; }
void layers_actions_update (GimpActionGroup *group, gpointer data) { GimpImage *image = action_data_get_image (data); GimpLayer *layer = NULL; GimpLayerMask *mask = NULL; /* layer mask */ gboolean fs = FALSE; /* floating sel */ gboolean ac = FALSE; /* active channel */ gboolean sel = FALSE; gboolean alpha = FALSE; /* alpha channel present */ gboolean indexed = FALSE; /* is indexed */ gboolean lock_alpha = FALSE; gboolean text_layer = FALSE; GList *next = NULL; GList *prev = NULL; if (image) { fs = (gimp_image_floating_sel (image) != NULL); ac = (gimp_image_get_active_channel (image) != NULL); sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); indexed = (gimp_image_base_type (image) == GIMP_INDEXED); layer = gimp_image_get_active_layer (image); if (layer) { GList *list; mask = gimp_layer_get_mask (layer); lock_alpha = gimp_layer_get_lock_alpha (layer); alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)); list = g_list_find (GIMP_LIST (image->layers)->list, layer); if (list) { prev = g_list_previous (list); next = g_list_next (list); } if (layer) text_layer = gimp_drawable_is_text_layer (GIMP_DRAWABLE (layer)); } } #define SET_VISIBLE(action,condition) \ gimp_action_group_set_action_visible (group, action, (condition) != 0) #define SET_SENSITIVE(action,condition) \ gimp_action_group_set_action_sensitive (group, action, (condition) != 0) #define SET_ACTIVE(action,condition) \ gimp_action_group_set_action_active (group, action, (condition) != 0) SET_VISIBLE ("layers-text-tool", text_layer && !ac); SET_SENSITIVE ("layers-edit-attributes", layer && !fs && !ac); SET_SENSITIVE ("layers-new", image); SET_SENSITIVE ("layers-new-last-values", image); SET_SENSITIVE ("layers-duplicate", layer && !fs && !ac); SET_SENSITIVE ("layers-delete", layer && !ac); SET_SENSITIVE ("layers-select-top", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-select-bottom", layer && !fs && !ac && next); SET_SENSITIVE ("layers-select-previous", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-select-next", layer && !fs && !ac && next); SET_SENSITIVE ("layers-raise", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-raise-to-top", layer && !fs && !ac && prev); SET_SENSITIVE ("layers-lower", layer && !fs && !ac && next); SET_SENSITIVE ("layers-lower-to-bottom", layer && !fs && !ac && next); SET_SENSITIVE ("layers-anchor", layer && fs && !ac); SET_SENSITIVE ("layers-merge-down", layer && !fs && !ac && next); SET_SENSITIVE ("layers-merge-layers", layer && !fs && !ac); SET_SENSITIVE ("layers-flatten-image", layer && !fs && !ac); SET_VISIBLE ("layers-text-discard", text_layer && !ac); SET_VISIBLE ("layers-text-to-vectors", text_layer && !ac); SET_VISIBLE ("layers-text-along-vectors", text_layer && !ac); SET_VISIBLE ("layers-text-selection-replace", text_layer && !ac); SET_VISIBLE ("layers-text-selection-add", text_layer && !ac); SET_VISIBLE ("layers-text-selection-subtract", text_layer && !ac); SET_VISIBLE ("layers-text-selection-intersect", text_layer && !ac); SET_SENSITIVE ("layers-resize", layer && !ac); SET_SENSITIVE ("layers-resize-to-image", layer && !ac); SET_SENSITIVE ("layers-scale", layer && !ac); SET_SENSITIVE ("layers-crop", layer && sel); SET_SENSITIVE ("layers-alpha-add", layer && !fs && !alpha); SET_SENSITIVE ("layers-alpha-remove", layer && !fs && alpha); SET_SENSITIVE ("layers-lock-alpha", layer); SET_ACTIVE ("layers-lock-alpha", lock_alpha); SET_SENSITIVE ("layers-mask-add", layer && !fs && !ac && !mask); SET_SENSITIVE ("layers-mask-apply", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-delete", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-edit", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-show", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-disable", layer && !fs && !ac && mask); SET_ACTIVE ("layers-mask-edit", mask && gimp_layer_mask_get_edit (mask)); SET_ACTIVE ("layers-mask-show", mask && gimp_layer_mask_get_show (mask)); SET_ACTIVE ("layers-mask-disable", mask && !gimp_layer_mask_get_apply (mask)); SET_SENSITIVE ("layers-mask-selection-replace", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-add", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-subtract", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-mask-selection-intersect", layer && !fs && !ac && mask); SET_SENSITIVE ("layers-alpha-selection-replace", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-add", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-subtract", layer && !fs && !ac); SET_SENSITIVE ("layers-alpha-selection-intersect", layer && !fs && !ac); #undef SET_VISIBLE #undef SET_SENSITIVE #undef SET_ACTIVE }
GeglBuffer * gimp_drawable_transform_cut (GimpDrawable *drawable, GimpContext *context, gint *offset_x, gint *offset_y, gboolean *new_layer) { GimpImage *image; GeglBuffer *buffer; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (offset_x != NULL, NULL); g_return_val_if_fail (offset_y != NULL, NULL); g_return_val_if_fail (new_layer != NULL, NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); /* extract the selected mask if there is a selection */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint x, y, w, h; /* set the keep_indexed flag to FALSE here, since we use * gimp_layer_new_from_buffer() later which assumes that the buffer * are either RGB or GRAY. Eeek!!! (Sven) */ if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h)) { buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)), GIMP_PICKABLE (drawable), context, TRUE, FALSE, TRUE, offset_x, offset_y, NULL); /* clear the selection */ gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); *new_layer = TRUE; } else { buffer = NULL; *new_layer = FALSE; } } else /* otherwise, just copy the layer */ { buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)), GIMP_PICKABLE (drawable), context, FALSE, TRUE, GIMP_IS_LAYER (drawable), offset_x, offset_y, NULL); *new_layer = FALSE; } return buffer; }
/* 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); } }
static void gimp_move_tool_cursor_update (GimpTool *tool, const GimpCoords *coords, GdkModifierType state, GimpDisplay *display) { GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool); GimpDisplayShell *shell = gimp_display_get_shell (display); GimpImage *image = gimp_display_get_image (display); GimpCursorType cursor = GIMP_CURSOR_MOUSE; GimpToolCursorType tool_cursor = GIMP_TOOL_CURSOR_MOVE; GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE; if (options->move_type == GIMP_TRANSFORM_TYPE_PATH) { tool_cursor = GIMP_TOOL_CURSOR_PATHS; modifier = GIMP_CURSOR_MODIFIER_MOVE; if (options->move_current) { if (! gimp_image_get_active_vectors (image)) modifier = GIMP_CURSOR_MODIFIER_BAD; } else { if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display, coords, 7, 7, NULL, NULL, NULL, NULL, NULL, NULL)) { tool_cursor = GIMP_TOOL_CURSOR_HAND; } else { modifier = GIMP_CURSOR_MODIFIER_BAD; } } } else if (options->move_type == GIMP_TRANSFORM_TYPE_SELECTION) { tool_cursor = GIMP_TOOL_CURSOR_RECT_SELECT; modifier = GIMP_CURSOR_MODIFIER_MOVE; if (gimp_channel_is_empty (gimp_image_get_mask (image))) modifier = GIMP_CURSOR_MODIFIER_BAD; } else if (options->move_current) { if (! gimp_image_get_active_drawable (image)) modifier = GIMP_CURSOR_MODIFIER_BAD; } else { GimpGuide *guide; GimpLayer *layer; const gint snap_distance = display->config->snap_distance; if (gimp_display_shell_get_show_guides (shell) && (guide = gimp_image_find_guide (image, coords->x, coords->y, FUNSCALEX (shell, snap_distance), FUNSCALEY (shell, snap_distance)))) { tool_cursor = GIMP_TOOL_CURSOR_HAND; modifier = GIMP_CURSOR_MODIFIER_MOVE; } else if ((layer = gimp_image_pick_layer (image, coords->x, coords->y))) { /* if there is a floating selection, and this aint it... */ if (gimp_image_get_floating_selection (image) && ! gimp_layer_is_floating_sel (layer)) { tool_cursor = GIMP_TOOL_CURSOR_MOVE; modifier = GIMP_CURSOR_MODIFIER_ANCHOR; } else if (layer != gimp_image_get_active_layer (image)) { tool_cursor = GIMP_TOOL_CURSOR_HAND; modifier = GIMP_CURSOR_MODIFIER_MOVE; } } else { modifier = GIMP_CURSOR_MODIFIER_BAD; } } gimp_tool_control_set_cursor (tool->control, cursor); gimp_tool_control_set_tool_cursor (tool->control, tool_cursor); gimp_tool_control_set_cursor_modifier (tool->control, modifier); GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); }
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); } }
GimpLayer * text_render (GimpImage *image, GimpDrawable *drawable, GimpContext *context, gint text_x, gint text_y, const gchar *fontname, const gchar *text, gint border, gboolean antialias) { PangoFontDescription *desc; GimpText *gtext; GimpLayer *layer; GimpRGB color; gchar *font; gdouble size; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (drawable == NULL || gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (fontname != NULL, NULL); g_return_val_if_fail (text != NULL, NULL); if (border < 0) border = 0; desc = pango_font_description_from_string (fontname); size = PANGO_PIXELS (pango_font_description_get_size (desc)); pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE); font = pango_font_description_to_string (desc); pango_font_description_free (desc); gimp_context_get_foreground (context, &color); gtext = g_object_new (GIMP_TYPE_TEXT, "text", text, "font", font, "font-size", size, "antialias", antialias, "border", border, "color", &color, NULL); g_free (font); layer = gimp_text_layer_new (image, gtext); g_object_unref (gtext); if (!layer) return NULL; /* Start a group undo */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, _("Add Text Layer")); /* Set the layer offsets */ gimp_item_set_offset (GIMP_ITEM (layer), text_x, text_y); /* If there is a selection mask clear it-- * this might not always be desired, but in general, * it seems like the correct behavior. */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE); if (drawable == NULL) { /* If the drawable is NULL, create a new layer */ gimp_image_add_layer (image, layer, NULL, -1, TRUE); } else { /* Otherwise, instantiate the text as the new floating selection */ floating_sel_attach (layer, drawable); } /* end the group undo */ gimp_image_undo_group_end (image); return layer; }
gboolean gimp_paint_core_start (GimpPaintCore *core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, GError **error) { GimpItem *item; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (coords != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); item = GIMP_ITEM (drawable); if (core->stroke_buffer) { g_array_free (core->stroke_buffer, TRUE); core->stroke_buffer = NULL; } core->stroke_buffer = g_array_sized_new (TRUE, TRUE, sizeof (GimpCoords), STROKE_BUFFER_INIT_SIZE); core->cur_coords = *coords; if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable, paint_options, coords, error)) { return FALSE; } /* Allocate the undo structure */ if (core->undo_buffer) g_object_unref (core->undo_buffer); core->undo_buffer = gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); /* Allocate the saved proj structure */ if (core->saved_proj_buffer) { g_object_unref (core->saved_proj_buffer); core->saved_proj_buffer = NULL; } if (core->use_saved_proj) { GimpImage *image = gimp_item_get_image (item); GimpPickable *pickable = GIMP_PICKABLE (gimp_image_get_projection (image)); GeglBuffer *buffer = gimp_pickable_get_buffer (pickable); core->saved_proj_buffer = gegl_buffer_dup (buffer); } /* Allocate the canvas blocks structure */ if (core->canvas_buffer) g_object_unref (core->canvas_buffer); core->canvas_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), babl_format ("Y u8")); /* Get the initial undo extents */ core->x1 = core->x2 = core->cur_coords.x; core->y1 = core->y2 = core->cur_coords.y; core->last_paint.x = -1e6; core->last_paint.y = -1e6; { GimpImage *image; GimpChannel *mask; GeglBuffer *mask_buffer = NULL; gint offset_x = 0; gint offset_y = 0; image = gimp_item_get_image (item); mask = gimp_image_get_mask (image); /* don't apply the mask to itself and don't apply an empty mask */ if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask)) mask = NULL; if (mask) { mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); gimp_item_get_offset (item, &offset_x, &offset_y); } core->applicator = gimp_applicator_new (gimp_drawable_get_buffer (drawable), gimp_drawable_get_active_mask (drawable), mask_buffer, -offset_x, -offset_y); } /* Freeze the drawable preview so that it isn't constantly updated. */ gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable)); return TRUE; }
static GeglBuffer * gradient_precalc_shapeburst (GimpImage *image, GimpDrawable *drawable, const GeglRectangle *region, gdouble dist, GimpProgress *progress) { GimpChannel *mask; GeglBuffer *dist_buffer; GeglBuffer *temp_buffer; GeglNode *shapeburst; gdouble max; gfloat max_iteration; gimp_progress_set_text (progress, _("Calculating distance map")); /* allocate the distance map */ dist_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, region->width, region->height), babl_format ("Y float")); /* allocate the selection mask copy * XXX: its format should be the same of gimp:shapeburst input buffer * porting the op to 'float' should be reflected here as well */ temp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, region->width, region->height), babl_format ("Y u8")); mask = gimp_image_get_mask (image); /* If the image mask is not empty, use it as the shape burst source */ if (! gimp_channel_is_empty (mask)) { gint x, y, width, height; gint off_x, off_y; gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height); gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); /* copy the mask to the temp mask */ gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), GEGL_RECTANGLE (x + off_x, y + off_y, width, height), temp_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); } else { /* If the intended drawable has an alpha channel, use that */ if (gimp_drawable_has_alpha (drawable)) { const Babl *component_format; component_format = babl_format ("A u8"); /* extract the aplha into the temp mask */ gegl_buffer_set_format (temp_buffer, component_format); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (region->x, region->y, region->width, region->height), temp_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gegl_buffer_set_format (temp_buffer, NULL); } else { GeglColor *white = gegl_color_new ("white"); /* Otherwise, just fill the shapeburst to white */ gegl_buffer_set_color (temp_buffer, NULL, white); g_object_unref (white); } } shapeburst = gegl_node_new_child (NULL, "operation", "gimp:shapeburst", NULL); gimp_gegl_progress_connect (shapeburst, progress, NULL); gimp_gegl_apply_operation (temp_buffer, NULL, NULL, shapeburst, dist_buffer, NULL); gegl_node_get (shapeburst, "max-iterations", &max, NULL); g_object_unref (shapeburst); max_iteration = max; g_object_unref (temp_buffer); /* normalize the shapeburst with the max iteration */ if (max_iteration > 0) { GeglBufferIterator *iter; iter = gegl_buffer_iterator_new (dist_buffer, NULL, 0, NULL, GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { gint count = iter->length; gfloat *data = iter->data[0]; while (count--) *data++ /= max_iteration; } } return dist_buffer; }
void image_actions_update (GimpActionGroup *group, gpointer data) { GimpImage *image = action_data_get_image (data); gboolean is_indexed = FALSE; gboolean is_u8_gamma = FALSE; gboolean aux = FALSE; gboolean lp = FALSE; gboolean sel = FALSE; gboolean groups = FALSE; gboolean profile = FALSE; if (image) { GimpContainer *layers; const gchar *action = NULL; switch (gimp_image_get_base_type (image)) { case GIMP_RGB: action = "image-convert-rgb"; break; case GIMP_GRAY: action = "image-convert-grayscale"; break; case GIMP_INDEXED: action = "image-convert-indexed"; break; } gimp_action_group_set_action_active (group, action, TRUE); switch (gimp_image_get_precision (image)) { case GIMP_PRECISION_U8_LINEAR: action = "image-convert-u8-linear"; break; case GIMP_PRECISION_U8_GAMMA: action = "image-convert-u8-gamma"; break; case GIMP_PRECISION_U16_LINEAR: action = "image-convert-u16-linear"; break; case GIMP_PRECISION_U16_GAMMA: action = "image-convert-u16-gamma"; break; case GIMP_PRECISION_U32_LINEAR: action = "image-convert-u32-linear"; break; case GIMP_PRECISION_U32_GAMMA: action = "image-convert-u32-gamma"; break; case GIMP_PRECISION_HALF_LINEAR: action = "image-convert-half-linear"; break; case GIMP_PRECISION_HALF_GAMMA: action = "image-convert-half-gamma"; break; case GIMP_PRECISION_FLOAT_LINEAR: action = "image-convert-float-linear"; break; case GIMP_PRECISION_FLOAT_GAMMA: action = "image-convert-float-gamma"; break; case GIMP_PRECISION_DOUBLE_LINEAR: action = "image-convert-double-linear"; break; case GIMP_PRECISION_DOUBLE_GAMMA: action = "image-convert-double-gamma"; break; } gimp_action_group_set_action_active (group, action, TRUE); is_indexed = (gimp_image_get_base_type (image) == GIMP_INDEXED); is_u8_gamma = (gimp_image_get_precision (image) == GIMP_PRECISION_U8_GAMMA); aux = (gimp_image_get_active_channel (image) != NULL); lp = ! gimp_image_is_empty (image); sel = ! gimp_channel_is_empty (gimp_image_get_mask (image)); layers = gimp_image_get_layers (image); groups = ! gimp_item_stack_is_flat (GIMP_ITEM_STACK (layers)); profile = (gimp_image_get_icc_parasite (image) != NULL); } #define SET_SENSITIVE(action,condition) \ gimp_action_group_set_action_sensitive (group, action, (condition) != 0) SET_SENSITIVE ("image-convert-rgb", image); SET_SENSITIVE ("image-convert-grayscale", image); SET_SENSITIVE ("image-convert-indexed", image && !groups && is_u8_gamma); SET_SENSITIVE ("image-convert-u8-gamma", image); SET_SENSITIVE ("image-convert-u8-linear", image && !is_indexed); SET_SENSITIVE ("image-convert-u16-gamma", image && !is_indexed); SET_SENSITIVE ("image-convert-u16-linear", image && !is_indexed); SET_SENSITIVE ("image-convert-u32-gamma", image && !is_indexed); SET_SENSITIVE ("image-convert-u32-linear", image && !is_indexed); SET_SENSITIVE ("image-convert-half-gamma", image && !is_indexed); SET_SENSITIVE ("image-convert-half-linear", image && !is_indexed); SET_SENSITIVE ("image-convert-float-gamma", image && !is_indexed); SET_SENSITIVE ("image-convert-float-linear", image && !is_indexed); SET_SENSITIVE ("image-convert-double-gamma", image && !is_indexed); SET_SENSITIVE ("image-convert-double-linear", image && !is_indexed); SET_SENSITIVE ("image-color-profile-assign", image); SET_SENSITIVE ("image-color-profile-convert", image); SET_SENSITIVE ("image-color-profile-discard", image && profile); SET_SENSITIVE ("image-flip-horizontal", image); SET_SENSITIVE ("image-flip-vertical", image); SET_SENSITIVE ("image-rotate-90", image); SET_SENSITIVE ("image-rotate-180", image); SET_SENSITIVE ("image-rotate-270", image); SET_SENSITIVE ("image-resize", image); SET_SENSITIVE ("image-resize-to-layers", image); SET_SENSITIVE ("image-resize-to-selection", image && sel); SET_SENSITIVE ("image-print-size", image); SET_SENSITIVE ("image-scale", image); SET_SENSITIVE ("image-crop-to-selection", image && sel); SET_SENSITIVE ("image-crop-to-content", image); SET_SENSITIVE ("image-duplicate", image); SET_SENSITIVE ("image-merge-layers", image && !aux && lp); SET_SENSITIVE ("image-flatten", image && !aux && lp); SET_SENSITIVE ("image-configure-grid", image); SET_SENSITIVE ("image-properties", image); #undef SET_SENSITIVE }