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 / exchange.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-20  |  24.8 KB  |  814 lines

  1. /*
  2.  * This is a plug-in for the GIMP.
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  *
  18.  *
  19.  */
  20.  
  21. /*
  22.  * Exchange one color with the other (settable threshold to convert from
  23.  * one color-shade to another...might do wonders on certain images, or be
  24.  * totally useless on others).
  25.  * 
  26.  * Author: robert@experimental.net
  27.  *
  28.  * Added ability to select "from" color by clicking on the preview image.
  29.  * As a side effect, clicking twice on the same spot reverses the action,
  30.  * i.e. you can click once, see the result, and click again to revert.
  31.  *
  32.  * Also changed update policies for all sliders to delayed.  On a slow machine
  33.  * the algorithm really chewes up CPU time.
  34.  *
  35.  * - timecop@japan.co.jp
  36.  */
  37.  
  38. #include "config.h"
  39.  
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42.  
  43. #include <gtk/gtk.h>
  44.  
  45. #include <libgimp/gimp.h>
  46. #include <libgimp/gimpui.h>
  47.  
  48. #include "libgimp/stdplugins-intl.h"
  49.  
  50.  
  51. #define    SCALE_WIDTH  128
  52. #define PREVIEW_SIZE 128
  53.  
  54. /* datastructure to store parameters in */
  55. typedef struct
  56. {
  57.   guchar  fromred, fromgreen, fromblue;
  58.   guchar  tored, togreen, toblue;
  59.   guchar  red_threshold, green_threshold, blue_threshold;
  60.   gint32  image;
  61.   gint32  drawable;
  62. } myParams;
  63.  
  64. /* lets prototype */
  65. static void    query (void);
  66. static void    run   (gchar   *name,
  67.                gint     nparams,
  68.                GimpParam  *param,
  69.                gint    *nreturn_vals,
  70.                GimpParam **return_vals);
  71.  
  72. static void    exchange              (void);
  73. static void    real_exchange         (gint, gint, gint, gint, gboolean);
  74.  
  75. static int    exchange_dialog       (void);
  76. static void    update_preview        (void);
  77. static void    ok_callback           (GtkWidget *, gpointer);
  78. static void    color_button_callback (GtkWidget *, gpointer);
  79. static void    scale_callback        (GtkAdjustment *, gpointer);
  80.  
  81. /* some global variables */
  82. static GimpDrawable *drw;
  83. static gboolean   has_alpha;
  84. static myParams   xargs = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  85. static gint       running = 0;
  86. static GimpPixelRgn  origregion;
  87. static GtkWidget *preview;
  88. static GtkWidget *from_colorbutton;
  89. static GtkWidget *to_colorbutton;
  90. static gint       sel_x1, sel_y1, sel_x2, sel_y2;
  91. static gint       prev_width, prev_height, sel_width, sel_height;
  92. static gboolean   lock_threshold = FALSE;
  93.  
  94. /* lets declare what we want to do */
  95. GimpPlugInInfo PLUG_IN_INFO =
  96. {
  97.   NULL,  /* init_proc  */
  98.   NULL,  /* quit_proc  */
  99.   query, /* query_proc */
  100.   run,   /* run_proc   */
  101. };
  102.  
  103. /* run program */
  104. MAIN ()
  105.  
  106. /* tell GIMP who we are */
  107. static void
  108. query (void)
  109. {
  110.   static GimpParamDef args[] =
  111.   {
  112.     { GIMP_PDB_INT32, "run_mode", "Interactive" },
  113.     { GIMP_PDB_IMAGE, "image", "Input image" },
  114.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  115.     { GIMP_PDB_INT8, "fromred", "Red value (from)" },
  116.     { GIMP_PDB_INT8, "fromgreen", "Green value (from)" },
  117.     { GIMP_PDB_INT8, "fromblue", "Blue value (from)" },
  118.     { GIMP_PDB_INT8, "tored", "Red value (to)" },
  119.     { GIMP_PDB_INT8, "togreen", "Green value (to)" },
  120.     { GIMP_PDB_INT8, "toblue", "Blue value (to)" },
  121.     { GIMP_PDB_INT8, "red_threshold", "Red threshold" },
  122.     { GIMP_PDB_INT8, "green_threshold", "Green threshold" },
  123.     { GIMP_PDB_INT8, "blue_threshold", "Blue threshold" }
  124.   };
  125.   static gint nargs = sizeof (args) / sizeof (args[0]);
  126.  
  127.   gimp_install_procedure ("plug_in_exchange",
  128.               "Color Exchange",
  129.               "Exchange one color with another, optionally setting a threshold "
  130.               "to convert from one shade to another",
  131.               "robert@experimental.net",
  132.               "robert@experimental.net",
  133.               "June 17th, 1997",
  134.               N_("<Image>/Filters/Colors/Map/Color Exchange..."),
  135.               "RGB*",
  136.               GIMP_PLUGIN,
  137.               nargs, 0,
  138.               args, NULL);
  139. }
  140.  
  141. /* main function */
  142. static void
  143. run (gchar   *name,
  144.      gint     nparams,
  145.      GimpParam  *param,
  146.      gint    *nreturn_vals,
  147.      GimpParam **return_vals)
  148. {
  149.   static GimpParam    values[1];
  150.   GimpRunModeType    runmode;
  151.   GimpPDBStatusType     status = GIMP_PDB_SUCCESS;
  152.  
  153.   *nreturn_vals = 1;
  154.   *return_vals = values;
  155.  
  156.   values[0].type = GIMP_PDB_STATUS;
  157.   values[0].data.d_status = status;
  158.  
  159.   runmode        = param[0].data.d_int32;
  160.   xargs.image    = param[1].data.d_image;
  161.   xargs.drawable = param[2].data.d_drawable;
  162.   drw = gimp_drawable_get (xargs.drawable);
  163.  
  164.   /* initialize misc. things */
  165.   gimp_drawable_mask_bounds (drw->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  166.   sel_width = sel_x2 - sel_x1;
  167.   sel_height = sel_y2 - sel_y1;
  168.  
  169.   if (sel_width > PREVIEW_SIZE)
  170.     prev_width = PREVIEW_SIZE;
  171.   else
  172.     prev_width = sel_width;
  173.  
  174.   if (sel_height > PREVIEW_SIZE)
  175.     prev_height = PREVIEW_SIZE;
  176.   else
  177.     prev_height = sel_height;
  178.  
  179.   has_alpha = gimp_drawable_has_alpha (drw->id);
  180.  
  181.   switch (runmode)
  182.     {
  183.     case GIMP_RUN_INTERACTIVE:
  184.       INIT_I18N_UI();
  185.       /* retrieve stored arguments (if any) */
  186.       gimp_get_data ("plug_in_exchange", &xargs);
  187.       /* initialize using foreground color */
  188.       gimp_palette_get_foreground (&xargs.fromred,
  189.                    &xargs.fromgreen,
  190.                    &xargs.fromblue);
  191.       if (! exchange_dialog ())
  192.     return;
  193.       break;
  194.  
  195.     case GIMP_RUN_WITH_LAST_VALS:
  196.       INIT_I18N();
  197.       gimp_get_data ("plug_in_exchange", &xargs);
  198.       /* 
  199.        * instead of recalling the last-set values,
  200.        * run with the current foreground as 'from'
  201.        * color, making ALT-F somewhat more useful.
  202.        */
  203.       gimp_palette_get_foreground (&xargs.fromred,
  204.                    &xargs.fromgreen,
  205.                    &xargs.fromblue);
  206.       break;
  207.  
  208.     case GIMP_RUN_NONINTERACTIVE:
  209.       INIT_I18N();
  210.       if (nparams != 12)
  211.     {
  212.       status = GIMP_PDB_EXECUTION_ERROR;
  213.     }
  214.       else
  215.     {
  216.       xargs.fromred         = param[3].data.d_int8;
  217.       xargs.fromgreen       = param[4].data.d_int8;
  218.       xargs.fromblue        = param[5].data.d_int8;
  219.       xargs.tored           = param[6].data.d_int8;
  220.       xargs.togreen         = param[7].data.d_int8;
  221.       xargs.toblue          = param[8].data.d_int8;
  222.       xargs.red_threshold   = param[9].data.d_int32;
  223.       xargs.green_threshold = param[10].data.d_int32;
  224.       xargs.blue_threshold  = param[11].data.d_int32;
  225.     }
  226.       break;
  227.  
  228.     default:    
  229.       break;
  230.     }
  231.  
  232.   if (status == GIMP_PDB_SUCCESS)
  233.     {
  234.       if (gimp_drawable_is_rgb (drw->id))
  235.     {
  236.       gimp_progress_init (_("Color Exchange..."));
  237.       gimp_tile_cache_ntiles (2 * (drw->width / gimp_tile_width () + 1));
  238.       exchange ();    
  239.       gimp_drawable_detach( drw);
  240.  
  241.       /* store our settings */
  242.       if (runmode == GIMP_RUN_INTERACTIVE)
  243.         gimp_set_data ("plug_in_exchange", &xargs, sizeof (myParams));
  244.  
  245.       /* and flush */
  246.       if (runmode != GIMP_RUN_NONINTERACTIVE)
  247.         gimp_displays_flush ();
  248.     }
  249.       else
  250.     status = GIMP_PDB_EXECUTION_ERROR;
  251.     }
  252.   values[0].data.d_status = status;
  253. }
  254.  
  255. /* do the exchanging */
  256. static void
  257. exchange (void)
  258. {
  259.   /* do the real exchange */
  260.   real_exchange (-1, -1, -1, -1, FALSE);
  261. }
  262.  
  263. static gboolean
  264. preview_event_handler (GtkWidget *widget, 
  265.                GdkEvent  *event)
  266. {
  267.   gint    x;
  268.   gint    y;
  269.   gint    pos;
  270.   guchar *buf;
  271.   guint   red_handler   = 0;
  272.   guint   green_handler = 0;
  273.   guint   blue_handler  = 0;
  274.   GtkAdjustment *r, *g, *b;
  275.  
  276.   buf = GTK_PREVIEW (widget)->buffer;
  277.  
  278.   switch(event->type) 
  279.     {
  280.     case GDK_BUTTON_PRESS:
  281.       x = event->button.x;
  282.       y = event->button.y;
  283.       pos = x * GTK_PREVIEW(widget)->bpp + y * GTK_PREVIEW(widget)->rowstride;
  284.       xargs.fromred   = buf[pos];
  285.       xargs.fromgreen = buf[pos + 1];
  286.       xargs.fromblue  = buf[pos + 2];
  287.       
  288.       r = gtk_object_get_data (GTK_OBJECT (from_colorbutton), "red");
  289.       g = gtk_object_get_data (GTK_OBJECT (from_colorbutton), "green");
  290.       b = gtk_object_get_data (GTK_OBJECT (from_colorbutton), "blue");
  291.    
  292.       red_handler   = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (r), 
  293.                                  "handler"));
  294.       green_handler = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (g), 
  295.                                  "handler"));
  296.       blue_handler  = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (b), 
  297.                                  "handler"));
  298.  
  299.       gtk_signal_handler_block (GTK_OBJECT (r), red_handler);
  300.       gtk_signal_handler_block (GTK_OBJECT (g), green_handler);
  301.       gtk_signal_handler_block (GTK_OBJECT (b), blue_handler);
  302.       
  303.       gtk_adjustment_set_value (GTK_ADJUSTMENT (r), xargs.fromred);
  304.       gtk_adjustment_set_value (GTK_ADJUSTMENT (g), xargs.fromgreen);
  305.       gtk_adjustment_set_value (GTK_ADJUSTMENT (b), xargs.fromblue);
  306.       
  307.       gtk_signal_handler_unblock (GTK_OBJECT (r), red_handler);
  308.       gtk_signal_handler_unblock (GTK_OBJECT (g), green_handler);
  309.       gtk_signal_handler_unblock (GTK_OBJECT (b), blue_handler);
  310.  
  311.       gimp_color_button_update(GIMP_COLOR_BUTTON(from_colorbutton));
  312.       update_preview();
  313.       break;
  314.  
  315.    default:
  316.       break;
  317.     }
  318.  
  319.   return FALSE;
  320. }
  321.  
  322. /* show our dialog */
  323. static gint
  324. exchange_dialog (void)
  325. {
  326.   GtkWidget *dialog;
  327.   GtkWidget *mainbox;
  328.   GtkWidget *frame;
  329.   GtkWidget *abox;
  330.   GtkWidget *pframe;
  331.   GtkWidget *table;
  332.   GtkWidget *colorbutton;
  333.   GtkObject *adj;
  334.   GtkWidget *scale;
  335.   GtkObject *red_threshold   = NULL;
  336.   GtkObject *green_threshold = NULL;
  337.   GtkObject *blue_threshold  = NULL;
  338.   gint       framenumber;
  339.  
  340.   gimp_ui_init ("exchange", TRUE);
  341.  
  342.   /* load pixelregion */
  343.   gimp_pixel_rgn_init (&origregion, drw,
  344.                0, 0, PREVIEW_SIZE, PREVIEW_SIZE, FALSE, FALSE);
  345.  
  346.   /* set up the dialog */
  347.   dialog = gimp_dialog_new (_("Color Exchange"), "exchange",
  348.                 gimp_standard_help_func, "filters/exchange.html",
  349.                 GTK_WIN_POS_MOUSE,
  350.                 FALSE, TRUE, FALSE,
  351.  
  352.                 _("OK"), ok_callback,
  353.                 NULL, NULL, NULL, TRUE, FALSE,
  354.                 _("Cancel"), gtk_widget_destroy,
  355.                 NULL, 1, NULL, FALSE, TRUE,
  356.  
  357.                 NULL);
  358.  
  359.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  360.               GTK_SIGNAL_FUNC (gtk_main_quit),
  361.               NULL);
  362.  
  363.   /* do some boxes here */
  364.   mainbox = gtk_vbox_new (FALSE, 4);
  365.   gtk_container_set_border_width (GTK_CONTAINER (mainbox), 6);
  366.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), mainbox,
  367.               TRUE, TRUE, 0);
  368.  
  369.   frame = gtk_frame_new (_("Preview: Click Inside to Pick \"From Color\""));
  370.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  371.   gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
  372.   gtk_widget_show (frame);
  373.  
  374.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  375.   gtk_container_add (GTK_CONTAINER (frame), abox);
  376.   gtk_widget_show (abox);
  377.  
  378.   pframe = gtk_frame_new (NULL);
  379.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  380.   gtk_container_set_border_width (GTK_CONTAINER (pframe), 4);
  381.   gtk_container_add (GTK_CONTAINER (abox), pframe);
  382.   gtk_widget_show (pframe);
  383.  
  384.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  385.   gtk_preview_size (GTK_PREVIEW (preview), prev_width, prev_height);
  386.   gtk_container_add (GTK_CONTAINER (pframe), preview);
  387.   gtk_widget_set_events (GTK_WIDGET(preview),
  388.              GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK);
  389.   gtk_signal_connect (GTK_OBJECT (preview), "event", 
  390.               GTK_SIGNAL_FUNC (preview_event_handler), 
  391.               NULL);
  392.   update_preview ();
  393.   gtk_widget_show (preview);
  394.  
  395.   /* and our scales */
  396.   for (framenumber = 0; framenumber < 2; framenumber++)
  397.     {
  398.       guint id;
  399.  
  400.       frame = gtk_frame_new (framenumber ? _("To Color") : _("From Color"));
  401.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  402.       gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
  403.       gtk_widget_show (frame);
  404.  
  405.       table = gtk_table_new (framenumber ? 3 : 9, 4, FALSE);
  406.       gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  407.       gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  408.       gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  409.       gtk_container_add (GTK_CONTAINER (frame), table);
  410.       gtk_widget_show (table);
  411.  
  412.       colorbutton = gimp_color_button_new (framenumber ?
  413.                        _("Color Exchange: To Color") :
  414.                        _("Color Exchange: From Color"),
  415.                        SCALE_WIDTH / 2, 16,
  416.                        framenumber ?
  417.                        &xargs.tored : &xargs.fromred,
  418.                        3);
  419.       gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  420.                  NULL, 0.0, 0.0,
  421.                  colorbutton, 1, TRUE);
  422.       gtk_signal_connect (GTK_OBJECT (colorbutton), "color_changed",
  423.               GTK_SIGNAL_FUNC (color_button_callback),
  424.               NULL);
  425.  
  426.       if (framenumber)
  427.     to_colorbutton = colorbutton;
  428.       else
  429.     from_colorbutton = colorbutton;
  430.  
  431.       adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  432.                   _("Red:"), SCALE_WIDTH, 0,
  433.                   framenumber ? xargs.tored : xargs.fromred,
  434.                   0, 255, 1, 8, 0,
  435.                   TRUE, 0, 0,
  436.                   NULL, NULL);
  437.  
  438.       gtk_object_set_user_data (GTK_OBJECT (adj), colorbutton);
  439.       gtk_object_set_data (GTK_OBJECT (colorbutton), "red", adj);
  440.  
  441.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  442.                    GTK_SIGNAL_FUNC (scale_callback),
  443.                    framenumber ? &xargs.tored : &xargs.fromred);
  444.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  445.                GUINT_TO_POINTER (id));
  446.  
  447.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  448.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  449.  
  450.       if (! framenumber)
  451.     {
  452.       red_threshold = 
  453.         adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  454.                     _("Red Threshold:"), SCALE_WIDTH, 0,
  455.                     xargs.red_threshold,
  456.                     0, 255, 1, 8, 0,
  457.                     TRUE, 0, 0,
  458.                     NULL, NULL);
  459.  
  460.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  461.                    GTK_SIGNAL_FUNC (scale_callback),
  462.                    &xargs.red_threshold);
  463.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  464.                    GUINT_TO_POINTER (id));
  465.  
  466.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  467.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  468.     }
  469.  
  470.       adj = gimp_scale_entry_new (GTK_TABLE (table), 0, framenumber ? 2 : 3,
  471.                   _("Green:"), SCALE_WIDTH, 0,
  472.                   framenumber ? xargs.togreen : xargs.fromgreen,
  473.                   0, 255, 1, 8, 0,
  474.                   TRUE, 0, 0,
  475.                   NULL, NULL);
  476.  
  477.       gtk_object_set_user_data (GTK_OBJECT (adj), colorbutton);
  478.       gtk_object_set_data (GTK_OBJECT (colorbutton), "green", adj);
  479.  
  480.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  481.                    GTK_SIGNAL_FUNC (scale_callback),
  482.                    framenumber ? &xargs.togreen : &xargs.fromgreen);
  483.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  484.                GUINT_TO_POINTER (id));
  485.  
  486.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  487.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  488.  
  489.       if (! framenumber)
  490.     {
  491.       green_threshold =
  492.         adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
  493.                     _("Green Threshold:"), SCALE_WIDTH, 0,
  494.                     xargs.green_threshold,
  495.                     0, 255, 1, 8, 0,
  496.                     TRUE, 0, 0,
  497.                     NULL, NULL);
  498.  
  499.       gtk_object_set_user_data (GTK_OBJECT (adj), red_threshold);
  500.  
  501.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  502.                    GTK_SIGNAL_FUNC (scale_callback),
  503.                    &xargs.green_threshold);
  504.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  505.                    GUINT_TO_POINTER (id));
  506.  
  507.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  508.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  509.     }
  510.  
  511.       adj = gimp_scale_entry_new (GTK_TABLE (table), 0, framenumber ? 3 : 5,
  512.                   _("Blue:"), SCALE_WIDTH, 0,
  513.                   framenumber ? xargs.toblue : xargs.fromblue,
  514.                   0, 255, 1, 8, 0,
  515.                   TRUE, 0, 0,
  516.                   NULL, NULL);
  517.  
  518.       gtk_object_set_user_data (GTK_OBJECT (adj), colorbutton);
  519.       gtk_object_set_data (GTK_OBJECT (colorbutton), "blue", adj);
  520.  
  521.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  522.                    GTK_SIGNAL_FUNC (scale_callback),
  523.                    framenumber ? &xargs.toblue : &xargs.fromblue);
  524.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  525.                GUINT_TO_POINTER (id));
  526.  
  527.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  528.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  529.  
  530.       if (! framenumber)
  531.     {
  532.       blue_threshold = 
  533.         adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
  534.                     _("Blue Threshold:"), SCALE_WIDTH, 0,
  535.                     xargs.blue_threshold,
  536.                     0, 255, 1, 8, 0,
  537.                     TRUE, 0, 0,
  538.                     NULL, NULL);
  539.  
  540.       gtk_object_set_user_data (GTK_OBJECT (adj), green_threshold);
  541.  
  542.       id = gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  543.                    GTK_SIGNAL_FUNC (scale_callback),
  544.                    &xargs.blue_threshold);
  545.       gtk_object_set_data (GTK_OBJECT (adj), "handler", 
  546.                    GUINT_TO_POINTER (id));
  547.  
  548.       scale = GTK_WIDGET (GIMP_SCALE_ENTRY_SCALE (adj));
  549.       gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  550.       gtk_object_set_user_data (GTK_OBJECT (red_threshold), 
  551.                     blue_threshold);
  552.     }
  553.  
  554.       if (! framenumber)
  555.     {
  556.       GtkWidget *button;
  557.  
  558.       button = gtk_check_button_new_with_label (_("Lock Thresholds"));
  559.       gtk_table_attach (GTK_TABLE (table), button, 1, 3, 7, 8,
  560.                 GTK_FILL, 0, 0, 0);
  561.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), 
  562.                     lock_threshold);
  563.       gtk_signal_connect (GTK_OBJECT (button), "clicked",
  564.                   GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  565.                   &lock_threshold);
  566.       gtk_widget_show (button);
  567.     }
  568.     }
  569.  
  570.   /* show everything */
  571.   gtk_widget_show (mainbox);
  572.   gtk_widget_show (dialog);
  573.  
  574.   gtk_main ();
  575.   gdk_flush ();
  576.  
  577.   return running;
  578. }
  579.  
  580. static void
  581. ok_callback (GtkWidget *widget,
  582.          gpointer   data)
  583. {
  584.   running = TRUE;
  585.  
  586.   gtk_widget_destroy (GTK_WIDGET (data));
  587. }
  588.  
  589. static void
  590. color_button_callback (GtkWidget *widget,
  591.                gpointer   data)
  592. {
  593.   GtkObject *red_adj;
  594.   GtkObject *green_adj;
  595.   GtkObject *blue_adj;
  596.  
  597.   guint red_handler;
  598.   guint green_handler;
  599.   guint blue_handler;
  600.  
  601.   red_adj   = (GtkObject *) gtk_object_get_data (GTK_OBJECT (widget), "red");
  602.   green_adj = (GtkObject *) gtk_object_get_data (GTK_OBJECT (widget), "green");
  603.   blue_adj  = (GtkObject *) gtk_object_get_data (GTK_OBJECT (widget), "blue");
  604.  
  605.   red_handler   = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (red_adj),
  606.                              "handler"));
  607.   green_handler = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (green_adj),
  608.                              "handler"));
  609.   blue_handler  = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (blue_adj),
  610.                              "handler"));
  611.  
  612.   gtk_signal_handler_block (GTK_OBJECT (red_adj),   red_handler);
  613.   gtk_signal_handler_block (GTK_OBJECT (green_adj), green_handler);
  614.   gtk_signal_handler_block (GTK_OBJECT (blue_adj),  blue_handler);
  615.  
  616.   if (widget == from_colorbutton)
  617.     {
  618.       gtk_adjustment_set_value (GTK_ADJUSTMENT (red_adj),   xargs.fromred);
  619.       gtk_adjustment_set_value (GTK_ADJUSTMENT (green_adj), xargs.fromgreen);
  620.       gtk_adjustment_set_value (GTK_ADJUSTMENT (blue_adj),  xargs.fromblue);
  621.     }
  622.   else
  623.     {
  624.       gtk_adjustment_set_value (GTK_ADJUSTMENT (red_adj),   xargs.tored);
  625.       gtk_adjustment_set_value (GTK_ADJUSTMENT (green_adj), xargs.togreen);
  626.       gtk_adjustment_set_value (GTK_ADJUSTMENT (blue_adj),  xargs.toblue);
  627.     }
  628.  
  629.   gtk_signal_handler_unblock (GTK_OBJECT (red_adj),   red_handler);
  630.   gtk_signal_handler_unblock (GTK_OBJECT (green_adj), green_handler);
  631.   gtk_signal_handler_unblock (GTK_OBJECT (blue_adj),  blue_handler);
  632.  
  633.   update_preview ();
  634. }
  635.  
  636. static void
  637. scale_callback (GtkAdjustment *adj,
  638.         gpointer       data)
  639. {
  640.   GtkObject *object;
  641.   guchar    *val = data;
  642.   guint      handler;
  643.   gint       i;
  644.  
  645.   *val = (guchar) adj->value;
  646.  
  647.   object = gtk_object_get_user_data (GTK_OBJECT (adj));
  648.  
  649.   if (GIMP_IS_COLOR_BUTTON (object))
  650.     {
  651.       gimp_color_button_update (GIMP_COLOR_BUTTON (object));
  652.     }
  653.   else if (GTK_IS_ADJUSTMENT (object) && lock_threshold == TRUE)
  654.     {
  655.       for (i = 0; i < 2; i++)
  656.     {
  657.       handler = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (object),
  658.                                "handler"));
  659.       gtk_signal_handler_block (GTK_OBJECT (object), handler);
  660.       gtk_adjustment_set_value (GTK_ADJUSTMENT (object), adj->value);
  661.       gtk_signal_handler_unblock (GTK_OBJECT (object), handler);
  662.  
  663.       object = gtk_object_get_user_data (GTK_OBJECT (object));
  664.     }
  665.       xargs.red_threshold = xargs.green_threshold = xargs.blue_threshold = *val;
  666.     }
  667.  
  668.   update_preview ();
  669. }
  670.  
  671. static void
  672. update_preview (void)
  673. {
  674.   real_exchange (sel_x1, sel_y1,
  675.          sel_x1 + prev_width, sel_y1 + prev_height,
  676.          TRUE);
  677.  
  678.   gtk_widget_queue_draw (preview);
  679.   gdk_flush ();
  680. }
  681.  
  682. static void
  683. real_exchange (gint     x1,
  684.            gint     y1,
  685.            gint     x2,
  686.            gint     y2,
  687.            gboolean do_preview)
  688. {
  689.   GimpPixelRgn  srcPR, destPR;
  690.   guchar    *src_row, *dest_row;
  691.   gint       x, y, bpp = drw->bpp;
  692.   gint       width, height;
  693.  
  694.   /* fill if necessary */
  695.   if (x1 == -1 || y1 == -1 || x2 == -1 || y2 == -1)
  696.     {
  697.       x1 = sel_x1;
  698.       y1 = sel_y1;
  699.       x2 = sel_x2;
  700.       y2 = sel_y2;
  701.     }
  702.  
  703.   /* check for valid coordinates */
  704.   width  = x2 - x1;
  705.   height = y2 - y1;
  706.  
  707.   /* allocate memory */
  708.   src_row  = g_new (guchar, drw->width * bpp);
  709.  
  710.   if (do_preview && has_alpha)
  711.     dest_row = g_new (guchar, drw->width * (bpp - 1));
  712.   else
  713.     dest_row = g_new (guchar, drw->width * bpp);
  714.  
  715.   gimp_pixel_rgn_init (&srcPR, drw, 0, 0, drw->width, drw->height, FALSE, FALSE);
  716.  
  717.   if (! do_preview)
  718.     gimp_pixel_rgn_init (&destPR, drw, 0, 0, width, height, TRUE, TRUE);
  719.  
  720.   for (y = y1; y < y2; y++)
  721.     {
  722.       gimp_pixel_rgn_get_row (&srcPR, src_row, 0, y, drw->width);
  723.       for (x = x1; x < x2; x++)
  724.     {
  725.       gint pixel_red, pixel_green, pixel_blue;
  726.       gint min_red, max_red, min_green, max_green, min_blue, max_blue;
  727.       gint new_red, new_green, new_blue;
  728.       gint idx, rest;
  729.  
  730.       /* get boundary values */
  731.       min_red   = MAX (xargs.fromred - xargs.red_threshold, 0);
  732.       min_green = MAX (xargs.fromgreen - xargs.green_threshold, 0);
  733.       min_blue  = MAX (xargs.fromblue - xargs.blue_threshold, 0);
  734.  
  735.       max_red   = MIN (xargs.fromred + xargs.red_threshold, 255);
  736.       max_green = MIN (xargs.fromgreen + xargs.green_threshold, 255);
  737.       max_blue  = MIN (xargs.fromblue + xargs.blue_threshold, 255);
  738.  
  739.       /* get current pixel-values */
  740.       pixel_red   = src_row[x * bpp];
  741.       pixel_green = src_row[x * bpp + 1];
  742.       pixel_blue  = src_row[x * bpp + 2];
  743.  
  744.       /* shift down for preview */
  745.       if (do_preview)
  746.         {
  747.           if (has_alpha)
  748.         idx = (x - x1) * (bpp - 1);
  749.           else
  750.         idx = (x - x1) * bpp;
  751.         }
  752.       else
  753.         idx = x * bpp;
  754.  
  755.       /* want this pixel? */
  756.       if (pixel_red >= min_red &&
  757.           pixel_red <= max_red &&
  758.           pixel_green >= min_green &&
  759.           pixel_green <= max_green &&
  760.           pixel_blue >= min_blue &&
  761.           pixel_blue <= max_blue)
  762.         {
  763.           gint red_delta, green_delta, blue_delta;
  764.  
  765.           red_delta   = pixel_red - xargs.fromred;
  766.           green_delta = pixel_green - xargs.fromgreen;
  767.           blue_delta  = pixel_blue - xargs.fromblue;
  768.           new_red   = (guchar) CLAMP (xargs.tored + red_delta, 0, 255);
  769.           new_green = (guchar) CLAMP (xargs.togreen + green_delta, 0, 255);
  770.           new_blue  = (guchar) CLAMP (xargs.toblue + blue_delta, 0, 255);
  771.         }
  772.       else
  773.         {
  774.           new_red   = pixel_red;
  775.           new_green = pixel_green;
  776.           new_blue  = pixel_blue;
  777.         }
  778.  
  779.       /* fill buffer (cast it too) */
  780.       dest_row[idx + 0] = (guchar) new_red;
  781.       dest_row[idx + 1] = (guchar) new_green;
  782.       dest_row[idx + 2] = (guchar) new_blue;
  783.  
  784.       /* copy rest (ie the alpha-channel).  But, only if we're not
  785.        * previewing, otherwise we don't have room for the alpha
  786.        * and overrun the buffer, causing a segfault on the
  787.        * g_free() at the bottom of this function.  Maybe we should
  788.        * convert the alpha and blend in the chequerboard pattern,
  789.        * but I'll leave that as an excercise for the reader... */
  790.       if (!do_preview)
  791.         for (rest = 3; rest < bpp; rest++)
  792.           dest_row[idx + rest] = src_row[x * bpp + rest];
  793.     }
  794.       /* store the dest */
  795.       if (do_preview) {
  796.     gtk_preview_draw_row (GTK_PREVIEW (preview), dest_row, 0, y - y1, width);
  797.       }
  798.       else
  799.     gimp_pixel_rgn_set_row (&destPR, dest_row, 0, y, drw->width);
  800.       /* and tell the user what we're doing */
  801.       if (! do_preview && (y % 10) == 0)
  802.     gimp_progress_update ((double) y / (double) height);
  803.     }
  804.   g_free(src_row);
  805.   g_free(dest_row);
  806.   if (! do_preview)
  807.     {
  808.       /* update the processed region */
  809.       gimp_drawable_flush (drw);
  810.       gimp_drawable_merge_shadow (drw->id, TRUE);
  811.       gimp_drawable_update (drw->id, x1, y1, width, height);
  812.     }
  813. }
  814.