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