gimpdrawable-transform.c 28.3 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2
 * Copyright (C) 1995-2003 Spencer Kimball, Peter Mattis, and others
Nate Summers's avatar
Nate Summers committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

Sven Neumann's avatar
Sven Neumann committed
21 22
#include <stdlib.h>

23
#include <glib-object.h>
Nate Summers's avatar
Nate Summers committed
24

25
#include "libgimpbase/gimpbase.h"
Nate Summers's avatar
Nate Summers committed
26 27
#include "libgimpmath/gimpmath.h"

28
#include "core-types.h"
Nate Summers's avatar
Nate Summers committed
29

Michael Natterer's avatar
Michael Natterer committed
30 31 32
#include "base/pixel-region.h"
#include "base/tile-manager.h"

33 34
#include "paint-funcs/paint-funcs.h"

35
#include "gimp.h"
36
#include "gimp-transform-region.h"
37
#include "gimp-transform-resize.h"
38 39 40 41
#include "gimpchannel.h"
#include "gimpcontext.h"
#include "gimpdrawable-transform.h"
#include "gimpimage.h"
42
#include "gimpimage-undo.h"
43
#include "gimpimage-undo-push.h"
44
#include "gimplayer.h"
45
#include "gimplayer-floating-sel.h"
46
#include "gimppickable.h"
47
#include "gimpprogress.h"
48
#include "gimpselection.h"
49

50
#include "gimp-intl.h"
Nate Summers's avatar
Nate Summers committed
51

52

53 54 55 56 57 58 59 60 61 62 63
#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


64 65
#define MIN4(a,b,c,d) MIN(MIN(a,b),MIN(c,d))
#define MAX4(a,b,c,d) MAX(MAX(a,b),MAX(c,d))
Nate Summers's avatar
Nate Summers committed
66 67


68
/*  public functions  */
Nate Summers's avatar
Nate Summers committed
69

Nate Summers's avatar
Nate Summers committed
70
TileManager *
71
gimp_drawable_transform_tiles_affine (GimpDrawable           *drawable,
72
                                      GimpContext            *context,
73
                                      TileManager            *orig_tiles,
74
                                      const GimpMatrix3      *matrix,
75
                                      GimpTransformDirection  direction,
76
                                      GimpInterpolationType   interpolation_type,
77
                                      gint                    recursion_level,
78
                                      GimpTransformResize     clip_result,
79
                                      GimpProgress           *progress)
Nate Summers's avatar
Nate Summers committed
80
{
81 82 83 84 85 86 87
  GimpImage   *image;
  PixelRegion  destPR;
  TileManager *new_tiles;
  GimpMatrix3  m;
  GimpMatrix3  inv;
  gint         u1, v1, u2, v2;  /* source bounding box */
  gint         x1, y1, x2, y2;  /* target bounding box */
88

89
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
90
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
91
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
92
  g_return_val_if_fail (orig_tiles != NULL, NULL);
93
  g_return_val_if_fail (matrix != NULL, NULL);
94
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
95

96
  image = gimp_item_get_image (GIMP_ITEM (drawable));
97

98 99 100
  m   = *matrix;
  inv = *matrix;

101
  if (direction == GIMP_TRANSFORM_BACKWARD)
Nate Summers's avatar
Nate Summers committed
102 103
    {
      /*  keep the original matrix here, so we dont need to recalculate
104 105
       *  the inverse later
       */
106
      gimp_matrix3_invert (&inv);
Nate Summers's avatar
Nate Summers committed
107 108 109 110
    }
  else
    {
      /*  Find the inverse of the transformation matrix  */
111
      gimp_matrix3_invert (&m);
Nate Summers's avatar
Nate Summers committed
112 113
    }

114 115 116
  tile_manager_get_offsets (orig_tiles, &u1, &v1);
  u2 = u1 + tile_manager_width (orig_tiles);
  v2 = v1 + tile_manager_height (orig_tiles);
Nate Summers's avatar
Nate Summers committed
117

118
  /*  Always clip unfloated tiles since they must keep their size  */
119 120
  if (G_TYPE_FROM_INSTANCE (drawable) == GIMP_TYPE_CHANNEL &&
      tile_manager_bpp (orig_tiles)   == 1)
121
    clip_result = GIMP_TRANSFORM_RESIZE_CLIP;
122

123
  /*  Find the bounding coordinates of target */
Michael Natterer's avatar
Michael Natterer committed
124 125 126 127
  gimp_transform_resize_boundary (&inv, clip_result,
                                  u1, v1, u2, v2,
                                  &x1, &y1, &x2, &y2);

Nate Summers's avatar
Nate Summers committed
128
  /*  Get the new temporary buffer for the transformed result  */
129 130 131
  new_tiles = tile_manager_new (x2 - x1, y2 - y1,
                                tile_manager_bpp (orig_tiles));
  pixel_region_init (&destPR, new_tiles,
132
                     0, 0, x2 - x1, y2 - y1, TRUE);
133
  tile_manager_set_offsets (new_tiles, x1, y1);
Nate Summers's avatar
Nate Summers committed
134

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  gimp_transform_region (GIMP_PICKABLE (drawable),
                         context,
                         orig_tiles,
                         &destPR,
                         x1,
                         y1,
                         x2,
                         y2,
                         &inv,
                         interpolation_type,
                         recursion_level,
                         progress);

  return new_tiles;
}

