home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / threshold.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-25  |  13.7 KB  |  558 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  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. #include "config.h"
  20.  
  21. #include <glib.h>
  22.  
  23. #include "apptypes.h"
  24.  
  25. #include "appenv.h"
  26. #include "drawable.h"
  27. #include "gdisplay.h"
  28. #include "gimpui.h"
  29. #include "threshold.h"
  30.  
  31. #include "libgimp/gimpintl.h"
  32.  
  33.  
  34. #define HISTOGRAM_WIDTH  256
  35. #define HISTOGRAM_HEIGHT 150
  36.  
  37. #define LOW        0x1
  38. #define HIGH       0x2
  39. #define HISTORGAM  0x4
  40. #define ALL       (LOW | HIGH | HISTOGRAM)
  41.  
  42. /*  the threshold structures  */
  43.  
  44. typedef struct _Threshold Threshold;
  45.  
  46. struct _Threshold
  47. {
  48.   gint x, y;    /*  coords for last mouse click  */
  49. };
  50.  
  51. /*  the threshold tool options  */
  52. static ToolOptions *threshold_options = NULL;
  53.  
  54. /*  the threshold tool dialog  */
  55. static ThresholdDialog *threshold_dialog = NULL;
  56.  
  57. /*  threshold action functions  */
  58. static void   threshold_control (Tool *, ToolAction, gpointer);
  59.  
  60. static ThresholdDialog * threshold_dialog_new (void);
  61.  
  62. static void   threshold_update                     (ThresholdDialog *,
  63.                             gint);
  64. static void   threshold_preview                    (ThresholdDialog *);
  65. static void   threshold_reset_callback             (GtkWidget *, gpointer);
  66. static void   threshold_ok_callback                (GtkWidget *, gpointer);
  67. static void   threshold_cancel_callback            (GtkWidget *, gpointer);
  68. static void   threshold_preview_update             (GtkWidget *, gpointer);
  69. static void   threshold_low_threshold_adjustment_update  (GtkAdjustment *,
  70.                               gpointer);
  71. static void   threshold_high_threshold_adjustment_update (GtkAdjustment *,
  72.                               gpointer);
  73.  
  74. static void   threshold                 (PixelRegion *, PixelRegion *, void *);
  75. static void   threshold_histogram_range (HistogramWidget *, gint, gint,
  76.                      gpointer);
  77. /*  threshold machinery  */
  78.  
  79. void
  80. threshold_2 (void        *data,
  81.          PixelRegion *srcPR,
  82.          PixelRegion *destPR)
  83. {
  84.   /*  this function just re-orders the arguments so we can use 
  85.    *  pixel_regions_process_paralell
  86.    */
  87.   threshold (srcPR, destPR, data);
  88. }
  89.  
  90. static void
  91. threshold (PixelRegion *srcPR,
  92.        PixelRegion *destPR,
  93.        void        *data)
  94. {
  95.   ThresholdDialog *td;
  96.   unsigned char *src, *s;
  97.   unsigned char *dest, *d;
  98.   int has_alpha, alpha;
  99.   int w, h, b;
  100.   int value;
  101.  
  102.   td = (ThresholdDialog *) data;
  103.  
  104.   h = srcPR->h;
  105.   src = srcPR->data;
  106.   dest = destPR->data;
  107.   has_alpha = (srcPR->bytes == 2 || srcPR->bytes == 4);
  108.   alpha = has_alpha ? srcPR->bytes - 1 : srcPR->bytes;
  109.  
  110.   while (h--)
  111.     {
  112.       w = srcPR->w;
  113.       s = src;
  114.       d = dest;
  115.       while (w--)
  116.     {
  117.       if (td->color)
  118.         {
  119.           value = MAX (s[RED_PIX], s[GREEN_PIX]);
  120.           value = MAX (value, s[BLUE_PIX]);
  121.  
  122.           value = (value >= td->low_threshold && value <= td->high_threshold ) ? 255 : 0;
  123.         }
  124.       else
  125.         value = (s[GRAY_PIX] >= td->low_threshold && s[GRAY_PIX] <= td->high_threshold) ? 255 : 0;
  126.  
  127.       for (b = 0; b < alpha; b++)
  128.         d[b] = value;
  129.  
  130.       if (has_alpha)
  131.         d[alpha] = s[alpha];
  132.  
  133.       s += srcPR->bytes;
  134.       d += destPR->bytes;
  135.     }
  136.  
  137.       src += srcPR->rowstride;
  138.       dest += destPR->rowstride;
  139.     }
  140. }
  141.  
  142. /*  threshold action functions  */
  143.  
  144. static void
  145. threshold_control (Tool       *tool,
  146.            ToolAction  action,
  147.            gpointer    gdisp_ptr)
  148. {
  149.   switch (action)
  150.     {
  151.     case PAUSE:
  152.       break;
  153.  
  154.     case RESUME:
  155.       break;
  156.  
  157.     case HALT:
  158.       threshold_dialog_hide ();
  159.       break;
  160.  
  161.     default:
  162.       break;
  163.     }
  164. }
  165.  
  166. Tool *
  167. tools_new_threshold (void)
  168. {
  169.   Tool * tool;
  170.   Threshold * private;
  171.  
  172.   /*  The tool options  */
  173.   if (! threshold_options)
  174.     {
  175.       threshold_options = tool_options_new (_("Threshold"));
  176.       tools_register (THRESHOLD, threshold_options);
  177.     }
  178.  
  179.   tool = tools_new_tool (THRESHOLD);
  180.   private = g_new0 (Threshold, 1);
  181.  
  182.   tool->scroll_lock = TRUE;   /*  Disallow scrolling  */
  183.   tool->preserve    = FALSE;  /*  Don't preserve on drawable change  */
  184.  
  185.   tool->private = (void *) private;
  186.  
  187.   tool->control_func = threshold_control;
  188.  
  189.   return tool;
  190. }
  191.  
  192. void
  193. threshold_dialog_hide (void)
  194. {
  195.   if (threshold_dialog)
  196.     threshold_cancel_callback (NULL, (gpointer) threshold_dialog);
  197. }
  198.   
  199. void
  200. tools_free_threshold (Tool *tool)
  201. {
  202.   Threshold * thresh;
  203.  
  204.   thresh = (Threshold *) tool->private;
  205.  
  206.   /*  Close the threshold dialog  */
  207.   threshold_dialog_hide ();
  208.  
  209.   g_free (thresh);
  210. }
  211.  
  212. void
  213. threshold_initialize (GDisplay *gdisp)
  214. {
  215.   if (drawable_indexed (gimage_active_drawable (gdisp->gimage)))
  216.     {
  217.       g_message (_("Threshold does not operate on indexed drawables."));
  218.       return;
  219.     }
  220.  
  221.   /*  The threshold dialog  */
  222.   if (!threshold_dialog)
  223.     threshold_dialog = threshold_dialog_new ();
  224.   else
  225.     if (!GTK_WIDGET_VISIBLE (threshold_dialog->shell))
  226.       gtk_widget_show (threshold_dialog->shell);
  227.  
  228.   threshold_dialog->low_threshold  = 127;
  229.   threshold_dialog->high_threshold = 255;
  230.  
  231.   threshold_dialog->drawable = gimage_active_drawable (gdisp->gimage);
  232.   threshold_dialog->color = drawable_color (threshold_dialog->drawable);
  233.   threshold_dialog->image_map =
  234.     image_map_create (gdisp, threshold_dialog->drawable);
  235.  
  236.   gimp_histogram_calculate_drawable (threshold_dialog->hist,
  237.                      threshold_dialog->drawable);
  238.  
  239.   gtk_signal_handler_block_by_data (GTK_OBJECT (threshold_dialog->histogram),
  240.                     threshold_dialog);
  241.   histogram_widget_update (threshold_dialog->histogram,
  242.                threshold_dialog->hist);
  243.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (threshold_dialog->histogram),
  244.                       threshold_dialog);
  245.  
  246.   threshold_update (threshold_dialog, ALL);
  247.  
  248.   if (threshold_dialog->preview)
  249.     threshold_preview (threshold_dialog);
  250. }
  251.  
  252. /**********************/
  253. /*  Threshold dialog  */
  254. /**********************/
  255.  
  256. static ThresholdDialog *
  257. threshold_dialog_new (void)
  258. {
  259.   ThresholdDialog *td;
  260.   GtkWidget *vbox;
  261.   GtkWidget *hbox;
  262.   GtkWidget *spinbutton;
  263.   GtkWidget *label;
  264.   GtkWidget *frame;
  265.   GtkWidget *toggle;
  266.   GtkObject *data;
  267.  
  268.   td = g_new (ThresholdDialog, 1);
  269.   td->preview        = TRUE;
  270.   td->low_threshold  = 127;
  271.   td->high_threshold = 255;
  272.   td->hist           = gimp_histogram_new ();
  273.  
  274.   /*  The shell and main vbox  */
  275.   td->shell =
  276.     gimp_dialog_new (_("Threshold"), "threshold",
  277.              tools_help_func, NULL,
  278.              GTK_WIN_POS_NONE,
  279.              FALSE, TRUE, FALSE,
  280.  
  281.              _("OK"), threshold_ok_callback,
  282.              td, NULL, NULL, TRUE, FALSE,
  283.              _("Reset"), threshold_reset_callback,
  284.              td, NULL, NULL, TRUE, FALSE,
  285.              _("Cancel"), threshold_cancel_callback,
  286.              td, NULL, NULL, FALSE, TRUE,
  287.  
  288.              NULL);
  289.  
  290.   vbox = gtk_vbox_new (FALSE, 4);
  291.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  292.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (td->shell)->vbox), vbox);
  293.  
  294.   /*  Horizontal box for threshold text widget  */
  295.   hbox = gtk_hbox_new (FALSE, 4);
  296.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  297.  
  298.   label = gtk_label_new (_("Threshold Range:"));
  299.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  300.   gtk_widget_show (label);
  301.  
  302.   /*  low threshold spinbutton  */
  303.   data = gtk_adjustment_new (td->low_threshold, 0.0, 255.0, 1.0, 10.0, 0.0);
  304.   td->low_threshold_data = GTK_ADJUSTMENT (data);
  305.  
  306.   spinbutton = gtk_spin_button_new (td->low_threshold_data, 1.0, 0);
  307.   gtk_widget_set_usize (spinbutton, 75, -1);
  308.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  309.  
  310.   gtk_signal_connect (GTK_OBJECT (td->low_threshold_data), "value_changed",
  311.               GTK_SIGNAL_FUNC (threshold_low_threshold_adjustment_update),
  312.               td);
  313.  
  314.   gtk_widget_show (spinbutton);
  315.  
  316.   /* high threshold spinbutton  */
  317.   data = gtk_adjustment_new (td->high_threshold, 0.0, 255.0, 1.0, 10.0, 0.0);
  318.   td->high_threshold_data = GTK_ADJUSTMENT (data);
  319.  
  320.   spinbutton = gtk_spin_button_new (td->high_threshold_data, 1.0, 0);
  321.   gtk_widget_set_usize (spinbutton, 75, -1);
  322.   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
  323.  
  324.   gtk_signal_connect (GTK_OBJECT (td->high_threshold_data), "value_changed",
  325.               GTK_SIGNAL_FUNC (threshold_high_threshold_adjustment_update),
  326.               td);
  327.  
  328.   gtk_widget_show (spinbutton);
  329.  
  330.   gtk_widget_show (hbox);
  331.  
  332.   /*  The threshold histogram  */
  333.   hbox = gtk_hbox_new (TRUE, 0);
  334.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  335.  
  336.   frame = gtk_frame_new (NULL);
  337.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  338.   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, FALSE, 0);
  339.  
  340.   td->histogram = histogram_widget_new (HISTOGRAM_WIDTH, HISTOGRAM_HEIGHT);
  341.  
  342.   gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (td->histogram));
  343.  
  344.   gtk_signal_connect (GTK_OBJECT (td->histogram), "range_changed",
  345.               GTK_SIGNAL_FUNC (threshold_histogram_range),
  346.               td);
  347.  
  348.   gtk_widget_show (GTK_WIDGET(td->histogram));
  349.  
  350.   gtk_widget_show (frame);
  351.   gtk_widget_show (hbox);
  352.  
  353.   /*  Horizontal box for preview  */
  354.   hbox = gtk_hbox_new (FALSE, 4);
  355.   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  356.  
  357.   /*  The preview toggle  */
  358.   toggle = gtk_check_button_new_with_label (_("Preview"));
  359.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), td->preview);
  360.   gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
  361.  
  362.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  363.               GTK_SIGNAL_FUNC (threshold_preview_update),
  364.               td);
  365.  
  366.   gtk_widget_show (toggle);
  367.   gtk_widget_show (hbox);
  368.  
  369.   gtk_widget_show (vbox);
  370.   gtk_widget_show (td->shell);
  371.  
  372.   return td;
  373. }
  374.  
  375. static void
  376. threshold_update (ThresholdDialog *td,
  377.           gint             update)
  378. {
  379.   if (update & LOW)
  380.     {
  381.       gtk_adjustment_set_value (td->low_threshold_data, td->low_threshold);
  382.     }
  383.   if (update & HIGH)
  384.     {
  385.       gtk_adjustment_set_value (td->high_threshold_data, td->high_threshold);
  386.     }
  387.   if (update & HISTOGRAM)
  388.     {
  389.       histogram_widget_range (td->histogram,
  390.                   td->low_threshold,
  391.                   td->high_threshold);
  392.     }
  393. }
  394.  
  395. static void
  396. threshold_preview (ThresholdDialog *td)
  397. {
  398.   if (!td->image_map)
  399.     {
  400.       g_warning ("threshold_preview(): No image map");
  401.       return;
  402.     }
  403.  
  404.   active_tool->preserve = TRUE;
  405.   image_map_apply (td->image_map, threshold, td);
  406.   active_tool->preserve = FALSE;
  407. }
  408.  
  409. static void
  410. threshold_reset_callback (GtkWidget *widget,
  411.               gpointer   data)
  412. {
  413.   ThresholdDialog *td;
  414.  
  415.   td = (ThresholdDialog *) data;
  416.  
  417.   td->low_threshold  = 127.0;
  418.   td->high_threshold = 255.0;
  419.  
  420.   threshold_update (td, ALL);
  421.  
  422.   if (td->preview)
  423.     threshold_preview (td);
  424. }
  425.  
  426. static void
  427. threshold_ok_callback (GtkWidget *widget,
  428.                gpointer   data)
  429. {
  430.   ThresholdDialog *td;
  431.  
  432.   td = (ThresholdDialog *) data;
  433.  
  434.   gimp_dialog_hide (td->shell);
  435.   
  436.   active_tool->preserve = TRUE;
  437.  
  438.   if (!td->preview)
  439.     image_map_apply (td->image_map, threshold, (void *) td);
  440.  
  441.   if (td->image_map)
  442.     image_map_commit (td->image_map);
  443.  
  444.   active_tool->preserve = FALSE;
  445.  
  446.   td->image_map = NULL;
  447.  
  448.   active_tool->gdisp_ptr = NULL;
  449.   active_tool->drawable = NULL;
  450. }
  451.  
  452. static void
  453. threshold_cancel_callback (GtkWidget *widget,
  454.                gpointer   data)
  455. {
  456.   ThresholdDialog *td;
  457.  
  458.   td = (ThresholdDialog *) data;
  459.  
  460.   gimp_dialog_hide (td->shell);
  461.  
  462.   if (td->image_map)
  463.     {
  464.       active_tool->preserve = TRUE;
  465.       image_map_abort (td->image_map);
  466.       active_tool->preserve = FALSE;
  467.  
  468.       td->image_map = NULL;
  469.       gdisplays_flush ();
  470.     }
  471.  
  472.   active_tool->gdisp_ptr = NULL;
  473.   active_tool->drawable = NULL;
  474. }
  475.  
  476. static void
  477. threshold_preview_update (GtkWidget *widget,
  478.               gpointer   data)
  479. {
  480.   ThresholdDialog *td;
  481.  
  482.   td = (ThresholdDialog *) data;
  483.  
  484.   if (GTK_TOGGLE_BUTTON (widget)->active)
  485.     {
  486.       td->preview = TRUE;
  487.       threshold_preview (td);
  488.     }
  489.   else
  490.     {    
  491.       td->preview = FALSE;
  492.       if (td->image_map)
  493.     {
  494.       active_tool->preserve = TRUE;
  495.       image_map_clear (td->image_map);
  496.       active_tool->preserve = FALSE;
  497.       gdisplays_flush ();
  498.     }
  499.     }
  500. }
  501.  
  502. static void
  503. threshold_low_threshold_adjustment_update (GtkAdjustment *adjustment,
  504.                        gpointer       data)
  505. {
  506.   ThresholdDialog *td;
  507.  
  508.   td = (ThresholdDialog *) data;
  509.  
  510.   if (td->low_threshold != adjustment->value)
  511.     {
  512.       td->low_threshold = adjustment->value;
  513.  
  514.       threshold_update (td, HISTOGRAM);
  515.  
  516.       if (td->preview)
  517.     threshold_preview (td);
  518.     }
  519. }
  520.  
  521. static void
  522. threshold_high_threshold_adjustment_update (GtkAdjustment *adjustment,
  523.                         gpointer       data)
  524. {
  525.   ThresholdDialog *td;
  526.  
  527.   td = (ThresholdDialog *) data;
  528.  
  529.   if (td->high_threshold != adjustment->value)
  530.     {
  531.       td->high_threshold = adjustment->value;
  532.  
  533.       threshold_update (td, HISTOGRAM);
  534.  
  535.       if (td->preview)
  536.     threshold_preview (td);
  537.     }
  538. }
  539.  
  540. static void
  541. threshold_histogram_range (HistogramWidget *widget,
  542.                gint             start,
  543.                gint             end,
  544.                gpointer         data)
  545. {
  546.   ThresholdDialog *td;
  547.  
  548.   td = (ThresholdDialog *) data;
  549.  
  550.   td->low_threshold  = start;
  551.   td->high_threshold = end;
  552.  
  553.   threshold_update (td, LOW | HIGH);
  554.  
  555.   if (td->preview)
  556.     threshold_preview (td);
  557. }
  558.