static void gimp_paint_tool_motion (GimpTool *tool, const GimpCoords *coords, guint32 time, GdkModifierType state, GimpDisplay *display) { GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (tool); GimpPaintOptions *paint_options = GIMP_PAINT_TOOL_GET_OPTIONS (tool); GimpPaintCore *core = paint_tool->core; GimpImage *image = gimp_display_get_image (display); GimpDrawable *drawable = gimp_image_get_active_drawable (image); GimpCoords curr_coords; gint off_x, off_y; GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state, display); if (gimp_color_tool_is_enabled (GIMP_COLOR_TOOL (tool))) return; curr_coords = *coords; gimp_paint_core_smooth_coords (core, paint_options, &curr_coords); gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); curr_coords.x -= off_x; curr_coords.y -= off_y; /* don't paint while the Shift key is pressed for line drawing */ if (paint_tool->draw_line) { gimp_paint_core_set_current_coords (core, &curr_coords); return; } gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); gimp_paint_core_interpolate (core, drawable, paint_options, &curr_coords, time); gimp_projection_flush_now (gimp_image_get_projection (image)); gimp_display_flush_now (display); gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); }
/* * Position the dropped item in the middle of the viewport. */ static void gimp_display_shell_dnd_position_item (GimpDisplayShell *shell, GimpItem *item) { gint x, y; gint width, height; gint off_x, off_y; gimp_display_shell_untransform_viewport (shell, &x, &y, &width, &height); gimp_item_get_offset (item, &off_x, &off_y); off_x = x + (width - gimp_item_get_width (item)) / 2 - off_x; off_y = y + (height - gimp_item_get_height (item)) / 2 - off_y; gimp_item_translate (item, off_x, off_y, FALSE); }
void drawable_flip_cmd_callback (GtkAction *action, gint value, gpointer data) { GimpImage *image; GimpDrawable *drawable; GimpItem *item; GimpContext *context; gint off_x, off_y; gdouble axis = 0.0; return_if_no_drawable (image, drawable, data); return_if_no_context (context, data); item = GIMP_ITEM (drawable); gimp_item_get_offset (item, &off_x, &off_y); switch ((GimpOrientationType) value) { case GIMP_ORIENTATION_HORIZONTAL: axis = ((gdouble) off_x + (gdouble) gimp_item_get_width (item) / 2.0); break; case GIMP_ORIENTATION_VERTICAL: axis = ((gdouble) off_y + (gdouble) gimp_item_get_height (item) / 2.0); break; default: break; } if (gimp_item_get_linked (item)) { gimp_item_linked_flip (item, context, (GimpOrientationType) value, axis, FALSE); } else { gimp_item_flip (item, context, (GimpOrientationType) value, axis, FALSE); } gimp_image_flush (image); }
GimpTextLayer * gimp_image_pick_text_layer (const GimpImage *image, gint x, gint y) { GList *all_layers; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); all_layers = gimp_image_get_layer_list (image); for (list = all_layers; list; list = g_list_next (list)) { GimpLayer *layer = list->data; gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); if (GIMP_IS_TEXT_LAYER (layer) && x >= off_x && y >= off_y && x < off_x + gimp_item_get_width (GIMP_ITEM (layer)) && y < off_y + gimp_item_get_height (GIMP_ITEM (layer))) { g_list_free (all_layers); return GIMP_TEXT_LAYER (layer); } else if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer), x - off_x, y - off_y) > 63) { /* a normal layer covers any possible text layers below, * bail out */ break; } } g_list_free (all_layers); return NULL; }
static void gimp_blend_tool_commit (GimpBlendTool *blend_tool) { GimpTool *tool = GIMP_TOOL (blend_tool); GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (tool); GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); GimpContext *context = GIMP_CONTEXT (options); GimpImage *image = gimp_display_get_image (tool->display); GimpDrawable *drawable = gimp_image_get_active_drawable (image); GimpProgress *progress; gint off_x; gint off_y; progress = gimp_progress_start (GIMP_PROGRESS (tool), FALSE, _("Blending")); gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); gimp_drawable_blend (drawable, context, gimp_context_get_gradient (context), gimp_context_get_paint_mode (context), options->gradient_type, gimp_context_get_opacity (context), options->offset, paint_options->gradient_options->gradient_repeat, paint_options->gradient_options->gradient_reverse, options->supersample, options->supersample_depth, options->supersample_threshold, options->dither, blend_tool->start_x - off_x, blend_tool->start_y - off_y, blend_tool->end_x - off_x, blend_tool->end_y - off_y, progress); if (progress) gimp_progress_end (progress); gimp_image_flush (image); }
void gimp_channel_select_alpha (GimpChannel *channel, GimpDrawable *drawable, GimpChannelOps op, gboolean feather, gdouble feather_radius_x, gdouble feather_radius_y) { GimpItem *item; GimpChannel *add_on; gint off_x, off_y; g_return_if_fail (GIMP_IS_CHANNEL (channel)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); item = GIMP_ITEM (channel); if (gimp_drawable_has_alpha (drawable)) { add_on = gimp_channel_new_from_alpha (gimp_item_get_image (item), drawable, NULL, NULL); } else { /* no alpha is equivalent to completely opaque alpha, * so simply select the whole layer's extents. --mitch */ add_on = gimp_channel_new_mask (gimp_item_get_image (item), gimp_item_get_width (GIMP_ITEM (drawable)), gimp_item_get_height (GIMP_ITEM (drawable))); gimp_channel_all (add_on, FALSE); } gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); gimp_channel_select_channel (channel, C_("undo-type", "Alpha to Selection"), add_on, off_x, off_y, op, feather, feather_radius_x, feather_radius_y); g_object_unref (add_on); }
SioxState * gimp_drawable_foreground_extract_siox_init (GimpDrawable *drawable, gint x, gint y, gint width, gint height) { GeglBuffer *buffer; const guchar *colormap = NULL; gboolean intersect; gint offset_x; gint offset_y; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); if (gimp_drawable_is_indexed (drawable)) colormap = gimp_drawable_get_colormap (drawable); gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); intersect = gimp_rectangle_intersect (offset_x, offset_y, gimp_item_get_width (GIMP_ITEM (drawable)), gimp_item_get_height (GIMP_ITEM (drawable)), x, y, width, height, &x, &y, &width, &height); /* FIXME: * Clear the mask outside the rectangle that we are working on? */ if (! intersect) return NULL; buffer = gimp_drawable_get_buffer (drawable); return siox_init (gimp_gegl_buffer_get_tiles (buffer), colormap, offset_x, offset_y, x, y, width, height); }
GimpLayer * gimp_image_pick_layer_by_bounds (const GimpImage *image, gint x, gint y) { GList *all_layers; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); all_layers = gimp_image_get_layer_list (image); for (list = all_layers; list; list = g_list_next (list)) { GimpLayer *layer = list->data; if (gimp_item_get_visible (GIMP_ITEM (layer))) { gint off_x, off_y; gint width, height; gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); width = gimp_item_get_width (GIMP_ITEM (layer)); height = gimp_item_get_height (GIMP_ITEM (layer)); if (x >= off_x && y >= off_y && x < off_x + width && y < off_y + height) { g_list_free (all_layers); return layer; } } } g_list_free (all_layers); return NULL; }
static GeglBuffer * gimp_fuzzy_select_tool_get_mask (GimpRegionSelectTool *region_select, GimpDisplay *display) { GimpTool *tool = GIMP_TOOL (region_select); GimpSelectionOptions *sel_options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool); GimpRegionSelectOptions *options = GIMP_REGION_SELECT_TOOL_GET_OPTIONS (tool); GimpImage *image = gimp_display_get_image (display); GimpDrawable *drawable = gimp_image_get_active_drawable (image); GimpPickable *pickable; gint x, y; x = region_select->x; y = region_select->y; if (! options->sample_merged) { gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); x -= off_x; y -= off_y; pickable = GIMP_PICKABLE (drawable); } else { pickable = GIMP_PICKABLE (image); } return gimp_pickable_contiguous_region_by_seed (pickable, sel_options->antialias, options->threshold / 255.0, options->select_transparent, options->select_criterion, options->diagonal_neighbors, x, y); }
static gboolean gimp_image_map_tool_pick_color (GimpColorTool *color_tool, gint x, gint y, const Babl **sample_format, gpointer pixel, GimpRGB *color) { GimpImageMapTool *im_tool = GIMP_IMAGE_MAP_TOOL (color_tool); gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (im_tool->drawable), &off_x, &off_y); *sample_format = gimp_drawable_get_format (im_tool->drawable); return gimp_pickable_pick_color (GIMP_PICKABLE (im_tool->drawable), x - off_x, y - off_y, color_tool->options->sample_average, color_tool->options->average_radius, pixel, color); }
void drawable_rotate_cmd_callback (GtkAction *action, gint value, gpointer data) { GimpImage *image; GimpDrawable *drawable; GimpContext *context; GimpItem *item; gint off_x, off_y; gdouble center_x, center_y; gboolean clip_result = FALSE; return_if_no_drawable (image, drawable, data); return_if_no_context (context, data); item = GIMP_ITEM (drawable); gimp_item_get_offset (item, &off_x, &off_y); center_x = ((gdouble) off_x + (gdouble) gimp_item_get_width (item) / 2.0); center_y = ((gdouble) off_y + (gdouble) gimp_item_get_height (item) / 2.0); if (GIMP_IS_CHANNEL (item)) clip_result = TRUE; if (gimp_item_get_linked (item)) { gimp_item_linked_rotate (item, context, (GimpRotationType) value, center_x, center_y, FALSE); } else { gimp_item_rotate (item, context, (GimpRotationType) value, center_x, center_y, clip_result); } gimp_image_flush (image); }
static void gimp_source_tool_draw (GimpDrawTool *draw_tool) { GimpSourceTool *source_tool = GIMP_SOURCE_TOOL (draw_tool); GimpSourceOptions *options = GIMP_SOURCE_TOOL_GET_OPTIONS (draw_tool); GimpSourceCore *source; source = GIMP_SOURCE_CORE (GIMP_PAINT_TOOL (draw_tool)->core); if (options->use_source && source->src_drawable && source_tool->src_display) { GimpDisplay *tmp_display = draw_tool->display; gint off_x; gint off_y; draw_tool->display = source_tool->src_display; gimp_item_get_offset (GIMP_ITEM (source->src_drawable), &off_x, &off_y); if (source_tool->show_source_outline) gimp_brush_tool_draw_brush (GIMP_BRUSH_TOOL (source_tool), source_tool->src_x + off_x, source_tool->src_y + off_y, FALSE); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_CROSS, source_tool->src_x + off_x, source_tool->src_y + off_y, GIMP_TOOL_HANDLE_SIZE_CROSS, GIMP_TOOL_HANDLE_SIZE_CROSS, GIMP_HANDLE_ANCHOR_CENTER); draw_tool->display = tmp_display; } GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool); }
void gimp_channel_select_by_index (GimpChannel *channel, GimpDrawable *drawable, gint index, GimpChannelOps op, gboolean feather, gdouble feather_radius_x, gdouble feather_radius_y) { GeglBuffer *add_on; gint add_on_x = 0; gint add_on_y = 0; g_return_if_fail (GIMP_IS_CHANNEL (channel)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_drawable_is_indexed (drawable)); add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (GIMP_ITEM (drawable)), gimp_item_get_height (GIMP_ITEM (drawable))), babl_format ("Y float")); gimp_gegl_index_to_mask (gimp_drawable_get_buffer (drawable), NULL, gimp_drawable_get_format_without_alpha (drawable), add_on, NULL, index); gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y); gimp_channel_select_buffer (channel, C_("undo-type", "Select by Indexed Color"), add_on, add_on_x, add_on_y, op, feather, feather_radius_x, feather_radius_y); g_object_unref (add_on); }
void gimp_layer_mask_set_layer (GimpLayerMask *layer_mask, GimpLayer *layer) { g_return_if_fail (GIMP_IS_LAYER_MASK (layer_mask)); g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer)); layer_mask->layer = layer; if (layer) { gchar *mask_name; gint offset_x; gint offset_y; gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y); gimp_item_set_offset (GIMP_ITEM (layer_mask), offset_x, offset_y); mask_name = g_strdup_printf (_("%s mask"), gimp_object_get_name (layer)); gimp_object_take_name (GIMP_OBJECT (layer_mask), mask_name); } }
static void gimp_drawable_bucket_fill_internal (GimpDrawable *drawable, GimpFillType fill_type, gint paint_mode, gdouble opacity, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gboolean diagonal_neighbors, gdouble x, gdouble y, const GimpRGB *color, GimpPattern *pattern) { GimpImage *image; GimpPickable *pickable; GeglBuffer *buffer; GeglBuffer *mask_buffer; gint x1, y1, x2, y2; gint mask_offset_x = 0; gint mask_offset_y = 0; gboolean selection; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); g_return_if_fail (fill_type != GIMP_FILL_PATTERN || GIMP_IS_PATTERN (pattern)); g_return_if_fail (fill_type == GIMP_FILL_PATTERN || color != NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); selection = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2); if ((x1 == x2) || (y1 == y2)) return; gimp_set_busy (image->gimp); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. If there is a selection, calculate the * intersection of this region with the existing selection. */ mask_buffer = gimp_pickable_contiguous_region_by_seed (pickable, TRUE, threshold, fill_transparent, fill_criterion, diagonal_neighbors, (gint) x, (gint) y); if (selection) { GimpDrawable *sel; gint off_x = 0; gint off_y = 0; if (! sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); sel = GIMP_DRAWABLE (gimp_image_get_mask (image)); gimp_gegl_mask_combine_buffer (mask_buffer, gimp_drawable_get_buffer (sel), GIMP_CHANNEL_OP_INTERSECT, -off_x, -off_y); } gimp_gegl_mask_bounds (mask_buffer, &x1, &y1, &x2, &y2); /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); x1 = CLAMP (x1, off_x, (off_x + gimp_item_get_width (item))); y1 = CLAMP (y1, off_y, (off_y + gimp_item_get_height (item))); x2 = CLAMP (x2, off_x, (off_x + gimp_item_get_width (item))); y2 = CLAMP (y2, off_y, (off_y + gimp_item_get_height (item))); mask_offset_x = x1; mask_offset_y = y1; /* translate mask bounds to drawable coords */ x1 -= off_x; y1 -= off_y; x2 -= off_x; y2 -= off_y; } else { mask_offset_x = x1; mask_offset_y = y1; } buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gimp_drawable_get_format_with_alpha (drawable)); switch (fill_type) { case GIMP_FILL_FOREGROUND: case GIMP_FILL_BACKGROUND: case GIMP_FILL_WHITE: case GIMP_FILL_TRANSPARENT: { GeglColor *gegl_color = gimp_gegl_color_new (color); gegl_buffer_set_color (buffer, NULL, gegl_color); g_object_unref (gegl_color); } break; case GIMP_FILL_PATTERN: { GeglBuffer *pattern_buffer = gimp_pattern_create_buffer (pattern); gegl_buffer_set_pattern (buffer, NULL, pattern_buffer, -x1, -y1); g_object_unref (pattern_buffer); } break; } gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, mask_buffer, -mask_offset_x, -mask_offset_y, 1.0); g_object_unref (mask_buffer); /* Apply it to the image */ gimp_drawable_apply_buffer (drawable, buffer, GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), TRUE, C_("undo-type", "Bucket Fill"), opacity, paint_mode, NULL, x1, y1); g_object_unref (buffer); gimp_drawable_update (drawable, x1, y1, x2 - x1, y2 - y1); gimp_unset_busy (image->gimp); }
static void gimp_n_point_deformation_tool_start (GimpNPointDeformationTool *npd_tool, GimpDisplay *display) { GimpTool *tool = GIMP_TOOL (npd_tool); GimpNPointDeformationOptions *npd_options; GimpImage *image; GeglBuffer *source_buffer; GeglBuffer *preview_buffer; NPDModel *model; npd_options = GIMP_N_POINT_DEFORMATION_TOOL_GET_OPTIONS (npd_tool); gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display); image = gimp_display_get_image (display); tool->display = display; tool->drawable = gimp_image_get_active_drawable (image); npd_tool->active = TRUE; /* create GEGL graph */ source_buffer = gimp_drawable_get_buffer (tool->drawable); preview_buffer = gegl_buffer_new (gegl_buffer_get_extent (source_buffer), babl_format ("cairo-ARGB32")); npd_tool->graph = gegl_node_new (); npd_tool->source = gegl_node_new_child (npd_tool->graph, "operation", "gegl:buffer-source", "buffer", source_buffer, NULL); npd_tool->npd_node = gegl_node_new_child (npd_tool->graph, "operation", "gegl:npd", NULL); npd_tool->sink = gegl_node_new_child (npd_tool->graph, "operation", "gegl:write-buffer", "buffer", preview_buffer, NULL); gegl_node_link_many (npd_tool->source, npd_tool->npd_node, npd_tool->sink, NULL); /* initialize some options */ g_object_set (G_OBJECT (npd_options), "mesh-visible", TRUE, NULL); gimp_n_point_deformation_options_set_sensitivity (npd_options, TRUE); /* compute and get model */ gegl_node_process (npd_tool->npd_node); gegl_node_get (npd_tool->npd_node, "model", &model, NULL); npd_tool->model = model; npd_tool->preview_buffer = preview_buffer; npd_tool->selected_cp = NULL; npd_tool->hovering_cp = NULL; npd_tool->selected_cps = NULL; npd_tool->rubber_band = FALSE; npd_tool->lattice_points = g_new (GimpVector2, 5 * model->hidden_model->num_of_bones); gimp_item_get_offset (GIMP_ITEM (tool->drawable), &npd_tool->offset_x, &npd_tool->offset_y); gimp_npd_debug (("offset: %f %f\n", npd_tool->offset_x, npd_tool->offset_y)); gimp_draw_tool_start (GIMP_DRAW_TOOL (npd_tool), display); gimp_n_point_deformation_tool_perform_deformation (npd_tool); /* hide original image */ gimp_item_set_visible (GIMP_ITEM (tool->drawable), FALSE, FALSE); gimp_image_flush (image); /* create and start a deformation thread */ npd_tool->deform_thread = g_thread_new ("deform thread", (GThreadFunc) gimp_n_point_deformation_tool_deform_thread_func, npd_tool); /* create and start canvas update timeout */ npd_tool->draw_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, GIMP_NPD_DRAW_INTERVAL, (GSourceFunc) gimp_n_point_deformation_tool_canvas_update_timeout, npd_tool, NULL); }
static void gimp_source_tool_draw (GimpDrawTool *draw_tool) { GimpSourceTool *source_tool = GIMP_SOURCE_TOOL (draw_tool); GimpSourceOptions *options = GIMP_SOURCE_TOOL_GET_OPTIONS (draw_tool); GimpSourceCore *source; source = GIMP_SOURCE_CORE (GIMP_PAINT_TOOL (draw_tool)->core); GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool); if (gimp_source_core_use_source (source, options) && source->src_drawable && source_tool->src_display) { GimpDisplayShell *src_shell; gint off_x; gint off_y; src_shell = gimp_display_get_shell (source_tool->src_display); gimp_item_get_offset (GIMP_ITEM (source->src_drawable), &off_x, &off_y); if (source_tool->src_outline) { gimp_display_shell_remove_tool_item (src_shell, source_tool->src_outline); source_tool->src_outline = NULL; } if (source_tool->show_source_outline) { source_tool->src_outline = gimp_brush_tool_create_outline (GIMP_BRUSH_TOOL (source_tool), source_tool->src_display, source_tool->src_x + off_x, source_tool->src_y + off_y); if (source_tool->src_outline) { gimp_display_shell_add_tool_item (src_shell, source_tool->src_outline); g_object_unref (source_tool->src_outline); } } if (source_tool->src_outline) { if (source_tool->src_handle) { gimp_display_shell_remove_tool_item (src_shell, source_tool->src_handle); source_tool->src_handle = NULL; } } else { if (! source_tool->src_handle) { source_tool->src_handle = gimp_canvas_handle_new (src_shell, GIMP_HANDLE_CROSS, GIMP_HANDLE_ANCHOR_CENTER, source_tool->src_x + off_x, source_tool->src_y + off_y, GIMP_TOOL_HANDLE_SIZE_CROSS, GIMP_TOOL_HANDLE_SIZE_CROSS); gimp_display_shell_add_tool_item (src_shell, source_tool->src_handle); g_object_unref (source_tool->src_handle); } else { gimp_canvas_handle_set_position (source_tool->src_handle, source_tool->src_x + off_x, source_tool->src_y + off_y); } } } }
/* some rather complex logic here. If the user clicks without modifiers, * then we start a new list, and use the first object in it as reference. * If the user clicks using Shift, or draws a rubber-band box, then * we add objects to the list, but do not specify which one should * be used as reference. */ static void gimp_align_tool_button_release (GimpTool *tool, const GimpCoords *coords, guint32 time, GdkModifierType state, GimpButtonReleaseType release_type, GimpDisplay *display) { GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (tool); GimpAlignOptions *options = GIMP_ALIGN_TOOL_GET_OPTIONS (tool); GimpDisplayShell *shell = gimp_display_get_shell (display); GObject *object = NULL; GimpImage *image = gimp_display_get_image (display); GdkModifierType extend_mask; gint i; extend_mask = gimp_get_extend_selection_mask (); gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool)); gimp_tool_control_halt (tool->control); if (release_type == GIMP_BUTTON_RELEASE_CANCEL) { align_tool->x2 = align_tool->x1; align_tool->y2 = align_tool->y1; gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); return; } if (! (state & extend_mask)) /* start a new list */ { gimp_align_tool_clear_selected (align_tool); align_tool->set_reference = FALSE; } /* if mouse has moved less than EPSILON pixels since button press, * select the nearest thing, otherwise make a rubber-band rectangle */ if (hypot (coords->x - align_tool->x1, coords->y - align_tool->y1) < EPSILON) { GimpVectors *vectors; GimpGuide *guide; GimpLayer *layer; gint snap_distance = display->config->snap_distance; if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display, coords, snap_distance, snap_distance, NULL, NULL, NULL, NULL, NULL, &vectors)) { object = G_OBJECT (vectors); } else 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)))) { object = G_OBJECT (guide); } else { if ((layer = gimp_image_pick_layer_by_bounds (image, coords->x, coords->y))) { object = G_OBJECT (layer); } } if (object) { if (! g_list_find (align_tool->selected_objects, object)) { align_tool->selected_objects = g_list_append (align_tool->selected_objects, object); g_signal_connect (object, "removed", G_CALLBACK (gimp_align_tool_object_removed), align_tool); /* if an object has been selected using unmodified click, * it should be used as the reference */ if (! (state & extend_mask)) align_tool->set_reference = TRUE; } } } else /* FIXME: look for vectors too */ { gint X0 = MIN (coords->x, align_tool->x1); gint X1 = MAX (coords->x, align_tool->x1); gint Y0 = MIN (coords->y, align_tool->y1); gint Y1 = MAX (coords->y, align_tool->y1); GList *all_layers; GList *list; all_layers = gimp_image_get_layer_list (image); for (list = all_layers; list; list = g_list_next (list)) { GimpLayer *layer = list->data; gint x0, y0, x1, y1; if (! gimp_item_get_visible (GIMP_ITEM (layer))) continue; gimp_item_get_offset (GIMP_ITEM (layer), &x0, &y0); x1 = x0 + gimp_item_get_width (GIMP_ITEM (layer)); y1 = y0 + gimp_item_get_height (GIMP_ITEM (layer)); if (x0 < X0 || y0 < Y0 || x1 > X1 || y1 > Y1) continue; if (g_list_find (align_tool->selected_objects, layer)) continue; align_tool->selected_objects = g_list_append (align_tool->selected_objects, layer); g_signal_connect (layer, "removed", G_CALLBACK (gimp_align_tool_object_removed), align_tool); } g_list_free (all_layers); } for (i = 0; i < ALIGN_OPTIONS_N_BUTTONS; i++) { if (options->button[i]) gtk_widget_set_sensitive (options->button[i], align_tool->selected_objects != NULL); } align_tool->x2 = align_tool->x1; align_tool->y2 = align_tool->y1; gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool)); }
static void gimp_align_tool_draw (GimpDrawTool *draw_tool) { GimpAlignTool *align_tool = GIMP_ALIGN_TOOL (draw_tool); GList *list; gint x, y, w, h; /* draw rubber-band rectangle */ x = MIN (align_tool->x2, align_tool->x1); y = MIN (align_tool->y2, align_tool->y1); w = MAX (align_tool->x2, align_tool->x1) - x; h = MAX (align_tool->y2, align_tool->y1) - y; gimp_draw_tool_add_rectangle (draw_tool, FALSE, x, y, w, h); for (list = align_tool->selected_objects; list; list = g_list_next (list)) { if (GIMP_IS_ITEM (list->data)) { GimpItem *item = list->data; if (GIMP_IS_VECTORS (item)) { gdouble x1_f, y1_f, x2_f, y2_f; gimp_vectors_bounds (GIMP_VECTORS (item), &x1_f, &y1_f, &x2_f, &y2_f); x = ROUND (x1_f); y = ROUND (y1_f); w = ROUND (x2_f - x1_f); h = ROUND (y2_f - y1_f); } else { gimp_item_get_offset (item, &x, &y); w = gimp_item_get_width (item); h = gimp_item_get_height (item); } gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x, y, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_NORTH_WEST); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x + w, y, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_NORTH_EAST); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x, y + h, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_SOUTH_WEST); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x + w, y + h, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_SOUTH_EAST); } else if (GIMP_IS_GUIDE (list->data)) { GimpGuide *guide = list->data; GimpImage *image = gimp_display_get_image (GIMP_TOOL (draw_tool)->display); gint x, y; gint w, h; switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_VERTICAL: x = gimp_guide_get_position (guide); h = gimp_image_get_height (image); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x, h, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_SOUTH); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, x, 0, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_NORTH); break; case GIMP_ORIENTATION_HORIZONTAL: y = gimp_guide_get_position (guide); w = gimp_image_get_width (image); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, w, y, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_EAST); gimp_draw_tool_add_handle (draw_tool, GIMP_HANDLE_FILLED_SQUARE, 0, y, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_TOOL_HANDLE_SIZE_SMALL, GIMP_HANDLE_ANCHOR_WEST); break; default: break; } } } }
void gimp_source_core_motion (GimpSourceCore *source_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords) { GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options); GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GimpPickable *src_pickable = NULL; GeglBuffer *src_buffer = NULL; GeglRectangle src_rect; gint src_offset_x; gint src_offset_y; GeglBuffer *paint_buffer; gint paint_buffer_x; gint paint_buffer_y; gint paint_area_offset_x; gint paint_area_offset_y; gint paint_area_width; gint paint_area_height; gdouble fade_point; gdouble opacity; fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); opacity = gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_OPACITY, coords, paint_options, fade_point); if (opacity == 0.0) return; src_offset_x = source_core->offset_x; src_offset_y = source_core->offset_y; if (gimp_source_core_use_source (source_core, options)) { src_pickable = GIMP_PICKABLE (source_core->src_drawable); if (options->sample_merged) { GimpImage *src_image = gimp_pickable_get_image (src_pickable); gint off_x, off_y; src_pickable = GIMP_PICKABLE (src_image); gimp_item_get_offset (GIMP_ITEM (source_core->src_drawable), &off_x, &off_y); src_offset_x += off_x; src_offset_y += off_y; } gimp_pickable_flush (src_pickable); } paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, paint_options, coords, &paint_buffer_x, &paint_buffer_y); if (! paint_buffer) return; paint_area_offset_x = 0; paint_area_offset_y = 0; paint_area_width = gegl_buffer_get_width (paint_buffer); paint_area_height = gegl_buffer_get_height (paint_buffer); if (gimp_source_core_use_source (source_core, options)) { src_buffer = GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core, drawable, paint_options, src_pickable, src_offset_x, src_offset_y, paint_buffer, paint_buffer_x, paint_buffer_y, &paint_area_offset_x, &paint_area_offset_y, &paint_area_width, &paint_area_height, &src_rect); if (! src_buffer) return; } /* Set the paint buffer to transparent */ gegl_buffer_clear (paint_buffer, NULL); GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core, drawable, paint_options, coords, opacity, src_pickable, src_buffer, &src_rect, src_offset_x, src_offset_y, paint_buffer, paint_buffer_x, paint_buffer_y, paint_area_offset_x, paint_area_offset_y, paint_area_width, paint_area_height); if (src_buffer) g_object_unref (src_buffer); }
static void gimp_operation_tool_color_picked (GimpImageMapTool *im_tool, gpointer identifier, gdouble x, gdouble y, const Babl *sample_format, const GimpRGB *color) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); gchar **pspecs; pspecs = g_strsplit (identifier, ":", 2); if (pspecs[1]) { GimpImageMapOptions *options = GIMP_IMAGE_MAP_TOOL_GET_OPTIONS (tool); GimpDrawable *drawable = GIMP_TOOL (im_tool)->drawable; GObjectClass *object_class = G_OBJECT_GET_CLASS (im_tool->config); GParamSpec *pspec_x; GParamSpec *pspec_y; gint width = 1; gint height = 1; if (drawable) { gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); x -= off_x; y -= off_y; switch (options->region) { case GIMP_IMAGE_MAP_REGION_SELECTION: if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &off_x, &off_y, &width, &height)) { x -= off_x; y -= off_y; } break; case GIMP_IMAGE_MAP_REGION_DRAWABLE: width = gimp_item_get_width (GIMP_ITEM (drawable)); height = gimp_item_get_height (GIMP_ITEM (drawable)); break; } } pspec_x = g_object_class_find_property (object_class, pspecs[0]); pspec_y = g_object_class_find_property (object_class, pspecs[1]); if (pspec_x && pspec_y && G_PARAM_SPEC_TYPE (pspec_x) == G_PARAM_SPEC_TYPE (pspec_y)) { GValue value_x = G_VALUE_INIT; GValue value_y = G_VALUE_INIT; g_value_init (&value_x, G_PARAM_SPEC_VALUE_TYPE (pspec_x)); g_value_init (&value_y, G_PARAM_SPEC_VALUE_TYPE (pspec_y)); #define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v) if (HAS_KEY (pspec_x, "unit", "relative-coordinate") && HAS_KEY (pspec_y, "unit", "relative-coordinate")) { x /= (gdouble) width; y /= (gdouble) height; } if (G_IS_PARAM_SPEC_INT (pspec_x)) { g_value_set_int (&value_x, x); g_value_set_int (&value_y, y); g_param_value_validate (pspec_x, &value_x); g_param_value_validate (pspec_y, &value_y); g_object_set (im_tool->config, pspecs[0], g_value_get_int (&value_x), pspecs[1], g_value_get_int (&value_y), NULL); } else if (G_IS_PARAM_SPEC_DOUBLE (pspec_x)) { g_value_set_double (&value_x, x); g_value_set_double (&value_y, y); g_param_value_validate (pspec_x, &value_x); g_param_value_validate (pspec_y, &value_y); g_object_set (im_tool->config, pspecs[0], g_value_get_double (&value_x), pspecs[1], g_value_get_double (&value_y), NULL); } else { g_warning ("%s: unhandled param spec of type %s", G_STRFUNC, G_PARAM_SPEC_TYPE_NAME (pspec_x)); } g_value_unset (&value_x); g_value_unset (&value_y); } } else { g_object_set (im_tool->config, pspecs[0], color, NULL); } g_strfreev (pspecs); }
static void gimp_seamless_clone_tool_filter_update (GimpSeamlessCloneTool *sc) { GimpTool *tool = GIMP_TOOL (sc); GimpDisplayShell *shell = gimp_display_get_shell (tool->display); GimpItem *item = GIMP_ITEM (tool->drawable); gint x, y; gint w, h; gint off_x, off_y; GeglRectangle visible; GeglOperation *op = NULL; GimpProgress *progress; GeglNode *output; GeglProcessor *processor; gdouble value; progress = gimp_progress_start (GIMP_PROGRESS (sc), FALSE, _("Cloning the foreground object")); /* Find out at which x,y is the top left corner of the currently * displayed part */ gimp_display_shell_untransform_viewport (shell, &x, &y, &w, &h); /* Find out where is our drawable positioned */ gimp_item_get_offset (item, &off_x, &off_y); /* Create a rectangle from the intersection of the currently displayed * part with the drawable */ gimp_rectangle_intersect (x, y, w, h, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &visible.x, &visible.y, &visible.width, &visible.height); /* Since the filter_apply function receives a rectangle describing * where it should update the preview, and since that rectangle should * be relative to the drawable's location, we now offset back by the * drawable's offsetts. */ visible.x -= off_x; visible.y -= off_y; g_object_get (sc->sc_node, "gegl-operation", &op, NULL); /* If any cache of the visible area was present, clear it! * We need to clear the cache in the sc_node, since that is * where the previous paste was located */ gegl_operation_invalidate (op, &visible, TRUE); g_object_unref (op); /* Now update the image map and show this area */ gimp_drawable_filter_apply (sc->filter, NULL); /* Show update progress. */ output = gegl_node_get_output_proxy (sc->render_node, "output"); processor = gegl_node_new_processor (output, NULL); while (gegl_processor_work (processor, &value)) { if (progress) gimp_progress_set_value (progress, value); } if (progress) gimp_progress_end (progress); g_object_unref (processor); }
const GimpBoundSeg * floating_sel_boundary (GimpLayer *layer, gint *n_segs) { g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL); g_return_val_if_fail (n_segs != NULL, NULL); if (layer->fs.boundary_known == FALSE) { gint width, height; gint off_x, off_y; width = gimp_item_get_width (GIMP_ITEM (layer)); height = gimp_item_get_height (GIMP_ITEM (layer)); gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); if (layer->fs.segs) g_free (layer->fs.segs); if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) { GeglBuffer *buffer; gint i; /* find the segments */ buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); layer->fs.segs = gimp_boundary_find (buffer, NULL, babl_format ("A float"), GIMP_BOUNDARY_WITHIN_BOUNDS, 0, 0, width, height, GIMP_BOUNDARY_HALF_WAY, &layer->fs.num_segs); /* offset the segments */ for (i = 0; i < layer->fs.num_segs; i++) { layer->fs.segs[i].x1 += off_x; layer->fs.segs[i].y1 += off_y; layer->fs.segs[i].x2 += off_x; layer->fs.segs[i].y2 += off_y; } } else { layer->fs.num_segs = 4; layer->fs.segs = g_new0 (GimpBoundSeg, 4); /* top */ layer->fs.segs[0].x1 = off_x; layer->fs.segs[0].y1 = off_y; layer->fs.segs[0].x2 = off_x + width; layer->fs.segs[0].y2 = off_y; /* left */ layer->fs.segs[1].x1 = off_x; layer->fs.segs[1].y1 = off_y; layer->fs.segs[1].x2 = off_x; layer->fs.segs[1].y2 = off_y + height; /* right */ layer->fs.segs[2].x1 = off_x + width; layer->fs.segs[2].y1 = off_y; layer->fs.segs[2].x2 = off_x + width; layer->fs.segs[2].y2 = off_y + height; /* bottom */ layer->fs.segs[3].x1 = off_x; layer->fs.segs[3].y1 = off_y + height; layer->fs.segs[3].x2 = off_x + width; layer->fs.segs[3].y2 = off_y + height; } layer->fs.boundary_known = TRUE; } *n_segs = layer->fs.num_segs; return layer->fs.segs; }
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; }
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; }
static void gimp_item_prop_undo_pop (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GimpItemPropUndo *item_prop_undo = GIMP_ITEM_PROP_UNDO (undo); GimpItem *item = GIMP_ITEM_UNDO (undo)->item; GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); switch (undo->undo_type) { case GIMP_UNDO_ITEM_REORDER: { GimpItem *parent; gint position; parent = gimp_item_get_parent (item); position = gimp_item_get_index (item); gimp_item_tree_reorder_item (gimp_item_get_tree (item), item, item_prop_undo->parent, item_prop_undo->position, FALSE, NULL); item_prop_undo->parent = parent; item_prop_undo->position = position; } break; case GIMP_UNDO_ITEM_RENAME: { gchar *name; name = g_strdup (gimp_object_get_name (item)); gimp_item_tree_rename_item (gimp_item_get_tree (item), item, item_prop_undo->name, FALSE, NULL); g_free (item_prop_undo->name); item_prop_undo->name = name; } break; case GIMP_UNDO_ITEM_DISPLACE: { gint offset_x; gint offset_y; gimp_item_get_offset (item, &offset_x, &offset_y); gimp_item_translate (item, item_prop_undo->offset_x - offset_x, item_prop_undo->offset_y - offset_y, FALSE); item_prop_undo->offset_x = offset_x; item_prop_undo->offset_y = offset_y; } break; case GIMP_UNDO_ITEM_VISIBILITY: { gboolean visible; visible = gimp_item_get_visible (item); gimp_item_set_visible (item, item_prop_undo->visible, FALSE); item_prop_undo->visible = visible; } break; case GIMP_UNDO_ITEM_LINKED: { gboolean linked; linked = gimp_item_get_linked (item); gimp_item_set_linked (item, item_prop_undo->linked, FALSE); item_prop_undo->linked = linked; } break; case GIMP_UNDO_ITEM_COLOR_TAG: { GimpColorTag color_tag; color_tag = gimp_item_get_color_tag (item); gimp_item_set_color_tag (item, item_prop_undo->color_tag, FALSE); item_prop_undo->color_tag = color_tag; } break; case GIMP_UNDO_ITEM_LOCK_CONTENT: { gboolean lock_content; lock_content = gimp_item_get_lock_content (item); gimp_item_set_lock_content (item, item_prop_undo->lock_content, FALSE); item_prop_undo->lock_content = lock_content; } break; case GIMP_UNDO_ITEM_LOCK_POSITION: { gboolean lock_position; lock_position = gimp_item_get_lock_position (item); gimp_item_set_lock_position (item, item_prop_undo->lock_position, FALSE); item_prop_undo->lock_position = lock_position; } break; case GIMP_UNDO_PARASITE_ATTACH: case GIMP_UNDO_PARASITE_REMOVE: { GimpParasite *parasite; parasite = item_prop_undo->parasite; item_prop_undo->parasite = gimp_parasite_copy (gimp_item_parasite_find (item, item_prop_undo->parasite_name)); if (parasite) gimp_item_parasite_attach (item, parasite, FALSE); else gimp_item_parasite_detach (item, item_prop_undo->parasite_name, FALSE); if (parasite) gimp_parasite_free (parasite); } break; default: g_assert_not_reached (); } }
void gimp_source_core_motion (GimpSourceCore *source_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, GimpSymmetry *sym) { GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options); GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GimpPickable *src_pickable = NULL; GeglBuffer *src_buffer = NULL; GeglRectangle src_rect; gint base_src_offset_x; gint base_src_offset_y; gint src_offset_x; gint src_offset_y; GeglBuffer *paint_buffer; gint paint_buffer_x; gint paint_buffer_y; gint paint_area_offset_x; gint paint_area_offset_y; gint paint_area_width; gint paint_area_height; gdouble fade_point; gdouble opacity; GeglNode *op; GimpCoords *origin; GimpCoords *coords; gint n_strokes; gint i; fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); origin = gimp_symmetry_get_origin (sym); /* Some settings are based on the original stroke. */ opacity = gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_OPACITY, origin, paint_options, fade_point); if (opacity == 0.0) return; base_src_offset_x = source_core->offset_x; base_src_offset_y = source_core->offset_y; if (gimp_source_core_use_source (source_core, options)) { src_pickable = GIMP_PICKABLE (source_core->src_drawable); if (options->sample_merged) { GimpImage *src_image = gimp_pickable_get_image (src_pickable); gint off_x, off_y; src_pickable = GIMP_PICKABLE (src_image); gimp_item_get_offset (GIMP_ITEM (source_core->src_drawable), &off_x, &off_y); base_src_offset_x += off_x; base_src_offset_y += off_y; } gimp_pickable_flush (src_pickable); } gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core), drawable, paint_options, origin); n_strokes = gimp_symmetry_get_size (sym); for (i = 0; i < n_strokes; i++) { coords = gimp_symmetry_get_coords (sym, i); paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, paint_options, coords, &paint_buffer_x, &paint_buffer_y, NULL, NULL); if (! paint_buffer) continue; paint_area_offset_x = 0; paint_area_offset_y = 0; paint_area_width = gegl_buffer_get_width (paint_buffer); paint_area_height = gegl_buffer_get_height (paint_buffer); src_offset_x = base_src_offset_x; src_offset_y = base_src_offset_y; if (gimp_source_core_use_source (source_core, options)) { /* When using a source, use the same for every stroke. */ src_offset_x = src_offset_x - coords->x + origin->x; src_offset_y = src_offset_y - coords->y + origin->y; src_buffer = GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core, drawable, paint_options, src_pickable, src_offset_x, src_offset_y, paint_buffer, paint_buffer_x, paint_buffer_y, &paint_area_offset_x, &paint_area_offset_y, &paint_area_width, &paint_area_height, &src_rect); if (! src_buffer) continue; } /* Set the paint buffer to transparent */ gegl_buffer_clear (paint_buffer, NULL); op = gimp_symmetry_get_operation (sym, i, gegl_buffer_get_width (paint_buffer), gegl_buffer_get_height (paint_buffer)); GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core, drawable, paint_options, coords, op, opacity, src_pickable, src_buffer, &src_rect, src_offset_x, src_offset_y, paint_buffer, paint_buffer_x, paint_buffer_y, paint_area_offset_x, paint_area_offset_y, paint_area_width, paint_area_height); if (src_buffer) g_object_unref (src_buffer); } }
/** * 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; }
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; }
/** * 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; }