Nate Summers's avatar
Nate Summers committed
151
TileManager *
152
gimp_drawable_transform_tiles_flip (GimpDrawable        *drawable,
153
                                    GimpContext         *context,
154
                                    TileManager         *orig_tiles,
155
                                    GimpOrientationType  flip_type,
156 157
                                    gdouble              axis,
                                    gboolean             clip_result)
Nate Summers's avatar
Nate Summers committed
158
{
159
  GimpImage   *image;
160
  TileManager *new_tiles;
161 162
  PixelRegion  srcPR, destPR;
  gint         orig_x, orig_y;
163 164 165 166
  gint         orig_width, orig_height;
  gint         orig_bpp;
  gint         new_x, new_y;
  gint         new_width, new_height;
167 168 169
  gint         i;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
170
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
171
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
172
  g_return_val_if_fail (orig_tiles != NULL, NULL);
173

174
  image = gimp_item_get_image (GIMP_ITEM (drawable));
175

176 177 178 179
  orig_width  = tile_manager_width (orig_tiles);
  orig_height = tile_manager_height (orig_tiles);
  orig_bpp    = tile_manager_bpp (orig_tiles);
  tile_manager_get_offsets (orig_tiles, &orig_x, &orig_y);
180

181 182 183 184
  new_x      = orig_x;
  new_y      = orig_y;
  new_width  = orig_width;
  new_height = orig_height;
185 186 187 188

  switch (flip_type)
    {
    case GIMP_ORIENTATION_HORIZONTAL:
189 190
      new_x = RINT (-((gdouble) orig_x +
                      (gdouble) orig_width - axis) + axis);
191 192 193
      break;

    case GIMP_ORIENTATION_VERTICAL:
194 195
      new_y = RINT (-((gdouble) orig_y +
                      (gdouble) orig_height - axis) + axis);
196 197 198 199 200 201
      break;

    default:
      break;
    }

202 203 204 205 206 207 208 209 210 211
  new_tiles = tile_manager_new (new_width, new_height, orig_bpp);

  if (clip_result && (new_x != orig_y || new_y != orig_y))
    {
      guchar bg_color[MAX_CHANNELS];
      gint   clip_x, clip_y;
      gint   clip_width, clip_height;

      tile_manager_set_offsets (new_tiles, orig_x, orig_y);

212 213
      gimp_image_get_background (image, context, gimp_drawable_type (drawable),
                                 bg_color);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

      /*  "Outside" a channel is transparency, not the bg color  */
      if (GIMP_IS_CHANNEL (drawable))
        bg_color[0] = TRANSPARENT_OPACITY;

      pixel_region_init (&destPR, new_tiles,
                         0, 0, new_width, new_height, TRUE);
      color_region (&destPR, bg_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
    {
      tile_manager_set_offsets (new_tiles, new_x, new_y);

      orig_x = 0;
      orig_y = 0;
      new_x  = 0;
      new_y  = 0;
    }

  if (new_width == 0 && new_height == 0)
    return new_tiles;
247

248
  if (flip_type == GIMP_ORIENTATION_HORIZONTAL)
249 250 251
    {
      for (i = 0; i < orig_width; i++)
        {
252 253 254 255 256 257
          pixel_region_init (&srcPR, orig_tiles,
                             i + orig_x, orig_y,
                             1, orig_height, FALSE);
          pixel_region_init (&destPR, new_tiles,
                             new_x + new_width - i - 1, new_y,
                             1, new_height, TRUE);
258
          copy_region (&srcPR, &destPR);
259 260 261 262 263 264
        }
    }
  else
    {
      for (i = 0; i < orig_height; i++)
        {
265 266 267 268 269 270
          pixel_region_init (&srcPR, orig_tiles,
                             orig_x, i + orig_y,
                             orig_width, 1, FALSE);
          pixel_region_init (&destPR, new_tiles,
                             new_x, new_y + new_height - i - 1,
                             new_width, 1, TRUE);
271 272 273 274
          copy_region (&srcPR, &destPR);
        }
    }

275
  return new_tiles;
276 277
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
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:
293 294
      *new_x = RINT (center_x - (gdouble) y + center_y);
      *new_y = RINT (center_y + (gdouble) x - center_x);
295 296 297
      break;

    case GIMP_ROTATE_180:
298 299
      *new_x = RINT (center_x - ((gdouble) x - center_x));
      *new_y = RINT (center_y - ((gdouble) y - center_y));
300 301 302
      break;

    case GIMP_ROTATE_270:
303 304
      *new_x = RINT (center_x + (gdouble) y - center_y);
      *new_y = RINT (center_y - (gdouble) x + center_x);
305 306 307 308 309 310 311
      break;

    default:
      g_assert_not_reached ();
    }
}

312 313
TileManager *
gimp_drawable_transform_tiles_rotate (GimpDrawable     *drawable,
314
                                      GimpContext      *context,
315 316 317 318 319 320
                                      TileManager      *orig_tiles,
                                      GimpRotationType  rotate_type,
                                      gdouble           center_x,
                                      gdouble           center_y,
                                      gboolean          clip_result)
{
321
  GimpImage   *image;
322 323 324 325 326 327 328 329 330 331 332
  TileManager *new_tiles;
  PixelRegion  srcPR, destPR;
  guchar      *buf = NULL;
  gint         orig_x, orig_y;
  gint         orig_width, orig_height;
  gint         orig_bpp;
  gint         new_x, new_y;
  gint         new_width, new_height;
  gint         i, j, k;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
333
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
334
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
335 336
  g_return_val_if_fail (orig_tiles != NULL, NULL);

337
  image = gimp_item_get_image (GIMP_ITEM (drawable));
338 339 340 341 342 343 344 345 346

  orig_width  = tile_manager_width (orig_tiles);
  orig_height = tile_manager_height (orig_tiles);
  orig_bpp    = tile_manager_bpp (orig_tiles);
  tile_manager_get_offsets (orig_tiles, &orig_x, &orig_y);

  switch (rotate_type)
    {
    case GIMP_ROTATE_90:
347 348 349 350
      gimp_drawable_transform_rotate_point (orig_x,
                                            orig_y + orig_height,
                                            rotate_type, center_x, center_y,
                                            &new_x, &new_y);
351 352 353 354 355
      new_width  = orig_height;
      new_height = orig_width;
      break;

    case GIMP_ROTATE_180:
356 357 358 359
      gimp_drawable_transform_rotate_point (orig_x + orig_width,
                                            orig_y + orig_height,
                                            rotate_type, center_x, center_y,
                                            &new_x, &new_y);
360 361 362 363 364
      new_width  = orig_width;
      new_height = orig_height;
      break;

    case GIMP_ROTATE_270:
365 366 367 368
      gimp_drawable_transform_rotate_point (orig_x + orig_width,
                                            orig_y,
                                            rotate_type, center_x, center_y,
                                            &new_x, &new_y);
369 370 371 372 373 374 375 376 377
      new_width  = orig_height;
      new_height = orig_width;
      break;

    default:
      g_assert_not_reached ();
      return NULL;
    }

378 379
  if (clip_result && (new_x != orig_x || new_y != orig_y ||
                      new_width != orig_width || new_height != orig_height))
380

381 382 383 384 385
    {
      guchar bg_color[MAX_CHANNELS];
      gint   clip_x, clip_y;
      gint   clip_width, clip_height;

386 387
      new_tiles = tile_manager_new (orig_width, orig_height, orig_bpp);

388 389
      tile_manager_set_offsets (new_tiles, orig_x, orig_y);

390 391
      gimp_image_get_background (image, context, gimp_drawable_type (drawable),
                                 bg_color);
392 393 394 395 396 397

      /*  "Outside" a channel is transparency, not the bg color  */
      if (GIMP_IS_CHANNEL (drawable))
        bg_color[0] = TRANSPARENT_OPACITY;

      pixel_region_init (&destPR, new_tiles,
398
                         0, 0, orig_width, orig_height, TRUE);
399 400 401 402 403 404 405
      color_region (&destPR, bg_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))
        {
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
          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;
            }
449 450
        }

451 452
      new_width  = clip_width;
      new_height = clip_height;
453 454 455
    }
  else
    {
456 457
      new_tiles = tile_manager_new (new_width, new_height, orig_bpp);

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
      tile_manager_set_offsets (new_tiles, new_x, new_y);

      orig_x = 0;
      orig_y = 0;
      new_x  = 0;
      new_y  = 0;
    }

  if (new_width == 0 && new_height == 0)
    return new_tiles;

  pixel_region_init (&srcPR, orig_tiles,
                     orig_x, orig_y, orig_width, orig_height, FALSE);
  pixel_region_init (&destPR, new_tiles,
                     new_x, new_y, new_width, new_height, TRUE);

  switch (rotate_type)
    {
    case GIMP_ROTATE_90:
      g_assert (new_height == orig_width);
      buf = g_new (guchar, new_height * orig_bpp);

      for (i = 0; i < orig_height; i++)
        {
          pixel_region_get_row (&srcPR, orig_x, orig_y + orig_height - 1 - i,
                                orig_width, buf, 1);
          pixel_region_set_col (&destPR, new_x + i, new_y, new_height, buf);
        }
      break;

    case GIMP_ROTATE_180:
489
      g_assert (new_width == orig_width);
490 491 492 493 494 495
      buf = g_new (guchar, new_width * orig_bpp);

      for (i = 0; i < orig_height; i++)
        {
          pixel_region_get_row (&srcPR, orig_x, orig_y + orig_height - 1 - i,
                                orig_width, buf, 1);
496

497
          for (j = 0; j < orig_width / 2; j++)
498 499 500
            {
              guchar *left  = buf + j * orig_bpp;
              guchar *right = buf + (orig_width - 1 - j) * orig_bpp;
501

502 503 504 505 506 507 508
              for (k = 0; k < orig_bpp; k++)
                {
                  guchar tmp = left[k];
                  left[k]    = right[k];
                  right[k]   = tmp;
                }
            }
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

          pixel_region_set_row (&destPR, new_x, new_y + i, new_width, buf);
        }
      break;

    case GIMP_ROTATE_270:
      g_assert (new_width == orig_height);
      buf = g_new (guchar, new_width * orig_bpp);

      for (i = 0; i < orig_width; i++)
        {
          pixel_region_get_col (&srcPR, orig_x + orig_width - 1 - i, orig_y,
                                orig_height, buf, 1);
          pixel_region_set_row (&destPR, new_x, new_y + i, new_width, buf);
        }
      break;
    }

  g_free (buf);

  return new_tiles;
}

