mirror of https://github.com/GNOME/gimp.git
4025 lines
118 KiB
C
4025 lines
118 KiB
C
/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <cairo.h>
|
|
#include <gegl.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpconfig/gimpconfig.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "config/gimpcoreconfig.h"
|
|
|
|
#include "gegl/gimp-babl.h"
|
|
|
|
#include "gimp.h"
|
|
#include "gimp-parasites.h"
|
|
#include "gimp-utils.h"
|
|
#include "gimpcontext.h"
|
|
#include "gimpdrawablestack.h"
|
|
#include "gimpgrid.h"
|
|
#include "gimperror.h"
|
|
#include "gimpguide.h"
|
|
#include "gimpidtable.h"
|
|
#include "gimpimage.h"
|
|
#include "gimpimage-colormap.h"
|
|
#include "gimpimage-guides.h"
|
|
#include "gimpimage-sample-points.h"
|
|
#include "gimpimage-preview.h"
|
|
#include "gimpimage-private.h"
|
|
#include "gimpimage-quick-mask.h"
|
|
#include "gimpimage-undo.h"
|
|
#include "gimpimage-undo-push.h"
|
|
#include "gimpitemtree.h"
|
|
#include "gimplayer.h"
|
|
#include "gimplayer-floating-sel.h"
|
|
#include "gimplayermask.h"
|
|
#include "gimpmarshal.h"
|
|
#include "gimpparasitelist.h"
|
|
#include "gimppickable.h"
|
|
#include "gimpprojectable.h"
|
|
#include "gimpprojection.h"
|
|
#include "gimpsamplepoint.h"
|
|
#include "gimpselection.h"
|
|
#include "gimptempbuf.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) g_printerr x
|
|
#else
|
|
#define TRC(x)
|
|
#endif
|
|
|
|
/* Data keys for GimpImage */
|
|
#define GIMP_FILE_EXPORT_URI_KEY "gimp-file-export-uri"
|
|
#define GIMP_FILE_SAVE_A_COPY_URI_KEY "gimp-file-save-a-copy-uri"
|
|
#define GIMP_FILE_IMPORT_SOURCE_URI_KEY "gimp-file-import-source-uri"
|
|
|
|
|
|
enum
|
|
{
|
|
MODE_CHANGED,
|
|
PRECISION_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,
|
|
SIZE_CHANGED_DETAILED,
|
|
UNIT_CHANGED,
|
|
QUICK_MASK_CHANGED,
|
|
SELECTION_INVALIDATE,
|
|
CLEAN,
|
|
DIRTY,
|
|
SAVED,
|
|
EXPORTED,
|
|
GUIDE_ADDED,
|
|
GUIDE_REMOVED,
|
|
GUIDE_MOVED,
|
|
SAMPLE_POINT_ADDED,
|
|
SAMPLE_POINT_REMOVED,
|
|
SAMPLE_POINT_MOVED,
|
|
PARASITE_ATTACHED,
|
|
PARASITE_DETACHED,
|
|
COLORMAP_CHANGED,
|
|
UNDO_EVENT,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_GIMP,
|
|
PROP_ID,
|
|
PROP_WIDTH,
|
|
PROP_HEIGHT,
|
|
PROP_BASE_TYPE,
|
|
PROP_PRECISION
|
|
};
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface);
|
|
static void gimp_projectable_iface_init (GimpProjectableInterface *iface);
|
|
|
|
static void gimp_image_constructed (GObject *object);
|
|
static void gimp_image_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_image_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
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 gboolean gimp_image_get_size (GimpViewable *viewable,
|
|
gint *width,
|
|
gint *height);
|
|
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 *image);
|
|
static void gimp_image_real_precision_changed(GimpImage *image);
|
|
static void gimp_image_real_size_changed_detailed
|
|
(GimpImage *image,
|
|
gint previous_origin_x,
|
|
gint previous_origin_y,
|
|
gint previous_width,
|
|
gint previous_height);
|
|
static void gimp_image_real_colormap_changed (GimpImage *image,
|
|
gint color_index);
|
|
|
|
static const guint8 * gimp_image_get_icc_profile (GimpColorManaged *managed,
|
|
gsize *len);
|
|
|
|
static void gimp_image_projectable_flush (GimpProjectable *projectable,
|
|
gboolean invalidate_preview);
|
|
static GeglNode * gimp_image_get_graph (GimpProjectable *projectable);
|
|
static GimpImage * gimp_image_get_image (GimpProjectable *projectable);
|
|
static const Babl * gimp_image_get_proj_format (GimpProjectable *projectable);
|
|
|
|
static void gimp_image_mask_update (GimpDrawable *drawable,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height,
|
|
GimpImage *image);
|
|
static void gimp_image_layer_alpha_changed (GimpDrawable *drawable,
|
|
GimpImage *image);
|
|
static void gimp_image_channel_add (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *image);
|
|
static void gimp_image_channel_remove (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *image);
|
|
static void gimp_image_channel_name_changed (GimpChannel *channel,
|
|
GimpImage *image);
|
|
static void gimp_image_channel_color_changed (GimpChannel *channel,
|
|
GimpImage *image);
|
|
static void gimp_image_active_layer_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image);
|
|
static void gimp_image_active_channel_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image);
|
|
static void gimp_image_active_vectors_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image);
|
|
|
|
|
|
static const 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 },
|
|
};
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GimpImage, gimp_image, GIMP_TYPE_VIEWABLE,
|
|
G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED,
|
|
gimp_color_managed_iface_init)
|
|
G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROJECTABLE,
|
|
gimp_projectable_iface_init))
|
|
|
|
#define parent_class gimp_image_parent_class
|
|
|
|
static guint gimp_image_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static void
|
|
gimp_image_class_init (GimpImageClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
|
|
GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (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[PRECISION_CHANGED] =
|
|
g_signal_new ("precision-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, precision_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[SIZE_CHANGED_DETAILED] =
|
|
g_signal_new ("size-changed-detailed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, size_changed_detailed),
|
|
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[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[QUICK_MASK_CHANGED] =
|
|
g_signal_new ("quick-mask-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, quick_mask_changed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gimp_image_signals[SELECTION_INVALIDATE] =
|
|
g_signal_new ("selection-invalidate",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, selection_invalidate),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
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__FLAGS,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_DIRTY_MASK);
|
|
|
|
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__FLAGS,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_DIRTY_MASK);
|
|
|
|
gimp_image_signals[SAVED] =
|
|
g_signal_new ("saved",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, saved),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
gimp_image_signals[EXPORTED] =
|
|
g_signal_new ("exported",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, exported),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
gimp_image_signals[GUIDE_ADDED] =
|
|
g_signal_new ("guide-added",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, guide_added),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_GUIDE);
|
|
|
|
gimp_image_signals[GUIDE_REMOVED] =
|
|
g_signal_new ("guide-removed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, guide_removed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_GUIDE);
|
|
|
|
gimp_image_signals[GUIDE_MOVED] =
|
|
g_signal_new ("guide-moved",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, guide_moved),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_GUIDE);
|
|
|
|
gimp_image_signals[SAMPLE_POINT_ADDED] =
|
|
g_signal_new ("sample-point-added",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, sample_point_added),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_POINTER);
|
|
|
|
gimp_image_signals[SAMPLE_POINT_REMOVED] =
|
|
g_signal_new ("sample-point-removed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, sample_point_removed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_POINTER);
|
|
|
|
gimp_image_signals[SAMPLE_POINT_MOVED] =
|
|
g_signal_new ("sample-point-moved",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, sample_point_moved),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_POINTER);
|
|
|
|
gimp_image_signals[PARASITE_ATTACHED] =
|
|
g_signal_new ("parasite-attached",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, parasite_attached),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
gimp_image_signals[PARASITE_DETACHED] =
|
|
g_signal_new ("parasite-detached",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpImageClass, parasite_detached),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
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__ENUM_OBJECT,
|
|
G_TYPE_NONE, 2,
|
|
GIMP_TYPE_UNDO_EVENT,
|
|
GIMP_TYPE_UNDO);
|
|
|
|
object_class->constructed = gimp_image_constructed;
|
|
object_class->set_property = gimp_image_set_property;
|
|
object_class->get_property = gimp_image_get_property;
|
|
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->get_size = gimp_image_get_size;
|
|
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->precision_changed = gimp_image_real_precision_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->resolution_changed = NULL;
|
|
klass->size_changed_detailed = gimp_image_real_size_changed_detailed;
|
|
klass->unit_changed = NULL;
|
|
klass->quick_mask_changed = NULL;
|
|
klass->selection_invalidate = NULL;
|
|
|
|
klass->clean = NULL;
|
|
klass->dirty = NULL;
|
|
klass->saved = NULL;
|
|
klass->exported = NULL;
|
|
klass->guide_added = NULL;
|
|
klass->guide_removed = NULL;
|
|
klass->guide_moved = NULL;
|
|
klass->sample_point_added = NULL;
|
|
klass->sample_point_removed = NULL;
|
|
klass->sample_point_moved = NULL;
|
|
klass->parasite_attached = NULL;
|
|
klass->parasite_detached = NULL;
|
|
klass->colormap_changed = gimp_image_real_colormap_changed;
|
|
klass->undo_event = NULL;
|
|
|
|
g_object_class_install_property (object_class, PROP_GIMP,
|
|
g_param_spec_object ("gimp", NULL, NULL,
|
|
GIMP_TYPE_GIMP,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property (object_class, PROP_ID,
|
|
g_param_spec_int ("id", NULL, NULL,
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_WIDTH,
|
|
g_param_spec_int ("width", NULL, NULL,
|
|
1, GIMP_MAX_IMAGE_SIZE, 1,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_HEIGHT,
|
|
g_param_spec_int ("height", NULL, NULL,
|
|
1, GIMP_MAX_IMAGE_SIZE, 1,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_BASE_TYPE,
|
|
g_param_spec_enum ("base-type", NULL, NULL,
|
|
GIMP_TYPE_IMAGE_BASE_TYPE,
|
|
GIMP_RGB,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_PRECISION,
|
|
g_param_spec_enum ("precision", NULL, NULL,
|
|
GIMP_TYPE_PRECISION,
|
|
GIMP_PRECISION_U8,
|
|
GIMP_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_type_class_add_private (klass, sizeof (GimpImagePrivate));
|
|
}
|
|
|
|
static void
|
|
gimp_color_managed_iface_init (GimpColorManagedInterface *iface)
|
|
{
|
|
iface->get_icc_profile = gimp_image_get_icc_profile;
|
|
}
|
|
|
|
static void
|
|
gimp_projectable_iface_init (GimpProjectableInterface *iface)
|
|
{
|
|
iface->flush = gimp_image_projectable_flush;
|
|
iface->get_image = gimp_image_get_image;
|
|
iface->get_format = gimp_image_get_proj_format;
|
|
iface->get_size = (void (*) (GimpProjectable*, gint*, gint*)) gimp_image_get_size;
|
|
iface->get_graph = gimp_image_get_graph;
|
|
iface->invalidate_preview = (void (*) (GimpProjectable*)) gimp_viewable_invalidate_preview;
|
|
iface->get_layers = (GList * (*) (GimpProjectable*)) gimp_image_get_layer_iter;
|
|
iface->get_channels = (GList * (*) (GimpProjectable*)) gimp_image_get_channel_iter;
|
|
}
|
|
|
|
static void
|
|
gimp_image_init (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
gint i;
|
|
|
|
private->ID = 0;
|
|
|
|
private->load_proc = NULL;
|
|
private->save_proc = NULL;
|
|
|
|
private->width = 0;
|
|
private->height = 0;
|
|
private->xresolution = 1.0;
|
|
private->yresolution = 1.0;
|
|
private->resolution_unit = GIMP_UNIT_INCH;
|
|
private->base_type = GIMP_RGB;
|
|
private->precision = GIMP_PRECISION_U8;
|
|
|
|
private->colormap = NULL;
|
|
private->n_colors = 0;
|
|
private->palette = NULL;
|
|
|
|
private->dirty = 1;
|
|
private->dirty_time = 0;
|
|
private->undo_freeze_count = 0;
|
|
|
|
private->export_dirty = 1;
|
|
|
|
private->instance_count = 0;
|
|
private->disp_count = 0;
|
|
|
|
private->tattoo_state = 0;
|
|
|
|
private->projection = gimp_projection_new (GIMP_PROJECTABLE (image));
|
|
|
|
private->guides = NULL;
|
|
private->grid = NULL;
|
|
private->sample_points = NULL;
|
|
|
|
private->layers = gimp_item_tree_new (image,
|
|
GIMP_TYPE_DRAWABLE_STACK,
|
|
GIMP_TYPE_LAYER);
|
|
private->channels = gimp_item_tree_new (image,
|
|
GIMP_TYPE_DRAWABLE_STACK,
|
|
GIMP_TYPE_CHANNEL);
|
|
private->vectors = gimp_item_tree_new (image,
|
|
GIMP_TYPE_ITEM_STACK,
|
|
GIMP_TYPE_VECTORS);
|
|
private->layer_stack = NULL;
|
|
|
|
g_signal_connect (private->layers, "notify::active-item",
|
|
G_CALLBACK (gimp_image_active_layer_notify),
|
|
image);
|
|
g_signal_connect (private->channels, "notify::active-item",
|
|
G_CALLBACK (gimp_image_active_channel_notify),
|
|
image);
|
|
g_signal_connect (private->vectors, "notify::active-item",
|
|
G_CALLBACK (gimp_image_active_vectors_notify),
|
|
image);
|
|
|
|
g_signal_connect_swapped (private->layers->container, "update",
|
|
G_CALLBACK (gimp_image_invalidate),
|
|
image);
|
|
|
|
private->layer_alpha_handler =
|
|
gimp_container_add_handler (private->layers->container, "alpha-changed",
|
|
G_CALLBACK (gimp_image_layer_alpha_changed),
|
|
image);
|
|
|
|
g_signal_connect_swapped (private->channels->container, "update",
|
|
G_CALLBACK (gimp_image_invalidate),
|
|
image);
|
|
|
|
private->channel_name_changed_handler =
|
|
gimp_container_add_handler (private->channels->container, "name-changed",
|
|
G_CALLBACK (gimp_image_channel_name_changed),
|
|
image);
|
|
private->channel_color_changed_handler =
|
|
gimp_container_add_handler (private->channels->container, "color-changed",
|
|
G_CALLBACK (gimp_image_channel_color_changed),
|
|
image);
|
|
|
|
g_signal_connect (private->channels->container, "add",
|
|
G_CALLBACK (gimp_image_channel_add),
|
|
image);
|
|
g_signal_connect (private->channels->container, "remove",
|
|
G_CALLBACK (gimp_image_channel_remove),
|
|
image);
|
|
|
|
private->floating_sel = NULL;
|
|
private->selection_mask = NULL;
|
|
|
|
private->parasites = gimp_parasite_list_new ();
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++)
|
|
{
|
|
private->visible[i] = TRUE;
|
|
private->active[i] = TRUE;
|
|
}
|
|
|
|
private->quick_mask_state = FALSE;
|
|
private->quick_mask_inverted = FALSE;
|
|
gimp_rgba_set (&private->quick_mask_color, 1.0, 0.0, 0.0, 0.5);
|
|
|
|
private->undo_stack = gimp_undo_stack_new (image);
|
|
private->redo_stack = gimp_undo_stack_new (image);
|
|
private->group_count = 0;
|
|
private->pushing_undo_group = GIMP_UNDO_GROUP_NONE;
|
|
|
|
private->preview = NULL;
|
|
|
|
private->flush_accum.alpha_changed = FALSE;
|
|
private->flush_accum.mask_changed = FALSE;
|
|
private->flush_accum.floating_selection_changed = FALSE;
|
|
private->flush_accum.preview_invalidated = FALSE;
|
|
}
|
|
|
|
static void
|
|
gimp_image_constructed (GObject *object)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
GimpChannel *selection;
|
|
GimpCoreConfig *config;
|
|
GimpTemplate *template;
|
|
|
|
if (G_OBJECT_CLASS (parent_class)->constructed)
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
g_assert (GIMP_IS_GIMP (image->gimp));
|
|
|
|
config = image->gimp->config;
|
|
|
|
private->ID = gimp_id_table_insert (image->gimp->image_table, image);
|
|
|
|
template = config->default_image;
|
|
|
|
private->xresolution = gimp_template_get_resolution_x (template);
|
|
private->yresolution = gimp_template_get_resolution_y (template);
|
|
private->resolution_unit = gimp_template_get_resolution_unit (template);
|
|
|
|
private->grid = gimp_config_duplicate (GIMP_CONFIG (config->default_grid));
|
|
|
|
private->quick_mask_color = config->quick_mask_color;
|
|
|
|
if (private->base_type == GIMP_INDEXED)
|
|
gimp_image_colormap_init (image);
|
|
|
|
selection = gimp_selection_new (image,
|
|
gimp_image_get_width (image),
|
|
gimp_image_get_height (image));
|
|
gimp_image_take_mask (image, selection);
|
|
|
|
g_signal_connect_object (config, "notify::transparency-type",
|
|
G_CALLBACK (gimp_item_stack_invalidate_previews),
|
|
private->layers->container, G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (config, "notify::transparency-size",
|
|
G_CALLBACK (gimp_item_stack_invalidate_previews),
|
|
private->layers->container, G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (config, "notify::layer-previews",
|
|
G_CALLBACK (gimp_viewable_size_changed),
|
|
image, G_CONNECT_SWAPPED);
|
|
|
|
gimp_container_add (image->gimp->images, GIMP_OBJECT (image));
|
|
}
|
|
|
|
static void
|
|
gimp_image_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_GIMP:
|
|
image->gimp = g_value_get_object (value);
|
|
break;
|
|
case PROP_ID:
|
|
g_assert_not_reached ();
|
|
break;
|
|
case PROP_WIDTH:
|
|
private->width = g_value_get_int (value);
|
|
break;
|
|
case PROP_HEIGHT:
|
|
private->height = g_value_get_int (value);
|
|
break;
|
|
case PROP_BASE_TYPE:
|
|
private->base_type = g_value_get_enum (value);
|
|
break;
|
|
case PROP_PRECISION:
|
|
private->precision = g_value_get_enum (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_GIMP:
|
|
g_value_set_object (value, image->gimp);
|
|
break;
|
|
case PROP_ID:
|
|
g_value_set_int (value, private->ID);
|
|
break;
|
|
case PROP_WIDTH:
|
|
g_value_set_int (value, private->width);
|
|
break;
|
|
case PROP_HEIGHT:
|
|
g_value_set_int (value, private->height);
|
|
break;
|
|
case PROP_BASE_TYPE:
|
|
g_value_set_enum (value, private->base_type);
|
|
break;
|
|
case PROP_PRECISION:
|
|
g_value_set_enum (value, private->precision);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_dispose (GObject *object)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->colormap)
|
|
gimp_image_colormap_dispose (image);
|
|
|
|
gimp_image_undo_free (image);
|
|
|
|
g_signal_handlers_disconnect_by_func (private->layers->container,
|
|
gimp_image_invalidate,
|
|
image);
|
|
|
|
gimp_container_remove_handler (private->layers->container,
|
|
private->layer_alpha_handler);
|
|
|
|
g_signal_handlers_disconnect_by_func (private->channels->container,
|
|
gimp_image_invalidate,
|
|
image);
|
|
|
|
gimp_container_remove_handler (private->channels->container,
|
|
private->channel_name_changed_handler);
|
|
gimp_container_remove_handler (private->channels->container,
|
|
private->channel_color_changed_handler);
|
|
|
|
g_signal_handlers_disconnect_by_func (private->channels->container,
|
|
gimp_image_channel_add,
|
|
image);
|
|
g_signal_handlers_disconnect_by_func (private->channels->container,
|
|
gimp_image_channel_remove,
|
|
image);
|
|
|
|
gimp_container_foreach (private->layers->container,
|
|
(GFunc) gimp_item_removed, NULL);
|
|
gimp_container_foreach (private->channels->container,
|
|
(GFunc) gimp_item_removed, NULL);
|
|
gimp_container_foreach (private->vectors->container,
|
|
(GFunc) gimp_item_removed, NULL);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gimp_image_finalize (GObject *object)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->projection)
|
|
{
|
|
g_object_unref (private->projection);
|
|
private->projection = NULL;
|
|
}
|
|
|
|
if (private->graph)
|
|
{
|
|
g_object_unref (private->graph);
|
|
private->graph = NULL;
|
|
}
|
|
|
|
if (private->colormap)
|
|
gimp_image_colormap_free (image);
|
|
|
|
if (private->layers)
|
|
{
|
|
g_object_unref (private->layers);
|
|
private->layers = NULL;
|
|
}
|
|
if (private->channels)
|
|
{
|
|
g_object_unref (private->channels);
|
|
private->channels = NULL;
|
|
}
|
|
if (private->vectors)
|
|
{
|
|
g_object_unref (private->vectors);
|
|
private->vectors = NULL;
|
|
}
|
|
if (private->layer_stack)
|
|
{
|
|
g_slist_free (private->layer_stack);
|
|
private->layer_stack = NULL;
|
|
}
|
|
|
|
if (private->selection_mask)
|
|
{
|
|
g_object_unref (private->selection_mask);
|
|
private->selection_mask = NULL;
|
|
}
|
|
|
|
if (private->preview)
|
|
{
|
|
gimp_temp_buf_unref (private->preview);
|
|
private->preview = NULL;
|
|
}
|
|
|
|
if (private->parasites)
|
|
{
|
|
g_object_unref (private->parasites);
|
|
private->parasites = NULL;
|
|
}
|
|
|
|
if (private->guides)
|
|
{
|
|
g_list_free_full (private->guides, (GDestroyNotify) g_object_unref);
|
|
private->guides = NULL;
|
|
}
|
|
|
|
if (private->grid)
|
|
{
|
|
g_object_unref (private->grid);
|
|
private->grid = NULL;
|
|
}
|
|
|
|
if (private->sample_points)
|
|
{
|
|
g_list_free_full (private->sample_points,
|
|
(GDestroyNotify) gimp_sample_point_unref);
|
|
private->sample_points = NULL;
|
|
}
|
|
|
|
if (private->undo_stack)
|
|
{
|
|
g_object_unref (private->undo_stack);
|
|
private->undo_stack = NULL;
|
|
}
|
|
if (private->redo_stack)
|
|
{
|
|
g_object_unref (private->redo_stack);
|
|
private->redo_stack = NULL;
|
|
}
|
|
|
|
if (image->gimp && image->gimp->image_table)
|
|
{
|
|
gimp_id_table_remove (image->gimp->image_table, private->ID);
|
|
image->gimp = NULL;
|
|
}
|
|
|
|
if (private->display_name)
|
|
{
|
|
g_free (private->display_name);
|
|
private->display_name = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gimp_image_name_changed (GimpObject *object)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
const gchar *name;
|
|
|
|
if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
|
|
GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
|
|
|
|
if (private->display_name)
|
|
{
|
|
g_free (private->display_name);
|
|
private->display_name = NULL;
|
|
}
|
|
|
|
/* We never want the empty string as a name, so change empty strings
|
|
* to NULL strings (without emitting the "name-changed" signal
|
|
* again)
|
|
*/
|
|
name = gimp_object_get_name (object);
|
|
if (name && strlen (name) == 0)
|
|
gimp_object_name_free (object);
|
|
}
|
|
|
|
static gint64
|
|
gimp_image_get_memsize (GimpObject *object,
|
|
gint64 *gui_size)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (object);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
gint64 memsize = 0;
|
|
|
|
if (gimp_image_get_colormap (image))
|
|
memsize += GIMP_IMAGE_COLORMAP_SIZE;
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->palette),
|
|
gui_size);
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->projection),
|
|
gui_size);
|
|
|
|
memsize += gimp_g_list_get_memsize (gimp_image_get_guides (image),
|
|
sizeof (GimpGuide));
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->grid), gui_size);
|
|
|
|
memsize += gimp_g_list_get_memsize (gimp_image_get_sample_points (image),
|
|
sizeof (GimpSamplePoint));
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->layers),
|
|
gui_size);
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->channels),
|
|
gui_size);
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->vectors),
|
|
gui_size);
|
|
|
|
memsize += gimp_g_slist_get_memsize (private->layer_stack, 0);
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->selection_mask),
|
|
gui_size);
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->parasites),
|
|
gui_size);
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->undo_stack),
|
|
gui_size);
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (private->redo_stack),
|
|
gui_size);
|
|
|
|
*gui_size += gimp_temp_buf_get_memsize (private->preview);
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
|
gui_size);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_image_get_size (GimpViewable *viewable,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (viewable);
|
|
|
|
*width = gimp_image_get_width (image);
|
|
*height = gimp_image_get_height (image);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_image_invalidate_preview (GimpViewable *viewable)
|
|
{
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (viewable);
|
|
|
|
GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable);
|
|
|
|
if (private->preview)
|
|
{
|
|
gimp_temp_buf_unref (private->preview);
|
|
private->preview = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_size_changed (GimpViewable *viewable)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (viewable);
|
|
GList *all_items;
|
|
GList *list;
|
|
|
|
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
|
|
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
|
|
|
|
all_items = gimp_image_get_layer_list (image);
|
|
for (list = all_items; list; list = g_list_next (list))
|
|
{
|
|
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (list->data));
|
|
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (list->data));
|
|
|
|
if (mask)
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (mask));
|
|
}
|
|
g_list_free (all_items);
|
|
|
|
all_items = gimp_image_get_channel_list (image);
|
|
g_list_free_full (all_items, (GDestroyNotify) gimp_viewable_size_changed);
|
|
|
|
all_items = gimp_image_get_vectors_list (image);
|
|
g_list_free_full (all_items, (GDestroyNotify) gimp_viewable_size_changed);
|
|
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (image)));
|
|
|
|
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
|
|
}
|
|
|
|
static gchar *
|
|
gimp_image_get_description (GimpViewable *viewable,
|
|
gchar **tooltip)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (viewable);
|
|
|
|
if (tooltip)
|
|
{
|
|
*tooltip = file_utils_uri_display_name (gimp_image_get_uri_or_untitled (image));
|
|
}
|
|
|
|
return g_strdup_printf ("%s-%d",
|
|
gimp_image_get_display_name (image),
|
|
gimp_image_get_ID (image));
|
|
}
|
|
|
|
static void
|
|
gimp_image_real_mode_changed (GimpImage *image)
|
|
{
|
|
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
|
|
}
|
|
|
|
static void
|
|
gimp_image_real_precision_changed (GimpImage *image)
|
|
{
|
|
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
|
|
}
|
|
|
|
static void
|
|
gimp_image_real_size_changed_detailed (GimpImage *image,
|
|
gint previous_origin_x,
|
|
gint previous_origin_y,
|
|
gint previous_width,
|
|
gint previous_height)
|
|
{
|
|
/* Whenever GimpImage::size-changed-detailed is emitted, so is
|
|
* GimpViewable::size-changed. Clients choose what signal to listen
|
|
* to depending on how much info they need.
|
|
*/
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (image));
|
|
}
|
|
|
|
static void
|
|
gimp_image_real_colormap_changed (GimpImage *image,
|
|
gint color_index)
|
|
{
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->colormap)
|
|
{
|
|
babl_palette_set_palette (private->babl_palette_rgb,
|
|
gimp_babl_format (GIMP_RGB,
|
|
private->precision, FALSE),
|
|
private->colormap,
|
|
private->n_colors);
|
|
babl_palette_set_palette (private->babl_palette_rgba,
|
|
gimp_babl_format (GIMP_RGB,
|
|
private->precision, FALSE),
|
|
private->colormap,
|
|
private->n_colors);
|
|
}
|
|
|
|
if (gimp_image_get_base_type (image) == GIMP_INDEXED)
|
|
{
|
|
/* A colormap alteration affects the whole image */
|
|
gimp_image_invalidate (image,
|
|
0, 0,
|
|
gimp_image_get_width (image),
|
|
gimp_image_get_height (image));
|
|
|
|
gimp_item_stack_invalidate_previews (GIMP_ITEM_STACK (private->layers->container));
|
|
}
|
|
}
|
|
|
|
static const guint8 *
|
|
gimp_image_get_icc_profile (GimpColorManaged *managed,
|
|
gsize *len)
|
|
{
|
|
const GimpParasite *parasite;
|
|
|
|
parasite = gimp_image_parasite_find (GIMP_IMAGE (managed), "icc-profile");
|
|
|
|
if (parasite)
|
|
{
|
|
gsize data_size = gimp_parasite_data_size (parasite);
|
|
|
|
if (data_size > 0)
|
|
{
|
|
*len = data_size;
|
|
|
|
return gimp_parasite_data (parasite);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gimp_image_projectable_flush (GimpProjectable *projectable,
|
|
gboolean invalidate_preview)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (projectable);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->flush_accum.alpha_changed)
|
|
{
|
|
gimp_image_alpha_changed (image);
|
|
private->flush_accum.alpha_changed = FALSE;
|
|
}
|
|
|
|
if (private->flush_accum.mask_changed)
|
|
{
|
|
gimp_image_mask_changed (image);
|
|
private->flush_accum.mask_changed = FALSE;
|
|
}
|
|
|
|
if (private->flush_accum.floating_selection_changed)
|
|
{
|
|
gimp_image_floating_selection_changed (image);
|
|
private->flush_accum.floating_selection_changed = FALSE;
|
|
}
|
|
|
|
if (private->flush_accum.preview_invalidated)
|
|
{
|
|
/* don't invalidate the preview here, the projection does this when
|
|
* it is completely constructed.
|
|
*/
|
|
private->flush_accum.preview_invalidated = FALSE;
|
|
}
|
|
}
|
|
|
|
static GimpImage *
|
|
gimp_image_get_image (GimpProjectable *projectable)
|
|
{
|
|
return GIMP_IMAGE (projectable);
|
|
}
|
|
|
|
static const Babl *
|
|
gimp_image_get_proj_format (GimpProjectable *projectable)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (projectable);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
switch (private->base_type)
|
|
{
|
|
case GIMP_RGB:
|
|
case GIMP_INDEXED:
|
|
#if 0
|
|
/* XXX use real format once the legacy projection is gone */
|
|
return gimp_image_get_format (image, GIMP_RGB, GIMP_PRECISION_U8, TRUE);
|
|
#else
|
|
return babl_format ("R'G'B'A u8");
|
|
#endif
|
|
|
|
case GIMP_GRAY:
|
|
#if 0
|
|
/* XXX use real format once the legacy projection is gone */
|
|
return gimp_image_get_format (image, GIMP_GRAY, GIMP_PRECISION_U8, TRUE);
|
|
#else
|
|
return babl_format ("Y'A u8");
|
|
#endif
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GeglNode *
|
|
gimp_image_get_graph (GimpProjectable *projectable)
|
|
{
|
|
GimpImage *image = GIMP_IMAGE (projectable);
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
GeglNode *layers_node;
|
|
GeglNode *channels_node;
|
|
GeglNode *blend_node;
|
|
GeglNode *output;
|
|
|
|
if (private->graph)
|
|
return private->graph;
|
|
|
|
private->graph = gegl_node_new ();
|
|
|
|
layers_node =
|
|
gimp_drawable_stack_get_graph (GIMP_DRAWABLE_STACK (private->layers->container));
|
|
|
|
gegl_node_add_child (private->graph, layers_node);
|
|
|
|
channels_node =
|
|
gimp_drawable_stack_get_graph (GIMP_DRAWABLE_STACK (private->channels->container));
|
|
|
|
gegl_node_add_child (private->graph, channels_node);
|
|
|
|
blend_node = gegl_node_new_child (private->graph,
|
|
"operation", "gegl:over",
|
|
NULL);
|
|
|
|
gegl_node_connect_to (layers_node, "output",
|
|
blend_node, "input");
|
|
gegl_node_connect_to (channels_node, "output",
|
|
blend_node, "aux");
|
|
|
|
output = gegl_node_get_output_proxy (private->graph, "output");
|
|
|
|
gegl_node_connect_to (blend_node, "output",
|
|
output, "input");
|
|
|
|
return private->graph;
|
|
}
|
|
|
|
static void
|
|
gimp_image_mask_update (GimpDrawable *drawable,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height,
|
|
GimpImage *image)
|
|
{
|
|
GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.mask_changed = TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_image_layer_alpha_changed (GimpDrawable *drawable,
|
|
GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (gimp_container_get_n_children (private->layers->container) == 1)
|
|
private->flush_accum.alpha_changed = TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_add (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *image)
|
|
{
|
|
if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME,
|
|
gimp_object_get_name (channel)))
|
|
{
|
|
gimp_image_set_quick_mask_state (image, TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_remove (GimpContainer *container,
|
|
GimpChannel *channel,
|
|
GimpImage *image)
|
|
{
|
|
if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME,
|
|
gimp_object_get_name (channel)))
|
|
{
|
|
gimp_image_set_quick_mask_state (image, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_name_changed (GimpChannel *channel,
|
|
GimpImage *image)
|
|
{
|
|
if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME,
|
|
gimp_object_get_name (channel)))
|
|
{
|
|
gimp_image_set_quick_mask_state (image, TRUE);
|
|
}
|
|
else if (gimp_image_get_quick_mask_state (image) &&
|
|
! gimp_image_get_quick_mask (image))
|
|
{
|
|
gimp_image_set_quick_mask_state (image, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_channel_color_changed (GimpChannel *channel,
|
|
GimpImage *image)
|
|
{
|
|
if (! strcmp (GIMP_IMAGE_QUICK_MASK_NAME,
|
|
gimp_object_get_name (channel)))
|
|
{
|
|
GIMP_IMAGE_GET_PRIVATE (image)->quick_mask_color = channel->color;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_image_active_layer_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
GimpLayer *layer = gimp_image_get_active_layer (image);
|
|
|
|
if (layer)
|
|
{
|
|
/* Configure the layer stack to reflect this change */
|
|
private->layer_stack = g_slist_remove (private->layer_stack, layer);
|
|
private->layer_stack = g_slist_prepend (private->layer_stack, layer);
|
|
}
|
|
|
|
g_signal_emit (image, gimp_image_signals[ACTIVE_LAYER_CHANGED], 0);
|
|
|
|
if (layer && gimp_image_get_active_channel (image))
|
|
gimp_image_set_active_channel (image, NULL);
|
|
}
|
|
|
|
static void
|
|
gimp_image_active_channel_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image)
|
|
{
|
|
GimpChannel *channel = gimp_image_get_active_channel (image);
|
|
|
|
g_signal_emit (image, gimp_image_signals[ACTIVE_CHANNEL_CHANGED], 0);
|
|
|
|
if (channel && gimp_image_get_active_layer (image))
|
|
gimp_image_set_active_layer (image, NULL);
|
|
}
|
|
|
|
static void
|
|
gimp_image_active_vectors_notify (GimpItemTree *tree,
|
|
const GParamSpec *pspec,
|
|
GimpImage *image)
|
|
{
|
|
g_signal_emit (image, gimp_image_signals[ACTIVE_VECTORS_CHANGED], 0);
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
GimpImage *
|
|
gimp_image_new (Gimp *gimp,
|
|
gint width,
|
|
gint height,
|
|
GimpImageBaseType base_type,
|
|
GimpPrecision precision)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
g_return_val_if_fail (base_type != GIMP_INDEXED ||
|
|
precision == GIMP_PRECISION_U8, NULL);
|
|
|
|
return g_object_new (GIMP_TYPE_IMAGE,
|
|
"gimp", gimp,
|
|
"width", width,
|
|
"height", height,
|
|
"base-type", base_type,
|
|
"precision", precision,
|
|
NULL);
|
|
}
|
|
|
|
GimpImageBaseType
|
|
gimp_image_get_base_type (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), -1);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->base_type;
|
|
}
|
|
|
|
GimpPrecision
|
|
gimp_image_get_precision (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), -1);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->precision;
|
|
}
|
|
|
|
CombinationMode
|
|
gimp_image_get_combination_mode (GimpImageType dest_type,
|
|
gint src_bytes)
|
|
{
|
|
return valid_combinations[dest_type][src_bytes];
|
|
}
|
|
|
|
const Babl *
|
|
gimp_image_get_format (const GimpImage *image,
|
|
GimpImageBaseType base_type,
|
|
GimpPrecision precision,
|
|
gboolean with_alpha)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
switch (base_type)
|
|
{
|
|
case GIMP_RGB:
|
|
case GIMP_GRAY:
|
|
return gimp_babl_format (base_type, precision, with_alpha);
|
|
|
|
case GIMP_INDEXED:
|
|
if (precision == GIMP_PRECISION_U8)
|
|
{
|
|
if (with_alpha)
|
|
return gimp_image_colormap_get_rgba_format (image);
|
|
else
|
|
return gimp_image_colormap_get_rgb_format (image);
|
|
}
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
const Babl *
|
|
gimp_image_get_layer_format (const GimpImage *image,
|
|
gboolean with_alpha)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return gimp_image_get_format (image,
|
|
gimp_image_get_base_type (image),
|
|
gimp_image_get_precision (image),
|
|
with_alpha);
|
|
}
|
|
|
|
const Babl *
|
|
gimp_image_get_channel_format (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return gimp_image_get_format (image, GIMP_GRAY,
|
|
gimp_image_get_precision (image),
|
|
FALSE);
|
|
}
|
|
|
|
const Babl *
|
|
gimp_image_get_mask_format (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
switch (gimp_image_get_precision (image))
|
|
{
|
|
case GIMP_PRECISION_U8: return babl_format ("Y u8");
|
|
case GIMP_PRECISION_U16: return babl_format ("Y u16");
|
|
case GIMP_PRECISION_U32: return babl_format ("Y u32");
|
|
case GIMP_PRECISION_HALF: return babl_format ("Y half");
|
|
case GIMP_PRECISION_FLOAT: return babl_format ("Y float");
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_ID (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), -1);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->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 *) gimp_id_table_lookup (gimp->image_table, image_id);
|
|
}
|
|
|
|
void
|
|
gimp_image_set_uri (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
gimp_object_set_name (GIMP_OBJECT (image), uri);
|
|
}
|
|
|
|
static void
|
|
gimp_image_take_uri (GimpImage *image,
|
|
gchar *uri)
|
|
{
|
|
gimp_object_take_name (GIMP_OBJECT (image), uri);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_untitled_string:
|
|
*
|
|
* Returns: The (translated) "Untitled" string for newly created
|
|
* images.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_string_untitled (void)
|
|
{
|
|
return _("Untitled");
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_uri_or_untitled:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Get the URI of the XCF image, or "Untitled" if there is no URI.
|
|
*
|
|
* Returns: The URI, or "Untitled".
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_uri_or_untitled (const GimpImage *image)
|
|
{
|
|
const gchar *uri;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
uri = gimp_image_get_uri (image);
|
|
|
|
return uri ? uri : gimp_image_get_string_untitled ();
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_uri:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Get the URI of the XCF image, or NULL if there is no URI.
|
|
*
|
|
* Returns: The URI, or NULL.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_uri (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return gimp_object_get_name (image);
|
|
}
|
|
|
|
void
|
|
gimp_image_set_filename (GimpImage *image,
|
|
const gchar *filename)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
if (filename && strlen (filename))
|
|
{
|
|
gimp_image_take_uri (image,
|
|
file_utils_filename_to_uri (image->gimp,
|
|
filename,
|
|
NULL));
|
|
}
|
|
else
|
|
{
|
|
gimp_image_set_uri (image, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_imported_uri:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Returns: The URI of the imported image, or NULL if the image has
|
|
* been saved as XCF after it was imported.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_imported_uri (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return g_object_get_data (G_OBJECT (image),
|
|
GIMP_FILE_IMPORT_SOURCE_URI_KEY);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_exported_uri:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Returns: The URI of the image last exported from this XCF file, or
|
|
* NULL if the image has never been exported.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_exported_uri (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return g_object_get_data (G_OBJECT (image),
|
|
GIMP_FILE_EXPORT_URI_KEY);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_save_a_copy_uri:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Returns: The URI of the last copy that was saved of this XCF file.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_save_a_copy_uri (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return g_object_get_data (G_OBJECT (image),
|
|
GIMP_FILE_SAVE_A_COPY_URI_KEY);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_get_any_uri:
|
|
* @image: A #GimpImage.
|
|
*
|
|
* Returns: The XCF file URI, the imported file URI, or the exported
|
|
* file URI, in that order of precedence. Only to help implement
|
|
* backwards compatibility with GIMP 2.6 API.
|
|
**/
|
|
const gchar *
|
|
gimp_image_get_any_uri (const GimpImage *image)
|
|
{
|
|
const gchar *uri;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
uri = gimp_image_get_uri (image);
|
|
if (! uri)
|
|
{
|
|
uri = gimp_image_get_imported_uri (image);
|
|
if (! uri)
|
|
{
|
|
uri = gimp_image_get_exported_uri (image);
|
|
}
|
|
}
|
|
|
|
return uri;
|
|
}
|
|
|
|
/**
|
|
* gimp_image_set_imported_uri:
|
|
* @image: A #GimpImage.
|
|
* @uri:
|
|
*
|
|
* Sets the URI this file was imported from.
|
|
**/
|
|
void
|
|
gimp_image_set_imported_uri (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
if (gimp_image_get_imported_uri (image) == uri)
|
|
return;
|
|
|
|
g_object_set_data_full (G_OBJECT (image), GIMP_FILE_IMPORT_SOURCE_URI_KEY,
|
|
g_strdup (uri), (GDestroyNotify) g_free);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_set_exported_uri:
|
|
* @image: A #GimpImage.
|
|
* @uri:
|
|
*
|
|
* Sets the URI this file was last exported to. Note that saving as
|
|
* XCF is not "exporting".
|
|
**/
|
|
void
|
|
gimp_image_set_exported_uri (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
if (gimp_image_get_exported_uri (image) == uri)
|
|
return;
|
|
|
|
g_object_set_data_full (G_OBJECT (image),
|
|
GIMP_FILE_EXPORT_URI_KEY,
|
|
g_strdup (uri), (GDestroyNotify) g_free);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_set_save_a_copy_uri:
|
|
* @image: A #GimpImage.
|
|
* @uri:
|
|
*
|
|
* Set the URI to the last copy this XCF file was saved to through the
|
|
* "save a copy" action.
|
|
**/
|
|
void
|
|
gimp_image_set_save_a_copy_uri (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
if (gimp_image_get_save_a_copy_uri (image) == uri)
|
|
return;
|
|
|
|
g_object_set_data_full (G_OBJECT (image),
|
|
GIMP_FILE_SAVE_A_COPY_URI_KEY,
|
|
g_strdup (uri), (GDestroyNotify) g_free);
|
|
}
|
|
|
|
gchar *
|
|
gimp_image_get_filename (const GimpImage *image)
|
|
{
|
|
const gchar *uri;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
uri = gimp_image_get_uri (image);
|
|
|
|
if (! uri)
|
|
return NULL;
|
|
|
|
return g_filename_from_uri (uri, NULL, NULL);
|
|
}
|
|
|
|
const gchar *
|
|
gimp_image_get_display_name (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (! private->display_name)
|
|
{
|
|
const gchar *uri = gimp_image_get_uri_or_untitled (image);
|
|
|
|
private->display_name = file_utils_uri_display_basename (uri);
|
|
}
|
|
|
|
return private->display_name;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_load_proc (GimpImage *image,
|
|
GimpPlugInProcedure *proc)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->load_proc = proc;
|
|
}
|
|
|
|
GimpPlugInProcedure *
|
|
gimp_image_get_load_proc (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->load_proc;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_save_proc (GimpImage *image,
|
|
GimpPlugInProcedure *proc)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->save_proc = proc;
|
|
}
|
|
|
|
GimpPlugInProcedure *
|
|
gimp_image_get_save_proc (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->save_proc;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_resolution (GimpImage *image,
|
|
gdouble xresolution,
|
|
gdouble yresolution)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* 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 (private->xresolution - xresolution) >= 1e-5) ||
|
|
(ABS (private->yresolution - yresolution) >= 1e-5))
|
|
{
|
|
gimp_image_undo_push_image_resolution (image,
|
|
C_("undo-type", "Change Image Resolution"));
|
|
|
|
private->xresolution = xresolution;
|
|
private->yresolution = yresolution;
|
|
|
|
gimp_image_resolution_changed (image);
|
|
gimp_image_size_changed_detailed (image,
|
|
0,
|
|
0,
|
|
gimp_image_get_width (image),
|
|
gimp_image_get_height (image));
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_image_get_resolution (const GimpImage *image,
|
|
gdouble *xresolution,
|
|
gdouble *yresolution)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (xresolution != NULL && yresolution != NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
*xresolution = private->xresolution;
|
|
*yresolution = private->yresolution;
|
|
}
|
|
|
|
void
|
|
gimp_image_resolution_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[RESOLUTION_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_set_unit (GimpImage *image,
|
|
GimpUnit unit)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (unit > GIMP_UNIT_PIXEL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->resolution_unit != unit)
|
|
{
|
|
gimp_image_undo_push_image_resolution (image,
|
|
C_("undo-type", "Change Image Unit"));
|
|
|
|
private->resolution_unit = unit;
|
|
gimp_image_unit_changed (image);
|
|
}
|
|
}
|
|
|
|
GimpUnit
|
|
gimp_image_get_unit (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_UNIT_INCH);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->resolution_unit;
|
|
}
|
|
|
|
void
|
|
gimp_image_unit_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[UNIT_CHANGED], 0);
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_width (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->width;
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_height (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->height;
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_has_alpha (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpLayer *layer;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), TRUE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
layer = GIMP_LAYER (gimp_container_get_first_child (private->layers->container));
|
|
|
|
return ((gimp_image_get_n_layers (image) > 1) ||
|
|
(layer && gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))));
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_is_empty (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), TRUE);
|
|
|
|
return gimp_container_is_empty (GIMP_IMAGE_GET_PRIVATE (image)->layers->container);
|
|
}
|
|
|
|
void
|
|
gimp_image_set_floating_selection (GimpImage *image,
|
|
GimpLayer *floating_sel)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (floating_sel == NULL || GIMP_IS_LAYER (floating_sel));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->floating_sel != floating_sel)
|
|
{
|
|
private->floating_sel = floating_sel;
|
|
|
|
private->flush_accum.floating_selection_changed = TRUE;
|
|
}
|
|
}
|
|
|
|
GimpLayer *
|
|
gimp_image_get_floating_selection (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->floating_sel;
|
|
}
|
|
|
|
void
|
|
gimp_image_floating_selection_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[FLOATING_SELECTION_CHANGED], 0);
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_get_mask (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->selection_mask;
|
|
}
|
|
|
|
void
|
|
gimp_image_mask_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[MASK_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_take_mask (GimpImage *image,
|
|
GimpChannel *mask)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_SELECTION (mask));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (private->selection_mask)
|
|
g_object_unref (private->selection_mask);
|
|
|
|
private->selection_mask = g_object_ref_sink (mask);
|
|
|
|
g_signal_connect (private->selection_mask, "update",
|
|
G_CALLBACK (gimp_image_mask_update),
|
|
image);
|
|
}
|
|
|
|
|
|
/* image components */
|
|
|
|
const Babl *
|
|
gimp_image_get_component_format (const GimpImage *image,
|
|
GimpChannelType channel)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
switch (channel)
|
|
{
|
|
case GIMP_RED_CHANNEL:
|
|
return gimp_babl_component_format (GIMP_RGB,
|
|
gimp_image_get_precision (image),
|
|
RED);
|
|
|
|
case GIMP_GREEN_CHANNEL:
|
|
return gimp_babl_component_format (GIMP_RGB,
|
|
gimp_image_get_precision (image),
|
|
GREEN);
|
|
|
|
case GIMP_BLUE_CHANNEL:
|
|
return gimp_babl_component_format (GIMP_RGB,
|
|
gimp_image_get_precision (image),
|
|
BLUE);
|
|
|
|
case GIMP_ALPHA_CHANNEL:
|
|
return gimp_babl_component_format (GIMP_RGB,
|
|
gimp_image_get_precision (image),
|
|
ALPHA);
|
|
|
|
case GIMP_GRAY_CHANNEL:
|
|
return gimp_babl_component_format (GIMP_GRAY,
|
|
gimp_image_get_precision (image),
|
|
GRAY);
|
|
|
|
case GIMP_INDEXED_CHANNEL:
|
|
return babl_format ("Y u8"); /* will extract grayscale, the best
|
|
* we can do here */
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_component_index (const GimpImage *image,
|
|
GimpChannelType channel)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), -1);
|
|
|
|
switch (channel)
|
|
{
|
|
case GIMP_RED_CHANNEL: return RED;
|
|
case GIMP_GREEN_CHANNEL: return GREEN;
|
|
case GIMP_BLUE_CHANNEL: return BLUE;
|
|
case GIMP_GRAY_CHANNEL: return GRAY;
|
|
case GIMP_INDEXED_CHANNEL: return INDEXED;
|
|
case GIMP_ALPHA_CHANNEL:
|
|
switch (gimp_image_get_base_type (image))
|
|
{
|
|
case GIMP_RGB: return ALPHA;
|
|
case GIMP_GRAY: return ALPHA_G;
|
|
case GIMP_INDEXED: return ALPHA_I;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_component_active (GimpImage *image,
|
|
GimpChannelType channel,
|
|
gboolean active)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gint index = -1;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
index = gimp_image_get_component_index (image, channel);
|
|
|
|
if (index != -1 && active != private->active[index])
|
|
{
|
|
GimpLayer *floating_sel = gimp_image_get_floating_selection (image);
|
|
|
|
private->active[index] = active ? TRUE : FALSE;
|
|
|
|
if (floating_sel)
|
|
gimp_drawable_update (GIMP_DRAWABLE (floating_sel),
|
|
0, 0,
|
|
gimp_item_get_width (GIMP_ITEM (floating_sel)),
|
|
gimp_item_get_height (GIMP_ITEM (floating_sel)));
|
|
|
|
/* If there is an active channel and we mess with the components,
|
|
* the active channel gets unset...
|
|
*/
|
|
gimp_image_unset_active_channel (image);
|
|
|
|
g_signal_emit (image,
|
|
gimp_image_signals[COMPONENT_ACTIVE_CHANGED], 0,
|
|
channel);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_get_component_active (const GimpImage *image,
|
|
GimpChannelType channel)
|
|
{
|
|
gint index = -1;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
index = gimp_image_get_component_index (image, channel);
|
|
|
|
if (index != -1)
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->active[index];
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
gimp_image_get_active_array (const GimpImage *image,
|
|
gboolean *components)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gint i;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (components != NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++)
|
|
components[i] = private->active[i];
|
|
}
|
|
|
|
GimpComponentMask
|
|
gimp_image_get_active_mask (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpComponentMask mask = 0;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
switch (gimp_image_get_base_type (image))
|
|
{
|
|
case GIMP_RGB:
|
|
mask |= (private->active[RED]) ? GIMP_COMPONENT_RED : 0;
|
|
mask |= (private->active[GREEN]) ? GIMP_COMPONENT_GREEN : 0;
|
|
mask |= (private->active[BLUE]) ? GIMP_COMPONENT_BLUE : 0;
|
|
mask |= (private->active[ALPHA]) ? GIMP_COMPONENT_ALPHA : 0;
|
|
break;
|
|
|
|
case GIMP_GRAY:
|
|
case GIMP_INDEXED:
|
|
mask |= (private->active[GRAY]) ? GIMP_COMPONENT_RED : 0;
|
|
mask |= (private->active[GRAY]) ? GIMP_COMPONENT_GREEN : 0;
|
|
mask |= (private->active[GRAY]) ? GIMP_COMPONENT_BLUE : 0;
|
|
mask |= (private->active[ALPHA]) ? GIMP_COMPONENT_ALPHA : 0;
|
|
break;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
void
|
|
gimp_image_set_component_visible (GimpImage *image,
|
|
GimpChannelType channel,
|
|
gboolean visible)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gint index = -1;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
index = gimp_image_get_component_index (image, channel);
|
|
|
|
if (index != -1 && visible != private->visible[index])
|
|
{
|
|
private->visible[index] = visible ? TRUE : FALSE;
|
|
|
|
g_signal_emit (image,
|
|
gimp_image_signals[COMPONENT_VISIBILITY_CHANGED], 0,
|
|
channel);
|
|
|
|
gimp_image_invalidate (image,
|
|
0, 0,
|
|
gimp_image_get_width (image),
|
|
gimp_image_get_height (image));
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_get_component_visible (const GimpImage *image,
|
|
GimpChannelType channel)
|
|
{
|
|
gint index = -1;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
index = gimp_image_get_component_index (image, channel);
|
|
|
|
if (index != -1)
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->visible[index];
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
gimp_image_get_visible_array (const GimpImage *image,
|
|
gboolean *components)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gint i;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (components != NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
for (i = 0; i < MAX_CHANNELS; i++)
|
|
components[i] = private->visible[i];
|
|
}
|
|
|
|
|
|
/* emitting image signals */
|
|
|
|
void
|
|
gimp_image_mode_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[MODE_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_precision_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[PRECISION_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_alpha_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[ALPHA_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_invalidate (GimpImage *image,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
gimp_projectable_invalidate (GIMP_PROJECTABLE (image),
|
|
x, y, width, height);
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.preview_invalidated = TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_guide_added (GimpImage *image,
|
|
GimpGuide *guide)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_GUIDE (guide));
|
|
|
|
g_signal_emit (image, gimp_image_signals[GUIDE_ADDED], 0,
|
|
guide);
|
|
}
|
|
|
|
void
|
|
gimp_image_guide_removed (GimpImage *image,
|
|
GimpGuide *guide)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_GUIDE (guide));
|
|
|
|
g_signal_emit (image, gimp_image_signals[GUIDE_REMOVED], 0,
|
|
guide);
|
|
}
|
|
|
|
void
|
|
gimp_image_guide_moved (GimpImage *image,
|
|
GimpGuide *guide)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_GUIDE (guide));
|
|
|
|
g_signal_emit (image, gimp_image_signals[GUIDE_MOVED], 0,
|
|
guide);
|
|
}
|
|
|
|
void
|
|
gimp_image_sample_point_added (GimpImage *image,
|
|
GimpSamplePoint *sample_point)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (sample_point != NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_ADDED], 0,
|
|
sample_point);
|
|
}
|
|
|
|
void
|
|
gimp_image_sample_point_removed (GimpImage *image,
|
|
GimpSamplePoint *sample_point)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (sample_point != NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_REMOVED], 0,
|
|
sample_point);
|
|
}
|
|
|
|
void
|
|
gimp_image_sample_point_moved (GimpImage *image,
|
|
GimpSamplePoint *sample_point)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (sample_point != NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[SAMPLE_POINT_MOVED], 0,
|
|
sample_point);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_size_changed_detailed:
|
|
* @image:
|
|
* @previous_origin_x:
|
|
* @previous_origin_y:
|
|
*
|
|
* Emits the size-changed-detailed signal that is typically used to adjust the
|
|
* position of the image in the display shell on various operations,
|
|
* e.g. crop.
|
|
*
|
|
* This function makes sure that GimpViewable::size-changed is also emitted.
|
|
**/
|
|
void
|
|
gimp_image_size_changed_detailed (GimpImage *image,
|
|
gint previous_origin_x,
|
|
gint previous_origin_y,
|
|
gint previous_width,
|
|
gint previous_height)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[SIZE_CHANGED_DETAILED], 0,
|
|
previous_origin_x,
|
|
previous_origin_y,
|
|
previous_width,
|
|
previous_height);
|
|
}
|
|
|
|
void
|
|
gimp_image_colormap_changed (GimpImage *image,
|
|
gint color_index)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (color_index >= -1 &&
|
|
color_index < GIMP_IMAGE_GET_PRIVATE (image)->n_colors);
|
|
|
|
g_signal_emit (image, gimp_image_signals[COLORMAP_CHANGED], 0,
|
|
color_index);
|
|
}
|
|
|
|
void
|
|
gimp_image_selection_invalidate (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[SELECTION_INVALIDATE], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_quick_mask_changed (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_signal_emit (image, gimp_image_signals[QUICK_MASK_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
gimp_image_undo_event (GimpImage *image,
|
|
GimpUndoEvent event,
|
|
GimpUndo *undo)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
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 (image, gimp_image_signals[UNDO_EVENT], 0, event, undo);
|
|
}
|
|
|
|
|
|
/* dirty counters */
|
|
|
|
/* NOTE about the image->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 100000, this is due to undo-ing back
|
|
* before a saved version, then changing 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 gimpimage-undo.c for the gory 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 *image,
|
|
GimpDirtyMask dirty_mask)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
private->dirty++;
|
|
private->export_dirty++;
|
|
|
|
if (! private->dirty_time)
|
|
private->dirty_time = time (NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[DIRTY], 0, dirty_mask);
|
|
|
|
TRC (("dirty %d -> %d\n", private->dirty - 1, private->dirty));
|
|
|
|
return private->dirty;
|
|
}
|
|
|
|
gint
|
|
gimp_image_clean (GimpImage *image,
|
|
GimpDirtyMask dirty_mask)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
private->dirty--;
|
|
private->export_dirty--;
|
|
|
|
g_signal_emit (image, gimp_image_signals[CLEAN], 0, dirty_mask);
|
|
|
|
TRC (("clean %d -> %d\n", private->dirty + 1, private->dirty));
|
|
|
|
return private->dirty;
|
|
}
|
|
|
|
void
|
|
gimp_image_clean_all (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
private->dirty = 0;
|
|
private->dirty_time = 0;
|
|
|
|
g_signal_emit (image, gimp_image_signals[CLEAN], 0, GIMP_DIRTY_ALL);
|
|
}
|
|
|
|
void
|
|
gimp_image_export_clean_all (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
private->export_dirty = 0;
|
|
|
|
g_signal_emit (image, gimp_image_signals[CLEAN], 0, GIMP_DIRTY_ALL);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_is_dirty:
|
|
* @image:
|
|
*
|
|
* Returns: True if the image is dirty, false otherwise.
|
|
**/
|
|
gint
|
|
gimp_image_is_dirty (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->dirty != 0;
|
|
}
|
|
|
|
/**
|
|
* gimp_image_is_export_dirty:
|
|
* @image:
|
|
*
|
|
* Returns: True if the image export is dirty, false otherwise.
|
|
**/
|
|
gboolean
|
|
gimp_image_is_export_dirty (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->export_dirty != 0;
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_dirty_time (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->dirty_time;
|
|
}
|
|
|
|
/**
|
|
* gimp_image_saved:
|
|
* @image:
|
|
* @uri:
|
|
*
|
|
* Emits the "saved" signal, indicating that @image was saved to the
|
|
* location specified by @uri.
|
|
*/
|
|
void
|
|
gimp_image_saved (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (uri != NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[SAVED], 0, uri);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_exported:
|
|
* @image:
|
|
* @uri:
|
|
*
|
|
* Emits the "exported" signal, indicating that @image was exported to the
|
|
* location specified by @uri.
|
|
*/
|
|
void
|
|
gimp_image_exported (GimpImage *image,
|
|
const gchar *uri)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (uri != NULL);
|
|
|
|
g_signal_emit (image, gimp_image_signals[EXPORTED], 0, uri);
|
|
}
|
|
|
|
|
|
/* flush this image's displays */
|
|
|
|
void
|
|
gimp_image_flush (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
gimp_projectable_flush (GIMP_PROJECTABLE (image),
|
|
GIMP_IMAGE_GET_PRIVATE (image)->flush_accum.preview_invalidated);
|
|
}
|
|
|
|
|
|
/* display / instance counters */
|
|
|
|
gint
|
|
gimp_image_get_display_count (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->disp_count;
|
|
}
|
|
|
|
void
|
|
gimp_image_inc_display_count (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->disp_count++;
|
|
}
|
|
|
|
void
|
|
gimp_image_dec_display_count (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->disp_count--;
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_instance_count (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->instance_count;
|
|
}
|
|
|
|
void
|
|
gimp_image_inc_instance_count (GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
GIMP_IMAGE_GET_PRIVATE (image)->instance_count++;
|
|
}
|
|
|
|
|
|
/* parasites */
|
|
|
|
const GimpParasite *
|
|
gimp_image_parasite_find (const GimpImage *image,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return gimp_parasite_list_find (GIMP_IMAGE_GET_PRIVATE (image)->parasites,
|
|
name);
|
|
}
|
|
|
|
static void
|
|
list_func (gchar *key,
|
|
GimpParasite *p,
|
|
gchar ***cur)
|
|
{
|
|
*(*cur)++ = (gchar *) g_strdup (key);
|
|
}
|
|
|
|
gchar **
|
|
gimp_image_parasite_list (const GimpImage *image,
|
|
gint *count)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gchar **list;
|
|
gchar **cur;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
*count = gimp_parasite_list_length (private->parasites);
|
|
cur = list = g_new (gchar *, *count);
|
|
|
|
gimp_parasite_list_foreach (private->parasites, (GHFunc) list_func, &cur);
|
|
|
|
return list;
|
|
}
|
|
|
|
void
|
|
gimp_image_parasite_attach (GimpImage *image,
|
|
const GimpParasite *parasite)
|
|
{
|
|
GimpParasite copy;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (parasite != NULL);
|
|
|
|
/* make a temporary copy of the GimpParasite struct because
|
|
* gimp_parasite_shift_parent() changes it
|
|
*/
|
|
copy = *parasite;
|
|
|
|
/* 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 (©))
|
|
gimp_image_undo_push_image_parasite (image,
|
|
C_("undo-type", "Attach Parasite to Image"),
|
|
©);
|
|
|
|
/* 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 (GIMP_IMAGE_GET_PRIVATE (image)->parasites, ©);
|
|
|
|
if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT))
|
|
{
|
|
gimp_parasite_shift_parent (©);
|
|
gimp_parasite_attach (image->gimp, ©);
|
|
}
|
|
|
|
g_signal_emit (image, gimp_image_signals[PARASITE_ATTACHED], 0,
|
|
parasite->name);
|
|
|
|
if (strcmp (parasite->name, "icc-profile") == 0)
|
|
gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));
|
|
}
|
|
|
|
void
|
|
gimp_image_parasite_detach (GimpImage *image,
|
|
const gchar *name)
|
|
{
|
|
GimpImagePrivate *private;
|
|
const GimpParasite *parasite;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (name != NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (! (parasite = gimp_parasite_list_find (private->parasites, name)))
|
|
return;
|
|
|
|
if (gimp_parasite_is_undoable (parasite))
|
|
gimp_image_undo_push_image_parasite_remove (image,
|
|
C_("undo-type", "Remove Parasite from Image"),
|
|
name);
|
|
|
|
gimp_parasite_list_remove (private->parasites, name);
|
|
|
|
g_signal_emit (image, gimp_image_signals[PARASITE_DETACHED], 0,
|
|
name);
|
|
|
|
if (strcmp (name, "icc-profile") == 0)
|
|
gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));
|
|
}
|
|
|
|
|
|
/* tattoos */
|
|
|
|
GimpTattoo
|
|
gimp_image_get_new_tattoo (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
private->tattoo_state++;
|
|
|
|
if (G_UNLIKELY (private->tattoo_state == 0))
|
|
g_warning ("%s: Tattoo state corrupted (integer overflow).", G_STRFUNC);
|
|
|
|
return private->tattoo_state;
|
|
}
|
|
|
|
GimpTattoo
|
|
gimp_image_get_tattoo_state (GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->tattoo_state;
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_set_tattoo_state (GimpImage *image,
|
|
GimpTattoo val)
|
|
{
|
|
GList *all_items;
|
|
GList *list;
|
|
gboolean retval = TRUE;
|
|
GimpTattoo maxval = 0;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
/* Check that the layer tattoos don't overlap with channel or vector ones */
|
|
all_items = gimp_image_get_layer_list (image);
|
|
|
|
for (list = all_items; 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 (image, ltattoo))
|
|
retval = FALSE; /* Oopps duplicated tattoo in channel */
|
|
|
|
if (gimp_image_get_vectors_by_tattoo (image, ltattoo))
|
|
retval = FALSE; /* Oopps duplicated tattoo in vectors */
|
|
}
|
|
|
|
g_list_free (all_items);
|
|
|
|
/* Now check that the channel and vectors tattoos don't overlap */
|
|
all_items = gimp_image_get_channel_list (image);
|
|
|
|
for (list = all_items; 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 (image, ctattoo))
|
|
retval = FALSE; /* Oopps duplicated tattoo in vectors */
|
|
}
|
|
|
|
g_list_free (all_items);
|
|
|
|
/* Find the max tattoo value in the vectors */
|
|
all_items = gimp_image_get_vectors_list (image);
|
|
|
|
for (list = all_items; list; list = g_list_next (list))
|
|
{
|
|
GimpTattoo vtattoo;
|
|
|
|
vtattoo = gimp_item_get_tattoo (GIMP_ITEM (list->data));
|
|
if (vtattoo > maxval)
|
|
maxval = vtattoo;
|
|
}
|
|
|
|
g_list_free (all_items);
|
|
|
|
if (val < maxval)
|
|
retval = FALSE;
|
|
|
|
/* Must check if the state is valid */
|
|
if (retval == TRUE)
|
|
GIMP_IMAGE_GET_PRIVATE (image)->tattoo_state = val;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/* projection */
|
|
|
|
GimpProjection *
|
|
gimp_image_get_projection (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->projection;
|
|
}
|
|
|
|
|
|
/* layers / channels / vectors */
|
|
|
|
GimpItemTree *
|
|
gimp_image_get_layer_tree (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->layers;
|
|
}
|
|
|
|
GimpItemTree *
|
|
gimp_image_get_channel_tree (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->channels;
|
|
}
|
|
|
|
GimpItemTree *
|
|
gimp_image_get_vectors_tree (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->vectors;
|
|
}
|
|
|
|
GimpContainer *
|
|
gimp_image_get_layers (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->layers->container;
|
|
}
|
|
|
|
GimpContainer *
|
|
gimp_image_get_channels (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->channels->container;
|
|
}
|
|
|
|
GimpContainer *
|
|
gimp_image_get_vectors (const GimpImage *image)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
return GIMP_IMAGE_GET_PRIVATE (image)->vectors->container;
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_n_layers (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_layers (image));
|
|
|
|
return gimp_item_stack_get_n_items (stack);
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_n_channels (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_channels (image));
|
|
|
|
return gimp_item_stack_get_n_items (stack);
|
|
}
|
|
|
|
gint
|
|
gimp_image_get_n_vectors (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), 0);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image));
|
|
|
|
return gimp_item_stack_get_n_items (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_layer_iter (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_layers (image));
|
|
|
|
return gimp_item_stack_get_item_iter (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_channel_iter (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_channels (image));
|
|
|
|
return gimp_item_stack_get_item_iter (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_vectors_iter (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image));
|
|
|
|
return gimp_item_stack_get_item_iter (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_layer_list (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_layers (image));
|
|
|
|
return gimp_item_stack_get_item_list (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_channel_list (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_channels (image));
|
|
|
|
return gimp_item_stack_get_item_list (stack);
|
|
}
|
|
|
|
GList *
|
|
gimp_image_get_vectors_list (const GimpImage *image)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image));
|
|
|
|
return gimp_item_stack_get_item_list (stack);
|
|
}
|
|
|
|
|
|
/* active drawable, layer, channel, vectors */
|
|
|
|
GimpDrawable *
|
|
gimp_image_get_active_drawable (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpItem *active_channel;
|
|
GimpItem *active_layer;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
active_channel = gimp_item_tree_get_active_item (private->channels);
|
|
active_layer = gimp_item_tree_get_active_item (private->layers);
|
|
|
|
/* If there is an active channel (a saved selection, etc.),
|
|
* we ignore the active layer
|
|
*/
|
|
if (active_channel)
|
|
{
|
|
return GIMP_DRAWABLE (active_channel);
|
|
}
|
|
else if (active_layer)
|
|
{
|
|
GimpLayer *layer = GIMP_LAYER (active_layer);
|
|
GimpLayerMask *mask = gimp_layer_get_mask (layer);
|
|
|
|
if (mask && gimp_layer_get_edit_mask (layer))
|
|
return GIMP_DRAWABLE (mask);
|
|
else
|
|
return GIMP_DRAWABLE (layer);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GimpLayer *
|
|
gimp_image_get_active_layer (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
return GIMP_LAYER (gimp_item_tree_get_active_item (private->layers));
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_get_active_channel (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
return GIMP_CHANNEL (gimp_item_tree_get_active_item (private->channels));
|
|
}
|
|
|
|
GimpVectors *
|
|
gimp_image_get_active_vectors (const GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
return GIMP_VECTORS (gimp_item_tree_get_active_item (private->vectors));
|
|
}
|
|
|
|
GimpLayer *
|
|
gimp_image_set_active_layer (GimpImage *image,
|
|
GimpLayer *layer)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpLayer *floating_sel;
|
|
GimpLayer *active_layer;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), NULL);
|
|
g_return_val_if_fail (layer == NULL ||
|
|
(gimp_item_is_attached (GIMP_ITEM (layer)) &&
|
|
gimp_item_get_image (GIMP_ITEM (layer)) == image),
|
|
NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
floating_sel = gimp_image_get_floating_selection (image);
|
|
|
|
/* Make sure the floating_sel always is the active layer */
|
|
if (floating_sel && layer != floating_sel)
|
|
return floating_sel;
|
|
|
|
active_layer = gimp_image_get_active_layer (image);
|
|
|
|
if (layer != active_layer)
|
|
{
|
|
/* Don't cache selection info for the previous active layer */
|
|
if (active_layer)
|
|
gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (active_layer));
|
|
|
|
gimp_item_tree_set_active_item (private->layers, GIMP_ITEM (layer));
|
|
}
|
|
|
|
return gimp_image_get_active_layer (image);
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_set_active_channel (GimpImage *image,
|
|
GimpChannel *channel)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (channel == NULL || GIMP_IS_CHANNEL (channel), NULL);
|
|
g_return_val_if_fail (channel == NULL ||
|
|
(gimp_item_is_attached (GIMP_ITEM (channel)) &&
|
|
gimp_item_get_image (GIMP_ITEM (channel)) == image),
|
|
NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* Not if there is a floating selection */
|
|
if (channel && gimp_image_get_floating_selection (image))
|
|
return NULL;
|
|
|
|
if (channel != gimp_image_get_active_channel (image))
|
|
{
|
|
gimp_item_tree_set_active_item (private->channels, GIMP_ITEM (channel));
|
|
}
|
|
|
|
return gimp_image_get_active_channel (image);
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_unset_active_channel (GimpImage *image)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpChannel *channel;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
channel = gimp_image_get_active_channel (image);
|
|
|
|
if (channel)
|
|
{
|
|
gimp_image_set_active_channel (image, NULL);
|
|
|
|
if (private->layer_stack)
|
|
gimp_image_set_active_layer (image, private->layer_stack->data);
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
GimpVectors *
|
|
gimp_image_set_active_vectors (GimpImage *image,
|
|
GimpVectors *vectors)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors), NULL);
|
|
g_return_val_if_fail (vectors == NULL ||
|
|
(gimp_item_is_attached (GIMP_ITEM (vectors)) &&
|
|
gimp_item_get_image (GIMP_ITEM (vectors)) == image),
|
|
NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (vectors != gimp_image_get_active_vectors (image))
|
|
{
|
|
gimp_item_tree_set_active_item (private->vectors, GIMP_ITEM (vectors));
|
|
}
|
|
|
|
return gimp_image_get_active_vectors (image);
|
|
}
|
|
|
|
|
|
/* layer, channel, vectors by tattoo */
|
|
|
|
GimpLayer *
|
|
gimp_image_get_layer_by_tattoo (const GimpImage *image,
|
|
GimpTattoo tattoo)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_layers (image));
|
|
|
|
return GIMP_LAYER (gimp_item_stack_get_item_by_tattoo (stack, tattoo));
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_get_channel_by_tattoo (const GimpImage *image,
|
|
GimpTattoo tattoo)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_channels (image));
|
|
|
|
return GIMP_CHANNEL (gimp_item_stack_get_item_by_tattoo (stack, tattoo));
|
|
}
|
|
|
|
GimpVectors *
|
|
gimp_image_get_vectors_by_tattoo (const GimpImage *image,
|
|
GimpTattoo tattoo)
|
|
{
|
|
GimpItemStack *stack;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
|
|
stack = GIMP_ITEM_STACK (gimp_image_get_vectors (image));
|
|
|
|
return GIMP_VECTORS (gimp_item_stack_get_item_by_tattoo (stack, tattoo));
|
|
}
|
|
|
|
|
|
/* layer, channel, vectors by name */
|
|
|
|
GimpLayer *
|
|
gimp_image_get_layer_by_name (const GimpImage *image,
|
|
const gchar *name)
|
|
{
|
|
GimpItemTree *tree;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
tree = gimp_image_get_layer_tree (image);
|
|
|
|
return GIMP_LAYER (gimp_item_tree_get_item_by_name (tree, name));
|
|
}
|
|
|
|
GimpChannel *
|
|
gimp_image_get_channel_by_name (const GimpImage *image,
|
|
const gchar *name)
|
|
{
|
|
GimpItemTree *tree;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
tree = gimp_image_get_channel_tree (image);
|
|
|
|
return GIMP_CHANNEL (gimp_item_tree_get_item_by_name (tree, name));
|
|
}
|
|
|
|
GimpVectors *
|
|
gimp_image_get_vectors_by_name (const GimpImage *image,
|
|
const gchar *name)
|
|
{
|
|
GimpItemTree *tree;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
tree = gimp_image_get_vectors_tree (image);
|
|
|
|
return GIMP_VECTORS (gimp_item_tree_get_item_by_name (tree, name));
|
|
}
|
|
|
|
|
|
/* items */
|
|
|
|
gboolean
|
|
gimp_image_reorder_item (GimpImage *image,
|
|
GimpItem *item,
|
|
GimpItem *new_parent,
|
|
gint new_index,
|
|
gboolean push_undo,
|
|
const gchar *undo_desc)
|
|
{
|
|
GimpItemTree *tree;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (gimp_item_get_image (item) == image, FALSE);
|
|
|
|
tree = gimp_item_get_tree (item);
|
|
|
|
g_return_val_if_fail (tree != NULL, FALSE);
|
|
|
|
if (push_undo && ! undo_desc)
|
|
undo_desc = GIMP_ITEM_GET_CLASS (item)->reorder_desc;
|
|
|
|
/* item and new_parent are type-checked in GimpItemTree
|
|
*/
|
|
return gimp_item_tree_reorder_item (tree, item,
|
|
new_parent, new_index,
|
|
push_undo, undo_desc);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_raise_item (GimpImage *image,
|
|
GimpItem *item,
|
|
GError **error)
|
|
{
|
|
gint index;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
index = gimp_item_get_index (item);
|
|
|
|
g_return_val_if_fail (index != -1, FALSE);
|
|
|
|
if (index == 0)
|
|
{
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
|
GIMP_ITEM_GET_CLASS (item)->raise_failed);
|
|
return FALSE;
|
|
}
|
|
|
|
return gimp_image_reorder_item (image, item,
|
|
gimp_item_get_parent (item), index - 1,
|
|
TRUE, GIMP_ITEM_GET_CLASS (item)->raise_desc);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_raise_item_to_top (GimpImage *image,
|
|
GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return gimp_image_reorder_item (image, item,
|
|
gimp_item_get_parent (item), 0,
|
|
TRUE, GIMP_ITEM_GET_CLASS (item)->raise_to_top_desc);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_lower_item (GimpImage *image,
|
|
GimpItem *item,
|
|
GError **error)
|
|
{
|
|
GimpContainer *container;
|
|
gint index;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
container = gimp_item_get_container (item);
|
|
|
|
g_return_val_if_fail (container != NULL, FALSE);
|
|
|
|
index = gimp_item_get_index (item);
|
|
|
|
if (index == gimp_container_get_n_children (container) - 1)
|
|
{
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
|
GIMP_ITEM_GET_CLASS (item)->lower_failed);
|
|
return FALSE;
|
|
}
|
|
|
|
return gimp_image_reorder_item (image, item,
|
|
gimp_item_get_parent (item), index + 1,
|
|
TRUE, GIMP_ITEM_GET_CLASS (item)->lower_desc);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_lower_item_to_bottom (GimpImage *image,
|
|
GimpItem *item)
|
|
{
|
|
GimpContainer *container;
|
|
gint length;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
container = gimp_item_get_container (item);
|
|
|
|
g_return_val_if_fail (container != NULL, FALSE);
|
|
|
|
length = gimp_container_get_n_children (container);
|
|
|
|
return gimp_image_reorder_item (image, item,
|
|
gimp_item_get_parent (item), length - 1,
|
|
TRUE, GIMP_ITEM_GET_CLASS (item)->lower_to_bottom_desc);
|
|
}
|
|
|
|
|
|
/* layers */
|
|
|
|
gboolean
|
|
gimp_image_add_layer (GimpImage *image,
|
|
GimpLayer *layer,
|
|
GimpLayer *parent,
|
|
gint position,
|
|
gboolean push_undo)
|
|
{
|
|
GimpImagePrivate *private;
|
|
gboolean old_has_alpha;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* item and parent are type-checked in GimpItemTree
|
|
*/
|
|
if (! gimp_item_tree_get_insert_pos (private->layers,
|
|
(GimpItem *) layer,
|
|
(GimpItem **) &parent,
|
|
&position))
|
|
return FALSE;
|
|
|
|
/* If there is a floating selection (and this isn't it!),
|
|
* make sure the insert position is greater than 0
|
|
*/
|
|
if (parent == NULL && position == 0 &&
|
|
gimp_image_get_floating_selection (image))
|
|
position = 1;
|
|
|
|
old_has_alpha = gimp_image_has_alpha (image);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_layer_add (image, C_("undo-type", "Add Layer"),
|
|
layer,
|
|
gimp_image_get_active_layer (image));
|
|
|
|
gimp_item_tree_add_item (private->layers, GIMP_ITEM (layer),
|
|
GIMP_ITEM (parent), position);
|
|
|
|
gimp_image_set_active_layer (image, layer);
|
|
|
|
/* If the layer is a floating selection, attach it to the drawable */
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
gimp_drawable_attach_floating_sel (gimp_layer_get_floating_sel_drawable (layer),
|
|
layer);
|
|
|
|
if (old_has_alpha != gimp_image_has_alpha (image))
|
|
private->flush_accum.alpha_changed = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_remove_layer (GimpImage *image,
|
|
GimpLayer *layer,
|
|
gboolean push_undo,
|
|
GimpLayer *new_active)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpLayer *active_layer;
|
|
gboolean old_has_alpha;
|
|
gboolean undo_group = FALSE;
|
|
const gchar *undo_desc;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_LAYER (layer));
|
|
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
|
|
g_return_if_fail (gimp_item_get_image (GIMP_ITEM (layer)) == image);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
if (gimp_drawable_get_floating_sel (GIMP_DRAWABLE (layer)))
|
|
{
|
|
if (! push_undo)
|
|
{
|
|
g_warning ("%s() was called from an undo function while the layer "
|
|
"had a floating selection. Please report this at "
|
|
"http://www.gimp.org/bugs/", G_STRFUNC);
|
|
return;
|
|
}
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE,
|
|
C_("undo-type", "Remove Layer"));
|
|
undo_group = TRUE;
|
|
|
|
gimp_image_remove_layer (image,
|
|
gimp_drawable_get_floating_sel (GIMP_DRAWABLE (layer)),
|
|
TRUE, NULL);
|
|
}
|
|
|
|
active_layer = gimp_image_get_active_layer (image);
|
|
|
|
old_has_alpha = gimp_image_has_alpha (image);
|
|
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
{
|
|
undo_desc = C_("undo-type", "Remove Floating Selection");
|
|
|
|
gimp_drawable_detach_floating_sel (gimp_layer_get_floating_sel_drawable (layer));
|
|
}
|
|
else
|
|
{
|
|
undo_desc = C_("undo-type", "Remove Layer");
|
|
}
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_layer_remove (image, undo_desc, layer,
|
|
gimp_layer_get_parent (layer),
|
|
gimp_item_get_index (GIMP_ITEM (layer)),
|
|
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));
|
|
|
|
private->layer_stack = g_slist_remove (private->layer_stack, layer);
|
|
|
|
/* Also remove all children of a group layer from the layer_stack */
|
|
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
|
|
{
|
|
GimpContainer *stack = gimp_viewable_get_children (GIMP_VIEWABLE (layer));
|
|
GList *children;
|
|
GList *list;
|
|
|
|
children = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (stack));
|
|
|
|
for (list = children; list; list = g_list_next (list))
|
|
{
|
|
private->layer_stack = g_slist_remove (private->layer_stack,
|
|
list->data);
|
|
}
|
|
|
|
g_list_free (children);
|
|
}
|
|
|
|
if (! new_active && private->layer_stack)
|
|
new_active = private->layer_stack->data;
|
|
|
|
new_active =
|
|
GIMP_LAYER (gimp_item_tree_remove_item (private->layers,
|
|
GIMP_ITEM (layer),
|
|
GIMP_ITEM (new_active)));
|
|
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
{
|
|
/* If this was the floating selection, activate the underlying drawable
|
|
*/
|
|
floating_sel_activate_drawable (layer);
|
|
}
|
|
else if (active_layer &&
|
|
(layer == active_layer ||
|
|
gimp_viewable_is_ancestor (GIMP_VIEWABLE (layer),
|
|
GIMP_VIEWABLE (active_layer))))
|
|
{
|
|
gimp_image_set_active_layer (image, new_active);
|
|
}
|
|
|
|
g_object_unref (layer);
|
|
|
|
if (old_has_alpha != gimp_image_has_alpha (image))
|
|
private->flush_accum.alpha_changed = TRUE;
|
|
|
|
if (undo_group)
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
void
|
|
gimp_image_add_layers (GimpImage *image,
|
|
GList *layers,
|
|
GimpLayer *parent,
|
|
gint position,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height,
|
|
const gchar *undo_desc)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GList *list;
|
|
gint layers_x = G_MAXINT;
|
|
gint layers_y = G_MAXINT;
|
|
gint layers_width = 0;
|
|
gint layers_height = 0;
|
|
gint offset_x;
|
|
gint offset_y;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (layers != NULL);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* item and parent are type-checked in GimpItemTree
|
|
*/
|
|
if (! gimp_item_tree_get_insert_pos (private->layers,
|
|
(GimpItem *) layers->data,
|
|
(GimpItem **) &parent,
|
|
&position))
|
|
return;
|
|
|
|
for (list = layers; list; list = g_list_next (list))
|
|
{
|
|
GimpItem *item = GIMP_ITEM (list->data);
|
|
gint off_x, off_y;
|
|
|
|
gimp_item_get_offset (item, &off_x, &off_y);
|
|
|
|
layers_x = MIN (layers_x, off_x);
|
|
layers_y = MIN (layers_y, off_y);
|
|
|
|
layers_width = MAX (layers_width,
|
|
off_x + gimp_item_get_width (item) - layers_x);
|
|
layers_height = MAX (layers_height,
|
|
off_y + gimp_item_get_height (item) - layers_y);
|
|
}
|
|
|
|
offset_x = x + (width - layers_width) / 2 - layers_x;
|
|
offset_y = y + (height - layers_height) / 2 - layers_y;
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, undo_desc);
|
|
|
|
for (list = layers; list; list = g_list_next (list))
|
|
{
|
|
GimpItem *new_item = GIMP_ITEM (list->data);
|
|
|
|
gimp_item_translate (new_item, offset_x, offset_y, FALSE);
|
|
|
|
gimp_image_add_layer (image, GIMP_LAYER (new_item),
|
|
parent, position, TRUE);
|
|
position++;
|
|
}
|
|
|
|
if (layers)
|
|
gimp_image_set_active_layer (image, layers->data);
|
|
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
|
|
/* channels */
|
|
|
|
gboolean
|
|
gimp_image_add_channel (GimpImage *image,
|
|
GimpChannel *channel,
|
|
GimpChannel *parent,
|
|
gint position,
|
|
gboolean push_undo)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* item and parent are type-checked in GimpItemTree
|
|
*/
|
|
if (! gimp_item_tree_get_insert_pos (private->channels,
|
|
(GimpItem *) channel,
|
|
(GimpItem **) &parent,
|
|
&position))
|
|
return FALSE;
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_channel_add (image, C_("undo-type", "Add Channel"),
|
|
channel,
|
|
gimp_image_get_active_channel (image));
|
|
|
|
gimp_item_tree_add_item (private->channels, GIMP_ITEM (channel),
|
|
GIMP_ITEM (parent), position);
|
|
|
|
gimp_image_set_active_channel (image, channel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_remove_channel (GimpImage *image,
|
|
GimpChannel *channel,
|
|
gboolean push_undo,
|
|
GimpChannel *new_active)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpChannel *active_channel;
|
|
gboolean undo_group = FALSE;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_CHANNEL (channel));
|
|
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
|
|
g_return_if_fail (gimp_item_get_image (GIMP_ITEM (channel)) == image);
|
|
|
|
if (gimp_drawable_get_floating_sel (GIMP_DRAWABLE (channel)))
|
|
{
|
|
if (! push_undo)
|
|
{
|
|
g_warning ("%s() was called from an undo function while the channel "
|
|
"had a floating selection. Please report this at "
|
|
"http://www.gimp.org/bugs/", G_STRFUNC);
|
|
return;
|
|
}
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE,
|
|
C_("undo-type", "Remove Channel"));
|
|
undo_group = TRUE;
|
|
|
|
gimp_image_remove_layer (image,
|
|
gimp_drawable_get_floating_sel (GIMP_DRAWABLE (channel)),
|
|
TRUE, NULL);
|
|
}
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
active_channel = gimp_image_get_active_channel (image);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_channel_remove (image, C_("undo-type", "Remove Channel"), channel,
|
|
gimp_channel_get_parent (channel),
|
|
gimp_item_get_index (GIMP_ITEM (channel)),
|
|
active_channel);
|
|
|
|
g_object_ref (channel);
|
|
|
|
new_active =
|
|
GIMP_CHANNEL (gimp_item_tree_remove_item (private->channels,
|
|
GIMP_ITEM (channel),
|
|
GIMP_ITEM (new_active)));
|
|
|
|
if (active_channel &&
|
|
(channel == active_channel ||
|
|
gimp_viewable_is_ancestor (GIMP_VIEWABLE (channel),
|
|
GIMP_VIEWABLE (active_channel))))
|
|
{
|
|
if (new_active)
|
|
gimp_image_set_active_channel (image, new_active);
|
|
else
|
|
gimp_image_unset_active_channel (image);
|
|
}
|
|
|
|
g_object_unref (channel);
|
|
|
|
if (undo_group)
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
|
|
/* vectors */
|
|
|
|
gboolean
|
|
gimp_image_add_vectors (GimpImage *image,
|
|
GimpVectors *vectors,
|
|
GimpVectors *parent,
|
|
gint position,
|
|
gboolean push_undo)
|
|
{
|
|
GimpImagePrivate *private;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
/* item and parent are type-checked in GimpItemTree
|
|
*/
|
|
if (! gimp_item_tree_get_insert_pos (private->vectors,
|
|
(GimpItem *) vectors,
|
|
(GimpItem **) &parent,
|
|
&position))
|
|
return FALSE;
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_vectors_add (image, C_("undo-type", "Add Path"),
|
|
vectors,
|
|
gimp_image_get_active_vectors (image));
|
|
|
|
gimp_item_tree_add_item (private->vectors, GIMP_ITEM (vectors),
|
|
GIMP_ITEM (parent), position);
|
|
|
|
gimp_image_set_active_vectors (image, vectors);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gimp_image_remove_vectors (GimpImage *image,
|
|
GimpVectors *vectors,
|
|
gboolean push_undo,
|
|
GimpVectors *new_active)
|
|
{
|
|
GimpImagePrivate *private;
|
|
GimpVectors *active_vectors;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
g_return_if_fail (GIMP_IS_VECTORS (vectors));
|
|
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)));
|
|
g_return_if_fail (gimp_item_get_image (GIMP_ITEM (vectors)) == image);
|
|
|
|
private = GIMP_IMAGE_GET_PRIVATE (image);
|
|
|
|
active_vectors = gimp_image_get_active_vectors (image);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_push_vectors_remove (image, C_("undo-type", "Remove Path"), vectors,
|
|
gimp_vectors_get_parent (vectors),
|
|
gimp_item_get_index (GIMP_ITEM (vectors)),
|
|
active_vectors);
|
|
|
|
g_object_ref (vectors);
|
|
|
|
new_active =
|
|
GIMP_VECTORS (gimp_item_tree_remove_item (private->vectors,
|
|
GIMP_ITEM (vectors),
|
|
GIMP_ITEM (new_active)));
|
|
|
|
if (active_vectors &&
|
|
(vectors == active_vectors ||
|
|
gimp_viewable_is_ancestor (GIMP_VIEWABLE (vectors),
|
|
GIMP_VIEWABLE (active_vectors))))
|
|
{
|
|
gimp_image_set_active_vectors (image, new_active);
|
|
}
|
|
|
|
g_object_unref (vectors);
|
|
}
|
|
|
|
gboolean
|
|
gimp_image_coords_in_active_pickable (GimpImage *image,
|
|
const GimpCoords *coords,
|
|
gboolean sample_merged,
|
|
gboolean selected_only)
|
|
{
|
|
gint x, y;
|
|
gboolean in_pickable = FALSE;
|
|
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
|
|
|
x = floor (coords->x);
|
|
y = floor (coords->y);
|
|
|
|
if (sample_merged)
|
|
{
|
|
if (x >= 0 && x < gimp_image_get_width (image) &&
|
|
y >= 0 && y < gimp_image_get_height (image))
|
|
in_pickable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
GimpDrawable *drawable = gimp_image_get_active_drawable (image);
|
|
|
|
if (drawable)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (drawable);
|
|
gint off_x, off_y;
|
|
gint d_x, d_y;
|
|
|
|
gimp_item_get_offset (item, &off_x, &off_y);
|
|
|
|
d_x = x - off_x;
|
|
d_y = y - off_y;
|
|
|
|
if (d_x >= 0 && d_x < gimp_item_get_width (item) &&
|
|
d_y >= 0 && d_y < gimp_item_get_height (item))
|
|
in_pickable = TRUE;
|
|
}
|
|
}
|
|
|
|
if (in_pickable && selected_only)
|
|
{
|
|
GimpChannel *selection = gimp_image_get_mask (image);
|
|
|
|
if (! gimp_channel_is_empty (selection) &&
|
|
! gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection),
|
|
x, y))
|
|
{
|
|
in_pickable = FALSE;
|
|
}
|
|
}
|
|
|
|
return in_pickable;
|
|
}
|
|
|
|
void
|
|
gimp_image_invalidate_previews (GimpImage *image)
|
|
{
|
|
GimpItemStack *layers;
|
|
GimpItemStack *channels;
|
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
layers = GIMP_ITEM_STACK (gimp_image_get_layers (image));
|
|
channels = GIMP_ITEM_STACK (gimp_image_get_channels (image));
|
|
|
|
gimp_item_stack_invalidate_previews (layers);
|
|
gimp_item_stack_invalidate_previews (channels);
|
|
}
|