2002-11-18 Sven Neumann <sven@gimp.org> Finally landed the new GimpConfig based gimprc parser. It's not finished yet but we need to start somewhere. This release removes the old gimprc.[ch] files. The gimprc format changes slightly, but the changes are minimal. The Preferences dialog is temporarily disabled since it still needs to be ported. If you are are afraid, stay away from CVS for a few days ;-) * app/Makefile.am * app/gimprc.[ch]: removed the old gimprc system. * app/base/Makefile.am * app/base/base-config.[ch]: removed these files in favor of config/gimpbaseconfig.[ch]. * app/core/Makefile.am * app/core/gimpcoreconfig.[ch]: removed these files in favor of config/gimpcoreconfig.[ch]. * app/config/Makefile.am * app/config/config-types.h: moved typedefs into this new file. * app/config/gimpbaseconfig.[ch] * app/config/gimpcoreconfig.[ch] * app/config/gimpdisplayconfig.[ch] * app/config/gimpguiconfig.[ch] * app/config/gimprc.[ch] * app/config/test-config.c: brought into shape for real use. * app/base/base-types.h: include config/config-types.h here. Added a global GimpBaseConfig *base_config variable to ease migration. * app/gui/Makefile.am: temporarily disabled the preferences dialog. * app/app_procs.c * app/undo.c * app/undo_history.c * app/base/base.[ch] * app/base/gimphistogram.c * app/base/pixel-processor.c * app/base/temp-buf.c * app/base/tile-cache.c * app/core/core-types.h * app/core/gimp-documents.c * app/core/gimp.c * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpcontext.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-new.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimpmodules.c * app/core/gimppattern.c * app/display/Makefile.am * app/display/gimpdisplay-handlers.c * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell-callbacks.c * app/display/gimpdisplayshell-handlers.c * app/display/gimpdisplayshell-layer-select.c * app/display/gimpdisplayshell-render.c * app/display/gimpdisplayshell-scale.c * app/display/gimpdisplayshell-scroll.c * app/display/gimpdisplayshell-selection.c * app/display/gimpdisplayshell.[ch] * app/display/gimpnavigationview.c * app/file/file-save.c * app/gui/device-status-dialog.c * app/gui/dialogs-constructors.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/file-save-dialog.c * app/gui/gui.c * app/gui/menus.c * app/gui/paths-dialog.c * app/gui/resize-dialog.c * app/gui/session.c * app/gui/test-commands.c * app/gui/tips-dialog.c * app/gui/tips-dialog.h * app/gui/user-install-dialog.c * app/gui/view-commands.c * app/paint/gimppaintcore.c * app/plug-in/plug-in.c * app/plug-in/plug-ins.c * app/tools/gimpbezierselecttool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpinktool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimppainttool.c * app/tools/gimppathtool.c * app/tools/gimptexttool.[ch] * app/tools/selection_options.c * app/tools/tools.c * app/tools/transform_options.c * app/widgets/gimphelp.c * app/widgets/gimpitemfactory.c * app/widgets/gimpselectioneditor.c * app/xcf/xcf-load.c * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gimprc.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/transform_tools.pdb: use the new config system instead of the old gimprc stuff. * etc/gimprc.in * etc/gimprc_user.in: adapted to the new gimprc format. Will update the man-page later... * app/pdb/fileops_cmds.c * app/pdb/gimprc_cmds.c * app/pdb/image_cmds.c * app/pdb/layer_cmds.c * app/pdb/transform_tools_cmds.c * libgimp/gimpgimprc_pdb.c: regenerated.
3391 lines
86 KiB
C
3391 lines
86 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/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-mask.h"
|
|
#include "gimpimage-projection.h"
|
|
#include "gimpimage-undo.h"
|
|
#include "gimplayer.h"
|
|
#include "gimplayer-floating-sel.h"
|
|
#include "gimplayermask.h"
|
|
#include "gimplist.h"
|
|
#include "gimpmarshal.h"
|
|
#include "gimpparasitelist.h"
|
|
#include "gimpundostack.h"
|
|
|
|
#include "file/file-utils.h"
|
|
|
|
#include "vectors/gimpvectors.h"
|
|
|
|
#include "path.h"
|
|
#include "undo.h"
|
|
|
|
#include "libgimp/gimpintl.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_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);
|
|
|
|
static void gimp_image_invalidate_preview (GimpViewable *viewable);
|
|
static void gimp_image_size_changed (GimpViewable *viewable);
|
|
static TempBuf *gimp_image_get_preview (GimpViewable *gimage,
|
|
gint width,
|
|
gint height);
|
|
static TempBuf *gimp_image_get_new_preview (GimpViewable *viewable,
|
|
gint width,
|
|
gint height);
|
|
|
|
static void gimp_image_real_colormap_changed (GimpImage *gimage,
|
|
gint ncol);
|
|
|
|
|
|
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_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__INT,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_INT);
|
|
|
|
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->invalidate_preview = gimp_image_invalidate_preview;
|
|
viewable_class->size_changed = gimp_image_size_changed;
|
|
viewable_class->get_preview = gimp_image_get_preview;
|
|
viewable_class->get_new_preview = gimp_image_get_new_preview;
|
|
|
|
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_event = NULL;
|
|
klass->undo = gimp_image_undo;
|
|
klass->redo = gimp_image_redo;
|
|
|
|
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->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->active_layer = NULL;
|
|
gimage->active_channel = NULL;
|
|
gimage->floating_sel = NULL;
|
|
gimage->selection_mask = NULL;
|
|
|
|
gimage->parasites = gimp_parasite_list_new ();
|
|
|
|
gimage->paths = NULL;
|
|
|
|
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 = NULL;
|
|
gimage->redo_stack = NULL;
|
|
gimage->undo_bytes = 0;
|
|
gimage->undo_levels = 0;
|
|
gimage->group_count = 0;
|
|
gimage->pushing_undo_group = NO_UNDO_GROUP;
|
|
|
|
gimage->new_undo_stack = gimp_undo_stack_new (gimage);
|
|
gimage->new_redo_stack = gimp_undo_stack_new (gimage);
|
|
|
|
gimage->comp_preview = NULL;
|
|
gimage->comp_preview_valid = FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_image_dispose (GObject *object)
|
|
{
|
|
GimpImage *gimage;
|
|
|
|
gimage = GIMP_IMAGE (object);
|
|
|
|
undo_free (gimage);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gimp_image_finalize (GObject *object)
|
|
{
|
|
GimpImage *gimage;
|
|
|
|
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 (G_OBJECT (gimage->layers));
|
|
gimage->layers = NULL;
|
|
}
|
|
if (gimage->channels)
|
|
{
|
|
g_object_unref (G_OBJECT (gimage->channels));
|
|
gimage->channels = NULL;
|
|
}
|
|
if (gimage->vectors)
|
|
{
|
|
g_object_unref (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (gimage->parasites));
|
|
gimage->parasites = NULL;
|
|
}
|
|
|
|
if (gimage->guides)
|
|
{
|
|
g_list_foreach (gimage->guides, (GFunc) g_free, NULL);
|
|
g_list_free (gimage->guides);
|
|
gimage->guides = NULL;
|
|
}
|
|
|
|
if (gimage->new_undo_stack)
|
|
{
|
|
g_object_unref (G_OBJECT (gimage->new_undo_stack));
|
|
gimage->new_undo_stack = NULL;
|
|
}
|
|
if (gimage->new_redo_stack)
|
|
{
|
|
g_object_unref (G_OBJECT (gimage->new_redo_stack));
|
|
gimage->new_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)
|
|
{
|
|
GimpImage *gimage;
|
|
gsize memsize = 0;
|
|
|
|
gimage = GIMP_IMAGE (object);
|
|
|
|
if (gimage->cmap)
|
|
memsize += 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)));
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->layers));
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->channels));
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->vectors));
|
|
|
|
memsize += g_slist_length (gimage->layer_stack) * sizeof (GSList);
|
|
|
|
if (gimage->selection_mask)
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->selection_mask));
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->parasites));
|
|
|
|
/* FIXME paths */
|
|
|
|
memsize += g_slist_length (gimage->undo_stack) * (3 * sizeof (gint) +
|
|
4 * sizeof (gpointer)); /* FIXME */
|
|
memsize += g_slist_length (gimage->redo_stack) * (3 * sizeof (gint) +
|
|
4 * sizeof (gpointer)); /* FIXME */
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->new_undo_stack));
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (gimage->new_redo_stack));
|
|
|
|
if (gimage->comp_preview)
|
|
memsize += temp_buf_get_memsize (gimage->comp_preview);
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
gimp_image_size_changed (GimpViewable *viewable)
|
|
{
|
|
GimpImage *gimage;
|
|
|
|
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
|
|
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
|
|
|
|
gimage = GIMP_IMAGE (viewable);
|
|
|
|
gimp_image_invalidate_layer_previews (gimage);
|
|
gimp_image_invalidate_channel_previews (gimage);
|
|
}
|
|
|
|
static TempBuf *
|
|
gimp_image_get_preview (GimpViewable *viewable,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
GimpImage *gimage;
|
|
|
|
gimage = GIMP_IMAGE (viewable);
|
|
|
|
if (gimage->comp_preview_valid &&
|
|
gimage->comp_preview->width == width &&
|
|
gimage->comp_preview->height == height)
|
|
{
|
|
/* The easy way */
|
|
return gimage->comp_preview;
|
|
}
|
|
else
|
|
{
|
|
/* The hard way */
|
|
if (gimage->comp_preview)
|
|
temp_buf_free (gimage->comp_preview);
|
|
|
|
/* Actually construct the composite preview from the layer previews!
|
|
* This might seem ridiculous, but it's actually the best way, given
|
|
* a number of unsavory alternatives.
|
|
*/
|
|
gimage->comp_preview = gimp_image_get_new_preview (viewable,
|
|
width, height);
|
|
|
|
gimage->comp_preview_valid = TRUE;
|
|
|
|
return gimage->comp_preview;
|
|
}
|
|
}
|
|
|
|
static TempBuf *
|
|
gimp_image_get_new_preview (GimpViewable *viewable,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
GimpImage *gimage;
|
|
GimpLayer *layer;
|
|
GimpLayer *floating_sel;
|
|
PixelRegion src1PR, src2PR, maskPR;
|
|
PixelRegion *mask;
|
|
TempBuf *comp;
|
|
TempBuf *layer_buf;
|
|
TempBuf *mask_buf;
|
|
GList *list;
|
|
GSList *reverse_list = NULL;
|
|
gdouble ratio;
|
|
gint x, y, w, h;
|
|
gint x1, y1, x2, y2;
|
|
gint bytes;
|
|
gboolean construct_flag;
|
|
gboolean visible_components[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE };
|
|
gint off_x, off_y;
|
|
|
|
gimage = GIMP_IMAGE (viewable);
|
|
|
|
ratio = (gdouble) width / (gdouble) gimage->width;
|
|
|
|
switch (gimp_image_base_type (gimage))
|
|
{
|
|
case GIMP_RGB:
|
|
case GIMP_INDEXED:
|
|
bytes = 4;
|
|
break;
|
|
case GIMP_GRAY:
|
|
bytes = 2;
|
|
break;
|
|
default:
|
|
bytes = 0;
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
/* The construction buffer */
|
|
comp = temp_buf_new (width, height, bytes, 0, 0, NULL);
|
|
temp_buf_data_clear (comp);
|
|
|
|
floating_sel = NULL;
|
|
|
|
for (list = GIMP_LIST (gimage->layers)->list;
|
|
list;
|
|
list = g_list_next (list))
|
|
{
|
|
layer = (GimpLayer *) list->data;
|
|
|
|
/* only add layers that are visible to the list */
|
|
if (gimp_drawable_get_visible (GIMP_DRAWABLE (layer)))
|
|
{
|
|
/* floating selections are added right above the layer
|
|
* they are attached to
|
|
*/
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
{
|
|
floating_sel = layer;
|
|
}
|
|
else
|
|
{
|
|
if (floating_sel &&
|
|
floating_sel->fs.drawable == GIMP_DRAWABLE (layer))
|
|
{
|
|
reverse_list = g_slist_prepend (reverse_list, floating_sel);
|
|
}
|
|
|
|
reverse_list = g_slist_prepend (reverse_list, layer);
|
|
}
|
|
}
|
|
}
|
|
|
|
construct_flag = FALSE;
|
|
|
|
for (; reverse_list; reverse_list = g_slist_next (reverse_list))
|
|
{
|
|
layer = (GimpLayer *) reverse_list->data;
|
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
|
|
|
|
x = (gint) RINT (ratio * off_x);
|
|
y = (gint) RINT (ratio * off_y);
|
|
w = (gint) RINT (ratio * gimp_drawable_width (GIMP_DRAWABLE (layer)));
|
|
h = (gint) RINT (ratio * gimp_drawable_height (GIMP_DRAWABLE (layer)));
|
|
|
|
if (w < 1 || h < 1)
|
|
continue;
|
|
|
|
x1 = CLAMP (x, 0, width);
|
|
y1 = CLAMP (y, 0, height);
|
|
x2 = CLAMP (x + w, 0, width);
|
|
y2 = CLAMP (y + h, 0, height);
|
|
|
|
src1PR.bytes = comp->bytes;
|
|
src1PR.x = x1;
|
|
src1PR.y = y1;
|
|
src1PR.w = (x2 - x1);
|
|
src1PR.h = (y2 - y1);
|
|
src1PR.rowstride = comp->width * src1PR.bytes;
|
|
src1PR.data = (temp_buf_data (comp) +
|
|
y1 * src1PR.rowstride + x1 * src1PR.bytes);
|
|
|
|
layer_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer), w, h);
|
|
|
|
g_assert (layer_buf);
|
|
g_assert (layer_buf->bytes <= comp->bytes);
|
|
|
|
src2PR.bytes = layer_buf->bytes;
|
|
src2PR.w = src1PR.w;
|
|
src2PR.h = src1PR.h;
|
|
src2PR.x = src1PR.x;
|
|
src2PR.y = src1PR.y;
|
|
src2PR.rowstride = layer_buf->width * src2PR.bytes;
|
|
src2PR.data = (temp_buf_data (layer_buf) +
|
|
(y1 - y) * src2PR.rowstride +
|
|
(x1 - x) * src2PR.bytes);
|
|
|
|
if (layer->mask && layer->mask->apply_mask)
|
|
{
|
|
mask_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer->mask),
|
|
w, h);
|
|
maskPR.bytes = mask_buf->bytes;
|
|
maskPR.rowstride = mask_buf->width;
|
|
maskPR.data = (mask_buf_data (mask_buf) +
|
|
(y1 - y) * maskPR.rowstride +
|
|
(x1 - x) * maskPR.bytes);
|
|
mask = &maskPR;
|
|
}
|
|
else
|
|
{
|
|
mask = NULL;
|
|
}
|
|
|
|
/* Based on the type of the layer, project the layer onto the
|
|
* composite preview...
|
|
* Indexed images are actually already converted to RGB and RGBA,
|
|
* so just project them as if they were type "intensity"
|
|
* Send in all TRUE for visible since that info doesn't matter
|
|
* for previews
|
|
*/
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
|
|
{
|
|
if (! construct_flag)
|
|
initial_region (&src2PR, &src1PR,
|
|
mask, NULL,
|
|
layer->opacity * 255.999,
|
|
layer->mode,
|
|
visible_components,
|
|
INITIAL_INTENSITY_ALPHA);
|
|
else
|
|
combine_regions (&src1PR, &src2PR, &src1PR,
|
|
mask, NULL,
|
|
layer->opacity * 255.999,
|
|
layer->mode,
|
|
visible_components,
|
|
COMBINE_INTEN_A_INTEN_A);
|
|
}
|
|
else
|
|
{
|
|
if (! construct_flag)
|
|
initial_region (&src2PR, &src1PR,
|
|
mask, NULL,
|
|
layer->opacity * 255.999,
|
|
layer->mode,
|
|
visible_components,
|
|
INITIAL_INTENSITY);
|
|
else
|
|
combine_regions (&src1PR, &src2PR, &src1PR,
|
|
mask, NULL,
|
|
layer->opacity * 255.999,
|
|
layer->mode,
|
|
visible_components,
|
|
COMBINE_INTEN_A_INTEN);
|
|
}
|
|
|
|
construct_flag = TRUE;
|
|
}
|
|
|
|
g_slist_free (reverse_list);
|
|
|
|
return comp;
|
|
}
|
|
|
|
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_image_color_hash_invalidate (gimage, ncol);
|
|
}
|
|
}
|
|
|
|
|
|
/* 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 = GIMP_IMAGE (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 = (guchar *) g_malloc0 (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_channel_new_mask (gimage,
|
|
gimage->width,
|
|
gimage->height);
|
|
|
|
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)
|
|
{
|
|
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))
|
|
{
|
|
undo_push_image_resolution (gimage);
|
|
|
|
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 (G_OBJECT (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)
|
|
{
|
|
undo_push_image_resolution (gimage);
|
|
|
|
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 (G_OBJECT (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 (G_OBJECT (gimage),
|
|
gimp_image_signals[FLOATING_SELECTION_CHANGED], 0);
|
|
}
|
|
|
|
guchar *
|
|
gimp_image_get_colormap (const GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
|
|
|
|
return gimage->cmap;
|
|
}
|
|
|
|
void
|
|
gimp_image_colormap_changed (GimpImage *gimage,
|
|
gint col)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
g_return_if_fail (col < gimage->num_cols);
|
|
|
|
g_signal_emit (G_OBJECT (gimage), gimp_image_signals[COLORMAP_CHANGED], 0,
|
|
col);
|
|
}
|
|
|
|
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 (G_OBJECT (gimage), gimp_image_signals[MASK_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_set_component_active (GimpImage *gimage,
|
|
GimpChannelType type,
|
|
gboolean active)
|
|
{
|
|
gint pixel = -1;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
switch (type)
|
|
{
|
|
case GIMP_RED_CHANNEL: pixel = RED_PIX; break;
|
|
case GIMP_GREEN_CHANNEL: pixel = GREEN_PIX; break;
|
|
case GIMP_BLUE_CHANNEL: pixel = BLUE_PIX; break;
|
|
case GIMP_GRAY_CHANNEL: pixel = GRAY_PIX; break;
|
|
case GIMP_INDEXED_CHANNEL: pixel = INDEXED_PIX; break;
|
|
case GIMP_ALPHA_CHANNEL:
|
|
switch (gimp_image_base_type (gimage))
|
|
{
|
|
case GIMP_RGB: pixel = ALPHA_PIX; break;
|
|
case GIMP_GRAY: pixel = ALPHA_G_PIX; break;
|
|
case GIMP_INDEXED: pixel = ALPHA_I_PIX; break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (pixel != -1 && active != gimage->active[pixel])
|
|
{
|
|
gimage->active[pixel] = 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 (G_OBJECT (gimage),
|
|
gimp_image_signals[COMPONENT_ACTIVE_CHANGED], 0,
|
|
type);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_get_component_active (const GimpImage *gimage,
|
|
GimpChannelType type)
|
|
{
|
|
/* No sanity checking here... */
|
|
switch (type)
|
|
{
|
|
case GIMP_RED_CHANNEL: return gimage->active[RED_PIX]; break;
|
|
case GIMP_GREEN_CHANNEL: return gimage->active[GREEN_PIX]; break;
|
|
case GIMP_BLUE_CHANNEL: return gimage->active[BLUE_PIX]; break;
|
|
case GIMP_GRAY_CHANNEL: return gimage->active[GRAY_PIX]; break;
|
|
case GIMP_INDEXED_CHANNEL: return gimage->active[INDEXED_PIX]; break;
|
|
case GIMP_ALPHA_CHANNEL:
|
|
switch (gimp_image_base_type (gimage))
|
|
{
|
|
case GIMP_RGB: return gimage->active[ALPHA_PIX]; break;
|
|
case GIMP_GRAY: return gimage->active[ALPHA_G_PIX]; break;
|
|
case GIMP_INDEXED: return gimage->active[ALPHA_I_PIX]; break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
gimp_image_get_active_components (const GimpImage *gimage,
|
|
const GimpDrawable *drawable,
|
|
gint *active)
|
|
{
|
|
gint i;
|
|
|
|
/* first, blindly copy the gimage active channels */
|
|
for (i = 0; i < MAX_CHANNELS; i++)
|
|
active[i] = gimage->active[i];
|
|
|
|
/* If the drawable is a channel (a saved selection, etc.)
|
|
* make sure that the alpha channel is not valid
|
|
*/
|
|
if (GIMP_IS_CHANNEL (drawable))
|
|
{
|
|
active[ALPHA_G_PIX] = 0; /* no alpha values in channels */
|
|
}
|
|
else
|
|
{
|
|
/* otherwise, check whether preserve transparency is
|
|
* enabled in the layer and if the layer has alpha
|
|
*/
|
|
if (GIMP_IS_LAYER (drawable))
|
|
{
|
|
GimpLayer *layer;
|
|
|
|
layer = GIMP_LAYER (drawable);
|
|
|
|
if (gimp_drawable_has_alpha (drawable) && layer->preserve_trans)
|
|
active[gimp_drawable_bytes (drawable) - 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_image_set_component_visible (GimpImage *gimage,
|
|
GimpChannelType type,
|
|
gboolean visible)
|
|
{
|
|
gint pixel = -1;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
switch (type)
|
|
{
|
|
case GIMP_RED_CHANNEL: pixel = RED_PIX; break;
|
|
case GIMP_GREEN_CHANNEL: pixel = GREEN_PIX; break;
|
|
case GIMP_BLUE_CHANNEL: pixel = BLUE_PIX; break;
|
|
case GIMP_GRAY_CHANNEL: pixel = GRAY_PIX; break;
|
|
case GIMP_INDEXED_CHANNEL: pixel = INDEXED_PIX; break;
|
|
case GIMP_ALPHA_CHANNEL:
|
|
switch (gimp_image_base_type (gimage))
|
|
{
|
|
case GIMP_RGB: pixel = ALPHA_PIX; break;
|
|
case GIMP_GRAY: pixel = ALPHA_G_PIX; break;
|
|
case GIMP_INDEXED: pixel = ALPHA_I_PIX; break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (pixel != -1 && visible != gimage->visible[pixel])
|
|
{
|
|
gimage->visible[pixel] = visible ? TRUE : FALSE;
|
|
|
|
g_signal_emit (G_OBJECT (gimage),
|
|
gimp_image_signals[COMPONENT_VISIBILITY_CHANGED], 0,
|
|
type);
|
|
|
|
gimp_image_update (gimage, 0, 0, gimage->width, gimage->height);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_get_component_visible (const GimpImage *gimage,
|
|
GimpChannelType type)
|
|
{
|
|
/* No sanity checking here... */
|
|
switch (type)
|
|
{
|
|
case GIMP_RED_CHANNEL: return gimage->visible[RED_PIX]; break;
|
|
case GIMP_GREEN_CHANNEL: return gimage->visible[GREEN_PIX]; break;
|
|
case GIMP_BLUE_CHANNEL: return gimage->visible[BLUE_PIX]; break;
|
|
case GIMP_GRAY_CHANNEL: return gimage->visible[GRAY_PIX]; break;
|
|
case GIMP_INDEXED_CHANNEL: return gimage->visible[INDEXED_PIX]; break;
|
|
case GIMP_ALPHA_CHANNEL:
|
|
switch (gimp_image_base_type (gimage))
|
|
{
|
|
case GIMP_RGB: return gimage->visible[ALPHA_PIX]; break;
|
|
case GIMP_GRAY: return gimage->visible[ALPHA_G_PIX]; break;
|
|
case GIMP_INDEXED: return gimage->visible[ALPHA_I_PIX]; break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
gimp_image_mode_changed (GimpImage *gimage)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
g_signal_emit (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (gimage), gimp_image_signals[UPDATE_GUIDE], 0,
|
|
guide);
|
|
}
|
|
|
|
void
|
|
gimp_image_selection_control (GimpImage *gimage,
|
|
GimpSelectionControl control)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
g_signal_emit (G_OBJECT (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 (G_OBJECT (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 */
|
|
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_event (GimpImage *gimage,
|
|
gint event)
|
|
{
|
|
g_signal_emit (G_OBJECT (gimage), gimp_image_signals[UNDO_EVENT], 0,
|
|
event);
|
|
}
|
|
|
|
|
|
/* 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 (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (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, pfg, fg, GIMP_RGB);
|
|
}
|
|
|
|
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, pbg, bg, GIMP_RGB);
|
|
}
|
|
|
|
void
|
|
gimp_image_get_color (const GimpImage *gimage,
|
|
GimpImageType d_type,
|
|
guchar *rgb,
|
|
guchar *src)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
switch (d_type)
|
|
{
|
|
case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE:
|
|
map_to_color (0, NULL, src, rgb);
|
|
break;
|
|
case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE:
|
|
map_to_color (1, NULL, src, rgb);
|
|
break;
|
|
case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
|
|
map_to_color (2, gimage->cmap, src, rgb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_image_transform_color (const GimpImage *gimage,
|
|
const GimpDrawable *drawable,
|
|
guchar *src,
|
|
guchar *dest,
|
|
GimpImageBaseType type)
|
|
{
|
|
GimpImageType drawable_type;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
drawable_type = (drawable ?
|
|
gimp_drawable_type (drawable) :
|
|
gimp_image_base_type_with_alpha (gimage));
|
|
|
|
switch (type)
|
|
{
|
|
case GIMP_RGB:
|
|
switch (drawable_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 (gimage,
|
|
src[RED_PIX],
|
|
src[GREEN_PIX],
|
|
src[BLUE_PIX]);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GIMP_GRAY:
|
|
switch (drawable_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 (gimage,
|
|
src[GRAY_PIX],
|
|
src[GRAY_PIX],
|
|
src[GRAY_PIX]);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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_destroy (gimage->shadow);
|
|
gimage->shadow = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* combine functions */
|
|
|
|
void
|
|
gimp_image_apply_image (GimpImage *gimage,
|
|
GimpDrawable *drawable,
|
|
PixelRegion *src2PR,
|
|
gboolean push_undo,
|
|
gdouble opacity,
|
|
GimpLayerModeEffects mode,
|
|
/* alternative to using drawable tiles as src1: */
|
|
TileManager *src1_tiles,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
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));
|
|
|
|
/* get the selection mask if one exists */
|
|
mask = (gimp_image_mask_is_empty (gimage) ?
|
|
NULL : gimp_image_get_mask (gimage));
|
|
|
|
/* 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_drawable_offsets (drawable, &offset_x, &offset_y);
|
|
|
|
/* make sure the image application coordinates are within gimage bounds */
|
|
x1 = CLAMP (x, 0, gimp_drawable_width (drawable));
|
|
y1 = CLAMP (y, 0, gimp_drawable_height (drawable));
|
|
x2 = CLAMP (x + src2PR->w, 0, gimp_drawable_width (drawable));
|
|
y2 = CLAMP (y + src2PR->h, 0, gimp_drawable_height (drawable));
|
|
|
|
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_drawable_width (GIMP_DRAWABLE (mask))-offset_x);
|
|
y1 = CLAMP (y1, -offset_y, gimp_drawable_height(GIMP_DRAWABLE (mask))-offset_y);
|
|
x2 = CLAMP (x2, -offset_x, gimp_drawable_width (GIMP_DRAWABLE (mask))-offset_x);
|
|
y2 = CLAMP (y2, -offset_y, gimp_drawable_height(GIMP_DRAWABLE (mask))-offset_y);
|
|
}
|
|
|
|
/* If the calling procedure specified an undo step... */
|
|
if (push_undo)
|
|
gimp_drawable_push_undo (drawable, 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,
|
|
gdouble opacity,
|
|
PixelRegion *maskPR,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
GimpChannel *mask;
|
|
gint x1, y1, x2, y2;
|
|
gint offset_x, offset_y;
|
|
PixelRegion src1PR, destPR;
|
|
PixelRegion mask2PR, tempPR;
|
|
guchar *temp_data;
|
|
CombinationMode operation;
|
|
gboolean active_components[MAX_CHANNELS];
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
/* get the selection mask if one exists */
|
|
mask = (gimp_image_mask_is_empty (gimage)) ? NULL : gimp_image_get_mask (gimage);
|
|
|
|
/* 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_drawable_offsets (drawable, &offset_x, &offset_y);
|
|
|
|
/* make sure the image application coordinates are within gimage bounds */
|
|
x1 = CLAMP (x, 0, gimp_drawable_width (drawable));
|
|
y1 = CLAMP (y, 0, gimp_drawable_height (drawable));
|
|
x2 = CLAMP (x + src2PR->w, 0, gimp_drawable_width (drawable));
|
|
y2 = CLAMP (y + src2PR->h, 0, gimp_drawable_height (drawable));
|
|
|
|
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_drawable_width (GIMP_DRAWABLE (mask))-offset_x);
|
|
y1 = CLAMP (y1, -offset_y, gimp_drawable_height(GIMP_DRAWABLE (mask))-offset_y);
|
|
x2 = CLAMP (x2, -offset_x, gimp_drawable_width (GIMP_DRAWABLE (mask))-offset_x);
|
|
y2 = CLAMP (y2, -offset_y, gimp_drawable_height(GIMP_DRAWABLE (mask))-offset_y);
|
|
}
|
|
|
|
/* If the calling procedure specified an undo step... */
|
|
if (push_undo)
|
|
gimp_drawable_push_undo (drawable, 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)
|
|
{
|
|
int 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;
|
|
temp_data = g_malloc (tempPR.h * tempPR.rowstride);
|
|
tempPR.data = temp_data;
|
|
|
|
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))
|
|
undo_push_image_parasite (gimage, 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))
|
|
undo_push_image_parasite_remove (gimage, 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 / paths */
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_paths (GimpImage *gimage,
|
|
PathList *paths)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
gimage->paths = paths;
|
|
}
|
|
|
|
PathList *
|
|
gimp_image_get_paths (const GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
|
|
|
|
return gimage->paths;
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* 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);
|
|
|
|
/* invalidate the selection boundary because of a layer modification */
|
|
gimp_layer_invalidate_boundary (layer);
|
|
}
|
|
|
|
if (layer != gimage->active_layer)
|
|
{
|
|
gimage->active_layer = layer;
|
|
|
|
g_signal_emit (G_OBJECT (gimage),
|
|
gimp_image_signals[ACTIVE_LAYER_CHANGED], 0);
|
|
|
|
if (gimage->active_channel)
|
|
{
|
|
gimage->active_channel = NULL;
|
|
|
|
g_signal_emit (G_OBJECT (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 (G_OBJECT (gimage),
|
|
gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0);
|
|
|
|
if (gimage->active_layer)
|
|
{
|
|
gimage->active_layer = NULL;
|
|
|
|
g_signal_emit (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (gimage),
|
|
gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0);
|
|
}
|
|
|
|
return vectors;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 (! strcmp (gimp_object_get_name (GIMP_OBJECT (layer)), name))
|
|
return layer;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_get_channel_by_name (const GimpImage *gimage,
|
|
const gchar *name)
|
|
{
|
|
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 (! strcmp (gimp_object_get_name (GIMP_OBJECT (channel)), name))
|
|
return channel;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GimpVectors *
|
|
gimp_image_get_vectors_by_name (const GimpImage *gimage,
|
|
const gchar *name)
|
|
{
|
|
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 (! strcmp (gimp_object_get_name (GIMP_OBJECT (vectors)), name))
|
|
return vectors;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
undo_push_layer_add (gimage,
|
|
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;
|
|
}
|
|
|
|
gimp_container_insert (gimage->layers, GIMP_OBJECT (layer), position);
|
|
g_object_unref (G_OBJECT (layer));
|
|
|
|
/* notify the layers dialog of the currently active layer */
|
|
gimp_image_set_active_layer (gimage, layer);
|
|
|
|
/* update the new layer's area */
|
|
gimp_drawable_update (GIMP_DRAWABLE (layer),
|
|
0, 0,
|
|
gimp_drawable_width (GIMP_DRAWABLE (layer)),
|
|
gimp_drawable_height (GIMP_DRAWABLE (layer)));
|
|
|
|
if (alpha_changed)
|
|
gimp_image_alpha_changed (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_remove_layer (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
gint x, y, w, h;
|
|
|
|
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)));
|
|
|
|
undo_push_layer_remove (gimage,
|
|
layer,
|
|
gimp_container_get_child_index (gimage->layers,
|
|
GIMP_OBJECT (layer)),
|
|
layer);
|
|
|
|
g_object_ref (G_OBJECT (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 (G_OBJECT (gimage),
|
|
gimp_image_signals[ACTIVE_LAYER_CHANGED], 0);
|
|
}
|
|
}
|
|
|
|
/* Send out REMOVED signal from layer */
|
|
gimp_item_removed (GIMP_ITEM (layer));
|
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE (layer), &x, &y);
|
|
w = gimp_drawable_width (GIMP_DRAWABLE (layer));
|
|
h = gimp_drawable_height (GIMP_DRAWABLE (layer));
|
|
|
|
g_object_unref (G_OBJECT (layer));
|
|
|
|
gimp_image_update (gimage, x, y, w, h);
|
|
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage));
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_position_layer (GimpImage *gimage,
|
|
GimpLayer *layer,
|
|
gint new_index,
|
|
gboolean push_undo)
|
|
{
|
|
gint off_x, off_y;
|
|
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)
|
|
undo_push_layer_reposition (gimage, layer);
|
|
|
|
gimp_container_reorder (gimage->layers, GIMP_OBJECT (layer), new_index);
|
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
|
|
|
|
gimp_image_update (gimage,
|
|
off_x, off_y,
|
|
gimp_drawable_width (GIMP_DRAWABLE (layer)),
|
|
gimp_drawable_height (GIMP_DRAWABLE (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;
|
|
}
|
|
|
|
undo_push_channel_add (gimage,
|
|
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;
|
|
}
|
|
}
|
|
|
|
gimp_container_insert (gimage->channels, GIMP_OBJECT (channel), position);
|
|
g_object_unref (G_OBJECT (channel));
|
|
|
|
/* notify this gimage of the currently active channel */
|
|
gimp_image_set_active_channel (gimage, channel);
|
|
|
|
/* if channel is visible, update the image */
|
|
if (gimp_drawable_get_visible (GIMP_DRAWABLE (channel)))
|
|
gimp_drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
gimp_drawable_width (GIMP_DRAWABLE (channel)),
|
|
gimp_drawable_height (GIMP_DRAWABLE (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)));
|
|
|
|
undo_push_channel_remove (gimage,
|
|
channel,
|
|
gimp_container_get_child_index (gimage->channels,
|
|
GIMP_OBJECT (channel)),
|
|
gimp_image_get_active_channel (gimage));
|
|
|
|
g_object_ref (G_OBJECT (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 (G_OBJECT (channel));
|
|
|
|
gimp_image_update (gimage, 0, 0, gimage->width, gimage->height);
|
|
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_position_channel (GimpImage *gimage,
|
|
GimpChannel *channel,
|
|
gint new_index,
|
|
gboolean push_undo)
|
|
{
|
|
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)
|
|
undo_push_channel_reposition (gimage, channel);
|
|
|
|
gimp_container_reorder (gimage->channels,
|
|
GIMP_OBJECT (channel), new_index);
|
|
|
|
gimp_drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
gimp_drawable_width (GIMP_DRAWABLE (channel)),
|
|
gimp_drawable_height (GIMP_DRAWABLE (channel)));
|
|
|
|
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;
|
|
}
|
|
|
|
undo_push_vectors_add (gimage,
|
|
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;
|
|
}
|
|
}
|
|
|
|
gimp_container_insert (gimage->vectors, GIMP_OBJECT (vectors), position);
|
|
g_object_unref (G_OBJECT (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)));
|
|
|
|
undo_push_vectors_remove (gimage,
|
|
vectors,
|
|
gimp_container_get_child_index (gimage->vectors,
|
|
GIMP_OBJECT (vectors)),
|
|
gimp_image_get_active_vectors (gimage));
|
|
|
|
g_object_ref (G_OBJECT (vectors));
|
|
|
|
gimp_container_remove (gimage->vectors, GIMP_OBJECT (vectors));
|
|
|
|
gimp_item_set_image (GIMP_ITEM (vectors), NULL);
|
|
|
|
/* 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 (G_OBJECT (gimage),
|
|
gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0);
|
|
}
|
|
}
|
|
|
|
g_object_unref (G_OBJECT (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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_position_vectors (GimpImage *gimage,
|
|
GimpVectors *vectors,
|
|
gint new_index,
|
|
gboolean push_undo)
|
|
{
|
|
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)
|
|
undo_push_vectors_reposition (gimage, 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);
|
|
}
|