532 533
gboolean
gimp_drawable_transform_affine (GimpDrawable           *drawable,
534
                                GimpContext            *context,
535
                                const GimpMatrix3      *matrix,
536 537
                                GimpTransformDirection  direction,
                                GimpInterpolationType   interpolation_type,
538
                                gint                    recursion_level,
539
                                GimpTransformResize     clip_result,
540
                                GimpProgress           *progress)
541
{
542
  GimpImage   *image;
543
  TileManager *orig_tiles;
544 545 546 547
  gboolean     new_layer;
  gboolean     success = FALSE;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
548
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
549
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
550
  g_return_val_if_fail (matrix != NULL, FALSE);
551
  g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
552

553
  image = gimp_item_get_image (GIMP_ITEM (drawable));
554 555

  /* Start a transform undo group */
556
  gimp_image_undo_group_start (image,
557
                               GIMP_UNDO_GROUP_TRANSFORM, _("Transform"));
558 559

  /* Cut/Copy from the specified drawable */
560
  orig_tiles = gimp_drawable_transform_cut (drawable, context, &new_layer);
561

562
  if (orig_tiles)
563 564 565
    {
      TileManager *new_tiles;

566
      /*  always clip unfloated tiles so they keep their size  */
567
      if (GIMP_IS_CHANNEL (drawable) && tile_manager_bpp (orig_tiles) == 1)
568
        clip_result = GIMP_TRANSFORM_RESIZE_CLIP;
569

570
      /* transform the buffer */
571
      new_tiles = gimp_drawable_transform_tiles_affine (drawable, context,
572
                                                        orig_tiles,
573 574
                                                        matrix,
                                                        GIMP_TRANSFORM_FORWARD,
575
                                                        interpolation_type,
576
                                                        recursion_level,
577
                                                        clip_result,
578
                                                        progress);
579 580

      /* Free the cut/copied buffer */
581
      tile_manager_unref (orig_tiles);
582 583

      if (new_tiles)
584 585 586 587 588
        {
          success = gimp_drawable_transform_paste (drawable, new_tiles,
                                                   new_layer);
          tile_manager_unref (new_tiles);
        }
589 590 591
    }

  /*  push the undo group end  */
592
  gimp_image_undo_group_end (image);
593 594 595 596 597

  return success;
}

