app: add gimp_drawable_filter_set_crop()

Add gimp_drawable_filter_set_crop(), which allows setting an output
crop rectangle for the filter; anything outside the rectangle
doesn't get filtered.  The crop area is combined with the preview
area to determine the filtered area during preview, however, unlike
the preview area, the crop area remains in effect while committing
the filter.

Consequently, when merging a drawable filter, if the filter has a
crop, only process the cropped area.
This commit is contained in:
Ell 2018-12-30 04:46:10 -05:00
parent 7534ae53d6
commit 5c27d14fdf
3 changed files with 186 additions and 117 deletions

View File

@ -111,98 +111,104 @@ gimp_drawable_merge_filter (GimpDrawable *drawable,
const gchar *undo_desc,
gboolean cancellable)
{
GeglRectangle rect;
gboolean success = TRUE;
GimpImage *image;
GimpApplicator *applicator;
GeglBuffer *undo_buffer;
GeglRectangle undo_rect;
GeglBuffer *cache = NULL;
GeglRectangle *rects = NULL;
gint n_rects = 0;
GeglRectangle rect;
gboolean success = TRUE;
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE);
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
if (gimp_item_mask_intersect (GIMP_ITEM (drawable),
&rect.x, &rect.y,
&rect.width, &rect.height))
image = gimp_item_get_image (GIMP_ITEM (drawable));
applicator = gimp_filter_get_applicator (filter);
if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
&rect.x, &rect.y,
&rect.width, &rect.height))
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
GeglBuffer *undo_buffer;
GeglRectangle undo_rect;
GimpApplicator *applicator;
GeglBuffer *cache = NULL;
GeglRectangle *rects = NULL;
gint n_rects = 0;
return TRUE;
}
gimp_gegl_rectangle_align_to_tile_grid (
&undo_rect,
&rect,
gimp_drawable_get_buffer (drawable));
if (applicator)
{
const GeglRectangle *crop_rect;
undo_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
undo_rect.width,
undo_rect.height),
gimp_drawable_get_format (drawable));
crop_rect = gimp_applicator_get_crop (applicator);
gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
&undo_rect,
GEGL_ABYSS_NONE,
undo_buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
if (crop_rect && ! gegl_rectangle_intersect (&rect, &rect, crop_rect))
return TRUE;
applicator = gimp_filter_get_applicator (filter);
/* the cache and its valid rectangles are the region that
* has already been processed by this applicator.
*/
cache = gimp_applicator_get_cache_buffer (applicator,
&rects, &n_rects);
}
if (applicator)
{
/* disable the output crop */
gimp_applicator_set_crop (applicator, NULL);
gimp_gegl_rectangle_align_to_tile_grid (
&undo_rect,
&rect,
gimp_drawable_get_buffer (drawable));
/* the cache and its valid rectangles are the region that
* has already been processed by this applicator.
*/
cache = gimp_applicator_get_cache_buffer (applicator,
&rects, &n_rects);
}
gimp_projection_stop_rendering (gimp_image_get_projection (image));
if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable),
progress, undo_desc,
gimp_filter_get_node (filter),
gimp_drawable_get_buffer (drawable),
&rect, FALSE,
cache, rects, n_rects,
cancellable))
{
/* finished successfully */
gimp_drawable_push_undo (drawable, undo_desc, undo_buffer,
undo_rect.x, undo_rect.y,
undo_rect.width, undo_rect.height);
}
else
{
/* canceled by the user */
gimp_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (0, 0,
undo_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
undo_rect.width,
undo_rect.height),
GEGL_ABYSS_NONE,
gimp_drawable_get_buffer (drawable),
&undo_rect);
gimp_drawable_get_format (drawable));
success = FALSE;
}
gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
&undo_rect,
GEGL_ABYSS_NONE,
undo_buffer,
GEGL_RECTANGLE (0, 0, 0, 0));
g_object_unref (undo_buffer);
gimp_projection_stop_rendering (gimp_image_get_projection (image));
if (cache)
{
g_object_unref (cache);
g_free (rects);
}
if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable),
progress, undo_desc,
gimp_filter_get_node (filter),
gimp_drawable_get_buffer (drawable),
&rect, FALSE,
cache, rects, n_rects,
cancellable))
{
/* finished successfully */
gimp_drawable_update (drawable,
rect.x, rect.y,
rect.width, rect.height);
gimp_drawable_push_undo (drawable, undo_desc, undo_buffer,
undo_rect.x, undo_rect.y,
undo_rect.width, undo_rect.height);
}
else
{
/* canceled by the user */
gimp_gegl_buffer_copy (undo_buffer,
GEGL_RECTANGLE (0, 0,
undo_rect.width,
undo_rect.height),
GEGL_ABYSS_NONE,
gimp_drawable_get_buffer (drawable),
&undo_rect);
success = FALSE;
}
g_object_unref (undo_buffer);
if (cache)
{
g_object_unref (cache);
g_free (rects);
}
gimp_drawable_update (drawable,
rect.x, rect.y,
rect.width, rect.height);
return success;
}

