warp.c 60 KB
Newer Older
1
/* Warp  --- image filter plug-in for GIMP
Marc Lehmann's avatar
Marc Lehmann committed
2 3
 * Copyright (C) 1997 John P. Beale
 * Much of the 'warp' is from the Displace plug-in: 1996 Stephen Robert Norris
4
 * Much of the 'displace' code taken in turn  from the pinch plug-in
Marc Lehmann's avatar
Marc Lehmann committed
5 6
 *   which is by 1996 Federico Mena Quintero
 *
7
 * This program is free software: you can redistribute it and/or modify
Marc Lehmann's avatar
Marc Lehmann committed
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
Marc Lehmann's avatar
Marc Lehmann committed
10 11 12 13 14 15 16 17
 * (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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Marc Lehmann's avatar
Marc Lehmann committed
19 20 21
 *
 * You can contact me (the warp author) at beale@best.com
 * Please send me any patches or enhancements to this code.
22
 * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
Marc Lehmann's avatar
Marc Lehmann committed
23 24 25
 *
 * --------------------------------------------------------------------
 * Warp Program structure: after running the user interface and setting the
26
 * parameters, warp generates a brand-new image (later to be deleted
Marc Lehmann's avatar
Marc Lehmann committed
27 28 29 30 31
 * before the user ever sees it) which contains two grayscale layers,
 * representing the X and Y gradients of the "control" image. For this
 * purpose, all channels of the control image are summed for a scalar
 * value at each pixel coordinate for the gradient operation.
 *
32 33 34 35 36
 * The X,Y components of the calculated gradient are then used to
 * displace pixels from the source image into the destination
 * image. The displacement vector is rotated a user-specified amount
 * first. This displacement operation happens iteratively, generating
 * a new displaced image from each prior image.
Marc Lehmann's avatar
Marc Lehmann committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50
 * -------------------------------------------------------------------
 *
 * Revision History:
 * Version 0.37  12/19/98 Fixed Tooltips and freeing memory
 * Version 0.36  11/9/97  Changed XY vector layers  back to own image
 *               fixed 'undo' problem (hopefully)
 *
 * Version 0.35  11/3/97  Added vector-map, mag-map, grad-map to
 *               diff vector instead of separate operation
 *               further futzing with drawable updates
 *               starting adding tooltips
 *
 * Version 0.34  10/30/97   'Fixed' drawable update problem
 *               Added 16-bit resolution to differential map
David Odin's avatar
David Odin committed
51
 *               Added substep increments for finer control
Marc Lehmann's avatar
Marc Lehmann committed
52 53 54 55 56 57 58 59
 *
 * Version 0.33  10/26/97   Added 'angle increment' to user interface
 *
 * Version 0.32  10/25/97   Added magnitude control map (secondary control)
 *               Changed undo behavior to be one undo-step per warp call.
 *
 * Version 0.31  10/25/97   Fixed src/dest pixregions so program works
 *               with multiple-layer images. Still don't know
60 61
 *               exactly what I did to fix it :-/  Also, added 'color' option
 *               for border pixels to use the current selected foreground color.
Marc Lehmann's avatar
Marc Lehmann committed
62 63 64 65
 *
 * Version 0.3   10/20/97  Initial release for Gimp 0.99.xx
 */

66
#include "config.h"
Marc Lehmann's avatar
Marc Lehmann committed
67

68 69 70
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

Manish Singh's avatar
Manish Singh committed
71
#include "libgimp/stdplugins-intl.h"
Marc Lehmann's avatar
Marc Lehmann committed
72

73

Marc Lehmann's avatar
Marc Lehmann committed
74 75
/* Some useful macros */

76 77
#define PLUG_IN_PROC    "plug-in-warp"
#define PLUG_IN_BINARY  "warp"
78
#define PLUG_IN_ROLE    "gimp-warp"
79 80 81
#define ENTRY_WIDTH     75
#define TILE_CACHE_SIZE 30  /* was 48. There is a cache flush problem in GIMP preventing sequential updates */
#define MIN_ARGS         6  /* minimum number of arguments required */
Marc Lehmann's avatar
Marc Lehmann committed
82

83 84 85 86 87 88 89
enum
{
  WRAP,
  SMEAR,
  BLACK,
  COLOR
};
Marc Lehmann's avatar
Marc Lehmann committed
90

91 92
typedef struct
{
Marc Lehmann's avatar
Marc Lehmann committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
  gdouble amount;
  gint    warp_map;
  gint    iter;
  gdouble dither;
  gdouble angle;
  gint    wrap_type;
  gint    mag_map;
  gint    mag_use;
  gint    substeps;
  gint    grad_map;
  gdouble grad_scale;
  gint    vector_map;
  gdouble vector_scale;
  gdouble vector_angle;
} WarpVals;


/*
 * Function prototypes.
 */

static void      query  (void);
115
static void      run    (const gchar      *name,
David Odin's avatar
David Odin committed
116 117 118 119
                         gint              nparams,
                         const GimpParam  *param,
                         gint             *nreturn_vals,
                         GimpParam       **return_vals);
Marc Lehmann's avatar
Marc Lehmann committed
120

Sven Neumann's avatar
Sven Neumann committed
121
static void      blur16           (GimpDrawable *drawable);
Marc Lehmann's avatar
Marc Lehmann committed
122

123
static void      diff             (GimpDrawable *drawable,
David Odin's avatar
David Odin committed
124 125
                                   gint32       *xl_id,
                                   gint32       *yl_id);
Marc Lehmann's avatar
Marc Lehmann committed
126

127
static void      diff_prepare_row (GimpPixelRgn *pixel_rgn,
David Odin's avatar
David Odin committed
128 129 130 131
                                   guchar       *data,
                                   gint          x,
                                   gint          y,
                                   gint          w);
Marc Lehmann's avatar
Marc Lehmann committed
132

133
static void      warp_one         (GimpDrawable *draw,
David Odin's avatar
David Odin committed
134 135 136 137 138 139
                                   GimpDrawable *new,
                                   GimpDrawable *map_x,
                                   GimpDrawable *map_y,
                                   GimpDrawable *mag_draw,
                                   gboolean      first_time,
                                   gint          step);
Marc Lehmann's avatar
Marc Lehmann committed
140

Sven Neumann's avatar
Sven Neumann committed
141
static void      warp        (GimpDrawable *drawable);
142

143
static gboolean  warp_dialog (GimpDrawable *drawable);
144
static GimpTile *warp_pixel  (GimpDrawable *drawable,
David Odin's avatar
David Odin committed
145 146 147 148 149 150 151 152 153 154 155 156
                              GimpTile     *tile,
                              gint          width,
                              gint          height,
                              gint          x1,
                              gint          y1,
                              gint          x2,
                              gint          y2,
                              gint          x,
                              gint          y,
                              gint         *row,
                              gint         *col,
                              guchar       *pixel);
157

158
static gboolean  warp_map_constrain       (gint32     image_id,
David Odin's avatar
David Odin committed
159 160
                                           gint32     drawable_id,
                                           gpointer   data);
161
static gdouble   warp_map_mag_give_value  (guchar    *pt,
David Odin's avatar
David Odin committed
162 163
                                           gint       alpha,
                                           gint       bytes);
Marc Lehmann's avatar
Marc Lehmann committed
164