gboolean
598
gimp_drawable_transform_flip (GimpDrawable        *drawable,
599
                              GimpContext         *context,
600
                              GimpOrientationType  flip_type,
601
                              gboolean             auto_center,
602 603
                              gdouble              axis,
                              gboolean             clip_result)
604
{
605
  GimpImage   *image;
606
  TileManager *orig_tiles;
607 608 609 610
  gboolean     new_layer;
  gboolean     success = FALSE;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
611
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
612
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
613

614
  image = gimp_item_get_image (GIMP_ITEM (drawable));
615 616

  /* Start a transform undo group */
617
  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, Q_("command|Flip"));
618 619

  /* Cut/Copy from the specified drawable */
620
  orig_tiles = gimp_drawable_transform_cut (drawable, context, &new_layer);
621

622
  if (orig_tiles)
623
    {
624
      TileManager *new_tiles = NULL;
625

626
      if (auto_center)
627
        {
628 629 630 631 632 633 634
          gint off_x, off_y;
          gint width, height;

          tile_manager_get_offsets (orig_tiles, &off_x, &off_y);
          width  = tile_manager_width  (orig_tiles);
          height = tile_manager_height (orig_tiles);

635 636 637 638 639
          switch (flip_type)
            {
            case GIMP_ORIENTATION_HORIZONTAL:
              axis = ((gdouble) off_x + (gdouble) width / 2.0);
              break;
640

641 642 643
            case GIMP_ORIENTATION_VERTICAL:
              axis = ((gdouble) off_y + (gdouble) height / 2.0);
              break;
644

645 646 647
            default:
              break;
            }
648
        }
649

650 651 652 653
      /*  always clip unfloated tiles so they keep their size  */
      if (GIMP_IS_CHANNEL (drawable) && tile_manager_bpp (orig_tiles) == 1)
        clip_result = TRUE;

654
      /* transform the buffer */
655 656 657 658 659 660
      if (orig_tiles)
        {
          new_tiles = gimp_drawable_transform_tiles_flip (drawable, context,
                                                          orig_tiles,
                                                          flip_type, axis,
                                                          clip_result);
661

662 663 664
          /* Free the cut/copied buffer */
          tile_manager_unref (orig_tiles);
        }
665 666

      if (new_tiles)
667 668 669 670 671
        {
          success = gimp_drawable_transform_paste (drawable, new_tiles,
                                                   new_layer);
          tile_manager_unref (new_tiles);
        }
672 673 674
    }

  /*  push the undo group end  */
