home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / displace.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  22.2 KB  |  801 lines

  1. /* Displace --- image filter plug-in for The Gimp image manipulation program
  2.  * Copyright (C) 1996 Stephen Robert Norris
  3.  * Much of the code taken from the pinch plug-in by 1996 Federico Mena Quintero
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  *
  19.  * You can contact me at srn@flibble.cs.su.oz.au.
  20.  * Please send me any patches or enhancements to this code.
  21.  * You can contact the original The Gimp authors at gimp@xcf.berkeley.edu
  22.  *
  23.  * Extensive modifications to the dialog box, parameters, and some
  24.  * legibility stuff in displace() by Federico Mena Quintero ---
  25.  * federico@nuclecu.unam.mx.  If there are any bugs in these
  26.  * changes, they are my fault and not Stephen's.
  27.  *
  28.  * JTL: May 29th 1997
  29.  * Added (part of) the patch from Eiichi Takamori -- the part which removes the border artefacts
  30.  * (http://ha1.seikyou.ne.jp/home/taka/gimp/displace/displace.html)
  31.  * Added ability to use transparency as the identity transformation
  32.  * (Full transparency is treated as if it was grey 0.5)
  33.  * and the possibility to use RGB/RGBA pictures where the intensity of the pixel is taken into account
  34.  *
  35.  */
  36.  
  37. /* Version 1.12. */
  38.  
  39. #include "config.h"
  40.  
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #ifdef HAVE_UNISTD_H
  44. #include <unistd.h>
  45. #endif
  46. #include <signal.h>
  47.  
  48. #include <gtk/gtk.h>
  49.  
  50. #include <libgimp/gimp.h>
  51. #include <libgimp/gimpui.h>
  52.  
  53. #include "libgimp/stdplugins-intl.h"
  54.  
  55.  
  56. /* Some useful macros */
  57.  
  58. #define ENTRY_WIDTH     75
  59. #define TILE_CACHE_SIZE 48
  60.  
  61. #define WRAP   0
  62. #define SMEAR  1
  63. #define BLACK  2
  64.  
  65. typedef struct
  66. {
  67.   gdouble amount_x;
  68.   gdouble amount_y;
  69.   gint    do_x;
  70.   gint    do_y;
  71.   gint    displace_map_x;
  72.   gint    displace_map_y;
  73.   gint    displace_type;
  74. } DisplaceVals;
  75.  
  76. typedef struct
  77. {
  78.   gint run;
  79. } DisplaceInterface;
  80.  
  81. /*
  82.  * Function prototypes.
  83.  */
  84.  
  85. static void      query  (void);
  86. static void      run    (gchar    *name,
  87.              gint      nparams,
  88.              GimpParam   *param,
  89.              gint     *nreturn_vals,
  90.              GimpParam  **return_vals);
  91.  
  92. static void      displace        (GimpDrawable *drawable);
  93. static gint      displace_dialog (GimpDrawable *drawable);
  94. static GimpTile *   displace_pixel  (GimpDrawable * drawable,
  95.                   GimpTile *     tile,
  96.                   gint        width,
  97.                   gint        height,
  98.                   gint        x1,
  99.                   gint        y1,
  100.                   gint        x2,
  101.                   gint        y2,
  102.                   gint        x,
  103.                   gint        y,
  104.                   gint *      row,
  105.                   gint *      col,
  106.                   guchar *    pixel);
  107. static guchar    bilinear        (gdouble    x,
  108.                   gdouble    y,
  109.                   guchar *   v);
  110.  
  111. static gint      displace_map_constrain    (gint32     image_id,
  112.                         gint32     drawable_id,
  113.                         gpointer   data);
  114. static void      displace_map_x_callback   (gint32     id,
  115.                         gpointer   data);
  116. static void      displace_map_y_callback   (gint32     id,
  117.                         gpointer   data);
  118. static void      displace_ok_callback      (GtkWidget *widget,
  119.                         gpointer   data);
  120. static gdouble   displace_map_give_value   (guchar* ptr,
  121.                         gint    alpha,
  122.                         gint    bytes);
  123. /***** Local vars *****/
  124.  
  125. GimpPlugInInfo PLUG_IN_INFO =
  126. {
  127.   NULL,  /* init_proc  */
  128.   NULL,  /* quit_proc  */
  129.   query, /* query_proc */
  130.   run,   /* run_proc   */
  131. };
  132.  
  133. static DisplaceVals dvals =
  134. {
  135.   20.0,    /* amount_x */
  136.   20.0,    /* amount_y */
  137.   TRUE,    /* do_x */
  138.   TRUE,    /* do_y */
  139.   -1,      /* displace_map_x */
  140.   -1,      /* displace_map_y */
  141.   WRAP     /* displace_type */
  142. };
  143.  
  144. static DisplaceInterface dint =
  145. {
  146.   FALSE   /*  run  */
  147. };
  148.  
  149. /***** Functions *****/
  150.  
  151. MAIN ()
  152.  
  153. static void
  154. query (void)
  155. {
  156.   static GimpParamDef args[] =
  157.   {
  158.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  159.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  160.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  161.     { GIMP_PDB_FLOAT, "amount_x", "Displace multiplier for X direction" },
  162.     { GIMP_PDB_FLOAT, "amount_y", "Displace multiplier for Y direction" },
  163.     { GIMP_PDB_INT32, "do_x", "Displace in X direction?" },
  164.     { GIMP_PDB_INT32, "do_y", "Displace in Y direction?" },
  165.     { GIMP_PDB_DRAWABLE, "displace_map_x", "Displacement map for X direction" },
  166.     { GIMP_PDB_DRAWABLE, "displace_map_y", "Displacement map for Y direction" },
  167.     { GIMP_PDB_INT32, "displace_type", "Edge behavior: { WRAP (0), SMEAR (1), BLACK (2) }" }
  168.   };
  169.   static gint nargs = sizeof (args) / sizeof (args[0]);
  170.  
  171.   gimp_install_procedure ("plug_in_displace",
  172.               "Displace the contents of the specified drawable",
  173.               "Displaces the contents of the specified drawable "
  174.               "by the amounts specified by 'amount_x' and "
  175.               "'amount_y' multiplied by the intensity of "
  176.               "corresponding pixels in the 'displace_map' "
  177.               "drawables.  Both 'displace_map' drawables must be "
  178.               "of type GIMP_GRAY_IMAGE for this operation to succeed.",
  179.               "Stephen Robert Norris & (ported to 1.0 by) "
  180.               "Spencer Kimball",
  181.               "Stephen Robert Norris",
  182.               "1996",
  183.               N_("<Image>/Filters/Map/Displace..."),
  184.               "RGB*, GRAY*",
  185.               GIMP_PLUGIN,
  186.               nargs, 0,
  187.               args, NULL);
  188. }
  189.  
  190. static void
  191. run (gchar  *name,
  192.      gint    nparams,
  193.      GimpParam  *param,
  194.      gint   *nreturn_vals,
  195.      GimpParam **return_vals)
  196. {
  197.   static GimpParam values[1];
  198.   GimpDrawable *drawable;
  199.   GimpRunModeType run_mode;
  200.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  201.  
  202.   run_mode = param[0].data.d_int32;
  203.  
  204.   /*  Get the specified drawable  */
  205.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  206.  
  207.   *nreturn_vals = 1;
  208.   *return_vals  = values;
  209.   values[0].type          = GIMP_PDB_STATUS;
  210.   values[0].data.d_status = status;
  211.  
  212.   switch (run_mode)
  213.     {
  214.     case GIMP_RUN_INTERACTIVE:
  215.       /*  Possibly retrieve data  */
  216.       INIT_I18N_UI();
  217.       gimp_get_data ("plug_in_displace", &dvals);
  218.  
  219.       /*  First acquire information with a dialog  */
  220.       if (! displace_dialog (drawable))
  221.     return;
  222.       break;
  223.  
  224.     case GIMP_RUN_NONINTERACTIVE:
  225.       INIT_I18N();
  226.       /*  Make sure all the arguments are there!  */
  227.       if (nparams != 10)
  228.     {
  229.       status = GIMP_PDB_CALLING_ERROR;
  230.     }
  231.       else
  232.     {
  233.       dvals.amount_x       = param[3].data.d_float;
  234.       dvals.amount_y       = param[4].data.d_float;
  235.       dvals.do_x           = param[5].data.d_int32;
  236.       dvals.do_y           = param[6].data.d_int32;
  237.       dvals.displace_map_x = param[7].data.d_int32;
  238.       dvals.displace_map_y = param[8].data.d_int32;
  239.       dvals.displace_type  = param[9].data.d_int32;
  240.     }
  241.       break;
  242.  
  243.     case GIMP_RUN_WITH_LAST_VALS:
  244.       /*  Possibly retrieve data  */
  245.       gimp_get_data ("plug_in_displace", &dvals);
  246.       break;
  247.  
  248.     default:
  249.       break;
  250.     }
  251.  
  252.   if (status == GIMP_PDB_SUCCESS && (dvals.do_x || dvals.do_y))
  253.     {
  254.       gimp_progress_init (_("Displacing..."));
  255.  
  256.       /*  set the tile cache size  */
  257.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  258.  
  259.       /*  run the displace effect  */
  260.       displace (drawable);
  261.  
  262.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  263.     gimp_displays_flush ();
  264.  
  265.       /*  Store data  */
  266.       if (run_mode == GIMP_RUN_INTERACTIVE)
  267.     gimp_set_data ("plug_in_displace", &dvals, sizeof (DisplaceVals));
  268.     }
  269.  
  270.   values[0].data.d_status = status;
  271.  
  272.   gimp_drawable_detach (drawable);
  273. }
  274.  
  275. static int
  276. displace_dialog (GimpDrawable *drawable)
  277. {
  278.   GtkWidget *dlg;
  279.   GtkWidget *label;
  280.   GtkWidget *toggle;
  281.   GtkWidget *toggle_hbox;
  282.   GtkWidget *frame;
  283.   GtkWidget *table;
  284.   GtkWidget *spinbutton;
  285.   GtkObject *adj;
  286.   GtkWidget *option_menu;
  287.   GtkWidget *menu;
  288.   GtkWidget *sep;
  289.   GSList *group = NULL;
  290.  
  291.   gimp_ui_init ("displace", FALSE);
  292.  
  293.   dlg = gimp_dialog_new (_("Displace"), "displace",
  294.              gimp_standard_help_func, "filters/displace.html",
  295.              GTK_WIN_POS_MOUSE,
  296.              FALSE, TRUE, FALSE,
  297.  
  298.              _("OK"), displace_ok_callback,
  299.              NULL, NULL, NULL, TRUE, FALSE,
  300.              _("Cancel"), gtk_widget_destroy,
  301.              NULL, 1, NULL, FALSE, TRUE,
  302.  
  303.              NULL);
  304.  
  305.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  306.               GTK_SIGNAL_FUNC (gtk_main_quit),
  307.               NULL);
  308.  
  309.   /*  The main table  */
  310.   frame = gtk_frame_new (_("Displace Options"));
  311.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  312.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  313.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  314.  
  315.   table = gtk_table_new (4, 3, FALSE);
  316.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  317.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  318.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  319.   gtk_container_add (GTK_CONTAINER (frame), table);
  320.  
  321.   /*  X options  */
  322.   toggle = gtk_check_button_new_with_label (_("X Displacement:"));
  323.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 0, 1,
  324.             GTK_FILL, GTK_FILL, 0, 0);
  325.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  326.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  327.               &dvals.do_x);
  328.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dvals.do_x);
  329.   gtk_widget_show (toggle);
  330.  
  331.   spinbutton = gimp_spin_button_new (&adj, dvals.amount_x,
  332.                      (gint) drawable->width * -2,
  333.                      drawable->width * 2,
  334.                      1, 10, 0, 1, 2);
  335.   gtk_table_attach (GTK_TABLE (table), spinbutton, 1, 2, 0, 1,
  336.             GTK_FILL, GTK_FILL, 0, 0);
  337.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  338.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  339.               &dvals.amount_x);
  340.  
  341.   gtk_widget_set_sensitive (spinbutton, dvals.do_x);
  342.   gtk_object_set_data (GTK_OBJECT (toggle), "set_sensitive", spinbutton);
  343.   gtk_widget_show (spinbutton);
  344.  
  345.   option_menu = gtk_option_menu_new ();
  346.   gtk_table_attach (GTK_TABLE (table), option_menu, 2, 3, 0, 1,
  347.             GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  348.   menu = gimp_drawable_menu_new (displace_map_constrain, displace_map_x_callback,
  349.                  drawable, dvals.displace_map_x);
  350.   gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  351.  
  352.   gtk_widget_set_sensitive (option_menu, dvals.do_x);
  353.   gtk_object_set_data (GTK_OBJECT (spinbutton), "set_sensitive", option_menu);
  354.   gtk_widget_show (option_menu);
  355.  
  356.   /*  Y Options  */
  357.   toggle = gtk_check_button_new_with_label (_("Y Displacement:"));
  358.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 1, 2,
  359.             GTK_FILL, GTK_FILL, 0, 0);
  360.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  361.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  362.               &dvals.do_y);
  363.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dvals.do_y);
  364.   gtk_widget_show (toggle);
  365.  
  366.   spinbutton = gimp_spin_button_new (&adj, dvals.amount_y,
  367.                      (gint) drawable->height * -2,
  368.                      drawable->height * 2,
  369.                      1, 10, 0, 1, 2);
  370.   gtk_table_attach (GTK_TABLE (table), spinbutton, 1, 2, 1, 2,
  371.             GTK_FILL, GTK_FILL, 0, 0);
  372.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  373.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  374.               &dvals.amount_y);
  375.  
  376.   gtk_widget_set_sensitive (spinbutton, dvals.do_y);
  377.   gtk_object_set_data (GTK_OBJECT (toggle), "set_sensitive", spinbutton);
  378.   gtk_widget_show (spinbutton);
  379.  
  380.   option_menu = gtk_option_menu_new ();
  381.   gtk_table_attach (GTK_TABLE (table), option_menu, 2, 3, 1, 2,
  382.             GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  383.   menu = gimp_drawable_menu_new (displace_map_constrain, displace_map_y_callback,
  384.                  drawable, dvals.displace_map_y);
  385.   gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  386.  
  387.   gtk_widget_set_sensitive (option_menu, dvals.do_y);
  388.   gtk_object_set_data (GTK_OBJECT (spinbutton), "set_sensitive", option_menu);
  389.   gtk_widget_show (option_menu);
  390.  
  391.   /*  Displacement Type  */
  392.   sep = gtk_hseparator_new ();
  393.   gtk_table_attach_defaults (GTK_TABLE (table), sep, 0, 3, 2, 3);
  394.   gtk_widget_show (sep);
  395.  
  396.   toggle_hbox = gtk_hbox_new (FALSE, 6);
  397.   gtk_table_attach (GTK_TABLE (table), toggle_hbox, 0, 3, 3, 4,
  398.             GTK_FILL, GTK_FILL, 0, 0);
  399.  
  400.   label = gtk_label_new ( _("On Edges:"));
  401.   gtk_box_pack_start (GTK_BOX (toggle_hbox), label, FALSE, FALSE, 0);
  402.   gtk_widget_show (label);
  403.  
  404.   toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
  405.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  406.   gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
  407.   gtk_object_set_user_data (GTK_OBJECT (toggle), (gpointer) WRAP);
  408.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  409.               GTK_SIGNAL_FUNC (gimp_radio_button_update),
  410.               &dvals.displace_type);
  411.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  412.                 dvals.displace_type == WRAP);
  413.   gtk_widget_show (toggle);
  414.  
  415.   toggle = gtk_radio_button_new_with_label (group, _("Smear"));
  416.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  417.   gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
  418.   gtk_object_set_user_data (GTK_OBJECT (toggle), (gpointer) SMEAR);
  419.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  420.               GTK_SIGNAL_FUNC (gimp_radio_button_update),
  421.               &dvals.displace_type);
  422.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  423.                 dvals.displace_type == SMEAR);
  424.   gtk_widget_show (toggle);
  425.  
  426.   toggle = gtk_radio_button_new_with_label (group, _("Black"));
  427.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  428.   gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
  429.   gtk_object_set_user_data (GTK_OBJECT (toggle), (gpointer) BLACK);
  430.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  431.               GTK_SIGNAL_FUNC (gimp_radio_button_update),
  432.               &dvals.displace_type);
  433.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  434.                 dvals.displace_type == BLACK);
  435.   gtk_widget_show (toggle);
  436.  
  437.   gtk_widget_show (toggle_hbox);
  438.   gtk_widget_show (table);
  439.   gtk_widget_show (frame);
  440.   gtk_widget_show (dlg);
  441.  
  442.   gtk_main ();
  443.   gdk_flush ();
  444.  
  445.   return dint.run;
  446. }
  447.  
  448. /* The displacement is done here. */
  449.  
  450. static void
  451. displace (GimpDrawable *drawable)
  452. {
  453.   GimpDrawable *map_x;
  454.   GimpDrawable *map_y;
  455.   GimpPixelRgn dest_rgn;
  456.   GimpPixelRgn map_x_rgn;
  457.   GimpPixelRgn map_y_rgn;
  458.   GimpTile   * tile = NULL;
  459.   gint      row = -1;
  460.   gint      col = -1;
  461.   gpointer  pr;
  462.  
  463.   gint    width;
  464.   gint    height;
  465.   gint    bytes;
  466.   guchar *destrow, *dest;
  467.   guchar *mxrow, *mx;
  468.   guchar *myrow, *my;
  469.   guchar  pixel[4][4];
  470.   gint    x1, y1, x2, y2;
  471.   gint    x, y;
  472.   gint    progress, max_progress;
  473.  
  474.   gdouble amnt;
  475.   gdouble needx, needy;
  476.   gint    xi, yi;
  477.  
  478.   guchar  values[4];
  479.   guchar  val;
  480.  
  481.   gint k;
  482.  
  483.   gdouble xm_val, ym_val;
  484.   gint    xm_alpha = 0;
  485.   gint    ym_alpha = 0;
  486.   gint    xm_bytes = 1;
  487.   gint    ym_bytes = 1;
  488.  
  489.   /* initialize */
  490.  
  491.   mxrow = NULL;
  492.   myrow = NULL;
  493.   
  494.   /* Get selection area */
  495.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  496.  
  497.   width  = drawable->width;
  498.   height = drawable->height;
  499.   bytes  = drawable->bpp;
  500.  
  501.   progress     = 0;
  502.   max_progress = (x2 - x1) * (y2 - y1);
  503.  
  504.   /*
  505.    * The algorithm used here is simple - see
  506.    * http://the-tech.mit.edu/KPT/Tips/KPT7/KPT7.html for a description.
  507.    */
  508.  
  509.   /* Get the drawables  */
  510.   if (dvals.displace_map_x != -1 && dvals.do_x)
  511.     {
  512.       map_x = gimp_drawable_get (dvals.displace_map_x);
  513.       gimp_pixel_rgn_init (&map_x_rgn, map_x,
  514.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  515.       if (gimp_drawable_has_alpha(map_x->id))
  516.     xm_alpha = 1;
  517.       xm_bytes = gimp_drawable_bpp(map_x->id);
  518.     }
  519.   else
  520.     map_x = NULL;
  521.  
  522.   if (dvals.displace_map_y != -1 && dvals.do_y)
  523.     {
  524.       map_y = gimp_drawable_get (dvals.displace_map_y);
  525.       gimp_pixel_rgn_init (&map_y_rgn, map_y,
  526.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  527.       if (gimp_drawable_has_alpha(map_y->id))
  528.     ym_alpha = 1;
  529.       ym_bytes = gimp_drawable_bpp(map_y->id);
  530.     }
  531.   else
  532.     map_y = NULL;
  533.  
  534.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  535.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  536.  
  537.   /*  Register the pixel regions  */
  538.   if (dvals.do_x && dvals.do_y)
  539.     pr = gimp_pixel_rgns_register (3, &dest_rgn, &map_x_rgn, &map_y_rgn);
  540.   else if (dvals.do_x)
  541.     pr = gimp_pixel_rgns_register (2, &dest_rgn, &map_x_rgn);
  542.   else if (dvals.do_y)
  543.     pr = gimp_pixel_rgns_register (2, &dest_rgn, &map_y_rgn);
  544.   else
  545.     pr = NULL;
  546.  
  547.   for (pr = pr; pr != NULL; pr = gimp_pixel_rgns_process (pr))
  548.     {
  549.       destrow = dest_rgn.data;
  550.       if (dvals.do_x)
  551.     mxrow = map_x_rgn.data;
  552.       if (dvals.do_y)
  553.     myrow = map_y_rgn.data;
  554.       
  555.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  556.     {
  557.       dest = destrow;
  558.       mx = mxrow;
  559.       my = myrow;
  560.       
  561.       /*
  562.        * We could move the displacement image address calculation out of here,
  563.        * but when we can have different sized displacement and destination
  564.        * images we'd have to move it back anyway.
  565.        */
  566.       
  567.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  568.         {
  569.           if (dvals.do_x)
  570.         {
  571.           xm_val = displace_map_give_value(mx, xm_alpha, xm_bytes); 
  572.           amnt = dvals.amount_x * (xm_val - 127.5) / 127.5;
  573.           needx = x + amnt;
  574.           mx += xm_bytes;
  575.         }
  576.           else
  577.         needx = x;
  578.           
  579.           if (dvals.do_y)
  580.         {
  581.           ym_val = displace_map_give_value(my, ym_alpha, ym_bytes);
  582.           amnt = dvals.amount_y * (ym_val - 127.5) / 127.5;
  583.           needy = y + amnt;
  584.           my += ym_bytes;
  585.         }
  586.           else
  587.         needy = y;
  588.           
  589.           /* Calculations complete; now copy the proper pixel */
  590.           
  591.           if (needx >= 0.0)
  592.               xi = (int) needx;
  593.           else
  594.               xi = -((int) -needx + 1);
  595.  
  596.           if (needy >= 0.0)
  597.               yi = (int) needy;
  598.           else
  599.               yi = -((int) -needy + 1);
  600.           
  601.           tile = displace_pixel (drawable, tile, width, height, x1, y1, x2, y2, xi, yi, &row, &col, pixel[0]);
  602.           tile = displace_pixel (drawable, tile, width, height, x1, y1, x2, y2, xi + 1, yi, &row, &col, pixel[1]);
  603.           tile = displace_pixel (drawable, tile, width, height, x1, y1, x2, y2, xi, yi + 1, &row, &col, pixel[2]);
  604.           tile = displace_pixel (drawable, tile, width, height, x1, y1, x2, y2, xi + 1, yi + 1, &row, &col, pixel[3]);
  605.           
  606.           for (k = 0; k < bytes; k++)
  607.         {
  608.           values[0] = pixel[0][k];
  609.           values[1] = pixel[1][k];
  610.           values[2] = pixel[2][k];
  611.           values[3] = pixel[3][k];
  612.           val = bilinear(needx, needy, values);
  613.           
  614.           *dest++ = val;
  615.         } /* for */
  616.         }
  617.       
  618.       destrow += dest_rgn.rowstride;
  619.       
  620.       if (dvals.do_x)
  621.         mxrow += map_x_rgn.rowstride;
  622.       if (dvals.do_y)
  623.         myrow += map_y_rgn.rowstride;
  624.     }
  625.       
  626.       progress += dest_rgn.w * dest_rgn.h;
  627.       gimp_progress_update ((double) progress / (double) max_progress);
  628.     } /* for */
  629.  
  630.   if (tile)
  631.     gimp_tile_unref (tile, FALSE);
  632.  
  633.   /*  detach from the map drawables  */
  634.   if (dvals.do_x)
  635.     gimp_drawable_detach (map_x);
  636.   if (dvals.do_y)
  637.     gimp_drawable_detach (map_y);
  638.  
  639.   /*  update the region  */
  640.   gimp_drawable_flush (drawable);
  641.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  642.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  643. } /* displace */
  644.  
  645.  
  646. static gdouble
  647. displace_map_give_value (guchar *pt,
  648.              gint    alpha,
  649.              gint    bytes)
  650. {
  651.   gdouble ret, val_alpha;
  652.   
  653.   if (bytes >= 3)
  654.     ret =  INTENSITY (pt[0], pt[1], pt[2]);
  655.   else
  656.     ret = (gdouble) *pt;
  657.   
  658.   if (alpha)
  659.     {
  660.       val_alpha = pt[bytes - 1];
  661.       ret = ((ret - 127.5) * val_alpha / 255.0) + 127.5;
  662.     };
  663.   
  664.   return (ret);
  665. }
  666.  
  667.  
  668. static GimpTile *
  669. displace_pixel (GimpDrawable *drawable,
  670.         GimpTile     *tile,
  671.         gint       width,
  672.         gint       height,
  673.         gint       x1,
  674.         gint       y1,
  675.         gint       x2,
  676.         gint       y2,
  677.         gint       x,
  678.         gint       y,
  679.         gint      *row,
  680.         gint      *col,
  681.         guchar    *pixel)
  682. {
  683.   static guchar empty_pixel[4] = {0, 0, 0, 0};
  684.   guchar *data;
  685.   gint b;
  686.  
  687.   /* Tile the image. */
  688.   if (dvals.displace_type == WRAP)
  689.     {
  690.       if (x < 0)
  691.     x = width - (-x % width);
  692.       else
  693.     x %= width;
  694.  
  695.       if (y < 0)
  696.     y = height - (-y % height);
  697.       else
  698.     y %= height;
  699.     }
  700.   /* Smear out the edges of the image by repeating pixels. */
  701.   else if (dvals.displace_type == SMEAR)
  702.     {
  703.       if (x < 0)
  704.     x = 0;
  705.       else if (x > width - 1)
  706.     x = width - 1;
  707.  
  708.       if (y < 0)
  709.     y = 0;
  710.       else if (y > height - 1)
  711.     y = height - 1;
  712.     }
  713.  
  714.   if (x >= x1 && y >= y1 && x < x2 && y < y2)
  715.     {
  716.       if ((x >> 6 != *col) || (y >> 6 != *row))
  717.     {
  718.       *col = x / 64;
  719.       *row = y / 64;
  720.       if (tile)
  721.         gimp_tile_unref (tile, FALSE);
  722.       tile = gimp_drawable_get_tile (drawable, FALSE, *row, *col);
  723.       gimp_tile_ref (tile);
  724.     }
  725.  
  726.       data = tile->data + tile->bpp * (tile->ewidth * (y % 64) + (x % 64));
  727.     }
  728.   else
  729.     data = empty_pixel;
  730.  
  731.   for (b = 0; b < drawable->bpp; b++)
  732.     pixel[b] = data[b];
  733.  
  734.   return tile;
  735. }
  736.  
  737.  
  738. static guchar
  739. bilinear (gdouble x,
  740.       gdouble y,
  741.       guchar *v)
  742. {
  743.   gdouble m0, m1;
  744.  
  745.   x = fmod(x, 1.0);
  746.   y = fmod(y, 1.0);
  747.  
  748.   if (x < 0)
  749.     x += 1.0;
  750.   if (y < 0)
  751.     y += 1.0;
  752.  
  753.   m0 = (gdouble) v[0] + x * ((gdouble) v[1] - v[0]);
  754.   m1 = (gdouble) v[2] + x * ((gdouble) v[3] - v[2]);
  755.  
  756.   return (guchar) (m0 + y * (m1 - m0));
  757. }
  758.  
  759. /*  Displace interface functions  */
  760.  
  761. static gint
  762. displace_map_constrain (gint32   image_id,
  763.             gint32   drawable_id,
  764.             gpointer data)
  765. {
  766.   GimpDrawable *drawable;
  767.  
  768.   drawable = (GimpDrawable *) data;
  769.  
  770.   if (drawable_id == -1)
  771.     return TRUE;
  772.  
  773.   if (gimp_drawable_width (drawable_id) == drawable->width &&
  774.       gimp_drawable_height (drawable_id) == drawable->height)
  775.     return TRUE;
  776.   else
  777.     return FALSE;
  778. }
  779.  
  780. static void
  781. displace_map_x_callback (gint32   id,
  782.              gpointer data)
  783. {
  784.   dvals.displace_map_x = id;
  785. }
  786.  
  787. static void
  788. displace_map_y_callback (gint32   id,
  789.              gpointer data)
  790. {
  791.   dvals.displace_map_y = id;
  792. }
  793.  
  794. static void
  795. displace_ok_callback (GtkWidget *widget,
  796.               gpointer   data)
  797. {
  798.   dint.run = TRUE;
  799.   gtk_widget_destroy (GTK_WIDGET (data));
  800. }
  801.