165 166 167
/* -------------------------------------------------------------------------- */
/*   Variables global over entire plug-in scope                               */
/* -------------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
168

169
const GimpPlugInInfo PLUG_IN_INFO =
Marc Lehmann's avatar
Marc Lehmann committed
170
{
171 172 173 174
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Marc Lehmann's avatar
Marc Lehmann committed
175 176 177 178
};

static WarpVals dvals =
{
179 180 181 182 183 184 185 186 187 188 189 190 191 192
  10.0,   /* amount       */
  -1,     /* warp_map     */
  5,      /* iterations   */
  0.0,    /* dither       */
  90.0,   /* angle        */
  WRAP,   /* wrap_type    */
  -1,     /* mag_map      */
  FALSE,  /* mag_use      */
  1,      /* substeps     */
  -1,     /* grad_map     */
  0.0,    /* grad_scale   */
  -1,     /* vector_map   */
  0.0,    /* vector_scale */
  0.0     /* vector_angle */
Marc Lehmann's avatar
Marc Lehmann committed
193 194
};

195
/* -------------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
196

197 198
static gint         progress = 0;              /* progress indicator bar      */
static guint        tile_width, tile_height;   /* size of an image tile       */
199
static GimpRunMode  run_mode;                  /* interactive, non-, etc.     */
200
static guchar       color_pixel[4] = {0, 0, 0, 255};  /* current fg color     */
Marc Lehmann's avatar
Marc Lehmann committed
201

202
/* -------------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
203 204 205 206 207 208

/***** Functions *****/

MAIN ()

static void
209
query (void)
Marc Lehmann's avatar
Marc Lehmann committed
210
{
211
  static const GimpParamDef args[] =
Marc Lehmann's avatar
Marc Lehmann committed
212
  {
213
    { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    { GIMP_PDB_IMAGE,    "image",        "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable",     "Input drawable" },
    { GIMP_PDB_FLOAT,    "amount",       "Pixel displacement multiplier" },
    { GIMP_PDB_DRAWABLE, "warp-map",     "Displacement control map" },
    { GIMP_PDB_INT32,    "iter",         "Iteration count (last required argument)" },
    { GIMP_PDB_FLOAT,    "dither",       "Random dither amount (first optional argument)" },
    { GIMP_PDB_FLOAT,    "angle",        "Angle of gradient vector rotation" },
    { GIMP_PDB_INT32,    "wrap-type",    "Edge behavior: { WRAP (0), SMEAR (1), BLACK (2), COLOR (3) }" },
    { GIMP_PDB_DRAWABLE, "mag-map",      "Magnitude control map" },
    { GIMP_PDB_INT32,    "mag-use",      "Use magnitude map: { FALSE (0), TRUE (1) }" },
    { GIMP_PDB_INT32,    "substeps",     "Substeps between image updates" },
    { GIMP_PDB_INT32,    "grad-map",     "Gradient control map" },
    { GIMP_PDB_FLOAT,    "grad-scale",   "Scaling factor for gradient map (0=don't use)" },
    { GIMP_PDB_INT32,    "vector-map",   "Fixed vector control map" },
    { GIMP_PDB_FLOAT,    "vector-scale", "Scaling factor for fixed vector map (0=don't use)" },
    { GIMP_PDB_FLOAT,    "vector-angle", "Angle for fixed vector map" }
Marc Lehmann's avatar
Marc Lehmann committed
230 231
  };

232
  gimp_install_procedure (PLUG_IN_PROC,
David Odin's avatar
David Odin committed
233 234 235 236 237 238 239 240 241 242 243 244 245
                          N_("Twist or smear image in many different ways"),
                          "Smears an image along vector paths calculated as "
                          "the gradient of a separate control matrix. The "
                          "effect can look like brushstrokes of acrylic or "
                          "watercolor paint, in some cases.",
                          "John P. Beale",
                          "John P. Beale",
                          "1997",
                          N_("_Warp..."),
                          "RGB*, GRAY*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), 0,
                          args, NULL);
Marc Lehmann's avatar
Marc Lehmann committed
246 247 248
}

static void
249 250 251 252 253
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Marc Lehmann's avatar
Marc Lehmann committed
254
{
255
  static GimpParam  values[1];
Sven Neumann's avatar
Sven Neumann committed
256
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
257 258
  GimpDrawable     *drawable;
  GimpRGB           color;
Marc Lehmann's avatar
Marc Lehmann committed
259 260 261

  run_mode = param[0].data.d_int32;

262 263
  INIT_I18N ();

264 265
  tile_width  = gimp_tile_width ();    /* initialize some globals */
  tile_height = gimp_tile_height ();
Marc Lehmann's avatar
Marc Lehmann committed
266 267

  /* get currently selected foreground pixel color */
268
  gimp_context_get_foreground (&color);
269
  gimp_rgb_get_uchar (&color,
David Odin's avatar
David Odin committed
270 271 272
                      &color_pixel[0],
                      &color_pixel[1],
                      &color_pixel[2]);
Marc Lehmann's avatar
Marc Lehmann committed
273 274 275 276 277

  /*  Get the specified drawable  */
  drawable = gimp_drawable_get (param[2].data.d_drawable);

  *nreturn_vals = 1;
278
  *return_vals  = values;
279

Sven Neumann's avatar
Sven Neumann committed
280
  values[0].type          = GIMP_PDB_STATUS;
Marc Lehmann's avatar
Marc Lehmann committed
281 282 283 284
  values[0].data.d_status = status;

  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
285
    case GIMP_RUN_INTERACTIVE:
Marc Lehmann's avatar
Marc Lehmann committed
286
      /*  Possibly retrieve data  */
287
      gimp_get_data (PLUG_IN_PROC, &dvals);
Marc Lehmann's avatar
Marc Lehmann committed
288 289 290

      /*  First acquire information with a dialog  */
      if (! warp_dialog (drawable))
David Odin's avatar
David Odin committed
291
        return;
Marc Lehmann's avatar
Marc Lehmann committed
292 293
      break;

Sven Neumann's avatar
Sven Neumann committed
294
    case GIMP_RUN_NONINTERACTIVE:
295
      /*  Make sure minimum args
296
       *  (mode, image, draw, amount, warp_map, iter) are there
297
       */
Marc Lehmann's avatar
Marc Lehmann committed
298
      if (nparams < MIN_ARGS)
David Odin's avatar
David Odin committed
299 300 301
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
302
      else
David Odin's avatar
David Odin committed
303
        {
Sven Neumann's avatar
Sven Neumann committed
304 305
          gint  pcnt = MIN_ARGS;

David Odin's avatar
David Odin committed
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
          dvals.amount   = param[3].data.d_float;
          dvals.warp_map = param[4].data.d_int32;
          dvals.iter     = param[5].data.d_int32;

          if (nparams > pcnt++) dvals.dither       = param[6].data.d_float;
          if (nparams > pcnt++) dvals.angle        = param[7].data.d_float;
          if (nparams > pcnt++) dvals.wrap_type    = param[8].data.d_int32;
          if (nparams > pcnt++) dvals.mag_map      = param[9].data.d_int32;
          if (nparams > pcnt++) dvals.mag_use      = param[10].data.d_int32;
          if (nparams > pcnt++) dvals.substeps     = param[11].data.d_int32;
          if (nparams > pcnt++) dvals.grad_map     = param[12].data.d_int32;
          if (nparams > pcnt++) dvals.grad_scale   = param[13].data.d_float;
          if (nparams > pcnt++) dvals.vector_map   = param[14].data.d_int32;
          if (nparams > pcnt++) dvals.vector_scale = param[15].data.d_float;
          if (nparams > pcnt++) dvals.vector_angle = param[16].data.d_float;
        }
Marc Lehmann's avatar
Marc Lehmann committed
322 323
      break;

Sven Neumann's avatar
Sven Neumann committed
324
    case GIMP_RUN_WITH_LAST_VALS:
Marc Lehmann's avatar
Marc Lehmann committed
325
      /*  Possibly retrieve data  */
326
      gimp_get_data (PLUG_IN_PROC, &dvals);
Marc Lehmann's avatar
Marc Lehmann committed
327 328 329 330 331 332
      break;

    default:
      break;
    }

Sven Neumann's avatar
Sven Neumann committed
333
  if (status == GIMP_PDB_SUCCESS)
Marc Lehmann's avatar
Marc Lehmann committed
334 335 336 337
    {
      /*  set the tile cache size  */
      gimp_tile_cache_ntiles (TILE_CACHE_SIZE);

338
      /*  run the warp effect  */
Sven Neumann's avatar
Sven Neumann committed
339
      warp (drawable);
Marc Lehmann's avatar
Marc Lehmann committed
340 341

      /*  Store data  */
Sven Neumann's avatar
Sven Neumann committed
342
      if (run_mode == GIMP_RUN_INTERACTIVE)
David Odin's avatar
David Odin committed
343
        gimp_set_data (PLUG_IN_PROC, &dvals, sizeof (WarpVals));
Marc Lehmann's avatar
Marc Lehmann committed
344 345
    }

Sven Neumann's avatar
Sven Neumann committed
346
  if (run_mode != GIMP_RUN_NONINTERACTIVE)
347
    gimp_displays_flush ();
Sven Neumann's avatar
Sven Neumann committed
348 349 350 351

  gimp_drawable_detach (drawable);

  values[0].data.d_status = status;
Marc Lehmann's avatar
Marc Lehmann committed
352 353
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
static GtkWidget *
spin_button_new (GtkAdjustment **adjustment,  /* return value */
                 gdouble         value,
                 gdouble         lower,
                 gdouble         upper,
                 gdouble         step_increment,
                 gdouble         page_increment,
                 gdouble         page_size,
                 gdouble         climb_rate,
                 guint           digits)
{
  GtkWidget *spinbutton;

  *adjustment = gtk_adjustment_new (value, lower, upper,
                                    step_increment, page_increment, 0);

370
  spinbutton = gtk_spin_button_new (*adjustment,
371 372 373 374 375 376 377
                                    climb_rate, digits);

  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);

  return spinbutton;
}

