diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
commit | e42129241681dde7adae7d20697e7b421682fbb4 (patch) | |
tree | af1fe815a5e639e68e59fabd8395ec69458b3e5e /app/core/gimpdrawable-transform.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimpdrawable-transform.c')
-rw-r--r-- | app/core/gimpdrawable-transform.c | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/app/core/gimpdrawable-transform.c b/app/core/gimpdrawable-transform.c new file mode 100644 index 0000000..9cbedfd --- /dev/null +++ b/app/core/gimpdrawable-transform.c @@ -0,0 +1,1070 @@ +/* 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 */ + +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); + + /* 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; + + /* 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; + + /* 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; + + /* 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, + GEGL_RECTANGLE (offset_x, offset_y, 0, 0), + TRUE); + } + + gimp_image_undo_group_end (image); + + return drawable; +} |