675
  gimp_image_undo_group_end (image);
676 677 678 679

  return success;
}

680 681
gboolean
gimp_drawable_transform_rotate (GimpDrawable     *drawable,
682
                                GimpContext      *context,
683 684 685 686 687
                                GimpRotationType  rotate_type,
                                gboolean          auto_center,
                                gdouble           center_x,
                                gdouble           center_y,
                                gboolean          clip_result)
688
{
689
  GimpImage   *image;
690 691 692 693 694
  TileManager *orig_tiles;
  gboolean     new_layer;
  gboolean     success = FALSE;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
695
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
696
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
697

698
  image = gimp_item_get_image (GIMP_ITEM (drawable));
699 700

  /* Start a transform undo group */
701
  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, Q_("command|Rotate"));
702 703

  /* Cut/Copy from the specified drawable */
704
  orig_tiles = gimp_drawable_transform_cut (drawable, context, &new_layer);
705 706 707 708 709

  if (orig_tiles)
    {
      TileManager *new_tiles;

710 711 712 713 714 715 716 717 718 719 720 721
      if (auto_center)
        {
          gint off_x, off_y;
          gint width, height;

          tile_manager_get_offsets (orig_tiles, &off_x, &off_y);
          width  = tile_manager_width  (orig_tiles);
          height = tile_manager_height (orig_tiles);

          center_x = (gdouble) off_x + (gdouble) width  / 2.0;
          center_y = (gdouble) off_y + (gdouble) height / 2.0;
        }
722

723 724 725
      /*  always clip unfloated tiles so they keep their size  */
      if (GIMP_IS_CHANNEL (drawable) && tile_manager_bpp (orig_tiles) == 1)
        clip_result = TRUE;
726 727

      /* transform the buffer */
728 729
      new_tiles = gimp_drawable_transform_tiles_rotate (drawable, context,
                                                        orig_tiles,
730 731
                                                        rotate_type,
                                                        center_x, center_y,
732
                                                        clip_result);
733 734

      /* Free the cut/copied buffer */
735
      tile_manager_unref (orig_tiles);
736 737

      if (new_tiles)
738 739 740 741 742
        {
          success = gimp_drawable_transform_paste (drawable, new_tiles,
                                                   new_layer);
          tile_manager_unref (new_tiles);
        }
743 744 745
    }

  /*  push the undo group end  */
