From 420d17d286451b1f2f87761491bfcc9bdff95fcf Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Tue, 2 Sep 2003 23:07:40 +0000 Subject: [PATCH] made all functions which push an undo step virtual and added them all as 2003-09-03 Michael Natterer * app/core/gimpchannel.[ch]: made all functions which push an undo step virtual and added them all as default implementations. * app/core/Makefile.am * app/core/core-types.h * app/core/gimpselection.[ch]: new object which is a GimpChannel subclass and implements all of its virtual functions, pushes an image_mask undo and chains up with "push_undo = FALSE". * app/core/gimpimage-mask.[ch]: made most functions simple wrappers like gimp_channel_invert(gimp_image_get_mask(gimage)); so the API stays the same for now. * app/core/gimpimage.[ch]: create a GimpSelection object as gimage->selection_mask. Removed "gboolean mask_stroking" since it is in GimpSelection now. * app/xcf/xcf-load.c (xcf_load_channel_props): added an evil hack which turns a GimpChannel into a GimpSelection once we figured the loaded channel is the selection. * app/core/gimplayer.c (gimp_layer_create_mask): gimp_channel_clear() takes an additional "const gchar *undo_desc" parameter now. * app/core/gimpscanconvert.c (gimp_scan_convert_to_channel): set mask->bounds_known to FALSE before returning the new channel * app/tools/gimpiscissorstool.c (iscissors_convert): no need to call gimp_channel_invalidate_boundary() on the channel returned by the above function. * app/core/gimpchannel.[ch]: removed gimp_channel_invalidate_boundary() since it is no longer needed. --- ChangeLog | 37 + app/core/Makefile.am | 2 + app/core/core-types.h | 1 + app/core/gimpchannel-combine.c | 1292 ++++++++++++++++++-------------- app/core/gimpchannel-combine.h | 254 ++++--- app/core/gimpchannel.c | 1292 ++++++++++++++++++-------------- app/core/gimpchannel.h | 254 ++++--- app/core/gimpimage-mask.c | 461 ++++-------- app/core/gimpimage-mask.h | 82 +- app/core/gimpimage.c | 8 +- app/core/gimpimage.h | 1 - app/core/gimplayer.c | 4 +- app/core/gimpscanconvert.c | 2 + app/core/gimpselection.c | 586 +++++++++++++++ app/core/gimpselection.h | 59 ++ app/tools/gimpiscissorstool.c | 2 - app/xcf/xcf-load.c | 126 ++-- 17 files changed, 2708 insertions(+), 1755 deletions(-) create mode 100644 app/core/gimpselection.c create mode 100644 app/core/gimpselection.h diff --git a/ChangeLog b/ChangeLog index c176c8c0d0..9aa92155ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +2003-09-03 Michael Natterer + + * app/core/gimpchannel.[ch]: made all functions which push an + undo step virtual and added them all as default implementations. + + * app/core/Makefile.am + * app/core/core-types.h + * app/core/gimpselection.[ch]: new object which is a GimpChannel + subclass and implements all of its virtual functions, pushes + an image_mask undo and chains up with "push_undo = FALSE". + + * app/core/gimpimage-mask.[ch]: made most functions simple + wrappers like gimp_channel_invert(gimp_image_get_mask(gimage)); + so the API stays the same for now. + + * app/core/gimpimage.[ch]: create a GimpSelection object + as gimage->selection_mask. Removed "gboolean mask_stroking" + since it is in GimpSelection now. + + * app/xcf/xcf-load.c (xcf_load_channel_props): added an evil hack + which turns a GimpChannel into a GimpSelection once we figured the + loaded channel is the selection. + + * app/core/gimplayer.c (gimp_layer_create_mask): + gimp_channel_clear() takes an additional "const gchar *undo_desc" + parameter now. + + * app/core/gimpscanconvert.c (gimp_scan_convert_to_channel): set + mask->bounds_known to FALSE before returning the new channel + + * app/tools/gimpiscissorstool.c (iscissors_convert): no need to + call gimp_channel_invalidate_boundary() on the channel returned by + the above function. + + * app/core/gimpchannel.[ch]: removed + gimp_channel_invalidate_boundary() since it is no longer needed. + 2003-09-03 Sven Neumann * libgimpcolor/gimpcolorspace.[ch] (gimp_rgb_to_cmyk_int): made diff --git a/app/core/Makefile.am b/app/core/Makefile.am index b45e1e3668..6e285eeb96 100644 --- a/app/core/Makefile.am +++ b/app/core/Makefile.am @@ -172,6 +172,8 @@ libappcore_a_sources = \ gimppreviewcache.h \ gimpscanconvert.c \ gimpscanconvert.h \ + gimpselection.c \ + gimpselection.h \ gimptemplate.c \ gimptemplate.h \ gimptoolinfo.c \ diff --git a/app/core/core-types.h b/app/core/core-types.h index fb40a9abff..62d5afc467 100644 --- a/app/core/core-types.h +++ b/app/core/core-types.h @@ -80,6 +80,7 @@ typedef struct _GimpGrid GimpGrid; typedef struct _GimpDrawable GimpDrawable; typedef struct _GimpChannel GimpChannel; +typedef struct _GimpSelection GimpSelection; typedef struct _GimpLayer GimpLayer; typedef struct _GimpLayerMask GimpLayerMask; diff --git a/app/core/gimpchannel-combine.c b/app/core/gimpchannel-combine.c index f470051294..50d40f583e 100644 --- a/app/core/gimpchannel-combine.c +++ b/app/core/gimpchannel-combine.c @@ -53,56 +53,101 @@ #include "gimp-intl.h" -static void gimp_channel_class_init (GimpChannelClass *klass); -static void gimp_channel_init (GimpChannel *channel); +static void gimp_channel_class_init (GimpChannelClass *klass); +static void gimp_channel_init (GimpChannel *channel); -static void gimp_channel_finalize (GObject *object); +static void gimp_channel_finalize (GObject *object); -static gsize gimp_channel_get_memsize (GimpObject *object, - gsize *gui_size); +static gsize gimp_channel_get_memsize (GimpObject *object, + gsize *gui_size); -static GimpItem * gimp_channel_duplicate (GimpItem *item, - GType new_type, - gboolean add_alpha); -static void gimp_channel_translate (GimpItem *item, - gint off_x, - gint off_y, - gboolean push_undo); -static void gimp_channel_scale (GimpItem *item, - gint new_width, - gint new_height, - gint new_offset_x, - gint new_offset_y, - GimpInterpolationType interp_type); -static void gimp_channel_resize (GimpItem *item, - gint new_width, - gint new_height, - gint offx, - gint offy); -static void gimp_channel_flip (GimpItem *item, - GimpOrientationType flip_type, - gdouble axis, - gboolean flip_result); -static void gimp_channel_rotate (GimpItem *item, - GimpRotationType flip_type, - gdouble center_x, - gdouble center_y, - gboolean flip_result); -static void gimp_channel_transform (GimpItem *item, - const GimpMatrix3 *matrix, - GimpTransformDirection direction, - GimpInterpolationType interpolation_type, - gboolean clip_result, - GimpProgressFunc progress_callback, - gpointer progress_data); -static gboolean gimp_channel_stroke (GimpItem *item, - GimpDrawable *drawable, - GimpPaintInfo *paint_info); +static GimpItem * gimp_channel_duplicate (GimpItem *item, + GType new_type, + gboolean add_alpha); +static void gimp_channel_translate (GimpItem *item, + gint off_x, + gint off_y, + gboolean push_undo); +static void gimp_channel_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type); +static void gimp_channel_resize (GimpItem *item, + gint new_width, + gint new_height, + gint offx, + gint offy); +static void gimp_channel_flip (GimpItem *item, + GimpOrientationType flip_type, + gdouble axis, + gboolean flip_result); +static void gimp_channel_rotate (GimpItem *item, + GimpRotationType flip_type, + gdouble center_x, + gdouble center_y, + gboolean flip_result); +static void gimp_channel_transform (GimpItem *item, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + gboolean clip_result, + GimpProgressFunc progress_callback, + gpointer progress_data); +static gboolean gimp_channel_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpPaintInfo *paint_info); -static void gimp_channel_push_undo (GimpChannel *mask, - const gchar *undo_desc); -static void gimp_channel_validate (TileManager *tm, - Tile *tile); +static gboolean gimp_channel_real_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +static gboolean gimp_channel_real_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +static gint gimp_channel_real_value (GimpChannel *channel, + gint x, + gint y); +static gboolean gimp_channel_real_is_empty (GimpChannel *channel); +static void gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); +static void gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); +static void gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); + +static void gimp_channel_push_undo (GimpChannel *mask, + const gchar *undo_desc); +static void gimp_channel_validate (TileManager *tm, + Tile *tile); static GimpDrawableClass * parent_class = NULL; @@ -167,6 +212,19 @@ gimp_channel_class_init (GimpChannelClass *klass) item_class->stroke = gimp_channel_stroke; item_class->default_name = _("Channel"); item_class->rename_desc = _("Rename Channel"); + + klass->boundary = gimp_channel_real_boundary; + klass->bounds = gimp_channel_real_bounds; + klass->value = gimp_channel_real_value; + klass->is_empty = gimp_channel_real_is_empty; + klass->feather = gimp_channel_real_feather; + klass->sharpen = gimp_channel_real_sharpen; + klass->clear = gimp_channel_real_clear; + klass->all = gimp_channel_real_all; + klass->invert = gimp_channel_real_invert; + klass->border = gimp_channel_real_border; + klass->grow = gimp_channel_real_grow; + klass->shrink = gimp_channel_real_shrink; } static void @@ -551,6 +609,582 @@ gimp_channel_stroke (GimpItem *item, return retval; } +static gboolean +gimp_channel_real_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2) +{ + gint x3, y3, x4, y4; + PixelRegion bPR; + + if (! channel->boundary_known) + { + /* free the out of date boundary segments */ + if (channel->segs_in) + g_free (channel->segs_in); + if (channel->segs_out) + g_free (channel->segs_out); + + if (gimp_channel_bounds (channel, &x3, &y3, &x4, &y4)) + { + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, + x3, y3, (x4 - x3), (y4 - y3), FALSE); + channel->segs_out = find_mask_boundary (&bPR, &channel->num_segs_out, + IgnoreBounds, + x1, y1, + x2, y2, + HALF_WAY); + x1 = MAX (x1, x3); + y1 = MAX (y1, y3); + x2 = MIN (x2, x4); + y2 = MIN (y2, y4); + + if (x2 > x1 && y2 > y1) + { + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + channel->segs_in = find_mask_boundary (&bPR, &channel->num_segs_in, + WithinBounds, + x1, y1, + x2, y2, + HALF_WAY); + } + else + { + channel->segs_in = NULL; + channel->num_segs_in = 0; + } + } + else + { + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + } + + channel->boundary_known = TRUE; + } + + *segs_in = channel->segs_in; + *segs_out = channel->segs_out; + *num_segs_in = channel->num_segs_in; + *num_segs_out = channel->num_segs_out; + + return TRUE; +} + +static gboolean +gimp_channel_real_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + PixelRegion maskPR; + guchar *data, *data1; + gint x, y; + gint ex, ey; + gint tx1, tx2, ty1, ty2; + gint minx, maxx; + gpointer pr; + + /* if the channel's bounds have already been reliably calculated... */ + if (channel->bounds_known) + { + *x1 = channel->x1; + *y1 = channel->y1; + *x2 = channel->x2; + *y2 = channel->y2; + + return ! channel->empty; + } + + /* go through and calculate the bounds */ + tx1 = GIMP_ITEM (channel)->width; + ty1 = GIMP_ITEM (channel)->height; + tx2 = 0; + ty2 = 0; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + + for (pr = pixel_regions_register (1, &maskPR); + pr != NULL; + pr = pixel_regions_process (pr)) + { + data1 = data = maskPR.data; + ex = maskPR.x + maskPR.w; + ey = maskPR.y + maskPR.h; + /* only check the pixels if this tile is not fully within the currently + computed bounds */ + if (maskPR.x < tx1 || ex > tx2 || + maskPR.y < ty1 || ey > ty2) + { + /* Check upper left and lower right corners to see if we can + avoid checking the rest of the pixels in this tile */ + if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1]) + { + if (maskPR.x < tx1) + tx1 = maskPR.x; + if (ex > tx2) + tx2 = ex; + if (maskPR.y < ty1) + ty1 = maskPR.y; + if (ey > ty2) + ty2 = ey; + } + else + { + for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride) + { + for (x = maskPR.x, data = data1; x < ex; x++, data++) + { + if (*data) + { + minx = x; + maxx = x; + + for (; x < ex; x++, data++) + if (*data) + maxx = x; + + if (minx < tx1) + tx1 = minx; + if (maxx > tx2) + tx2 = maxx; + if (y < ty1) + ty1 = y; + if (y > ty2) + ty2 = y; + } + } + } + } + } + } + + tx2 = CLAMP (tx2 + 1, 0, GIMP_ITEM (channel)->width); + ty2 = CLAMP (ty2 + 1, 0, GIMP_ITEM (channel)->height); + + if (tx1 == GIMP_ITEM (channel)->width && ty1 == GIMP_ITEM (channel)->height) + { + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; + } + else + { + channel->empty = FALSE; + channel->x1 = tx1; + channel->y1 = ty1; + channel->x2 = tx2; + channel->y2 = ty2; + } + + channel->bounds_known = TRUE; + + *x1 = channel->x1; + *x2 = channel->x2; + *y1 = channel->y1; + *y2 = channel->y2; + + return ! channel->empty; +} + +static gint +gimp_channel_real_value (GimpChannel *channel, + gint x, + gint y) +{ + Tile *tile; + gint val; + + /* Some checks to cut back on unnecessary work */ + if (channel->bounds_known) + { + if (channel->empty) + return 0; + else if (x < channel->x1 || x >= channel->x2 || + y < channel->y1 || y >= channel->y2) + return 0; + } + else + { + if (x < 0 || x >= GIMP_ITEM (channel)->width || + y < 0 || y >= GIMP_ITEM (channel)->height) + return 0; + } + + tile = tile_manager_get_tile (GIMP_DRAWABLE (channel)->tiles, x, y, + TRUE, FALSE); + val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT)); + tile_release (tile, FALSE); + + return val; +} + +static gboolean +gimp_channel_real_is_empty (GimpChannel *channel) +{ + PixelRegion maskPR; + guchar *data; + gint x, y; + gpointer pr; + + if (channel->bounds_known) + return channel->empty; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + + for (pr = pixel_regions_register (1, &maskPR); + pr != NULL; + pr = pixel_regions_process (pr)) + { + /* check if any pixel in the channel is non-zero */ + data = maskPR.data; + + for (y = 0; y < maskPR.h; y++) + for (x = 0; x < maskPR.w; x++) + if (*data++) + { + pixel_regions_process_stop (pr); + return FALSE; + } + } + + /* The mask is empty, meaning we can set the bounds as known */ + if (channel->segs_in) + g_free (channel->segs_in); + if (channel->segs_out) + g_free (channel->segs_out); + + channel->empty = TRUE; + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + channel->bounds_known = TRUE; + channel->boundary_known = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; + + return TRUE; +} + +static void +gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo) +{ + PixelRegion srcPR; + + if (push_undo) + gimp_channel_push_undo (channel, _("Feather Channel")); + + pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + gimp_item_width (GIMP_ITEM (channel)), + gimp_item_height (GIMP_ITEM (channel)), + FALSE); + gaussian_blur_region (&srcPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + PixelRegion maskPR; + GimpLut *lut; + + if (push_undo) + gimp_channel_push_undo (channel, _("Sharpen Channel")); + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + gimp_item_width (GIMP_ITEM (channel)), + gimp_item_height (GIMP_ITEM (channel)), + TRUE); + lut = threshold_lut_new (0.5, 1); + + pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, + lut, 1, &maskPR); + gimp_lut_free (lut); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + PixelRegion maskPR; + guchar bg = TRANSPARENT_OPACITY; + + if (push_undo) + { + if (! undo_desc) + undo_desc = _("Clear Channel"); + + gimp_channel_push_undo (channel, undo_desc); + } + + if (channel->bounds_known && !channel->empty) + { + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + channel->x1, channel->y1, + (channel->x2 - channel->x1), (channel->y2 - channel->y1), TRUE); + color_region (&maskPR, &bg); + } + else + { + /* clear the mask */ + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + color_region (&maskPR, &bg); + } + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; +} + +static void +gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo) +{ + PixelRegion maskPR; + guchar bg = OPAQUE_OPACITY; + + if (push_undo) + gimp_channel_push_undo (channel, _("Fill Channel")); + + /* clear the channel */ + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + color_region (&maskPR, &bg); + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = FALSE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; +} + +static void +gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo) +{ + if (push_undo) + gimp_channel_push_undo (channel, _("Invert Channel")); + + if (channel->bounds_known && channel->empty) + { + gimp_channel_all (channel, FALSE); + } + else + { + PixelRegion maskPR; + GimpLut *lut; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + + lut = invert_lut_new (1); + + pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, lut, + 1, &maskPR); + + gimp_lut_free (lut); + + channel->bounds_known = FALSE; + } +} + +static void +gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x < 0) + x1 = 0; + else + x1 -= radius_x; + if (x2 + radius_x > GIMP_ITEM (channel)->width) + x2 = GIMP_ITEM (channel)->width; + else + x2 += radius_x; + + if (y1 - radius_y < 0) + y1 = 0; + else + y1 -= radius_y; + if (y2 + radius_y > GIMP_ITEM (channel)->height) + y2 = GIMP_ITEM (channel)->height; + else + y2 += radius_y; + + if (push_undo) + gimp_channel_push_undo (channel, _("Border Channel")); + + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, + (x2-x1), (y2-y1), TRUE); + + border_region (&bPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_shrink (channel, -radius_x, -radius_y, FALSE, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x > 0) + x1 = x1 - radius_x; + else + x1 = 0; + if (y1 - radius_y > 0) + y1 = y1 - radius_y; + else + y1 = 0; + if (x2 + radius_x < GIMP_ITEM (channel)->width) + x2 = x2 + radius_x; + else + x2 = GIMP_ITEM (channel)->width; + if (y2 + radius_y < GIMP_ITEM (channel)->height) + y2 = y2 + radius_y; + else + y2 = GIMP_ITEM (channel)->height; + + if (push_undo) + gimp_channel_push_undo (channel, _("Grow Channel")); + + /* need full extents for grow, not! */ + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), + (y2 - y1), TRUE); + + fatten_region (&bPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_grow (channel, -radius_x, -radius_y, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 > 0) + x1--; + if (y1 > 0) + y1--; + if (x2 < GIMP_ITEM (channel)->width) + x2++; + if (y2 < GIMP_ITEM (channel)->height) + y2++; + + if (push_undo) + gimp_channel_push_undo (channel, _("Shrink Channel")); + + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), + (y2 - y1), TRUE); + + thin_region (&bPR, radius_x, radius_y, edge_lock); + + channel->bounds_known = FALSE; +} + static void gimp_channel_push_undo (GimpChannel *mask, const gchar *undo_desc) @@ -633,7 +1267,7 @@ gimp_channel_new_from_alpha (GimpImage *gimage, channel = gimp_channel_new (gimage, width, height, name, color); - gimp_channel_clear (channel, FALSE); + gimp_channel_clear (channel, NULL, FALSE); if (gimp_rectangle_intersect (0, 0, width, height, GIMP_ITEM (layer)->offset_x, @@ -827,7 +1461,7 @@ gimp_channel_new_mask (GimpImage *gimage, } gboolean -gimp_channel_boundary (GimpChannel *mask, +gimp_channel_boundary (GimpChannel *channel, const BoundSeg **segs_in, const BoundSeg **segs_out, gint *num_segs_in, @@ -837,287 +1471,51 @@ gimp_channel_boundary (GimpChannel *mask, gint x2, gint y2) { - gint x3, y3, x4, y4; - PixelRegion bPR; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (segs_in != NULL, FALSE); g_return_val_if_fail (segs_out != NULL, FALSE); g_return_val_if_fail (num_segs_in != NULL, FALSE); g_return_val_if_fail (num_segs_out != NULL, FALSE); - if (! mask->boundary_known) - { - /* free the out of date boundary segments */ - if (mask->segs_in) - g_free (mask->segs_in); - if (mask->segs_out) - g_free (mask->segs_out); - - if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4)) - { - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, - x3, y3, (x4 - x3), (y4 - y3), FALSE); - mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out, - IgnoreBounds, - x1, y1, - x2, y2, - HALF_WAY); - x1 = MAX (x1, x3); - y1 = MAX (y1, y3); - x2 = MIN (x2, x4); - y2 = MIN (y2, y4); - - if (x2 > x1 && y2 > y1) - { - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - mask->segs_in = find_mask_boundary (&bPR, &mask->num_segs_in, - WithinBounds, - x1, y1, - x2, y2, - HALF_WAY); - } - else - { - mask->segs_in = NULL; - mask->num_segs_in = 0; - } - } - else - { - mask->segs_in = NULL; - mask->segs_out = NULL; - mask->num_segs_in = 0; - mask->num_segs_out = 0; - } - - mask->boundary_known = TRUE; - } - - *segs_in = mask->segs_in; - *segs_out = mask->segs_out; - *num_segs_in = mask->num_segs_in; - *num_segs_out = mask->num_segs_out; - - return TRUE; -} - -gint -gimp_channel_value (GimpChannel *mask, - gint x, - gint y) -{ - Tile *tile; - gint val; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0); - - /* Some checks to cut back on unnecessary work */ - if (mask->bounds_known) - { - if (mask->empty) - return 0; - else if (x < mask->x1 || x >= mask->x2 || y < mask->y1 || y >= mask->y2) - return 0; - } - else - { - if (x < 0 || x >= GIMP_ITEM (mask)->width || - y < 0 || y >= GIMP_ITEM (mask)->height) - return 0; - } - - tile = tile_manager_get_tile (GIMP_DRAWABLE (mask)->tiles, x, y, TRUE, FALSE); - val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT)); - tile_release (tile, FALSE); - - return val; + return GIMP_CHANNEL_GET_CLASS (channel)->boundary (channel, + segs_in, segs_out, + num_segs_in, num_segs_out, + x1, y1, + x2, y2); } gboolean -gimp_channel_bounds (GimpChannel *mask, +gimp_channel_bounds (GimpChannel *channel, gint *x1, gint *y1, gint *x2, gint *y2) { - PixelRegion maskPR; - guchar *data, *data1; - gint x, y; - gint ex, ey; - gint tx1, tx2, ty1, ty2; - gint minx, maxx; - gpointer pr; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (x1 != NULL, FALSE); g_return_val_if_fail (y1 != NULL, FALSE); g_return_val_if_fail (x2 != NULL, FALSE); g_return_val_if_fail (y2 != NULL, FALSE); - /* if the mask's bounds have already been reliably calculated... */ - if (mask->bounds_known) - { - *x1 = mask->x1; - *y1 = mask->y1; - *x2 = mask->x2; - *y2 = mask->y2; + return GIMP_CHANNEL_GET_CLASS (channel)->bounds (channel, x1, y1, x2, y2); +} - return ! mask->empty; - } +gint +gimp_channel_value (GimpChannel *channel, + gint x, + gint y) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0); - /* go through and calculate the bounds */ - tx1 = GIMP_ITEM (mask)->width; - ty1 = GIMP_ITEM (mask)->height; - tx2 = 0; - ty2 = 0; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - - for (pr = pixel_regions_register (1, &maskPR); - pr != NULL; - pr = pixel_regions_process (pr)) - { - data1 = data = maskPR.data; - ex = maskPR.x + maskPR.w; - ey = maskPR.y + maskPR.h; - /* only check the pixels if this tile is not fully within the currently - computed bounds */ - if (maskPR.x < tx1 || ex > tx2 || - maskPR.y < ty1 || ey > ty2) - { - /* Check upper left and lower right corners to see if we can - avoid checking the rest of the pixels in this tile */ - if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1]) - { - if (maskPR.x < tx1) - tx1 = maskPR.x; - if (ex > tx2) - tx2 = ex; - if (maskPR.y < ty1) - ty1 = maskPR.y; - if (ey > ty2) - ty2 = ey; - } - else - { - for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride) - { - for (x = maskPR.x, data = data1; x < ex; x++, data++) - { - if (*data) - { - minx = x; - maxx = x; - - for (; x < ex; x++, data++) - if (*data) - maxx = x; - - if (minx < tx1) - tx1 = minx; - if (maxx > tx2) - tx2 = maxx; - if (y < ty1) - ty1 = y; - if (y > ty2) - ty2 = y; - } - } - } - } - } - } - - tx2 = CLAMP (tx2 + 1, 0, GIMP_ITEM (mask)->width); - ty2 = CLAMP (ty2 + 1, 0, GIMP_ITEM (mask)->height); - - if (tx1 == GIMP_ITEM (mask)->width && ty1 == GIMP_ITEM (mask)->height) - { - mask->empty = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; - } - else - { - mask->empty = FALSE; - mask->x1 = tx1; - mask->y1 = ty1; - mask->x2 = tx2; - mask->y2 = ty2; - } - - mask->bounds_known = TRUE; - - *x1 = mask->x1; - *x2 = mask->x2; - *y1 = mask->y1; - *y2 = mask->y2; - - return !mask->empty; + return GIMP_CHANNEL_GET_CLASS (channel)->value (channel, x, y); } gboolean -gimp_channel_is_empty (GimpChannel *mask) +gimp_channel_is_empty (GimpChannel *channel) { - PixelRegion maskPR; - guchar *data; - gint x, y; - gpointer pr; + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); - - if (mask->bounds_known) - return mask->empty; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - - for (pr = pixel_regions_register (1, &maskPR); - pr != NULL; - pr = pixel_regions_process (pr)) - { - /* check if any pixel in the mask is non-zero */ - data = maskPR.data; - - for (y = 0; y < maskPR.h; y++) - for (x = 0; x < maskPR.w; x++) - if (*data++) - { - pixel_regions_process_stop (pr); - return FALSE; - } - } - - /* The mask is empty, meaning we can set the bounds as known */ - if (mask->segs_in) - g_free (mask->segs_in); - if (mask->segs_out) - g_free (mask->segs_out); - - mask->empty = TRUE; - mask->segs_in = NULL; - mask->segs_out = NULL; - mask->num_segs_in = 0; - mask->num_segs_out = 0; - mask->bounds_known = TRUE; - mask->boundary_known = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; - - return TRUE; + return GIMP_CHANNEL_GET_CLASS (channel)->is_empty (channel); } void @@ -1578,309 +1976,89 @@ gimp_channel_combine_mask (GimpChannel *mask, } void -gimp_channel_feather (GimpChannel *mask, +gimp_channel_feather (GimpChannel *channel, gdouble radius_x, gdouble radius_y, gboolean push_undo) { - PixelRegion srcPR; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Feather Channel")); - - pixel_region_init (&srcPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - gimp_item_width (GIMP_ITEM (mask)), - gimp_item_height (GIMP_ITEM (mask)), - FALSE); - gaussian_blur_region (&srcPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->feather (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_sharpen (GimpChannel *mask, +gimp_channel_sharpen (GimpChannel *channel, gboolean push_undo) { - PixelRegion maskPR; - GimpLut *lut; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Sharpen Channel")); - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - gimp_item_width (GIMP_ITEM (mask)), - gimp_item_height (GIMP_ITEM (mask)), - TRUE); - lut = threshold_lut_new (0.5, 1); - - pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, - lut, 1, &maskPR); - gimp_lut_free (lut); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->sharpen (channel, push_undo); } void -gimp_channel_clear (GimpChannel *mask, +gimp_channel_clear (GimpChannel *channel, + const gchar *undo_desc, gboolean push_undo) { - PixelRegion maskPR; - guchar bg = TRANSPARENT_OPACITY; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Clear Channel")); - - if (mask->bounds_known && !mask->empty) - { - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - mask->x1, mask->y1, - (mask->x2 - mask->x1), (mask->y2 - mask->y1), TRUE); - color_region (&maskPR, &bg); - } - else - { - /* clear the mask */ - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - color_region (&maskPR, &bg); - } - - /* we know the bounds */ - mask->bounds_known = TRUE; - mask->empty = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; + GIMP_CHANNEL_GET_CLASS (channel)->clear (channel, undo_desc, push_undo); } void -gimp_channel_all (GimpChannel *mask, +gimp_channel_all (GimpChannel *channel, gboolean push_undo) { - PixelRegion maskPR; - guchar bg = OPAQUE_OPACITY; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Fill Channel")); - - /* clear the mask */ - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - color_region (&maskPR, &bg); - - /* we know the bounds */ - mask->bounds_known = TRUE; - mask->empty = FALSE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; + GIMP_CHANNEL_GET_CLASS (channel)->all (channel, push_undo); } void -gimp_channel_invert (GimpChannel *mask, +gimp_channel_invert (GimpChannel *channel, gboolean push_undo) { - g_return_if_fail (GIMP_IS_CHANNEL (mask)); + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - if (push_undo) - gimp_channel_push_undo (mask, _("Invert Channel")); - - if (mask->bounds_known && mask->empty) - { - gimp_channel_all (mask, FALSE); - } - else - { - PixelRegion maskPR; - GimpLut *lut; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - - lut = invert_lut_new (1); - - pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, lut, - 1, &maskPR); - - gimp_lut_free (lut); - - mask->bounds_known = FALSE; - } + GIMP_CHANNEL_GET_CLASS (channel)->invert (channel, push_undo); } void -gimp_channel_border (GimpChannel *mask, +gimp_channel_border (GimpChannel *channel, gint radius_x, gint radius_y, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 - radius_x < 0) - x1 = 0; - else - x1 -= radius_x; - if (x2 + radius_x > GIMP_ITEM (mask)->width) - x2 = GIMP_ITEM (mask)->width; - else - x2 += radius_x; - - if (y1 - radius_y < 0) - y1 = 0; - else - y1 -= radius_y; - if (y2 + radius_y > GIMP_ITEM (mask)->height) - y2 = GIMP_ITEM (mask)->height; - else - y2 += radius_y; - - if (push_undo) - gimp_channel_push_undo (mask, _("Border Channel")); - - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, - (x2-x1), (y2-y1), TRUE); - - border_region (&bPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->border (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_grow (GimpChannel *mask, +gimp_channel_grow (GimpChannel *channel, gint radius_x, gint radius_y, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x == 0 && radius_y == 0) - return; - - if (radius_x <= 0 && radius_y <= 0) - { - gimp_channel_shrink (mask, -radius_x, -radius_y, FALSE, push_undo); - return; - } - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 - radius_x > 0) - x1 = x1 - radius_x; - else - x1 = 0; - if (y1 - radius_y > 0) - y1 = y1 - radius_y; - else - y1 = 0; - if (x2 + radius_x < GIMP_ITEM (mask)->width) - x2 = x2 + radius_x; - else - x2 = GIMP_ITEM (mask)->width; - if (y2 + radius_y < GIMP_ITEM (mask)->height) - y2 = y2 + radius_y; - else - y2 = GIMP_ITEM (mask)->height; - - if (push_undo) - gimp_channel_push_undo (mask, _("Grow Channel")); - - /* need full extents for grow, not! */ - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1), - (y2 - y1), TRUE); - - fatten_region (&bPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->grow (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_shrink (GimpChannel *mask, +gimp_channel_shrink (GimpChannel *channel, gint radius_x, gint radius_y, gboolean edge_lock, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x == 0 && radius_y == 0) - return; - - if (radius_x <= 0 && radius_y <= 0) - { - gimp_channel_grow (mask, -radius_x, -radius_y, push_undo); - return; - } - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 > 0) - x1--; - if (y1 > 0) - y1--; - if (x2 < GIMP_ITEM (mask)->width) - x2++; - if (y2 < GIMP_ITEM (mask)->height) - y2++; - - if (push_undo) - gimp_channel_push_undo (mask, _("Shrink Channel")); - - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1), - (y2 - y1), TRUE); - - thin_region (&bPR, radius_x, radius_y, edge_lock); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->shrink (channel, radius_x, radius_y, + edge_lock, push_undo); } void @@ -1909,11 +2087,3 @@ gimp_channel_load (GimpChannel *mask, mask->bounds_known = FALSE; } - -void -gimp_channel_invalidate_bounds (GimpChannel *channel) -{ - g_return_if_fail (GIMP_IS_CHANNEL (channel)); - - channel->bounds_known = FALSE; -} diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h index 60a1f7c7ae..a4345bb309 100644 --- a/app/core/gimpchannel-combine.h +++ b/app/core/gimpchannel-combine.h @@ -59,131 +59,175 @@ struct _GimpChannel struct _GimpChannelClass { GimpDrawableClass parent_class; + + gboolean (* boundary) (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); + gboolean (* bounds) (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2); + gint (* value) (GimpChannel *channel, + gint x, + gint y); + gboolean (* is_empty) (GimpChannel *channel); + void (* feather) (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); + void (* sharpen) (GimpChannel *channel, + gboolean push_undo); + void (* clear) (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); + void (* all) (GimpChannel *channel, + gboolean push_undo); + void (* invert) (GimpChannel *channel, + gboolean push_undo); + void (* border) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); + void (* grow) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); + void (* shrink) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); }; /* function declarations */ -GType gimp_channel_get_type (void) G_GNUC_CONST; +GType gimp_channel_get_type (void) G_GNUC_CONST; -GimpChannel * gimp_channel_new (GimpImage *gimage, - gint width, - gint height, - const gchar *name, - const GimpRGB *color); +GimpChannel * gimp_channel_new (GimpImage *gimage, + gint width, + gint height, + const gchar *name, + const GimpRGB *color); -GimpChannel * gimp_channel_new_from_alpha (GimpImage *gimage, - GimpLayer *layer, - const gchar *name, - const GimpRGB *color); -GimpChannel * gimp_channel_new_from_component (GimpImage *gimage, - GimpChannelType type, - const gchar *name, - const GimpRGB *color); +GimpChannel * gimp_channel_new_from_alpha (GimpImage *gimage, + GimpLayer *layer, + const gchar *name, + const GimpRGB *color); +GimpChannel * gimp_channel_new_from_component (GimpImage *gimage, + GimpChannelType type, + const gchar *name, + const GimpRGB *color); -gdouble gimp_channel_get_opacity (const GimpChannel *channel); -void gimp_channel_set_opacity (GimpChannel *channel, - gdouble opacity, - gboolean push_undo); +gdouble gimp_channel_get_opacity (const GimpChannel *channel); +void gimp_channel_set_opacity (GimpChannel *channel, + gdouble opacity, + gboolean push_undo); -void gimp_channel_get_color (const GimpChannel *channel, - GimpRGB *color); -void gimp_channel_set_color (GimpChannel *channel, - const GimpRGB *color, - gboolean push_undo); +void gimp_channel_get_color (const GimpChannel *channel, + GimpRGB *color); +void gimp_channel_set_color (GimpChannel *channel, + const GimpRGB *color, + gboolean push_undo); -gboolean gimp_channel_get_show_masked (GimpChannel *channel); -void gimp_channel_set_show_masked (GimpChannel *channel, - gboolean show_masked); +gboolean gimp_channel_get_show_masked (GimpChannel *channel); +void gimp_channel_set_show_masked (GimpChannel *channel, + gboolean show_masked); /* selection mask functions */ -GimpChannel * gimp_channel_new_mask (GimpImage *gimage, - gint width, - gint height); +GimpChannel * gimp_channel_new_mask (GimpImage *gimage, + gint width, + gint height); -gboolean gimp_channel_boundary (GimpChannel *mask, - const BoundSeg **segs_in, - const BoundSeg **segs_out, - gint *num_segs_in, - gint *num_segs_out, - gint x1, - gint y1, - gint x2, - gint y2); -gboolean gimp_channel_bounds (GimpChannel *mask, - gint *x1, - gint *y1, - gint *x2, - gint *y2); -gint gimp_channel_value (GimpChannel *mask, - gint x, - gint y); -gboolean gimp_channel_is_empty (GimpChannel *mask); +gboolean gimp_channel_boundary (GimpChannel *mask, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +gboolean gimp_channel_bounds (GimpChannel *mask, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +gint gimp_channel_value (GimpChannel *mask, + gint x, + gint y); +gboolean gimp_channel_is_empty (GimpChannel *mask); -void gimp_channel_add_segment (GimpChannel *mask, - gint x, - gint y, - gint width, - gint value); -void gimp_channel_sub_segment (GimpChannel *mask, - gint x, - gint y, - gint width, - gint value); -void gimp_channel_combine_rect (GimpChannel *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h); -void gimp_channel_combine_ellipse (GimpChannel *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h, - gboolean antialias); -void gimp_channel_combine_mask (GimpChannel *mask, - GimpChannel *add_on, - GimpChannelOps op, - gint off_x, - gint off_y); +void gimp_channel_add_segment (GimpChannel *mask, + gint x, + gint y, + gint width, + gint value); +void gimp_channel_sub_segment (GimpChannel *mask, + gint x, + gint y, + gint width, + gint value); +void gimp_channel_combine_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h); +void gimp_channel_combine_ellipse (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias); +void gimp_channel_combine_mask (GimpChannel *mask, + GimpChannel *add_on, + GimpChannelOps op, + gint off_x, + gint off_y); -void gimp_channel_feather (GimpChannel *mask, - gdouble radius_x, - gdouble radius_y, - gboolean push_undo); -void gimp_channel_sharpen (GimpChannel *mask, - gboolean push_undo); +void gimp_channel_feather (GimpChannel *mask, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); +void gimp_channel_sharpen (GimpChannel *mask, + gboolean push_undo); -void gimp_channel_clear (GimpChannel *mask, - gboolean push_undo); -void gimp_channel_all (GimpChannel *mask, - gboolean push_undo); -void gimp_channel_invert (GimpChannel *mask, - gboolean push_undo); +void gimp_channel_clear (GimpChannel *mask, + const gchar *undo_desc, + gboolean push_undo); +void gimp_channel_all (GimpChannel *mask, + gboolean push_undo); +void gimp_channel_invert (GimpChannel *mask, + gboolean push_undo); -void gimp_channel_border (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean push_undo); -void gimp_channel_grow (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean push_undo); -void gimp_channel_shrink (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean edge_lock, - gboolean push_undo); +void gimp_channel_border (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean push_undo); +void gimp_channel_grow (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean push_undo); +void gimp_channel_shrink (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); -void gimp_channel_load (GimpChannel *mask, - GimpChannel *channel, - gboolean push_undo); - -void gimp_channel_invalidate_bounds (GimpChannel *channel); +void gimp_channel_load (GimpChannel *mask, + GimpChannel *channel, + gboolean push_undo); #endif /* __GIMP_CHANNEL_H__ */ diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c index f470051294..50d40f583e 100644 --- a/app/core/gimpchannel.c +++ b/app/core/gimpchannel.c @@ -53,56 +53,101 @@ #include "gimp-intl.h" -static void gimp_channel_class_init (GimpChannelClass *klass); -static void gimp_channel_init (GimpChannel *channel); +static void gimp_channel_class_init (GimpChannelClass *klass); +static void gimp_channel_init (GimpChannel *channel); -static void gimp_channel_finalize (GObject *object); +static void gimp_channel_finalize (GObject *object); -static gsize gimp_channel_get_memsize (GimpObject *object, - gsize *gui_size); +static gsize gimp_channel_get_memsize (GimpObject *object, + gsize *gui_size); -static GimpItem * gimp_channel_duplicate (GimpItem *item, - GType new_type, - gboolean add_alpha); -static void gimp_channel_translate (GimpItem *item, - gint off_x, - gint off_y, - gboolean push_undo); -static void gimp_channel_scale (GimpItem *item, - gint new_width, - gint new_height, - gint new_offset_x, - gint new_offset_y, - GimpInterpolationType interp_type); -static void gimp_channel_resize (GimpItem *item, - gint new_width, - gint new_height, - gint offx, - gint offy); -static void gimp_channel_flip (GimpItem *item, - GimpOrientationType flip_type, - gdouble axis, - gboolean flip_result); -static void gimp_channel_rotate (GimpItem *item, - GimpRotationType flip_type, - gdouble center_x, - gdouble center_y, - gboolean flip_result); -static void gimp_channel_transform (GimpItem *item, - const GimpMatrix3 *matrix, - GimpTransformDirection direction, - GimpInterpolationType interpolation_type, - gboolean clip_result, - GimpProgressFunc progress_callback, - gpointer progress_data); -static gboolean gimp_channel_stroke (GimpItem *item, - GimpDrawable *drawable, - GimpPaintInfo *paint_info); +static GimpItem * gimp_channel_duplicate (GimpItem *item, + GType new_type, + gboolean add_alpha); +static void gimp_channel_translate (GimpItem *item, + gint off_x, + gint off_y, + gboolean push_undo); +static void gimp_channel_scale (GimpItem *item, + gint new_width, + gint new_height, + gint new_offset_x, + gint new_offset_y, + GimpInterpolationType interp_type); +static void gimp_channel_resize (GimpItem *item, + gint new_width, + gint new_height, + gint offx, + gint offy); +static void gimp_channel_flip (GimpItem *item, + GimpOrientationType flip_type, + gdouble axis, + gboolean flip_result); +static void gimp_channel_rotate (GimpItem *item, + GimpRotationType flip_type, + gdouble center_x, + gdouble center_y, + gboolean flip_result); +static void gimp_channel_transform (GimpItem *item, + const GimpMatrix3 *matrix, + GimpTransformDirection direction, + GimpInterpolationType interpolation_type, + gboolean clip_result, + GimpProgressFunc progress_callback, + gpointer progress_data); +static gboolean gimp_channel_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpPaintInfo *paint_info); -static void gimp_channel_push_undo (GimpChannel *mask, - const gchar *undo_desc); -static void gimp_channel_validate (TileManager *tm, - Tile *tile); +static gboolean gimp_channel_real_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +static gboolean gimp_channel_real_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +static gint gimp_channel_real_value (GimpChannel *channel, + gint x, + gint y); +static gboolean gimp_channel_real_is_empty (GimpChannel *channel); +static void gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); +static void gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); +static void gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo); +static void gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); + +static void gimp_channel_push_undo (GimpChannel *mask, + const gchar *undo_desc); +static void gimp_channel_validate (TileManager *tm, + Tile *tile); static GimpDrawableClass * parent_class = NULL; @@ -167,6 +212,19 @@ gimp_channel_class_init (GimpChannelClass *klass) item_class->stroke = gimp_channel_stroke; item_class->default_name = _("Channel"); item_class->rename_desc = _("Rename Channel"); + + klass->boundary = gimp_channel_real_boundary; + klass->bounds = gimp_channel_real_bounds; + klass->value = gimp_channel_real_value; + klass->is_empty = gimp_channel_real_is_empty; + klass->feather = gimp_channel_real_feather; + klass->sharpen = gimp_channel_real_sharpen; + klass->clear = gimp_channel_real_clear; + klass->all = gimp_channel_real_all; + klass->invert = gimp_channel_real_invert; + klass->border = gimp_channel_real_border; + klass->grow = gimp_channel_real_grow; + klass->shrink = gimp_channel_real_shrink; } static void @@ -551,6 +609,582 @@ gimp_channel_stroke (GimpItem *item, return retval; } +static gboolean +gimp_channel_real_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2) +{ + gint x3, y3, x4, y4; + PixelRegion bPR; + + if (! channel->boundary_known) + { + /* free the out of date boundary segments */ + if (channel->segs_in) + g_free (channel->segs_in); + if (channel->segs_out) + g_free (channel->segs_out); + + if (gimp_channel_bounds (channel, &x3, &y3, &x4, &y4)) + { + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, + x3, y3, (x4 - x3), (y4 - y3), FALSE); + channel->segs_out = find_mask_boundary (&bPR, &channel->num_segs_out, + IgnoreBounds, + x1, y1, + x2, y2, + HALF_WAY); + x1 = MAX (x1, x3); + y1 = MAX (y1, y3); + x2 = MIN (x2, x4); + y2 = MIN (y2, y4); + + if (x2 > x1 && y2 > y1) + { + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + channel->segs_in = find_mask_boundary (&bPR, &channel->num_segs_in, + WithinBounds, + x1, y1, + x2, y2, + HALF_WAY); + } + else + { + channel->segs_in = NULL; + channel->num_segs_in = 0; + } + } + else + { + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + } + + channel->boundary_known = TRUE; + } + + *segs_in = channel->segs_in; + *segs_out = channel->segs_out; + *num_segs_in = channel->num_segs_in; + *num_segs_out = channel->num_segs_out; + + return TRUE; +} + +static gboolean +gimp_channel_real_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + PixelRegion maskPR; + guchar *data, *data1; + gint x, y; + gint ex, ey; + gint tx1, tx2, ty1, ty2; + gint minx, maxx; + gpointer pr; + + /* if the channel's bounds have already been reliably calculated... */ + if (channel->bounds_known) + { + *x1 = channel->x1; + *y1 = channel->y1; + *x2 = channel->x2; + *y2 = channel->y2; + + return ! channel->empty; + } + + /* go through and calculate the bounds */ + tx1 = GIMP_ITEM (channel)->width; + ty1 = GIMP_ITEM (channel)->height; + tx2 = 0; + ty2 = 0; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + + for (pr = pixel_regions_register (1, &maskPR); + pr != NULL; + pr = pixel_regions_process (pr)) + { + data1 = data = maskPR.data; + ex = maskPR.x + maskPR.w; + ey = maskPR.y + maskPR.h; + /* only check the pixels if this tile is not fully within the currently + computed bounds */ + if (maskPR.x < tx1 || ex > tx2 || + maskPR.y < ty1 || ey > ty2) + { + /* Check upper left and lower right corners to see if we can + avoid checking the rest of the pixels in this tile */ + if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1]) + { + if (maskPR.x < tx1) + tx1 = maskPR.x; + if (ex > tx2) + tx2 = ex; + if (maskPR.y < ty1) + ty1 = maskPR.y; + if (ey > ty2) + ty2 = ey; + } + else + { + for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride) + { + for (x = maskPR.x, data = data1; x < ex; x++, data++) + { + if (*data) + { + minx = x; + maxx = x; + + for (; x < ex; x++, data++) + if (*data) + maxx = x; + + if (minx < tx1) + tx1 = minx; + if (maxx > tx2) + tx2 = maxx; + if (y < ty1) + ty1 = y; + if (y > ty2) + ty2 = y; + } + } + } + } + } + } + + tx2 = CLAMP (tx2 + 1, 0, GIMP_ITEM (channel)->width); + ty2 = CLAMP (ty2 + 1, 0, GIMP_ITEM (channel)->height); + + if (tx1 == GIMP_ITEM (channel)->width && ty1 == GIMP_ITEM (channel)->height) + { + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; + } + else + { + channel->empty = FALSE; + channel->x1 = tx1; + channel->y1 = ty1; + channel->x2 = tx2; + channel->y2 = ty2; + } + + channel->bounds_known = TRUE; + + *x1 = channel->x1; + *x2 = channel->x2; + *y1 = channel->y1; + *y2 = channel->y2; + + return ! channel->empty; +} + +static gint +gimp_channel_real_value (GimpChannel *channel, + gint x, + gint y) +{ + Tile *tile; + gint val; + + /* Some checks to cut back on unnecessary work */ + if (channel->bounds_known) + { + if (channel->empty) + return 0; + else if (x < channel->x1 || x >= channel->x2 || + y < channel->y1 || y >= channel->y2) + return 0; + } + else + { + if (x < 0 || x >= GIMP_ITEM (channel)->width || + y < 0 || y >= GIMP_ITEM (channel)->height) + return 0; + } + + tile = tile_manager_get_tile (GIMP_DRAWABLE (channel)->tiles, x, y, + TRUE, FALSE); + val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT)); + tile_release (tile, FALSE); + + return val; +} + +static gboolean +gimp_channel_real_is_empty (GimpChannel *channel) +{ + PixelRegion maskPR; + guchar *data; + gint x, y; + gpointer pr; + + if (channel->bounds_known) + return channel->empty; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, FALSE); + + for (pr = pixel_regions_register (1, &maskPR); + pr != NULL; + pr = pixel_regions_process (pr)) + { + /* check if any pixel in the channel is non-zero */ + data = maskPR.data; + + for (y = 0; y < maskPR.h; y++) + for (x = 0; x < maskPR.w; x++) + if (*data++) + { + pixel_regions_process_stop (pr); + return FALSE; + } + } + + /* The mask is empty, meaning we can set the bounds as known */ + if (channel->segs_in) + g_free (channel->segs_in); + if (channel->segs_out) + g_free (channel->segs_out); + + channel->empty = TRUE; + channel->segs_in = NULL; + channel->segs_out = NULL; + channel->num_segs_in = 0; + channel->num_segs_out = 0; + channel->bounds_known = TRUE; + channel->boundary_known = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; + + return TRUE; +} + +static void +gimp_channel_real_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo) +{ + PixelRegion srcPR; + + if (push_undo) + gimp_channel_push_undo (channel, _("Feather Channel")); + + pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + gimp_item_width (GIMP_ITEM (channel)), + gimp_item_height (GIMP_ITEM (channel)), + FALSE); + gaussian_blur_region (&srcPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + PixelRegion maskPR; + GimpLut *lut; + + if (push_undo) + gimp_channel_push_undo (channel, _("Sharpen Channel")); + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + gimp_item_width (GIMP_ITEM (channel)), + gimp_item_height (GIMP_ITEM (channel)), + TRUE); + lut = threshold_lut_new (0.5, 1); + + pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, + lut, 1, &maskPR); + gimp_lut_free (lut); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + PixelRegion maskPR; + guchar bg = TRANSPARENT_OPACITY; + + if (push_undo) + { + if (! undo_desc) + undo_desc = _("Clear Channel"); + + gimp_channel_push_undo (channel, undo_desc); + } + + if (channel->bounds_known && !channel->empty) + { + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + channel->x1, channel->y1, + (channel->x2 - channel->x1), (channel->y2 - channel->y1), TRUE); + color_region (&maskPR, &bg); + } + else + { + /* clear the mask */ + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + color_region (&maskPR, &bg); + } + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = TRUE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; +} + +static void +gimp_channel_real_all (GimpChannel *channel, + gboolean push_undo) +{ + PixelRegion maskPR; + guchar bg = OPAQUE_OPACITY; + + if (push_undo) + gimp_channel_push_undo (channel, _("Fill Channel")); + + /* clear the channel */ + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + color_region (&maskPR, &bg); + + /* we know the bounds */ + channel->bounds_known = TRUE; + channel->empty = FALSE; + channel->x1 = 0; + channel->y1 = 0; + channel->x2 = GIMP_ITEM (channel)->width; + channel->y2 = GIMP_ITEM (channel)->height; +} + +static void +gimp_channel_real_invert (GimpChannel *channel, + gboolean push_undo) +{ + if (push_undo) + gimp_channel_push_undo (channel, _("Invert Channel")); + + if (channel->bounds_known && channel->empty) + { + gimp_channel_all (channel, FALSE); + } + else + { + PixelRegion maskPR; + GimpLut *lut; + + pixel_region_init (&maskPR, GIMP_DRAWABLE (channel)->tiles, + 0, 0, + GIMP_ITEM (channel)->width, + GIMP_ITEM (channel)->height, TRUE); + + lut = invert_lut_new (1); + + pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, lut, + 1, &maskPR); + + gimp_lut_free (lut); + + channel->bounds_known = FALSE; + } +} + +static void +gimp_channel_real_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x < 0) + x1 = 0; + else + x1 -= radius_x; + if (x2 + radius_x > GIMP_ITEM (channel)->width) + x2 = GIMP_ITEM (channel)->width; + else + x2 += radius_x; + + if (y1 - radius_y < 0) + y1 = 0; + else + y1 -= radius_y; + if (y2 + radius_y > GIMP_ITEM (channel)->height) + y2 = GIMP_ITEM (channel)->height; + else + y2 += radius_y; + + if (push_undo) + gimp_channel_push_undo (channel, _("Border Channel")); + + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, + (x2-x1), (y2-y1), TRUE); + + border_region (&bPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_shrink (channel, -radius_x, -radius_y, FALSE, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 - radius_x > 0) + x1 = x1 - radius_x; + else + x1 = 0; + if (y1 - radius_y > 0) + y1 = y1 - radius_y; + else + y1 = 0; + if (x2 + radius_x < GIMP_ITEM (channel)->width) + x2 = x2 + radius_x; + else + x2 = GIMP_ITEM (channel)->width; + if (y2 + radius_y < GIMP_ITEM (channel)->height) + y2 = y2 + radius_y; + else + y2 = GIMP_ITEM (channel)->height; + + if (push_undo) + gimp_channel_push_undo (channel, _("Grow Channel")); + + /* need full extents for grow, not! */ + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), + (y2 - y1), TRUE); + + fatten_region (&bPR, radius_x, radius_y); + + channel->bounds_known = FALSE; +} + +static void +gimp_channel_real_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + PixelRegion bPR; + gint x1, y1, x2, y2; + + if (radius_x == 0 && radius_y == 0) + return; + + if (radius_x <= 0 && radius_y <= 0) + { + gimp_channel_grow (channel, -radius_x, -radius_y, push_undo); + return; + } + + if (radius_x < 0 || radius_y < 0) + return; + + if (! gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) + return; + + if (gimp_channel_is_empty (channel)) + return; + + if (x1 > 0) + x1--; + if (y1 > 0) + y1--; + if (x2 < GIMP_ITEM (channel)->width) + x2++; + if (y2 < GIMP_ITEM (channel)->height) + y2++; + + if (push_undo) + gimp_channel_push_undo (channel, _("Shrink Channel")); + + pixel_region_init (&bPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), + (y2 - y1), TRUE); + + thin_region (&bPR, radius_x, radius_y, edge_lock); + + channel->bounds_known = FALSE; +} + static void gimp_channel_push_undo (GimpChannel *mask, const gchar *undo_desc) @@ -633,7 +1267,7 @@ gimp_channel_new_from_alpha (GimpImage *gimage, channel = gimp_channel_new (gimage, width, height, name, color); - gimp_channel_clear (channel, FALSE); + gimp_channel_clear (channel, NULL, FALSE); if (gimp_rectangle_intersect (0, 0, width, height, GIMP_ITEM (layer)->offset_x, @@ -827,7 +1461,7 @@ gimp_channel_new_mask (GimpImage *gimage, } gboolean -gimp_channel_boundary (GimpChannel *mask, +gimp_channel_boundary (GimpChannel *channel, const BoundSeg **segs_in, const BoundSeg **segs_out, gint *num_segs_in, @@ -837,287 +1471,51 @@ gimp_channel_boundary (GimpChannel *mask, gint x2, gint y2) { - gint x3, y3, x4, y4; - PixelRegion bPR; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (segs_in != NULL, FALSE); g_return_val_if_fail (segs_out != NULL, FALSE); g_return_val_if_fail (num_segs_in != NULL, FALSE); g_return_val_if_fail (num_segs_out != NULL, FALSE); - if (! mask->boundary_known) - { - /* free the out of date boundary segments */ - if (mask->segs_in) - g_free (mask->segs_in); - if (mask->segs_out) - g_free (mask->segs_out); - - if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4)) - { - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, - x3, y3, (x4 - x3), (y4 - y3), FALSE); - mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out, - IgnoreBounds, - x1, y1, - x2, y2, - HALF_WAY); - x1 = MAX (x1, x3); - y1 = MAX (y1, y3); - x2 = MIN (x2, x4); - y2 = MIN (y2, y4); - - if (x2 > x1 && y2 > y1) - { - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - mask->segs_in = find_mask_boundary (&bPR, &mask->num_segs_in, - WithinBounds, - x1, y1, - x2, y2, - HALF_WAY); - } - else - { - mask->segs_in = NULL; - mask->num_segs_in = 0; - } - } - else - { - mask->segs_in = NULL; - mask->segs_out = NULL; - mask->num_segs_in = 0; - mask->num_segs_out = 0; - } - - mask->boundary_known = TRUE; - } - - *segs_in = mask->segs_in; - *segs_out = mask->segs_out; - *num_segs_in = mask->num_segs_in; - *num_segs_out = mask->num_segs_out; - - return TRUE; -} - -gint -gimp_channel_value (GimpChannel *mask, - gint x, - gint y) -{ - Tile *tile; - gint val; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0); - - /* Some checks to cut back on unnecessary work */ - if (mask->bounds_known) - { - if (mask->empty) - return 0; - else if (x < mask->x1 || x >= mask->x2 || y < mask->y1 || y >= mask->y2) - return 0; - } - else - { - if (x < 0 || x >= GIMP_ITEM (mask)->width || - y < 0 || y >= GIMP_ITEM (mask)->height) - return 0; - } - - tile = tile_manager_get_tile (GIMP_DRAWABLE (mask)->tiles, x, y, TRUE, FALSE); - val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT)); - tile_release (tile, FALSE); - - return val; + return GIMP_CHANNEL_GET_CLASS (channel)->boundary (channel, + segs_in, segs_out, + num_segs_in, num_segs_out, + x1, y1, + x2, y2); } gboolean -gimp_channel_bounds (GimpChannel *mask, +gimp_channel_bounds (GimpChannel *channel, gint *x1, gint *y1, gint *x2, gint *y2) { - PixelRegion maskPR; - guchar *data, *data1; - gint x, y; - gint ex, ey; - gint tx1, tx2, ty1, ty2; - gint minx, maxx; - gpointer pr; - - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (x1 != NULL, FALSE); g_return_val_if_fail (y1 != NULL, FALSE); g_return_val_if_fail (x2 != NULL, FALSE); g_return_val_if_fail (y2 != NULL, FALSE); - /* if the mask's bounds have already been reliably calculated... */ - if (mask->bounds_known) - { - *x1 = mask->x1; - *y1 = mask->y1; - *x2 = mask->x2; - *y2 = mask->y2; + return GIMP_CHANNEL_GET_CLASS (channel)->bounds (channel, x1, y1, x2, y2); +} - return ! mask->empty; - } +gint +gimp_channel_value (GimpChannel *channel, + gint x, + gint y) +{ + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0); - /* go through and calculate the bounds */ - tx1 = GIMP_ITEM (mask)->width; - ty1 = GIMP_ITEM (mask)->height; - tx2 = 0; - ty2 = 0; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - - for (pr = pixel_regions_register (1, &maskPR); - pr != NULL; - pr = pixel_regions_process (pr)) - { - data1 = data = maskPR.data; - ex = maskPR.x + maskPR.w; - ey = maskPR.y + maskPR.h; - /* only check the pixels if this tile is not fully within the currently - computed bounds */ - if (maskPR.x < tx1 || ex > tx2 || - maskPR.y < ty1 || ey > ty2) - { - /* Check upper left and lower right corners to see if we can - avoid checking the rest of the pixels in this tile */ - if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1]) - { - if (maskPR.x < tx1) - tx1 = maskPR.x; - if (ex > tx2) - tx2 = ex; - if (maskPR.y < ty1) - ty1 = maskPR.y; - if (ey > ty2) - ty2 = ey; - } - else - { - for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride) - { - for (x = maskPR.x, data = data1; x < ex; x++, data++) - { - if (*data) - { - minx = x; - maxx = x; - - for (; x < ex; x++, data++) - if (*data) - maxx = x; - - if (minx < tx1) - tx1 = minx; - if (maxx > tx2) - tx2 = maxx; - if (y < ty1) - ty1 = y; - if (y > ty2) - ty2 = y; - } - } - } - } - } - } - - tx2 = CLAMP (tx2 + 1, 0, GIMP_ITEM (mask)->width); - ty2 = CLAMP (ty2 + 1, 0, GIMP_ITEM (mask)->height); - - if (tx1 == GIMP_ITEM (mask)->width && ty1 == GIMP_ITEM (mask)->height) - { - mask->empty = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; - } - else - { - mask->empty = FALSE; - mask->x1 = tx1; - mask->y1 = ty1; - mask->x2 = tx2; - mask->y2 = ty2; - } - - mask->bounds_known = TRUE; - - *x1 = mask->x1; - *x2 = mask->x2; - *y1 = mask->y1; - *y2 = mask->y2; - - return !mask->empty; + return GIMP_CHANNEL_GET_CLASS (channel)->value (channel, x, y); } gboolean -gimp_channel_is_empty (GimpChannel *mask) +gimp_channel_is_empty (GimpChannel *channel) { - PixelRegion maskPR; - guchar *data; - gint x, y; - gpointer pr; + g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); - g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); - - if (mask->bounds_known) - return mask->empty; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, FALSE); - - for (pr = pixel_regions_register (1, &maskPR); - pr != NULL; - pr = pixel_regions_process (pr)) - { - /* check if any pixel in the mask is non-zero */ - data = maskPR.data; - - for (y = 0; y < maskPR.h; y++) - for (x = 0; x < maskPR.w; x++) - if (*data++) - { - pixel_regions_process_stop (pr); - return FALSE; - } - } - - /* The mask is empty, meaning we can set the bounds as known */ - if (mask->segs_in) - g_free (mask->segs_in); - if (mask->segs_out) - g_free (mask->segs_out); - - mask->empty = TRUE; - mask->segs_in = NULL; - mask->segs_out = NULL; - mask->num_segs_in = 0; - mask->num_segs_out = 0; - mask->bounds_known = TRUE; - mask->boundary_known = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; - - return TRUE; + return GIMP_CHANNEL_GET_CLASS (channel)->is_empty (channel); } void @@ -1578,309 +1976,89 @@ gimp_channel_combine_mask (GimpChannel *mask, } void -gimp_channel_feather (GimpChannel *mask, +gimp_channel_feather (GimpChannel *channel, gdouble radius_x, gdouble radius_y, gboolean push_undo) { - PixelRegion srcPR; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Feather Channel")); - - pixel_region_init (&srcPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - gimp_item_width (GIMP_ITEM (mask)), - gimp_item_height (GIMP_ITEM (mask)), - FALSE); - gaussian_blur_region (&srcPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->feather (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_sharpen (GimpChannel *mask, +gimp_channel_sharpen (GimpChannel *channel, gboolean push_undo) { - PixelRegion maskPR; - GimpLut *lut; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Sharpen Channel")); - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - gimp_item_width (GIMP_ITEM (mask)), - gimp_item_height (GIMP_ITEM (mask)), - TRUE); - lut = threshold_lut_new (0.5, 1); - - pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, - lut, 1, &maskPR); - gimp_lut_free (lut); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->sharpen (channel, push_undo); } void -gimp_channel_clear (GimpChannel *mask, +gimp_channel_clear (GimpChannel *channel, + const gchar *undo_desc, gboolean push_undo) { - PixelRegion maskPR; - guchar bg = TRANSPARENT_OPACITY; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Clear Channel")); - - if (mask->bounds_known && !mask->empty) - { - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - mask->x1, mask->y1, - (mask->x2 - mask->x1), (mask->y2 - mask->y1), TRUE); - color_region (&maskPR, &bg); - } - else - { - /* clear the mask */ - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - color_region (&maskPR, &bg); - } - - /* we know the bounds */ - mask->bounds_known = TRUE; - mask->empty = TRUE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; + GIMP_CHANNEL_GET_CLASS (channel)->clear (channel, undo_desc, push_undo); } void -gimp_channel_all (GimpChannel *mask, +gimp_channel_all (GimpChannel *channel, gboolean push_undo) { - PixelRegion maskPR; - guchar bg = OPAQUE_OPACITY; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (push_undo) - gimp_channel_push_undo (mask, _("Fill Channel")); - - /* clear the mask */ - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - color_region (&maskPR, &bg); - - /* we know the bounds */ - mask->bounds_known = TRUE; - mask->empty = FALSE; - mask->x1 = 0; - mask->y1 = 0; - mask->x2 = GIMP_ITEM (mask)->width; - mask->y2 = GIMP_ITEM (mask)->height; + GIMP_CHANNEL_GET_CLASS (channel)->all (channel, push_undo); } void -gimp_channel_invert (GimpChannel *mask, +gimp_channel_invert (GimpChannel *channel, gboolean push_undo) { - g_return_if_fail (GIMP_IS_CHANNEL (mask)); + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - if (push_undo) - gimp_channel_push_undo (mask, _("Invert Channel")); - - if (mask->bounds_known && mask->empty) - { - gimp_channel_all (mask, FALSE); - } - else - { - PixelRegion maskPR; - GimpLut *lut; - - pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, - 0, 0, - GIMP_ITEM (mask)->width, - GIMP_ITEM (mask)->height, TRUE); - - lut = invert_lut_new (1); - - pixel_regions_process_parallel ((p_func) gimp_lut_process_inline, lut, - 1, &maskPR); - - gimp_lut_free (lut); - - mask->bounds_known = FALSE; - } + GIMP_CHANNEL_GET_CLASS (channel)->invert (channel, push_undo); } void -gimp_channel_border (GimpChannel *mask, +gimp_channel_border (GimpChannel *channel, gint radius_x, gint radius_y, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 - radius_x < 0) - x1 = 0; - else - x1 -= radius_x; - if (x2 + radius_x > GIMP_ITEM (mask)->width) - x2 = GIMP_ITEM (mask)->width; - else - x2 += radius_x; - - if (y1 - radius_y < 0) - y1 = 0; - else - y1 -= radius_y; - if (y2 + radius_y > GIMP_ITEM (mask)->height) - y2 = GIMP_ITEM (mask)->height; - else - y2 += radius_y; - - if (push_undo) - gimp_channel_push_undo (mask, _("Border Channel")); - - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, - (x2-x1), (y2-y1), TRUE); - - border_region (&bPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->border (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_grow (GimpChannel *mask, +gimp_channel_grow (GimpChannel *channel, gint radius_x, gint radius_y, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x == 0 && radius_y == 0) - return; - - if (radius_x <= 0 && radius_y <= 0) - { - gimp_channel_shrink (mask, -radius_x, -radius_y, FALSE, push_undo); - return; - } - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 - radius_x > 0) - x1 = x1 - radius_x; - else - x1 = 0; - if (y1 - radius_y > 0) - y1 = y1 - radius_y; - else - y1 = 0; - if (x2 + radius_x < GIMP_ITEM (mask)->width) - x2 = x2 + radius_x; - else - x2 = GIMP_ITEM (mask)->width; - if (y2 + radius_y < GIMP_ITEM (mask)->height) - y2 = y2 + radius_y; - else - y2 = GIMP_ITEM (mask)->height; - - if (push_undo) - gimp_channel_push_undo (mask, _("Grow Channel")); - - /* need full extents for grow, not! */ - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1), - (y2 - y1), TRUE); - - fatten_region (&bPR, radius_x, radius_y); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->grow (channel, radius_x, radius_y, + push_undo); } void -gimp_channel_shrink (GimpChannel *mask, +gimp_channel_shrink (GimpChannel *channel, gint radius_x, gint radius_y, gboolean edge_lock, gboolean push_undo) { - PixelRegion bPR; - gint x1, y1, x2, y2; + g_return_if_fail (GIMP_IS_CHANNEL (channel)); - g_return_if_fail (GIMP_IS_CHANNEL (mask)); - - if (radius_x == 0 && radius_y == 0) - return; - - if (radius_x <= 0 && radius_y <= 0) - { - gimp_channel_grow (mask, -radius_x, -radius_y, push_undo); - return; - } - - if (radius_x < 0 || radius_y < 0) - return; - - if (! gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) - return; - - if (gimp_channel_is_empty (mask)) - return; - - if (x1 > 0) - x1--; - if (y1 > 0) - y1--; - if (x2 < GIMP_ITEM (mask)->width) - x2++; - if (y2 < GIMP_ITEM (mask)->height) - y2++; - - if (push_undo) - gimp_channel_push_undo (mask, _("Shrink Channel")); - - pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1), - (y2 - y1), TRUE); - - thin_region (&bPR, radius_x, radius_y, edge_lock); - - mask->bounds_known = FALSE; + GIMP_CHANNEL_GET_CLASS (channel)->shrink (channel, radius_x, radius_y, + edge_lock, push_undo); } void @@ -1909,11 +2087,3 @@ gimp_channel_load (GimpChannel *mask, mask->bounds_known = FALSE; } - -void -gimp_channel_invalidate_bounds (GimpChannel *channel) -{ - g_return_if_fail (GIMP_IS_CHANNEL (channel)); - - channel->bounds_known = FALSE; -} diff --git a/app/core/gimpchannel.h b/app/core/gimpchannel.h index 60a1f7c7ae..a4345bb309 100644 --- a/app/core/gimpchannel.h +++ b/app/core/gimpchannel.h @@ -59,131 +59,175 @@ struct _GimpChannel struct _GimpChannelClass { GimpDrawableClass parent_class; + + gboolean (* boundary) (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); + gboolean (* bounds) (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2); + gint (* value) (GimpChannel *channel, + gint x, + gint y); + gboolean (* is_empty) (GimpChannel *channel); + void (* feather) (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); + void (* sharpen) (GimpChannel *channel, + gboolean push_undo); + void (* clear) (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); + void (* all) (GimpChannel *channel, + gboolean push_undo); + void (* invert) (GimpChannel *channel, + gboolean push_undo); + void (* border) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); + void (* grow) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); + void (* shrink) (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); }; /* function declarations */ -GType gimp_channel_get_type (void) G_GNUC_CONST; +GType gimp_channel_get_type (void) G_GNUC_CONST; -GimpChannel * gimp_channel_new (GimpImage *gimage, - gint width, - gint height, - const gchar *name, - const GimpRGB *color); +GimpChannel * gimp_channel_new (GimpImage *gimage, + gint width, + gint height, + const gchar *name, + const GimpRGB *color); -GimpChannel * gimp_channel_new_from_alpha (GimpImage *gimage, - GimpLayer *layer, - const gchar *name, - const GimpRGB *color); -GimpChannel * gimp_channel_new_from_component (GimpImage *gimage, - GimpChannelType type, - const gchar *name, - const GimpRGB *color); +GimpChannel * gimp_channel_new_from_alpha (GimpImage *gimage, + GimpLayer *layer, + const gchar *name, + const GimpRGB *color); +GimpChannel * gimp_channel_new_from_component (GimpImage *gimage, + GimpChannelType type, + const gchar *name, + const GimpRGB *color); -gdouble gimp_channel_get_opacity (const GimpChannel *channel); -void gimp_channel_set_opacity (GimpChannel *channel, - gdouble opacity, - gboolean push_undo); +gdouble gimp_channel_get_opacity (const GimpChannel *channel); +void gimp_channel_set_opacity (GimpChannel *channel, + gdouble opacity, + gboolean push_undo); -void gimp_channel_get_color (const GimpChannel *channel, - GimpRGB *color); -void gimp_channel_set_color (GimpChannel *channel, - const GimpRGB *color, - gboolean push_undo); +void gimp_channel_get_color (const GimpChannel *channel, + GimpRGB *color); +void gimp_channel_set_color (GimpChannel *channel, + const GimpRGB *color, + gboolean push_undo); -gboolean gimp_channel_get_show_masked (GimpChannel *channel); -void gimp_channel_set_show_masked (GimpChannel *channel, - gboolean show_masked); +gboolean gimp_channel_get_show_masked (GimpChannel *channel); +void gimp_channel_set_show_masked (GimpChannel *channel, + gboolean show_masked); /* selection mask functions */ -GimpChannel * gimp_channel_new_mask (GimpImage *gimage, - gint width, - gint height); +GimpChannel * gimp_channel_new_mask (GimpImage *gimage, + gint width, + gint height); -gboolean gimp_channel_boundary (GimpChannel *mask, - const BoundSeg **segs_in, - const BoundSeg **segs_out, - gint *num_segs_in, - gint *num_segs_out, - gint x1, - gint y1, - gint x2, - gint y2); -gboolean gimp_channel_bounds (GimpChannel *mask, - gint *x1, - gint *y1, - gint *x2, - gint *y2); -gint gimp_channel_value (GimpChannel *mask, - gint x, - gint y); -gboolean gimp_channel_is_empty (GimpChannel *mask); +gboolean gimp_channel_boundary (GimpChannel *mask, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +gboolean gimp_channel_bounds (GimpChannel *mask, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +gint gimp_channel_value (GimpChannel *mask, + gint x, + gint y); +gboolean gimp_channel_is_empty (GimpChannel *mask); -void gimp_channel_add_segment (GimpChannel *mask, - gint x, - gint y, - gint width, - gint value); -void gimp_channel_sub_segment (GimpChannel *mask, - gint x, - gint y, - gint width, - gint value); -void gimp_channel_combine_rect (GimpChannel *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h); -void gimp_channel_combine_ellipse (GimpChannel *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h, - gboolean antialias); -void gimp_channel_combine_mask (GimpChannel *mask, - GimpChannel *add_on, - GimpChannelOps op, - gint off_x, - gint off_y); +void gimp_channel_add_segment (GimpChannel *mask, + gint x, + gint y, + gint width, + gint value); +void gimp_channel_sub_segment (GimpChannel *mask, + gint x, + gint y, + gint width, + gint value); +void gimp_channel_combine_rect (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h); +void gimp_channel_combine_ellipse (GimpChannel *mask, + GimpChannelOps op, + gint x, + gint y, + gint w, + gint h, + gboolean antialias); +void gimp_channel_combine_mask (GimpChannel *mask, + GimpChannel *add_on, + GimpChannelOps op, + gint off_x, + gint off_y); -void gimp_channel_feather (GimpChannel *mask, - gdouble radius_x, - gdouble radius_y, - gboolean push_undo); -void gimp_channel_sharpen (GimpChannel *mask, - gboolean push_undo); +void gimp_channel_feather (GimpChannel *mask, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); +void gimp_channel_sharpen (GimpChannel *mask, + gboolean push_undo); -void gimp_channel_clear (GimpChannel *mask, - gboolean push_undo); -void gimp_channel_all (GimpChannel *mask, - gboolean push_undo); -void gimp_channel_invert (GimpChannel *mask, - gboolean push_undo); +void gimp_channel_clear (GimpChannel *mask, + const gchar *undo_desc, + gboolean push_undo); +void gimp_channel_all (GimpChannel *mask, + gboolean push_undo); +void gimp_channel_invert (GimpChannel *mask, + gboolean push_undo); -void gimp_channel_border (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean push_undo); -void gimp_channel_grow (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean push_undo); -void gimp_channel_shrink (GimpChannel *mask, - gint radius_x, - gint radius_y, - gboolean edge_lock, - gboolean push_undo); +void gimp_channel_border (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean push_undo); +void gimp_channel_grow (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean push_undo); +void gimp_channel_shrink (GimpChannel *mask, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); -void gimp_channel_load (GimpChannel *mask, - GimpChannel *channel, - gboolean push_undo); - -void gimp_channel_invalidate_bounds (GimpChannel *channel); +void gimp_channel_load (GimpChannel *mask, + GimpChannel *channel, + gboolean push_undo); #endif /* __GIMP_CHANNEL_H__ */ diff --git a/app/core/gimpimage-mask.c b/app/core/gimpimage-mask.c index 2ac4da7029..2b3caaadf0 100644 --- a/app/core/gimpimage-mask.c +++ b/app/core/gimpimage-mask.c @@ -28,8 +28,6 @@ #include "paint-funcs/paint-funcs.h" -#include "paint/gimppaintcore-stroke.h" - #include "gimp.h" #include "gimpchannel.h" #include "gimpcontainer.h" @@ -41,7 +39,7 @@ #include "gimplayer.h" #include "gimplayer-floating-sel.h" #include "gimplayermask.h" -#include "gimppaintinfo.h" +#include "gimpselection.h" #include "gimp-intl.h" @@ -55,83 +53,14 @@ gimp_image_mask_boundary (GimpImage *gimage, gint *num_segs_in, gint *num_segs_out) { - GimpDrawable *drawable; - GimpLayer *layer; - g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); - g_return_val_if_fail (segs_in != NULL, FALSE); - g_return_val_if_fail (segs_out != NULL, FALSE); - g_return_val_if_fail (num_segs_in != NULL, FALSE); - g_return_val_if_fail (num_segs_out != NULL, FALSE); - if ((layer = gimp_image_floating_sel (gimage))) - { - /* If there is a floating selection, then - * we need to do some slightly different boundaries. - * Instead of inside and outside boundaries being defined - * by the extents of the layer, the inside boundary (the one - * that actually marches and is black/white) is the boundary of - * the floating selection. The outside boundary (doesn't move, - * is black/gray) is defined as the normal selection mask - */ - - /* Find the selection mask boundary */ - gimp_channel_boundary (gimp_image_get_mask (gimage), - segs_in, segs_out, - num_segs_in, num_segs_out, - 0, 0, 0, 0); - - /* Find the floating selection boundary */ - *segs_in = floating_sel_boundary (layer, num_segs_in); - - return TRUE; - } - else if ((drawable = gimp_image_active_drawable (gimage)) && - GIMP_IS_CHANNEL (drawable)) - { - /* Otherwise, return the boundary...if a channel is active */ - - return gimp_channel_boundary (gimp_image_get_mask (gimage), - segs_in, segs_out, - num_segs_in, num_segs_out, - 0, 0, gimage->width, gimage->height); - } - else if ((layer = gimp_image_get_active_layer (gimage))) - { - /* If a layer is active, we return multiple boundaries based - * on the extents - */ - - gint x1, y1; - gint x2, y2; - gint off_x, off_y; - - gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); - - x1 = CLAMP (off_x, 0, gimage->width); - y1 = CLAMP (off_y, 0, gimage->height); - x2 = CLAMP (off_x + gimp_item_width (GIMP_ITEM (layer)), 0, - gimage->width); - y2 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), 0, - gimage->height); - - return gimp_channel_boundary (gimp_image_get_mask (gimage), - segs_in, segs_out, - num_segs_in, num_segs_out, - x1, y1, x2, y2); - } - else - { - *segs_in = NULL; - *segs_out = NULL; - *num_segs_in = 0; - *num_segs_out = 0; - - return FALSE; - } + return gimp_channel_boundary (gimp_image_get_mask (gimage), + segs_in, segs_out, + num_segs_in, num_segs_out, + 0, 0, 0, 0); } - gboolean gimp_image_mask_bounds (GimpImage *gimage, gint *x1, @@ -139,61 +68,160 @@ gimp_image_mask_bounds (GimpImage *gimage, gint *x2, gint *y2) { + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); + return gimp_channel_bounds (gimp_image_get_mask (gimage), x1, y1, x2, y2); } - -void -gimp_image_mask_invalidate (GimpImage *gimage) -{ - GimpLayer *layer; - GimpChannel *mask; - - /* Turn the current selection off */ - gimp_image_selection_control (gimage, GIMP_SELECTION_OFF); - - mask = gimp_image_get_mask (gimage); - mask->boundary_known = FALSE; - - /* If there is a floating selection, update it's area... - * we need to do this since this selection mask can act as an additional - * mask in the composition of the floating selection - */ - layer = gimp_image_get_active_layer (gimage); - - if (layer && gimp_layer_is_floating_sel (layer)) - gimp_drawable_update (GIMP_DRAWABLE (layer), - 0, 0, - GIMP_ITEM (layer)->width, - GIMP_ITEM (layer)->height); - - /* invalidate the preview */ - GIMP_DRAWABLE (mask)->preview_valid = FALSE; -} - - gint gimp_image_mask_value (GimpImage *gimage, gint x, gint y) { + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), 0); + return gimp_channel_value (gimp_image_get_mask (gimage), x, y); } - gboolean gimp_image_mask_is_empty (GimpImage *gimage) { - /* in order to allow stroking of selections, we need to pretend here - * that the selection mask is empty so that it doesn't mask the paint - * during the stroke operation. - */ - if (gimage->mask_stroking) - return TRUE; - else - return gimp_channel_is_empty (gimp_image_get_mask (gimage)); + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); + + return gimp_channel_is_empty (gimp_image_get_mask (gimage)); } +void +gimp_image_mask_feather (GimpImage *gimage, + gdouble feather_radius_x, + gdouble feather_radius_y) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_feather (gimp_image_get_mask (gimage), + feather_radius_x, + feather_radius_y, + TRUE); +} + +void +gimp_image_mask_sharpen (GimpImage *gimage) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_sharpen (gimp_image_get_mask (gimage), TRUE); +} + +void +gimp_image_mask_clear (GimpImage *gimage, + const gchar *undo_desc) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_clear (gimp_image_get_mask (gimage), undo_desc, TRUE); +} + +void +gimp_image_mask_all (GimpImage *gimage) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_all (gimp_image_get_mask (gimage), TRUE); +} + +void +gimp_image_mask_invert (GimpImage *gimage) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_invert (gimp_image_get_mask (gimage), TRUE); +} + +void +gimp_image_mask_border (GimpImage *gimage, + gint border_radius_x, + gint border_radius_y) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_border (gimp_image_get_mask (gimage), + border_radius_x, + border_radius_y, + TRUE); +} + +void +gimp_image_mask_grow (GimpImage *gimage, + gint grow_pixels_x, + gint grow_pixels_y) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_grow (gimp_image_get_mask (gimage), + grow_pixels_x, + grow_pixels_y, + TRUE); +} + +void +gimp_image_mask_shrink (GimpImage *gimage, + gint shrink_pixels_x, + gint shrink_pixels_y, + gboolean edge_lock) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_channel_shrink (gimp_image_get_mask (gimage), + shrink_pixels_x, + shrink_pixels_y, + edge_lock, + TRUE); +} + +void +gimp_image_mask_translate (GimpImage *gimage, + gint off_x, + gint off_y, + gboolean push_undo) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_item_translate (GIMP_ITEM (gimp_image_get_mask (gimage)), + off_x, off_y, TRUE); +} + +gboolean +gimp_image_mask_stroke (GimpImage *gimage, + GimpDrawable *drawable, + GimpPaintInfo *paint_info) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); + + return gimp_item_stroke (GIMP_ITEM (gimp_image_get_mask (gimage)), + drawable, paint_info); +} + +void +gimp_image_mask_push_undo (GimpImage *gimage, + const gchar *undo_desc) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_selection_push_undo (GIMP_SELECTION (gimp_image_get_mask (gimage)), + undo_desc); +} + +void +gimp_image_mask_invalidate (GimpImage *gimage) +{ + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_selection_invalidate (GIMP_SELECTION (gimp_image_get_mask (gimage))); +} + + + + TileManager * gimp_image_mask_extract (GimpImage *gimage, @@ -421,163 +449,6 @@ gimp_image_mask_float (GimpImage *gimage, return layer; } - -void -gimp_image_mask_push_undo (GimpImage *gimage, - const gchar *undo_desc) -{ - GimpChannel *mask; - - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - mask = gimp_image_get_mask (gimage); - - gimp_image_undo_push_mask (gimage, undo_desc, mask); - - gimp_image_mask_invalidate (gimage); -} - -void -gimp_image_mask_feather (GimpImage *gimage, - gdouble feather_radius_x, - gdouble feather_radius_y) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Feather Selection")); - - gimp_channel_feather (gimp_image_get_mask (gimage), - feather_radius_x, - feather_radius_y, - FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_sharpen (GimpImage *gimage) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Sharpen Selection")); - - gimp_channel_sharpen (gimp_image_get_mask (gimage), FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_clear (GimpImage *gimage, - const gchar *undo_desc) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - if (! undo_desc) - undo_desc = _("Select None"); - - gimp_image_mask_push_undo (gimage, undo_desc); - - gimp_channel_clear (gimp_image_get_mask (gimage), FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_all (GimpImage *gimage) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Select All")); - - gimp_channel_all (gimp_image_get_mask (gimage), FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_invert (GimpImage *gimage) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Invert Selection")); - - gimp_channel_invert (gimp_image_get_mask (gimage), FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_border (GimpImage *gimage, - gint border_radius_x, - gint border_radius_y) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Border Selection")); - - gimp_channel_border (gimp_image_get_mask (gimage), - border_radius_x, - border_radius_y, - FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_grow (GimpImage *gimage, - gint grow_pixels_x, - gint grow_pixels_y) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Grow Selection")); - - gimp_channel_grow (gimp_image_get_mask (gimage), - grow_pixels_x, - grow_pixels_y, - FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_shrink (GimpImage *gimage, - gint shrink_pixels_x, - gint shrink_pixels_y, - gboolean edge_lock) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - gimp_image_mask_push_undo (gimage, _("Shrink Selection")); - - gimp_channel_shrink (gimp_image_get_mask (gimage), - shrink_pixels_x, - shrink_pixels_y, - edge_lock, - FALSE); - - gimp_image_mask_changed (gimage); -} - -void -gimp_image_mask_translate (GimpImage *gimage, - gint off_x, - gint off_y, - gboolean push_undo) -{ - g_return_if_fail (GIMP_IS_IMAGE (gimage)); - - if (push_undo) - gimp_image_mask_push_undo (gimage, _("Move Selection")); - else - gimp_image_mask_invalidate (gimage); - - gimp_item_translate (GIMP_ITEM (gimp_image_get_mask (gimage)), - off_x, off_y, FALSE); - - gimp_image_mask_changed (gimage); -} - void gimp_image_mask_load (GimpImage *gimage, GimpChannel *channel) @@ -604,7 +475,7 @@ gimp_image_mask_save (GimpImage *gimage) mask = gimp_image_get_mask (gimage); new_channel = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (mask), - G_TYPE_FROM_INSTANCE (mask), + GIMP_TYPE_CHANNEL, FALSE)); /* saved selections are not visible by default */ @@ -614,47 +485,3 @@ gimp_image_mask_save (GimpImage *gimage) return new_channel; } - -gboolean -gimp_image_mask_stroke (GimpImage *gimage, - GimpDrawable *drawable, - GimpPaintInfo *paint_info) -{ - const BoundSeg *bs_in; - const BoundSeg *bs_out; - gint num_segs_in; - gint num_segs_out; - GimpPaintCore *core; - gboolean retval; - - g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); - g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); - g_return_val_if_fail (GIMP_IS_PAINT_INFO (paint_info), FALSE); - - if (! gimp_image_mask_boundary (gimage, &bs_in, &bs_out, - &num_segs_in, &num_segs_out)) - { - g_message (_("No selection to stroke.")); - return FALSE; - } - - gimage->mask_stroking = TRUE; - - gimp_image_undo_group_start (gimage, GIMP_UNDO_GROUP_PAINT, - _("Stroke Selection")); - - core = g_object_new (paint_info->paint_type, NULL); - - retval = gimp_paint_core_stroke_boundary (core, drawable, - paint_info->paint_options, - bs_in, num_segs_in, - 0, 0); - - g_object_unref (core); - - gimp_image_undo_group_end (gimage); - - gimage->mask_stroking = FALSE; - - return retval; -} diff --git a/app/core/gimpimage-mask.h b/app/core/gimpimage-mask.h index fa17d83b45..04cd17ecb6 100644 --- a/app/core/gimpimage-mask.h +++ b/app/core/gimpimage-mask.h @@ -20,26 +20,63 @@ #define __GIMP_IMAGE_MASK_H__ +/* pure wrappers around the resp. GimpChannel::foo() functions: */ + gboolean gimp_image_mask_boundary (GimpImage *gimage, const BoundSeg **segs_in, const BoundSeg **segs_out, gint *num_segs_in, gint *num_segs_out); - gboolean gimp_image_mask_bounds (GimpImage *gimage, gint *x1, gint *y1, gint *x2, gint *y2); - -void gimp_image_mask_invalidate (GimpImage *gimage); - gint gimp_image_mask_value (GimpImage *gimage, gint x, gint y); - gboolean gimp_image_mask_is_empty (GimpImage *gimage); +void gimp_image_mask_feather (GimpImage *gimage, + gdouble feather_radius_x, + gdouble feather_radius_y); +void gimp_image_mask_sharpen (GimpImage *gimage); +void gimp_image_mask_clear (GimpImage *gimage, + const gchar *undo_name); +void gimp_image_mask_all (GimpImage *gimage); +void gimp_image_mask_invert (GimpImage *gimage); +void gimp_image_mask_border (GimpImage *gimage, + gint border_radius_x, + gint border_radius_y); +void gimp_image_mask_grow (GimpImage *gimage, + gint grow_pixels_x, + gint grow_pixels_y); +void gimp_image_mask_shrink (GimpImage *gimage, + gint shrink_pixels_x, + gint shrink_pixels_y, + gboolean edge_lock); + + +/* pure wrappers around the resp. GimpItem::foo() functions: */ + +void gimp_image_mask_translate (GimpImage *gimage, + gint off_x, + gint off_y, + gboolean push_undo); +gboolean gimp_image_mask_stroke (GimpImage *gimage, + GimpDrawable *drawable, + GimpPaintInfo *paint_info); + + +/* pure wrappers around the resp. GimpSelection functions: */ + +void gimp_image_mask_push_undo (GimpImage *gimage, + const gchar *undo_desc); +void gimp_image_mask_invalidate (GimpImage *gimage); + + +/* really implemented here: */ + TileManager * gimp_image_mask_extract (GimpImage *gimage, GimpDrawable *drawable, gboolean cut_image, @@ -52,44 +89,9 @@ GimpLayer * gimp_image_mask_float (GimpImage *gimage, gint off_x, gint off_y); -void gimp_image_mask_push_undo (GimpImage *gimage, - const gchar *undo_desc); - -void gimp_image_mask_feather (GimpImage *gimage, - gdouble feather_radius_x, - gdouble feather_radius_y); -void gimp_image_mask_sharpen (GimpImage *gimage); - -void gimp_image_mask_clear (GimpImage *gimage, - const gchar *undo_name); -void gimp_image_mask_all (GimpImage *gimage); -void gimp_image_mask_invert (GimpImage *gimage); - -void gimp_image_mask_border (GimpImage *gimage, - gint border_radius_x, - gint border_radius_y); - -void gimp_image_mask_grow (GimpImage *gimage, - gint grow_pixels_x, - gint grow_pixels_y); - -void gimp_image_mask_shrink (GimpImage *gimage, - gint shrink_pixels_x, - gint shrink_pixels_y, - gboolean edge_lock); - -void gimp_image_mask_translate (GimpImage *gimage, - gint off_x, - gint off_y, - gboolean push_undo); - void gimp_image_mask_load (GimpImage *gimage, GimpChannel *channel); GimpChannel * gimp_image_mask_save (GimpImage *gimage); -gboolean gimp_image_mask_stroke (GimpImage *gimage, - GimpDrawable *drawable, - GimpPaintInfo *paint_info); - #endif /* __GIMP_IMAGE_MASK_H__ */ diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c index 36c64da162..8ae5bc4560 100644 --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -54,6 +54,7 @@ #include "gimplist.h" #include "gimpmarshal.h" #include "gimpparasitelist.h" +#include "gimpselection.h" #include "gimpundostack.h" #include "file/file-utils.h" @@ -483,7 +484,6 @@ gimp_image_init (GimpImage *gimage) gimage->floating_sel = NULL; gimage->selection_mask = NULL; - gimage->mask_stroking = FALSE; gimage->parasites = gimp_parasite_list_new (); @@ -851,9 +851,9 @@ gimp_image_new (Gimp *gimp, } /* create the selection mask */ - gimage->selection_mask = gimp_channel_new_mask (gimage, - gimage->width, - gimage->height); + gimage->selection_mask = gimp_selection_new (gimage, + gimage->width, + gimage->height); g_signal_connect_object (gimp->config, "notify::transparency-type", G_CALLBACK (gimp_image_invalidate_layer_previews), diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h index cfc41f6db9..2a71cebade 100644 --- a/app/core/gimpimage.h +++ b/app/core/gimpimage.h @@ -138,7 +138,6 @@ struct _GimpImage GimpLayer *floating_sel; /* the FS layer */ GimpChannel *selection_mask; /* the selection mask channel */ - gboolean mask_stroking; /* is a stroke being done */ GimpParasiteList *parasites; /* Plug-in parasite data */ diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c index 922392e0c5..e80f7e5ca7 100644 --- a/app/core/gimplayer.c +++ b/app/core/gimplayer.c @@ -925,7 +925,7 @@ gimp_layer_create_mask (const GimpLayer *layer, return mask; case GIMP_ADD_BLACK_MASK: - gimp_channel_clear (GIMP_CHANNEL (mask), FALSE); + gimp_channel_clear (GIMP_CHANNEL (mask), NULL, FALSE); return mask; default: @@ -973,7 +973,7 @@ gimp_layer_create_mask (const GimpLayer *layer, if (copy_width < item->width || copy_height < item->height || selection_empty) - gimp_channel_clear (GIMP_CHANNEL (mask), FALSE); + gimp_channel_clear (GIMP_CHANNEL (mask), NULL, FALSE); if ((copy_width || copy_height) && ! selection_empty) { diff --git a/app/core/gimpscanconvert.c b/app/core/gimpscanconvert.c index e5a42dccbe..be2c43faeb 100644 --- a/app/core/gimpscanconvert.c +++ b/app/core/gimpscanconvert.c @@ -255,5 +255,7 @@ gimp_scan_convert_to_channel (GimpScanConvert *sc, art_free (svp); art_free (pert_vpath); + mask->bounds_known = FALSE; + return mask; } diff --git a/app/core/gimpselection.c b/app/core/gimpselection.c new file mode 100644 index 0000000000..beeee6d204 --- /dev/null +++ b/app/core/gimpselection.c @@ -0,0 +1,586 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include "core-types.h" + +#include "base/tile.h" +#include "base/tile-manager.h" + +#include "gimpimage.h" +#include "gimpimage-undo.h" +#include "gimpimage-undo-push.h" +#include "gimplayer.h" +#include "gimplayer-floating-sel.h" +#include "gimpselection.h" + +#include "gimp-intl.h" + + +static void gimp_selection_class_init (GimpSelectionClass *klass); +static void gimp_selection_init (GimpSelection *selection); + +static void gimp_selection_translate (GimpItem *item, + gint offset_x, + gint offset_y, + gboolean push_undo); +static gboolean gimp_selection_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpPaintInfo *paint_info); + +static gboolean gimp_selection_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint x1, + gint y1, + gint x2, + gint y2); +static gboolean gimp_selection_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2); +static gint gimp_selection_value (GimpChannel *channel, + gint x, + gint y); +static gboolean gimp_selection_is_empty (GimpChannel *channel); +static void gimp_selection_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo); +static void gimp_selection_sharpen (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo); +static void gimp_selection_all (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_invert (GimpChannel *channel, + gboolean push_undo); +static void gimp_selection_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_selection_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo); +static void gimp_selection_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo); + +static void gimp_selection_changed (GimpSelection *selection); +static void gimp_selection_validate (TileManager *tm, + Tile *tile); + + +static GimpChannelClass *parent_class = NULL; + + +GType +gimp_selection_get_type (void) +{ + static GType selection_type = 0; + + if (! selection_type) + { + static const GTypeInfo selection_info = + { + sizeof (GimpSelectionClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gimp_selection_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GimpSelection), + 0, /* n_preallocs */ + (GInstanceInitFunc) gimp_selection_init, + }; + + selection_type = g_type_register_static (GIMP_TYPE_CHANNEL, + "GimpSelection", + &selection_info, 0); + } + + return selection_type; +} + +static void +gimp_selection_class_init (GimpSelectionClass *klass) +{ + GimpItemClass *item_class; + GimpChannelClass *channel_class; + + item_class = GIMP_ITEM_CLASS (klass); + channel_class = GIMP_CHANNEL_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + item_class->translate = gimp_selection_translate; + item_class->stroke = gimp_selection_stroke; + + channel_class->boundary = gimp_selection_boundary; + channel_class->bounds = gimp_selection_bounds; + channel_class->value = gimp_selection_value; + channel_class->is_empty = gimp_selection_is_empty; + channel_class->feather = gimp_selection_feather; + channel_class->sharpen = gimp_selection_sharpen; + channel_class->clear = gimp_selection_clear; + channel_class->all = gimp_selection_all; + channel_class->invert = gimp_selection_invert; + channel_class->border = gimp_selection_border; + channel_class->grow = gimp_selection_grow; + channel_class->shrink = gimp_selection_shrink; +} + +static void +gimp_selection_init (GimpSelection *selection) +{ + selection->stroking = FALSE; +} + +static void +gimp_selection_translate (GimpItem *item, + gint offset_x, + gint offset_y, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (item); + + if (push_undo) + gimp_selection_push_undo (selection, _("Move Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_ITEM_CLASS (parent_class)->translate (item, offset_x, offset_y, FALSE); + + gimp_selection_changed (selection); +} + +static gboolean +gimp_selection_stroke (GimpItem *item, + GimpDrawable *drawable, + GimpPaintInfo *paint_info) +{ + GimpSelection *selection; + GimpImage *gimage; + const BoundSeg *dummy_in; + const BoundSeg *dummy_out; + gint num_dummy_in; + gint num_dummy_out; + gboolean retval; + + selection = GIMP_SELECTION (item); + + if (! gimp_channel_boundary (GIMP_CHANNEL (selection), + &dummy_in, &dummy_out, + &num_dummy_in, &num_dummy_out, + 0, 0, 0, 0)) + { + g_message (_("No selection to stroke.")); + return FALSE; + } + + gimage = gimp_item_get_image (item); + + selection->stroking = TRUE; + + gimp_image_undo_group_start (gimage, GIMP_UNDO_GROUP_PAINT, + _("Stroke Selection")); + + retval = GIMP_ITEM_CLASS (parent_class)->stroke (item, drawable, paint_info); + + gimp_image_undo_group_end (gimage); + + selection->stroking = FALSE; + + return retval; +} + +static gboolean +gimp_selection_boundary (GimpChannel *channel, + const BoundSeg **segs_in, + const BoundSeg **segs_out, + gint *num_segs_in, + gint *num_segs_out, + gint unused1, + gint unused2, + gint unused3, + gint unused4) +{ + GimpImage *gimage; + GimpDrawable *drawable; + GimpLayer *layer; + + gimage = gimp_item_get_image (GIMP_ITEM (channel)); + + if ((layer = gimp_image_floating_sel (gimage))) + { + /* If there is a floating selection, then + * we need to do some slightly different boundaries. + * Instead of inside and outside boundaries being defined + * by the extents of the layer, the inside boundary (the one + * that actually marches and is black/white) is the boundary of + * the floating selection. The outside boundary (doesn't move, + * is black/gray) is defined as the normal selection mask + */ + + /* Find the selection mask boundary */ + GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, num_segs_out, + 0, 0, 0, 0); + + /* Find the floating selection boundary */ + *segs_in = floating_sel_boundary (layer, num_segs_in); + + return TRUE; + } + else if ((drawable = gimp_image_active_drawable (gimage)) && + GIMP_IS_CHANNEL (drawable)) + { + /* Otherwise, return the boundary...if a channel is active */ + + return GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, + num_segs_out, + 0, 0, + gimage->width, + gimage->height); + } + else if ((layer = gimp_image_get_active_layer (gimage))) + { + /* If a layer is active, we return multiple boundaries based + * on the extents + */ + + gint x1, y1; + gint x2, y2; + gint off_x, off_y; + + gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); + + x1 = CLAMP (off_x, 0, gimage->width); + y1 = CLAMP (off_y, 0, gimage->height); + x2 = CLAMP (off_x + gimp_item_width (GIMP_ITEM (layer)), 0, + gimage->width); + y2 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), 0, + gimage->height); + + return GIMP_CHANNEL_CLASS (parent_class)->boundary (channel, + segs_in, segs_out, + num_segs_in, + num_segs_out, + x1, y1, x2, y2); + } + + *segs_in = NULL; + *segs_out = NULL; + *num_segs_in = 0; + *num_segs_out = 0; + + return FALSE; +} + +static gboolean +gimp_selection_bounds (GimpChannel *channel, + gint *x1, + gint *y1, + gint *x2, + gint *y2) +{ + return GIMP_CHANNEL_CLASS (parent_class)->bounds (channel, x1, y1, x2, y2); +} + +static gint +gimp_selection_value (GimpChannel *channel, + gint x, + gint y) +{ + return GIMP_CHANNEL_CLASS (parent_class)->value (channel, x, y); +} + +static gboolean +gimp_selection_is_empty (GimpChannel *channel) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + /* in order to allow stroking of selections, we need to pretend here + * that the selection mask is empty so that it doesn't mask the paint + * during the stroke operation. + */ + if (selection->stroking) + return TRUE; + + return GIMP_CHANNEL_CLASS (parent_class)->is_empty (channel); +} + +static void +gimp_selection_feather (GimpChannel *channel, + gdouble radius_x, + gdouble radius_y, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Feather Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->feather (channel, radius_x, radius_y, + FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_sharpen (GimpChannel *channel, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Sharpen Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->sharpen (channel, FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_clear (GimpChannel *channel, + const gchar *undo_desc, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + { + if (! undo_desc) + undo_desc = _("Select None"); + + gimp_selection_push_undo (selection, undo_desc); + } + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->clear (channel, NULL, FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_all (GimpChannel *channel, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Select All")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->all (channel, FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_invert (GimpChannel *channel, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Invert Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->invert (channel, FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_border (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Border Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->border (channel, radius_x, radius_y, + FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_grow (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Grow Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->grow (channel, radius_x, radius_y, + FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_shrink (GimpChannel *channel, + gint radius_x, + gint radius_y, + gboolean edge_lock, + gboolean push_undo) +{ + GimpSelection *selection = GIMP_SELECTION (channel); + + if (push_undo) + gimp_selection_push_undo (selection, _("Shrink Selection")); + else + gimp_selection_invalidate (selection); + + GIMP_CHANNEL_CLASS (parent_class)->shrink (channel, radius_x, radius_y, + edge_lock, FALSE); + + gimp_selection_changed (selection); +} + +static void +gimp_selection_changed (GimpSelection *selection) +{ + gimp_image_mask_changed (gimp_item_get_image (GIMP_ITEM (selection))); +} + +static void +gimp_selection_validate (TileManager *tm, + Tile *tile) +{ + /* Set the contents of the tile to empty */ + memset (tile_data_pointer (tile, 0, 0), + TRANSPARENT_OPACITY, tile_size (tile)); +} + + +/* public functions */ + +GimpChannel * +gimp_selection_new (GimpImage *gimage, + gint width, + gint height) +{ + GimpRGB black = { 0.0, 0.0, 0.0, 0.5 }; + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); + + channel = g_object_new (GIMP_TYPE_SELECTION, NULL); + + gimp_drawable_configure (GIMP_DRAWABLE (channel), + gimage, + 0, 0, width, height, + GIMP_GRAY_IMAGE, + _("Selection Mask")); + + channel->color = black; + channel->show_masked = TRUE; + channel->x2 = width; + channel->y2 = height; + + /* Set the validate procedure */ + tile_manager_set_validate_proc (GIMP_DRAWABLE (channel)->tiles, + gimp_selection_validate); + + return channel; +} + +void +gimp_selection_push_undo (GimpSelection *selection, + const gchar *undo_desc) +{ + GimpImage *gimage; + + g_return_if_fail (GIMP_IS_SELECTION (selection)); + + gimage = gimp_item_get_image (GIMP_ITEM (selection)); + + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + gimp_image_undo_push_mask (gimage, undo_desc, GIMP_CHANNEL (selection)); + + gimp_selection_invalidate (selection); +} + +void +gimp_selection_invalidate (GimpSelection *selection) +{ + GimpLayer *layer; + GimpImage *gimage; + + g_return_if_fail (GIMP_IS_SELECTION (selection)); + + gimage = gimp_item_get_image (GIMP_ITEM (selection)); + + g_return_if_fail (GIMP_IS_IMAGE (gimage)); + + /* Turn the current selection off */ + gimp_image_selection_control (gimage, GIMP_SELECTION_OFF); + + GIMP_CHANNEL (selection)->boundary_known = FALSE; + + /* If there is a floating selection, update it's area... + * we need to do this since this selection mask can act as an additional + * mask in the composition of the floating selection + */ + layer = gimp_image_get_active_layer (gimage); + + if (layer && gimp_layer_is_floating_sel (layer)) + gimp_drawable_update (GIMP_DRAWABLE (layer), + 0, 0, + GIMP_ITEM (layer)->width, + GIMP_ITEM (layer)->height); + + /* invalidate the preview */ + GIMP_DRAWABLE (selection)->preview_valid = FALSE; +} diff --git a/app/core/gimpselection.h b/app/core/gimpselection.h new file mode 100644 index 0000000000..ff6fa47221 --- /dev/null +++ b/app/core/gimpselection.h @@ -0,0 +1,59 @@ +/* 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. + */ + +#ifndef __GIMP_SELECTION_H__ +#define __GIMP_SELECTION_H__ + + +#include "gimpchannel.h" + + +#define GIMP_TYPE_SELECTION (gimp_selection_get_type ()) +#define GIMP_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SELECTION, GimpSelection)) +#define GIMP_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SELECTION, GimpSelectionClass)) +#define GIMP_IS_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SELECTION)) +#define GIMP_IS_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SELECTION)) +#define GIMP_SELECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SELECTION, GimpSelectionClass)) + + +typedef struct _GimpSelectionClass GimpSelectionClass; + +struct _GimpSelection +{ + GimpChannel parent_instance; + + gboolean stroking; +}; + +struct _GimpSelectionClass +{ + GimpChannelClass parent_class; +}; + + +GType gimp_selection_get_type (void) G_GNUC_CONST; + +GimpChannel * gimp_selection_new (GimpImage *gimage, + gint width, + gint height); +void gimp_selection_push_undo (GimpSelection *selection, + const gchar *undo_desc); +void gimp_selection_invalidate (GimpSelection *selection); + + +#endif /* __GIMP_SELECTION_H__ */ diff --git a/app/tools/gimpiscissorstool.c b/app/tools/gimpiscissorstool.c index 04dfc2e23e..c7b132b0f9 100644 --- a/app/tools/gimpiscissorstool.c +++ b/app/tools/gimpiscissorstool.c @@ -568,8 +568,6 @@ iscissors_convert (GimpIscissorsTool *iscissors, iscissors->mask = gimp_scan_convert_to_channel (sc, gdisp->gimage); gimp_scan_convert_free (sc); - - gimp_channel_invalidate_bounds (iscissors->mask); } static void diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c index 1f70b7d10f..6c14d0f672 100644 --- a/app/xcf/xcf-load.c +++ b/app/xcf/xcf-load.c @@ -35,7 +35,6 @@ #include "config/gimpcoreconfig.h" #include "core/gimp.h" -#include "core/gimpchannel.h" #include "core/gimpcontainer.h" #include "core/gimpdrawable.h" #include "core/gimpimage.h" @@ -45,6 +44,7 @@ #include "core/gimplayer-floating-sel.h" #include "core/gimplayermask.h" #include "core/gimpparasitelist.h" +#include "core/gimpselection.h" #include "core/gimpunit.h" #include "text/gimptextlayer.h" @@ -61,46 +61,46 @@ #include "gimp-intl.h" -static gboolean xcf_load_image_props (XcfInfo *info, - GimpImage *gimage); -static gboolean xcf_load_layer_props (XcfInfo *info, - GimpImage *gimage, - GimpLayer *layer, - gboolean *apply_mask, - gboolean *edit_mask, - gboolean *show_mask); -static gboolean xcf_load_channel_props (XcfInfo *info, - GimpImage *gimage, - GimpChannel *channel); -static gboolean xcf_load_prop (XcfInfo *info, - PropType *prop_type, - guint32 *prop_size); -static GimpLayer * xcf_load_layer (XcfInfo *info, - GimpImage *gimage); -static GimpChannel * xcf_load_channel (XcfInfo *info, - GimpImage *gimage); -static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info, - GimpImage *gimage); -static gboolean xcf_load_hierarchy (XcfInfo *info, - TileManager *tiles); -static gboolean xcf_load_level (XcfInfo *info, - TileManager *tiles); -static gboolean xcf_load_tile (XcfInfo *info, - Tile *tile); -static gboolean xcf_load_tile_rle (XcfInfo *info, - Tile *tile, - gint data_length); -static GimpParasite * xcf_load_parasite (XcfInfo *info); -static gboolean xcf_load_old_paths (XcfInfo *info, - GimpImage *gimage); -static gboolean xcf_load_old_path (XcfInfo *info, - GimpImage *gimage); +static gboolean xcf_load_image_props (XcfInfo *info, + GimpImage *gimage); +static gboolean xcf_load_layer_props (XcfInfo *info, + GimpImage *gimage, + GimpLayer *layer, + gboolean *apply_mask, + gboolean *edit_mask, + gboolean *show_mask); +static gboolean xcf_load_channel_props (XcfInfo *info, + GimpImage *gimage, + GimpChannel **channel); +static gboolean xcf_load_prop (XcfInfo *info, + PropType *prop_type, + guint32 *prop_size); +static GimpLayer * xcf_load_layer (XcfInfo *info, + GimpImage *gimage); +static GimpChannel * xcf_load_channel (XcfInfo *info, + GimpImage *gimage); +static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info, + GimpImage *gimage); +static gboolean xcf_load_hierarchy (XcfInfo *info, + TileManager *tiles); +static gboolean xcf_load_level (XcfInfo *info, + TileManager *tiles); +static gboolean xcf_load_tile (XcfInfo *info, + Tile *tile); +static gboolean xcf_load_tile_rle (XcfInfo *info, + Tile *tile, + gint data_length); +static GimpParasite * xcf_load_parasite (XcfInfo *info); +static gboolean xcf_load_old_paths (XcfInfo *info, + GimpImage *gimage); +static gboolean xcf_load_old_path (XcfInfo *info, + GimpImage *gimage); #ifdef SWAP_FROM_FILE -static gboolean xcf_swap_func (gint fd, - Tile *tile, - gint cmd, - gpointer user_data); +static gboolean xcf_swap_func (gint fd, + Tile *tile, + gint cmd, + gpointer user_data); #endif @@ -624,9 +624,9 @@ xcf_load_layer_props (XcfInfo *info, } static gboolean -xcf_load_channel_props (XcfInfo *info, - GimpImage *gimage, - GimpChannel *channel) +xcf_load_channel_props (XcfInfo *info, + GimpImage *gimage, + GimpChannel **channel) { PropType prop_type; guint32 prop_size; @@ -641,20 +641,29 @@ xcf_load_channel_props (XcfInfo *info, case PROP_END: return TRUE; case PROP_ACTIVE_CHANNEL: - info->active_channel = channel; + info->active_channel = *channel; break; case PROP_SELECTION: g_object_unref (gimage->selection_mask); - gimage->selection_mask = channel; - channel->boundary_known = FALSE; - channel->bounds_known = FALSE; + gimage->selection_mask = + gimp_selection_new (gimage, + gimp_item_width (GIMP_ITEM (*channel)), + gimp_item_height (GIMP_ITEM (*channel))); + tile_manager_unref (GIMP_DRAWABLE (gimage->selection_mask)->tiles); + GIMP_DRAWABLE (gimage->selection_mask)->tiles = + GIMP_DRAWABLE (*channel)->tiles; + GIMP_DRAWABLE (*channel)->tiles = NULL; + g_object_unref (*channel); + *channel = gimage->selection_mask; + (*channel)->boundary_known = FALSE; + (*channel)->bounds_known = FALSE; break; case PROP_OPACITY: { guint32 opacity; info->cp += xcf_read_int32 (info->fp, &opacity, 1); - channel->color.a = opacity / 255.0; + (*channel)->color.a = opacity / 255.0; } break; case PROP_VISIBLE: @@ -662,7 +671,7 @@ xcf_load_channel_props (XcfInfo *info, gboolean visible; info->cp += xcf_read_int32 (info->fp, (guint32 *) &visible, 1); - gimp_drawable_set_visible (GIMP_DRAWABLE (channel), + gimp_drawable_set_visible (GIMP_DRAWABLE (*channel), visible ? TRUE : FALSE, FALSE); } break; @@ -671,13 +680,17 @@ xcf_load_channel_props (XcfInfo *info, gboolean linked; info->cp += xcf_read_int32 (info->fp, (guint32 *) &linked, 1); - gimp_item_set_linked (GIMP_ITEM (channel), + gimp_item_set_linked (GIMP_ITEM (*channel), linked ? TRUE : FALSE, FALSE); } break; case PROP_SHOW_MASKED: - info->cp += - xcf_read_int32 (info->fp, (guint32 *) &channel->show_masked, 1); + { + gboolean show_masked; + + info->cp += xcf_read_int32 (info->fp, (guint32 *) &show_masked, 1); + gimp_channel_set_show_masked (*channel, show_masked); + } break; case PROP_COLOR: { @@ -685,13 +698,12 @@ xcf_load_channel_props (XcfInfo *info, info->cp += xcf_read_int8 (info->fp, (guint8 *) col, 3); - gimp_rgb_set_uchar (&channel->color, col[0], col[1], col[2]); - + gimp_rgb_set_uchar (&(*channel)->color, col[0], col[1], col[2]); } break; case PROP_TATTOO: info->cp += - xcf_read_int32 (info->fp, &GIMP_ITEM (channel)->tattoo, 1); + xcf_read_int32 (info->fp, &GIMP_ITEM (*channel)->tattoo, 1); break; case PROP_PARASITES: { @@ -701,7 +713,7 @@ xcf_load_channel_props (XcfInfo *info, while ((info->cp - base) < prop_size) { p = xcf_load_parasite (info); - gimp_item_parasite_attach (GIMP_ITEM (channel), p); + gimp_item_parasite_attach (GIMP_ITEM (*channel), p); gimp_parasite_free (p); } if (info->cp - base != prop_size) @@ -896,7 +908,7 @@ xcf_load_channel (XcfInfo *info, return NULL; /* read in the channel properties */ - if (!xcf_load_channel_props (info, gimage, channel)) + if (!xcf_load_channel_props (info, gimage, &channel)) goto error; /* read the hierarchy and layer mask offsets */ @@ -958,7 +970,7 @@ xcf_load_layer_mask (XcfInfo *info, return NULL; /* read in the layer_mask properties */ - if (!xcf_load_channel_props (info, gimage, GIMP_CHANNEL (layer_mask))) + if (!xcf_load_channel_props (info, gimage, (GimpChannel **) &layer_mask)) goto error; /* read the hierarchy and layer mask offsets */