378
static gboolean
Sven Neumann's avatar
Sven Neumann committed
379
warp_dialog (GimpDrawable *drawable)
Marc Lehmann's avatar
Marc Lehmann committed
380
{
381 382 383 384 385 386
  GtkWidget     *dlg;
  GtkWidget     *vbox;
  GtkWidget     *label;
  GtkWidget     *toggle;
  GtkWidget     *toggle_hbox;
  GtkWidget     *frame;
Simon Budig's avatar
Simon Budig committed
387
  GtkWidget     *grid;
388 389 390 391 392 393 394
  GtkWidget     *spinbutton;
  GtkAdjustment *adj;
  GtkWidget     *combo;
  GtkSizeGroup  *label_group;
  GtkSizeGroup  *spin_group;
  GSList        *group = NULL;
  gboolean       run;
395

396
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
Marc Lehmann's avatar
Marc Lehmann committed
397

398
  dlg = gimp_dialog_new (_("Warp"), PLUG_IN_ROLE,
399
                         NULL, 0,
David Odin's avatar
David Odin committed
400
                         gimp_standard_help_func, PLUG_IN_PROC,
Marc Lehmann's avatar
Marc Lehmann committed
401

402 403
                         _("_Cancel"), GTK_RESPONSE_CANCEL,
                         _("_OK"),     GTK_RESPONSE_OK,
Marc Lehmann's avatar
Marc Lehmann committed
404

405
                         NULL);
Marc Lehmann's avatar
Marc Lehmann committed
406

407
  gimp_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
408 409 410
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
411

412
  gimp_window_set_transient (GTK_WINDOW (dlg));
413

414
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
415
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
416 417
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
                      vbox, TRUE, TRUE, 0);
418 419
  gtk_widget_show (vbox);

420
  frame = gimp_frame_new (_("Basic Options"));
421
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
422
  gtk_widget_show (frame);
Marc Lehmann's avatar
Marc Lehmann committed
423

Simon Budig's avatar
Simon Budig committed
424 425 426 427 428
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_container_add (GTK_CONTAINER (frame), grid);
  gtk_widget_show (grid);
Marc Lehmann's avatar
Marc Lehmann committed
429

430 431 432
  spin_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);

433
  /*  amount, iter */
434 435 436
  spinbutton = spin_button_new (&adj, dvals.amount,
                                -1000, 1000, /* ??? */
                                1, 10, 0, 1, 2);
437 438 439
  gtk_size_group_add_widget (spin_group, spinbutton);
  g_object_unref (spin_group);

Simon Budig's avatar
Simon Budig committed
440 441 442
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                    _("Step size:"), 0.0, 0.5,
                                    spinbutton, 1);
443 444 445
  gtk_size_group_add_widget (label_group, label);
  g_object_unref (label_group);

446
  g_signal_connect (adj, "value-changed",
447 448
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.amount);
Marc Lehmann's avatar
Marc Lehmann committed
449

450 451
  spinbutton = spin_button_new (&adj, dvals.iter,
                                1, 100, 1, 5, 0, 1, 0);
452 453
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
454 455 456
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                    _("Iterations:"), 0.0, 0.5,
                                    spinbutton, 1);
457 458
  gtk_size_group_add_widget (label_group, label);

459
  g_signal_connect (adj, "value-changed",
460 461
                    G_CALLBACK (gimp_int_adjustment_update),
                    &dvals.iter);
Marc Lehmann's avatar
Marc Lehmann committed
462 463

  /*  Displacement map menu  */
464
  label = gtk_label_new (_("Displacement map:"));
465 466
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
  gtk_label_set_yalign (GTK_LABEL (label), 1.0);
Simon Budig's avatar
Simon Budig committed
467 468 469
  gtk_widget_set_margin_start (label, 12);
  gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
                    // GTK_FILL, GTK_FILL, 0, 0);
Marc Lehmann's avatar
Marc Lehmann committed
470 471
  gtk_widget_show (label);

472
  combo = gimp_drawable_combo_box_new (warp_map_constrain, drawable);
Simon Budig's avatar
Simon Budig committed
473
  gtk_widget_set_margin_start (combo, 12);
474 475 476 477
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.warp_map,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &dvals.warp_map);

Simon Budig's avatar
Simon Budig committed
478 479
  gtk_grid_attach (GTK_GRID (grid), combo, 2, 1, 1, 1);
                    // GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
480
  gtk_widget_show (combo);
Marc Lehmann's avatar
Marc Lehmann committed
481

482
  /* ======================================================================= */
Marc Lehmann's avatar
Marc Lehmann committed
483 484

  /*  Displacement Type  */
485
  label = gtk_label_new (_("On edges:"));
486
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
Simon Budig's avatar
Simon Budig committed
487 488
  gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
                    // GTK_FILL, GTK_FILL, 0, 0);
Marc Lehmann's avatar
Marc Lehmann committed
489 490
  gtk_widget_show (label);

491
  toggle_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
Simon Budig's avatar
Simon Budig committed
492 493
  gtk_grid_attach (GTK_GRID (grid), toggle_hbox, 1, 2, 2, 1);
                    // GTK_FILL, GTK_FILL, 0, 0);
494
  gtk_widget_show (toggle_hbox);
495

Manish Singh's avatar
Manish Singh committed
496
  toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
497
  group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
Marc Lehmann's avatar
Marc Lehmann committed
498
  gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
499 500 501 502 503
  gtk_widget_show (toggle);

  g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
                     GINT_TO_POINTER (WRAP));

504
  g_signal_connect (toggle, "toggled",
505 506 507
                    G_CALLBACK (gimp_radio_button_update),
                    &dvals.wrap_type);

508
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
David Odin's avatar
David Odin committed
509
                                dvals.wrap_type == WRAP);