746
  gimp_image_undo_group_end (image);
747 748 749 750

  return success;
}

751 752
TileManager *
gimp_drawable_transform_cut (GimpDrawable *drawable,
753
                             GimpContext  *context,
754 755
                             gboolean     *new_layer)
{
756
  GimpImage   *image;
Nate Summers's avatar
Nate Summers committed
757 758
  TileManager *tiles;

759
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
760
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
761
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
762 763
  g_return_val_if_fail (new_layer != NULL, NULL);

764
  image = gimp_item_get_image (GIMP_ITEM (drawable));
765

Nate Summers's avatar
Nate Summers committed
766
  /*  extract the selected mask if there is a selection  */
767
  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
Nate Summers's avatar
Nate Summers committed
768
    {
769 770
      gint x, y, w, h;

Nate Summers's avatar
Nate Summers committed
771
      /* set the keep_indexed flag to FALSE here, since we use
772 773
       * gimp_layer_new_from_tiles() later which assumes that the tiles
       * are either RGB or GRAY.  Eeek!!!              (Sven)
Nate Summers's avatar
Nate Summers committed
774
       */
775 776
      if (gimp_drawable_mask_intersect (drawable, &x, &y, &w, &h))
        {
777
          tiles = gimp_selection_extract (gimp_image_get_mask (image),
778 779
                                          GIMP_PICKABLE (drawable),
                                          context, TRUE, FALSE, TRUE);
780

781 782 783 784 785 786 787
          *new_layer = TRUE;
        }
      else
        {
          tiles = NULL;
          *new_layer = FALSE;
        }
Nate Summers's avatar
Nate Summers committed
788
    }
789
  else  /*  otherwise, just copy the layer  */
Nate Summers's avatar
Nate Summers committed
790 791
    {
      if (GIMP_IS_LAYER (drawable))
792
        tiles = gimp_selection_extract (gimp_image_get_mask (image),
793 794
                                        GIMP_PICKABLE (drawable),
                                        context, FALSE, TRUE, TRUE);
Nate Summers's avatar
Nate Summers committed
795
      else
796
        tiles = gimp_selection_extract (gimp_image_get_mask (image),
797 798
                                        GIMP_PICKABLE (drawable),
                                        context, FALSE, TRUE, FALSE);
799

Nate Summers's avatar
Nate Summers committed
800 801 802 803 804 805 806
      *new_layer = FALSE;
    }

  return tiles;
}

