
... it's no longer needed, since GEGL commit gegl@9dcd2cde63f95a080bf16a58c10e9ffbdd99aace. Partially reverts commits:6fca9959c7
cc10af72cc
49c53568d7
8edbc0d491
29f63616d2
3a2014984d
ee48ec6877
4165a315d5
764085278f
b7633c722e
6ab12061b7
754a3c5b18
22b4b647bd
55b3438328
c6d23add65
f03a84d607
822f9f0d2b
95358ca1fa
cdda37f4ee
41e8035635
6761da42b2
fb5d7832a8
97ed7817d8
46e9036578
ea9c5e6a49
24fbdfb591
beb4ecb238
4b77831e03
fcf113a39c
567ffe94ff
(cherry picked from commit053e5edc93
)
1127 lines
39 KiB
C
1127 lines
39 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995-2003 Spencer Kimball, Peter Mattis, and others
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <cairo.h>
|
|
#include <gegl.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "gegl/gimp-gegl-apply-operation.h"
|
|
#include "gegl/gimp-gegl-utils.h"
|
|
|
|
#include "gimp.h"
|
|
#include "gimp-transform-resize.h"
|
|
#include "gimpchannel.h"
|
|
#include "gimpcontext.h"
|
|
#include "gimpdrawable-transform.h"
|
|
#include "gimpimage.h"
|
|
#include "gimpimage-undo.h"
|
|
#include "gimpimage-undo-push.h"
|
|
#include "gimplayer.h"
|
|
#include "gimplayer-floating-selection.h"
|
|
#include "gimplayer-new.h"
|
|
#include "gimppickable.h"
|
|
#include "gimpprogress.h"
|
|
#include "gimpselection.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
#if defined (HAVE_FINITE)
|
|
#define FINITE(x) finite(x)
|
|
#elif defined (HAVE_ISFINITE)
|
|
#define FINITE(x) isfinite(x)
|
|
#elif defined (G_OS_WIN32)
|
|
#define FINITE(x) _finite(x)
|
|
#else
|
|
#error "no FINITE() implementation available?!"
|
|
#endif
|
|
|
|
|
|
/* public functions */
|
|
|
|
GimpTransformResize
|
|
gimp_drawable_transform_get_effective_clip (GimpDrawable *drawable,
|
|
GeglBuffer *orig_buffer,
|
|
GimpTransformResize clip_result)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), clip_result);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)),
|
|
clip_result);
|
|
g_return_val_if_fail (orig_buffer == NULL || GEGL_IS_BUFFER (orig_buffer),
|
|
clip_result);
|
|
|
|
/* Always clip unfloated buffers since they must keep their size */
|
|
if (GIMP_IS_CHANNEL (drawable))
|
|
{
|
|
if (orig_buffer)
|
|
{
|
|
if (! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer)))
|
|
clip_result = GIMP_TRANSFORM_RESIZE_CLIP;
|
|
}
|
|
else
|
|
{
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
if (gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
clip_result = GIMP_TRANSFORM_RESIZE_CLIP;
|
|
}
|
|
}
|
|
|
|
return clip_result;
|
|
}
|
|
|
|
GeglBuffer *
|
|
gimp_drawable_transform_buffer_affine (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GeglBuffer *orig_buffer,
|
|
gint orig_offset_x,
|
|
gint orig_offset_y,
|
|
const GimpMatrix3 *matrix,
|
|
GimpTransformDirection direction,
|
|
GimpInterpolationType interpolation_type,
|
|
GimpTransformResize clip_result,
|
|
GimpColorProfile **buffer_profile,
|
|
gint *new_offset_x,
|
|
gint *new_offset_y,
|
|
GimpProgress *progress)
|
|
{
|
|
GeglBuffer *new_buffer;
|
|
GimpMatrix3 m;
|
|
gint u1, v1, u2, v2; /* source bounding box */
|
|
gint x1, y1, x2, y2; /* target bounding box */
|
|
GimpMatrix3 gegl_matrix;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
|
|
g_return_val_if_fail (matrix != NULL, NULL);
|
|
g_return_val_if_fail (buffer_profile != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_x != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_y != NULL, NULL);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
|
|
|
|
*buffer_profile =
|
|
gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
|
|
|
|
m = *matrix;
|
|
|
|
if (direction == GIMP_TRANSFORM_BACKWARD)
|
|
{
|
|
/* Find the inverse of the transformation matrix */
|
|
gimp_matrix3_invert (&m);
|
|
}
|
|
|
|
u1 = orig_offset_x;
|
|
v1 = orig_offset_y;
|
|
u2 = u1 + gegl_buffer_get_width (orig_buffer);
|
|
v2 = v1 + gegl_buffer_get_height (orig_buffer);
|
|
|
|
/* Don't modify the clipping mode of layer masks here, so that,
|
|
* when transformed together with their layer, they match the
|
|
* layer's clipping mode.
|
|
*/
|
|
if (G_TYPE_FROM_INSTANCE (drawable) == GIMP_TYPE_CHANNEL)
|
|
{
|
|
clip_result = gimp_drawable_transform_get_effective_clip (drawable,
|
|
orig_buffer,
|
|
clip_result);
|
|
}
|
|
|
|
/* Find the bounding coordinates of target */
|
|
gimp_transform_resize_boundary (&m, clip_result,
|
|
u1, v1, u2, v2,
|
|
&x1, &y1, &x2, &y2);
|
|
|
|
/* Get the new temporary buffer for the transformed result */
|
|
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1),
|
|
gegl_buffer_get_format (orig_buffer));
|
|
|
|
gimp_matrix3_identity (&gegl_matrix);
|
|
gimp_matrix3_translate (&gegl_matrix, u1, v1);
|
|
gimp_matrix3_mult (&m, &gegl_matrix);
|
|
gimp_matrix3_translate (&gegl_matrix, -x1, -y1);
|
|
|
|
gimp_gegl_apply_transform (orig_buffer, progress, NULL,
|
|
new_buffer,
|
|
interpolation_type,
|
|
&gegl_matrix);
|
|
|
|
*new_offset_x = x1;
|
|
*new_offset_y = y1;
|
|
|
|
return new_buffer;
|
|
}
|
|
|
|
GeglBuffer *
|
|
gimp_drawable_transform_buffer_flip (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GeglBuffer *orig_buffer,
|
|
gint orig_offset_x,
|
|
gint orig_offset_y,
|
|
GimpOrientationType flip_type,
|
|
gdouble axis,
|
|
gboolean clip_result,
|
|
GimpColorProfile **buffer_profile,
|
|
gint *new_offset_x,
|
|
gint *new_offset_y)
|
|
{
|
|
const Babl *format;
|
|
GeglBuffer *new_buffer;
|
|
GeglBufferIterator *iter;
|
|
GeglRectangle src_rect;
|
|
GeglRectangle dest_rect;
|
|
gint bpp;
|
|
gint orig_x, orig_y;
|
|
gint orig_width, orig_height;
|
|
gint new_x, new_y;
|
|
gint new_width, new_height;
|
|
gint x, y;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
|
|
g_return_val_if_fail (buffer_profile != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_x != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_y != NULL, NULL);
|
|
|
|
*buffer_profile =
|
|
gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
|
|
|
|
orig_x = orig_offset_x;
|
|
orig_y = orig_offset_y;
|
|
orig_width = gegl_buffer_get_width (orig_buffer);
|
|
orig_height = gegl_buffer_get_height (orig_buffer);
|
|
|
|
new_x = orig_x;
|
|
new_y = orig_y;
|
|
new_width = orig_width;
|
|
new_height = orig_height;
|
|
|
|
switch (flip_type)
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
new_x = RINT (-((gdouble) orig_x +
|
|
(gdouble) orig_width - axis) + axis);
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
new_y = RINT (-((gdouble) orig_y +
|
|
(gdouble) orig_height - axis) + axis);
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_UNKNOWN:
|
|
g_return_val_if_reached (NULL);
|
|
break;
|
|
}
|
|
|
|
format = gegl_buffer_get_format (orig_buffer);
|
|
bpp = babl_format_get_bytes_per_pixel (format);
|
|
|
|
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
|
new_width, new_height),
|
|
format);
|
|
|
|
if (clip_result && (new_x != orig_x || new_y != orig_y))
|
|
{
|
|
GimpRGB bg;
|
|
GeglColor *color;
|
|
gint clip_x, clip_y;
|
|
gint clip_width, clip_height;
|
|
|
|
*new_offset_x = orig_x;
|
|
*new_offset_y = orig_y;
|
|
|
|
/* Use transparency, rather than the bg color, as the "outside" color of
|
|
* channels, and drawables with an alpha channel.
|
|
*/
|
|
if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format))
|
|
{
|
|
gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
else
|
|
{
|
|
gimp_context_get_background (context, &bg);
|
|
gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable),
|
|
&bg, &bg);
|
|
}
|
|
|
|
color = gimp_gegl_color_new (&bg);
|
|
gegl_buffer_set_color (new_buffer, NULL, color);
|
|
g_object_unref (color);
|
|
|
|
if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height,
|
|
new_x, new_y, new_width, new_height,
|
|
&clip_x, &clip_y,
|
|
&clip_width, &clip_height))
|
|
{
|
|
orig_x = new_x = clip_x - orig_x;
|
|
orig_y = new_y = clip_y - orig_y;
|
|
}
|
|
|
|
orig_width = new_width = clip_width;
|
|
orig_height = new_height = clip_height;
|
|
}
|
|
else
|
|
{
|
|
*new_offset_x = new_x;
|
|
*new_offset_y = new_y;
|
|
|
|
orig_x = 0;
|
|
orig_y = 0;
|
|
new_x = 0;
|
|
new_y = 0;
|
|
}
|
|
|
|
if (new_width == 0 && new_height == 0)
|
|
return new_buffer;
|
|
|
|
dest_rect.x = new_x;
|
|
dest_rect.y = new_y;
|
|
dest_rect.width = new_width;
|
|
dest_rect.height = new_height;
|
|
|
|
iter = gegl_buffer_iterator_new (new_buffer, &dest_rect, 0, NULL,
|
|
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE, 1);
|
|
|
|
switch (flip_type)
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
while (gegl_buffer_iterator_next (iter))
|
|
{
|
|
gint stride = iter->items[0].roi.width * bpp;
|
|
|
|
src_rect = iter->items[0].roi;
|
|
|
|
src_rect.x = (orig_x + orig_width) -
|
|
(iter->items[0].roi.x - dest_rect.x) -
|
|
iter->items[0].roi.width;
|
|
|
|
gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data,
|
|
stride, GEGL_ABYSS_NONE);
|
|
|
|
for (y = 0; y < iter->items[0].roi.height; y++)
|
|
{
|
|
guint8 *left = iter->items[0].data;
|
|
guint8 *right = iter->items[0].data;
|
|
|
|
left += y * stride;
|
|
right += y * stride + (iter->items[0].roi.width - 1) * bpp;
|
|
|
|
for (x = 0; x < iter->items[0].roi.width / 2; x++)
|
|
{
|
|
guint8 temp[bpp];
|
|
|
|
memcpy (temp, left, bpp);
|
|
memcpy (left, right, bpp);
|
|
memcpy (right, temp, bpp);
|
|
|
|
left += bpp;
|
|
right -= bpp;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
while (gegl_buffer_iterator_next (iter))
|
|
{
|
|
gint stride = iter->items[0].roi.width * bpp;
|
|
|
|
src_rect = iter->items[0].roi;
|
|
|
|
src_rect.y = (orig_y + orig_height) -
|
|
(iter->items[0].roi.y - dest_rect.y) -
|
|
iter->items[0].roi.height;
|
|
|
|
gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data,
|
|
stride, GEGL_ABYSS_NONE);
|
|
|
|
for (x = 0; x < iter->items[0].roi.width; x++)
|
|
{
|
|
guint8 *top = iter->items[0].data;
|
|
guint8 *bottom = iter->items[0].data;
|
|
|
|
top += x * bpp;
|
|
bottom += x * bpp + (iter->items[0].roi.height - 1) * stride;
|
|
|
|
for (y = 0; y < iter->items[0].roi.height / 2; y++)
|
|
{
|
|
guint8 temp[bpp];
|
|
|
|
memcpy (temp, top, bpp);
|
|
memcpy (top, bottom, bpp);
|
|
memcpy (bottom, temp, bpp);
|
|
|
|
top += stride;
|
|
bottom -= stride;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_UNKNOWN:
|
|
gegl_buffer_iterator_stop (iter);
|
|
break;
|
|
}
|
|
|
|
return new_buffer;
|
|
}
|
|
|
|
static void
|
|
gimp_drawable_transform_rotate_point (gint x,
|
|
gint y,
|
|
GimpRotationType rotate_type,
|
|
gdouble center_x,
|
|
gdouble center_y,
|
|
gint *new_x,
|
|
gint *new_y)
|
|
{
|
|
g_return_if_fail (new_x != NULL);
|
|
g_return_if_fail (new_y != NULL);
|
|
|
|
switch (rotate_type)
|
|
{
|
|
case GIMP_ROTATE_90:
|
|
*new_x = RINT (center_x - (gdouble) y + center_y);
|
|
*new_y = RINT (center_y + (gdouble) x - center_x);
|
|
break;
|
|
|
|
case GIMP_ROTATE_180:
|
|
*new_x = RINT (center_x - ((gdouble) x - center_x));
|
|
*new_y = RINT (center_y - ((gdouble) y - center_y));
|
|
break;
|
|
|
|
case GIMP_ROTATE_270:
|
|
*new_x = RINT (center_x + (gdouble) y - center_y);
|
|
*new_y = RINT (center_y - (gdouble) x + center_x);
|
|
break;
|
|
|
|
default:
|
|
*new_x = x;
|
|
*new_y = y;
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
GeglBuffer *
|
|
gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GeglBuffer *orig_buffer,
|
|
gint orig_offset_x,
|
|
gint orig_offset_y,
|
|
GimpRotationType rotate_type,
|
|
gdouble center_x,
|
|
gdouble center_y,
|
|
gboolean clip_result,
|
|
GimpColorProfile **buffer_profile,
|
|
gint *new_offset_x,
|
|
gint *new_offset_y)
|
|
{
|
|
const Babl *format;
|
|
GeglBuffer *new_buffer;
|
|
GeglRectangle src_rect;
|
|
GeglRectangle dest_rect;
|
|
gint orig_x, orig_y;
|
|
gint orig_width, orig_height;
|
|
gint orig_bpp;
|
|
gint new_x, new_y;
|
|
gint new_width, new_height;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
|
|
g_return_val_if_fail (buffer_profile != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_x != NULL, NULL);
|
|
g_return_val_if_fail (new_offset_y != NULL, NULL);
|
|
|
|
*buffer_profile =
|
|
gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
|
|
|
|
orig_x = orig_offset_x;
|
|
orig_y = orig_offset_y;
|
|
orig_width = gegl_buffer_get_width (orig_buffer);
|
|
orig_height = gegl_buffer_get_height (orig_buffer);
|
|
orig_bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (orig_buffer));
|
|
|
|
switch (rotate_type)
|
|
{
|
|
case GIMP_ROTATE_90:
|
|
gimp_drawable_transform_rotate_point (orig_x,
|
|
orig_y + orig_height,
|
|
rotate_type, center_x, center_y,
|
|
&new_x, &new_y);
|
|
new_width = orig_height;
|
|
new_height = orig_width;
|
|
break;
|
|
|
|
case GIMP_ROTATE_180:
|
|
gimp_drawable_transform_rotate_point (orig_x + orig_width,
|
|
orig_y + orig_height,
|
|
rotate_type, center_x, center_y,
|
|
&new_x, &new_y);
|
|
new_width = orig_width;
|
|
new_height = orig_height;
|
|
break;
|
|
|
|
case GIMP_ROTATE_270:
|
|
gimp_drawable_transform_rotate_point (orig_x + orig_width,
|
|
orig_y,
|
|
rotate_type, center_x, center_y,
|
|
&new_x, &new_y);
|
|
new_width = orig_height;
|
|
new_height = orig_width;
|
|
break;
|
|
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
break;
|
|
}
|
|
|
|
format = gegl_buffer_get_format (orig_buffer);
|
|
|
|
if (clip_result && (new_x != orig_x || new_y != orig_y ||
|
|
new_width != orig_width || new_height != orig_height))
|
|
|
|
{
|
|
GimpRGB bg;
|
|
GeglColor *color;
|
|
gint clip_x, clip_y;
|
|
gint clip_width, clip_height;
|
|
|
|
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
|
orig_width, orig_height),
|
|
format);
|
|
|
|
*new_offset_x = orig_x;
|
|
*new_offset_y = orig_y;
|
|
|
|
/* Use transparency, rather than the bg color, as the "outside" color of
|
|
* channels, and drawables with an alpha channel.
|
|
*/
|
|
if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format))
|
|
{
|
|
gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
else
|
|
{
|
|
gimp_context_get_background (context, &bg);
|
|
gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable),
|
|
&bg, &bg);
|
|
}
|
|
|
|
color = gimp_gegl_color_new (&bg);
|
|
gegl_buffer_set_color (new_buffer, NULL, color);
|
|
g_object_unref (color);
|
|
|
|
if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height,
|
|
new_x, new_y, new_width, new_height,
|
|
&clip_x, &clip_y,
|
|
&clip_width, &clip_height))
|
|
{
|
|
gint saved_orig_x = orig_x;
|
|
gint saved_orig_y = orig_y;
|
|
|
|
new_x = clip_x - orig_x;
|
|
new_y = clip_y - orig_y;
|
|
|
|
switch (rotate_type)
|
|
{
|
|
case GIMP_ROTATE_90:
|
|
gimp_drawable_transform_rotate_point (clip_x + clip_width,
|
|
clip_y,
|
|
GIMP_ROTATE_270,
|
|
center_x,
|
|
center_y,
|
|
&orig_x,
|
|
&orig_y);
|
|
orig_x -= saved_orig_x;
|
|
orig_y -= saved_orig_y;
|
|
orig_width = clip_height;
|
|
orig_height = clip_width;
|
|
break;
|
|
|
|
case GIMP_ROTATE_180:
|
|
orig_x = clip_x - orig_x;
|
|
orig_y = clip_y - orig_y;
|
|
orig_width = clip_width;
|
|
orig_height = clip_height;
|
|
break;
|
|
|
|
case GIMP_ROTATE_270:
|
|
gimp_drawable_transform_rotate_point (clip_x,
|
|
clip_y + clip_height,
|
|
GIMP_ROTATE_90,
|
|
center_x,
|
|
center_y,
|
|
&orig_x,
|
|
&orig_y);
|
|
orig_x -= saved_orig_x;
|
|
orig_y -= saved_orig_y;
|
|
orig_width = clip_height;
|
|
orig_height = clip_width;
|
|
break;
|
|
}
|
|
|
|
new_width = clip_width;
|
|
new_height = clip_height;
|
|
}
|
|
else
|
|
{
|
|
new_width = 0;
|
|
new_height = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
|
new_width, new_height),
|
|
format);
|
|
|
|
*new_offset_x = new_x;
|
|
*new_offset_y = new_y;
|
|
|
|
orig_x = 0;
|
|
orig_y = 0;
|
|
new_x = 0;
|
|
new_y = 0;
|
|
}
|
|
|
|
if (new_width < 1 || new_height < 1)
|
|
return new_buffer;
|
|
|
|
src_rect.x = orig_x;
|
|
src_rect.y = orig_y;
|
|
src_rect.width = orig_width;
|
|
src_rect.height = orig_height;
|
|
|
|
dest_rect.x = new_x;
|
|
dest_rect.y = new_y;
|
|
dest_rect.width = new_width;
|
|
dest_rect.height = new_height;
|
|
|
|
switch (rotate_type)
|
|
{
|
|
case GIMP_ROTATE_90:
|
|
{
|
|
guchar *buf = g_new (guchar, new_height * orig_bpp);
|
|
gint i;
|
|
|
|
/* Not cool, we leak memory if we return, but anyway that is
|
|
* never supposed to happen. If we see this warning, a bug has
|
|
* to be fixed!
|
|
*/
|
|
g_return_val_if_fail (new_height == orig_width, NULL);
|
|
|
|
src_rect.y = orig_y + orig_height - 1;
|
|
src_rect.height = 1;
|
|
|
|
dest_rect.x = new_x;
|
|
dest_rect.width = 1;
|
|
|
|
for (i = 0; i < orig_height; i++)
|
|
{
|
|
src_rect.y = orig_y + orig_height - 1 - i;
|
|
dest_rect.x = new_x + i;
|
|
|
|
gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
}
|
|
|
|
g_free (buf);
|
|
}
|
|
break;
|
|
|
|
case GIMP_ROTATE_180:
|
|
{
|
|
guchar *buf = g_new (guchar, new_width * orig_bpp);
|
|
gint i, j, k;
|
|
|
|
/* Not cool, we leak memory if we return, but anyway that is
|
|
* never supposed to happen. If we see this warning, a bug has
|
|
* to be fixed!
|
|
*/
|
|
g_return_val_if_fail (new_width == orig_width, NULL);
|
|
|
|
src_rect.y = orig_y + orig_height - 1;
|
|
src_rect.height = 1;
|
|
|
|
dest_rect.y = new_y;
|
|
dest_rect.height = 1;
|
|
|
|
for (i = 0; i < orig_height; i++)
|
|
{
|
|
src_rect.y = orig_y + orig_height - 1 - i;
|
|
dest_rect.y = new_y + i;
|
|
|
|
gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
|
|
for (j = 0; j < orig_width / 2; j++)
|
|
{
|
|
guchar *left = buf + j * orig_bpp;
|
|
guchar *right = buf + (orig_width - 1 - j) * orig_bpp;
|
|
|
|
for (k = 0; k < orig_bpp; k++)
|
|
{
|
|
guchar tmp = left[k];
|
|
left[k] = right[k];
|
|
right[k] = tmp;
|
|
}
|
|
}
|
|
|
|
gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
}
|
|
|
|
g_free (buf);
|
|
}
|
|
break;
|
|
|
|
case GIMP_ROTATE_270:
|
|
{
|
|
guchar *buf = g_new (guchar, new_width * orig_bpp);
|
|
gint i;
|
|
|
|
/* Not cool, we leak memory if we return, but anyway that is
|
|
* never supposed to happen. If we see this warning, a bug has
|
|
* to be fixed!
|
|
*/
|
|
g_return_val_if_fail (new_width == orig_height, NULL);
|
|
|
|
src_rect.x = orig_x + orig_width - 1;
|
|
src_rect.width = 1;
|
|
|
|
dest_rect.y = new_y;
|
|
dest_rect.height = 1;
|
|
|
|
for (i = 0; i < orig_width; i++)
|
|
{
|
|
src_rect.x = orig_x + orig_width - 1 - i;
|
|
dest_rect.y = new_y + i;
|
|
|
|
gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
}
|
|
|
|
g_free (buf);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return new_buffer;
|
|
}
|
|
|
|
GimpDrawable *
|
|
gimp_drawable_transform_affine (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
const GimpMatrix3 *matrix,
|
|
GimpTransformDirection direction,
|
|
GimpInterpolationType interpolation_type,
|
|
GimpTransformResize clip_result,
|
|
GimpProgress *progress)
|
|
{
|
|
GimpImage *image;
|
|
GeglBuffer *orig_buffer;
|
|
gint orig_offset_x;
|
|
gint orig_offset_y;
|
|
gboolean new_layer;
|
|
GimpDrawable *result = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (matrix != NULL, NULL);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
|
|
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
/* Start a transform undo group */
|
|
gimp_image_undo_group_start (image,
|
|
GIMP_UNDO_GROUP_TRANSFORM,
|
|
C_("undo-type", "Transform"));
|
|
|
|
/* Cut/Copy from the specified drawable */
|
|
orig_buffer = gimp_drawable_transform_cut (drawable, context,
|
|
&orig_offset_x, &orig_offset_y,
|
|
&new_layer);
|
|
|
|
if (orig_buffer)
|
|
{
|
|
GeglBuffer *new_buffer;
|
|
gint new_offset_x;
|
|
gint new_offset_y;
|
|
GimpColorProfile *profile;
|
|
|
|
clip_result = gimp_drawable_transform_get_effective_clip (drawable,
|
|
orig_buffer,
|
|
clip_result);
|
|
|
|
/* also transform the mask if we are transforming an entire layer */
|
|
if (GIMP_IS_LAYER (drawable) &&
|
|
gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
|
|
gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
{
|
|
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
|
|
|
|
gimp_item_transform (GIMP_ITEM (mask), context,
|
|
matrix,
|
|
direction,
|
|
interpolation_type,
|
|
clip_result,
|
|
progress);
|
|
}
|
|
|
|
/* transform the buffer */
|
|
new_buffer = gimp_drawable_transform_buffer_affine (drawable, context,
|
|
orig_buffer,
|
|
orig_offset_x,
|
|
orig_offset_y,
|
|
matrix,
|
|
direction,
|
|
interpolation_type,
|
|
clip_result,
|
|
&profile,
|
|
&new_offset_x,
|
|
&new_offset_y,
|
|
progress);
|
|
|
|
/* Free the cut/copied buffer */
|
|
g_object_unref (orig_buffer);
|
|
|
|
if (new_buffer)
|
|
{
|
|
result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
|
|
new_offset_x, new_offset_y,
|
|
new_layer);
|
|
g_object_unref (new_buffer);
|
|
}
|
|
}
|
|
|
|
/* push the undo group end */
|
|
gimp_image_undo_group_end (image);
|
|
|
|
return result;
|
|
}
|
|
|
|
GimpDrawable *
|
|
gimp_drawable_transform_flip (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GimpOrientationType flip_type,
|
|
gdouble axis,
|
|
gboolean clip_result)
|
|
{
|
|
GimpImage *image;
|
|
GeglBuffer *orig_buffer;
|
|
gint orig_offset_x;
|
|
gint orig_offset_y;
|
|
gboolean new_layer;
|
|
GimpDrawable *result = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
/* Start a transform undo group */
|
|
gimp_image_undo_group_start (image,
|
|
GIMP_UNDO_GROUP_TRANSFORM,
|
|
C_("undo-type", "Flip"));
|
|
|
|
/* Cut/Copy from the specified drawable */
|
|
orig_buffer = gimp_drawable_transform_cut (drawable, context,
|
|
&orig_offset_x, &orig_offset_y,
|
|
&new_layer);
|
|
|
|
if (orig_buffer)
|
|
{
|
|
GeglBuffer *new_buffer;
|
|
gint new_offset_x;
|
|
gint new_offset_y;
|
|
GimpColorProfile *profile;
|
|
|
|
/* always clip unfloated buffers so they keep their size */
|
|
if (GIMP_IS_CHANNEL (drawable) &&
|
|
! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer)))
|
|
clip_result = TRUE;
|
|
|
|
/* also transform the mask if we are transforming an entire layer */
|
|
if (GIMP_IS_LAYER (drawable) &&
|
|
gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
|
|
gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
{
|
|
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
|
|
|
|
gimp_item_flip (GIMP_ITEM (mask), context,
|
|
flip_type,
|
|
axis,
|
|
clip_result);
|
|
}
|
|
|
|
/* transform the buffer */
|
|
new_buffer = gimp_drawable_transform_buffer_flip (drawable, context,
|
|
orig_buffer,
|
|
orig_offset_x,
|
|
orig_offset_y,
|
|
flip_type, axis,
|
|
clip_result,
|
|
&profile,
|
|
&new_offset_x,
|
|
&new_offset_y);
|
|
|
|
/* Free the cut/copied buffer */
|
|
g_object_unref (orig_buffer);
|
|
|
|
if (new_buffer)
|
|
{
|
|
result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
|
|
new_offset_x, new_offset_y,
|
|
new_layer);
|
|
g_object_unref (new_buffer);
|
|
}
|
|
}
|
|
|
|
/* push the undo group end */
|
|
gimp_image_undo_group_end (image);
|
|
|
|
return result;
|
|
}
|
|
|
|
GimpDrawable *
|
|
gimp_drawable_transform_rotate (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GimpRotationType rotate_type,
|
|
gdouble center_x,
|
|
gdouble center_y,
|
|
gboolean clip_result)
|
|
{
|
|
GimpImage *image;
|
|
GeglBuffer *orig_buffer;
|
|
gint orig_offset_x;
|
|
gint orig_offset_y;
|
|
gboolean new_layer;
|
|
GimpDrawable *result = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
/* Start a transform undo group */
|
|
gimp_image_undo_group_start (image,
|
|
GIMP_UNDO_GROUP_TRANSFORM,
|
|
C_("undo-type", "Rotate"));
|
|
|
|
/* Cut/Copy from the specified drawable */
|
|
orig_buffer = gimp_drawable_transform_cut (drawable, context,
|
|
&orig_offset_x, &orig_offset_y,
|
|
&new_layer);
|
|
|
|
if (orig_buffer)
|
|
{
|
|
GeglBuffer *new_buffer;
|
|
gint new_offset_x;
|
|
gint new_offset_y;
|
|
GimpColorProfile *profile;
|
|
|
|
/* always clip unfloated buffers so they keep their size */
|
|
if (GIMP_IS_CHANNEL (drawable) &&
|
|
! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer)))
|
|
clip_result = TRUE;
|
|
|
|
/* also transform the mask if we are transforming an entire layer */
|
|
if (GIMP_IS_LAYER (drawable) &&
|
|
gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
|
|
gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
{
|
|
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
|
|
|
|
gimp_item_rotate (GIMP_ITEM (mask), context,
|
|
rotate_type,
|
|
center_x,
|
|
center_y,
|
|
clip_result);
|
|
}
|
|
|
|
/* transform the buffer */
|
|
new_buffer = gimp_drawable_transform_buffer_rotate (drawable, context,
|
|
orig_buffer,
|
|
orig_offset_x,
|
|
orig_offset_y,
|
|
rotate_type,
|
|
center_x, center_y,
|
|
clip_result,
|
|
&profile,
|
|
&new_offset_x,
|
|
&new_offset_y);
|
|
|
|
/* Free the cut/copied buffer */
|
|
g_object_unref (orig_buffer);
|
|
|
|
if (new_buffer)
|
|
{
|
|
result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
|
|
new_offset_x, new_offset_y,
|
|
new_layer);
|
|
g_object_unref (new_buffer);
|
|
}
|
|
}
|
|
|
|
/* push the undo group end */
|
|
gimp_image_undo_group_end (image);
|
|
|
|
return result;
|
|
}
|
|
|
|
GeglBuffer *
|
|
gimp_drawable_transform_cut (GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
gint *offset_x,
|
|
gint *offset_y,
|
|
gboolean *new_layer)
|
|
{
|
|
GimpImage *image;
|
|
GeglBuffer *buffer;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (offset_x != NULL, NULL);
|
|
g_return_val_if_fail (offset_y != NULL, NULL);
|
|
g_return_val_if_fail (new_layer != NULL, NULL);
|
|
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
/* extract the selected mask if there is a selection */
|
|
if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
|
|
{
|
|
gint x, y, w, h;
|
|
|
|
/* set the keep_indexed flag to FALSE here, since we use
|
|
* gimp_layer_new_from_gegl_buffer() later which assumes that
|
|
* the buffer are either RGB or GRAY. Eeek!!! (Sven)
|
|
*/
|
|
if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h))
|
|
{
|
|
buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
|
|
GIMP_PICKABLE (drawable),
|
|
context,
|
|
TRUE, FALSE, TRUE,
|
|
offset_x, offset_y,
|
|
NULL);
|
|
/* clear the selection */
|
|
gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
|
|
|
|
*new_layer = TRUE;
|
|
}
|
|
else
|
|
{
|
|
buffer = NULL;
|
|
*new_layer = FALSE;
|
|
}
|
|
}
|
|
else /* otherwise, just copy the layer */
|
|
{
|
|
buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
|
|
GIMP_PICKABLE (drawable),
|
|
context,
|
|
FALSE, TRUE, GIMP_IS_LAYER (drawable),
|
|
offset_x, offset_y,
|
|
NULL);
|
|
|
|
*new_layer = FALSE;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
GimpDrawable *
|
|
gimp_drawable_transform_paste (GimpDrawable *drawable,
|
|
GeglBuffer *buffer,
|
|
GimpColorProfile *buffer_profile,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
gboolean new_layer)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer = NULL;
|
|
const gchar *undo_desc = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
|
|
g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
|
|
g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (buffer_profile), NULL);
|
|
|
|
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
|
|
|
if (GIMP_IS_LAYER (drawable))
|
|
undo_desc = C_("undo-type", "Transform Layer");
|
|
else if (GIMP_IS_CHANNEL (drawable))
|
|
undo_desc = C_("undo-type", "Transform Channel");
|
|
else
|
|
return NULL;
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, undo_desc);
|
|
|
|
if (new_layer)
|
|
{
|
|
layer =
|
|
gimp_layer_new_from_gegl_buffer (buffer, image,
|
|
gimp_drawable_get_format_with_alpha (drawable),
|
|
_("Transformation"),
|
|
GIMP_OPACITY_OPAQUE,
|
|
gimp_image_get_default_new_layer_mode (image),
|
|
buffer_profile);
|
|
|
|
gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y);
|
|
|
|
floating_sel_attach (layer, drawable);
|
|
|
|
drawable = GIMP_DRAWABLE (layer);
|
|
}
|
|
else
|
|
{
|
|
gimp_drawable_set_buffer_full (drawable, TRUE, NULL,
|
|
buffer,
|
|
offset_x, offset_y,
|
|
TRUE);
|
|
}
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
return drawable;
|
|
}
|