mirror of https://github.com/GNOME/gimp.git
3249 lines
85 KiB
C
3249 lines
85 KiB
C
/* 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 <string.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "base/temp-buf.h"
|
|
#include "base/tile-manager.h"
|
|
|
|
#include "config/gimpconfig.h"
|
|
#include "config/gimpconfig-utils.h"
|
|
#include "config/gimpcoreconfig.h"
|
|
|
|
#include "gimp.h"
|
|
#include "gimp-parasites.h"
|
|
#include "gimp-utils.h"
|
|
#include "gimpcontext.h"
|
|
#include "gimpgrid.h"
|
|
#include "gimpimage.h"
|
|
#include "gimpimage-colorhash.h"
|
|
#include "gimpimage-colormap.h"
|
|
#include "gimpimage-guides.h"
|
|
#include "gimpimage-preview.h"
|
|
#include "gimpimage-projection.h"
|
|
#include "gimpimage-qmask.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 "gimptemplate.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,
|
|
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 gint64 gimp_image_get_memsize (GimpObject *object,
|
|
gint64 *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_mode_changed (GimpImage *gimage);
|
|
static void gimp_image_real_colormap_changed (GimpImage *gimage,
|
|
gint color_index);
|
|
static void gimp_image_real_flush (GimpImage *gimage);
|
|
|
|
static void gimp_image_mask_update (GimpDrawable *drawable,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height,
|
|
GimpImage *gimage);
|
|
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_layer_add (GimpContainer *container,
|
|
GimpLayer *layer,
|
|
GimpImage *gimage);
|
|
static void gimp_image_layer_remove (GimpContainer *container,
|
|
GimpLayer *layer,
|
|
GimpImage *gimage);
|
|
static void gimp_image_channel_add (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *gimage);
|
|
static void gimp_image_channel_remove (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *gimage);
|
|
static void gimp_image_channel_name_changed (GimpChannel *channel,
|
|
GimpImage *gimage);
|
|
|
|
|
|
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[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->mode_changed = gimp_image_real_mode_changed;
|
|
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 = gimp_image_real_flush;
|
|
|
|
gimp_image_color_hash_init ();
|
|
}
|
|
|
|
static void
|
|
gimp_image_init (GimpImage *gimage)
|
|
{
|
|
gint i;
|
|
|
|
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);
|
|
|
|
gimage->channel_name_changed_handler =
|
|
gimp_container_add_handler (gimage->channels, "name_changed",
|
|
G_CALLBACK (gimp_image_channel_name_changed),
|
|
gimage);
|
|
|
|
g_signal_connect (gimage->layers, "add",
|
|
G_CALLBACK (gimp_image_layer_add),
|
|
gimage);
|
|
g_signal_connect (gimage->layers, "remove",
|
|
G_CALLBACK (gimp_image_layer_remove),
|
|
gimage);
|
|
|
|
g_signal_connect (gimage->channels, "add",
|
|
G_CALLBACK (gimp_image_channel_add),
|
|
gimage);
|
|
g_signal_connect (gimage->channels, "remove",
|
|
G_CALLBACK (gimp_image_channel_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 ();
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++)
|
|
{
|
|
gimage->visible[i] = TRUE;
|
|
gimage->active[i] = TRUE;
|
|
}
|
|
|
|
gimage->qmask_state = FALSE;
|
|
gimage->qmask_inverted = FALSE;
|
|
gimp_rgba_set (&gimage->qmask_color, 1.0, 0.0, 0.0, 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;
|
|
|
|
gimage->flush_accum.mask_changed = 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);
|
|
|
|
gimp_container_remove_handler (gimage->channels,
|
|
gimage->channel_name_changed_handler);
|
|
|
|
g_signal_handlers_disconnect_by_func (gimage->layers,
|
|
gimp_image_layer_add,
|
|
gimage);
|
|
g_signal_handlers_disconnect_by_func (gimage->layers,
|
|
gimp_image_layer_remove,
|
|
gimage);
|
|
|
|
g_signal_handlers_disconnect_by_func (gimage->channels,
|
|
gimp_image_channel_add,
|
|
gimage);
|
|
g_signal_handlers_disconnect_by_func (gimage->channels,
|
|
gimp_image_channel_remove,
|
|
gimage);
|
|
|
|
gimp_container_foreach (gimage->layers, (GFunc) gimp_item_removed, NULL);
|
|
gimp_container_foreach (gimage->channels, (GFunc) gimp_item_removed, NULL);
|
|
gimp_container_foreach (gimage->vectors, (GFunc) gimp_item_removed, NULL);
|
|
|
|
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)
|
|
{
|
|
const gchar *name;
|
|
|
|
if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
|
|
GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
|
|
|
|
name = gimp_object_get_name (object);
|
|
|
|
if (! (name && strlen (name)))
|
|
{
|
|
g_free (object->name);
|
|
object->name = NULL;
|
|
}
|
|
}
|
|
|
|
static gint64
|
|
gimp_image_get_memsize (GimpObject *object,
|
|
gint64 *gui_size)
|
|
{
|
|
GimpImage *gimage = GIMP_IMAGE (object);
|
|
gint64 memsize = 0;
|
|
|
|
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 += gimp_g_list_get_memsize (gimage->guides, 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 += gimp_g_slist_get_memsize (gimage->layer_stack, 0);
|
|
|
|
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 = GIMP_IMAGE (viewable);
|
|
|
|
if (GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview)
|
|
GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (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 = GIMP_IMAGE (viewable);
|
|
GList *list;
|
|
|
|
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
|
|
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (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)));
|
|
|
|
gimp_image_projection_allocate (gimage);
|
|
}
|
|
|
|
static gchar *
|
|
gimp_image_get_description (GimpViewable *viewable,
|
|
gchar **tooltip)
|
|
{
|
|
GimpImage *gimage = GIMP_IMAGE (viewable);
|
|
const gchar *uri;
|
|
gchar *basename;
|
|
gchar *retval;
|
|
|
|
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_mode_changed (GimpImage *gimage)
|
|
{
|
|
gimp_image_projection_allocate (gimage);
|
|
}
|
|
|
|
static void
|
|
gimp_image_real_colormap_changed (GimpImage *gimage,
|
|
gint color_index)
|
|
{
|
|
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, color_index);
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_image_real_flush (GimpImage *gimage)
|
|
{
|
|
if (gimage->flush_accum.mask_changed)
|
|
{
|
|
gimp_image_mask_changed (gimage);
|
|
gimage->flush_accum.mask_changed = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_mask_update (GimpDrawable *drawable,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height,
|
|
GimpImage *gimage)
|
|
{
|
|
gimage->flush_accum.mask_changed = TRUE;
|
|
}
|
|
|
|
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_layer_add (GimpContainer *container,
|
|
GimpLayer *layer,
|
|
GimpImage *gimage)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (layer);
|
|
|
|
if (gimp_item_get_visible (item))
|
|
gimp_image_drawable_visibility (item, gimage);
|
|
}
|
|
|
|
static void
|
|
gimp_image_layer_remove (GimpContainer *container,
|
|
GimpLayer *layer,
|
|
GimpImage *gimage)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (layer);
|
|
|
|
if (gimp_item_get_visible (item))
|
|
gimp_image_drawable_visibility (item, gimage);
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_add (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *gimage)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (channel);
|
|
|
|
if (gimp_item_get_visible (item))
|
|
gimp_image_drawable_visibility (item, gimage);
|
|
|
|
if (! strcmp (GIMP_IMAGE_QMASK_NAME,
|
|
gimp_object_get_name (GIMP_OBJECT (channel))))
|
|
{
|
|
gimp_image_set_qmask_state (gimage, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_remove (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *gimage)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (channel);
|
|
|
|
if (gimp_item_get_visible (item))
|
|
gimp_image_drawable_visibility (item, gimage);
|
|
|
|
if (! strcmp (GIMP_IMAGE_QMASK_NAME,
|
|
gimp_object_get_name (GIMP_OBJECT (channel))))
|
|
{
|
|
gimp_image_set_qmask_state (gimage, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_name_changed (GimpChannel *channel,
|
|
GimpImage *gimage)
|
|
{
|
|
if (! strcmp (GIMP_IMAGE_QMASK_NAME,
|
|
gimp_object_get_name (GIMP_OBJECT (channel))))
|
|
{
|
|
gimp_image_set_qmask_state (gimage, TRUE);
|
|
}
|
|
else if (gimp_image_get_qmask_state (gimage) &&
|
|
! gimp_image_get_qmask (gimage))
|
|
{
|
|
gimp_image_set_qmask_state (gimage, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
GimpImage *
|
|
gimp_image_new (Gimp *gimp,
|
|
gint width,
|
|
gint height,
|
|
GimpImageBaseType base_type)
|
|
{
|
|
GimpImage *gimage;
|
|
|
|
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_image->xresolution;
|
|
gimage->yresolution = gimp->config->default_image->yresolution;
|
|
gimage->unit = gimp->config->default_image->unit;
|
|
|
|
gimage->grid = gimp_config_duplicate (GIMP_CONFIG (gimp->config->default_grid));
|
|
|
|
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;
|
|
}
|
|
|
|
/* create the selection mask */
|
|
gimage->selection_mask = gimp_selection_new (gimage,
|
|
gimage->width,
|
|
gimage->height);
|
|
g_object_ref (gimage->selection_mask);
|
|
gimp_item_sink (GIMP_ITEM (gimage->selection_mask));
|
|
|
|
g_signal_connect (gimage->selection_mask, "update",
|
|
G_CALLBACK (gimp_image_mask_update),
|
|
gimage);
|
|
|
|
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_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 color_index)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
g_return_if_fail (color_index >= -1 && color_index < gimage->num_cols);
|
|
|
|
g_signal_emit (gimage, gimp_image_signals[COLORMAP_CHANGED], 0,
|
|
color_index);
|
|
}
|
|
|
|
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;
|
|
|
|
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_FREEZE, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_undo_thaw (GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE);
|
|
|
|
gimage->undo_on = TRUE;
|
|
|
|
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_THAW, NULL);
|
|
|
|
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 ||
|
|
event == GIMP_UNDO_EVENT_UNDO_FREEZE ||
|
|
event == GIMP_UNDO_EVENT_UNDO_THAW) && undo == NULL) ||
|
|
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 *rgba)
|
|
{
|
|
gboolean has_alpha = FALSE;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (src_gimage));
|
|
|
|
switch (src_type)
|
|
{
|
|
case GIMP_RGBA_IMAGE:
|
|
has_alpha = TRUE;
|
|
|
|
case GIMP_RGB_IMAGE:
|
|
/* Straight copy */
|
|
*rgba++ = *src++;
|
|
*rgba++ = *src++;
|
|
*rgba++ = *src++;
|
|
break;
|
|
|
|
case GIMP_GRAYA_IMAGE:
|
|
has_alpha = TRUE;
|
|
|
|
case GIMP_GRAY_IMAGE:
|
|
/* Gray to RG&B */
|
|
*rgba++ = *src;
|
|
*rgba++ = *src;
|
|
*rgba++ = *src++;
|
|
break;
|
|
|
|
case GIMP_INDEXEDA_IMAGE:
|
|
has_alpha = TRUE;
|
|
|
|
case GIMP_INDEXED_IMAGE:
|
|
/* Indexed palette lookup */
|
|
{
|
|
gint index = *src++ * 3;
|
|
|
|
*rgba++ = src_gimage->cmap[index++];
|
|
*rgba++ = src_gimage->cmap[index++];
|
|
*rgba++ = src_gimage->cmap[index++];
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (has_alpha)
|
|
*rgba = *src;
|
|
else
|
|
*rgba = OPAQUE_OPACITY;
|
|
}
|
|
|
|
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 = GIMP_RGB_INTENSITY (src[RED_PIX],
|
|
src[GREEN_PIX],
|
|
src[BLUE_PIX]) + 0.5;
|
|
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);
|
|
|
|
if (has_alpha)
|
|
ret_buf_type = gimp_drawable_type_with_alpha (dest_drawable);
|
|
else
|
|
ret_buf_type = gimp_drawable_type_without_alpha (dest_drawable);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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 Parasite 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;
|
|
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;
|
|
|
|
ctattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data));
|
|
if (ctattoo > maxval)
|
|
maxval = ctattoo;
|
|
|
|
if (gimp_image_get_vectors_by_tattoo (gimage, ctattoo))
|
|
retval = FALSE; /* Oopps duplicated tattoo in vectors */
|
|
}
|
|
|
|
/* Find the max tattoo 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 = 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)
|
|
{
|
|
GimpLayer *floating_sel;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
|
|
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL);
|
|
g_return_val_if_fail (layer == NULL ||
|
|
gimp_container_have (gimage->layers,
|
|
GIMP_OBJECT (layer)), NULL);
|
|
|
|
floating_sel = gimp_image_floating_sel (gimage);
|
|
|
|
/* Make sure the floating_sel always is the active layer */
|
|
if (floating_sel && layer != floating_sel)
|
|
return floating_sel;
|
|
|
|
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 */
|
|
if (gimage->active_layer)
|
|
gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (gimage->active_layer));
|
|
|
|
gimage->active_layer = layer;
|
|
|
|
g_signal_emit (gimage, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0);
|
|
|
|
if (layer && gimage->active_channel)
|
|
gimp_image_set_active_channel (gimage, NULL);
|
|
}
|
|
|
|
return gimage->active_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 (channel == NULL || GIMP_IS_CHANNEL (channel), NULL);
|
|
g_return_val_if_fail (channel == NULL ||
|
|
gimp_container_have (gimage->channels,
|
|
GIMP_OBJECT (channel)), NULL);
|
|
|
|
/* Not if there is a floating selection */
|
|
if (channel && gimp_image_floating_sel (gimage))
|
|
return NULL;
|
|
|
|
if (channel != gimage->active_channel)
|
|
{
|
|
gimage->active_channel = channel;
|
|
|
|
g_signal_emit (gimage, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0);
|
|
|
|
if (channel && gimage->active_layer)
|
|
gimp_image_set_active_layer (gimage, NULL);
|
|
}
|
|
|
|
return gimage->active_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)
|
|
{
|
|
gimp_image_set_active_channel (gimage, NULL);
|
|
|
|
if (gimage->layer_stack)
|
|
gimp_image_set_active_layer (gimage, gimage->layer_stack->data);
|
|
}
|
|
|
|
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 (vectors == NULL || GIMP_IS_VECTORS (vectors), NULL);
|
|
g_return_val_if_fail (vectors == NULL ||
|
|
gimp_container_have (gimage->vectors,
|
|
GIMP_OBJECT (vectors)), NULL);
|
|
|
|
if (vectors != gimage->active_vectors)
|
|
{
|
|
gimage->active_vectors = vectors;
|
|
|
|
g_signal_emit (gimage, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0);
|
|
}
|
|
|
|
return gimage->active_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)
|
|
{
|
|
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))
|
|
{
|
|
GimpLayer *layer = 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)
|
|
{
|
|
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))
|
|
{
|
|
GimpChannel *channel = 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)
|
|
{
|
|
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))
|
|
{
|
|
GimpVectors *vectors = 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)
|
|
{
|
|
GimpLayer *active_layer;
|
|
GimpLayer *floating_sel;
|
|
gboolean old_has_alpha;
|
|
|
|
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;
|
|
}
|
|
|
|
floating_sel = gimp_image_floating_sel (gimage);
|
|
|
|
if (floating_sel && gimp_layer_is_floating_sel (layer))
|
|
{
|
|
g_warning ("%s: trying to add floating layer to image which alyready "
|
|
"has a floating selection.",
|
|
G_GNUC_PRETTY_FUNCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
active_layer = gimp_image_get_active_layer (gimage);
|
|
|
|
old_has_alpha = gimp_image_has_alpha (gimage);
|
|
|
|
gimp_image_undo_push_layer_add (gimage, _("Add Layer"),
|
|
layer, 0, active_layer);
|
|
|
|
gimp_item_set_image (GIMP_ITEM (layer), gimage);
|
|
|
|
if (layer->mask)
|
|
gimp_item_set_image (GIMP_ITEM (layer->mask), gimage);
|
|
|
|
/* If the layer is a floating selection, set the ID */
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
gimage->floating_sel = layer;
|
|
|
|
/* add the layer to the list at the specified position */
|
|
if (position == -1)
|
|
{
|
|
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 && floating_sel)
|
|
position = 1;
|
|
|
|
/* 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);
|
|
gimp_item_sink (GIMP_ITEM (layer));
|
|
|
|
/* notify the layers dialog of the currently active layer */
|
|
gimp_image_set_active_layer (gimage, layer);
|
|
|
|
if (old_has_alpha != gimp_image_has_alpha (gimage))
|
|
gimp_image_alpha_changed (gimage);
|
|
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
gimp_image_floating_selection_changed (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_remove_layer (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
GimpLayer *active_layer;
|
|
gint index;
|
|
gboolean old_has_alpha;
|
|
|
|
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)));
|
|
|
|
active_layer = gimp_image_get_active_layer (gimage);
|
|
|
|
index = gimp_container_get_child_index (gimage->layers,
|
|
GIMP_OBJECT (layer));
|
|
|
|
old_has_alpha = gimp_image_has_alpha (gimage);
|
|
|
|
gimp_image_undo_push_layer_remove (gimage, _("Remove Layer"),
|
|
layer, index, active_layer);
|
|
|
|
g_object_ref (layer);
|
|
|
|
/* Make sure we're not caching any old selection info */
|
|
if (layer == active_layer)
|
|
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 (gimage->floating_sel == layer)
|
|
{
|
|
/* If this was the floating selection, reset the fs pointer
|
|
* and activate the underlying drawable
|
|
*/
|
|
gimage->floating_sel = NULL;
|
|
|
|
floating_sel_activate_drawable (layer);
|
|
|
|
gimp_image_floating_selection_changed (gimage);
|
|
}
|
|
else if (layer == active_layer)
|
|
{
|
|
if (gimage->layer_stack)
|
|
{
|
|
active_layer = gimage->layer_stack->data;
|
|
}
|
|
else
|
|
{
|
|
gint n_children = gimp_container_num_children (gimage->layers);
|
|
|
|
if (n_children > 0)
|
|
{
|
|
index = CLAMP (index, 0, n_children - 1);
|
|
|
|
active_layer = (GimpLayer *)
|
|
gimp_container_get_child_by_index (gimage->layers, index);
|
|
}
|
|
else
|
|
{
|
|
active_layer = NULL;
|
|
}
|
|
}
|
|
|
|
gimp_image_set_active_layer (gimage, active_layer);
|
|
}
|
|
|
|
gimp_item_removed (GIMP_ITEM (layer));
|
|
|
|
g_object_unref (layer);
|
|
|
|
if (old_has_alpha != gimp_image_has_alpha (gimage))
|
|
gimp_image_alpha_changed (gimage);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_raise_layer (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
gint index;
|
|
|
|
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)
|
|
{
|
|
g_message (_("Layer cannot be raised higher."));
|
|
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, index - 1,
|
|
TRUE, _("Raise Layer"));
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_lower_layer (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
gint index;
|
|
|
|
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 == gimp_container_num_children (gimage->layers) - 1)
|
|
{
|
|
g_message (_("Layer cannot be lowered more."));
|
|
return FALSE;
|
|
}
|
|
|
|
return gimp_image_position_layer (gimage, layer, index + 1,
|
|
TRUE, _("Lower Layer"));
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_raise_layer_to_top (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
gint index;
|
|
|
|
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)
|
|
{
|
|
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 index;
|
|
gint length;
|
|
|
|
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));
|
|
|
|
length = gimp_container_num_children (gimage->layers);
|
|
|
|
if (index == 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);
|
|
|
|
new_index = CLAMP (new_index, 0, 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. Layer 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)
|
|
{
|
|
GimpChannel *active_channel;
|
|
|
|
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;
|
|
}
|
|
|
|
active_channel = gimp_image_get_active_channel (gimage);
|
|
|
|
gimp_image_undo_push_channel_add (gimage, _("Add Channel"),
|
|
channel, 0, active_channel);
|
|
|
|
gimp_item_set_image (GIMP_ITEM (channel), gimage);
|
|
|
|
/* add the layer to the list at the specified position */
|
|
if (position == -1)
|
|
{
|
|
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);
|
|
gimp_item_sink (GIMP_ITEM (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)
|
|
{
|
|
GimpChannel *active_channel;
|
|
gint index;
|
|
|
|
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)));
|
|
|
|
active_channel = gimp_image_get_active_channel (gimage);
|
|
|
|
index = gimp_container_get_child_index (gimage->channels,
|
|
GIMP_OBJECT (channel));
|
|
|
|
gimp_image_undo_push_channel_remove (gimage, _("Remove Channel"),
|
|
channel, index, active_channel);
|
|
|
|
g_object_ref (channel);
|
|
|
|
gimp_container_remove (gimage->channels, GIMP_OBJECT (channel));
|
|
gimp_item_removed (GIMP_ITEM (channel));
|
|
|
|
if (channel == active_channel)
|
|
{
|
|
gint n_children = gimp_container_num_children (gimage->channels);
|
|
|
|
if (n_children > 0)
|
|
{
|
|
index = CLAMP (index, 0, n_children - 1);
|
|
|
|
active_channel = (GimpChannel *)
|
|
gimp_container_get_child_by_index (gimage->channels, index);
|
|
|
|
gimp_image_set_active_channel (gimage, active_channel);
|
|
}
|
|
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)
|
|
{
|
|
GimpVectors *active_vectors;
|
|
|
|
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;
|
|
}
|
|
|
|
active_vectors = gimp_image_get_active_vectors (gimage);
|
|
|
|
gimp_image_undo_push_vectors_add (gimage, _("Add Path"),
|
|
vectors, 0, active_vectors);
|
|
|
|
gimp_item_set_image (GIMP_ITEM (vectors), gimage);
|
|
|
|
/* add the layer to the list at the specified position */
|
|
if (position == -1)
|
|
{
|
|
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);
|
|
gimp_item_sink (GIMP_ITEM (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)
|
|
{
|
|
GimpVectors *active_vectors;
|
|
gint index;
|
|
|
|
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)));
|
|
|
|
active_vectors = gimp_image_get_active_vectors (gimage);
|
|
|
|
index = gimp_container_get_child_index (gimage->vectors,
|
|
GIMP_OBJECT (vectors));
|
|
|
|
gimp_image_undo_push_vectors_remove (gimage, _("Remove Path"),
|
|
vectors, index, active_vectors);
|
|
|
|
g_object_ref (vectors);
|
|
|
|
gimp_container_remove (gimage->vectors, GIMP_OBJECT (vectors));
|
|
gimp_item_removed (GIMP_ITEM (vectors));
|
|
|
|
if (vectors == active_vectors)
|
|
{
|
|
gint n_children = gimp_container_num_children (gimage->vectors);
|
|
|
|
if (n_children > 0)
|
|
{
|
|
index = CLAMP (index, 0, n_children - 1);
|
|
|
|
active_vectors = (GimpVectors *)
|
|
gimp_container_get_child_by_index (gimage->vectors, index);
|
|
}
|
|
else
|
|
{
|
|
active_vectors = NULL;
|
|
}
|
|
|
|
gimp_image_set_active_vectors (gimage, active_vectors);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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))
|
|
{
|
|
GimpLayer *layer = 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);
|
|
}
|