1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995-2003 Spencer Kimball, Peter Mattis, and others
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <cairo.h>
24 #include <gegl.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26
27 #include "libgimpbase/gimpbase.h"
28 #include "libgimpcolor/gimpcolor.h"
29 #include "libgimpmath/gimpmath.h"
30
31 #include "core-types.h"
32
33 #include "gegl/gimp-gegl-apply-operation.h"
34 #include "gegl/gimp-gegl-utils.h"
35
36 #include "gimp.h"
37 #include "gimp-transform-resize.h"
38 #include "gimpchannel.h"
39 #include "gimpcontext.h"
40 #include "gimpdrawable-transform.h"
41 #include "gimpimage.h"
42 #include "gimpimage-undo.h"
43 #include "gimpimage-undo-push.h"
44 #include "gimplayer.h"
45 #include "gimplayer-floating-selection.h"
46 #include "gimplayer-new.h"
47 #include "gimppickable.h"
48 #include "gimpprogress.h"
49 #include "gimpselection.h"
50
51 #include "gimp-intl.h"
52
53
54 #if defined (HAVE_FINITE)
55 #define FINITE(x) finite(x)
56 #elif defined (HAVE_ISFINITE)
57 #define FINITE(x) isfinite(x)
58 #elif defined (G_OS_WIN32)
59 #define FINITE(x) _finite(x)
60 #else
61 #error "no FINITE() implementation available?!"
62 #endif
63
64
65 /* public functions */
66
67 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)68 gimp_drawable_transform_buffer_affine (GimpDrawable *drawable,
69 GimpContext *context,
70 GeglBuffer *orig_buffer,
71 gint orig_offset_x,
72 gint orig_offset_y,
73 const GimpMatrix3 *matrix,
74 GimpTransformDirection direction,
75 GimpInterpolationType interpolation_type,
76 GimpTransformResize clip_result,
77 GimpColorProfile **buffer_profile,
78 gint *new_offset_x,
79 gint *new_offset_y,
80 GimpProgress *progress)
81 {
82 GeglBuffer *new_buffer;
83 GimpMatrix3 m;
84 gint u1, v1, u2, v2; /* source bounding box */
85 gint x1, y1, x2, y2; /* target bounding box */
86 GimpMatrix3 gegl_matrix;
87
88 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
89 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
90 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
91 g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
92 g_return_val_if_fail (matrix != NULL, NULL);
93 g_return_val_if_fail (buffer_profile != NULL, NULL);
94 g_return_val_if_fail (new_offset_x != NULL, NULL);
95 g_return_val_if_fail (new_offset_y != NULL, NULL);
96 g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
97
98 *buffer_profile =
99 gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
100
101 m = *matrix;
102
103 if (direction == GIMP_TRANSFORM_BACKWARD)
104 {
105 /* Find the inverse of the transformation matrix */
106 gimp_matrix3_invert (&m);
107 }
108
109 u1 = orig_offset_x;
110 v1 = orig_offset_y;
111 u2 = u1 + gegl_buffer_get_width (orig_buffer);
112 v2 = v1 + gegl_buffer_get_height (orig_buffer);
113
114 /* Find the bounding coordinates of target */
115 gimp_transform_resize_boundary (&m, clip_result,
116 u1, v1, u2, v2,
117 &x1, &y1, &x2, &y2);
118
119 /* Get the new temporary buffer for the transformed result */
120 new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1),
121 gegl_buffer_get_format (orig_buffer));
122
123 gimp_matrix3_identity (&gegl_matrix);
124 gimp_matrix3_translate (&gegl_matrix, u1, v1);
125 gimp_matrix3_mult (&m, &gegl_matrix);
126 gimp_matrix3_translate (&gegl_matrix, -x1, -y1);
127
128 gimp_gegl_apply_transform (orig_buffer, progress, NULL,
129 new_buffer,
130 interpolation_type,
131 &gegl_matrix);
132
133 *new_offset_x = x1;
134 *new_offset_y = y1;
135
136 return new_buffer;
137 }
138
139 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)140 gimp_drawable_transform_buffer_flip (GimpDrawable *drawable,
141 GimpContext *context,
142 GeglBuffer *orig_buffer,
143 gint orig_offset_x,
144 gint orig_offset_y,
145 GimpOrientationType flip_type,
146 gdouble axis,
147 gboolean clip_result,
148 GimpColorProfile **buffer_profile,
149 gint *new_offset_x,
150 gint *new_offset_y)
151 {
152 const Babl *format;
153 GeglBuffer *new_buffer;
154 GeglBufferIterator *iter;
155 GeglRectangle src_rect;
156 GeglRectangle dest_rect;
157 gint bpp;
158 gint orig_x, orig_y;
159 gint orig_width, orig_height;
160 gint new_x, new_y;
161 gint new_width, new_height;
162 gint x, y;
163
164 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
165 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
166 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
167 g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
168 g_return_val_if_fail (buffer_profile != NULL, NULL);
169 g_return_val_if_fail (new_offset_x != NULL, NULL);
170 g_return_val_if_fail (new_offset_y != NULL, NULL);
171
172 *buffer_profile =
173 gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
174
175 orig_x = orig_offset_x;
176 orig_y = orig_offset_y;
177 orig_width = gegl_buffer_get_width (orig_buffer);
178 orig_height = gegl_buffer_get_height (orig_buffer);
179
180 new_x = orig_x;
181 new_y = orig_y;
182 new_width = orig_width;
183 new_height = orig_height;
184
185 switch (flip_type)
186 {
187 case GIMP_ORIENTATION_HORIZONTAL:
188 new_x = RINT (-((gdouble) orig_x +
189 (gdouble) orig_width - axis) + axis);
190 break;
191
192 case GIMP_ORIENTATION_VERTICAL:
193 new_y = RINT (-((gdouble) orig_y +
194 (gdouble) orig_height - axis) + axis);
195 break;
196
197 case GIMP_ORIENTATION_UNKNOWN:
198 g_return_val_if_reached (NULL);
199 break;
200 }
201
202 format = gegl_buffer_get_format (orig_buffer);
203 bpp = babl_format_get_bytes_per_pixel (format);
204
205 new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
206 new_width, new_height),
207 format);
208
209 if (clip_result && (new_x != orig_x || new_y != orig_y))
210 {
211 GimpRGB bg;
212 GeglColor *color;
213 gint clip_x, clip_y;
214 gint clip_width, clip_height;
215
216 *new_offset_x = orig_x;
217 *new_offset_y = orig_y;
218
219 /* Use transparency, rather than the bg color, as the "outside" color of
220 * channels, and drawables with an alpha channel.
221 */
222 if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format))
223 {
224 gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0);
225 }
226 else
227 {
228 gimp_context_get_background (context, &bg);
229 gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable),
230 &bg, &bg);
231 }
232
233 color = gimp_gegl_color_new (&bg);
234 gegl_buffer_set_color (new_buffer, NULL, color);
235 g_object_unref (color);
236
237 if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height,
238 new_x, new_y, new_width, new_height,
239 &clip_x, &clip_y,
240 &clip_width, &clip_height))
241 {
242 orig_x = new_x = clip_x - orig_x;
243 orig_y = new_y = clip_y - orig_y;
244 }
245
246 orig_width = new_width = clip_width;
247 orig_height = new_height = clip_height;
248 }
249 else
250 {
251 *new_offset_x = new_x;
252 *new_offset_y = new_y;
253
254 orig_x = 0;
255 orig_y = 0;
256 new_x = 0;
257 new_y = 0;
258 }
259
260 if (new_width == 0 && new_height == 0)
261 return new_buffer;
262
263 dest_rect.x = new_x;
264 dest_rect.y = new_y;
265 dest_rect.width = new_width;
266 dest_rect.height = new_height;
267
268 iter = gegl_buffer_iterator_new (new_buffer, &dest_rect, 0, NULL,
269 GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE, 1);
270
271 switch (flip_type)
272 {
273 case GIMP_ORIENTATION_HORIZONTAL:
274 while (gegl_buffer_iterator_next (iter))
275 {
276 gint stride = iter->items[0].roi.width * bpp;
277
278 src_rect = iter->items[0].roi;
279
280 src_rect.x = (orig_x + orig_width) -
281 (iter->items[0].roi.x - dest_rect.x) -
282 iter->items[0].roi.width;
283
284 gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data,
285 stride, GEGL_ABYSS_NONE);
286
287 for (y = 0; y < iter->items[0].roi.height; y++)
288 {
289 guint8 *left = iter->items[0].data;
290 guint8 *right = iter->items[0].data;
291
292 left += y * stride;
293 right += y * stride + (iter->items[0].roi.width - 1) * bpp;
294
295 for (x = 0; x < iter->items[0].roi.width / 2; x++)
296 {
297 guint8 temp[bpp];
298
299 memcpy (temp, left, bpp);
300 memcpy (left, right, bpp);
301 memcpy (right, temp, bpp);
302
303 left += bpp;
304 right -= bpp;
305 }
306 }
307 }
308 break;
309
310 case GIMP_ORIENTATION_VERTICAL:
311 while (gegl_buffer_iterator_next (iter))
312 {
313 gint stride = iter->items[0].roi.width * bpp;
314
315 src_rect = iter->items[0].roi;
316
317 src_rect.y = (orig_y + orig_height) -
318 (iter->items[0].roi.y - dest_rect.y) -
319 iter->items[0].roi.height;
320
321 gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data,
322 stride, GEGL_ABYSS_NONE);
323
324 for (x = 0; x < iter->items[0].roi.width; x++)
325 {
326 guint8 *top = iter->items[0].data;
327 guint8 *bottom = iter->items[0].data;
328
329 top += x * bpp;
330 bottom += x * bpp + (iter->items[0].roi.height - 1) * stride;
331
332 for (y = 0; y < iter->items[0].roi.height / 2; y++)
333 {
334 guint8 temp[bpp];
335
336 memcpy (temp, top, bpp);
337 memcpy (top, bottom, bpp);
338 memcpy (bottom, temp, bpp);
339
340 top += stride;
341 bottom -= stride;
342 }
343 }
344 }
345 break;
346
347 case GIMP_ORIENTATION_UNKNOWN:
348 gegl_buffer_iterator_stop (iter);
349 break;
350 }
351
352 return new_buffer;
353 }
354
355 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)356 gimp_drawable_transform_rotate_point (gint x,
357 gint y,
358 GimpRotationType rotate_type,
359 gdouble center_x,
360 gdouble center_y,
361 gint *new_x,
362 gint *new_y)
363 {
364 g_return_if_fail (new_x != NULL);
365 g_return_if_fail (new_y != NULL);
366
367 switch (rotate_type)
368 {
369 case GIMP_ROTATE_90:
370 *new_x = RINT (center_x - (gdouble) y + center_y);
371 *new_y = RINT (center_y + (gdouble) x - center_x);
372 break;
373
374 case GIMP_ROTATE_180:
375 *new_x = RINT (center_x - ((gdouble) x - center_x));
376 *new_y = RINT (center_y - ((gdouble) y - center_y));
377 break;
378
379 case GIMP_ROTATE_270:
380 *new_x = RINT (center_x + (gdouble) y - center_y);
381 *new_y = RINT (center_y - (gdouble) x + center_x);
382 break;
383
384 default:
385 *new_x = x;
386 *new_y = y;
387 g_return_if_reached ();
388 }
389 }
390
391 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)392 gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable,
393 GimpContext *context,
394 GeglBuffer *orig_buffer,
395 gint orig_offset_x,
396 gint orig_offset_y,
397 GimpRotationType rotate_type,
398 gdouble center_x,
399 gdouble center_y,
400 gboolean clip_result,
401 GimpColorProfile **buffer_profile,
402 gint *new_offset_x,
403 gint *new_offset_y)
404 {
405 const Babl *format;
406 GeglBuffer *new_buffer;
407 GeglRectangle src_rect;
408 GeglRectangle dest_rect;
409 gint orig_x, orig_y;
410 gint orig_width, orig_height;
411 gint orig_bpp;
412 gint new_x, new_y;
413 gint new_width, new_height;
414
415 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
416 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
417 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
418 g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL);
419 g_return_val_if_fail (buffer_profile != NULL, NULL);
420 g_return_val_if_fail (new_offset_x != NULL, NULL);
421 g_return_val_if_fail (new_offset_y != NULL, NULL);
422
423 *buffer_profile =
424 gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable));
425
426 orig_x = orig_offset_x;
427 orig_y = orig_offset_y;
428 orig_width = gegl_buffer_get_width (orig_buffer);
429 orig_height = gegl_buffer_get_height (orig_buffer);
430 orig_bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (orig_buffer));
431
432 switch (rotate_type)
433 {
434 case GIMP_ROTATE_90:
435 gimp_drawable_transform_rotate_point (orig_x,
436 orig_y + orig_height,
437 rotate_type, center_x, center_y,
438 &new_x, &new_y);
439 new_width = orig_height;
440 new_height = orig_width;
441 break;
442
443 case GIMP_ROTATE_180:
444 gimp_drawable_transform_rotate_point (orig_x + orig_width,
445 orig_y + orig_height,
446 rotate_type, center_x, center_y,
447 &new_x, &new_y);
448 new_width = orig_width;
449 new_height = orig_height;
450 break;
451
452 case GIMP_ROTATE_270:
453 gimp_drawable_transform_rotate_point (orig_x + orig_width,
454 orig_y,
455 rotate_type, center_x, center_y,
456 &new_x, &new_y);
457 new_width = orig_height;
458 new_height = orig_width;
459 break;
460
461 default:
462 g_return_val_if_reached (NULL);
463 break;
464 }
465
466 format = gegl_buffer_get_format (orig_buffer);
467
468 if (clip_result && (new_x != orig_x || new_y != orig_y ||
469 new_width != orig_width || new_height != orig_height))
470
471 {
472 GimpRGB bg;
473 GeglColor *color;
474 gint clip_x, clip_y;
475 gint clip_width, clip_height;
476
477 new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
478 orig_width, orig_height),
479 format);
480
481 *new_offset_x = orig_x;
482 *new_offset_y = orig_y;
483
484 /* Use transparency, rather than the bg color, as the "outside" color of
485 * channels, and drawables with an alpha channel.
486 */
487 if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format))
488 {
489 gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0);
490 }
491 else
492 {
493 gimp_context_get_background (context, &bg);
494 gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable),
495 &bg, &bg);
496 }
497
498 color = gimp_gegl_color_new (&bg);
499 gegl_buffer_set_color (new_buffer, NULL, color);
500 g_object_unref (color);
501
502 if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height,
503 new_x, new_y, new_width, new_height,
504 &clip_x, &clip_y,
505 &clip_width, &clip_height))
506 {
507 gint saved_orig_x = orig_x;
508 gint saved_orig_y = orig_y;
509
510 new_x = clip_x - orig_x;
511 new_y = clip_y - orig_y;
512
513 switch (rotate_type)
514 {
515 case GIMP_ROTATE_90:
516 gimp_drawable_transform_rotate_point (clip_x + clip_width,
517 clip_y,
518 GIMP_ROTATE_270,
519 center_x,
520 center_y,
521 &orig_x,
522 &orig_y);
523 orig_x -= saved_orig_x;
524 orig_y -= saved_orig_y;
525 orig_width = clip_height;
526 orig_height = clip_width;
527 break;
528
529 case GIMP_ROTATE_180:
530 orig_x = clip_x - orig_x;
531 orig_y = clip_y - orig_y;
532 orig_width = clip_width;
533 orig_height = clip_height;
534 break;
535
536 case GIMP_ROTATE_270:
537 gimp_drawable_transform_rotate_point (clip_x,
538 clip_y + clip_height,
539 GIMP_ROTATE_90,
540 center_x,
541 center_y,
542 &orig_x,
543 &orig_y);
544 orig_x -= saved_orig_x;
545 orig_y -= saved_orig_y;
546 orig_width = clip_height;
547 orig_height = clip_width;
548 break;
549 }
550
551 new_width = clip_width;
552 new_height = clip_height;
553 }
554 else
555 {
556 new_width = 0;
557 new_height = 0;
558 }
559 }
560 else
561 {
562 new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
563 new_width, new_height),
564 format);
565
566 *new_offset_x = new_x;
567 *new_offset_y = new_y;
568
569 orig_x = 0;
570 orig_y = 0;
571 new_x = 0;
572 new_y = 0;
573 }
574
575 if (new_width < 1 || new_height < 1)
576 return new_buffer;
577
578 src_rect.x = orig_x;
579 src_rect.y = orig_y;
580 src_rect.width = orig_width;
581 src_rect.height = orig_height;
582
583 dest_rect.x = new_x;
584 dest_rect.y = new_y;
585 dest_rect.width = new_width;
586 dest_rect.height = new_height;
587
588 switch (rotate_type)
589 {
590 case GIMP_ROTATE_90:
591 {
592 guchar *buf = g_new (guchar, new_height * orig_bpp);
593 gint i;
594
595 /* Not cool, we leak memory if we return, but anyway that is
596 * never supposed to happen. If we see this warning, a bug has
597 * to be fixed!
598 */
599 g_return_val_if_fail (new_height == orig_width, NULL);
600
601 src_rect.y = orig_y + orig_height - 1;
602 src_rect.height = 1;
603
604 dest_rect.x = new_x;
605 dest_rect.width = 1;
606
607 for (i = 0; i < orig_height; i++)
608 {
609 src_rect.y = orig_y + orig_height - 1 - i;
610 dest_rect.x = new_x + i;
611
612 gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
613 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
614 gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
615 GEGL_AUTO_ROWSTRIDE);
616 }
617
618 g_free (buf);
619 }
620 break;
621
622 case GIMP_ROTATE_180:
623 {
624 guchar *buf = g_new (guchar, new_width * orig_bpp);
625 gint i, j, k;
626
627 /* Not cool, we leak memory if we return, but anyway that is
628 * never supposed to happen. If we see this warning, a bug has
629 * to be fixed!
630 */
631 g_return_val_if_fail (new_width == orig_width, NULL);
632
633 src_rect.y = orig_y + orig_height - 1;
634 src_rect.height = 1;
635
636 dest_rect.y = new_y;
637 dest_rect.height = 1;
638
639 for (i = 0; i < orig_height; i++)
640 {
641 src_rect.y = orig_y + orig_height - 1 - i;
642 dest_rect.y = new_y + i;
643
644 gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
645 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
646
647 for (j = 0; j < orig_width / 2; j++)
648 {
649 guchar *left = buf + j * orig_bpp;
650 guchar *right = buf + (orig_width - 1 - j) * orig_bpp;
651
652 for (k = 0; k < orig_bpp; k++)
653 {
654 guchar tmp = left[k];
655 left[k] = right[k];
656 right[k] = tmp;
657 }
658 }
659
660 gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
661 GEGL_AUTO_ROWSTRIDE);
662 }
663
664 g_free (buf);
665 }
666 break;
667
668 case GIMP_ROTATE_270:
669 {
670 guchar *buf = g_new (guchar, new_width * orig_bpp);
671 gint i;
672
673 /* Not cool, we leak memory if we return, but anyway that is
674 * never supposed to happen. If we see this warning, a bug has
675 * to be fixed!
676 */
677 g_return_val_if_fail (new_width == orig_height, NULL);
678
679 src_rect.x = orig_x + orig_width - 1;
680 src_rect.width = 1;
681
682 dest_rect.y = new_y;
683 dest_rect.height = 1;
684
685 for (i = 0; i < orig_width; i++)
686 {
687 src_rect.x = orig_x + orig_width - 1 - i;
688 dest_rect.y = new_y + i;
689
690 gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf,
691 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
692 gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf,
693 GEGL_AUTO_ROWSTRIDE);
694 }
695
696 g_free (buf);
697 }
698 break;
699 }
700
701 return new_buffer;
702 }
703
704 GimpDrawable *
gimp_drawable_transform_affine(GimpDrawable * drawable,GimpContext * context,const GimpMatrix3 * matrix,GimpTransformDirection direction,GimpInterpolationType interpolation_type,GimpTransformResize clip_result,GimpProgress * progress)705 gimp_drawable_transform_affine (GimpDrawable *drawable,
706 GimpContext *context,
707 const GimpMatrix3 *matrix,
708 GimpTransformDirection direction,
709 GimpInterpolationType interpolation_type,
710 GimpTransformResize clip_result,
711 GimpProgress *progress)
712 {
713 GimpImage *image;
714 GeglBuffer *orig_buffer;
715 gint orig_offset_x;
716 gint orig_offset_y;
717 gboolean new_layer;
718 GimpDrawable *result = NULL;
719
720 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
721 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
722 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
723 g_return_val_if_fail (matrix != NULL, NULL);
724 g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
725
726 image = gimp_item_get_image (GIMP_ITEM (drawable));
727
728 /* Start a transform undo group */
729 gimp_image_undo_group_start (image,
730 GIMP_UNDO_GROUP_TRANSFORM,
731 C_("undo-type", "Transform"));
732
733 /* Cut/Copy from the specified drawable */
734 orig_buffer = gimp_drawable_transform_cut (drawable, context,
735 &orig_offset_x, &orig_offset_y,
736 &new_layer);
737
738 if (orig_buffer)
739 {
740 GeglBuffer *new_buffer;
741 gint new_offset_x;
742 gint new_offset_y;
743 GimpColorProfile *profile;
744
745 /* also transform the mask if we are transforming an entire layer */
746 if (GIMP_IS_LAYER (drawable) &&
747 gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
748 gimp_channel_is_empty (gimp_image_get_mask (image)))
749 {
750 GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
751
752 gimp_item_transform (GIMP_ITEM (mask), context,
753 matrix,
754 direction,
755 interpolation_type,
756 clip_result,
757 progress);
758 }
759
760 /* transform the buffer */
761 new_buffer = gimp_drawable_transform_buffer_affine (drawable, context,
762 orig_buffer,
763 orig_offset_x,
764 orig_offset_y,
765 matrix,
766 direction,
767 interpolation_type,
768 clip_result,
769 &profile,
770 &new_offset_x,
771 &new_offset_y,
772 progress);
773
774 /* Free the cut/copied buffer */
775 g_object_unref (orig_buffer);
776
777 if (new_buffer)
778 {
779 result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
780 new_offset_x, new_offset_y,
781 new_layer);
782 g_object_unref (new_buffer);
783 }
784 }
785
786 /* push the undo group end */
787 gimp_image_undo_group_end (image);
788
789 return result;
790 }
791
792 GimpDrawable *
gimp_drawable_transform_flip(GimpDrawable * drawable,GimpContext * context,GimpOrientationType flip_type,gdouble axis,gboolean clip_result)793 gimp_drawable_transform_flip (GimpDrawable *drawable,
794 GimpContext *context,
795 GimpOrientationType flip_type,
796 gdouble axis,
797 gboolean clip_result)
798 {
799 GimpImage *image;
800 GeglBuffer *orig_buffer;
801 gint orig_offset_x;
802 gint orig_offset_y;
803 gboolean new_layer;
804 GimpDrawable *result = NULL;
805
806 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
807 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
808 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
809
810 image = gimp_item_get_image (GIMP_ITEM (drawable));
811
812 /* Start a transform undo group */
813 gimp_image_undo_group_start (image,
814 GIMP_UNDO_GROUP_TRANSFORM,
815 C_("undo-type", "Flip"));
816
817 /* Cut/Copy from the specified drawable */
818 orig_buffer = gimp_drawable_transform_cut (drawable, context,
819 &orig_offset_x, &orig_offset_y,
820 &new_layer);
821
822 if (orig_buffer)
823 {
824 GeglBuffer *new_buffer;
825 gint new_offset_x;
826 gint new_offset_y;
827 GimpColorProfile *profile;
828
829 /* also transform the mask if we are transforming an entire layer */
830 if (GIMP_IS_LAYER (drawable) &&
831 gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
832 gimp_channel_is_empty (gimp_image_get_mask (image)))
833 {
834 GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
835
836 gimp_item_flip (GIMP_ITEM (mask), context,
837 flip_type,
838 axis,
839 clip_result);
840 }
841
842 /* transform the buffer */
843 new_buffer = gimp_drawable_transform_buffer_flip (drawable, context,
844 orig_buffer,
845 orig_offset_x,
846 orig_offset_y,
847 flip_type, axis,
848 clip_result,
849 &profile,
850 &new_offset_x,
851 &new_offset_y);
852
853 /* Free the cut/copied buffer */
854 g_object_unref (orig_buffer);
855
856 if (new_buffer)
857 {
858 result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
859 new_offset_x, new_offset_y,
860 new_layer);
861 g_object_unref (new_buffer);
862 }
863 }
864
865 /* push the undo group end */
866 gimp_image_undo_group_end (image);
867
868 return result;
869 }
870
871 GimpDrawable *
gimp_drawable_transform_rotate(GimpDrawable * drawable,GimpContext * context,GimpRotationType rotate_type,gdouble center_x,gdouble center_y,gboolean clip_result)872 gimp_drawable_transform_rotate (GimpDrawable *drawable,
873 GimpContext *context,
874 GimpRotationType rotate_type,
875 gdouble center_x,
876 gdouble center_y,
877 gboolean clip_result)
878 {
879 GimpImage *image;
880 GeglBuffer *orig_buffer;
881 gint orig_offset_x;
882 gint orig_offset_y;
883 gboolean new_layer;
884 GimpDrawable *result = NULL;
885
886 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
887 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
888 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
889
890 image = gimp_item_get_image (GIMP_ITEM (drawable));
891
892 /* Start a transform undo group */
893 gimp_image_undo_group_start (image,
894 GIMP_UNDO_GROUP_TRANSFORM,
895 C_("undo-type", "Rotate"));
896
897 /* Cut/Copy from the specified drawable */
898 orig_buffer = gimp_drawable_transform_cut (drawable, context,
899 &orig_offset_x, &orig_offset_y,
900 &new_layer);
901
902 if (orig_buffer)
903 {
904 GeglBuffer *new_buffer;
905 gint new_offset_x;
906 gint new_offset_y;
907 GimpColorProfile *profile;
908
909 /* also transform the mask if we are transforming an entire layer */
910 if (GIMP_IS_LAYER (drawable) &&
911 gimp_layer_get_mask (GIMP_LAYER (drawable)) &&
912 gimp_channel_is_empty (gimp_image_get_mask (image)))
913 {
914 GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable));
915
916 gimp_item_rotate (GIMP_ITEM (mask), context,
917 rotate_type,
918 center_x,
919 center_y,
920 clip_result);
921 }
922
923 /* transform the buffer */
924 new_buffer = gimp_drawable_transform_buffer_rotate (drawable, context,
925 orig_buffer,
926 orig_offset_x,
927 orig_offset_y,
928 rotate_type,
929 center_x, center_y,
930 clip_result,
931 &profile,
932 &new_offset_x,
933 &new_offset_y);
934
935 /* Free the cut/copied buffer */
936 g_object_unref (orig_buffer);
937
938 if (new_buffer)
939 {
940 result = gimp_drawable_transform_paste (drawable, new_buffer, profile,
941 new_offset_x, new_offset_y,
942 new_layer);
943 g_object_unref (new_buffer);
944 }
945 }
946
947 /* push the undo group end */
948 gimp_image_undo_group_end (image);
949
950 return result;
951 }
952
953 GeglBuffer *
gimp_drawable_transform_cut(GimpDrawable * drawable,GimpContext * context,gint * offset_x,gint * offset_y,gboolean * new_layer)954 gimp_drawable_transform_cut (GimpDrawable *drawable,
955 GimpContext *context,
956 gint *offset_x,
957 gint *offset_y,
958 gboolean *new_layer)
959 {
960 GimpImage *image;
961 GeglBuffer *buffer;
962
963 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
964 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
965 g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
966 g_return_val_if_fail (offset_x != NULL, NULL);
967 g_return_val_if_fail (offset_y != NULL, NULL);
968 g_return_val_if_fail (new_layer != NULL, NULL);
969
970 image = gimp_item_get_image (GIMP_ITEM (drawable));
971
972 /* extract the selected mask if there is a selection */
973 if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
974 {
975 gint x, y, w, h;
976
977 /* set the keep_indexed flag to FALSE here, since we use
978 * gimp_layer_new_from_gegl_buffer() later which assumes that
979 * the buffer are either RGB or GRAY. Eeek!!! (Sven)
980 */
981 if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &w, &h))
982 {
983 buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
984 GIMP_PICKABLE (drawable),
985 context,
986 TRUE, FALSE, TRUE,
987 offset_x, offset_y,
988 NULL);
989 /* clear the selection */
990 gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
991
992 *new_layer = TRUE;
993 }
994 else
995 {
996 buffer = NULL;
997 *new_layer = FALSE;
998 }
999 }
1000 else /* otherwise, just copy the layer */
1001 {
1002 buffer = gimp_selection_extract (GIMP_SELECTION (gimp_image_get_mask (image)),
1003 GIMP_PICKABLE (drawable),
1004 context,
1005 FALSE, TRUE, GIMP_IS_LAYER (drawable),
1006 offset_x, offset_y,
1007 NULL);
1008
1009 *new_layer = FALSE;
1010 }
1011
1012 return buffer;
1013 }
1014
1015 GimpDrawable *
gimp_drawable_transform_paste(GimpDrawable * drawable,GeglBuffer * buffer,GimpColorProfile * buffer_profile,gint offset_x,gint offset_y,gboolean new_layer)1016 gimp_drawable_transform_paste (GimpDrawable *drawable,
1017 GeglBuffer *buffer,
1018 GimpColorProfile *buffer_profile,
1019 gint offset_x,
1020 gint offset_y,
1021 gboolean new_layer)
1022 {
1023 GimpImage *image;
1024 GimpLayer *layer = NULL;
1025 const gchar *undo_desc = NULL;
1026
1027 g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
1028 g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
1029 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
1030 g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (buffer_profile), NULL);
1031
1032 image = gimp_item_get_image (GIMP_ITEM (drawable));
1033
1034 if (GIMP_IS_LAYER (drawable))
1035 undo_desc = C_("undo-type", "Transform Layer");
1036 else if (GIMP_IS_CHANNEL (drawable))
1037 undo_desc = C_("undo-type", "Transform Channel");
1038 else
1039 return NULL;
1040
1041 gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, undo_desc);
1042
1043 if (new_layer)
1044 {
1045 layer =
1046 gimp_layer_new_from_gegl_buffer (buffer, image,
1047 gimp_drawable_get_format_with_alpha (drawable),
1048 _("Transformation"),
1049 GIMP_OPACITY_OPAQUE,
1050 gimp_image_get_default_new_layer_mode (image),
1051 buffer_profile);
1052
1053 gimp_item_set_offset (GIMP_ITEM (layer), offset_x, offset_y);
1054
1055 floating_sel_attach (layer, drawable);
1056
1057 drawable = GIMP_DRAWABLE (layer);
1058 }
1059 else
1060 {
1061 gimp_drawable_set_buffer_full (drawable, TRUE, NULL,
1062 buffer,
1063 GEGL_RECTANGLE (offset_x, offset_y, 0, 0),
1064 TRUE);
1065 }
1066
1067 gimp_image_undo_group_end (image);
1068
1069 return drawable;
1070 }
1071