Marc Lehmann's avatar
Marc Lehmann committed
510

Manish Singh's avatar
Manish Singh committed
511
  toggle = gtk_radio_button_new_with_label (group, _("Smear"));
512
  group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
Marc Lehmann's avatar
Marc Lehmann committed
513
  gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
514 515 516 517 518
  gtk_widget_show (toggle);

  g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
                     GINT_TO_POINTER (SMEAR));

519
  g_signal_connect (toggle, "toggled",
520 521 522
                    G_CALLBACK (gimp_radio_button_update),
                    &dvals.wrap_type);

523
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
David Odin's avatar
David Odin committed
524
                                dvals.wrap_type == SMEAR);
Marc Lehmann's avatar
Marc Lehmann committed
525

Manish Singh's avatar
Manish Singh committed
526
  toggle = gtk_radio_button_new_with_label (group, _("Black"));
527
  group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
Marc Lehmann's avatar
Marc Lehmann committed
528
  gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
529 530 531 532 533
  gtk_widget_show (toggle);

  g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
                     GINT_TO_POINTER (BLACK));

534
  g_signal_connect (toggle, "toggled",
535 536 537
                    G_CALLBACK (gimp_radio_button_update),
                    &dvals.wrap_type);

538
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
David Odin's avatar
David Odin committed
539
                                dvals.wrap_type == BLACK);
Marc Lehmann's avatar
Marc Lehmann committed
540

541
  toggle = gtk_radio_button_new_with_label (group, _("Foreground color"));
542
  group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
Marc Lehmann's avatar
Marc Lehmann committed
543
  gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
544 545 546 547 548
  gtk_widget_show (toggle);

  g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
                     GINT_TO_POINTER (COLOR));

549
  g_signal_connect (toggle, "toggled",
550 551 552
                    G_CALLBACK (gimp_radio_button_update),
                    &dvals.wrap_type);

553
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
David Odin's avatar
David Odin committed
554
                                dvals.wrap_type == COLOR);
Marc Lehmann's avatar
Marc Lehmann committed
555 556 557 558



  /* -------------------------------------------------------------------- */
Simon Budig's avatar
Simon Budig committed
559
  /* ---------    The secondary grid          --------------------------  */
Marc Lehmann's avatar
Marc Lehmann committed
560

561
  frame = gimp_frame_new (_("Advanced Options"));
562
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
563
  gtk_widget_show (frame);
Marc Lehmann's avatar
Marc Lehmann committed
564

Simon Budig's avatar
Simon Budig committed
565 566 567 568 569
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_container_add (GTK_CONTAINER (frame), grid);
  gtk_widget_show (grid);
Marc Lehmann's avatar
Marc Lehmann committed
570

571 572
  spinbutton = spin_button_new (&adj, dvals.dither,
                                0, 100, 1, 10, 0, 1, 2);
573 574
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
575 576 577
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                    _("Dither size:"), 0.0, 0.5,
                                    spinbutton, 1);
578 579
  gtk_size_group_add_widget (label_group, label);

580
  g_signal_connect (adj, "value-changed",
581 582
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.dither);
Marc Lehmann's avatar
Marc Lehmann committed
583

584 585
  spinbutton = spin_button_new (&adj, dvals.angle,
                                0, 360, 1, 15, 0, 1, 1);
586 587
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
588 589 590
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                    _("Rotation angle:"), 0.0, 0.5,
                                    spinbutton, 1);
591 592
  gtk_size_group_add_widget (label_group, label);

593
  g_signal_connect (adj, "value-changed",
594 595
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.angle);
Marc Lehmann's avatar
Marc Lehmann committed
596

597 598
  spinbutton = spin_button_new (&adj, dvals.substeps,
                                1, 100, 1, 5, 0, 1, 0);
599 600
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
601 602 603
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 2,
                                    _("Substeps:"), 0.0, 0.5,
                                    spinbutton, 1);
604 605
  gtk_size_group_add_widget (label_group, label);

606
  g_signal_connect (adj, "value-changed",
607 608
                    G_CALLBACK (gimp_int_adjustment_update),
                    &dvals.substeps);
Marc Lehmann's avatar
Marc Lehmann committed
609 610

  /*  Magnitude map menu  */
611
  label = gtk_label_new (_("Magnitude map:"));
612 613
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
  gtk_label_set_yalign (GTK_LABEL (label), 1.0);
Simon Budig's avatar
Simon Budig committed
614 615 616
  gtk_widget_set_margin_start (label, 12);
  gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
                    // GTK_FILL, GTK_FILL, 0, 0);
Marc Lehmann's avatar
Marc Lehmann committed
617 618
  gtk_widget_show (label);

619
  combo = gimp_drawable_combo_box_new (warp_map_constrain, drawable);
Simon Budig's avatar
Simon Budig committed
620
  gtk_widget_set_margin_start (combo, 12);
621 622 623 624
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.mag_map,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &dvals.mag_map);

Simon Budig's avatar
Simon Budig committed
625 626
  gtk_grid_attach (GTK_GRID (grid), combo, 2, 1, 1, 1);
                    // GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
627
  gtk_widget_show (combo);
Marc Lehmann's avatar
Marc Lehmann committed
628 629

  /*  Magnitude Usage  */
630
  toggle_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
631
  gtk_container_set_border_width (GTK_CONTAINER (toggle_hbox), 1);
Simon Budig's avatar
Simon Budig committed
632 633
  gtk_grid_attach (GTK_GRID (grid), toggle_hbox, 2, 2, 1, 1);
                    // GTK_FILL, GTK_FILL, 0, 0);
634
  gtk_widget_show (toggle_hbox);
Marc Lehmann's avatar
Marc Lehmann committed
635

636
  toggle = gtk_check_button_new_with_label (_("Use magnitude map"));
Simon Budig's avatar
Simon Budig committed
637
  gtk_widget_set_margin_start (toggle, 12);
Marc Lehmann's avatar
Marc Lehmann committed
638
  gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
639
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dvals.mag_use);
Marc Lehmann's avatar
Marc Lehmann committed
640 641
  gtk_widget_show (toggle);

642
  g_signal_connect (toggle, "toggled",
643 644 645
                    G_CALLBACK (gimp_toggle_button_update),
                    &dvals.mag_use);

Marc Lehmann's avatar
Marc Lehmann committed
646 647

  /* -------------------------------------------------------------------- */
Simon Budig's avatar
Simon Budig committed
648
  /* ---------    The "other" grid          --------------------------  */
Marc Lehmann's avatar
Marc Lehmann committed
649

650
  frame = gimp_frame_new (_("More Advanced Options"));
651
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
652
  gtk_widget_show (frame);
Marc Lehmann's avatar
Marc Lehmann committed
653

Simon Budig's avatar
Simon Budig committed
654 655 656 657 658
  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_container_add (GTK_CONTAINER (frame), grid);
  gtk_widget_show (grid);
Marc Lehmann's avatar
Marc Lehmann committed
659

660 661 662
  spinbutton = spin_button_new (&adj, dvals.grad_scale,
                                -1000, 1000, /* ??? */
                                0.01, 0.1, 0, 1, 3);
663 664
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
665 666 667
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                    _("Gradient scale:"), 0.0, 0.5,
                                    spinbutton, 1);
668 669
  gtk_size_group_add_widget (label_group, label);

670
  g_signal_connect (adj, "value-changed",
671 672
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.grad_scale);
Marc Lehmann's avatar
Marc Lehmann committed
673 674 675

  /* ---------  Gradient map menu ----------------  */

676
  combo = gimp_drawable_combo_box_new (warp_map_constrain, drawable);
