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

  1. /*
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This is a plug-in for the GIMP.
  5.  *
  6.  * Plugin to convert a selection to a path.
  7.  *
  8.  * Copyright (C) 1999 Andy Thomas  alt@gimp.org
  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. /* Change log:-
  27.  * 0.1 First version.
  28.  */
  29.  
  30. #include "config.h"
  31.  
  32. #include <glib.h>
  33.  
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <math.h>
  38.  
  39. #include <gtk/gtk.h>
  40.  
  41. #include <libgimp/gimp.h>
  42. #include <libgimp/gimpui.h>
  43.  
  44. #include "global.h"
  45. #include "types.h"
  46. #include "pxl-outline.h"
  47. #include "fit.h"
  48. #include "spline.h"
  49. #include "sel2path.h"
  50.  
  51. #include "libgimp/stdplugins-intl.h"
  52.  
  53.  
  54. #define MID_POINT 127
  55.  
  56. /***** Magic numbers *****/
  57.  
  58. /* Variables set in dialog box */
  59.  
  60. static void      query  (void);
  61. static void      run    (gchar    *name,
  62.              gint      nparams,
  63.              GimpParam   *param,
  64.              gint     *nreturn_vals,
  65.              GimpParam  **return_vals);
  66.  
  67. static gint      sel2path_dialog         (SELVALS   *sels);
  68. static void      sel2path_ok_callback    (GtkWidget *widget, 
  69.                       gpointer   data);
  70. static void      sel2path_reset_callback (GtkWidget *widget,
  71.                       gpointer   data);
  72. static void      dialog_print_selVals    (SELVALS   *sels);
  73. gboolean         do_sel2path             (gint32     drawable_ID, 
  74.                       gint32     image_ID);
  75.  
  76.  
  77. GimpPlugInInfo PLUG_IN_INFO =
  78. {
  79.   NULL,    /* init_proc */
  80.   NULL,    /* quit_proc */
  81.   query,   /* query_proc */
  82.   run,     /* run_proc */
  83. };
  84.  
  85. static gint    sel_x1, sel_y1, sel_x2, sel_y2;
  86. static gint    has_sel, sel_width, sel_height;
  87. static SELVALS selVals;
  88. GimpPixelRgn      selection_rgn;
  89. gboolean       retVal = TRUE;  /* Toggle if cancle button clicked */
  90.  
  91. MAIN ()
  92.  
  93. static void
  94. query_2 (void)
  95. {
  96.   static GimpParamDef args[] =
  97.   {
  98.     { GIMP_PDB_INT32,    "run_mode",                    "Interactive, non-interactive" },
  99.     { GIMP_PDB_IMAGE,    "image",                       "Input image (unused)" },
  100.     { GIMP_PDB_DRAWABLE, "drawable",                    "Input drawable" }, 
  101.     { GIMP_PDB_FLOAT,    "align_threshold",             "align_threshold"},
  102.     { GIMP_PDB_FLOAT,    "corner_always_threshold",     "corner_always_threshold"},
  103.     { GIMP_PDB_INT8,     "corner_surround",             "corner_surround"},
  104.     { GIMP_PDB_FLOAT,    "corner_threshold",            "corner_threshold"},
  105.     { GIMP_PDB_FLOAT,    "error_threshold",             "error_threshold"},
  106.     { GIMP_PDB_INT8,     "filter_alternative_surround", "filter_alternative_surround"},
  107.     { GIMP_PDB_FLOAT,    "filter_epsilon",              "filter_epsilon"},
  108.     { GIMP_PDB_INT8,     "filter_iteration_count",      "filter_iteration_count"},
  109.     { GIMP_PDB_FLOAT,    "filter_percent",              "filter_percent"},
  110.     { GIMP_PDB_INT8,     "filter_secondary_surround",   "filter_secondary_surround"},
  111.     { GIMP_PDB_INT8,     "filter_surround",             "filter_surround"},
  112.     { GIMP_PDB_INT8,     "keep_knees",                  "{1-Yes, 0-No}"},
  113.     { GIMP_PDB_FLOAT,    "line_reversion_threshold",    "line_reversion_threshold"},
  114.     { GIMP_PDB_FLOAT,    "line_threshold",              "line_threshold"},
  115.     { GIMP_PDB_FLOAT,    "reparameterize_improvement",  "reparameterize_improvement"},
  116.     { GIMP_PDB_FLOAT,    "reparameterize_threshold",    "reparameterize_threshold"},
  117.     { GIMP_PDB_FLOAT,    "subdivide_search",            "subdivide_search"},
  118.     { GIMP_PDB_INT8,     "subdivide_surround",          "subdivide_surround"},
  119.     { GIMP_PDB_FLOAT,    "subdivide_threshold",         "subdivide_threshold"},
  120.     { GIMP_PDB_INT8,     "tangent_surround",            "tangent_surround"},
  121.   };
  122.   static GimpParamDef *return_vals = NULL;
  123.   static int nargs = sizeof (args) / sizeof (args[0]);
  124.   static int nreturn_vals = 0;
  125.  
  126.   gimp_install_procedure ("plug_in_sel2path_advanced",
  127.               "Converts a selection to a path (with advanced user menu)",
  128.               "Converts a selection to a path (with advanced user menu)",
  129.               "Andy Thomas",
  130.               "Andy Thomas",
  131.               "1999",
  132.               NULL,
  133.               "RGB*, INDEXED*, GRAY*",
  134.               GIMP_PLUGIN,
  135.               nargs, nreturn_vals,
  136.               args, return_vals);
  137. }
  138.  
  139. static void
  140. query (void)
  141. {
  142.   static GimpParamDef args[] =
  143.   {
  144.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  145.     { GIMP_PDB_IMAGE,    "image",    "Input image (unused)" },
  146.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  147.   };
  148.   static GimpParamDef *return_vals = NULL;
  149.   static int nargs = sizeof (args) / sizeof (args[0]);
  150.   static int nreturn_vals = 0;
  151.  
  152.   INIT_I18N();
  153.  
  154.   gimp_install_procedure ("plug_in_sel2path",
  155.               "Converts a selection to a path",
  156.               "Converts a selection to a path",
  157.               "Andy Thomas",
  158.               "Andy Thomas",
  159.               "1999",
  160.               N_("<Image>/Select/To Path"),
  161.               "RGB*, INDEXED*, GRAY*",
  162.               GIMP_PLUGIN,
  163.               nargs, nreturn_vals,
  164.               args, return_vals);
  165.  
  166.   query_2 ();
  167. }
  168.  
  169. static void
  170. run (gchar    *name,
  171.      gint      nparams,
  172.      GimpParam   *param,
  173.      gint     *nreturn_vals,
  174.      GimpParam  **return_vals)
  175. {
  176.   static GimpParam values[1];
  177.   GimpDrawable *   drawable;
  178.   gint32        drawable_ID;
  179.   gint32        image_ID;
  180.   GimpRunModeType  run_mode;
  181.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  182.   gboolean      no_dialog = FALSE;
  183.  
  184.   run_mode = param[0].data.d_int32;
  185.  
  186.   if(!strcmp(name,"plug_in_sel2path")) 
  187.     {
  188.       no_dialog = TRUE;
  189.       INIT_I18N();
  190.     } 
  191.   else
  192.     INIT_I18N_UI();
  193.  
  194.   *nreturn_vals = 1;
  195.   *return_vals = values;
  196.  
  197.   values[0].type = GIMP_PDB_STATUS;
  198.   values[0].data.d_status = status;
  199.  
  200.   drawable_ID = param[2].data.d_drawable;
  201.   drawable = gimp_drawable_get (drawable_ID);
  202.  
  203.   image_ID = gimp_drawable_image_id(drawable_ID);
  204.  
  205.   if(gimp_selection_is_empty(image_ID))
  206.     {
  207.       g_message(_("No selection to convert"));
  208.       gimp_drawable_detach (drawable);
  209.       return;
  210.     }
  211.  
  212.   fit_set_default_params(&selVals);
  213.   
  214.   if(!no_dialog)
  215.     {
  216.       switch (run_mode)
  217.     {
  218.     case GIMP_RUN_INTERACTIVE:
  219.       if (gimp_get_data_size ("plug_in_sel2path_advanced") > 0)
  220.         {
  221.           gimp_get_data ("plug_in_sel2path_advanced", &selVals);
  222.         }
  223.  
  224.       if (!sel2path_dialog (&selVals))
  225.         {
  226.           gimp_drawable_detach (drawable);
  227.           return;
  228.         }
  229.       /* Get the current settings */
  230.       fit_set_params (&selVals);
  231.       break;
  232.       
  233.     case GIMP_RUN_NONINTERACTIVE:
  234.       if (nparams != 23)
  235.         status = GIMP_PDB_CALLING_ERROR;
  236.       
  237.       if (status == GIMP_PDB_SUCCESS) 
  238.         {
  239.           selVals.align_threshold             =  param[3].data.d_float;
  240.           selVals.corner_always_threshold     =  param[4].data.d_float;
  241.           selVals.corner_surround             =  param[5].data.d_int8;
  242.           selVals.corner_threshold            =  param[6].data.d_float;
  243.           selVals.error_threshold             =  param[7].data.d_float;
  244.           selVals.filter_alternative_surround =  param[8].data.d_int8;
  245.           selVals.filter_epsilon              =  param[9].data.d_float;
  246.           selVals.filter_iteration_count      = param[10].data.d_int8;
  247.           selVals.filter_percent              = param[11].data.d_float;
  248.           selVals.filter_secondary_surround   = param[12].data.d_int8;
  249.           selVals.filter_surround             = param[13].data.d_int8;
  250.           selVals.keep_knees                  = param[14].data.d_int8;
  251.           selVals.line_reversion_threshold    = param[15].data.d_float;
  252.           selVals.line_threshold              = param[16].data.d_float;
  253.           selVals.reparameterize_improvement  = param[17].data.d_float;
  254.           selVals.reparameterize_threshold    = param[18].data.d_float;
  255.           selVals.subdivide_search            = param[19].data.d_float;
  256.           selVals.subdivide_surround          = param[20].data.d_int8;
  257.           selVals.subdivide_threshold         = param[21].data.d_float;
  258.           selVals.tangent_surround            = param[22].data.d_int8;
  259.           fit_set_params(&selVals);
  260.       }
  261.  
  262.       break;
  263.       
  264.     case GIMP_RUN_WITH_LAST_VALS:
  265.       if(gimp_get_data_size ("plug_in_sel2path_advanced") > 0)
  266.         {
  267.           gimp_get_data ("plug_in_sel2path_advanced", &selVals);
  268.           
  269.           /* Set up the last values */
  270.           fit_set_params (&selVals);
  271.         }
  272.  
  273.       break;
  274.       
  275.     default:
  276.       break;
  277.     }
  278.     }
  279.  
  280.   do_sel2path (drawable_ID,image_ID);
  281.   values[0].data.d_status = status;
  282.  
  283.   if (status == GIMP_PDB_SUCCESS)
  284.     {
  285.       dialog_print_selVals(&selVals);
  286.       if (run_mode == GIMP_RUN_INTERACTIVE && !no_dialog)
  287.     gimp_set_data ("plug_in_sel2path_advanced", &selVals, sizeof(SELVALS));
  288.     }
  289.  
  290.   gimp_drawable_detach (drawable);
  291. }
  292.  
  293. static void
  294. dialog_print_selVals (SELVALS *sels)
  295. {
  296. #if 0
  297.   printf ("selVals.align_threshold %g\n",             selVals.align_threshold);
  298.   printf ("selVals.corner_always_threshol %g\n",      selVals.corner_always_threshold);
  299.   printf ("selVals.corner_surround %g\n",             selVals.corner_surround);
  300.   printf ("selVals.corner_threshold %g\n",            selVals.corner_threshold);
  301.   printf ("selVals.error_threshold %g\n",             selVals.error_threshold);
  302.   printf ("selVals.filter_alternative_surround %g\n", selVals.filter_alternative_surround);
  303.   printf ("selVals.filter_epsilon %g\n",              selVals.filter_epsilon);
  304.   printf ("selVals.filter_iteration_count %g\n",      selVals.filter_iteration_count);
  305.   printf ("selVals.filter_percent %g\n",              selVals.filter_percent);
  306.   printf ("selVals.filter_secondary_surround %g\n",   selVals.filter_secondary_surround);
  307.   printf ("selVals.filter_surround %g\n",             selVals.filter_surround);
  308.   printf ("selVals.keep_knees %d\n",                  selVals.keep_knees);
  309.   printf ("selVals.line_reversion_threshold %g\n",    selVals.line_reversion_threshold);
  310.   printf ("selVals.line_threshold %g\n",              selVals.line_threshold);
  311.   printf ("selVals.reparameterize_improvement %g\n",  selVals.reparameterize_improvement);
  312.   printf ("selVals.reparameterize_threshold %g\n",    selVals.reparameterize_threshold);
  313.   printf ("selVals.subdivide_search %g\n"             selVals.subdivide_search);
  314.   printf ("selVals.subdivide_surround %g\n",          selVals.subdivide_surround);
  315.   printf ("selVals.subdivide_threshold %g\n",         selVals.subdivide_threshold);
  316.   printf ("selVals.tangent_surround %g\n",            selVals.tangent_surround);
  317. #endif /* 0 */
  318. }
  319.  
  320. /* Build the dialog up. This was the hard part! */
  321. static gint
  322. sel2path_dialog (SELVALS *sels)
  323. {
  324.   GtkWidget *dlg;
  325.   GtkWidget *table;
  326.  
  327.   retVal = FALSE;
  328.  
  329.   gimp_ui_init ("sel2path", FALSE);
  330.  
  331.   dlg = gimp_dialog_new (_("Sel2Path Advanced Settings"), "sel2path",
  332.              gimp_standard_help_func, "filters/sel2path.html",
  333.              GTK_WIN_POS_MOUSE,
  334.              FALSE, TRUE, FALSE,
  335.  
  336.              _("OK"), sel2path_ok_callback,
  337.              NULL, NULL, NULL, TRUE, FALSE,
  338.  
  339.              _("Reset"), sel2path_reset_callback,
  340.              NULL, NULL, NULL, FALSE, FALSE,
  341.  
  342.              _("Cancel"), gtk_widget_destroy,
  343.              NULL, 1, NULL, FALSE, TRUE,
  344.  
  345.              NULL);
  346.  
  347.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  348.               GTK_SIGNAL_FUNC (gtk_main_quit),
  349.               NULL);
  350.   
  351.   table = dialog_create_selection_area(sels);
  352.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  353.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), table);
  354.   gtk_widget_show (table);
  355.  
  356.   gtk_widget_show (dlg);
  357.  
  358.   gtk_main ();
  359.  
  360.   return retVal;
  361. }
  362.  
  363. static void
  364. sel2path_ok_callback (GtkWidget *widget,
  365.               gpointer   data)
  366. {
  367.   gtk_widget_destroy (GTK_WIDGET (data));
  368.   retVal = TRUE;
  369. }
  370.  
  371. static void
  372. sel2path_reset_callback (GtkWidget *widget,
  373.              gpointer   data)
  374. {
  375.   reset_adv_dialog();
  376.   fit_set_params(&selVals);
  377. }
  378.  
  379. guchar
  380. sel_pixel_value (gint row, 
  381.          gint col)
  382. {
  383.   guchar ret;
  384.  
  385.   if(col > sel_width ||
  386.      row > sel_height)
  387.     {
  388.       g_warning ("sel_pixel_value [%d,%d] out of bounds", col, row);
  389.       return 0;
  390.     }
  391.  
  392.   gimp_pixel_rgn_get_pixel(&selection_rgn,&ret,col+sel_x1,row+sel_y1);
  393.  
  394.   return ret;
  395. }
  396.  
  397. gboolean
  398. sel_pixel_is_white (gint row, 
  399.             gint col)
  400. {
  401.   if (sel_pixel_value (row, col) < MID_POINT)
  402.     return TRUE;
  403.   else
  404.     return FALSE;
  405. }
  406.  
  407. gint 
  408. sel_get_width (void)
  409. {
  410.   return sel_width;
  411. }
  412.  
  413. gint
  414. sel_get_height (void)
  415. {
  416.   return sel_height;
  417. }
  418.  
  419. gint 
  420. sel_valid_pixel (gint row, 
  421.          gint col)
  422. {
  423.   return (0 <= (row) && (row) < sel_get_height ()
  424.       && 0 <= (col) && (col) < sel_get_width ());
  425. }
  426.  
  427. void
  428. gen_anchor (gdouble  *p,
  429.         gdouble   x,
  430.         gdouble   y,
  431.         gboolean  is_newcurve)
  432. {
  433. /*   printf("TYPE: %s X: %d Y: %d\n",  */
  434. /*      (is_newcurve)?"3":"1",  */
  435. /*      sel_x1+(int)RINT(x),  */
  436. /*      sel_y1 + sel_height - (int)RINT(y)+1); */
  437.  
  438.   *p++ = (sel_x1 + (gint)RINT(x));
  439.   *p++ = sel_y1 + sel_height - (gint)RINT(y) + 1;
  440.   *p++ = (is_newcurve) ? 3.0 : 1.0;
  441. }
  442.   
  443.  
  444. void  
  445. gen_control (gdouble *p,
  446.          gdouble  x,
  447.          gdouble  y)
  448. {
  449. /*   printf("TYPE: 2 X: %d Y: %d\n",  */
  450. /*       sel_x1+(int)RINT(x),  */
  451. /*       sel_y1 + sel_height - (int)RINT(y)+1);  */
  452.  
  453.   *p++ = sel_x1 + (gint)RINT(x);
  454.   *p++ = sel_y1 + sel_height - (gint)RINT(y) + 1;
  455.   *p++ = 2.0;
  456.  
  457. }
  458.  
  459. void
  460. do_points (spline_list_array_type in_splines,
  461.        gint32                 image_ID)
  462. {
  463.   unsigned this_list;
  464.   gint seg_count = 0;
  465.   gint point_count = 0;
  466.   gdouble last_x,last_y;
  467.   gdouble *parray;
  468.   gdouble *cur_parray;
  469.   gint path_point_count;
  470.  
  471.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
  472.        this_list++)
  473.     {
  474.       spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
  475.       /* Ignore single points that are on their own */
  476.       if(SPLINE_LIST_LENGTH (in_list) < 2)
  477.     continue;
  478.       point_count += SPLINE_LIST_LENGTH (in_list);
  479.     }
  480.  
  481. /*   printf("Name SEL2PATH\n"); */
  482. /*   printf("#POINTS: %d\n",point_count*3); */
  483. /*   printf("CLOSED: 1\n"); */
  484. /*   printf("DRAW: 0\n"); */
  485. /*   printf("STATE: 4\n"); */
  486.  
  487.   path_point_count = point_count*9;
  488.   cur_parray = (gdouble *)g_new0(gdouble ,point_count*9);
  489.   parray = cur_parray;
  490.  
  491.   point_count = 0;
  492.  
  493.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
  494.        this_list++)
  495.     {
  496.       unsigned this_spline;
  497.       spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
  498.       
  499. /*       if(seg_count > 0 && point_count > 0)   */
  500. /*     gen_anchor(last_x,last_y,0);   */
  501.       
  502.       point_count = 0;
  503.  
  504.       /* Ignore single points that are on their own */
  505.       if(SPLINE_LIST_LENGTH (in_list) < 2)
  506.       continue;
  507.       
  508.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (in_list);
  509.        this_spline++)
  510.     {
  511.       spline_type s = SPLINE_LIST_ELT (in_list, this_spline);
  512.       
  513.       if (SPLINE_DEGREE (s) == LINEAR)
  514.         {
  515.           gen_anchor (cur_parray, 
  516.               START_POINT (s).x, START_POINT (s).y, 
  517.               seg_count && !point_count);
  518.           cur_parray += 3;
  519.           gen_control (cur_parray, START_POINT (s).x, START_POINT (s).y);
  520.           cur_parray += 3;
  521.           gen_control (cur_parray,END_POINT (s).x, END_POINT (s).y);
  522.           cur_parray += 3;
  523.           last_x = END_POINT (s).x;
  524.           last_y = END_POINT (s).y;
  525.         }
  526.       else if (SPLINE_DEGREE (s) == CUBIC)
  527.         {
  528.           gen_anchor (cur_parray,
  529.               START_POINT (s).x, START_POINT (s).y,
  530.               seg_count && !point_count);
  531.           cur_parray += 3;
  532.           gen_control (cur_parray,CONTROL1 (s).x, CONTROL1 (s).y);
  533.           cur_parray += 3;
  534.           gen_control (cur_parray,CONTROL2 (s).x, CONTROL2 (s).y);
  535.           cur_parray += 3;
  536.           last_x = END_POINT (s).x;
  537.           last_y = END_POINT (s).y;
  538.         }
  539.       else
  540.         g_message ( _("print_spline: strange degree (%d)"), 
  541.             SPLINE_DEGREE (s));
  542.       
  543.       point_count++;
  544.     }
  545.       seg_count++;
  546.     }
  547.  
  548.    gimp_path_set_points (image_ID,
  549.              _("selection_to_path"), 
  550.              1, 
  551.              path_point_count,
  552.              parray);
  553. }
  554.  
  555.  
  556. gboolean
  557. do_sel2path (gint32 drawable_ID,
  558.          gint32 image_ID)
  559. {
  560.   gint32      selection_ID;
  561.   GimpDrawable  *sel_drawable;
  562.   pixel_outline_list_type olt;
  563.   spline_list_array_type splines;
  564.  
  565.   gimp_selection_bounds (image_ID, &has_sel, 
  566.              &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  567.  
  568.   sel_width  = sel_x2 - sel_x1;
  569.   sel_height = sel_y2 - sel_y1;
  570.  
  571.   /* Now get the selection channel */
  572.  
  573.   selection_ID = gimp_image_get_selection(image_ID);
  574.  
  575.   if(selection_ID < 0)
  576.     {
  577.       g_message( _("gimp_image_get_selection failed"));
  578.       return FALSE;
  579.     }
  580.  
  581.   sel_drawable = gimp_drawable_get (selection_ID);
  582.  
  583.   if(gimp_drawable_bpp(selection_ID) != 1)
  584.     {
  585.       g_message( _("Internal error. Selection bpp > 1"));
  586.       return FALSE;
  587.     }
  588.  
  589.   gimp_pixel_rgn_init (&selection_rgn, sel_drawable,
  590.                sel_x1, sel_y1, sel_width, sel_height,
  591.                FALSE, FALSE);
  592.  
  593.   gimp_tile_cache_ntiles (2 * (sel_drawable->width + gimp_tile_width () - 1) / 
  594.               gimp_tile_width ());
  595.  
  596.   olt = find_outline_pixels ();
  597.  
  598.   splines = fitted_splines (olt);
  599.  
  600.   gimp_selection_none (image_ID);
  601.   gimp_displays_flush ();
  602.  
  603.   do_points (splines, image_ID);
  604.  
  605.   return TRUE;
  606. }
  607.  
  608. void
  609. safe_free (address *item)
  610. {
  611.   if (item == NULL || *item == NULL)
  612.     {
  613.       g_warning ("safe_free: Attempt to free a null item.");
  614.     }
  615.   
  616.   g_free (*item);
  617.   
  618.   *item = NULL;
  619. }
  620.