static void gimp_clipboard_send_buffer (GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, Gimp *gimp) { GimpClipboard *gimp_clip = gimp_clipboard_get (gimp); GdkPixbuf *pixbuf; gimp_set_busy (gimp); pixbuf = gimp_viewable_get_pixbuf (GIMP_VIEWABLE (gimp_clip->buffer), gimp_get_user_context (gimp), gimp_buffer_get_width (gimp_clip->buffer), gimp_buffer_get_height (gimp_clip->buffer)); if (pixbuf) { if (gimp->be_verbose) g_printerr ("clipboard: sending pixbuf data as '%s'\n", gimp_clip->target_entries[info].target); gtk_selection_data_set_pixbuf (selection_data, pixbuf); } else { g_warning ("%s: gimp_viewable_get_pixbuf() failed", G_STRFUNC); } gimp_unset_busy (gimp); }
GimpLayer * gimp_image_flatten (GimpImage *image, GimpContext *context, GError **error) { GList *list; GSList *merge_list = NULL; GimpLayer *layer; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (list = gimp_image_get_layer_iter (image); list; list = g_list_next (list)) { layer = list->data; if (gimp_layer_is_floating_sel (layer)) continue; if (gimp_item_get_visible (GIMP_ITEM (layer))) merge_list = g_slist_append (merge_list, layer); } if (merge_list) { gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, C_("undo-type", "Flatten Image")); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) floating_sel_anchor (gimp_image_get_floating_selection (image)); layer = gimp_image_merge_layers (image, gimp_image_get_layers (image), merge_list, context, GIMP_FLATTEN_IMAGE); g_slist_free (merge_list); gimp_image_alpha_changed (image); gimp_image_undo_group_end (image); gimp_unset_busy (image->gimp); return layer; } g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, _("Cannot flatten an image without any visible layer.")); return NULL; }
void gimp_set_busy_until_idle (Gimp *gimp) { g_return_if_fail (GIMP_IS_GIMP (gimp)); if (! gimp->busy_idle_id) { gimp_set_busy (gimp); gimp->busy_idle_id = g_idle_add_full (G_PRIORITY_HIGH, gimp_idle_unset_busy, gimp, NULL); } }
/** * gimp_clipboard_get_svg: * @gimp: pointer to #Gimp * @svg_length: returns the size of the SVG stream in bytes * * Retrieves SVG data from %GDK_SELECTION_CLIPBOARD or from the global * SVG buffer of @gimp. * * The returned data needs to be freed when it's no longer needed. * * Return value: a reference to a #GimpBuffer or %NULL if there's no * image data **/ gchar * gimp_clipboard_get_svg (Gimp *gimp, gsize *svg_length) { GimpClipboard *gimp_clip; GtkClipboard *clipboard; GdkAtom atom; gchar *svg = NULL; g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); g_return_val_if_fail (svg_length != NULL, NULL); clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (), GDK_SELECTION_CLIPBOARD); if (clipboard && gtk_clipboard_get_owner (clipboard) != G_OBJECT (gimp) && (atom = gimp_clipboard_wait_for_svg (gimp)) != GDK_NONE) { GtkSelectionData *data; gimp_set_busy (gimp); data = gtk_clipboard_wait_for_contents (clipboard, atom); if (data) { const guchar *stream; stream = gimp_selection_data_get_stream (data, svg_length); if (stream) svg = g_memdup (stream, *svg_length); gtk_selection_data_free (data); } gimp_unset_busy (gimp); } gimp_clip = gimp_clipboard_get (gimp); if (! svg && gimp_clip->svg) { svg = g_strdup (gimp_clip->svg); *svg_length = strlen (svg); } return svg; }
void gimp_fonts_load (Gimp *gimp) { FcConfig *config; GFile *fonts_conf; GList *path; g_return_if_fail (GIMP_IS_FONT_LIST (gimp->fonts)); gimp_set_busy (gimp); if (gimp->be_verbose) g_print ("Loading fonts\n"); gimp_container_freeze (GIMP_CONTAINER (gimp->fonts)); gimp_container_clear (GIMP_CONTAINER (gimp->fonts)); config = FcInitLoadConfig (); if (! config) goto cleanup; fonts_conf = gimp_directory_file (CONF_FNAME, NULL); if (! gimp_fonts_load_fonts_conf (config, fonts_conf)) goto cleanup; fonts_conf = gimp_sysconf_directory_file (CONF_FNAME, NULL); if (! gimp_fonts_load_fonts_conf (config, fonts_conf)) goto cleanup; path = gimp_config_path_expand_to_files (gimp->config->font_path, FALSE); gimp_fonts_add_directories (config, path); g_list_free_full (path, (GDestroyNotify) g_object_unref); if (! FcConfigBuildFonts (config)) { FcConfigDestroy (config); goto cleanup; } FcConfigSetCurrent (config); gimp_font_list_restore (GIMP_FONT_LIST (gimp->fonts)); cleanup: gimp_container_thaw (GIMP_CONTAINER (gimp->fonts)); gimp_unset_busy (gimp); }
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; GeglBuffer *buffer; gdouble mask_x; gdouble mask_y; gint width, 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)); gimp_set_busy (image->gimp); buffer = gimp_drawable_get_bucket_fill_buffer (drawable, options, fill_transparent, fill_criterion, threshold, sample_merged, diagonal_neighbors, seed_x, seed_y, NULL, &mask_x, &mask_y, &width, &height); if (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)), GIMP_LAYER_COLOR_SPACE_AUTO, GIMP_LAYER_COLOR_SPACE_AUTO, gimp_layer_mode_get_paint_composite_mode ( gimp_context_get_paint_mode (GIMP_CONTEXT (options))), NULL, (gint) mask_x, mask_y); g_object_unref (buffer); gimp_drawable_update (drawable, mask_x, mask_y, width, height); } gimp_unset_busy (image->gimp); }
void gimp_fonts_load (Gimp *gimp) { FcConfig *config; gchar *fonts_conf; gchar *path; g_return_if_fail (GIMP_IS_FONT_LIST (gimp->fonts)); gimp_set_busy (gimp); if (gimp->be_verbose) g_print ("Loading fonts\n"); gimp_container_freeze (GIMP_CONTAINER (gimp->fonts)); gimp_container_clear (GIMP_CONTAINER (gimp->fonts)); config = FcInitLoadConfig (); if (! config) goto cleanup; fonts_conf = gimp_personal_rc_file (CONF_FNAME); if (! gimp_fonts_load_fonts_conf (config, fonts_conf)) goto cleanup; fonts_conf = g_build_filename (gimp_sysconf_directory (), CONF_FNAME, NULL); if (! gimp_fonts_load_fonts_conf (config, fonts_conf)) goto cleanup; path = gimp_config_path_expand (gimp->config->font_path, TRUE, NULL); gimp_fonts_add_directories (config, path); g_free (path); if (! FcConfigBuildFonts (config)) { FcConfigDestroy (config); goto cleanup; } FcConfigSetCurrent (config); gimp_font_list_restore (GIMP_FONT_LIST (gimp->fonts)); cleanup: gimp_container_thaw (GIMP_CONTAINER (gimp->fonts)); gimp_unset_busy (gimp); }
GimpLayer * gimp_image_merge_down (GimpImage *image, GimpLayer *current_layer, GimpContext *context, GimpMergeType merge_type) { GimpLayer *layer; GList *list; GList *layer_list; GSList *merge_list; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); for (list = GIMP_LIST (image->layers)->list, layer_list = NULL; list && !layer_list; list = g_list_next (list)) { layer = list->data; if (layer == current_layer) break; } for (layer_list = g_list_next (list), merge_list = NULL; layer_list && !merge_list; layer_list = g_list_next (layer_list)) { layer = layer_list->data; if (gimp_item_get_visible (GIMP_ITEM (layer))) merge_list = g_slist_append (NULL, layer); } merge_list = g_slist_prepend (merge_list, current_layer); gimp_set_busy (image->gimp); layer = gimp_image_merge_layers (image, merge_list, context, merge_type, _("Merge Down")); g_slist_free (merge_list); gimp_unset_busy (image->gimp); return layer; }
/** * gimp_clipboard_get_buffer: * @gimp: pointer to #Gimp * * Retrieves either image data from %GDK_SELECTION_CLIPBOARD or from * the global cut buffer of @gimp. * * The returned #GimpBuffer needs to be unref'ed when it's no longer * needed. * * Return value: a reference to a #GimpBuffer or %NULL if there's no * image data **/ GimpBuffer * gimp_clipboard_get_buffer (Gimp *gimp) { GimpClipboard *gimp_clip; GtkClipboard *clipboard; GdkAtom atom; GimpBuffer *buffer = NULL; g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (), GDK_SELECTION_CLIPBOARD); if (clipboard && gtk_clipboard_get_owner (clipboard) != G_OBJECT (gimp) && (atom = gimp_clipboard_wait_for_buffer (gimp)) != GDK_NONE) { GtkSelectionData *data; gimp_set_busy (gimp); data = gtk_clipboard_wait_for_contents (clipboard, atom); if (data) { GdkPixbuf *pixbuf = gtk_selection_data_get_pixbuf (data); gtk_selection_data_free (data); if (pixbuf) { buffer = gimp_buffer_new_from_pixbuf (pixbuf, _("Clipboard")); g_object_unref (pixbuf); } } gimp_unset_busy (gimp); } gimp_clip = gimp_clipboard_get (gimp); if (! buffer && gimp_clip->buffer) buffer = g_object_ref (gimp_clip->buffer); return buffer; }
static void gimp_clipboard_send_svg (GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, Gimp *gimp) { GimpClipboard *gimp_clip = gimp_clipboard_get (gimp); gimp_set_busy (gimp); if (gimp_clip->svg) { if (gimp->be_verbose) g_printerr ("clipboard: sending SVG data as '%s'\n", gimp_clip->svg_target_entries[info].target); gimp_selection_data_set_stream (selection_data, (const guchar *) gimp_clip->svg, strlen (gimp_clip->svg)); } gimp_unset_busy (gimp); }
GimpLayer * gimp_image_flatten (GimpImage *image, GimpContext *context) { GList *list; GSList *merge_list = NULL; GimpLayer *layer; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); gimp_set_busy (image->gimp); /* if there's a floating selection, anchor it */ if (gimp_image_floating_sel (image)) floating_sel_anchor (image->floating_sel); for (list = GIMP_LIST (image->layers)->list; list; list = g_list_next (list)) { layer = list->data; if (gimp_item_get_visible (GIMP_ITEM (layer))) merge_list = g_slist_append (merge_list, layer); } layer = gimp_image_merge_layers (image, merge_list, context, GIMP_FLATTEN_IMAGE, _("Flatten Image")); g_slist_free (merge_list); gimp_image_alpha_changed (image); gimp_unset_busy (image->gimp); return layer; }
static GimpValueArray * xcf_save_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { XcfInfo info = { 0, }; GimpValueArray *return_vals; GimpImage *image; const gchar *uri; gchar *filename; GFile *file; gboolean success = FALSE; GError *my_error = NULL; gimp_set_busy (gimp); image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); uri = g_value_get_string (gimp_value_array_index (args, 3)); #ifdef GIO_IS_FIXED file = g_file_new_for_uri (uri); #else file = g_file_new_for_path (uri); #endif filename = g_file_get_parse_name (file); info.output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, 0, NULL, &my_error)); if (info.output) { info.gimp = gimp; info.seekable = G_SEEKABLE (info.output); info.progress = progress; info.filename = filename; info.compression = COMPRESS_RLE; if (progress) { gchar *name = g_filename_display_name (filename); gchar *msg = g_strdup_printf (_("Saving '%s'"), name); gimp_progress_start (progress, msg, FALSE); g_free (msg); g_free (name); } xcf_save_choose_format (&info, image); success = xcf_save_image (&info, image, error); g_object_unref (info.output); if (progress) gimp_progress_end (progress); } else { g_propagate_prefixed_error (error, my_error, _("Could not open '%s' for writing: "), filename); } g_free (filename); g_object_unref (file); return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); gimp_unset_busy (gimp); return return_vals; }
static GimpValueArray * xcf_load_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { XcfInfo info = { 0, }; GimpValueArray *return_vals; GimpImage *image = NULL; const gchar *uri; gchar *filename; GFile *file; gboolean success = FALSE; gchar id[14]; GError *my_error = NULL; gimp_set_busy (gimp); uri = g_value_get_string (gimp_value_array_index (args, 1)); #ifdef GIO_IS_FIXED file = g_file_new_for_uri (uri); #else file = g_file_new_for_path (uri); #endif filename = g_file_get_parse_name (file); info.input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); if (info.input) { info.gimp = gimp; info.seekable = G_SEEKABLE (info.input); info.progress = progress; info.filename = filename; info.compression = COMPRESS_NONE; if (progress) { gchar *name = g_filename_display_name (filename); gchar *msg = g_strdup_printf (_("Opening '%s'"), name); gimp_progress_start (progress, msg, FALSE); g_free (msg); g_free (name); } success = TRUE; info.cp += xcf_read_int8 (info.input, (guint8 *) id, 14); if (! g_str_has_prefix (id, "gimp xcf ")) { success = FALSE; } else if (strcmp (id + 9, "file") == 0) { info.file_version = 0; } else if (id[9] == 'v') { info.file_version = atoi (id + 10); } else { success = FALSE; } if (success) { if (info.file_version >= 0 && info.file_version < G_N_ELEMENTS (xcf_loaders)) { image = (*(xcf_loaders[info.file_version])) (gimp, &info, error); if (! image) success = FALSE; } else { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("XCF error: unsupported XCF file version %d " "encountered"), info.file_version); success = FALSE; } } g_object_unref (info.input); if (progress) gimp_progress_end (progress); } else { g_propagate_prefixed_error (error, my_error, _("Could not open '%s' for reading: "), filename); } g_free (filename); g_object_unref (file); return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) gimp_value_set_image (gimp_value_array_index (return_vals, 1), image); gimp_unset_busy (gimp); return return_vals; }
static void gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel, GimpDisplay *display) { GimpForegroundSelectTool *fg_select; GimpForegroundSelectOptions *options; GimpImage *image = gimp_display_get_image (display); GimpDrawable *drawable; GimpScanConvert *scan_convert; GimpChannel *mask; const GimpVector2 *points; gint n_points; drawable = gimp_image_get_active_drawable (image); fg_select = GIMP_FOREGROUND_SELECT_TOOL (free_sel); options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (free_sel); if (fg_select->idle_id) { g_source_remove (fg_select->idle_id); fg_select->idle_id = 0; } if (! drawable) return; scan_convert = gimp_scan_convert_new (); gimp_free_select_tool_get_points (free_sel, &points, &n_points); gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE); mask = gimp_channel_new (image, gimp_image_get_width (image), gimp_image_get_height (image), "foreground-extraction", NULL); gimp_scan_convert_render_value (scan_convert, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), 0, 0, 128); gimp_scan_convert_free (scan_convert); if (fg_select->strokes) { GList *list; gimp_set_busy (image->gimp); /* apply foreground and background markers */ for (list = fg_select->strokes; list; list = list->next) gimp_foreground_select_tool_stroke (mask, list->data); if (fg_select->state) gimp_drawable_foreground_extract_siox (GIMP_DRAWABLE (mask), fg_select->state, fg_select->refinement, options->smoothness, options->sensitivity, ! options->contiguous, GIMP_PROGRESS (fg_select)); fg_select->refinement = SIOX_REFINEMENT_NO_CHANGE; gimp_unset_busy (image->gimp); } else { gint x1, y1; gint x2, y2; g_object_set (options, "background", FALSE, NULL); gimp_foreground_select_tool_get_area (mask, &x1, &y1, &x2, &y2); if (fg_select->state) g_warning ("state should be NULL here"); fg_select->state = gimp_drawable_foreground_extract_siox_init (drawable, x1, y1, x2 - x1, y2 - y1); } gimp_foreground_select_tool_set_mask (fg_select, display, mask); g_object_unref (mask); }
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); }
GimpVectors * gimp_image_merge_visible_vectors (GimpImage *image, GError **error) { GList *list; GList *merge_list = NULL; GimpVectors *vectors; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (list = gimp_image_get_vectors_iter (image); list; list = g_list_next (list)) { vectors = list->data; if (gimp_item_get_visible (GIMP_ITEM (vectors))) merge_list = g_list_prepend (merge_list, vectors); } merge_list = g_list_reverse (merge_list); if (merge_list && merge_list->next) { GimpVectors *target_vectors; gchar *name; gint pos; gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, C_("undo-type", "Merge Visible Paths")); vectors = GIMP_VECTORS (merge_list->data); name = g_strdup (gimp_object_get_name (vectors)); pos = gimp_item_get_index (GIMP_ITEM (vectors)); target_vectors = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), GIMP_TYPE_VECTORS)); gimp_image_remove_vectors (image, vectors, TRUE, NULL); for (list = g_list_next (merge_list); list; list = g_list_next (list)) { vectors = list->data; gimp_vectors_add_strokes (vectors, target_vectors); gimp_image_remove_vectors (image, vectors, TRUE, NULL); } gimp_object_take_name (GIMP_OBJECT (target_vectors), name); g_list_free (merge_list); /* FIXME tree */ gimp_image_add_vectors (image, target_vectors, NULL, pos, TRUE); gimp_unset_busy (image->gimp); gimp_image_undo_group_end (image); return target_vectors; } else { g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, _("Not enough visible paths for a merge. " "There must be at least two.")); return NULL; } }
GimpLayer * gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean discard_invisible) { GList *list; GSList *merge_list = NULL; GSList *invisible_list = NULL; gboolean had_floating_sel = FALSE; GimpLayer *layer = NULL; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); /* if there's a floating selection, anchor it */ if (gimp_image_floating_sel (image)) { floating_sel_anchor (image->floating_sel); had_floating_sel = TRUE; } for (list = GIMP_LIST (image->layers)->list; list; list = g_list_next (list)) { layer = list->data; if (gimp_item_get_visible (GIMP_ITEM (layer))) { merge_list = g_slist_append (merge_list, layer); } else if (discard_invisible) { invisible_list = g_slist_append (invisible_list, layer); } } if (merge_list) { const gchar *undo_desc = _("Merge Visible Layers"); gimp_set_busy (image->gimp); if (invisible_list) { gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, undo_desc); } layer = gimp_image_merge_layers (image, merge_list, context, merge_type, _("Merge Visible Layers")); g_slist_free (merge_list); if (invisible_list) { GSList *list; for (list = invisible_list; list; list = g_slist_next (list)) gimp_image_remove_layer (image, list->data); gimp_image_undo_group_end (image); g_slist_free (invisible_list); } gimp_unset_busy (image->gimp); return layer; } return gimp_image_get_active_layer (image); }
void gimp_image_flip (GimpImage *image, GimpContext *context, GimpOrientationType flip_type, GimpProgress *progress) { GList *list; gdouble axis; gdouble progress_max; gdouble progress_current = 1.0; g_return_if_fail (GIMP_IS_IMAGE (image)); g_return_if_fail (GIMP_IS_CONTEXT (context)); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); gimp_set_busy (image->gimp); switch (flip_type) { case GIMP_ORIENTATION_HORIZONTAL: axis = (gdouble) gimp_image_get_width (image) / 2.0; break; case GIMP_ORIENTATION_VERTICAL: axis = (gdouble) gimp_image_get_height (image) / 2.0; break; default: g_warning ("%s: unknown flip_type", G_STRFUNC); return; } progress_max = (image->channels->num_children + image->layers->num_children + image->vectors->num_children + 1 /* selection */); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_FLIP, NULL); /* Flip all channels */ for (list = GIMP_LIST (image->channels)->list; list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_flip (item, context, flip_type, axis, TRUE); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Flip all vectors */ for (list = GIMP_LIST (image->vectors)->list; list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_flip (item, context, flip_type, axis, FALSE); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Don't forget the selection mask! */ gimp_item_flip (GIMP_ITEM (gimp_image_get_mask (image)), context, flip_type, axis, TRUE); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); /* Flip all layers */ for (list = GIMP_LIST (image->layers)->list; list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_flip (item, context, flip_type, axis, FALSE); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Flip all Guides */ for (list = gimp_image_get_guides (image); list; list = g_list_next (list)) { GimpGuide *guide = list->data; gint position = gimp_guide_get_position (guide); switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_HORIZONTAL: if (flip_type == GIMP_ORIENTATION_VERTICAL) gimp_image_move_guide (image, guide, gimp_image_get_height (image) - position, TRUE); break; case GIMP_ORIENTATION_VERTICAL: if (flip_type == GIMP_ORIENTATION_HORIZONTAL) gimp_image_move_guide (image, guide, gimp_image_get_width (image) - position, TRUE); break; default: break; } } /* Flip all sample points */ for (list = gimp_image_get_sample_points (image); list; list = g_list_next (list)) { GimpSamplePoint *sample_point = list->data; if (flip_type == GIMP_ORIENTATION_VERTICAL) gimp_image_move_sample_point (image, sample_point, sample_point->x, gimp_image_get_height (image) - sample_point->y, TRUE); if (flip_type == GIMP_ORIENTATION_HORIZONTAL) gimp_image_move_sample_point (image, sample_point, gimp_image_get_width (image) - sample_point->x, sample_point->y, TRUE); } gimp_image_undo_group_end (image); gimp_unset_busy (image->gimp); }
static GimpValueArray * xcf_save_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { XcfInfo info; GimpValueArray *return_vals; GimpImage *image; const gchar *filename; gboolean success = FALSE; gimp_set_busy (gimp); image = gimp_value_get_image (gimp_value_array_index (args, 1), gimp); filename = g_value_get_string (gimp_value_array_index (args, 3)); info.fp = g_fopen (filename, "wb"); if (info.fp) { info.gimp = gimp; info.progress = progress; info.cp = 0; info.filename = filename; info.active_layer = NULL; info.active_channel = NULL; info.floating_sel_drawable = NULL; info.floating_sel = NULL; info.floating_sel_offset = 0; info.swap_num = 0; info.ref_count = NULL; info.compression = COMPRESS_RLE; if (progress) { gchar *name = g_filename_display_name (filename); gchar *msg = g_strdup_printf (_("Saving '%s'"), name); gimp_progress_start (progress, msg, FALSE); g_free (msg); g_free (name); } xcf_save_choose_format (&info, image); success = xcf_save_image (&info, image, error); if (success) { if (fclose (info.fp) == EOF) { int save_errno = errno; g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Error saving XCF file: %s"), g_strerror (save_errno)); success = FALSE; } } else { fclose (info.fp); } if (progress) gimp_progress_end (progress); } else { int save_errno = errno; g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Could not open '%s' for writing: %s"), gimp_filename_to_utf8 (filename), g_strerror (save_errno)); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); gimp_unset_busy (gimp); return return_vals; }
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); }
void gimp_drawable_bucket_fill_full (GimpDrawable *drawable, GimpBucketFillMode fill_mode, gint paint_mode, gdouble opacity, gboolean do_seed_fill, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gdouble x, gdouble y, const GimpRGB *color, GimpPattern *pattern) { GimpImage *image; TileManager *buf_tiles; PixelRegion bufPR, maskPR; GimpChannel *mask = NULL; gint bytes; gint x1, y1, x2, y2; guchar col[MAX_CHANNELS]; TempBuf *pat_buf = NULL; gboolean new_buf = FALSE; gboolean selection; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); g_return_if_fail (fill_mode != GIMP_PATTERN_BUCKET_FILL || GIMP_IS_PATTERN (pattern)); g_return_if_fail (fill_mode == GIMP_PATTERN_BUCKET_FILL || color != NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); bytes = gimp_drawable_bytes (drawable); selection = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2); if ((x1 == x2) || (y1 == y2)) return; if (fill_mode == GIMP_FG_BUCKET_FILL || fill_mode == GIMP_BG_BUCKET_FILL) { guchar tmp_col[MAX_CHANNELS]; gimp_rgb_get_uchar (color, &tmp_col[RED], &tmp_col[GREEN], &tmp_col[BLUE]); gimp_image_transform_color (image, gimp_drawable_type (drawable), col, GIMP_RGB, tmp_col); col[gimp_drawable_bytes_with_alpha (drawable) - 1] = OPAQUE_OPACITY; } else if (fill_mode == GIMP_PATTERN_BUCKET_FILL) { pat_buf = gimp_image_transform_temp_buf (image, gimp_drawable_type (drawable), pattern->mask, &new_buf); } else { g_warning ("%s: invalid fill_mode passed", G_STRFUNC); return; } gimp_set_busy (image->gimp); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. If there is a selection, calculate the * intersection of this region with the existing selection. */ if (do_seed_fill) { mask = gimp_image_contiguous_region_by_seed (image, drawable, sample_merged, TRUE, (gint) threshold, fill_transparent, fill_criterion, (gint) x, (gint) y); if (selection) { gint off_x = 0; gint off_y = 0; if (! sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); gimp_channel_combine_mask (mask, gimp_image_get_mask (image), GIMP_CHANNEL_OP_INTERSECT, -off_x, -off_y); } gimp_channel_bounds (mask, &x1, &y1, &x2, &y2); /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item; gint off_x, off_y; item = GIMP_ITEM (drawable); /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); x1 = CLAMP (x1, off_x, (off_x + gimp_item_get_width (item))); y1 = CLAMP (y1, off_y, (off_y + gimp_item_get_height (item))); x2 = CLAMP (x2, off_x, (off_x + gimp_item_get_width (item))); y2 = CLAMP (y2, off_y, (off_y + gimp_item_get_height (item))); pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1, y1, (x2 - x1), (y2 - y1), TRUE); /* translate mask bounds to drawable coords */ x1 -= off_x; y1 -= off_y; x2 -= off_x; y2 -= off_y; } else { pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1, y1, (x2 - x1), (y2 - y1), TRUE); } /* if the image doesn't have an alpha channel, make sure that * the buf_tiles have. We need the alpha channel to fill with * the region calculated above */ if (! gimp_drawable_has_alpha (drawable)) bytes++; } else if (fill_mode == GIMP_PATTERN_BUCKET_FILL && (pat_buf->bytes == 2 || pat_buf->bytes == 4)) { /* If pattern being applied has an alpha channel, add one to the * buf_tiles. */ if (! gimp_drawable_has_alpha (drawable)) bytes++; } buf_tiles = tile_manager_new ((x2 - x1), (y2 - y1), bytes); pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE); switch (fill_mode) { case GIMP_FG_BUCKET_FILL: case GIMP_BG_BUCKET_FILL: if (mask) color_region_mask (&bufPR, &maskPR, col); else color_region (&bufPR, col); break; case GIMP_PATTERN_BUCKET_FILL: if (mask) pattern_region (&bufPR, &maskPR, pat_buf, x1, y1); else pattern_region (&bufPR, NULL, pat_buf, x1, y1); break; } /* Apply it to the image */ pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), FALSE); gimp_drawable_apply_region (drawable, &bufPR, TRUE, C_("undo-type", "Bucket Fill"), opacity, paint_mode, NULL, NULL, x1, y1); tile_manager_unref (buf_tiles); /* update the image */ gimp_drawable_update (drawable, x1, y1, x2 - x1, y2 - y1); /* free the mask */ if (mask) g_object_unref (mask); if (new_buf) temp_buf_free (pat_buf); gimp_unset_busy (image->gimp); }
GimpLayer * gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, gboolean discard_invisible) { GimpContainer *container; GList *list; GSList *merge_list = NULL; GSList *invisible_list = NULL; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); if (merge_active_group) { GimpLayer *active_layer = gimp_image_get_active_layer (image); /* if the active layer is the floating selection, get the * underlying drawable, but only if it is a layer */ if (active_layer && gimp_layer_is_floating_sel (active_layer)) { GimpDrawable *fs_drawable; fs_drawable = gimp_layer_get_floating_sel_drawable (active_layer); if (GIMP_IS_LAYER (fs_drawable)) active_layer = GIMP_LAYER (fs_drawable); } if (active_layer) container = gimp_item_get_container (GIMP_ITEM (active_layer)); else container = gimp_image_get_layers (image); } else { container = gimp_image_get_layers (image); } for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (container)); list; list = g_list_next (list)) { GimpLayer *layer = list->data; if (gimp_layer_is_floating_sel (layer)) continue; if (gimp_item_get_visible (GIMP_ITEM (layer))) { merge_list = g_slist_append (merge_list, layer); } else if (discard_invisible) { invisible_list = g_slist_append (invisible_list, layer); } } if (merge_list) { GimpLayer *layer; gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, C_("undo-type", "Merge Visible Layers")); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) floating_sel_anchor (gimp_image_get_floating_selection (image)); layer = gimp_image_merge_layers (image, container, merge_list, context, merge_type); g_slist_free (merge_list); if (invisible_list) { GSList *list; for (list = invisible_list; list; list = g_slist_next (list)) gimp_image_remove_layer (image, list->data, TRUE, NULL); g_slist_free (invisible_list); } gimp_image_undo_group_end (image); gimp_unset_busy (image->gimp); return layer; } return gimp_image_get_active_layer (image); }
static void gimp_thumb_box_create_thumbnails (GimpThumbBox *box, gboolean force) { Gimp *gimp = box->imagefile->gimp; GimpProgress *progress = GIMP_PROGRESS (box); GimpFileDialog *dialog = NULL; GtkWidget *toplevel; GSList *list; gint n_uris; gint i; if (gimp->config->thumbnail_size == GIMP_THUMBNAIL_SIZE_NONE) return; toplevel = gtk_widget_get_toplevel (GTK_WIDGET (box)); if (GIMP_IS_FILE_DIALOG (toplevel)) dialog = GIMP_FILE_DIALOG (toplevel); gimp_set_busy (gimp); if (dialog) gimp_file_dialog_set_sensitive (dialog, FALSE); else gtk_widget_set_sensitive (toplevel, FALSE); if (box->uris) { gtk_widget_hide (box->info); gtk_widget_show (box->progress); } n_uris = g_slist_length (box->uris); if (n_uris > 1) { gchar *str; gimp_progress_start (GIMP_PROGRESS (box), "", TRUE); progress = gimp_sub_progress_new (GIMP_PROGRESS (box)); gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (progress), 0, n_uris); for (list = box->uris->next, i = 1; list; list = g_slist_next (list), i++) { str = g_strdup_printf (_("Thumbnail %d of %d"), i, n_uris); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (box->progress), str); g_free (str); gimp_progress_set_value (progress, 0.0); while (gtk_events_pending ()) gtk_main_iteration (); gimp_thumb_box_create_thumbnail (box, list->data, gimp->config->thumbnail_size, force, progress); if (dialog && dialog->canceled) goto canceled; gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (progress), i, n_uris); } str = g_strdup_printf (_("Thumbnail %d of %d"), n_uris, n_uris); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (box->progress), str); g_free (str); gimp_progress_set_value (progress, 0.0); while (gtk_events_pending ()) gtk_main_iteration (); } if (box->uris) { gimp_thumb_box_create_thumbnail (box, box->uris->data, gimp->config->thumbnail_size, force, progress); gimp_progress_set_value (progress, 1.0); } canceled: if (n_uris > 1) { g_object_unref (progress); gimp_progress_end (GIMP_PROGRESS (box)); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (box->progress), ""); } if (box->uris) { gtk_widget_hide (box->progress); gtk_widget_show (box->info); } if (dialog) gimp_file_dialog_set_sensitive (dialog, TRUE); else gtk_widget_set_sensitive (toplevel, TRUE); gimp_unset_busy (gimp); }
/** * 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_image_resize_with_layers (GimpImage *image, GimpContext *context, gint new_width, gint new_height, gint offset_x, gint offset_y, GimpItemSet layer_set, gboolean resize_text_layers, GimpProgress *progress) { GList *list; GList *resize_layers; gdouble progress_max; gdouble progress_current = 1.0; gint old_width, old_height; g_return_if_fail (GIMP_IS_IMAGE (image)); g_return_if_fail (GIMP_IS_CONTEXT (context)); g_return_if_fail (new_width > 0 && new_height > 0); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); gimp_set_busy (image->gimp); g_object_freeze_notify (G_OBJECT (image)); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_RESIZE, C_("undo-type", "Resize Image")); resize_layers = gimp_image_item_list_get_list (image, NULL, GIMP_ITEM_TYPE_LAYERS, layer_set); progress_max = (gimp_container_get_n_children (gimp_image_get_layers (image)) + gimp_container_get_n_children (gimp_image_get_channels (image)) + gimp_container_get_n_children (gimp_image_get_vectors (image)) + g_list_length (resize_layers) + 1 /* selection */); old_width = gimp_image_get_width (image); old_height = gimp_image_get_height (image); /* Push the image size to the stack */ gimp_image_undo_push_image_size (image, NULL, -offset_x, -offset_y, new_width, new_height); /* Set the new width and height */ g_object_set (image, "width", new_width, "height", new_height, NULL); /* Resize all channels */ for (list = gimp_image_get_channel_iter (image); list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_resize (item, context, new_width, new_height, offset_x, offset_y); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Resize all vectors */ for (list = gimp_image_get_vectors_iter (image); list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_resize (item, context, new_width, new_height, offset_x, offset_y); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Don't forget the selection mask! */ gimp_item_resize (GIMP_ITEM (gimp_image_get_mask (image)), context, new_width, new_height, offset_x, offset_y); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); /* Reposition all layers */ for (list = gimp_image_get_layer_iter (image); list; list = g_list_next (list)) { GimpItem *item = list->data; gimp_item_translate (item, offset_x, offset_y, TRUE); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } /* Resize all resize_layers to image size */ for (list = resize_layers; list; list = g_list_next (list)) { GimpItem *item = list->data; gint old_offset_x; gint old_offset_y; /* group layers can't be resized here */ if (gimp_viewable_get_children (GIMP_VIEWABLE (item))) continue; if (! resize_text_layers && gimp_item_is_text_layer (item)) continue; gimp_item_get_offset (item, &old_offset_x, &old_offset_y); gimp_item_resize (item, context, new_width, new_height, old_offset_x, old_offset_y); if (progress) gimp_progress_set_value (progress, progress_current++ / progress_max); } g_list_free (resize_layers); /* Reposition or remove all guides */ list = gimp_image_get_guides (image); while (list) { GimpGuide *guide = list->data; gboolean remove_guide = FALSE; gint new_position = gimp_guide_get_position (guide); list = g_list_next (list); switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_HORIZONTAL: new_position += offset_y; if (new_position < 0 || new_position > new_height) remove_guide = TRUE; break; case GIMP_ORIENTATION_VERTICAL: new_position += offset_x; if (new_position < 0 || new_position > new_width) remove_guide = TRUE; break; default: break; } if (remove_guide) gimp_image_remove_guide (image, guide, TRUE); else if (new_position != gimp_guide_get_position (guide)) gimp_image_move_guide (image, guide, new_position, TRUE); } /* Reposition or remove sample points */ list = gimp_image_get_sample_points (image); while (list) { GimpSamplePoint *sample_point = list->data; gboolean remove_sample_point = FALSE; gint new_x = sample_point->x; gint new_y = sample_point->y; list = g_list_next (list); new_y += offset_y; if ((sample_point->y < 0) || (sample_point->y > new_height)) remove_sample_point = TRUE; new_x += offset_x; if ((sample_point->x < 0) || (sample_point->x > new_width)) remove_sample_point = TRUE; if (remove_sample_point) gimp_image_remove_sample_point (image, sample_point, TRUE); else if (new_x != sample_point->x || new_y != sample_point->y) gimp_image_move_sample_point (image, sample_point, new_x, new_y, TRUE); } gimp_image_undo_group_end (image); gimp_image_size_changed_detailed (image, offset_x, offset_y, old_width, old_height); g_object_thaw_notify (G_OBJECT (image)); gimp_unset_busy (image->gimp); }
/** * 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; }
static GimpValueArray * xcf_load_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { XcfInfo info; GimpValueArray *return_vals; GimpImage *image = NULL; const gchar *filename; gboolean success = FALSE; gchar id[14]; gimp_set_busy (gimp); filename = g_value_get_string (gimp_value_array_index (args, 1)); info.fp = g_fopen (filename, "rb"); if (info.fp) { info.gimp = gimp; info.progress = progress; info.cp = 0; info.filename = filename; info.tattoo_state = 0; info.active_layer = NULL; info.active_channel = NULL; info.floating_sel_drawable = NULL; info.floating_sel = NULL; info.floating_sel_offset = 0; info.swap_num = 0; info.ref_count = NULL; info.compression = COMPRESS_NONE; if (progress) { gchar *name = g_filename_display_name (filename); gchar *msg = g_strdup_printf (_("Opening '%s'"), name); gimp_progress_start (progress, msg, FALSE); g_free (msg); g_free (name); } success = TRUE; info.cp += xcf_read_int8 (info.fp, (guint8 *) id, 14); if (! g_str_has_prefix (id, "gimp xcf ")) { success = FALSE; } else if (strcmp (id + 9, "file") == 0) { info.file_version = 0; } else if (id[9] == 'v') { info.file_version = atoi (id + 10); } else { success = FALSE; } if (success) { if (info.file_version >= 0 && info.file_version < G_N_ELEMENTS (xcf_loaders)) { image = (*(xcf_loaders[info.file_version])) (gimp, &info, error); if (! image) success = FALSE; } else { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("XCF error: unsupported XCF file version %d " "encountered"), info.file_version); success = FALSE; } } fclose (info.fp); if (progress) gimp_progress_end (progress); } else { int save_errno = errno; g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (save_errno)); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) gimp_value_set_image (gimp_value_array_index (return_vals, 1), image); gimp_unset_busy (gimp); return return_vals; }
GimpLayer * gimp_image_merge_down (GimpImage *image, GimpLayer *current_layer, GimpContext *context, GimpMergeType merge_type, GError **error) { GimpLayer *layer; GList *list; GList *layer_list = NULL; GSList *merge_list = NULL; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_LAYER (current_layer), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (current_layer)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (list = gimp_item_get_container_iter (GIMP_ITEM (current_layer)); list; list = g_list_next (list)) { layer = list->data; if (layer == current_layer) break; } for (layer_list = g_list_next (list); layer_list; layer_list = g_list_next (layer_list)) { layer = layer_list->data; if (gimp_item_get_visible (GIMP_ITEM (layer))) { if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) { g_set_error_literal (error, 0, 0, _("Cannot merge down to a layer group.")); return NULL; } if (gimp_item_is_content_locked (GIMP_ITEM (layer))) { g_set_error_literal (error, 0, 0, _("The layer to merge down to is locked.")); return NULL; } merge_list = g_slist_append (NULL, layer); break; } } if (! merge_list) { g_set_error_literal (error, 0, 0, _("There is no visible layer to merge down to.")); return NULL; } merge_list = g_slist_prepend (merge_list, current_layer); gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, C_("undo-type", "Merge Down")); layer = gimp_image_merge_layers (image, gimp_item_get_container (GIMP_ITEM (current_layer)), merge_list, context, merge_type); g_slist_free (merge_list); gimp_image_undo_group_end (image); gimp_unset_busy (image->gimp); return layer; }
void gimp_drawable_blend (GimpDrawable *drawable, GimpContext *context, GimpBlendMode blend_mode, GimpLayerModeEffects paint_mode, GimpGradientType gradient_type, gdouble opacity, gdouble offset, GimpRepeatMode repeat, gboolean reverse, gboolean supersample, gint max_depth, gdouble threshold, gboolean dither, gdouble startx, gdouble starty, gdouble endx, gdouble endy, GimpProgress *progress) { GimpImage *image; GeglBuffer *buffer; gint x, y, width, 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_CONTEXT (context)); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) return; gimp_set_busy (image->gimp); /* Always create an alpha temp buf (for generality) */ buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gimp_drawable_get_format_with_alpha (drawable)); gradient_fill_region (image, drawable, context, buffer, GEGL_RECTANGLE (0, 0, width, height), blend_mode, gradient_type, offset, repeat, reverse, supersample, max_depth, threshold, dither, (startx - x), (starty - y), (endx - x), (endy - y), progress); gimp_drawable_apply_buffer (drawable, buffer, GEGL_RECTANGLE (0, 0, width, height), TRUE, C_("undo-type", "Blend"), opacity, paint_mode, NULL, x, y); /* update the image */ gimp_drawable_update (drawable, x, y, width, height); /* free the temporary buffer */ g_object_unref (buffer); gimp_unset_busy (image->gimp); }
GimpVectors * gimp_image_merge_visible_vectors (GimpImage *image, GError **error) { GList *list = NULL; GSList *merge_list = NULL; GSList *cur_item = NULL; GimpVectors *vectors = NULL; GimpVectors *target_vectors = NULL; gchar *name = NULL; gint pos = 0; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (list = GIMP_LIST (image->vectors)->list; list; list = g_list_next (list)) { vectors = list->data; if (gimp_item_get_visible (GIMP_ITEM (vectors))) merge_list = g_slist_append (merge_list, vectors); } if (merge_list && merge_list->next) { gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, _("Merge Visible Paths")); cur_item = merge_list; vectors = GIMP_VECTORS (cur_item->data); name = g_strdup (gimp_object_get_name (GIMP_OBJECT (vectors))); target_vectors = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), GIMP_TYPE_VECTORS)); pos = gimp_image_get_vectors_index (image, vectors); gimp_image_remove_vectors (image, vectors); cur_item = cur_item->next; while (cur_item) { vectors = GIMP_VECTORS (cur_item->data); gimp_vectors_add_strokes (vectors, target_vectors); gimp_image_remove_vectors (image, vectors); cur_item = g_slist_next (cur_item); } gimp_object_take_name (GIMP_OBJECT (target_vectors), name); g_slist_free (merge_list); gimp_image_add_vectors (image, target_vectors, pos); gimp_unset_busy (image->gimp); gimp_image_undo_group_end (image); return target_vectors; } else { g_set_error (error, 0, 0, _("Not enough visible paths for a merge. " "There must be at least two.")); return NULL; } }