/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "gimpdrawableP.h" #include "gimpsignal.h" #include "gimage.h" #include "gimage_mask.h" #include "gimpparasite.h" #include "parasitelist.h" #include "undo.h" #include "libgimp/parasite.h" #include "libgimp/gimpintl.h" enum { INVALIDATE_PREVIEW, LAST_SIGNAL }; static void gimp_drawable_class_init (GimpDrawableClass *klass); static void gimp_drawable_init (GimpDrawable *drawable); static void gimp_drawable_destroy (GtkObject *object); static guint gimp_drawable_signals[LAST_SIGNAL] = { 0 }; static GimpDrawableClass *parent_class = NULL; GtkType gimp_drawable_get_type (void) { static GtkType type; GIMP_TYPE_INIT(type, GimpDrawable, GimpDrawableClass, gimp_drawable_init, gimp_drawable_class_init, GIMP_TYPE_OBJECT); return type; } static void gimp_drawable_class_init (GimpDrawableClass *class) { GtkObjectClass *object_class; GtkType type = GIMP_TYPE_DRAWABLE; object_class = GTK_OBJECT_CLASS(class); parent_class = gtk_type_class (GIMP_TYPE_OBJECT); gimp_drawable_signals[INVALIDATE_PREVIEW] = gimp_signal_new ("invalidate_pr", GTK_RUN_LAST, type, GTK_SIGNAL_OFFSET(GimpDrawableClass, invalidate_preview), gimp_sigtype_void); gtk_object_class_add_signals (object_class, gimp_drawable_signals, LAST_SIGNAL); object_class->destroy = gimp_drawable_destroy; } /* * Static variables */ static gint global_drawable_ID = 1; static GHashTable *gimp_drawable_table = NULL; /**************************/ /* Function definitions */ GimpDrawable* gimp_drawable_get_ID (gint drawable_id) { if (gimp_drawable_table == NULL) return NULL; return (GimpDrawable*) g_hash_table_lookup (gimp_drawable_table, (gpointer) drawable_id); } void gimp_drawable_merge_shadow (GimpDrawable *drawable, gint undo) { GImage *gimage; PixelRegion shadowPR; int x1, y1, x2, y2; if (! drawable) return; if (! (gimage = gimp_drawable_gimage (drawable))) return; if (! gimage->shadow) return; /* A useful optimization here is to limit the update to the * extents of the selection mask, as it cannot extend beyond * them. */ gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2); pixel_region_init (&shadowPR, gimage->shadow, x1, y1, (x2 - x1), (y2 - y1), FALSE); gimage_apply_image (gimage, drawable, &shadowPR, undo, OPAQUE_OPACITY, REPLACE_MODE, NULL, x1, y1); } void gimp_drawable_fill (GimpDrawable *drawable, guchar r, guchar g, guchar b, guchar a) { GImage *gimage; PixelRegion destPR; guchar c[MAX_CHANNELS]; guchar i; g_assert (GIMP_IS_DRAWABLE (drawable)); gimage = gimp_drawable_gimage (drawable); g_assert (gimage); switch (gimp_drawable_type (drawable)) { case RGB_GIMAGE: case RGBA_GIMAGE: c[RED_PIX] = r; c[GREEN_PIX] = g; c[BLUE_PIX] = b; if (gimp_drawable_type (drawable) == RGBA_GIMAGE) c[ALPHA_PIX] = a; break; case GRAY_GIMAGE: case GRAYA_GIMAGE: c[GRAY_PIX] = r; if (gimp_drawable_type (drawable) == GRAYA_GIMAGE) c[ALPHA_G_PIX] = a; break; case INDEXED_GIMAGE: case INDEXEDA_GIMAGE: c[RED_PIX] = r; c[GREEN_PIX] = g; c[BLUE_PIX] = b; gimage_transform_color (gimage, drawable, c, &i, RGB); c[INDEXED_PIX] = i; if (gimp_drawable_type (drawable) == INDEXEDA_GIMAGE) c[ALPHA_I_PIX] = a; break; default: g_message (_("Can't fill unknown image type.")); break; } pixel_region_init (&destPR, gimp_drawable_data (drawable), 0, 0, gimp_drawable_width (drawable), gimp_drawable_height (drawable), TRUE); color_region (&destPR, c); } gboolean gimp_drawable_mask_bounds (GimpDrawable *drawable, gint *x1, gint *y1, gint *x2, gint *y2) { GImage *gimage; gint off_x, off_y; if (! drawable) return FALSE; if (! (gimage = gimp_drawable_gimage (drawable))) return FALSE; if (gimage_mask_bounds (gimage, x1, y1, x2, y2)) { gimp_drawable_offsets (drawable, &off_x, &off_y); *x1 = CLAMP (*x1 - off_x, 0, gimp_drawable_width (drawable)); *y1 = CLAMP (*y1 - off_y, 0, gimp_drawable_height (drawable)); *x2 = CLAMP (*x2 - off_x, 0, gimp_drawable_width (drawable)); *y2 = CLAMP (*y2 - off_y, 0, gimp_drawable_height (drawable)); return TRUE; } else { *x2 = gimp_drawable_width (drawable); *y2 = gimp_drawable_height (drawable); return FALSE; } } void gimp_drawable_invalidate_preview (GimpDrawable *drawable) { GImage *gimage; if (! drawable) return; drawable->preview_valid = FALSE; #if 0 gtk_signal_emit (GTK_OBJECT (drawable), gimp_drawable_signals[INVALIDATE_PREVIEW]); #endif gimage = gimp_drawable_gimage (drawable); if (gimage) { gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[2] = FALSE; } } GimpImage * gimp_drawable_gimage (GimpDrawable *drawable) { g_assert (GIMP_IS_DRAWABLE (drawable)); return drawable->gimage; } void gimp_drawable_set_gimage (GimpDrawable *drawable, GimpImage *gimage) { g_assert (GIMP_IS_DRAWABLE (drawable)); if (gimage == NULL) drawable->tattoo = 0; else if (drawable->tattoo == 0 || drawable->gimage != gimage ) drawable->tattoo = gimp_image_get_new_tattoo (gimage); drawable->gimage = gimage; } gboolean gimp_drawable_has_alpha (GimpDrawable *drawable) { if (drawable) return drawable->has_alpha; else return FALSE; } GimpImageType gimp_drawable_type (GimpDrawable *drawable) { if (drawable) return drawable->type; else return -1; } GimpImageType gimp_drawable_type_with_alpha (GimpDrawable *drawable) { GimpImageType type = gimp_drawable_type (drawable); gboolean has_alpha = gimp_drawable_has_alpha (drawable); if (has_alpha) return type; else switch (type) { case RGB_GIMAGE: return RGBA_GIMAGE; break; case GRAY_GIMAGE: return GRAYA_GIMAGE; break; case INDEXED_GIMAGE: return INDEXEDA_GIMAGE; break; default: g_assert_not_reached (); break; } return 0; } gboolean gimp_drawable_visible (GimpDrawable *drawable) { return drawable->visible; } gchar * gimp_drawable_get_name (GimpDrawable *drawable) { return drawable->name; } void gimp_drawable_set_name (GimpDrawable *drawable, gchar *name) { GSList *list, *listb, *base_list; GimpDrawable *drawableb; gint number = 1; gchar *newname; gchar *ext; gchar numberbuf[20]; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (name != NULL); if (drawable->name) { g_free (drawable->name); drawable->name = NULL; } if (drawable->gimage == 0 || drawable->gimage->layers == 0) { /* no other layers to check name against */ drawable->name = g_strdup (name); return; } if (GIMP_IS_LAYER (drawable)) base_list = drawable->gimage->layers; else if (GIMP_IS_CHANNEL (drawable)) base_list = drawable->gimage->channels; else base_list = NULL; list = base_list; while (list) { drawableb = GIMP_DRAWABLE (list->data); if (drawable != drawableb && strcmp (name, gimp_drawable_get_name (drawableb)) == 0) { /* names conflict */ newname = g_malloc (strlen (name) + 10); /* if this aint enough yer screwed */ strcpy (newname, name); if ((ext = strrchr (newname, '#'))) { number = atoi(ext+1); /* Check if there really was the number we think after the # */ sprintf (numberbuf, "#%d", number); if (strcmp (ext, numberbuf) != 0) { /* No, so just ignore the # */ number = 1; ext = &newname[strlen (newname)]; } } else { number = 1; ext = &newname[strlen (newname)]; } sprintf (ext, "#%d", number+1); listb = base_list; while (listb) /* make sure the new name is unique */ { drawableb = GIMP_DRAWABLE (listb->data); if (drawable != drawableb && strcmp (newname, gimp_drawable_get_name(drawableb)) == 0) { number++; sprintf (ext, "#%d", number+1); /* Rescan from beginning */ listb = base_list; continue; } listb = listb->next; } drawable->name = g_strdup (newname); g_free (newname); return; } list = list->next; } drawable->name = g_strdup (name); } guchar * gimp_drawable_get_color_at (GimpDrawable *drawable, gint x, gint y) { Tile *tile; guchar *src; guchar *dest; if (!drawable || (!gimp_drawable_gimage (drawable) && gimp_drawable_is_indexed (drawable)) || x < 0 || y < 0 || x >= drawable->width || y >= drawable->height) { return NULL; } dest = g_new (guchar, 5); tile = tile_manager_get_tile (gimp_drawable_data (drawable), x, y, TRUE, FALSE); src = tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT); gimp_image_get_color (gimp_drawable_gimage (drawable), gimp_drawable_type (drawable), dest, src); if(TYPE_HAS_ALPHA(gimp_drawable_type (drawable))) dest[3] = src[gimp_drawable_bytes (drawable) - 1]; else dest[3] = 255; if (gimp_drawable_is_indexed (drawable)) dest[4] = src[0]; else dest[4] = 0; tile_release (tile, FALSE); return dest; } Parasite * gimp_drawable_parasite_find (const GimpDrawable *drawable, const gchar *name) { return parasite_list_find (drawable->parasites, name); } static void list_func (gchar *key, Parasite *p, gchar ***cur) { *(*cur)++ = (gchar *) g_strdup (key); } gchar ** gimp_drawable_parasite_list (GimpDrawable *drawable, gint *count) { gchar **list, **cur; *count = parasite_list_length (drawable->parasites); cur = list = (gchar **) g_malloc (sizeof (gchar *) * *count); parasite_list_foreach (drawable->parasites, (GHFunc) list_func, &cur); return list; } void gimp_drawable_parasite_attach (GimpDrawable *drawable, Parasite *parasite) { /* only set the dirty bit manually if we can be saved and the new parasite differs from the current one and we arn't undoable */ if (parasite_is_undoable (parasite)) { /* do a group in case we have attach_parent set */ undo_push_group_start (drawable->gimage, PARASITE_ATTACH_UNDO); undo_push_drawable_parasite (drawable->gimage, drawable, parasite); } else if (parasite_is_persistent(parasite) && !parasite_compare( parasite, gimp_drawable_parasite_find (drawable, parasite_name (parasite)))) undo_push_cantundo (drawable->gimage, _("parasite attach to drawable")); parasite_list_add (drawable->parasites, parasite); if (parasite_has_flag (parasite, PARASITE_ATTACH_PARENT)) { parasite_shift_parent (parasite); gimp_image_parasite_attach (drawable->gimage, parasite); } else if (parasite_has_flag (parasite, PARASITE_ATTACH_GRANDPARENT)) { parasite_shift_parent (parasite); parasite_shift_parent (parasite); gimp_parasite_attach (parasite); } if (parasite_is_undoable (parasite)) { undo_push_group_end (drawable->gimage); } } void gimp_drawable_parasite_detach (GimpDrawable *drawable, const gchar *parasite) { Parasite *p; if (!(p = parasite_list_find (drawable->parasites, parasite))) return; if (parasite_is_undoable (p)) undo_push_drawable_parasite_remove (drawable->gimage, drawable, parasite_name (p)); else if (parasite_is_persistent (p)) undo_push_cantundo (drawable->gimage, _("detach parasite from drawable")); parasite_list_remove (drawable->parasites, parasite); } Tattoo gimp_drawable_get_tattoo (const GimpDrawable *drawable) { return drawable->tattoo; } gboolean gimp_drawable_is_rgb (GimpDrawable *drawable) { if (gimp_drawable_type (drawable) == RGBA_GIMAGE || gimp_drawable_type (drawable) == RGB_GIMAGE) return TRUE; else return FALSE; } gboolean gimp_drawable_is_gray (GimpDrawable *drawable) { if (gimp_drawable_type (drawable) == GRAYA_GIMAGE || gimp_drawable_type (drawable) == GRAY_GIMAGE) return TRUE; else return FALSE; } gboolean gimp_drawable_is_indexed (GimpDrawable *drawable) { if (gimp_drawable_type (drawable) == INDEXEDA_GIMAGE || gimp_drawable_type (drawable) == INDEXED_GIMAGE) return TRUE; else return FALSE; } TileManager * gimp_drawable_data (GimpDrawable *drawable) { if (drawable) return drawable->tiles; else return NULL; } TileManager * gimp_drawable_shadow (GimpDrawable *drawable) { GImage *gimage; if (! (gimage = gimp_drawable_gimage (drawable))) return NULL; if (drawable) return gimage_shadow (gimage, drawable->width, drawable->height, drawable->bytes); else return NULL; } int gimp_drawable_bytes (GimpDrawable *drawable) { if (drawable) return drawable->bytes; else return -1; } gint gimp_drawable_width (GimpDrawable *drawable) { if (drawable) return drawable->width; else return -1; } gint gimp_drawable_height (GimpDrawable *drawable) { if (drawable) return drawable->height; else return -1; } void gimp_drawable_offsets (GimpDrawable *drawable, gint *off_x, gint *off_y) { if (drawable) { *off_x = drawable->offset_x; *off_y = drawable->offset_y; } else *off_x = *off_y = 0; } guchar * gimp_drawable_cmap (GimpDrawable *drawable) { GImage *gimage; if ((gimage = gimp_drawable_gimage (drawable))) return gimage->cmap; else return NULL; } void gimp_drawable_deallocate (GimpDrawable *drawable) { if (drawable->tiles) tile_manager_destroy (drawable->tiles); } static void gimp_drawable_init (GimpDrawable *drawable) { drawable->name = NULL; drawable->tiles = NULL; drawable->visible = FALSE; drawable->width = 0; drawable->height = 0; drawable->offset_x = 0; drawable->offset_y = 0; drawable->bytes = 0; drawable->gimage = NULL; drawable->type = -1; drawable->has_alpha = FALSE; drawable->preview_cache = NULL; drawable->preview_valid = FALSE; drawable->parasites = parasite_list_new (); drawable->tattoo = 0; drawable->ID = global_drawable_ID++; if (gimp_drawable_table == NULL) gimp_drawable_table = g_hash_table_new (g_direct_hash, NULL); g_hash_table_insert (gimp_drawable_table, (gpointer) drawable->ID, (gpointer) drawable); } static void gimp_drawable_destroy (GtkObject *object) { GimpDrawable *drawable; g_return_if_fail (object != NULL); g_return_if_fail (GIMP_IS_DRAWABLE (object)); drawable = GIMP_DRAWABLE (object); g_hash_table_remove (gimp_drawable_table, (gpointer) drawable->ID); if (drawable->name) g_free (drawable->name); if (drawable->tiles) tile_manager_destroy (drawable->tiles); if (drawable->preview_cache) gimp_preview_cache_invalidate (&drawable->preview_cache); if (drawable->parasites) gtk_object_unref (GTK_OBJECT (drawable->parasites)); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } void gimp_drawable_configure (GimpDrawable *drawable, GimpImage *gimage, gint width, gint height, GimpImageType type, gchar *name) { gint bpp; gboolean alpha; if (!name) name = _("unnamed"); switch (type) { case RGB_GIMAGE: bpp = 3; alpha = FALSE; break; case GRAY_GIMAGE: bpp = 1; alpha = FALSE; break; case RGBA_GIMAGE: bpp = 4; alpha = TRUE; break; case GRAYA_GIMAGE: bpp = 2; alpha = TRUE; break; case INDEXED_GIMAGE: bpp = 1; alpha = FALSE; break; case INDEXEDA_GIMAGE: bpp = 2; alpha = TRUE; break; default: g_message (_("Layer type %d not supported."), type); return; } drawable->name = NULL; drawable->width = width; drawable->height = height; drawable->bytes = bpp; drawable->type = type; drawable->has_alpha = alpha; drawable->offset_x = 0; drawable->offset_y = 0; if (drawable->tiles) tile_manager_destroy (drawable->tiles); drawable->tiles = tile_manager_new (width, height, bpp); drawable->visible = TRUE; if (gimage) gimp_drawable_set_gimage (drawable, gimage); gimp_drawable_set_name (drawable, name); /* preview variables */ drawable->preview_cache = NULL; drawable->preview_valid = FALSE; }