Simon Budig's avatar
Simon Budig committed
677 678 679
  gtk_widget_set_margin_start (combo, 12);
  gtk_grid_attach (GTK_GRID (grid), combo, 2, 0, 1, 1);
                    // GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
680 681
  gtk_widget_show (combo);

682 683 684 685
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.grad_map,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &dvals.grad_map);

686
  gimp_help_set_help_data (combo, _("Gradient map selection menu"), NULL);
Marc Lehmann's avatar
Marc Lehmann committed
687 688 689

  /* ---------------------------------------------- */

690 691 692
  spinbutton = spin_button_new (&adj, dvals.vector_scale,
                                -1000, 1000, /* ??? */
                                0.01, 0.1, 0, 1, 3);
693 694
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
695 696 697
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                    _("Vector mag:"), 0.0, 0.5,
                                    spinbutton, 1);
698 699
  gtk_size_group_add_widget (label_group, label);

700
  g_signal_connect (adj, "value-changed",
701 702
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.vector_scale);
Marc Lehmann's avatar
Marc Lehmann committed
703 704

  /* -------------------------------------------------------- */
705

706 707
  spinbutton = spin_button_new (&adj, dvals.vector_angle,
                                0, 360, 1, 15, 0, 1, 1);
708 709
  gtk_size_group_add_widget (spin_group, spinbutton);

Simon Budig's avatar
Simon Budig committed
710 711 712
  label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 2,
                                    _("Angle:"), 0.0, 0.5,
                                    spinbutton, 1);
713 714
  gtk_size_group_add_widget (label_group, label);

715
  g_signal_connect (adj, "value-changed",
716 717
                    G_CALLBACK (gimp_double_adjustment_update),
                    &dvals.vector_angle);
Marc Lehmann's avatar
Marc Lehmann committed
718 719

  /* ---------  Vector map menu ----------------  */
720
  combo = gimp_drawable_combo_box_new (warp_map_constrain, drawable);
Simon Budig's avatar
Simon Budig committed
721 722 723
  gtk_widget_set_margin_start (combo, 12);
  gtk_grid_attach (GTK_GRID (grid), combo, 2, 1, 1, 1);
                   // GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
724 725
  gtk_widget_show (combo);

726 727 728 729
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.vector_map,
                              G_CALLBACK (gimp_int_combo_box_get_active),
                              &dvals.vector_map);

730
  gimp_help_set_help_data (combo,
David Odin's avatar
David Odin committed
731 732
                           _("Fixed-direction-vector map selection menu"),
                           NULL);
Marc Lehmann's avatar
Marc Lehmann committed
733 734 735

  gtk_widget_show (dlg);

736
  run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
Marc Lehmann's avatar
Marc Lehmann committed
737

738 739 740
  gtk_widget_destroy (dlg);

  return run;
Marc Lehmann's avatar
Marc Lehmann committed
741
}
742

Marc Lehmann's avatar
Marc Lehmann committed
743 744 745
/* ---------------------------------------------------------------------- */

static void
Sven Neumann's avatar
Sven Neumann committed
746
blur16 (GimpDrawable *drawable)
747 748 749 750
{
  /*  blur a 2-or-more byte-per-pixel drawable,
   *  1st 2 bytes interpreted as a 16-bit height field.
   */
Sven Neumann's avatar
Sven Neumann committed
751
  GimpPixelRgn srcPR, destPR;
Marc Lehmann's avatar
Marc Lehmann committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
  gint width, height;
  gint src_bytes;
  gint dest_bytes;
  gint dest_bytes_inc;
  gint offb, off1;

  guchar *dest, *d;  /* pointers to rows of X and Y diff. data */
  guchar *prev_row, *pr;
  guchar *cur_row, *cr;
  guchar *next_row, *nr;
  guchar *tmp;
  gint row, col;  /* relating to indexing into pixel row arrays */
  gint x1, y1, x2, y2;
  gdouble pval;          /* average pixel value of pixel & neighbors */

  /* --------------------------------------- */

769
  if (! gimp_drawable_mask_intersect (drawable->drawable_id,
770
                                      &x1, &y1, &width, &height))
771
    return;
Marc Lehmann's avatar
Marc Lehmann committed
772

773 774 775
  x2 = x1 + width;
  y2 = y1 + height;

Marc Lehmann's avatar
Marc Lehmann committed
776 777 778 779 780 781 782 783
  width = drawable->width;     /* size of input drawable*/
  height = drawable->height;
  src_bytes = drawable->bpp;   /* bytes per pixel in SOURCE drawable, must be 2 or more  */
  dest_bytes = drawable->bpp;   /* bytes per pixel in SOURCE drawable, >= 2  */
  dest_bytes_inc = dest_bytes - 2;  /* this is most likely zero, but I guess it's more conservative... */

  /*  allocate row buffers for source & dest. data  */

784 785 786 787
  prev_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  cur_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  next_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  dest = g_new (guchar, (x2 - x1) * src_bytes);
Marc Lehmann's avatar
Marc Lehmann committed
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807

  /* initialize the pixel regions (read from source, write into dest)  */
  gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);

  pr = prev_row + src_bytes;   /* row arrays are prepared for indexing to -1 (!) */
  cr = cur_row + src_bytes;
  nr = next_row + src_bytes;

  diff_prepare_row (&srcPR, pr, x1, y1, (x2 - x1));
  diff_prepare_row (&srcPR, cr, x1, y1+1, (x2 - x1));

  /*  loop through the rows, applying the smoothing function  */
  for (row = y1; row < y2; row++)
    {
      /*  prepare the next row  */
      diff_prepare_row (&srcPR, nr, x1, row + 1, (x2 - x1));

      d = dest;
      for (col = 0; col < (x2 - x1); col++) /* over columns of pixels */
David Odin's avatar
David Odin committed
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
        {
          offb = col*src_bytes;    /* base of byte pointer offset */
          off1 = offb+1;                 /* offset into row arrays */

          pval = (256.0 * pr[offb - src_bytes] + pr[off1 - src_bytes] +
                  256.0 * pr[offb] + pr[off1] +
                  256.0 * pr[offb + src_bytes] + pr[off1 + src_bytes] +
                  256.0 * cr[offb - src_bytes] + cr[off1 - src_bytes] +
                  256.0 * cr[offb]  + cr[off1] +
                  256.0 * cr[offb + src_bytes] + cr[off1 + src_bytes] +
                  256.0 * nr[offb - src_bytes] + nr[off1 - src_bytes] +
                  256.0 * nr[offb] + nr[off1] +
                  256.0 * nr[offb + src_bytes]) + nr[off1 + src_bytes];

          pval /= 9.0;  /* take the average */
          *d++ = (guchar) (((gint) pval) >> 8);   /* high-order byte */
          *d++ = (guchar) (((gint) pval) % 256);  /* low-order byte  */
          d += dest_bytes_inc;       /* move data pointer on to next destination pixel */

        }
Marc Lehmann's avatar
Marc Lehmann committed
828 829 830 831 832 833 834
      /*  store the dest  */
      gimp_pixel_rgn_set_row (&destPR, dest, x1, row, (x2 - x1));

      /*  shuffle the row pointers  */
      tmp = pr;
      pr = cr;
      cr = nr;
835
      nr = tmp;
Marc Lehmann's avatar
Marc Lehmann committed
836

837
      if ((row % 8) == 0)
David Odin's avatar
David Odin committed
838
        gimp_progress_update ((double) row / (double) (y2 - y1));
Marc Lehmann's avatar
Marc Lehmann committed
839 840
    }

841
  gimp_progress_update (1.0);
Marc Lehmann's avatar
Marc Lehmann committed
842 843
  /*  update the region  */
  gimp_drawable_flush (drawable);
844 845
  gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
Marc Lehmann's avatar
Marc Lehmann committed
846