View File

@ -65,6 +65,8 @@ struct _GimpDrawableFilter
gboolean has_input;
GimpFilterRegion region;
gboolean crop_enabled;
GeglRectangle crop_rect;
gboolean preview_enabled;
GimpAlignmentType preview_alignment;
gdouble preview_position;
@ -93,10 +95,13 @@ static void gimp_drawable_filter_dispose (GObject *ob
static void gimp_drawable_filter_finalize (GObject *object);
static void gimp_drawable_filter_sync_region (GimpDrawableFilter *filter);
static void gimp_drawable_filter_sync_preview (GimpDrawableFilter *filter,
gboolean old_enabled,
GimpAlignmentType old_alignment,
gdouble old_position);
static void gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter,
gboolean old_crop_enabled,
const GeglRectangle *old_crop_rect,
gboolean old_preview_enabled,
GimpAlignmentType old_preview_alignment,
gdouble old_preview_position,
gboolean update);
static void gimp_drawable_filter_sync_opacity (GimpDrawableFilter *filter);
static void gimp_drawable_filter_sync_mode (GimpDrawableFilter *filter);
static void gimp_drawable_filter_sync_affect (GimpDrawableFilter *filter);
@ -292,6 +297,39 @@ gimp_drawable_filter_set_region (GimpDrawableFilter *filter,
}
}
void
gimp_drawable_filter_set_crop (GimpDrawableFilter *filter,
const GeglRectangle *rect,
gboolean update)
{
g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter));
if ((rect != NULL) != filter->crop_enabled ||
(rect && ! gegl_rectangle_equal (rect, &filter->crop_rect)))
{
gboolean old_enabled = filter->crop_enabled;
GeglRectangle old_rect = filter->crop_rect;
if (rect)
{
filter->crop_enabled = TRUE;
filter->crop_rect = *rect;
}
else
{
filter->crop_enabled = FALSE;
}
gimp_drawable_filter_sync_crop (filter,
old_enabled,
&old_rect,
filter->preview_enabled,
filter->preview_alignment,
filter->preview_position,
update);
}
}
void
gimp_drawable_filter_set_preview (GimpDrawableFilter *filter,
gboolean enabled,
@ -318,9 +356,13 @@ gimp_drawable_filter_set_preview (GimpDrawableFilter *filter,
filter->preview_alignment = alignment;
filter->preview_position = position;
gimp_drawable_filter_sync_preview (filter,
old_enabled,
old_alignment, old_position);
gimp_drawable_filter_sync_crop (filter,
filter->crop_enabled,
&filter->crop_rect,
old_enabled,
old_alignment,
old_position,
TRUE);
}
}
@ -427,6 +469,10 @@ gimp_drawable_filter_commit (GimpDrawableFilter *filter,
if (gimp_drawable_filter_is_filtering (filter))
{
gimp_drawable_filter_set_preview (filter, FALSE,
filter->preview_alignment,
filter->preview_position);
success = gimp_drawable_merge_filter (filter->drawable,
GIMP_FILTER (filter),
progress,
@ -510,12 +556,14 @@ gimp_drawable_filter_sync_region (GimpDrawableFilter *filter)
}
}
static void
gimp_drawable_filter_get_preview_rect (GimpDrawableFilter *filter,
gboolean enabled,
GimpAlignmentType alignment,
gdouble position,
GeglRectangle *rect)
static gboolean
gimp_drawable_filter_get_crop_rect (GimpDrawableFilter *filter,
gboolean crop_enabled,
const GeglRectangle *crop_rect,
gboolean preview_enabled,
GimpAlignmentType preview_alignment,
gdouble preview_position,
GeglRectangle *rect)
{
gint width;
gint height;
@ -528,62 +576,71 @@ gimp_drawable_filter_get_preview_rect (GimpDrawableFilter *filter,
width = rect->width;
height = rect->height;
if (enabled)
if (preview_enabled)
{
switch (alignment)
switch (preview_alignment)
{
case GIMP_ALIGN_LEFT:
rect->width *= position;
rect->width *= preview_position;
break;
case GIMP_ALIGN_RIGHT:
rect->width *= (1.0 - position);
rect->width *= (1.0 - preview_position);
rect->x = width - rect->width;
break;
case GIMP_ALIGN_TOP:
rect->height *= position;
rect->height *= preview_position;
break;
case GIMP_ALIGN_BOTTOM:
rect->height *= (1.0 - position);
rect->height *= (1.0 - preview_position);
rect->y = height - rect->height;
break;
default:
g_return_if_reached ();
g_return_val_if_reached (FALSE);
}
}
if (crop_enabled)
gegl_rectangle_intersect (rect, rect, crop_rect);
return ! gegl_rectangle_equal_coords (rect, 0, 0, width, height);
}
static void
gimp_drawable_filter_sync_preview (GimpDrawableFilter *filter,
gboolean old_enabled,
GimpAlignmentType old_alignment,
gdouble old_position)
gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter,
gboolean old_crop_enabled,
const GeglRectangle *old_crop_rect,
gboolean old_preview_enabled,
GimpAlignmentType old_preview_alignment,
gdouble old_preview_position,
gboolean update)
{
GeglRectangle old_rect;
GeglRectangle new_rect;
gboolean enabled;
gimp_drawable_filter_get_preview_rect (filter,
old_enabled,
old_alignment,
old_position,
&old_rect);
gimp_drawable_filter_get_crop_rect (filter,
old_crop_enabled,
old_crop_rect,
old_preview_enabled,
old_preview_alignment,
old_preview_position,
&old_rect);
gimp_drawable_filter_get_preview_rect (filter,
filter->preview_enabled,
filter->preview_alignment,
filter->preview_position,
&new_rect);
enabled = gimp_drawable_filter_get_crop_rect (filter,
filter->crop_enabled,
&filter->crop_rect,
filter->preview_enabled,
filter->preview_alignment,
filter->preview_position,
&new_rect);
gimp_applicator_set_crop (filter->applicator,
filter->preview_enabled ? &new_rect : NULL);
gimp_applicator_set_crop (filter->applicator, enabled ? &new_rect : NULL);
if (old_rect.x != new_rect.x ||
old_rect.y != new_rect.y ||
old_rect.width != new_rect.width ||
old_rect.height != new_rect.height)
if (update && ! gegl_rectangle_equal (&old_rect, &new_rect))
{
cairo_region_t *region;
gint n_rects;
@ -877,10 +934,13 @@ gimp_drawable_filter_add_filter (GimpDrawableFilter *filter)
gimp_drawable_filter_sync_mask (filter);
gimp_drawable_filter_sync_region (filter);
gimp_drawable_filter_sync_preview (filter,
filter->preview_enabled,
filter->preview_alignment,
filter->preview_position);
gimp_drawable_filter_sync_crop (filter,
filter->crop_enabled,
&filter->crop_rect,
filter->preview_enabled,
filter->preview_alignment,
filter->preview_position,
TRUE);
gimp_drawable_filter_sync_opacity (filter);
gimp_drawable_filter_sync_mode (filter);
gimp_drawable_filter_sync_affect (filter);

View File

@ -57,6 +57,9 @@ GimpDrawableFilter *
void gimp_drawable_filter_set_region (GimpDrawableFilter *filter,
GimpFilterRegion region);
void gimp_drawable_filter_set_crop (GimpDrawableFilter *filter,
const GeglRectangle *rect,
gboolean update);
void gimp_drawable_filter_set_preview (GimpDrawableFilter *filter,
gboolean enabled,
GimpAlignmentType alignment,