/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include "libgimpcolor/gimpcolor.h" #include "libgimpmath/gimpmath.h" #include "libgimpbase/gimpbase.h" #include "core-types.h" #include "base/pixel-region.h" #include "base/temp-buf.h" #include "base/tile-manager.h" #include "paint-funcs/paint-funcs.h" #include "config/gimpcoreconfig.h" #include "gimp.h" #include "gimp-parasites.h" #include "gimpcontext.h" #include "gimpimage.h" #include "gimpimage-colorhash.h" #include "gimpimage-colormap.h" #include "gimpimage-guides.h" #include "gimpimage-mask.h" #include "gimpimage-preview.h" #include "gimpimage-projection.h" #include "gimpimage-undo.h" #include "gimpimage-undo-push.h" #include "gimplayer.h" #include "gimplayer-floating-sel.h" #include "gimplayermask.h" #include "gimplist.h" #include "gimpmarshal.h" #include "gimpparasitelist.h" #include "gimpselection.h" #include "gimpundostack.h" #include "file/file-utils.h" #include "vectors/gimpvectors.h" #include "gimp-intl.h" #ifdef DEBUG #define TRC(x) printf x #else #define TRC(x) #endif enum { MODE_CHANGED, ALPHA_CHANGED, FLOATING_SELECTION_CHANGED, ACTIVE_LAYER_CHANGED, ACTIVE_CHANNEL_CHANGED, ACTIVE_VECTORS_CHANGED, COMPONENT_VISIBILITY_CHANGED, COMPONENT_ACTIVE_CHANGED, GRID_CHANGED, MASK_CHANGED, RESOLUTION_CHANGED, UNIT_CHANGED, QMASK_CHANGED, SELECTION_CONTROL, CLEAN, DIRTY, UPDATE, UPDATE_GUIDE, COLORMAP_CHANGED, UNDO_START, UNDO_EVENT, FLUSH, LAST_SIGNAL }; /* local function prototypes */ static void gimp_image_class_init (GimpImageClass *klass); static void gimp_image_init (GimpImage *gimage); static void gimp_image_dispose (GObject *object); static void gimp_image_finalize (GObject *object); static void gimp_image_name_changed (GimpObject *object); static gsize gimp_image_get_memsize (GimpObject *object, gsize *gui_size); static void gimp_image_invalidate_preview (GimpViewable *viewable); static void gimp_image_size_changed (GimpViewable *viewable); static gchar * gimp_image_get_description (GimpViewable *viewable, gchar **tooltip); static void gimp_image_real_colormap_changed (GimpImage *gimage, gint ncol); static void gimp_image_drawable_update (GimpDrawable *drawable, gint x, gint y, gint width, gint height, GimpImage *gimage); static void gimp_image_drawable_visibility (GimpItem *item, GimpImage *gimage); static void gimp_image_drawable_add (GimpContainer *container, GimpItem *item, GimpImage *gimage); static void gimp_image_drawable_remove (GimpContainer *container, GimpItem *item, GimpImage *gimage); static void gimp_image_get_active_components (const GimpImage *gimage, const GimpDrawable *drawable, gboolean *active); static gint valid_combinations[][MAX_CHANNELS + 1] = { /* GIMP_RGB_IMAGE */ { -1, -1, -1, COMBINE_INTEN_INTEN, COMBINE_INTEN_INTEN_A }, /* GIMP_RGBA_IMAGE */ { -1, -1, -1, COMBINE_INTEN_A_INTEN, COMBINE_INTEN_A_INTEN_A }, /* GIMP_GRAY_IMAGE */ { -1, COMBINE_INTEN_INTEN, COMBINE_INTEN_INTEN_A, -1, -1 }, /* GIMP_GRAYA_IMAGE */ { -1, COMBINE_INTEN_A_INTEN, COMBINE_INTEN_A_INTEN_A, -1, -1 }, /* GIMP_INDEXED_IMAGE */ { -1, COMBINE_INDEXED_INDEXED, COMBINE_INDEXED_INDEXED_A, -1, -1 }, /* GIMP_INDEXEDA_IMAGE */ { -1, -1, COMBINE_INDEXED_A_INDEXED_A, -1, -1 }, }; static guint gimp_image_signals[LAST_SIGNAL] = { 0 }; static GimpViewableClass *parent_class = NULL; GType gimp_image_get_type (void) { static GType image_type = 0; if (! image_type) { static const GTypeInfo image_info = { sizeof (GimpImageClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gimp_image_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GimpImage), 0, /* n_preallocs */ (GInstanceInitFunc) gimp_image_init, }; image_type = g_type_register_static (GIMP_TYPE_VIEWABLE, "GimpImage", &image_info, 0); } return image_type; } /* private functions */ static void gimp_image_class_init (GimpImageClass *klass) { GObjectClass *object_class; GimpObjectClass *gimp_object_class; GimpViewableClass *viewable_class; object_class = G_OBJECT_CLASS (klass); gimp_object_class = GIMP_OBJECT_CLASS (klass); viewable_class = GIMP_VIEWABLE_CLASS (klass); parent_class = g_type_class_peek_parent (klass); gimp_image_signals[MODE_CHANGED] = g_signal_new ("mode_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, mode_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[ALPHA_CHANGED] = g_signal_new ("alpha_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, alpha_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[FLOATING_SELECTION_CHANGED] = g_signal_new ("floating_selection_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, floating_selection_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[ACTIVE_LAYER_CHANGED] = g_signal_new ("active_layer_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, active_layer_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[ACTIVE_CHANNEL_CHANGED] = g_signal_new ("active_channel_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, active_channel_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[ACTIVE_VECTORS_CHANGED] = g_signal_new ("active_vectors_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, active_vectors_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[COMPONENT_VISIBILITY_CHANGED] = g_signal_new ("component_visibility_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, component_visibility_changed), NULL, NULL, gimp_marshal_VOID__ENUM, G_TYPE_NONE, 1, GIMP_TYPE_CHANNEL_TYPE); gimp_image_signals[COMPONENT_ACTIVE_CHANGED] = g_signal_new ("component_active_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, component_active_changed), NULL, NULL, gimp_marshal_VOID__ENUM, G_TYPE_NONE, 1, GIMP_TYPE_CHANNEL_TYPE); gimp_image_signals[GRID_CHANGED] = g_signal_new ("grid_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, grid_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[MASK_CHANGED] = g_signal_new ("mask_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, mask_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[RESOLUTION_CHANGED] = g_signal_new ("resolution_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, resolution_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[UNIT_CHANGED] = g_signal_new ("unit_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, unit_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[QMASK_CHANGED] = g_signal_new ("qmask_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, qmask_changed), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[SELECTION_CONTROL] = g_signal_new ("selection_control", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, selection_control), NULL, NULL, gimp_marshal_VOID__ENUM, G_TYPE_NONE, 1, GIMP_TYPE_SELECTION_CONTROL); gimp_image_signals[CLEAN] = g_signal_new ("clean", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, clean), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[DIRTY] = g_signal_new ("dirty", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, dirty), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[UPDATE] = g_signal_new ("update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, update), NULL, NULL, gimp_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); gimp_image_signals[UPDATE_GUIDE] = g_signal_new ("update_guide", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, update_guide), NULL, NULL, gimp_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gimp_image_signals[COLORMAP_CHANGED] = g_signal_new ("colormap_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, colormap_changed), NULL, NULL, gimp_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); gimp_image_signals[UNDO_START] = g_signal_new ("undo_start", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, undo_start), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); gimp_image_signals[UNDO_EVENT] = g_signal_new ("undo_event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, undo_event), NULL, NULL, gimp_marshal_VOID__ENUM_OBJECT, G_TYPE_NONE, 2, GIMP_TYPE_UNDO_EVENT, GIMP_TYPE_UNDO); gimp_image_signals[FLUSH] = g_signal_new ("flush", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpImageClass, flush), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); object_class->dispose = gimp_image_dispose; object_class->finalize = gimp_image_finalize; gimp_object_class->name_changed = gimp_image_name_changed; gimp_object_class->get_memsize = gimp_image_get_memsize; viewable_class->default_stock_id = "gimp-image"; viewable_class->invalidate_preview = gimp_image_invalidate_preview; viewable_class->size_changed = gimp_image_size_changed; viewable_class->get_preview_size = gimp_image_get_preview_size; viewable_class->get_popup_size = gimp_image_get_popup_size; viewable_class->get_preview = gimp_image_get_preview; viewable_class->get_new_preview = gimp_image_get_new_preview; viewable_class->get_description = gimp_image_get_description; klass->grid_changed = NULL; klass->mode_changed = NULL; klass->alpha_changed = NULL; klass->floating_selection_changed = NULL; klass->active_layer_changed = NULL; klass->active_channel_changed = NULL; klass->active_vectors_changed = NULL; klass->component_visibility_changed = NULL; klass->component_active_changed = NULL; klass->mask_changed = NULL; klass->clean = NULL; klass->dirty = NULL; klass->update = NULL; klass->update_guide = NULL; klass->colormap_changed = gimp_image_real_colormap_changed; klass->undo_start = NULL; klass->undo_event = NULL; klass->flush = NULL; gimp_image_color_hash_init (); } static void gimp_image_init (GimpImage *gimage) { gimage->ID = 0; gimage->save_proc = NULL; gimage->width = 0; gimage->height = 0; gimage->xresolution = 1.0; gimage->yresolution = 1.0; gimage->unit = GIMP_UNIT_INCH; gimage->base_type = GIMP_RGB; gimage->cmap = NULL; gimage->num_cols = 0; gimage->dirty = 1; gimage->undo_on = TRUE; gimage->instance_count = 0; gimage->disp_count = 0; gimage->tattoo_state = 0; gimage->shadow = NULL; gimage->construct_flag = FALSE; gimage->proj_type = GIMP_RGBA_IMAGE; gimage->projection = NULL; gimage->guides = NULL; gimage->grid = NULL; gimage->layers = gimp_list_new (GIMP_TYPE_LAYER, GIMP_CONTAINER_POLICY_STRONG); gimage->channels = gimp_list_new (GIMP_TYPE_CHANNEL, GIMP_CONTAINER_POLICY_STRONG); gimage->vectors = gimp_list_new (GIMP_TYPE_VECTORS, GIMP_CONTAINER_POLICY_STRONG); gimage->layer_stack = NULL; gimage->layer_update_handler = gimp_container_add_handler (gimage->layers, "update", G_CALLBACK (gimp_image_drawable_update), gimage); gimage->channel_update_handler = gimp_container_add_handler (gimage->channels, "update", G_CALLBACK (gimp_image_drawable_update), gimage); gimage->layer_visible_handler = gimp_container_add_handler (gimage->layers, "visibility_changed", G_CALLBACK (gimp_image_drawable_visibility), gimage); gimage->channel_visible_handler = gimp_container_add_handler (gimage->channels, "visibility_changed", G_CALLBACK (gimp_image_drawable_visibility), gimage); g_signal_connect (gimage->layers, "add", G_CALLBACK (gimp_image_drawable_add), gimage); g_signal_connect (gimage->channels, "add", G_CALLBACK (gimp_image_drawable_add), gimage); g_signal_connect (gimage->layers, "remove", G_CALLBACK (gimp_image_drawable_remove), gimage); g_signal_connect (gimage->channels, "remove", G_CALLBACK (gimp_image_drawable_remove), gimage); gimage->active_layer = NULL; gimage->active_channel = NULL; gimage->active_vectors = NULL; gimage->floating_sel = NULL; gimage->selection_mask = NULL; gimage->parasites = gimp_parasite_list_new (); gimage->qmask_state = FALSE; gimage->qmask_inverted = FALSE; gimage->qmask_color.r = 1.0; gimage->qmask_color.g = 0.0; gimage->qmask_color.b = 0.0; gimage->qmask_color.a = 0.5; gimage->undo_stack = gimp_undo_stack_new (gimage); gimage->redo_stack = gimp_undo_stack_new (gimage); gimage->group_count = 0; gimage->pushing_undo_group = GIMP_UNDO_GROUP_NONE; gimage->comp_preview = NULL; gimage->comp_preview_valid = FALSE; } static void gimp_image_dispose (GObject *object) { GimpImage *gimage = GIMP_IMAGE (object); gimp_image_undo_free (gimage); gimp_container_remove_handler (gimage->layers, gimage->layer_update_handler); gimp_container_remove_handler (gimage->channels, gimage->channel_update_handler); gimp_container_remove_handler (gimage->layers, gimage->layer_visible_handler); gimp_container_remove_handler (gimage->channels, gimage->channel_visible_handler); g_signal_handlers_disconnect_by_func (gimage->layers, gimp_image_drawable_add, gimage); g_signal_handlers_disconnect_by_func (gimage->channels, gimp_image_drawable_add, gimage); g_signal_handlers_disconnect_by_func (gimage->layers, gimp_image_drawable_remove, gimage); g_signal_handlers_disconnect_by_func (gimage->channels, gimp_image_drawable_remove, gimage); G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_image_finalize (GObject *object) { GimpImage *gimage = GIMP_IMAGE (object); if (gimage->gimp && gimage->gimp->image_table) { g_hash_table_remove (gimage->gimp->image_table, GINT_TO_POINTER (gimage->ID)); gimage->gimp = NULL; } if (gimage->projection) gimp_image_projection_free (gimage); if (gimage->shadow) gimp_image_free_shadow (gimage); if (gimage->cmap) { g_free (gimage->cmap); gimage->cmap = NULL; } if (gimage->layers) { g_object_unref (gimage->layers); gimage->layers = NULL; } if (gimage->channels) { g_object_unref (gimage->channels); gimage->channels = NULL; } if (gimage->vectors) { g_object_unref (gimage->vectors); gimage->vectors = NULL; } if (gimage->layer_stack) { g_slist_free (gimage->layer_stack); gimage->layer_stack = NULL; } if (gimage->selection_mask) { g_object_unref (gimage->selection_mask); gimage->selection_mask = NULL; } if (gimage->comp_preview) { temp_buf_free (gimage->comp_preview); gimage->comp_preview = NULL; } if (gimage->parasites) { g_object_unref (gimage->parasites); gimage->parasites = NULL; } if (gimage->guides) { g_list_foreach (gimage->guides, (GFunc) gimp_image_guide_unref, NULL); g_list_free (gimage->guides); gimage->guides = NULL; } if (gimage->grid) { g_object_unref (gimage->grid); gimage->grid = NULL; } if (gimage->undo_stack) { g_object_unref (gimage->undo_stack); gimage->undo_stack = NULL; } if (gimage->redo_stack) { g_object_unref (gimage->redo_stack); gimage->redo_stack = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } static void gimp_image_name_changed (GimpObject *object) { GimpImage *gimage; const gchar *name; if (GIMP_OBJECT_CLASS (parent_class)->name_changed) GIMP_OBJECT_CLASS (parent_class)->name_changed (object); gimage = GIMP_IMAGE (object); name = gimp_object_get_name (object); if (! (name && strlen (name))) { g_free (object->name); object->name = NULL; } } static gsize gimp_image_get_memsize (GimpObject *object, gsize *gui_size) { GimpImage *gimage; gsize memsize = 0; gimage = GIMP_IMAGE (object); if (gimage->cmap) memsize += GIMP_IMAGE_COLORMAP_SIZE; if (gimage->shadow) memsize += tile_manager_get_memsize (gimage->shadow); if (gimage->projection) memsize += tile_manager_get_memsize (gimage->projection); memsize += (g_list_length (gimage->guides) * (sizeof (GList) + sizeof (GimpGuide))); if (gimage->grid) memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->grid), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->layers), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->channels), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->vectors), gui_size); memsize += g_slist_length (gimage->layer_stack) * sizeof (GSList); if (gimage->selection_mask) memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->selection_mask), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->parasites), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->undo_stack), gui_size); memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->redo_stack), gui_size); if (gimage->comp_preview) *gui_size += temp_buf_get_memsize (gimage->comp_preview); return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); } static void gimp_image_invalidate_preview (GimpViewable *viewable) { GimpImage *gimage; if (GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview) GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable); gimage = GIMP_IMAGE (viewable); gimage->comp_preview_valid = FALSE; if (gimage->comp_preview) { temp_buf_free (gimage->comp_preview); gimage->comp_preview = NULL; } } static void gimp_image_size_changed (GimpViewable *viewable) { GimpImage *gimage; GList *list; if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed) GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable); gimage = GIMP_IMAGE (viewable); gimp_container_foreach (gimage->layers, (GFunc) gimp_viewable_size_changed, NULL); gimp_container_foreach (gimage->channels, (GFunc) gimp_viewable_size_changed, NULL); gimp_container_foreach (gimage->vectors, (GFunc) gimp_viewable_size_changed, NULL); for (list = GIMP_LIST (gimage->layers)->list; list; list = g_list_next (list)) { GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (list->data)); if (mask) gimp_viewable_size_changed (GIMP_VIEWABLE (mask)); } gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (gimage))); } static gchar * gimp_image_get_description (GimpViewable *viewable, gchar **tooltip) { GimpImage *gimage; const gchar *uri; gchar *basename; gchar *retval; gimage = GIMP_IMAGE (viewable); uri = gimp_image_get_uri (GIMP_IMAGE (gimage)); basename = file_utils_uri_to_utf8_basename (uri); if (tooltip) *tooltip = file_utils_uri_to_utf8_filename (uri); retval = g_strdup_printf ("%s-%d", basename, gimp_image_get_ID (gimage)); g_free (basename); return retval; } static void gimp_image_real_colormap_changed (GimpImage *gimage, gint ncol) { if (gimp_image_base_type (gimage) == GIMP_INDEXED) { /* A colormap alteration affects the whole image */ gimp_image_update (gimage, 0, 0, gimage->width, gimage->height); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); gimp_image_color_hash_invalidate (gimage, ncol); } } static void gimp_image_drawable_update (GimpDrawable *drawable, gint x, gint y, gint width, gint height, GimpImage *gimage) { GimpItem *item = GIMP_ITEM (drawable); if (gimp_item_get_visible (item)) { gint offset_x; gint offset_y; gimp_item_offsets (item, &offset_x, &offset_y); x += offset_x; y += offset_y; gimp_image_update (gimage, x, y, width, height); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } } static void gimp_image_drawable_visibility (GimpItem *item, GimpImage *gimage) { gint offset_x; gint offset_y; gimp_item_offsets (item, &offset_x, &offset_y); gimp_image_update (gimage, offset_x, offset_y, gimp_item_width (item), gimp_item_height (item)); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } static void gimp_image_drawable_add (GimpContainer *container, GimpItem *item, GimpImage *gimage) { if (gimp_item_get_visible (item)) gimp_image_drawable_visibility (item, gimage); } static void gimp_image_drawable_remove (GimpContainer *container, GimpItem *item, GimpImage *gimage) { if (gimp_item_get_visible (item)) gimp_image_drawable_visibility (item, gimage); } static void gimp_image_get_active_components (const GimpImage *gimage, const GimpDrawable *drawable, gboolean *active) { /* If the drawable is a channel make sure that the alpha channel is * not valid. */ if (GIMP_IS_CHANNEL (drawable)) { active[GRAY_PIX] = TRUE; active[ALPHA_G_PIX] = FALSE; } /* otherwise, check whether preserve transparency is * enabled in the layer and if the layer has alpha */ else if (GIMP_IS_LAYER (drawable)) { GimpLayer *layer = GIMP_LAYER (drawable); gint i; /* first copy the gimage active channels */ for (i = 0; i < MAX_CHANNELS; i++) active[i] = gimage->active[i]; if (gimp_drawable_has_alpha (drawable) && layer->preserve_trans) active[gimp_drawable_bytes (drawable) - 1] = FALSE; } } /* public functions */ GimpImage * gimp_image_new (Gimp *gimp, gint width, gint height, GimpImageBaseType base_type) { GimpImage *gimage; gint i; g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); gimage = g_object_new (GIMP_TYPE_IMAGE, NULL); gimage->gimp = gimp; gimage->ID = gimp->next_image_ID++; g_hash_table_insert (gimp->image_table, GINT_TO_POINTER (gimage->ID), (gpointer) gimage); gimage->width = width; gimage->height = height; gimage->base_type = base_type; gimage->xresolution = gimp->config->default_xresolution; gimage->yresolution = gimp->config->default_yresolution; gimage->unit = gimp->config->default_unit; switch (base_type) { case GIMP_RGB: case GIMP_GRAY: break; case GIMP_INDEXED: /* always allocate 256 colors for the colormap */ gimage->num_cols = 0; gimage->cmap = g_new0 (guchar, GIMP_IMAGE_COLORMAP_SIZE); break; default: break; } /* set all color channels visible and active */ for (i = 0; i < MAX_CHANNELS; i++) { gimage->visible[i] = TRUE; gimage->active[i] = TRUE; } /* create the selection mask */ gimage->selection_mask = gimp_selection_new (gimage, gimage->width, gimage->height); g_signal_connect_object (gimp->config, "notify::transparency-type", G_CALLBACK (gimp_image_invalidate_layer_previews), gimage, G_CONNECT_SWAPPED); g_signal_connect_object (gimp->config, "notify::transparency-size", G_CALLBACK (gimp_image_invalidate_layer_previews), gimage, G_CONNECT_SWAPPED); g_signal_connect_object (gimp->config, "notify::layer-previews", G_CALLBACK (gimp_viewable_size_changed), gimage, G_CONNECT_SWAPPED); return gimage; } GimpImageBaseType gimp_image_base_type (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); return gimage->base_type; } GimpImageType gimp_image_base_type_with_alpha (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); switch (gimage->base_type) { case GIMP_RGB: return GIMP_RGBA_IMAGE; case GIMP_GRAY: return GIMP_GRAYA_IMAGE; case GIMP_INDEXED: return GIMP_INDEXEDA_IMAGE; } return GIMP_RGB_IMAGE; } CombinationMode gimp_image_get_combination_mode (GimpImageType dest_type, gint src_bytes) { return valid_combinations[dest_type][src_bytes]; } gint gimp_image_get_ID (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); return gimage->ID; } GimpImage * gimp_image_get_by_ID (Gimp *gimp, gint image_id) { g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); if (gimp->image_table == NULL) return NULL; return (GimpImage *) g_hash_table_lookup (gimp->image_table, GINT_TO_POINTER (image_id)); } void gimp_image_set_uri (GimpImage *gimage, const gchar *uri) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); gimp_object_set_name (GIMP_OBJECT (gimage), uri); } const gchar * gimp_image_get_uri (const GimpImage *gimage) { const gchar *uri; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); uri = gimp_object_get_name (GIMP_OBJECT (gimage)); return uri ? uri : _("Untitled"); } void gimp_image_set_filename (GimpImage *gimage, const gchar *filename) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); if (filename && strlen (filename)) { gchar *uri; uri = file_utils_filename_to_uri (gimage->gimp->load_procs, filename, NULL); gimp_image_set_uri (gimage, uri); g_free (uri); } else { gimp_image_set_uri (gimage, NULL); } } gchar * gimp_image_get_filename (const GimpImage *gimage) { const gchar *uri; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); uri = gimp_object_get_name (GIMP_OBJECT (gimage)); if (! uri) return NULL; return g_filename_from_uri (uri, NULL, NULL); } void gimp_image_set_save_proc (GimpImage *gimage, PlugInProcDef *proc) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); gimage->save_proc = proc; } PlugInProcDef * gimp_image_get_save_proc (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->save_proc; } void gimp_image_set_resolution (GimpImage *gimage, gdouble xresolution, gdouble yresolution) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); /* don't allow to set the resolution out of bounds */ if (xresolution < GIMP_MIN_RESOLUTION || xresolution > GIMP_MAX_RESOLUTION || yresolution < GIMP_MIN_RESOLUTION || yresolution > GIMP_MAX_RESOLUTION) return; if ((ABS (gimage->xresolution - xresolution) >= 1e-5) || (ABS (gimage->yresolution - yresolution) >= 1e-5)) { gimp_image_undo_push_image_resolution (gimage, _("Change Image Resolution")); gimage->xresolution = xresolution; gimage->yresolution = yresolution; gimp_image_resolution_changed (gimage); gimp_viewable_size_changed (GIMP_VIEWABLE (gimage)); } } void gimp_image_get_resolution (const GimpImage *gimage, gdouble *xresolution, gdouble *yresolution) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (xresolution && yresolution); *xresolution = gimage->xresolution; *yresolution = gimage->yresolution; } void gimp_image_resolution_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[RESOLUTION_CHANGED], 0); } void gimp_image_set_unit (GimpImage *gimage, GimpUnit unit) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); if (gimage->unit != unit) { gimp_image_undo_push_image_resolution (gimage, _("Change Image Unit")); gimage->unit = unit; gimp_image_unit_changed (gimage); } } GimpUnit gimp_image_get_unit (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), GIMP_UNIT_INCH); return gimage->unit; } void gimp_image_unit_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[UNIT_CHANGED], 0); } gint gimp_image_get_width (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), 0); return gimage->width; } gint gimp_image_get_height (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), 0); return gimage->height; } gboolean gimp_image_has_alpha (const GimpImage *gimage) { GimpLayer *layer; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), TRUE); layer = (GimpLayer *) gimp_container_get_child_by_index (gimage->layers, 0); return ((gimp_container_num_children (gimage->layers) > 1) || (layer && gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))); } gboolean gimp_image_is_empty (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), TRUE); return (gimp_container_num_children (gimage->layers) == 0); } GimpLayer * gimp_image_floating_sel (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if (gimage->floating_sel == NULL) return NULL; else return gimage->floating_sel; } void gimp_image_floating_selection_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[FLOATING_SELECTION_CHANGED], 0); } GimpChannel * gimp_image_get_mask (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->selection_mask; } void gimp_image_mask_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[MASK_CHANGED], 0); } gint gimp_image_get_component_index (const GimpImage *gimage, GimpChannelType channel) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); switch (channel) { case GIMP_RED_CHANNEL: return RED_PIX; case GIMP_GREEN_CHANNEL: return GREEN_PIX; case GIMP_BLUE_CHANNEL: return BLUE_PIX; case GIMP_GRAY_CHANNEL: return GRAY_PIX; case GIMP_INDEXED_CHANNEL: return INDEXED_PIX; case GIMP_ALPHA_CHANNEL: switch (gimp_image_base_type (gimage)) { case GIMP_RGB: return ALPHA_PIX; case GIMP_GRAY: return ALPHA_G_PIX; case GIMP_INDEXED: return ALPHA_I_PIX; } } return -1; } void gimp_image_set_component_active (GimpImage *gimage, GimpChannelType channel, gboolean active) { gint index = -1; g_return_if_fail (GIMP_IS_IMAGE (gimage)); index = gimp_image_get_component_index (gimage, channel); if (index != -1 && active != gimage->active[index]) { gimage->active[index] = active ? TRUE : FALSE; /* If there is an active channel and we mess with the components, * the active channel gets unset... */ gimp_image_unset_active_channel (gimage); g_signal_emit (gimage, gimp_image_signals[COMPONENT_ACTIVE_CHANGED], 0, channel); } } gboolean gimp_image_get_component_active (const GimpImage *gimage, GimpChannelType channel) { gint index = -1; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); index = gimp_image_get_component_index (gimage, channel); if (index != -1) return gimage->active[index]; return FALSE; } void gimp_image_set_component_visible (GimpImage *gimage, GimpChannelType channel, gboolean visible) { gint index = -1; g_return_if_fail (GIMP_IS_IMAGE (gimage)); index = gimp_image_get_component_index (gimage, channel); if (index != -1 && visible != gimage->visible[index]) { gimage->visible[index] = visible ? TRUE : FALSE; g_signal_emit (gimage, gimp_image_signals[COMPONENT_VISIBILITY_CHANGED], 0, channel); gimp_image_update (gimage, 0, 0, gimage->width, gimage->height); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } } gboolean gimp_image_get_component_visible (const GimpImage *gimage, GimpChannelType channel) { gint index = -1; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); index = gimp_image_get_component_index (gimage, channel); if (index != -1) return gimage->visible[index]; return FALSE; } void gimp_image_grid_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[GRID_CHANGED], 0); } void gimp_image_mode_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[MODE_CHANGED], 0); } void gimp_image_alpha_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[ALPHA_CHANGED], 0); } void gimp_image_update (GimpImage *gimage, gint x, gint y, gint width, gint height) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[UPDATE], 0, x, y, width, height); } void gimp_image_update_guide (GimpImage *gimage, GimpGuide *guide) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (guide != NULL); g_signal_emit (gimage, gimp_image_signals[UPDATE_GUIDE], 0, guide); } void gimp_image_colormap_changed (GimpImage *gimage, gint col) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (col >= -1 && col < gimage->num_cols); g_signal_emit (gimage, gimp_image_signals[COLORMAP_CHANGED], 0, col); } void gimp_image_selection_control (GimpImage *gimage, GimpSelectionControl control) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[SELECTION_CONTROL], 0, control); } void gimp_image_qmask_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[QMASK_CHANGED], 0); } /* undo */ gboolean gimp_image_undo_is_enabled (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); return gimage->undo_on; } gboolean gimp_image_undo_enable (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); /* Free all undo steps as they are now invalidated */ gimp_image_undo_free (gimage); return gimp_image_undo_thaw (gimage); } gboolean gimp_image_undo_disable (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); return gimp_image_undo_freeze (gimage); } gboolean gimp_image_undo_freeze (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); gimage->undo_on = FALSE; return TRUE; } gboolean gimp_image_undo_thaw (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); gimage->undo_on = TRUE; return TRUE; } void gimp_image_undo_start (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[UNDO_START], 0); } void gimp_image_undo_event (GimpImage *gimage, GimpUndoEvent event, GimpUndo *undo) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (event == GIMP_UNDO_EVENT_UNDO_FREE || GIMP_IS_UNDO (undo)); g_signal_emit (gimage, gimp_image_signals[UNDO_EVENT], 0, event, undo); } /* NOTE about the gimage->dirty counter: * If 0, then the image is clean (ie, copy on disk is the same as the one * in memory). * If positive, then that's the number of dirtying operations done * on the image since the last save. * If negative, then user has hit undo and gone back in time prior * to the saved copy. Hitting redo will eventually come back to * the saved copy. * * The image is dirty (ie, needs saving) if counter is non-zero. * * If the counter is around 10000, this is due to undo-ing back * before a saved version, then mutating the image (thus destroying * the redo stack). Once this has happened, it's impossible to get * the image back to the state on disk, since the redo info has been * freed. See undo.c for the gorey details. */ /* * NEVER CALL gimp_image_dirty() directly! * * If your code has just dirtied the image, push an undo instead. * Failing that, push the trivial undo which tells the user the * command is not undoable: undo_push_cantundo() (But really, it would * be best to push a proper undo). If you just dirty the image * without pushing an undo then the dirty count is increased, but * popping that many undo actions won't lead to a clean image. */ gint gimp_image_dirty (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); gimage->dirty++; g_signal_emit (gimage, gimp_image_signals[DIRTY], 0); TRC (("dirty %d -> %d\n", gimage->dirty-1, gimage->dirty)); return gimage->dirty; } gint gimp_image_clean (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); gimage->dirty--; g_signal_emit (gimage, gimp_image_signals[CLEAN], 0); TRC (("clean %d -> %d\n", gimage->dirty+1, gimage->dirty)); return gimage->dirty; } void gimp_image_clean_all (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); gimage->dirty = 0; g_signal_emit (gimage, gimp_image_signals[CLEAN], 0); } /* flush this image's displays */ void gimp_image_flush (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[FLUSH], 0); } /* color transforms / utilities */ /* Get rid of these! A "foreground" is an UI concept.. */ void gimp_image_get_foreground (const GimpImage *gimage, const GimpDrawable *drawable, guchar *fg) { GimpRGB color; guchar pfg[3]; g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (! drawable || GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (fg != NULL); gimp_context_get_foreground (gimp_get_current_context (gimage->gimp), &color); gimp_rgb_get_uchar (&color, &pfg[0], &pfg[1], &pfg[2]); gimp_image_transform_color (gimage, drawable, fg, GIMP_RGB, pfg); } void gimp_image_get_background (const GimpImage *gimage, const GimpDrawable *drawable, guchar *bg) { GimpRGB color; guchar pbg[3]; g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (! drawable || GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (bg != NULL); gimp_context_get_background (gimp_get_current_context (gimage->gimp), &color); gimp_rgb_get_uchar (&color, &pbg[0], &pbg[1], &pbg[2]); gimp_image_transform_color (gimage, drawable, bg, GIMP_RGB, pbg); } void gimp_image_get_color (const GimpImage *src_gimage, GimpImageType src_type, const guchar *src, guchar *rgb) { g_return_if_fail (GIMP_IS_IMAGE (src_gimage)); switch (src_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: /* Straight copy */ *rgb++ = *src++; *rgb++ = *src++; *rgb = *src; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: /* Gray to RG&B */ *rgb++ = *src; *rgb++ = *src; *rgb = *src; break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: /* Indexed palette lookup */ { gint index = *src * 3; *rgb++ = src_gimage->cmap[index++]; *rgb++ = src_gimage->cmap[index++]; *rgb = src_gimage->cmap[index++]; } break; } } void gimp_image_transform_color (const GimpImage *dest_gimage, const GimpDrawable *dest_drawable, guchar *dest, GimpImageBaseType src_type, const guchar *src) { GimpImageType dest_type; g_return_if_fail (GIMP_IS_IMAGE (dest_gimage)); g_return_if_fail (src_type != GIMP_INDEXED); dest_type = (dest_drawable ? gimp_drawable_type (dest_drawable) : gimp_image_base_type_with_alpha (dest_gimage)); switch (src_type) { case GIMP_RGB: switch (dest_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: /* Straight copy */ *dest++ = *src++; *dest++ = *src++; *dest++ = *src++; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: /* NTSC conversion */ *dest = INTENSITY (src[RED_PIX], src[GREEN_PIX], src[BLUE_PIX]); break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: /* Least squares method */ *dest = gimp_image_color_hash_rgb_to_indexed (dest_gimage, src[RED_PIX], src[GREEN_PIX], src[BLUE_PIX]); break; } break; case GIMP_GRAY: switch (dest_type) { case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: /* Gray to RG&B */ *dest++ = *src; *dest++ = *src; *dest++ = *src; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: /* Straight copy */ *dest = *src; break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: /* Least squares method */ *dest = gimp_image_color_hash_rgb_to_indexed (dest_gimage, src[GRAY_PIX], src[GRAY_PIX], src[GRAY_PIX]); break; } break; default: break; } } TempBuf * gimp_image_transform_temp_buf (const GimpImage *dest_image, const GimpDrawable *dest_drawable, TempBuf *temp_buf, gboolean *new_buf) { TempBuf *ret_buf; GimpImageType ret_buf_type; gboolean has_alpha; gboolean is_rgb; gint in_bytes; gint out_bytes; g_return_val_if_fail (GIMP_IMAGE (dest_image), NULL); g_return_val_if_fail (GIMP_DRAWABLE (dest_drawable), NULL); g_return_val_if_fail (temp_buf != NULL, NULL); g_return_val_if_fail (new_buf != NULL, NULL); in_bytes = temp_buf->bytes; has_alpha = (in_bytes == 2 || in_bytes == 4); is_rgb = (in_bytes == 3 || in_bytes == 4); ret_buf_type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (gimp_image_base_type (dest_image)); if (has_alpha) ret_buf_type = GIMP_IMAGE_TYPE_WITH_ALPHA (ret_buf_type); out_bytes = GIMP_IMAGE_TYPE_BYTES (ret_buf_type); /* If the pattern doesn't match the image in terms of color type, * transform it. (ie pattern is RGB, image is indexed) */ if (in_bytes != out_bytes || gimp_drawable_is_indexed (dest_drawable)) { guchar *src; guchar *dest; gint size; ret_buf = temp_buf_new (temp_buf->width, temp_buf->height, out_bytes, 0, 0, NULL); src = temp_buf_data (temp_buf); dest = temp_buf_data (ret_buf); size = temp_buf->width * temp_buf->height; while (size--) { gimp_image_transform_color (dest_image, dest_drawable, dest, is_rgb ? GIMP_RGB : GIMP_GRAY, src); /* Handle alpha */ if (has_alpha) dest[out_bytes - 1] = src[in_bytes - 1]; src += in_bytes; dest += out_bytes; } *new_buf = TRUE; } else { ret_buf = temp_buf; *new_buf = FALSE; } return ret_buf; } /* shadow tiles */ TileManager * gimp_image_shadow (GimpImage *gimage, gint width, gint height, gint bpp) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); if (gimage->shadow) { if ((width != tile_manager_width (gimage->shadow)) || (height != tile_manager_height (gimage->shadow)) || (bpp != tile_manager_bpp (gimage->shadow))) { gimp_image_free_shadow (gimage); } else { return gimage->shadow; } } gimage->shadow = tile_manager_new (width, height, bpp); return gimage->shadow; } void gimp_image_free_shadow (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); if (gimage->shadow) { tile_manager_unref (gimage->shadow); gimage->shadow = NULL; } } /* combine functions */ void gimp_image_apply_image (GimpImage *gimage, GimpDrawable *drawable, PixelRegion *src2PR, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GimpLayerModeEffects mode, /* alternative to using drawable tiles as src1: */ TileManager *src1_tiles, gint x, gint y) { GimpItem *item; GimpChannel *mask; gint x1, y1, x2, y2; gint offset_x, offset_y; PixelRegion src1PR, destPR, maskPR; CombinationMode operation; gboolean active_components[MAX_CHANNELS]; g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); item = GIMP_ITEM (drawable); /* get the selection mask if one exists */ mask = gimp_image_get_mask (gimage); if (gimp_channel_is_empty (mask)) mask = NULL; /* configure the active channel array */ gimp_image_get_active_components (gimage, drawable, active_components); /* determine what sort of operation is being attempted and * if it's actually legal... */ operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable), src2PR->bytes); if (operation == -1) { g_warning ("%s: illegal parameters.", G_GNUC_PRETTY_FUNCTION); return; } /* get the layer offsets */ gimp_item_offsets (item, &offset_x, &offset_y); /* make sure the image application coordinates are within gimage bounds */ x1 = CLAMP (x, 0, gimp_item_width (item)); y1 = CLAMP (y, 0, gimp_item_height (item)); x2 = CLAMP (x + src2PR->w, 0, gimp_item_width (item)); y2 = CLAMP (y + src2PR->h, 0, gimp_item_height (item)); if (mask) { /* make sure coordinates are in mask bounds ... * we need to add the layer offset to transform coords * into the mask coordinate system */ x1 = CLAMP (x1, -offset_x, gimp_item_width (GIMP_ITEM (mask))-offset_x); y1 = CLAMP (y1, -offset_y, gimp_item_height (GIMP_ITEM (mask))-offset_y); x2 = CLAMP (x2, -offset_x, gimp_item_width (GIMP_ITEM (mask))-offset_x); y2 = CLAMP (y2, -offset_y, gimp_item_height (GIMP_ITEM (mask))-offset_y); } /* If the calling procedure specified an undo step... */ if (push_undo) gimp_drawable_push_undo (drawable, undo_desc, x1, y1, x2, y2, NULL, FALSE); /* configure the pixel regions * If an alternative to using the drawable's data as src1 was provided... */ if (src1_tiles) pixel_region_init (&src1PR, src1_tiles, x1, y1, (x2 - x1), (y2 - y1), FALSE); else pixel_region_init (&src1PR, gimp_drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), FALSE); pixel_region_init (&destPR, gimp_drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), TRUE); pixel_region_resize (src2PR, src2PR->x + (x1 - x), src2PR->y + (y1 - y), (x2 - x1), (y2 - y1)); if (mask) { gint mx, my; /* configure the mask pixel region * don't use x1 and y1 because they are in layer * coordinate system. Need mask coordinate system */ mx = x1 + offset_x; my = y1 + offset_y; pixel_region_init (&maskPR, gimp_drawable_data (GIMP_DRAWABLE (mask)), mx, my, (x2 - x1), (y2 - y1), FALSE); combine_regions (&src1PR, src2PR, &destPR, &maskPR, NULL, opacity * 255.999, mode, active_components, operation); } else { combine_regions (&src1PR, src2PR, &destPR, NULL, NULL, opacity * 255.999, mode, active_components, operation); } } /* Similar to gimp_image_apply_image but works in "replace" mode (i.e. transparent pixels in src2 make the result transparent rather than opaque. Takes an additional mask pixel region as well. */ void gimp_image_replace_image (GimpImage *gimage, GimpDrawable *drawable, PixelRegion *src2PR, gboolean push_undo, const gchar *undo_desc, gdouble opacity, PixelRegion *maskPR, gint x, gint y) { GimpItem *item; GimpChannel *mask; gint x1, y1, x2, y2; gint offset_x, offset_y; PixelRegion src1PR, destPR; CombinationMode operation; gboolean active_components[MAX_CHANNELS]; g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); item = GIMP_ITEM (drawable); /* get the selection mask if one exists */ mask = gimp_image_get_mask (gimage); if (gimp_channel_is_empty (mask)) mask = NULL; /* configure the active channel array */ gimp_image_get_active_components (gimage, drawable, active_components); /* determine what sort of operation is being attempted and * if it's actually legal... */ operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable), src2PR->bytes); if (operation == -1) { g_warning ("%s: illegal parameters.", G_GNUC_PRETTY_FUNCTION); return; } /* get the layer offsets */ gimp_item_offsets (item, &offset_x, &offset_y); /* make sure the image application coordinates are within gimage bounds */ x1 = CLAMP (x, 0, gimp_item_width (item)); y1 = CLAMP (y, 0, gimp_item_height (item)); x2 = CLAMP (x + src2PR->w, 0, gimp_item_width (item)); y2 = CLAMP (y + src2PR->h, 0, gimp_item_height (item)); if (mask) { /* make sure coordinates are in mask bounds ... * we need to add the layer offset to transform coords * into the mask coordinate system */ x1 = CLAMP (x1, -offset_x, gimp_item_width (GIMP_ITEM (mask))-offset_x); y1 = CLAMP (y1, -offset_y, gimp_item_height(GIMP_ITEM (mask))-offset_y); x2 = CLAMP (x2, -offset_x, gimp_item_width (GIMP_ITEM (mask))-offset_x); y2 = CLAMP (y2, -offset_y, gimp_item_height(GIMP_ITEM (mask))-offset_y); } /* If the calling procedure specified an undo step... */ if (push_undo) gimp_drawable_push_undo (drawable, undo_desc, x1, y1, x2, y2, NULL, FALSE); /* configure the pixel regions * If an alternative to using the drawable's data as src1 was provided... */ pixel_region_init (&src1PR, gimp_drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), FALSE); pixel_region_init (&destPR, gimp_drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), TRUE); pixel_region_resize (src2PR, src2PR->x + (x1 - x), src2PR->y + (y1 - y), (x2 - x1), (y2 - y1)); if (mask) { PixelRegion mask2PR, tempPR; guchar *temp_data; gint mx, my; /* configure the mask pixel region * don't use x1 and y1 because they are in layer * coordinate system. Need mask coordinate system */ mx = x1 + offset_x; my = y1 + offset_y; pixel_region_init (&mask2PR, gimp_drawable_data (GIMP_DRAWABLE (mask)), mx, my, (x2 - x1), (y2 - y1), FALSE); tempPR.bytes = 1; tempPR.x = 0; tempPR.y = 0; tempPR.w = x2 - x1; tempPR.h = y2 - y1; tempPR.rowstride = tempPR.w * tempPR.bytes; tempPR.data = temp_data = g_malloc (tempPR.h * tempPR.rowstride); copy_region (&mask2PR, &tempPR); /* apparently, region operations can mutate some PR data. */ tempPR.x = 0; tempPR.y = 0; tempPR.w = x2 - x1; tempPR.h = y2 - y1; tempPR.data = temp_data; apply_mask_to_region (&tempPR, maskPR, OPAQUE_OPACITY); tempPR.x = 0; tempPR.y = 0; tempPR.w = x2 - x1; tempPR.h = y2 - y1; tempPR.data = temp_data; combine_regions_replace (&src1PR, src2PR, &destPR, &tempPR, NULL, opacity * 255.999, active_components, operation); g_free (temp_data); } else { combine_regions_replace (&src1PR, src2PR, &destPR, maskPR, NULL, opacity * 255.999, active_components, operation); } } /* parasites */ GimpParasite * gimp_image_parasite_find (const GimpImage *gimage, const gchar *name) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimp_parasite_list_find (gimage->parasites, name); } static void list_func (gchar *key, GimpParasite *p, gchar ***cur) { *(*cur)++ = (gchar *) g_strdup (key); } gchar ** gimp_image_parasite_list (const GimpImage *gimage, gint *count) { gchar **list; gchar **cur; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); *count = gimp_parasite_list_length (gimage->parasites); cur = list = g_new (gchar*, *count); gimp_parasite_list_foreach (gimage->parasites, (GHFunc) list_func, &cur); return list; } void gimp_image_parasite_attach (GimpImage *gimage, GimpParasite *parasite) { g_return_if_fail (GIMP_IS_IMAGE (gimage) && parasite != NULL); /* only set the dirty bit manually if we can be saved and the new parasite differs from the current one and we aren't undoable */ if (gimp_parasite_is_undoable (parasite)) gimp_image_undo_push_image_parasite (gimage, _("Attach Paraite to Image"), parasite); /* We used to push an cantundo on te stack here. This made the undo stack unusable (NULL on the stack) and prevented people from undoing after a save (since most save plug-ins attach an undoable comment parasite). Now we simply attach the parasite without pushing an undo. That way it's undoable but does not block the undo system. --Sven */ gimp_parasite_list_add (gimage->parasites, parasite); if (gimp_parasite_has_flag (parasite, GIMP_PARASITE_ATTACH_PARENT)) { gimp_parasite_shift_parent (parasite); gimp_parasite_attach (gimage->gimp, parasite); } } void gimp_image_parasite_detach (GimpImage *gimage, const gchar *parasite) { GimpParasite *p; g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (parasite != NULL); if (!(p = gimp_parasite_list_find (gimage->parasites, parasite))) return; if (gimp_parasite_is_undoable (p)) gimp_image_undo_push_image_parasite_remove (gimage, _("Remove Parasite from Image"), gimp_parasite_name (p)); gimp_parasite_list_remove (gimage->parasites, parasite); } /* tattoos */ GimpTattoo gimp_image_get_new_tattoo (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), 0); gimage->tattoo_state++; if (gimage->tattoo_state <= 0) g_warning ("%s: Tattoo state corrupted " "(integer overflow).", G_GNUC_PRETTY_FUNCTION); return gimage->tattoo_state; } GimpTattoo gimp_image_get_tattoo_state (GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), 0); return gimage->tattoo_state; } gboolean gimp_image_set_tattoo_state (GimpImage *gimage, GimpTattoo val) { GList *list; gboolean retval = TRUE; GimpChannel *channel; GimpTattoo maxval = 0; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); /* Check that the layer tatoos don't overlap with channel or vector ones */ for (list = GIMP_LIST (gimage->layers)->list; list; list = g_list_next (list)) { GimpTattoo ltattoo; ltattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data)); if (ltattoo > maxval) maxval = ltattoo; if (gimp_image_get_channel_by_tattoo (gimage, ltattoo)) retval = FALSE; /* Oopps duplicated tattoo in channel */ if (gimp_image_get_vectors_by_tattoo (gimage, ltattoo)) retval = FALSE; /* Oopps duplicated tattoo in vectors */ } /* Now check that the channel and vectors tattoos don't overlap */ for (list = GIMP_LIST (gimage->channels)->list; list; list = g_list_next (list)) { GimpTattoo ctattoo; channel = (GimpChannel *) list->data; ctattoo = gimp_item_get_tattoo (GIMP_ITEM (channel)); if (ctattoo > maxval) maxval = ctattoo; if (gimp_image_get_vectors_by_tattoo (gimage, ctattoo)) retval = FALSE; /* Oopps duplicated tattoo in vectors */ } /* Find the max tatto value in the vectors */ for (list = GIMP_LIST (gimage->vectors)->list; list; list = g_list_next (list)) { GimpTattoo vtattoo; vtattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data)); if (vtattoo > maxval) maxval = vtattoo; } if (val < maxval) retval = FALSE; /* Must check if the state is valid */ if (retval == TRUE) gimage->tattoo_state = val; return retval; } /* layers / channels / vectors */ GimpContainer * gimp_image_get_layers (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->layers; } GimpContainer * gimp_image_get_channels (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->channels; } GimpContainer * gimp_image_get_vectors (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->vectors; } GimpDrawable * gimp_image_active_drawable (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); /* If there is an active channel (a saved selection, etc.), * we ignore the active layer */ if (gimage->active_channel) { return GIMP_DRAWABLE (gimage->active_channel); } else if (gimage->active_layer) { GimpLayer *layer; layer = gimage->active_layer; if (layer->mask && layer->mask->edit_mask) return GIMP_DRAWABLE (layer->mask); else return GIMP_DRAWABLE (layer); } return NULL; } GimpLayer * gimp_image_get_active_layer (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->active_layer; } GimpChannel * gimp_image_get_active_channel (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->active_channel; } GimpVectors * gimp_image_get_active_vectors (const GimpImage *gimage) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return gimage->active_vectors; } GimpLayer * gimp_image_set_active_layer (GimpImage *gimage, GimpLayer *layer) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); /* First, find the layer in the gimage * If it isn't valid, find the first layer that is */ if (! gimp_container_have (gimage->layers, GIMP_OBJECT (layer))) layer = (GimpLayer *) gimp_container_get_child_by_index (gimage->layers, 0); if (layer != gimage->active_layer) { if (layer) { /* Configure the layer stack to reflect this change */ gimage->layer_stack = g_slist_remove (gimage->layer_stack, layer); gimage->layer_stack = g_slist_prepend (gimage->layer_stack, layer); /* Don't cache selection info for the previous active layer */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); } gimage->active_layer = layer; g_signal_emit (gimage, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0); if (gimage->active_channel) { gimage->active_channel = NULL; g_signal_emit (gimage, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0); } } /* return the layer */ return layer; } GimpChannel * gimp_image_set_active_channel (GimpImage *gimage, GimpChannel *channel) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL); /* Not if there is a floating selection */ if (gimp_image_floating_sel (gimage)) return NULL; /* First, find the channel * If it doesn't exist, find the first channel that does */ if (! gimp_container_have (gimage->channels, GIMP_OBJECT (channel))) channel = (GimpChannel *) gimp_container_get_child_by_index (gimage->channels, 0); if (channel != gimage->active_channel) { gimage->active_channel = channel; g_signal_emit (gimage, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0); if (gimage->active_layer) { /* Don't cache selection info for the previous active layer */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (gimage->active_layer)); gimage->active_layer = NULL; g_signal_emit (gimage, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0); } } /* return the channel */ return channel; } GimpChannel * gimp_image_unset_active_channel (GimpImage *gimage) { GimpChannel *channel; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); channel = gimp_image_get_active_channel (gimage); if (channel) { gimage->active_channel = NULL; g_signal_emit (gimage, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0); if (gimage->layer_stack) { GimpLayer *layer; layer = (GimpLayer *) gimage->layer_stack->data; gimp_image_set_active_layer (gimage, layer); } } return channel; } GimpVectors * gimp_image_set_active_vectors (GimpImage *gimage, GimpVectors *vectors) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL); /* First, find the vectors * If it doesn't exist, find the first vectors that does */ if (! gimp_container_have (gimage->vectors, GIMP_OBJECT (vectors))) vectors = (GimpVectors *) gimp_container_get_child_by_index (gimage->vectors, 0); if (vectors != gimage->active_vectors) { gimage->active_vectors = vectors; g_signal_emit (gimage, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0); } return vectors; } void gimp_image_active_layer_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0); } void gimp_image_active_channel_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0); } void gimp_image_active_vectors_changed (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_signal_emit (gimage, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0); } gint gimp_image_get_layer_index (const GimpImage *gimage, const GimpLayer *layer) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); g_return_val_if_fail (GIMP_IS_LAYER (layer), -1); return gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); } gint gimp_image_get_channel_index (const GimpImage *gimage, const GimpChannel *channel) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), -1); return gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (channel)); } gint gimp_image_get_vectors_index (const GimpImage *gimage, const GimpVectors *vectors) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), -1); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), -1); return gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (vectors)); } GimpLayer * gimp_image_get_layer_by_tattoo (const GimpImage *gimage, GimpTattoo tattoo) { GimpLayer *layer; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); for (list = GIMP_LIST (gimage->layers)->list; list; list = g_list_next (list)) { layer = (GimpLayer *) list->data; if (gimp_item_get_tattoo (GIMP_ITEM (layer)) == tattoo) return layer; } return NULL; } GimpChannel * gimp_image_get_channel_by_tattoo (const GimpImage *gimage, GimpTattoo tattoo) { GimpChannel *channel; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); for (list = GIMP_LIST (gimage->channels)->list; list; list = g_list_next (list)) { channel = (GimpChannel *) list->data; if (gimp_item_get_tattoo (GIMP_ITEM (channel)) == tattoo) return channel; } return NULL; } GimpVectors * gimp_image_get_vectors_by_tattoo (const GimpImage *gimage, GimpTattoo tattoo) { GimpVectors *vectors; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); for (list = GIMP_LIST (gimage->vectors)->list; list; list = g_list_next (list)) { vectors = (GimpVectors *) list->data; if (gimp_item_get_tattoo (GIMP_ITEM (vectors)) == tattoo) return vectors; } return NULL; } GimpLayer * gimp_image_get_layer_by_name (const GimpImage *gimage, const gchar *name) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return (GimpLayer *) gimp_container_get_child_by_name (gimage->layers, name); } GimpChannel * gimp_image_get_channel_by_name (const GimpImage *gimage, const gchar *name) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return (GimpChannel *) gimp_container_get_child_by_name (gimage->channels, name); } GimpVectors * gimp_image_get_vectors_by_name (const GimpImage *gimage, const gchar *name) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); return (GimpVectors *) gimp_container_get_child_by_name (gimage->vectors, name); } gboolean gimp_image_add_layer (GimpImage *gimage, GimpLayer *layer, gint position) { gboolean alpha_changed = FALSE; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); if (GIMP_ITEM (layer)->gimage != NULL && GIMP_ITEM (layer)->gimage != gimage) { g_warning ("%s: attempting to add layer to wrong image.", G_GNUC_PRETTY_FUNCTION); return FALSE; } if (gimp_container_have (gimage->layers, GIMP_OBJECT (layer))) { g_warning ("%s: trying to add layer to image twice.", G_GNUC_PRETTY_FUNCTION); return FALSE; } gimp_image_undo_push_layer_add (gimage, _("Add Layer"), layer, 0, gimp_image_get_active_layer (gimage)); /* If the layer is a floating selection, set the ID */ if (gimp_layer_is_floating_sel (layer)) gimage->floating_sel = layer; /* let the layer know about the gimage */ gimp_item_set_image (GIMP_ITEM (layer), gimage); /* If the layer has a mask, set the mask's gimage */ if (layer->mask) gimp_item_set_image (GIMP_ITEM (layer->mask), gimage); /* add the layer to the list at the specified position */ if (position == -1) { GimpLayer *active_layer; active_layer = gimp_image_get_active_layer (gimage); if (active_layer) { position = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (active_layer)); } else { position = 0; } } /* If there is a floating selection (and this isn't it!), * make sure the insert position is greater than 0 */ if (position == 0 && gimp_image_floating_sel (gimage) && (gimage->floating_sel != layer)) { position = 1; } if (gimp_container_num_children (gimage->layers) == 1 && ! gimp_drawable_has_alpha (GIMP_LIST (gimage->layers)->list->data)) { alpha_changed = TRUE; } /* Don't add at a non-existing index */ if (position > gimp_container_num_children (gimage->layers)) position = gimp_container_num_children (gimage->layers); gimp_container_insert (gimage->layers, GIMP_OBJECT (layer), position); g_object_unref (layer); /* notify the layers dialog of the currently active layer */ gimp_image_set_active_layer (gimage, layer); if (alpha_changed) gimp_image_alpha_changed (gimage); return TRUE; } void gimp_image_remove_layer (GimpImage *gimage, GimpLayer *layer) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_LAYER (layer)); g_return_if_fail (gimp_container_have (gimage->layers, GIMP_OBJECT (layer))); gimp_image_undo_push_layer_remove (gimage, _("Remove Layer"), layer, gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)), gimp_image_get_active_layer (gimage)); g_object_ref (layer); /* Make sure we're not caching any old selection info */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); gimp_container_remove (gimage->layers, GIMP_OBJECT (layer)); gimage->layer_stack = g_slist_remove (gimage->layer_stack, layer); /* If this was the floating selection, reset the fs pointer */ if (gimage->floating_sel == layer) { gimage->floating_sel = NULL; floating_sel_reset (layer); } if (layer == gimp_image_get_active_layer (gimage)) { if (gimage->layer_stack) { gimp_image_set_active_layer (gimage, gimage->layer_stack->data); } else { gimage->active_layer = NULL; g_signal_emit (gimage, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0); } } /* Send out REMOVED signal from layer */ gimp_item_removed (GIMP_ITEM (layer)); g_object_unref (layer); if (gimp_container_num_children (gimage->layers) == 1 && ! gimp_drawable_has_alpha (GIMP_LIST (gimage->layers)->list->data)) { gimp_image_alpha_changed (gimage); } } gboolean gimp_image_raise_layer (GimpImage *gimage, GimpLayer *layer) { gint curpos; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); curpos = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); /* is this the top layer already? */ if (curpos == 0) { g_message (_("Layer cannot be raised higher.")); return FALSE; } return gimp_image_position_layer (gimage, layer, curpos - 1, TRUE, _("Raise Layer")); } gboolean gimp_image_lower_layer (GimpImage *gimage, GimpLayer *layer) { gint curpos; gint length; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); curpos = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); /* is this the bottom layer already? */ length = gimp_container_num_children (gimage->layers); if (curpos >= length - 1) { g_message (_("Layer cannot be lowered more.")); return FALSE; } return gimp_image_position_layer (gimage, layer, curpos + 1, TRUE, _("Lower Layer")); } gboolean gimp_image_raise_layer_to_top (GimpImage *gimage, GimpLayer *layer) { gint curpos; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); curpos = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); if (curpos == 0) { g_message (_("Layer is already on top.")); return FALSE; } if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) { g_message (_("Cannot raise a layer without alpha.")); return FALSE; } return gimp_image_position_layer (gimage, layer, 0, TRUE, _("Raise Layer to Top")); } gboolean gimp_image_lower_layer_to_bottom (GimpImage *gimage, GimpLayer *layer) { gint curpos; gint length; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); curpos = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); length = gimp_container_num_children (gimage->layers); if (curpos >= length - 1) { g_message (_("Layer is already on the bottom.")); return FALSE; } return gimp_image_position_layer (gimage, layer, length - 1, TRUE, _("Lower Layer to Bottom")); } gboolean gimp_image_position_layer (GimpImage *gimage, GimpLayer *layer, gint new_index, gboolean push_undo, const gchar *undo_desc) { gint index; gint num_layers; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); index = gimp_container_get_child_index (gimage->layers, GIMP_OBJECT (layer)); if (index < 0) return FALSE; num_layers = gimp_container_num_children (gimage->layers); if (new_index < 0) new_index = 0; if (new_index >= num_layers) new_index = num_layers - 1; if (new_index == index) return TRUE; /* check if we want to move it below a bottom layer without alpha */ if (new_index == num_layers - 1) { GimpLayer *tmp; tmp = (GimpLayer *) gimp_container_get_child_by_index (gimage->layers, num_layers - 1); if (new_index == num_layers - 1 && ! gimp_drawable_has_alpha (GIMP_DRAWABLE (tmp))) { g_message (_("Layer \"%s\" has no alpha.\nLayer was placed above it."), GIMP_OBJECT (tmp)->name); new_index--; } } if (push_undo) gimp_image_undo_push_layer_reposition (gimage, undo_desc, layer); gimp_container_reorder (gimage->layers, GIMP_OBJECT (layer), new_index); if (gimp_item_get_visible (GIMP_ITEM (layer))) { gint off_x, off_y; gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); gimp_image_update (gimage, off_x, off_y, gimp_item_width (GIMP_ITEM (layer)), gimp_item_height (GIMP_ITEM (layer))); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } return TRUE; } gboolean gimp_image_add_channel (GimpImage *gimage, GimpChannel *channel, gint position) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); if (GIMP_ITEM (channel)->gimage != NULL && GIMP_ITEM (channel)->gimage != gimage) { g_warning ("%s: attempting to add channel to wrong image.", G_GNUC_PRETTY_FUNCTION); return FALSE; } if (gimp_container_have (gimage->channels, GIMP_OBJECT (channel))) { g_warning ("%s: trying to add channel to image twice.", G_GNUC_PRETTY_FUNCTION); return FALSE; } gimp_image_undo_push_channel_add (gimage, _("Add Channel"), channel, 0, gimp_image_get_active_channel (gimage)); /* add the layer to the list at the specified position */ if (position == -1) { GimpChannel *active_channel; active_channel = gimp_image_get_active_channel (gimage); if (active_channel) { position = gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (active_channel)); } else { position = 0; } } /* Don't add at a non-existing index */ if (position > gimp_container_num_children (gimage->channels)) position = gimp_container_num_children (gimage->channels); gimp_container_insert (gimage->channels, GIMP_OBJECT (channel), position); g_object_unref (channel); /* notify this gimage of the currently active channel */ gimp_image_set_active_channel (gimage, channel); return TRUE; } void gimp_image_remove_channel (GimpImage *gimage, GimpChannel *channel) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_CHANNEL (channel)); g_return_if_fail (gimp_container_have (gimage->channels, GIMP_OBJECT (channel))); gimp_image_undo_push_channel_remove (gimage, _("Remove Channel"), channel, gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (channel)), gimp_image_get_active_channel (gimage)); g_object_ref (channel); gimp_container_remove (gimage->channels, GIMP_OBJECT (channel)); /* Send out REMOVED signal from channel */ gimp_item_removed (GIMP_ITEM (channel)); if (channel == gimp_image_get_active_channel (gimage)) { if (gimp_container_num_children (gimage->channels) > 0) { gimp_image_set_active_channel (gimage, GIMP_CHANNEL (gimp_container_get_child_by_index (gimage->channels, 0))); } else { gimp_image_unset_active_channel (gimage); } } g_object_unref (channel); } gboolean gimp_image_raise_channel (GimpImage *gimage, GimpChannel *channel) { gint index; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); index = gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (channel)); if (index == 0) { g_message (_("Channel cannot be raised higher.")); return FALSE; } return gimp_image_position_channel (gimage, channel, index - 1, TRUE, _("Raise Channel")); } gboolean gimp_image_lower_channel (GimpImage *gimage, GimpChannel *channel) { gint index; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); index = gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (channel)); if (index == gimp_container_num_children (gimage->channels) - 1) { g_message (_("Channel cannot be lowered more.")); return FALSE; } return gimp_image_position_channel (gimage, channel, index + 1, TRUE, _("Lower Channel")); } gboolean gimp_image_position_channel (GimpImage *gimage, GimpChannel *channel, gint new_index, gboolean push_undo, const gchar *undo_desc) { gint index; gint num_channels; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); index = gimp_container_get_child_index (gimage->channels, GIMP_OBJECT (channel)); if (index < 0) return FALSE; num_channels = gimp_container_num_children (gimage->channels); new_index = CLAMP (new_index, 0, num_channels - 1); if (new_index == index) return TRUE; if (push_undo) gimp_image_undo_push_channel_reposition (gimage, undo_desc, channel); gimp_container_reorder (gimage->channels, GIMP_OBJECT (channel), new_index); if (gimp_item_get_visible (GIMP_ITEM (channel))) { gint off_x, off_y; gimp_item_offsets (GIMP_ITEM (channel), &off_x, &off_y); gimp_image_update (gimage, off_x, off_y, gimp_item_width (GIMP_ITEM (channel)), gimp_item_height (GIMP_ITEM (channel))); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } return TRUE; } gboolean gimp_image_add_vectors (GimpImage *gimage, GimpVectors *vectors, gint position) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); if (GIMP_ITEM (vectors)->gimage != NULL && GIMP_ITEM (vectors)->gimage != gimage) { g_warning ("%s: attempting to add vectors to wrong image.", G_GNUC_PRETTY_FUNCTION); return FALSE; } if (gimp_container_have (gimage->vectors, GIMP_OBJECT (vectors))) { g_warning ("%s: trying to add vectors to image twice.", G_GNUC_PRETTY_FUNCTION); return FALSE; } gimp_image_undo_push_vectors_add (gimage, _("Add Path"), vectors, 0, gimp_image_get_active_vectors (gimage)); gimp_item_set_image (GIMP_ITEM (vectors), gimage); /* add the layer to the list at the specified position */ if (position == -1) { GimpVectors *active_vectors; active_vectors = gimp_image_get_active_vectors (gimage); if (active_vectors) { position = gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (active_vectors)); } else { position = 0; } } /* Don't add at a non-existing index */ if (position > gimp_container_num_children (gimage->vectors)) position = gimp_container_num_children (gimage->vectors); gimp_container_insert (gimage->vectors, GIMP_OBJECT (vectors), position); g_object_unref (vectors); /* notify this gimage of the currently active vectors */ gimp_image_set_active_vectors (gimage, vectors); return TRUE; } void gimp_image_remove_vectors (GimpImage *gimage, GimpVectors *vectors) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_VECTORS (vectors)); g_return_if_fail (gimp_container_have (gimage->vectors, GIMP_OBJECT (vectors))); gimp_image_undo_push_vectors_remove (gimage, _("Remove Path"), vectors, gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (vectors)), gimp_image_get_active_vectors (gimage)); g_object_ref (vectors); gimp_container_remove (gimage->vectors, GIMP_OBJECT (vectors)); /* Send out REMOVED signal from vectors */ gimp_item_removed (GIMP_ITEM (vectors)); if (vectors == gimp_image_get_active_vectors (gimage)) { if (gimp_container_num_children (gimage->vectors) > 0) { gimp_image_set_active_vectors (gimage, GIMP_VECTORS (gimp_container_get_child_by_index (gimage->vectors, 0))); } else { gimage->active_vectors = NULL; g_signal_emit (gimage, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0); } } g_object_unref (vectors); } gboolean gimp_image_raise_vectors (GimpImage *gimage, GimpVectors *vectors) { gint index; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); index = gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (vectors)); if (index == 0) { g_message (_("Path cannot be raised higher.")); return FALSE; } return gimp_image_position_vectors (gimage, vectors, index - 1, TRUE, _("Raise Path")); } gboolean gimp_image_lower_vectors (GimpImage *gimage, GimpVectors *vectors) { gint index; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); index = gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (vectors)); if (index == gimp_container_num_children (gimage->vectors) - 1) { g_message (_("Path cannot be lowered more.")); return FALSE; } return gimp_image_position_vectors (gimage, vectors, index + 1, TRUE, _("Lower Path")); } gboolean gimp_image_position_vectors (GimpImage *gimage, GimpVectors *vectors, gint new_index, gboolean push_undo, const gchar *undo_desc) { gint index; gint num_vectors; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); index = gimp_container_get_child_index (gimage->vectors, GIMP_OBJECT (vectors)); if (index < 0) return FALSE; num_vectors = gimp_container_num_children (gimage->vectors); new_index = CLAMP (new_index, 0, num_vectors - 1); if (new_index == index) return TRUE; if (push_undo) gimp_image_undo_push_vectors_reposition (gimage, undo_desc, vectors); gimp_container_reorder (gimage->vectors, GIMP_OBJECT (vectors), new_index); return TRUE; } gboolean gimp_image_layer_boundary (const GimpImage *gimage, BoundSeg **segs, gint *n_segs) { GimpLayer *layer; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (segs != NULL, FALSE); g_return_val_if_fail (n_segs != NULL, FALSE); /* The second boundary corresponds to the active layer's * perimeter... */ layer = gimp_image_get_active_layer (gimage); if (layer) { *segs = gimp_layer_boundary (layer, n_segs); return TRUE; } else { *segs = NULL; *n_segs = 0; return FALSE; } } GimpLayer * gimp_image_pick_correlate_layer (const GimpImage *gimage, gint x, gint y) { GimpLayer *layer; GList *list; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); for (list = GIMP_LIST (gimage->layers)->list; list; list = g_list_next (list)) { layer = (GimpLayer *) list->data; if (gimp_layer_pick_correlate (layer, x, y)) return layer; } return NULL; } void gimp_image_invalidate_layer_previews (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); gimp_container_foreach (gimage->layers, (GFunc) gimp_viewable_invalidate_preview, NULL); } void gimp_image_invalidate_channel_previews (GimpImage *gimage) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); gimp_container_foreach (gimage->channels, (GFunc) gimp_viewable_invalidate_preview, NULL); }