847 848 849 850
  g_free (prev_row);  /* row buffers allocated at top of fn. */
  g_free (cur_row);
  g_free (next_row);
  g_free (dest);
Marc Lehmann's avatar
Marc Lehmann committed
851 852 853 854 855 856 857 858

} /* end blur16() */


/* ====================================================================== */
/* Get one row of pixels from the PixelRegion and put them in 'data'      */

static void
Sven Neumann's avatar
Sven Neumann committed
859
diff_prepare_row (GimpPixelRgn *pixel_rgn,
David Odin's avatar
David Odin committed
860 861 862 863
                  guchar       *data,
                  gint          x,
                  gint          y,
                  gint          w)
Marc Lehmann's avatar
Marc Lehmann committed
864
{
865
  gint b;
Marc Lehmann's avatar
Marc Lehmann committed
866

867 868 869
  y = CLAMP (y, 0, pixel_rgn->h - 1);

  gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
Marc Lehmann's avatar
Marc Lehmann committed
870 871 872 873

  /*  Fill in edge pixels  */
  for (b = 0; b < pixel_rgn->bpp; b++)
    {
874
      data[b - (gint) pixel_rgn->bpp] = data[b];
Marc Lehmann's avatar
Marc Lehmann committed
875 876 877 878
      data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
    }
}

879 880 881 882
/* -------------------------------------------------------------------------- */
/*  'diff' combines the input drawables to prepare the two                    */
/*  16-bit (X,Y) vector displacement maps                                     */
/* -------------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
883 884

static void
885
diff (GimpDrawable *drawable,
886 887
      gint32       *xl_id,
      gint32       *yl_id)
Marc Lehmann's avatar
Marc Lehmann committed
888
{
Sven Neumann's avatar
Sven Neumann committed
889
  GimpDrawable *draw_xd, *draw_yd; /* vector disp. drawables */
Sven Neumann's avatar
Sven Neumann committed
890
  GimpDrawable *mdraw, *vdraw, *gdraw;
Sven Neumann's avatar
Sven Neumann committed
891 892 893 894
  gint32 image_id;                 /* image holding X and Y diff. arrays */
  gint32 new_image_id;             /* image holding X and Y diff. layers */
  gint32 layer_active;             /* currently active layer */
  gint32 xlayer_id, ylayer_id;     /* individual X and Y layer ID numbers */
Sven Neumann's avatar
Sven Neumann committed
895 896
  GimpPixelRgn srcPR, destxPR, destyPR;
  GimpPixelRgn vecPR, magPR, gradPR;
Marc Lehmann's avatar
Marc Lehmann committed
897 898
  gint width, height;
  gint src_bytes;
Sven Neumann's avatar
Sven Neumann committed
899 900 901
  gint mbytes = 0;
  gint vbytes = 0;
  gint gbytes = 0;   /* bytes-per-pixel of various source drawables */
Marc Lehmann's avatar
Marc Lehmann committed
902 903 904
  gint dest_bytes;
  gint dest_bytes_inc;
  gint do_gradmap = FALSE;          /* whether to add in gradient of gradmap to final diff. map */
Sven Neumann's avatar
Sven Neumann committed
905 906
  gint do_vecmap = FALSE;           /* whether to add in a fixed vector scaled by the vector map */
  gint do_magmap = FALSE;           /* whether to multiply result by the magnitude map */
Marc Lehmann's avatar
Marc Lehmann committed
907 908 909 910 911 912

  guchar *destx, *dx, *desty, *dy;  /* pointers to rows of X and Y diff. data */
  guchar *tmp;
  guchar *prev_row, *pr;
  guchar *cur_row, *cr;
  guchar *next_row, *nr;
Sven Neumann's avatar
Sven Neumann committed
913 914 915 916 917 918
  guchar *prev_row_g, *prg = NULL;  /* pointers to gradient map data */
  guchar *cur_row_g, *crg = NULL;
  guchar *next_row_g, *nrg = NULL;
  guchar *cur_row_v, *crv = NULL;   /* pointers to vector map data */
  guchar *cur_row_m, *crm = NULL;   /* pointers to magnitude map data */
  gint row, col, offb, off, bytes;  /* relating to indexing into pixel row arrays */
Marc Lehmann's avatar
Marc Lehmann committed
919
  gint x1, y1, x2, y2;
Sven Neumann's avatar
Sven Neumann committed
920 921 922 923 924 925 926
  gint dvalx, dvaly;                /* differential value at particular pixel */
  gdouble tx, ty;                   /* temporary x,y differential value increments from gradmap, etc. */
  gdouble rdx, rdy;                 /* x,y differential values: real #s */
  gdouble rscalefac;                /* scaling factor for x,y differential of 'curl' map */
  gdouble gscalefac;                /* scaling factor for x,y differential of 'gradient' map */
  gdouble r, theta, dtheta;         /* rectangular<-> spherical coordinate transform for vector rotation */
  gdouble scale_vec_x, scale_vec_y; /* fixed vector X,Y component scaling factors */
927

928
  /* ----------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
929 930

  if (dvals.grad_scale != 0.0)
Sven Neumann's avatar
Sven Neumann committed
931
    do_gradmap = TRUE;              /* add in gradient of gradmap if scale != 0.000 */
932

Sven Neumann's avatar
Sven Neumann committed
933
  if (dvals.vector_scale != 0.0)    /* add in gradient of vectormap if scale != 0.000 */
934 935
    do_vecmap = TRUE;

Sven Neumann's avatar
Sven Neumann committed
936
  do_magmap = (dvals.mag_use == TRUE); /* multiply by magnitude map if so requested */
Marc Lehmann's avatar
Marc Lehmann committed
937 938 939 940 941 942 943

  /* Get the input area. This is the bounding box of the selection in
   *  the image (or the entire image if there is no selection). Only
   *  operating on the input area is simply an optimization. It doesn't
   *  need to be done for correct operation. (It simply makes it go
   *  faster, since fewer pixels need to be operated on).
   */
944
  if (! gimp_drawable_mask_intersect (drawable->drawable_id,
945
                                      &x1, &y1, &width, &height))
946
    return;
Marc Lehmann's avatar
Marc Lehmann committed
947

948 949 950
  x2 = x1 + width;
  y2 = y1 + height;

Marc Lehmann's avatar
Marc Lehmann committed
951 952 953
  /* Get the size of the input image. (This will/must be the same
   *  as the size of the output image.
   */
954 955 956
  width     = drawable->width;
  height    = drawable->height;
  src_bytes = drawable->bpp;
Marc Lehmann's avatar
Marc Lehmann committed
957 958

  /* -- Add two layers: X and Y Displacement vectors -- */
959 960
  /* -- I'm using a RGB  drawable and using the first two bytes for a
        16-bit pixel value. This is either clever, or a kluge,
Marc Lehmann's avatar
Marc Lehmann committed
961 962
        depending on your point of view.  */

963
  image_id = gimp_item_get_image (drawable->drawable_id);
964
  layer_active = gimp_image_get_active_layer (image_id);
Marc Lehmann's avatar
Marc Lehmann committed
965

Sven Neumann's avatar
Sven Neumann committed
966 967
  /* create new image for X,Y diff */
  new_image_id = gimp_image_new (width, height, GIMP_RGB);
Marc Lehmann's avatar
Marc Lehmann committed
968

969 970
  xlayer_id = gimp_layer_new (new_image_id, "Warp_X_Vectors",
                              width, height,
971
                              GIMP_RGB_IMAGE,
972 973
                              100.0,
                              gimp_image_get_default_new_layer_mode (new_image_id));
Marc Lehmann's avatar
Marc Lehmann committed
974

