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 / whirlpinch.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  25.5 KB  |  1,040 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Whirl and Pinch plug-in --- two common distortions in one place
  5.  * Copyright (C) 1997 Federico Mena Quintero
  6.  * federico@nuclecu.unam.mx
  7.  * Copyright (C) 1997 Scott Goehring
  8.  * scott@poverty.bloomington.in.us
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23.  */
  24.  
  25.  
  26. /* Version 2.09:
  27.  *
  28.  * - Another cool patch from Scott.  The radius is now in [0.0, 2.0],
  29.  * with 1.0 being the default as usual.  In addition to a minimal
  30.  * speed-up (one multiplication is eliminated in the calculation
  31.  * code), it is easier to think of nice round values instead of
  32.  * sqrt(2) :-)
  33.  *
  34.  * - Modified the way out-of-range pixels are handled.  This time the
  35.  * plug-in handles `outside' pixels better; it paints them with the
  36.  * current background color (for images without transparency) or with
  37.  * a completely transparent background color (for images with
  38.  * transparency).
  39.  */
  40.  
  41.  
  42. /* Version 2.08:
  43.  *
  44.  * This is the first version of this plug-in.  It is called 2.08
  45.  * because it came out of merging the old Whirl 2.08 and Pinch 2.08
  46.  * plug-ins.  */
  47.  
  48. #include "config.h"
  49.  
  50. #include <signal.h>
  51. #include <stdlib.h>
  52. #include <stdio.h>
  53. #ifdef HAVE_UNISTD_H
  54. #include <unistd.h>
  55. #endif
  56.  
  57. #include <gtk/gtk.h>
  58.  
  59. #include <libgimp/gimp.h>
  60. #include <libgimp/gimpui.h>
  61.  
  62. #include "libgimp/stdplugins-intl.h"
  63.  
  64.  
  65. #define PLUG_IN_NAME    "plug_in_whirl_pinch"
  66. #define PLUG_IN_VERSION "May 1997, 2.09"
  67.  
  68. /***** Magic numbers *****/
  69.  
  70. #define PREVIEW_SIZE 128
  71. #define SCALE_WIDTH  200
  72. #define ENTRY_WIDTH  60
  73.  
  74. /***** Types *****/
  75.  
  76. typedef struct
  77. {
  78.   gdouble whirl;
  79.   gdouble pinch;
  80.   gdouble radius;
  81. } whirl_pinch_vals_t;
  82.  
  83. typedef struct
  84. {
  85.   GtkWidget *preview;
  86.   guchar    *check_row_0;
  87.   guchar    *check_row_1;
  88.   guchar    *image;
  89.   guchar    *dimage;
  90.  
  91.   gint run;
  92. } whirl_pinch_interface_t;
  93.  
  94. typedef struct
  95. {
  96.   gint       col, row;
  97.   gint       img_width, img_height, img_bpp, img_has_alpha;
  98.   gint       tile_width, tile_height;
  99.   guchar     bg_color[4];
  100.   GimpDrawable *drawable;
  101.   GimpTile     *tile;
  102. } pixel_fetcher_t;
  103.  
  104.  
  105. /***** Prototypes *****/
  106.  
  107. static void query (void);
  108. static void run   (gchar   *name,
  109.            gint     nparams,
  110.            GimpParam  *param,
  111.            gint    *nreturn_vals,
  112.            GimpParam **return_vals);
  113.  
  114. static void   whirl_pinch (void);
  115. static int    calc_undistorted_coords (double wx, double wy,
  116.                        double whirl, double pinch,
  117.                        double *x, double *y);
  118. static guchar bilinear (double x, double y, guchar *values);
  119.  
  120. static pixel_fetcher_t *pixel_fetcher_new (GimpDrawable *drawable);
  121. static void             pixel_fetcher_set_bg_color (pixel_fetcher_t *pf,
  122.                             guchar r, guchar g,
  123.                             guchar b, guchar a);
  124. static void             pixel_fetcher_get_pixel (pixel_fetcher_t *pf, int x,
  125.                          int y, guchar *pixel);
  126. static void             pixel_fetcher_destroy (pixel_fetcher_t *pf);
  127.  
  128. static void build_preview_source_image (void);
  129.  
  130. static gint whirl_pinch_dialog    (void);
  131. static void dialog_update_preview (void);
  132. static void dialog_scale_update   (GtkAdjustment *adjustment, gdouble *value);
  133. static void dialog_ok_callback    (GtkWidget *widget, gpointer data);
  134.  
  135.  
  136. /***** Variables *****/
  137.  
  138. GimpPlugInInfo PLUG_IN_INFO =
  139. {
  140.   NULL,   /* init_proc  */
  141.   NULL,   /* quit_proc  */
  142.   query,  /* query_proc */
  143.   run     /* run_proc   */
  144. };
  145.  
  146. static whirl_pinch_vals_t wpvals =
  147. {
  148.   90.0, /* whirl */
  149.   0.0,  /* pinch */
  150.   1.0   /* radius */
  151. };
  152.  
  153. static whirl_pinch_interface_t wpint =
  154. {
  155.   NULL,  /* preview */
  156.   NULL,  /* check_row_0 */
  157.   NULL,  /* check_row_1 */
  158.   NULL,  /* image */
  159.   NULL,  /* dimage */
  160.   FALSE  /* run */
  161. };
  162.  
  163. static GimpDrawable *drawable;
  164.  
  165. static gint img_width, img_height, img_bpp, img_has_alpha;
  166. static gint sel_x1, sel_y1, sel_x2, sel_y2;
  167. static gint sel_width, sel_height;
  168. static gint preview_width, preview_height;
  169.  
  170. static double cen_x, cen_y;
  171. static double scale_x, scale_y;
  172. static double radius, radius2;
  173.  
  174.  
  175. /***** Functions *****/
  176.  
  177. MAIN()
  178.  
  179. static void
  180. query (void)
  181. {
  182.   static GimpParamDef args[] =
  183.   {
  184.     { GIMP_PDB_INT32,    "run_mode",  "Interactive, non-interactive" },
  185.     { GIMP_PDB_IMAGE,    "image",     "Input image" },
  186.     { GIMP_PDB_DRAWABLE, "drawable",  "Input drawable" },
  187.     { GIMP_PDB_FLOAT,    "whirl",     "Whirl angle (degrees)" },
  188.     { GIMP_PDB_FLOAT,    "pinch",     "Pinch amount" },
  189.     { GIMP_PDB_FLOAT,    "radius",    "Radius (1.0 is the largest circle that fits in the image, "
  190.       "and 2.0 goes all the way to the corners)" }
  191.   };
  192.   static gint nargs = sizeof (args) / sizeof (args[0]);
  193.  
  194.   gimp_install_procedure (PLUG_IN_NAME,
  195.               "Distort an image by whirling and pinching",
  196.               "Distorts the image by whirling and pinching, which "
  197.               "are two common center-based, circular distortions.  "
  198.               "Whirling is like projecting the image onto the "
  199.               "surface of water in a toilet and flushing.  "
  200.               "Pinching is similar to projecting the image onto "
  201.               "an elastic surface and pressing or pulling on the "
  202.               "center of the surface.",
  203.               "Federico Mena Quintero and Scott Goehring",
  204.               "Federico Mena Quintero and Scott Goehring",
  205.               PLUG_IN_VERSION,
  206.               N_("<Image>/Filters/Distorts/Whirl and Pinch..."),
  207.               "RGB*, GRAY*",
  208.               GIMP_PLUGIN,
  209.               nargs, 0,
  210.               args, NULL);
  211. }
  212.  
  213. static void
  214. run (gchar   *name,
  215.      gint     nparams,
  216.      GimpParam  *param,
  217.      gint    *nreturn_vals,
  218.      GimpParam **return_vals)
  219. {
  220.   static GimpParam values[1];
  221.  
  222.   GimpRunModeType run_mode;
  223.   GimpPDBStatusType  status;
  224.   double       xhsiz, yhsiz;
  225.   int          pwidth, pheight;
  226.  
  227.   status   = GIMP_PDB_SUCCESS;
  228.   run_mode = param[0].data.d_int32;
  229.  
  230.   values[0].type          = GIMP_PDB_STATUS;
  231.   values[0].data.d_status = status;
  232.  
  233.   *nreturn_vals = 1;
  234.   *return_vals  = values;
  235.  
  236.   /* Get the active drawable info */
  237.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  238.  
  239.   img_width     = gimp_drawable_width (drawable->id);
  240.   img_height    = gimp_drawable_height (drawable->id);
  241.   img_bpp       = gimp_drawable_bpp (drawable->id);
  242.   img_has_alpha = gimp_drawable_has_alpha (drawable->id);
  243.  
  244.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  245.  
  246.   /* Calculate scaling parameters */
  247.  
  248.   sel_width  = sel_x2 - sel_x1;
  249.   sel_height = sel_y2 - sel_y1;
  250.  
  251.   cen_x = (double) (sel_x1 + sel_x2 - 1) / 2.0;
  252.   cen_y = (double) (sel_y1 + sel_y2 - 1) / 2.0;
  253.  
  254.   xhsiz = (double) (sel_width - 1) / 2.0;
  255.   yhsiz = (double) (sel_height - 1) / 2.0;
  256.  
  257.   if (xhsiz < yhsiz)
  258.     {
  259.       scale_x = yhsiz / xhsiz;
  260.       scale_y = 1.0;
  261.     }
  262.   else if (xhsiz > yhsiz)
  263.     {
  264.       scale_x = 1.0;
  265.       scale_y = xhsiz / yhsiz;
  266.     }
  267.   else
  268.     {
  269.       scale_x = 1.0;
  270.       scale_y = 1.0;
  271.     }
  272.  
  273.   radius = MAX(xhsiz, yhsiz);
  274.  
  275.   /* Calculate preview size */
  276.  
  277.   if (sel_width > sel_height)
  278.     {
  279.       pwidth  = MIN (sel_width, PREVIEW_SIZE);
  280.       pheight = sel_height * pwidth / sel_width;
  281.     }
  282.   else
  283.     {
  284.       pheight = MIN (sel_height, PREVIEW_SIZE);
  285.       pwidth  = sel_width * pheight / sel_height;
  286.     }
  287.  
  288.   preview_width  = MAX (pwidth, 2); /* Min size is 2 */
  289.   preview_height = MAX (pheight, 2);
  290.  
  291.   /* See how we will run */
  292.  
  293.   switch (run_mode)
  294.     {
  295.     case GIMP_RUN_INTERACTIVE:
  296.       INIT_I18N_UI();
  297.       /* Possibly retrieve data */
  298.       gimp_get_data (PLUG_IN_NAME, &wpvals);
  299.  
  300.       /* Get information from the dialog */
  301.       if (!whirl_pinch_dialog ())
  302.     return;
  303.  
  304.       break;
  305.  
  306.     case GIMP_RUN_NONINTERACTIVE:
  307.       INIT_I18N();
  308.       /* Make sure all the arguments are present */
  309.       if (nparams != 6)
  310.     {
  311.       status = GIMP_PDB_CALLING_ERROR;
  312.     }
  313.       else
  314.     {
  315.       wpvals.whirl  = param[3].data.d_float;
  316.       wpvals.pinch  = param[4].data.d_float;
  317.       wpvals.radius = param[5].data.d_float;
  318.     }
  319.  
  320.       break;
  321.  
  322.     case GIMP_RUN_WITH_LAST_VALS:
  323.       INIT_I18N();
  324.       /* Possibly retrieve data */
  325.       gimp_get_data (PLUG_IN_NAME, &wpvals);
  326.       break;
  327.  
  328.     default:
  329.       break;
  330.     }
  331.  
  332.   /* Distort the image */
  333.   if ((status == GIMP_PDB_SUCCESS) &&
  334.       (gimp_drawable_is_rgb (drawable->id) ||
  335.        gimp_drawable_is_gray (drawable->id)))
  336.     {
  337.       /* Set the tile cache size */
  338.       gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width () - 1) /
  339.                   gimp_tile_width ());
  340.  
  341.       /* Run! */
  342.       whirl_pinch ();
  343.  
  344.       /* If run mode is interactive, flush displays */
  345.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  346.     gimp_displays_flush ();
  347.  
  348.       /* Store data */
  349.  
  350.       if (run_mode == GIMP_RUN_INTERACTIVE)
  351.     gimp_set_data (PLUG_IN_NAME, &wpvals, sizeof (whirl_pinch_vals_t));
  352.     }
  353.   else if (status == GIMP_PDB_SUCCESS)
  354.     status = GIMP_PDB_EXECUTION_ERROR;
  355.  
  356.   values[0].data.d_status = status;
  357.  
  358.   gimp_drawable_detach (drawable);
  359. }
  360.  
  361. static void
  362. whirl_pinch (void)
  363. {
  364.   GimpPixelRgn        dest_rgn;
  365.   gint             progress, max_progress;
  366.   guchar          *top_row, *bot_row;
  367.   guchar          *top_p, *bot_p;
  368.   gint             row, col;
  369.   guchar           pixel[4][4];
  370.   guchar           values[4];
  371.   double           whirl;
  372.   double           cx, cy;
  373.   int              ix, iy;
  374.   int              i;
  375.   guchar           bg_color[4];
  376.   pixel_fetcher_t *pft, *pfb;
  377.  
  378.   /* Initialize rows */
  379.   top_row = g_malloc (img_bpp * sel_width);
  380.   bot_row = g_malloc (img_bpp * sel_width);
  381.  
  382.   /* Initialize pixel region */
  383.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  384.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  385.  
  386.   pft = pixel_fetcher_new (drawable);
  387.   pfb = pixel_fetcher_new (drawable);
  388.  
  389.   gimp_palette_get_background (&bg_color[0], &bg_color[1], &bg_color[2]);
  390.   pixel_fetcher_set_bg_color (pft,
  391.                   bg_color[0],
  392.                   bg_color[1],
  393.                   bg_color[2],
  394.                   (img_has_alpha ? 0 : 255));
  395.   pixel_fetcher_set_bg_color (pfb,
  396.                   bg_color[0],
  397.                   bg_color[1],
  398.                   bg_color[2],
  399.                   (img_has_alpha ? 0 : 255));
  400.  
  401.   progress     = 0;
  402.   max_progress = sel_width * sel_height;
  403.  
  404.   gimp_progress_init (_("Whirling and pinching..."));
  405.  
  406.   whirl   = wpvals.whirl * G_PI / 180;
  407.   radius2 = radius * radius * wpvals.radius;
  408.  
  409.   for (row = sel_y1; row <= ((sel_y1 + sel_y2) / 2); row++)
  410.     {
  411.       top_p = top_row;
  412.       bot_p = bot_row + img_bpp * (sel_width - 1);
  413.  
  414.       for (col = sel_x1; col < sel_x2; col++)
  415.     {
  416.       if (calc_undistorted_coords (col, row, whirl, wpvals.pinch, &cx, &cy))
  417.         {
  418.           /* We are inside the distortion area */
  419.  
  420.           /* Top */
  421.  
  422.           if (cx >= 0.0)
  423.         ix = (int) cx;
  424.           else
  425.         ix = -((int) -cx + 1);
  426.  
  427.           if (cy >= 0.0)
  428.         iy = (int) cy;
  429.           else
  430.         iy = -((int) -cy + 1);
  431.  
  432.           pixel_fetcher_get_pixel (pft, ix,     iy,     pixel[0]);
  433.           pixel_fetcher_get_pixel (pft, ix + 1, iy,     pixel[1]);
  434.           pixel_fetcher_get_pixel (pft, ix,     iy + 1, pixel[2]);
  435.           pixel_fetcher_get_pixel (pft, ix + 1, iy + 1, pixel[3]);
  436.  
  437.           for (i = 0; i < img_bpp; i++)
  438.         {
  439.           values[0] = pixel[0][i];
  440.           values[1] = pixel[1][i];
  441.           values[2] = pixel[2][i];
  442.           values[3] = pixel[3][i];
  443.  
  444.           *top_p++ = bilinear (cx, cy, values);
  445.         }
  446.  
  447.           /* Bottom */
  448.  
  449.           cx = cen_x + (cen_x - cx);
  450.           cy = cen_y + (cen_y - cy);
  451.  
  452.           if (cx >= 0.0)
  453.         ix = (int) cx;
  454.           else
  455.         ix = -((int) -cx + 1);
  456.  
  457.           if (cy >= 0.0)
  458.         iy = (int) cy;
  459.           else
  460.         iy = -((int) -cy + 1);
  461.  
  462.           pixel_fetcher_get_pixel (pfb, ix,     iy,     pixel[0]);
  463.           pixel_fetcher_get_pixel (pfb, ix + 1, iy,     pixel[1]);
  464.           pixel_fetcher_get_pixel (pfb, ix,     iy + 1, pixel[2]);
  465.           pixel_fetcher_get_pixel (pfb, ix + 1, iy + 1, pixel[3]);
  466.  
  467.           for (i = 0; i < img_bpp; i++)
  468.         {
  469.           values[0] = pixel[0][i];
  470.           values[1] = pixel[1][i];
  471.           values[2] = pixel[2][i];
  472.           values[3] = pixel[3][i];
  473.  
  474.           *bot_p++ = bilinear (cx, cy, values);
  475.         }
  476.  
  477.           bot_p -= 2 * img_bpp; /* We move backwards! */
  478.         }
  479.       else
  480.         {
  481.           /*  We are outside the distortion area;
  482.            *  just copy the source pixels
  483.            */
  484.  
  485.           /* Top */
  486.  
  487.           pixel_fetcher_get_pixel (pft, col, row, pixel[0]);
  488.  
  489.           for (i = 0; i < img_bpp; i++)
  490.         *top_p++ = pixel[0][i];
  491.  
  492.           /* Bottom */
  493.  
  494.           pixel_fetcher_get_pixel (pfb,
  495.                        (sel_x2 - 1) - (col - sel_x1),
  496.                        (sel_y2 - 1) - (row - sel_y1),
  497.                        pixel[0]);
  498.  
  499.           for (i = 0; i < img_bpp; i++)
  500.         *bot_p++ = pixel[0][i];
  501.  
  502.           bot_p -= 2 * img_bpp; /* We move backwards! */
  503.         }
  504.     }
  505.  
  506.       /* Paint rows to image */
  507.  
  508.       gimp_pixel_rgn_set_row (&dest_rgn, top_row, sel_x1, row, sel_width);
  509.       gimp_pixel_rgn_set_row (&dest_rgn, bot_row,
  510.                   sel_x1, (sel_y2 - 1) - (row - sel_y1), sel_width);
  511.  
  512.       /* Update progress */
  513.  
  514.       progress += sel_width * 2;
  515.       gimp_progress_update ((double) progress / max_progress);
  516.     }
  517.  
  518.   pixel_fetcher_destroy (pft);
  519.   pixel_fetcher_destroy (pfb);
  520.  
  521.   g_free (top_row);
  522.   g_free (bot_row);
  523.  
  524.   gimp_drawable_flush (drawable);
  525.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  526.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  527. }
  528.  
  529. static gint
  530. calc_undistorted_coords (gdouble  wx,
  531.              gdouble  wy,
  532.              gdouble  whirl,
  533.              gdouble  pinch,
  534.              gdouble *x,
  535.              gdouble *y)
  536. {
  537.   gdouble dx, dy;
  538.   gdouble d, factor;
  539.   gdouble dist;
  540.   gdouble ang, sina, cosa;
  541.   gint    inside;
  542.  
  543.   /* Distances to center, scaled */
  544.  
  545.   dx = (wx - cen_x) * scale_x;
  546.   dy = (wy - cen_y) * scale_y;
  547.  
  548.   /* Distance^2 to center of *circle* (scaled ellipse) */
  549.  
  550.   d = dx * dx + dy * dy;
  551.  
  552.   /*  If we are inside circle, then distort.
  553.    *  Else, just return the same position
  554.    */
  555.  
  556.   inside = (d < radius2);
  557.  
  558.   if (inside)
  559.     {
  560.       dist = sqrt(d / wpvals.radius) / radius;
  561.  
  562.       /* Pinch */
  563.  
  564.       factor = pow (sin (G_PI_2 * dist), -pinch);
  565.  
  566.       dx *= factor;
  567.       dy *= factor;
  568.  
  569.       /* Whirl */
  570.  
  571.       factor = 1.0 - dist;
  572.  
  573.       ang = whirl * factor * factor;
  574.  
  575.       sina = sin (ang);
  576.       cosa = cos (ang);
  577.  
  578.       *x = (cosa * dx - sina * dy) / scale_x + cen_x;
  579.       *y = (sina * dx + cosa * dy) / scale_y + cen_y;
  580.     }
  581.   else
  582.     {
  583.       *x = wx;
  584.       *y = wy;
  585.     }
  586.  
  587.   return inside;
  588. }
  589.  
  590. static guchar
  591. bilinear (gdouble  x,
  592.       gdouble  y,
  593.       guchar  *values)
  594. {
  595.   gdouble m0, m1;
  596.  
  597.   x = fmod (x, 1.0);
  598.   y = fmod (y, 1.0);
  599.  
  600.   if (x < 0.0)
  601.     x += 1.0;
  602.  
  603.   if (y < 0.0)
  604.     y += 1.0;
  605.  
  606.   m0 = (double) values[0] + x * ((double) values[1] - values[0]);
  607.   m1 = (double) values[2] + x * ((double) values[3] - values[2]);
  608.  
  609.   return (guchar) (m0 + y * (m1 - m0));
  610. }
  611.  
  612. static pixel_fetcher_t *
  613. pixel_fetcher_new (GimpDrawable *drawable)
  614. {
  615.   pixel_fetcher_t *pf;
  616.  
  617.   pf = g_malloc (sizeof (pixel_fetcher_t));
  618.  
  619.   pf->col           = -1;
  620.   pf->row           = -1;
  621.   pf->img_width     = gimp_drawable_width (drawable->id);
  622.   pf->img_height    = gimp_drawable_height (drawable->id);
  623.   pf->img_bpp       = gimp_drawable_bpp (drawable->id);
  624.   pf->img_has_alpha = gimp_drawable_has_alpha (drawable->id);
  625.   pf->tile_width    = gimp_tile_width ();
  626.   pf->tile_height   = gimp_tile_height ();
  627.   pf->bg_color[0]   = 0;
  628.   pf->bg_color[1]   = 0;
  629.   pf->bg_color[2]   = 0;
  630.   pf->bg_color[3]   = 0;
  631.  
  632.   pf->drawable    = drawable;
  633.   pf->tile        = NULL;
  634.  
  635.   return pf;
  636. }
  637.  
  638. static void
  639. pixel_fetcher_set_bg_color (pixel_fetcher_t *pf,
  640.                 guchar           r,
  641.                 guchar           g,
  642.                 guchar           b,
  643.                 guchar           a)
  644. {
  645.   pf->bg_color[0] = r;
  646.   pf->bg_color[1] = g;
  647.   pf->bg_color[2] = b;
  648.  
  649.   if (pf->img_has_alpha)
  650.     pf->bg_color[pf->img_bpp - 1] = a;
  651. }
  652.  
  653. static void
  654. pixel_fetcher_get_pixel (pixel_fetcher_t *pf,
  655.              gint             x,
  656.              gint             y,
  657.              guchar          *pixel)
  658. {
  659.   gint    col, row;
  660.   gint    coloff, rowoff;
  661.   guchar *p;
  662.   gint    i;
  663.  
  664.   if ((x < sel_x1) || (x >= sel_x2) ||
  665.       (y < sel_y1) || (y >= sel_y2))
  666.     {
  667.       for (i = 0; i < pf->img_bpp; i++)
  668.     pixel[i] = pf->bg_color[i];
  669.  
  670.       return;
  671.     }
  672.  
  673.   col    = x / pf->tile_width;
  674.   coloff = x % pf->tile_width;
  675.   row    = y / pf->tile_height;
  676.   rowoff = y % pf->tile_height;
  677.  
  678.   if ((col != pf->col) ||
  679.       (row != pf->row) ||
  680.       (pf->tile == NULL))
  681.     {
  682.       if (pf->tile != NULL)
  683.     gimp_tile_unref (pf->tile, FALSE);
  684.  
  685.       pf->tile = gimp_drawable_get_tile (pf->drawable, FALSE, row, col);
  686.       gimp_tile_ref (pf->tile);
  687.  
  688.       pf->col = col;
  689.       pf->row = row;
  690.     }
  691.  
  692.   p = pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
  693.  
  694.   for (i = pf->img_bpp; i; i--)
  695.     *pixel++ = *p++;
  696. }
  697.  
  698. static void
  699. pixel_fetcher_destroy (pixel_fetcher_t *pf)
  700. {
  701.   if (pf->tile != NULL)
  702.     gimp_tile_unref (pf->tile, FALSE);
  703.  
  704.   g_free (pf);
  705. }
  706.  
  707. static void
  708. build_preview_source_image (void)
  709. {
  710.   gdouble          left, right, bottom, top;
  711.   gdouble          px, py;
  712.   gdouble          dx, dy;
  713.   gint             x, y;
  714.   guchar          *p;
  715.   guchar           pixel[4];
  716.   pixel_fetcher_t *pf;
  717.  
  718.   wpint.check_row_0 = g_new (guchar, preview_width);
  719.   wpint.check_row_1 = g_new (guchar, preview_width);
  720.   wpint.image       = g_new (guchar, preview_width * preview_height * 4);
  721.   wpint.dimage      = g_new (guchar, preview_width * preview_height * 3);
  722.  
  723.   left   = sel_x1;
  724.   right  = sel_x2 - 1;
  725.   bottom = sel_y2 - 1;
  726.   top    = sel_y1;
  727.  
  728.   dx = (right - left) / (preview_width - 1);
  729.   dy = (bottom - top) / (preview_height - 1);
  730.  
  731.   py = top;
  732.  
  733.   pf = pixel_fetcher_new (drawable);
  734.  
  735.   p = wpint.image;
  736.  
  737.   for (y = 0; y < preview_height; y++)
  738.     {
  739.       px = left;
  740.  
  741.       for (x = 0; x < preview_width; x++)
  742.     {
  743.       /* Checks */
  744.  
  745.       if ((x / GIMP_CHECK_SIZE) & 1)
  746.         {
  747.           wpint.check_row_0[x] = GIMP_CHECK_DARK * 255;
  748.           wpint.check_row_1[x] = GIMP_CHECK_LIGHT * 255;
  749.         }
  750.       else
  751.         {
  752.           wpint.check_row_0[x] = GIMP_CHECK_LIGHT * 255;
  753.           wpint.check_row_1[x] = GIMP_CHECK_DARK * 255;
  754.         }
  755.  
  756.       /* Thumbnail image */
  757.  
  758.       pixel_fetcher_get_pixel (pf, (int) px, (int) py, pixel);
  759.  
  760.       if (img_bpp < 3)
  761.         {
  762.           if (img_has_alpha)
  763.         pixel[3] = pixel[1];
  764.           else
  765.         pixel[3] = 255;
  766.  
  767.           pixel[1] = pixel[0];
  768.           pixel[2] = pixel[0];
  769.         }
  770.       else
  771.         if (!img_has_alpha)
  772.           pixel[3] = 255;
  773.  
  774.       *p++ = pixel[0];
  775.       *p++ = pixel[1];
  776.       *p++ = pixel[2];
  777.       *p++ = pixel[3];
  778.  
  779.       px += dx;
  780.     }
  781.  
  782.       py += dy;
  783.     }
  784.  
  785.   pixel_fetcher_destroy (pf);
  786. }
  787.  
  788. static gint
  789. whirl_pinch_dialog (void)
  790. {
  791.   GtkWidget *dialog;
  792.   GtkWidget *main_vbox;
  793.   GtkWidget *frame;
  794.   GtkWidget *abox;
  795.   GtkWidget *pframe;
  796.   GtkWidget *table;
  797.   GtkObject *adj;
  798.  
  799.   gimp_ui_init ("whirlpinch", TRUE);
  800.  
  801.   build_preview_source_image ();
  802.  
  803.   dialog = gimp_dialog_new ( _("Whirl and Pinch"), "whirlpinch",
  804.                 gimp_standard_help_func, "filters/whirlpinch.html",
  805.                 GTK_WIN_POS_MOUSE,
  806.                 FALSE, TRUE, FALSE,
  807.  
  808.                 _("OK"), dialog_ok_callback,
  809.                 NULL, NULL, NULL, TRUE, FALSE,
  810.                 _("Cancel"), gtk_widget_destroy,
  811.                 NULL, 1, NULL, FALSE, TRUE,
  812.  
  813.                 NULL);
  814.  
  815.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  816.               GTK_SIGNAL_FUNC (gtk_main_quit),
  817.               NULL);
  818.  
  819.   main_vbox = gtk_vbox_new (FALSE, 4);
  820.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  821.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->vbox), main_vbox,
  822.               FALSE, FALSE, 0);
  823.   gtk_widget_show (main_vbox);
  824.  
  825.   /* Preview */
  826.   frame = gtk_frame_new (_("Preview"));
  827.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  828.   gtk_widget_show (frame);
  829.  
  830.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  831.   gtk_container_add (GTK_CONTAINER (frame), abox);
  832.   gtk_widget_show (abox);
  833.  
  834.   pframe = gtk_frame_new (NULL);
  835.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  836.   gtk_container_set_border_width (GTK_CONTAINER (pframe), 4);
  837.   gtk_container_add (GTK_CONTAINER (abox), pframe);
  838.   gtk_widget_show (pframe);
  839.  
  840.   /* Preview */
  841.   wpint.preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  842.   gtk_preview_size (GTK_PREVIEW (wpint.preview), preview_width, preview_height);
  843.   gtk_container_add (GTK_CONTAINER (pframe), wpint.preview);
  844.   gtk_widget_show (wpint.preview);
  845.  
  846.   /* Controls */
  847.   frame = gtk_frame_new (_("Parameter Settings"));
  848.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  849.   gtk_widget_show (frame);
  850.  
  851.   table = gtk_table_new (3, 3, FALSE);
  852.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  853.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  854.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  855.   gtk_container_add (GTK_CONTAINER (frame), table);
  856.   gtk_widget_show (table);
  857.  
  858.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  859.                   _("Whirl Angle:"), SCALE_WIDTH, 0,
  860.                   wpvals.whirl, -360.0, 360.0, 1.0, 15.0, 2,
  861.                   TRUE, 0, 0,
  862.                   NULL, NULL);
  863.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  864.               GTK_SIGNAL_FUNC (dialog_scale_update),
  865.               &wpvals.whirl);
  866.  
  867.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  868.                   _("Pinch Amount:"), SCALE_WIDTH, 0,
  869.                   wpvals.pinch, -1.0, 1.0, 0.01, 0.1, 3,
  870.                   TRUE, 0, 0,
  871.                   NULL, NULL);
  872.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  873.               GTK_SIGNAL_FUNC (dialog_scale_update),
  874.               &wpvals.pinch);
  875.  
  876.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  877.                   _("Radius:"), SCALE_WIDTH, 0,
  878.                   wpvals.radius, 0.0, 2.0, 0.01, 0.1, 3,
  879.                   TRUE, 0, 0,
  880.                   NULL, NULL);
  881.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  882.               GTK_SIGNAL_FUNC (dialog_scale_update),
  883.               &wpvals.radius);
  884.  
  885.   /* Done */
  886.  
  887.   gtk_widget_show (dialog);
  888.   dialog_update_preview ();
  889.  
  890.   gtk_main ();
  891.   gdk_flush ();
  892.  
  893.   g_free (wpint.check_row_0);
  894.   g_free (wpint.check_row_1);
  895.   g_free (wpint.image);
  896.   g_free (wpint.dimage);
  897.  
  898.   return wpint.run;
  899. }
  900.  
  901. static void
  902. dialog_update_preview (void)
  903. {
  904.   gdouble  left, right, bottom, top;
  905.   gdouble  dx, dy;
  906.   gdouble  px, py;
  907.   gdouble  cx, cy;
  908.   gint     ix, iy;
  909.   gint     x, y;
  910.   gdouble  whirl;
  911.   gdouble  scale_x, scale_y;
  912.   guchar  *p_ul, *p_lr, *i, *p;
  913.   guchar  *check_ul, *check_lr;
  914.   gint     check;
  915.   guchar   outside[4];
  916.  
  917.   gimp_palette_get_background(&outside[0], &outside[1], &outside[2]);
  918.   outside[3] = (img_has_alpha ? 0 : 255);
  919.  
  920.   if (img_bpp < 3)
  921.     {
  922.       outside[1] = outside[0];
  923.       outside[2] = outside[0];
  924.     }
  925.  
  926.   left   = sel_x1;
  927.   right  = sel_x2 - 1;
  928.   bottom = sel_y2 - 1;
  929.   top    = sel_y1;
  930.  
  931.   dx = (right - left) / (preview_width - 1);
  932.   dy = (bottom - top) / (preview_height - 1);
  933.  
  934.   whirl   = wpvals.whirl * G_PI / 180.0;
  935.   radius2 = radius * radius * wpvals.radius;
  936.  
  937.   scale_x = (double) preview_width / (right - left + 1);
  938.   scale_y = (double) preview_height / (bottom - top + 1);
  939.  
  940.   py = top;
  941.  
  942.   p_ul = wpint.dimage;
  943.   p_lr = wpint.dimage + 3 * (preview_width * preview_height - 1);
  944.  
  945.   for (y = 0; y <= (preview_height / 2); y++)
  946.     {
  947.       px = left;
  948.  
  949.       if ((y / GIMP_CHECK_SIZE) & 1)
  950.     check_ul = wpint.check_row_0;
  951.       else
  952.     check_ul = wpint.check_row_1;
  953.  
  954.       if (((preview_height - y - 1) / GIMP_CHECK_SIZE) & 1)
  955.     check_lr = wpint.check_row_0;
  956.       else
  957.     check_lr = wpint.check_row_1;
  958.  
  959.       for (x = 0; x < preview_width; x++)
  960.     {
  961.       calc_undistorted_coords (px, py, whirl, wpvals.pinch, &cx, &cy);
  962.  
  963.       cx = (cx - left) * scale_x;
  964.       cy = (cy - top) * scale_y;
  965.  
  966.       /* Upper left mirror */
  967.  
  968.       ix = (int) (cx + 0.5);
  969.       iy = (int) (cy + 0.5);
  970.  
  971.       check = check_ul[x];
  972.  
  973.       if ((ix >= 0) && (ix < preview_width) &&
  974.           (iy >= 0) && (iy < preview_height))
  975.         i = wpint.image + 4 * (preview_width * iy + ix);
  976.       else
  977.         i = outside;
  978.  
  979.       p_ul[0] = check + ((i[0] - check) * i[3]) / 255;
  980.       p_ul[1] = check + ((i[1] - check) * i[3]) / 255;
  981.       p_ul[2] = check + ((i[2] - check) * i[3]) / 255;
  982.  
  983.       p_ul += 3;
  984.  
  985.       /* Lower right mirror */
  986.  
  987.       ix = preview_width - ix - 1;
  988.       iy = preview_height - iy - 1;
  989.  
  990.       check = check_lr[preview_width - x - 1];
  991.  
  992.       if ((ix >= 0) && (ix < preview_width) &&
  993.           (iy >= 0) && (iy < preview_height))
  994.         i = wpint.image + 4 * (preview_width * iy + ix);
  995.       else
  996.         i = outside;
  997.  
  998.       p_lr[0] = check + ((i[0] - check) * i[3]) / 255;
  999.       p_lr[1] = check + ((i[1] - check) * i[3]) / 255;
  1000.       p_lr[2] = check + ((i[2] - check) * i[3]) / 255;
  1001.  
  1002.       p_lr -= 3;
  1003.  
  1004.       px += dx;
  1005.     }
  1006.  
  1007.       py += dy;
  1008.     }
  1009.  
  1010.   p = wpint.dimage;
  1011.  
  1012.   for (y = 0; y < preview_height; y++)
  1013.     {
  1014.       gtk_preview_draw_row (GTK_PREVIEW (wpint.preview), p, 0, y, preview_width);
  1015.  
  1016.       p += preview_width * 3;
  1017.     }
  1018.  
  1019.   gtk_widget_draw (wpint.preview, NULL);
  1020.   gdk_flush ();
  1021. }
  1022.  
  1023. static void
  1024. dialog_scale_update (GtkAdjustment *adjustment,
  1025.              gdouble       *value)
  1026. {
  1027.   gimp_double_adjustment_update (adjustment, value);
  1028.  
  1029.   dialog_update_preview ();
  1030. }
  1031.  
  1032. static void
  1033. dialog_ok_callback (GtkWidget *widget,
  1034.             gpointer   data)
  1035. {
  1036.   wpint.run = TRUE;
  1037.  
  1038.   gtk_widget_destroy (GTK_WIDGET (data));
  1039. }
  1040.