gboolean
807 808 809
gimp_drawable_transform_paste (GimpDrawable *drawable,
                               TileManager  *tiles,
                               gboolean      new_layer)
Nate Summers's avatar
Nate Summers committed
810
{
811
  GimpImage   *image;
812 813 814 815
  GimpLayer   *layer     = NULL;
  const gchar *undo_desc = NULL;
  gint         offset_x;
  gint         offset_y;
Nate Summers's avatar
Nate Summers committed
816

817
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
818
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
819 820
  g_return_val_if_fail (tiles != NULL, FALSE);

821
  image = gimp_item_get_image (GIMP_ITEM (drawable));
822

823 824 825 826 827 828 829 830 831
  if (GIMP_IS_LAYER (drawable))
    undo_desc = _("Transform Layer");
  else if (GIMP_IS_CHANNEL (drawable))
    undo_desc = _("Transform Channel");
  else
    return FALSE;

  tile_manager_get_offsets (tiles, &offset_x, &offset_y);

832
  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE, undo_desc);
833

Nate Summers's avatar
Nate Summers committed
834 835 836
  if (new_layer)
    {
      layer =
837
        gimp_layer_new_from_tiles (tiles, image,
838
                                   gimp_drawable_type_with_alpha (drawable),
839 840
                                   _("Transformation"),
                                   GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
Nate Summers's avatar
Nate Summers committed
841

842 843
      GIMP_ITEM (layer)->offset_x = offset_x;
      GIMP_ITEM (layer)->offset_y = offset_y;
Nate Summers's avatar
Nate Summers committed
844 845 846 847 848

      floating_sel_attach (layer, drawable);
    }
  else
    {
849
      GimpImageType drawable_type;
850

851 852
      if (GIMP_IS_LAYER (drawable) && (tile_manager_bpp (tiles) == 2 ||
                                       tile_manager_bpp (tiles) == 4))
853
        {
854
          drawable_type = gimp_drawable_type_with_alpha (drawable);
855
        }
Nate Summers's avatar
Nate Summers committed
856
      else
857
        {
858
          drawable_type = gimp_drawable_type (drawable);
859
        }
Nate Summers's avatar
Nate Summers committed
860

861 862
      gimp_drawable_set_tiles_full (drawable, TRUE, NULL,
                                    tiles, drawable_type,
863
                                    offset_x, offset_y);
Nate Summers's avatar
Nate Summers committed
864
    }
865

866
  gimp_image_undo_group_end (image);
867

868
  return TRUE;
Nate Summers's avatar
Nate Summers committed
869
}