975 976
  ylayer_id = gimp_layer_new (new_image_id, "Warp_Y_Vectors",
                              width, height,
977
                              GIMP_RGB_IMAGE,
978 979
                              100.0,
                              gimp_image_get_default_new_layer_mode (new_image_id));
Marc Lehmann's avatar
Marc Lehmann committed
980 981 982

  draw_yd = gimp_drawable_get (ylayer_id);
  draw_xd = gimp_drawable_get (xlayer_id);
983

984 985
  gimp_image_insert_layer (new_image_id, xlayer_id, -1, 1);
  gimp_image_insert_layer (new_image_id, ylayer_id, -1, 1);
986 987
  gimp_drawable_fill (xlayer_id, GIMP_FILL_BACKGROUND);
  gimp_drawable_fill (ylayer_id, GIMP_FILL_BACKGROUND);
988
  gimp_image_set_active_layer (image_id, layer_active);
Marc Lehmann's avatar
Marc Lehmann committed
989

990
  dest_bytes = draw_xd->bpp;
Marc Lehmann's avatar
Marc Lehmann committed
991 992 993 994 995
  /* for a GRAYA drawable, I would expect this to be two bytes; any more would be excess */
  dest_bytes_inc = dest_bytes - 2;

  /*  allocate row buffers for source & dest. data  */

996 997 998
  prev_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  cur_row  = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  next_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
Marc Lehmann's avatar
Marc Lehmann committed
999

1000 1001 1002
  prev_row_g = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  cur_row_g  = g_new (guchar, (x2 - x1 + 2) * src_bytes);
  next_row_g = g_new (guchar, (x2 - x1 + 2) * src_bytes);
Marc Lehmann's avatar
Marc Lehmann committed
1003

1004 1005
  cur_row_v = g_new (guchar, (x2 - x1 + 2) * src_bytes);  /* vector map */
  cur_row_m = g_new (guchar, (x2 - x1 + 2) * src_bytes);  /* magnitude map */
Marc Lehmann's avatar
Marc Lehmann committed
1006

1007 1008
  destx = g_new (guchar, (x2 - x1) * dest_bytes);
  desty = g_new (guchar, (x2 - x1) * dest_bytes);
Marc Lehmann's avatar
Marc Lehmann committed
1009 1010

  /*  initialize the source and destination pixel regions  */
1011 1012 1013 1014 1015 1016 1017 1018 1019

  /* 'curl' vector-rotation input */
  gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);

  /* destination: X diff output */
  gimp_pixel_rgn_init (&destxPR, draw_xd, 0, 0, width, height, TRUE, FALSE);

  /* Y diff output */
  gimp_pixel_rgn_init (&destyPR, draw_yd, 0, 0, width, height, TRUE, FALSE);
Marc Lehmann's avatar
Marc Lehmann committed
1020 1021 1022 1023 1024 1025 1026 1027 1028

  pr = prev_row + src_bytes;
  cr = cur_row + src_bytes;
  nr = next_row + src_bytes;

  diff_prepare_row (&srcPR, pr, x1, y1, (x2 - x1));
  diff_prepare_row (&srcPR, cr, x1, y1+1, (x2 - x1));

 /* fixed-vector (x,y) component scale factors */
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
  scale_vec_x = (dvals.vector_scale *
                 cos ((90 - dvals.vector_angle) * G_PI / 180.0) * 256.0 / 10);
  scale_vec_y = (dvals.vector_scale *
                 sin ((90 - dvals.vector_angle) * G_PI / 180.0) * 256.0 / 10);

  if (do_vecmap)
    {
      vdraw = gimp_drawable_get(dvals.vector_map);
      vbytes = vdraw->bpp;   /* bytes per pixel in SOURCE drawable */
      /* fixed-vector scale-map */
      gimp_pixel_rgn_init (&vecPR, vdraw, 0, 0, width, height, FALSE, FALSE);
      crv = cur_row_v + vbytes;
      diff_prepare_row (&vecPR, crv, x1, y1, (x2 - x1));
    }

  if (do_gradmap)
    {
      gdraw = gimp_drawable_get(dvals.grad_map);
      gbytes = gdraw->bpp;
      /* fixed-vector scale-map */
      gimp_pixel_rgn_init (&gradPR, gdraw, 0, 0, width, height, FALSE, FALSE);
      prg = prev_row_g + gbytes;
      crg = cur_row_g + gbytes;
      nrg = next_row_g + gbytes;
      diff_prepare_row (&gradPR, prg, x1, y1 - 1, (x2 - x1));
      diff_prepare_row (&gradPR, crg, x1, y1, (x2 - x1));
    }

  if (do_magmap)
    {
      mdraw = gimp_drawable_get(dvals.mag_map);
      mbytes = mdraw->bpp;
      /* fixed-vector scale-map */
      gimp_pixel_rgn_init (&magPR, mdraw, 0, 0, width, height, FALSE, FALSE);
      crm = cur_row_m + mbytes;
      diff_prepare_row (&magPR, crm, x1, y1, (x2 - x1));
    }
Marc Lehmann's avatar
Marc Lehmann committed
1066

1067
  dtheta = dvals.angle * G_PI / 180.0;
1068 1069 1070 1071
  /* note that '3' is rather arbitrary here. */
  rscalefac = 256.0 / (3 * src_bytes);
  /* scale factor for gradient map components */
  gscalefac = dvals.grad_scale * 256.0 / (3 * gbytes);
