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 / scatter_hsv.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  17.9 KB  |  655 lines

  1. /* scatter_hsv.c -- This is a plug-in for the GIMP (1.0's API)
  2.  * Author: Shuji Narazaki <narazaki@InetQ.or.jp>
  3.  * Time-stamp: <2000-01-08 02:49:39 yasuhiro>
  4.  * Version: 0.42
  5.  *
  6.  * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. #include "config.h"
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <time.h>
  28.  
  29. #include <gtk/gtk.h>
  30.  
  31. #include <libgimp/gimp.h>
  32. #include <libgimp/gimpui.h>
  33.  
  34. #include "libgimp/stdplugins-intl.h"
  35.  
  36.  
  37. #define    PLUG_IN_NAME    "plug_in_scatter_hsv"
  38. #define SHORT_NAME    "scatter_hsv"
  39.  
  40. static void   query (void);
  41. static void   run   (gchar      *name,
  42.              gint        nparams,
  43.              GimpParam  *param,
  44.              gint       *nreturn_vals,
  45.              GimpParam **return_vals);
  46.  
  47. static GimpPDBStatusType scatter_hsv   (gint32  drawable_id);
  48. static void        scatter_hsv_scatter (guchar *r,
  49.                     guchar *g,
  50.                     guchar *b);
  51. static gint        randomize_value     (gint    now,
  52.                     gint    min,
  53.                     gint    max,
  54.                     gint    mod_p,
  55.                     gint    rand_max);
  56.  
  57. static gint    scatter_hsv_dialog         (void);
  58. static void    scatter_hsv_ok_callback    (GtkWidget     *widget,
  59.                         gpointer       data);
  60. static gint    preview_event_handler      (GtkWidget     *widget,
  61.                         GdkEvent      *event);
  62. static void    scatter_hsv_preview_update (void);
  63. static void     scatter_hsv_iscale_update  (GtkAdjustment *adjustment,
  64.                         gpointer       data);
  65.  
  66. #define PROGRESS_UPDATE_NUM 100
  67. #define PREVIEW_WIDTH       128
  68. #define PREVIEW_HEIGHT      128
  69. #define SCALE_WIDTH         100
  70.  
  71. static gint preview_width  = PREVIEW_WIDTH;
  72. static gint preview_height = PREVIEW_HEIGHT;
  73.  
  74. GimpPlugInInfo PLUG_IN_INFO =
  75. {
  76.   NULL,  /* init_proc  */
  77.   NULL,  /* quit_proc  */
  78.   query, /* query_proc */
  79.   run,   /* run_proc   */
  80. };
  81.  
  82. typedef struct
  83. {                /* gint, gdouble, and so on */
  84.   gint    holdness;
  85.   gint    hue_distance;
  86.   gint    saturation_distance;
  87.   gint    value_distance;
  88. } ValueType;
  89.  
  90. static ValueType VALS = 
  91. {
  92.   2,
  93.   3,
  94.   10,
  95.   10
  96. };
  97.  
  98. typedef struct 
  99. {
  100.   gint run;
  101. } Interface;
  102.  
  103. static Interface INTERFACE =
  104. {
  105.   FALSE
  106. };
  107.  
  108. static gint      drawable_id;
  109.  
  110. static GtkWidget *preview;
  111. static gint      preview_start_x = 0;
  112. static gint      preview_start_y = 0;
  113. static guchar     *preview_buffer = NULL;
  114. static gint      preview_offset_x = 0;
  115. static gint      preview_offset_y = 0;
  116. static gint      preview_dragging = FALSE;
  117. static gint      preview_drag_start_x = 0;
  118. static gint      preview_drag_start_y = 0;
  119.  
  120. MAIN ()
  121.  
  122. static void
  123. query (void)
  124. {
  125.   static GimpParamDef args [] =
  126.   {
  127.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
  128.     { GIMP_PDB_IMAGE, "image", "Input image (not used)"},
  129.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"},
  130.     { GIMP_PDB_INT32, "holdness", "convolution strength"},
  131.     { GIMP_PDB_INT32, "hue_distance", "distribution distance on hue axis [0,255]"},
  132.     { GIMP_PDB_INT32, "saturation_distance", "distribution distance on saturation axis [0,255]"},
  133.     { GIMP_PDB_INT32, "value_distance", "distribution distance on value axis [0,255]"}
  134.   };
  135.   static gint nargs = sizeof (args) / sizeof (args[0]);
  136.  
  137.   gimp_install_procedure (PLUG_IN_NAME,
  138.               "Scattering pixel values in HSV space",
  139.               "Scattering pixel values in HSV space",
  140.               "Shuji Narazaki (narazaki@InetQ.or.jp)",
  141.               "Shuji Narazaki",
  142.               "1997",
  143.               N_("<Image>/Filters/Noise/Scatter HSV..."),
  144.               "RGB*",
  145.               GIMP_PLUGIN,
  146.               nargs, 0,
  147.               args, NULL);
  148. }
  149.  
  150. static void
  151. run (gchar   *name,
  152.      gint     nparams,
  153.      GimpParam  *param,
  154.      gint    *nreturn_vals,
  155.      GimpParam **return_vals)
  156. {
  157.   static GimpParam    values[1];
  158.   GimpPDBStatusType   status = GIMP_PDB_EXECUTION_ERROR;
  159.   GimpRunModeType  run_mode;
  160.   
  161.   run_mode = param[0].data.d_int32;
  162.   drawable_id = param[2].data.d_int32;
  163.  
  164.   *nreturn_vals = 1;
  165.   *return_vals = values;
  166.   
  167.   values[0].type = GIMP_PDB_STATUS;
  168.   values[0].data.d_status = status;
  169.  
  170.   switch (run_mode)
  171.     {
  172.     case GIMP_RUN_INTERACTIVE:
  173.       INIT_I18N_UI();
  174.       gimp_get_data (PLUG_IN_NAME, &VALS);
  175.       if (!gimp_drawable_is_rgb (drawable_id))
  176.     {
  177.       g_message ("Scatter HSV: RGB drawable is not selected.");
  178.       return;
  179.     }
  180.       if (! scatter_hsv_dialog ())
  181.     return;
  182.       break;
  183.     case GIMP_RUN_NONINTERACTIVE:
  184.       INIT_I18N();
  185.       VALS.holdness = param[3].data.d_int32;
  186.       VALS.hue_distance = param[4].data.d_int32;
  187.       VALS.saturation_distance = param[5].data.d_int32;
  188.       VALS.value_distance = param[6].data.d_int32;
  189.       break;
  190.     case GIMP_RUN_WITH_LAST_VALS:
  191.       INIT_I18N();
  192.       gimp_get_data (PLUG_IN_NAME, &VALS);
  193.       break;
  194.     }
  195.   
  196.   status = scatter_hsv (drawable_id);
  197.  
  198.   if (run_mode != GIMP_RUN_NONINTERACTIVE)
  199.     gimp_displays_flush();
  200.   if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS )
  201.     gimp_set_data (PLUG_IN_NAME, &VALS, sizeof (ValueType));
  202.  
  203.   values[0].type = GIMP_PDB_STATUS;
  204.   values[0].data.d_status = status;
  205. }
  206.  
  207. static GimpPDBStatusType
  208. scatter_hsv (gint32 drawable_id)
  209. {
  210.   GimpDrawable *drawable;
  211.   GimpPixelRgn  src_rgn, dest_rgn;
  212.   guchar    *src, *dest;
  213.   gpointer   pr;
  214.   gint       x, y, x1, x2, y1, y2;
  215.   gint       gap, total, processed = 0;
  216.   
  217.   drawable = gimp_drawable_get (drawable_id);
  218.   gap = (gimp_drawable_has_alpha (drawable_id)) ? 1 : 0;
  219.   gimp_drawable_mask_bounds (drawable_id, &x1, &y1, &x2, &y2);
  220.   total = (x2 - x1) * (y2 - y1);
  221.   if (total < 1)
  222.     return GIMP_PDB_EXECUTION_ERROR;
  223.  
  224.   gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  225.   gimp_pixel_rgn_init (&src_rgn, drawable,
  226.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  227.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  228.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  229.  
  230.   gimp_progress_init (_("Scatter HSV: Scattering..."));
  231.   srand (time (NULL));
  232.   pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  233.   
  234.   for (; pr != NULL; pr = gimp_pixel_rgns_process (pr))
  235.     {
  236.       int offset;
  237.  
  238.       for (y = 0; y < src_rgn.h; y++)
  239.     {
  240.       src = src_rgn.data + y * src_rgn.rowstride;
  241.       dest = dest_rgn.data + y * dest_rgn.rowstride;
  242.       offset = 0;
  243.  
  244.       for (x = 0; x < src_rgn.w; x++)
  245.         {
  246.           guchar    h, s, v;
  247.  
  248.           h = *(src + offset);
  249.           s = *(src + offset + 1);
  250.           v = *(src + offset + 2);
  251.           
  252.           scatter_hsv_scatter (&h, &s, &v);
  253.  
  254.           *(dest + offset    ) = (guchar) h;
  255.           *(dest + offset + 1) = (guchar) s;
  256.           *(dest + offset + 2) = (guchar) v;
  257.  
  258.           offset += 3;
  259.           if (gap)
  260.         {
  261.           *(dest + offset) = *(src + offset);
  262.           offset++;
  263.         }
  264.           /* the function */
  265.           if ((++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0)
  266.         gimp_progress_update ((double) processed /(double) total); 
  267.         }
  268.     }
  269.   }
  270.  
  271.   gimp_progress_update (1.0);
  272.   gimp_drawable_flush (drawable);
  273.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  274.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  275.   gimp_drawable_detach (drawable);
  276.  
  277.   return GIMP_PDB_SUCCESS;
  278. }
  279.  
  280. static gint
  281. randomize_value (gint now,
  282.          gint min,
  283.          gint max,
  284.          gint mod_p,
  285.          gint rand_max)
  286. {
  287.   gint    flag, new, steps, index;
  288.   gdouble rand_val;
  289.   
  290.   steps = max - min + 1;
  291.   rand_val = ((double) rand () / (double) G_MAXRAND);
  292.   for (index = 1; index < VALS.holdness; index++)
  293.     {
  294.       double tmp = ((double) rand () / (double) G_MAXRAND);
  295.       if (tmp < rand_val)
  296.     rand_val = tmp;
  297.     }
  298.   
  299.   flag = ((G_MAXRAND / 2) < rand()) ? 1 : -1;
  300.   new = now + flag * ((int) (rand_max * rand_val) % steps);
  301.   
  302.   if (new < min)
  303.     {
  304.       if (mod_p == 1)
  305.     new += steps;
  306.       else
  307.     new = min;
  308.     }
  309.   if (max < new)
  310.     {
  311.       if (mod_p == 1)
  312.     new -= steps;
  313.       else
  314.     new = max;
  315.     }
  316.   return new;
  317. }
  318.  
  319. void scatter_hsv_scatter (guchar *r,
  320.               guchar *g,
  321.               guchar *b)
  322. {
  323.   gint h, s, v;
  324.   gint h1, s1, v1;
  325.   gint h2, s2, v2;
  326.   
  327.   h = *r; s = *g; v = *b;
  328.   
  329.   gimp_rgb_to_hsv (&h, &s, &v);
  330.  
  331.   if (0 < VALS.hue_distance)
  332.     h = randomize_value (h, 0, 255, 1, VALS.hue_distance);
  333.   if ((0 < VALS.saturation_distance))
  334.     s = randomize_value (s, 0, 255, 0, VALS.saturation_distance);
  335.   if ((0 < VALS.value_distance))
  336.     v = randomize_value (v, 0, 255, 0, VALS.value_distance);
  337.  
  338.   h1 = h; s1 = s; v1 = v;
  339.           
  340.   gimp_hsv_to_rgb (&h, &s, &v); /* don't believe ! */
  341.  
  342.   h2 = h; s2 = s; v2 = v;
  343.  
  344.   gimp_rgb_to_hsv (&h2, &s2, &v2); /* h2 should be h1. But... */
  345.   
  346.   if ((abs (h1 - h2) <= VALS.hue_distance)
  347.       && (abs (s1 - s2) <= VALS.saturation_distance)
  348.       && (abs (v1 - v2) <= VALS.value_distance))
  349.     {
  350.       *r = h;
  351.       *g = s;
  352.       *b = v;
  353.     }
  354. }
  355.  
  356. /* dialog stuff */
  357. static gint
  358. scatter_hsv_dialog (void)
  359. {
  360.   GtkWidget *dlg;
  361.   GtkWidget *vbox;
  362.   GtkWidget *frame;
  363.   GtkWidget *pframe;
  364.   GtkWidget *abox;
  365.   GtkWidget *table;
  366.   GtkObject *adj;
  367.  
  368.   gimp_ui_init (SHORT_NAME, TRUE);
  369.  
  370.   dlg = gimp_dialog_new (_("Scatter HSV"), SHORT_NAME,
  371.              gimp_standard_help_func, "filters/scatter_hsv.html",
  372.              GTK_WIN_POS_MOUSE,
  373.              FALSE, TRUE, FALSE,
  374.  
  375.              _("OK"), scatter_hsv_ok_callback,
  376.              NULL, NULL, NULL, TRUE, FALSE,
  377.              _("Cancel"), gtk_widget_destroy,
  378.              NULL, 1, NULL, FALSE, TRUE,
  379.  
  380.              NULL);
  381.  
  382.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  383.               GTK_SIGNAL_FUNC (gtk_main_quit),
  384.               NULL);
  385.  
  386.   vbox = gtk_vbox_new (FALSE, 6);
  387.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  388.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), vbox);
  389.  
  390.   frame = gtk_frame_new (_("Preview (1:4) - Right Click to Jump"));
  391.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  392.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  393.   gtk_widget_show (frame);
  394.  
  395.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  396.   gtk_container_add (GTK_CONTAINER (frame), abox);
  397.   gtk_widget_show (abox);
  398.  
  399.   pframe = gtk_frame_new (NULL);
  400.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  401.   gtk_container_set_border_width (GTK_CONTAINER (pframe), 4);
  402.   gtk_container_add (GTK_CONTAINER (abox), pframe);
  403.   gtk_widget_show (pframe);
  404.  
  405.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  406.   {
  407.     gint width  = gimp_drawable_width (drawable_id);
  408.     gint height = gimp_drawable_height (drawable_id);
  409.  
  410.     preview_width  = (PREVIEW_WIDTH  < width)  ? PREVIEW_WIDTH  : width;
  411.     preview_height = (PREVIEW_HEIGHT < height) ? PREVIEW_HEIGHT : height;
  412.   }
  413.   gtk_preview_size (GTK_PREVIEW (preview), preview_width * 2, preview_height);
  414.   scatter_hsv_preview_update ();
  415.   gtk_container_add (GTK_CONTAINER (pframe), preview);
  416.   gtk_widget_set_events (preview, 
  417.              GDK_BUTTON_PRESS_MASK |
  418.              GDK_BUTTON_RELEASE_MASK | 
  419.              GDK_BUTTON_MOTION_MASK |
  420.              GDK_POINTER_MOTION_HINT_MASK);
  421.   gtk_signal_connect (GTK_OBJECT (preview), "event",
  422.               (GtkSignalFunc) preview_event_handler,
  423.               NULL);
  424.   gtk_widget_show (preview);
  425.  
  426.   gtk_widget_show (frame);
  427.  
  428.   frame = gtk_frame_new (_("Parameter Settings"));
  429.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  430.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  431.   gtk_widget_show (frame);
  432.  
  433.   table = gtk_table_new (4, 3, FALSE);
  434.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  435.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  436.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  437.   gtk_container_add (GTK_CONTAINER (frame), table);
  438.   gtk_widget_show (table);
  439.  
  440.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  441.                   _("Holdness:"), SCALE_WIDTH, 0,
  442.                   VALS.holdness, 1, 8, 1, 2, 0,
  443.                   TRUE, 0, 0,
  444.                   NULL, NULL);
  445.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  446.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  447.               &VALS.holdness);
  448.  
  449.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  450.                   _("Hue:"), SCALE_WIDTH, 0,
  451.                   VALS.hue_distance, 0, 255, 1, 8, 0,
  452.                   TRUE, 0, 0,
  453.                   NULL, NULL);
  454.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  455.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  456.               &VALS.hue_distance);
  457.  
  458.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  459.                   _("Saturation:"), SCALE_WIDTH, 0,
  460.                   VALS.saturation_distance, 0, 255, 1, 8, 0,
  461.                   TRUE, 0, 0,
  462.                   NULL, NULL);
  463.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  464.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  465.               &VALS.saturation_distance);
  466.  
  467.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  468.                   _("Value:"), SCALE_WIDTH, 0,
  469.                   VALS.value_distance, 0, 255, 1, 8, 0,
  470.                   TRUE, 0, 0,
  471.                   NULL, NULL);
  472.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  473.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  474.               &VALS.value_distance);
  475.  
  476.   gtk_widget_show (table);
  477.   gtk_widget_show (frame);
  478.  
  479.   gtk_widget_show (vbox);
  480.   gtk_widget_show (dlg);
  481.   
  482.   gtk_main ();
  483.   gdk_flush ();
  484.  
  485.   return INTERFACE.run;
  486. }
  487.  
  488. static gint
  489. preview_event_handler (GtkWidget *widget,
  490.                GdkEvent  *event)
  491. {
  492.   gint            x, y;
  493.   gint            dx, dy;
  494.   GdkEventButton *bevent;
  495.     
  496.   gtk_widget_get_pointer (widget, &x, &y);
  497.  
  498.   bevent = (GdkEventButton *) event;
  499.  
  500.   switch (event->type) 
  501.     {
  502.     case GDK_BUTTON_PRESS:
  503.       if (x < preview_width)
  504.     {
  505.       if (bevent->button == 3)
  506.         {
  507.           preview_offset_x = - x;
  508.           preview_offset_y = - y;
  509.           scatter_hsv_preview_update ();
  510.         }
  511.       else
  512.         {
  513.           preview_dragging = TRUE;
  514.           preview_drag_start_x = x;
  515.           preview_drag_start_y = y;
  516.           gtk_grab_add (widget);
  517.         }
  518.     }
  519.       break;
  520.     case GDK_BUTTON_RELEASE:
  521.       if (preview_dragging)
  522.     {
  523.       gtk_grab_remove (widget);
  524.       preview_dragging = FALSE;
  525.       scatter_hsv_preview_update ();
  526.     }
  527.       break;
  528.     case GDK_MOTION_NOTIFY:
  529.       if (preview_dragging)
  530.     {
  531.       dx = x - preview_drag_start_x;
  532.       dy = y - preview_drag_start_y;
  533.  
  534.       preview_drag_start_x = x;
  535.       preview_drag_start_y = y;
  536.  
  537.       if ((dx == 0) && (dy == 0))
  538.         break;
  539.  
  540.       preview_offset_x = MAX (preview_offset_x - dx, 0);
  541.       preview_offset_y = MAX (preview_offset_y - dy, 0);
  542.       scatter_hsv_preview_update ();
  543.     }
  544.       break; 
  545.     default:
  546.       break;
  547.     }
  548.   return FALSE;
  549. }
  550.  
  551. static void
  552. scatter_hsv_preview_update (void)
  553. {
  554.   GimpDrawable    *drawable;
  555.   GimpPixelRgn    src_rgn;
  556.   gint    scale;
  557.   gint    x, y, dx, dy;
  558.   gint    bound_start_x, bound_start_y, bound_end_x, bound_end_y;
  559.   gint    src_has_alpha = FALSE;
  560.   gint    src_is_gray = FALSE;
  561.   gint    src_bpp, src_bpl;
  562.   guchar    data[3];
  563.   gdouble    shift_rate;
  564.     
  565.   drawable = gimp_drawable_get (drawable_id);
  566.   gimp_drawable_mask_bounds (drawable_id,
  567.                  &bound_start_x, &bound_start_y,
  568.                  &bound_end_x, &bound_end_y);
  569.   src_has_alpha  = gimp_drawable_has_alpha (drawable_id);
  570.   src_is_gray =  gimp_drawable_is_gray (drawable_id);
  571.   src_bpp = (src_is_gray ? 1 : 3) + (src_has_alpha ? 1 : 0);
  572.   src_bpl = preview_width * src_bpp;
  573.  
  574.   if (! preview_buffer)
  575.     preview_buffer
  576.       = (guchar *) g_malloc (src_bpl * preview_height * sizeof (guchar));
  577.  
  578.   if (preview_offset_x < 0)
  579.     preview_offset_x = (bound_end_x - bound_start_x) * (- preview_offset_x) /  preview_width;
  580.   if (preview_offset_y < 0)
  581.     preview_offset_y = (bound_end_y - bound_start_y) * (- preview_offset_y) /  preview_height;
  582.   preview_start_x = CLAMP (bound_start_x + preview_offset_x,
  583.                bound_start_x, MAX (bound_end_x - preview_width, 0));
  584.   preview_start_y = CLAMP (bound_start_y + preview_offset_y,
  585.                bound_start_y, MAX (bound_end_y - preview_height, 0));
  586.   if (preview_start_x == bound_start_x)
  587.     preview_offset_x = 0;
  588.   if (preview_start_y == bound_start_y)
  589.     preview_offset_y =0;
  590.  
  591.   gimp_pixel_rgn_init (&src_rgn, drawable, preview_start_x, preview_start_y,
  592.                preview_width, preview_height,
  593.                FALSE, FALSE);
  594.  
  595.   /* Since it's small, get whole data before processing. */
  596.   gimp_pixel_rgn_get_rect (&src_rgn, preview_buffer,
  597.                preview_start_x, preview_start_y,
  598.                preview_width, preview_height);
  599.  
  600.   scale = 4;
  601.   shift_rate = (gdouble) (scale - 1) / ( 2 * scale);
  602.   for (y = 0; y < preview_height/4; y++)
  603.     {
  604.       for (x = 0; x < preview_width/4; x++)
  605.     {
  606.       gint pos;
  607.       gint    i;
  608.  
  609.       pos = (gint)(y + preview_height * shift_rate) * src_bpl
  610.             + (gint)(x + preview_width * shift_rate) * src_bpp;
  611.  
  612.       for (i = 0; i < src_bpp; i++)
  613.         data[i] = preview_buffer[pos + i];
  614.  
  615.       scatter_hsv_scatter (data+0, data+1, data+2);
  616.       for (dy = 0; dy < scale; dy++)
  617.         for (dx = 0; dx < scale; dx++)
  618.           gtk_preview_draw_row (GTK_PREVIEW (preview), data,
  619.                     preview_width + x * scale + dx,
  620.                     y * scale + dy, 1);
  621.     }
  622.     }
  623.   for (y = 0; y < preview_height; y ++)
  624.     for (x = 0; x < preview_width; x++)
  625.       {
  626.     gint    i;
  627.  
  628.     for (i = 0; i < src_bpp; i++)
  629.       data[i] = preview_buffer[y * src_bpl + x * src_bpp + i];
  630.  
  631.     scatter_hsv_scatter (data+0, data+1, data+2);
  632.     gtk_preview_draw_row (GTK_PREVIEW (preview), data, x, y, 1);
  633.       }
  634.   gtk_widget_draw (preview, NULL);
  635.   gdk_flush ();
  636. }
  637.  
  638. static void
  639. scatter_hsv_ok_callback (GtkWidget *widget,
  640.              gpointer   data)
  641. {
  642.   INTERFACE.run = TRUE;
  643.  
  644.   gtk_widget_destroy (GTK_WIDGET (data));
  645. }
  646.  
  647. static void
  648. scatter_hsv_iscale_update (GtkAdjustment *adjustment,
  649.                gpointer       data)
  650. {
  651.   gimp_int_adjustment_update (adjustment, data);
  652.  
  653.   scatter_hsv_preview_update ();
  654. }
  655.