app: round layer bounds to global pixel grid when scaling image/group

Add gimp_item_scale_by_factors_with_origin(), which is an extension
of gimp_item_scale_by_factors(), taking the input/output points of
origin for the transformation (both of which are (0, 0) in the case
of gimp_item_scale_by_factors()).  Implement
gimp_item_scale_by_factors() in terms of the new function, and Use
the new function when scaling group layers, instead of manually
calculating the children boundaries, so that the behavior is
uniform across whole-image scaling and group-layer scaling.

The new function rounds all four edges of the boundary to the
image-global pixel grid, instead of only rounding the top/left
edges to the global grid, and the bottom/right edges to the item-
local grid.  This preserves layer-adjacency when scaling.
This commit is contained in:
Ell
2018-03-25 11:20:14 -04:00
parent 139a23451d
commit 5763b50d45
3 changed files with 99 additions and 48 deletions

View File

@ -810,34 +810,21 @@ gimp_group_layer_scale (GimpLayer *layer,
while (list) while (list)
{ {
GimpItem *child = list->data; GimpItem *child = list->data;
gint child_width;
gint child_height;
gint child_offset_x;
gint child_offset_y;
list = g_list_next (list); list = g_list_next (list);
if (queue) if (queue)
gimp_object_queue_pop (queue); gimp_object_queue_pop (queue);
child_width = ROUND (width_factor * gimp_item_get_width (child)); if (! gimp_item_scale_by_factors_with_origin (child,
child_height = ROUND (height_factor * gimp_item_get_height (child)); width_factor, height_factor,
child_offset_x = ROUND (width_factor * (gimp_item_get_offset_x (child) - old_offset_x, old_offset_y,
old_offset_x)); new_offset_x, new_offset_y,
child_offset_y = ROUND (height_factor * (gimp_item_get_offset_y (child) - interpolation_type,
old_offset_y)); progress))
child_offset_x += new_offset_x;
child_offset_y += new_offset_y;
if (child_width > 0 && child_height > 0)
{ {
gimp_item_scale (child, /* new width or height are 0; remove item */
child_width, child_height, if (gimp_item_is_attached (item))
child_offset_x, child_offset_y,
interpolation_type, progress);
}
else if (gimp_item_is_attached (item))
{ {
gimp_image_remove_layer (gimp_item_get_image (item), gimp_image_remove_layer (gimp_item_get_image (item),
GIMP_LAYER (child), GIMP_LAYER (child),
@ -848,6 +835,7 @@ gimp_group_layer_scale (GimpLayer *layer,
gimp_container_remove (private->children, GIMP_OBJECT (child)); gimp_container_remove (private->children, GIMP_OBJECT (child));
} }
} }
}
gimp_group_layer_resume_resize (group, TRUE); gimp_group_layer_resume_resize (group, TRUE);

View File

@ -1261,22 +1261,32 @@ gimp_item_check_scaling (GimpItem *item,
gint new_width, gint new_width,
gint new_height) gint new_height)
{ {
GimpItemPrivate *private;
GimpImage *image; GimpImage *image;
gdouble img_scale_w; gdouble img_scale_w;
gdouble img_scale_h; gdouble img_scale_h;
gint new_item_offset_x;
gint new_item_offset_y;
gint new_item_width; gint new_item_width;
gint new_item_height; gint new_item_height;
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
private = GET_PRIVATE (item);
image = gimp_item_get_image (item); image = gimp_item_get_image (item);
img_scale_w = ((gdouble) new_width / img_scale_w = ((gdouble) new_width /
(gdouble) gimp_image_get_width (image)); (gdouble) gimp_image_get_width (image));
img_scale_h = ((gdouble) new_height / img_scale_h = ((gdouble) new_height /
(gdouble) gimp_image_get_height (image)); (gdouble) gimp_image_get_height (image));
new_item_width = ROUND (img_scale_w * (gdouble) gimp_item_get_width (item)); new_item_offset_x = SIGNED_ROUND (img_scale_w * private->offset_x);
new_item_height = ROUND (img_scale_h * (gdouble) gimp_item_get_height (item)); new_item_offset_y = SIGNED_ROUND (img_scale_h * private->offset_y);
new_item_width = SIGNED_ROUND (img_scale_w * (private->offset_x +
gimp_item_get_width (item))) -
new_item_offset_x;
new_item_height = SIGNED_ROUND (img_scale_h * (private->offset_y +
gimp_item_get_height (item))) -
new_item_offset_y;
return (new_item_width > 0 && new_item_height > 0); return (new_item_width > 0 && new_item_height > 0);
} }
@ -1359,6 +1369,41 @@ gimp_item_scale_by_factors (GimpItem *item,
gdouble h_factor, gdouble h_factor,
GimpInterpolationType interpolation, GimpInterpolationType interpolation,
GimpProgress *progress) GimpProgress *progress)
{
return gimp_item_scale_by_factors_with_origin (item,
w_factor, h_factor,
0, 0, 0, 0,
interpolation, progress);
}
/**
* gimp_item_scale_by_factors:
* @item: Item to be transformed by explicit width and height factors.
* @w_factor: scale factor to apply to width and horizontal offset
* @h_factor: scale factor to apply to height and vertical offset
* @origin_x: x-coordinate of the transformation input origin
* @origin_y: y-coordinate of the transformation input origin
* @new_origin_x: x-coordinate of the transformation output origin
* @new_origin_y: y-coordinate of the transformation output origin
* @interpolation:
* @progress:
*
* Same as gimp_item_scale_by_factors(), but with the option to specify
* custom input and output points of origin for the transformation.
*
* Returns: #TRUE, if the scaled item has positive dimensions
* #FALSE if the scaled item has at least one zero dimension
**/
gboolean
gimp_item_scale_by_factors_with_origin (GimpItem *item,
gdouble w_factor,
gdouble h_factor,
gint origin_x,
gint origin_y,
gint new_origin_x,
gint new_origin_y,
GimpInterpolationType interpolation,
GimpProgress *progress)
{ {
GimpItemPrivate *private; GimpItemPrivate *private;
gint new_width, new_height; gint new_width, new_height;
@ -1369,18 +1414,26 @@ gimp_item_scale_by_factors (GimpItem *item,
private = GET_PRIVATE (item); private = GET_PRIVATE (item);
if (w_factor == 0.0 || h_factor == 0.0) if (w_factor <= 0.0 || h_factor <= 0.0)
{ {
g_warning ("%s: requested width or height scale equals zero", G_STRFUNC); g_warning ("%s: requested width or height scale is non-positive",
G_STRFUNC);
return FALSE; return FALSE;
} }
new_offset_x = SIGNED_ROUND (w_factor * (gdouble) private->offset_x); new_offset_x = SIGNED_ROUND (w_factor * (private->offset_x - origin_x));
new_offset_y = SIGNED_ROUND (h_factor * (gdouble) private->offset_y); new_offset_y = SIGNED_ROUND (h_factor * (private->offset_y - origin_y));
new_width = ROUND (w_factor * (gdouble) gimp_item_get_width (item)); new_width = SIGNED_ROUND (w_factor * (private->offset_x - origin_x +
new_height = ROUND (h_factor * (gdouble) gimp_item_get_height (item)); gimp_item_get_width (item))) -
new_offset_x;
new_height = SIGNED_ROUND (h_factor * (private->offset_y - origin_y +
gimp_item_get_height (item))) -
new_offset_y;
if (new_width != 0 && new_height != 0) new_offset_x += new_origin_x;
new_offset_y += new_origin_y;
if (new_width > 0 && new_height > 0)
{ {
gimp_item_scale (item, gimp_item_scale (item,
new_width, new_height, new_width, new_height,

View File

@ -236,6 +236,16 @@ gboolean gimp_item_scale_by_factors (GimpItem *item,
gdouble h_factor, gdouble h_factor,
GimpInterpolationType interpolation, GimpInterpolationType interpolation,
GimpProgress *progress); GimpProgress *progress);
gboolean
gimp_item_scale_by_factors_with_origin (GimpItem *item,
gdouble w_factor,
gdouble h_factor,
gint origin_x,
gint origin_y,
gint new_origin_x,
gint new_origin_y,
GimpInterpolationType interpolation,
GimpProgress *progress);
void gimp_item_scale_by_origin (GimpItem *item, void gimp_item_scale_by_origin (GimpItem *item,
gint new_width, gint new_width,
gint new_height, gint new_height,