Marc Lehmann's avatar
Marc Lehmann committed
1072 1073 1074 1075 1076 1077

  /*  loop through the rows, applying the differential convolution  */
  for (row = y1; row < y2; row++)
    {
      /*  prepare the next row  */
      diff_prepare_row (&srcPR, nr, x1, row + 1, (x2 - x1));
1078

Marc Lehmann's avatar
Marc Lehmann committed
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
      if (do_magmap)
        diff_prepare_row (&magPR, crm, x1, row + 1, (x2 - x1));
      if (do_vecmap)
        diff_prepare_row (&vecPR, crv, x1, row + 1, (x2 - x1));
      if (do_gradmap)
        diff_prepare_row (&gradPR, crg, x1, row + 1, (x2 - x1));

      dx = destx;
      dy = desty;

      for (col = 0; col < (x2 - x1); col++) /* over columns of pixels */
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
        {
          rdx = 0.0;
          rdy = 0.0;
          ty = 0.0;
          tx = 0.0;

          offb = col * src_bytes;    /* base of byte pointer offset */
          for (bytes=0; bytes < src_bytes; bytes++) /* add all channels together */
            {
              off = offb+bytes;                 /* offset into row arrays */
              rdx += ((gint) -pr[off - src_bytes]   + (gint) pr[off + src_bytes] +
                      (gint) -2*cr[off - src_bytes] + (gint) 2*cr[off + src_bytes] +
                      (gint) -nr[off - src_bytes]   + (gint) nr[off + src_bytes]);

              rdy += ((gint) -pr[off - src_bytes] - (gint)2*pr[off] - (gint) pr[off + src_bytes] +
                      (gint) nr[off - src_bytes] + (gint)2*nr[off] + (gint) nr[off + src_bytes]);
            }

          rdx *= rscalefac;   /* take average, then reduce. Assume max. rdx now 65535 */
          rdy *= rscalefac;   /* take average, then reduce */

          theta = atan2(rdy,rdx);          /* convert to polar, then back to rectang. coords */
          r = sqrt(rdy*rdy + rdx*rdx);
          theta += dtheta;              /* rotate gradient vector by this angle (radians) */
          rdx = r * cos(theta);
          rdy = r * sin(theta);

          if (do_gradmap)
            {
              offb = col*gbytes;     /* base of byte pointer offset into pixel values (R,G,B,Alpha, etc.) */
              for (bytes=0; bytes < src_bytes; bytes++) /* add all channels together */
                {
David Odin's avatar
David Odin committed
1122 1123
                  off = offb+bytes;                 /* offset into row arrays */
                  tx += ((gint) -prg[off - gbytes]   + (gint) prg[off + gbytes] +
1124 1125
                         (gint) -2*crg[off - gbytes] + (gint) 2*crg[off + gbytes] +
                         (gint) -nrg[off - gbytes]   + (gint) nrg[off + gbytes]);
Marc Lehmann's avatar
Marc Lehmann committed
1126

David Odin's avatar
David Odin committed
1127
                  ty += ((gint) -prg[off - gbytes] - (gint)2*prg[off] - (gint) prg[off + gbytes] +
1128
                         (gint) nrg[off - gbytes] + (gint)2*nrg[off] + (gint) nrg[off + gbytes]);
David Odin's avatar
David Odin committed
1129
                }
1130 1131
              tx *= gscalefac;
              ty *= gscalefac;
Marc Lehmann's avatar
Marc Lehmann committed
1132

1133 1134
              rdx += tx;         /* add gradient component in to the other one */
              rdy += ty;
Marc Lehmann's avatar
Marc Lehmann committed
1135

1136
            } /* if (do_gradmap) */
1137

1138 1139 1140 1141 1142 1143
          if (do_vecmap)
            {  /* add in fixed vector scaled by  vec. map data */
              tx = (gdouble) crv[col*vbytes];       /* use first byte only */
              rdx += scale_vec_x * tx;
              rdy += scale_vec_y * tx;
            } /* if (do_vecmap) */
Marc Lehmann's avatar
Marc Lehmann committed
1144

1145 1146 1147 1148 1149 1150
          if (do_magmap)
            {  /* multiply result by mag. map data */
              tx = (gdouble) crm[col*mbytes];
              rdx = (rdx * tx)/(255.0);
              rdy = (rdy * tx)/(255.0);
            } /* if do_magmap */
Marc Lehmann's avatar
Marc Lehmann committed
1151

1152 1153
          dvalx = rdx + (2<<14);         /* take zero point to be 2^15, since this is two bytes */
          dvaly = rdy + (2<<14);
Marc Lehmann's avatar
Marc Lehmann committed
1154

1155 1156
          if (dvalx < 0)
            dvalx = 0;
Marc Lehmann's avatar
Marc Lehmann committed
1157

1158 1159
          if (dvalx > 65535)
            dvalx = 65535;
Marc Lehmann's avatar
Marc Lehmann committed
1160

1161 1162 1163
          *dx++ = (guchar) (dvalx >> 8);    /* store high order byte in value channel */
          *dx++ = (guchar) (dvalx % 256);   /* store low order byte in alpha channel */
          dx += dest_bytes_inc;       /* move data pointer on to next destination pixel */
Marc Lehmann's avatar
Marc Lehmann committed
1164

1165 1166
          if (dvaly < 0)
            dvaly = 0;
Marc Lehmann's avatar
Marc Lehmann committed
1167

1168 1169
          if (dvaly > 65535)
            dvaly = 65535;
1170

1171 1172 1173
          *dy++ = (guchar) (dvaly >> 8);
          *dy++ = (guchar) (dvaly % 256);
          dy += dest_bytes_inc;
Marc Lehmann's avatar
Marc Lehmann committed
1174

1175
        } /* ------------------------------- for (col...) ----------------  */
Marc Lehmann's avatar
Marc Lehmann committed
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186

      /*  store the dest  */
      gimp_pixel_rgn_set_row (&destxPR, destx, x1, row, (x2 - x1));
      gimp_pixel_rgn_set_row (&destyPR, desty, x1, row, (x2 - x1));

      /*  swap around the pointers to row buffers  */
      tmp = pr;
      pr = cr;
      cr = nr;
      nr = tmp;

1187 1188 1189 1190 1191 1192 1193
      if (do_gradmap)
        {
          tmp = prg;
          prg = crg;
          crg = nrg;
          nrg = tmp;
        }
Marc Lehmann's avatar
Marc Lehmann committed
1194

1195
      if ((row % 8) == 0)
David Odin's avatar
David Odin committed
1196
        gimp_progress_update ((gdouble) row / (gdouble) (y2 - y1));
Marc Lehmann's avatar
Marc Lehmann committed
1197 1198 1199

    } /* for (row..) */

1200
  gimp_progress_update (1.0);
Marc Lehmann's avatar
Marc Lehmann committed
1201 1202 1203 1204
  /*  update the region  */
  gimp_drawable_flush (draw_xd);
  gimp_drawable_flush (draw_yd);

1205 1206
  gimp_drawable_update (draw_xd->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
  gimp_drawable_update (draw_yd->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
Marc Lehmann's avatar
Marc Lehmann committed
1207 1208 1209

  gimp_displays_flush();  /* make sure layer is visible */

1210
  gimp_progress_init (_("Smoothing X gradient"));
1211
  blur16(draw_xd);
1212

1213
  gimp_progress_init (_("Smoothing Y gradient"));
Marc Lehmann's avatar
Marc Lehmann committed
1214 1215
  blur16(draw_yd);

1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
  g_free (prev_row);  /* row buffers allocated at top of fn. */
  g_free (cur_row);
  g_free (next_row);
  g_free (prev_row_g);  /* row buffers allocated at top of fn. */
  g_free (cur_row_g);
  g_free (next_row_g);
  g_free (cur_row_v);
  g_free (cur_row_m);

  g_free (destx);
  g_free (desty);
Marc Lehmann's avatar
Marc Lehmann committed
1227 1228 1229 1230 1231 1232

  *xl_id = xlayer_id;  /* pass back the X and Y layer ID numbers */
  *yl_id = ylayer_id;

} /* end diff() */

1233 1234 1235
/* -------------------------------------------------------------------------- */
/*            The Warp displacement is done here.                             */
/* -------------------------------------------------------------------------- */
Marc Lehmann's avatar
Marc Lehmann committed
1236

1237
static void
Sven Neumann's avatar
Sven Neumann committed
1238
warp (GimpDrawable *orig_draw)
Marc Lehmann's avatar
Marc Lehmann committed
1239
{
Sven Neumann's avatar
Sven Neumann committed
1240 1241
  GimpDrawable *disp_map;    /* Displacement map, ie, control array */
  GimpDrawable *mag_draw;    /* Magnitude multiplier factor map */
Sven Neumann's avatar
Sven Neumann committed
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
  GimpDrawable *map_x;
  GimpDrawable *map_y;
  gboolean      first_time = TRUE;
  gint          width;
  gint          height;
  gint          x1, y1, x2, y2;
  gint32        xdlayer = -1;
  gint32        ydlayer = -1;
  gint32        image_ID;

  /* index var. over all "warp" Displacement iterations */
  gint          warp_iter;
Marc Lehmann's avatar
Marc Lehmann committed
1254

1255 1256
  disp_map = gimp_drawable_get (dvals.warp_map);
  mag_draw = gimp_drawable_get (dvals.mag_map);
Marc Lehmann's avatar
Marc Lehmann committed
1257 1258 1259

  /* calculate new X,Y Displacement image maps */

1260
  gimp_progress_init (_("Finding XY gradient"));
Marc Lehmann's avatar
Marc Lehmann committed
1261 1262

  /* Get selection area */
1263
  if (! gimp_drawable_mask_intersect (orig_draw->drawable_id,
1264
                                      &x1, &y1, &width, &height))
1265
    return;
Marc Lehmann's avatar
Marc Lehmann committed
1266

1267 1268 1269
  x2 = x1 + width;
  y2 = y1 + height;

1270 1271
  width  = orig_draw->width;
  height = orig_draw->height;
Marc Lehmann's avatar