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 / sparkle.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  28.0 KB  |  986 lines

  1.  
  2. /* Sparkle --- image filter plug-in for The Gimp image manipulation program
  3.  * Copyright (C) 1996 by John Beale;  ported to Gimp by Michael J. Hammel;
  4.  * It has been optimized a little, bugfixed and modified by Martin Weber
  5.  * for additional functionality.
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  *
  21.  * You can contact Michael at mjhammel@csn.net
  22.  * You can contact Martin at martweb@gmx.net
  23.  * Note: set tabstops to 3 to make this more readable.
  24.  */
  25.  
  26. /*
  27.  * Sparkle 1.26 - simulate pixel bloom and diffraction effects
  28.  */
  29.  
  30. #include "config.h"
  31.  
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35.  
  36. #include <gtk/gtk.h>
  37.  
  38. #include <libgimp/gimp.h>
  39. #include <libgimp/gimpui.h>
  40.  
  41. #include "libgimp/stdplugins-intl.h"
  42.  
  43.  
  44. #define SCALE_WIDTH  175
  45. #define MAX_CHANNELS   4
  46. #define PSV            2  /* point spread value */
  47. #define EPSILON        0.001
  48.  
  49. #define  NATURAL    0
  50. #define  FOREGROUND 1
  51. #define  BACKGROUND 2
  52.  
  53. typedef struct
  54. {
  55.   gdouble lum_threshold;
  56.   gdouble flare_inten;
  57.   gdouble spike_len;
  58.   gdouble spike_pts;
  59.   gdouble spike_angle;
  60.   gdouble density;
  61.   gdouble opacity;
  62.   gdouble random_hue;
  63.   gdouble random_saturation;
  64.   gint    preserve_luminosity;
  65.   gint    invers;
  66.   gint    border;
  67.   gint    colortype;
  68. } SparkleVals;
  69.  
  70. typedef struct
  71. {
  72.   gint       run;
  73. } SparkleInterface;
  74.  
  75. /* Declare local functions.
  76.  */
  77.  
  78. static void      query  (void);
  79. static void      run    (gchar   *name,
  80.              gint     nparams,
  81.              GimpParam  *param,
  82.              gint    *nreturn_vals,
  83.              GimpParam **return_vals);
  84.  
  85. static gint      sparkle_dialog        (void);
  86. static void      sparkle_ok_callback   (GtkWidget *widget,
  87.                     gpointer   data);
  88.  
  89. static gint      compute_luminosity    (guchar    *pixel,
  90.                     gint       gray,
  91.                     gint       has_alpha);
  92. static gint      compute_lum_threshold (GimpDrawable *drawable,
  93.                     gdouble    percentile);
  94. static void      sparkle               (GimpDrawable *drawable,
  95.                     gint       threshold);
  96. static void      fspike                (GimpPixelRgn *src_rgn,
  97.                     GimpPixelRgn *dest_rgn,
  98.                     gint       gray,
  99.                     gint       x1,
  100.                     gint       y1,
  101.                     gint       x2,
  102.                     gint       y2,
  103.                     gint       xr,
  104.                     gint       yr,
  105.                     gint       tile_width,
  106.                     gint       tile_height,
  107.                     gdouble    inten,
  108.                     gdouble    length,
  109.                     gdouble    angle);
  110. static GimpTile*    rpnt                  (GimpDrawable *drawable,
  111.                     GimpTile     *tile,
  112.                     gint       x1,
  113.                     gint       y1,
  114.                     gint       x2,
  115.                     gint       y2,
  116.                     gdouble    xr,
  117.                     gdouble    yr,
  118.                     gint       tile_width,
  119.                     gint       tile_height,
  120.                     gint      *row,
  121.                     gint      *col,
  122.                     gint       bytes,
  123.                     gdouble    inten,
  124.                     guchar     color[MAX_CHANNELS]);
  125.  
  126. GimpPlugInInfo PLUG_IN_INFO =
  127. {
  128.   NULL,  /* init_proc  */
  129.   NULL,  /* quit_proc  */
  130.   query, /* query_proc */
  131.   run,   /* run_proc   */
  132. };
  133.  
  134. static SparkleVals svals =
  135. {
  136.   0.001,  /* luminosity threshold */
  137.   0.5,      /* flare intensity */
  138.   20.0,      /* spike length */
  139.   4.0,    /* spike points */
  140.   15.0,      /* spike angle */
  141.   1.0,    /* spike density */
  142.   0.0,    /* opacity */
  143.   0.0,    /* random hue */
  144.   0.0,    /* random saturation */
  145.   FALSE,  /* preserve_luminosity */
  146.   FALSE,  /* invers */
  147.   FALSE,  /* border */
  148.   NATURAL /* colortype */
  149. };
  150.  
  151. static SparkleInterface sint =
  152. {
  153.   FALSE          /* run */
  154. };
  155.  
  156. static gint num_sparkles;
  157.  
  158.  
  159. MAIN ()
  160.  
  161. static void
  162. query (void)
  163. {
  164.   static GimpParamDef args[] =
  165.   {
  166.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  167.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  168.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  169.     { GIMP_PDB_FLOAT, "lum_threshold", "Luminosity threshold (0.0 - 1.0)" },
  170.     { GIMP_PDB_FLOAT, "flare_inten", "Flare intensity (0.0 - 1.0)" },
  171.     { GIMP_PDB_INT32, "spike_len", "Spike length (in pixels)" },
  172.     { GIMP_PDB_INT32, "spike_pts", "# of spike points" },
  173.     { GIMP_PDB_INT32, "spike_angle", "Spike angle (0-360 degrees, -1: random)" },
  174.     { GIMP_PDB_FLOAT, "density", "Spike density (0.0 - 1.0)" },
  175.     { GIMP_PDB_FLOAT, "opacity", "Opacity (0.0 - 1.0)" },
  176.     { GIMP_PDB_FLOAT, "random_hue", "Random hue (0.0 - 1.0)" },
  177.     { GIMP_PDB_FLOAT, "random_saturation", "Random saturation (0.0 - 1.0)" },
  178.     { GIMP_PDB_INT32, "preserve_luminosity", "Preserve luminosity (TRUE/FALSE)" },
  179.     { GIMP_PDB_INT32, "invers", "Invers (TRUE/FALSE)" },
  180.     { GIMP_PDB_INT32, "border", "Add border (TRUE/FALSE)" },
  181.     { GIMP_PDB_INT32, "colortype", "Color of sparkles: { NATURAL (0), FOREGROUND (1), BACKGROUND (2) }" }
  182.   };
  183.   static gint nargs = sizeof (args) / sizeof (args[0]);
  184.  
  185.   gimp_install_procedure ("plug_in_sparkle",
  186.               "Simulates pixel bloom and diffraction effects",
  187.               "No help yet",
  188.               "John Beale, & (ported to GIMP v0.54) Michael J. Hammel & ted to GIMP v1.0) Spencer Kimball",
  189.               "John Beale",
  190.               "Version 1.26, December 1998",
  191.               N_("<Image>/Filters/Light Effects/Sparkle..."),
  192.               "RGB*, GRAY*",
  193.               GIMP_PLUGIN,
  194.               nargs, 0,
  195.               args, NULL);
  196. }
  197.  
  198. static void
  199. run (gchar   *name,
  200.      gint     nparams,
  201.      GimpParam  *param,
  202.      gint    *nreturn_vals,
  203.      GimpParam **return_vals)
  204. {
  205.   static GimpParam values[1];
  206.   GimpDrawable *drawable;
  207.   GimpRunModeType run_mode;
  208.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  209.   gint threshold, x1, y1, x2, y2;
  210.  
  211.   run_mode = param[0].data.d_int32;
  212.  
  213.   *nreturn_vals = 1;
  214.   *return_vals = values;
  215.  
  216.   values[0].type = GIMP_PDB_STATUS;
  217.   values[0].data.d_status = status;
  218.  
  219.   switch (run_mode)
  220.     {
  221.     case GIMP_RUN_INTERACTIVE:
  222.       INIT_I18N_UI();
  223.       /*  Possibly retrieve data  */
  224.       gimp_get_data ("plug_in_sparkle", &svals);
  225.  
  226.       /*  First acquire information with a dialog  */
  227.       if (! sparkle_dialog ())
  228.     return;
  229.       break;
  230.  
  231.     case GIMP_RUN_NONINTERACTIVE:
  232.       INIT_I18N();
  233.       /*  Make sure all the arguments are there!  */
  234.       if (nparams != 16)
  235.     {
  236.       status = GIMP_PDB_CALLING_ERROR;
  237.     }
  238.       else
  239.     {
  240.       svals.lum_threshold = param[3].data.d_float;
  241.       svals.flare_inten = param[4].data.d_float;
  242.       svals.spike_len = param[5].data.d_int32;
  243.       svals.spike_pts = param[6].data.d_int32;
  244.       svals.spike_angle = param[7].data.d_int32;
  245.       svals.density = param[8].data.d_float;
  246.       svals.opacity = param[9].data.d_float;
  247.       svals.random_hue = param[10].data.d_float;
  248.       svals.random_saturation = param[11].data.d_float;
  249.       svals.preserve_luminosity = (param[12].data.d_int32) ? TRUE : FALSE;
  250.       svals.invers = (param[13].data.d_int32) ? TRUE : FALSE;
  251.       svals.border = (param[14].data.d_int32) ? TRUE : FALSE;
  252.       svals.colortype = param[15].data.d_int32;
  253.  
  254.       if (svals.lum_threshold < 0.0 || svals.lum_threshold > 1.0)
  255.         status = GIMP_PDB_CALLING_ERROR;
  256.       else if (svals.flare_inten < 0.0 || svals.flare_inten > 1.0)
  257.         status = GIMP_PDB_CALLING_ERROR;
  258.       else if (svals.spike_len < 0)
  259.         status = GIMP_PDB_CALLING_ERROR;
  260.       else if (svals.spike_pts < 0)
  261.         status = GIMP_PDB_CALLING_ERROR;
  262.       else if (svals.spike_angle < -1 || svals.spike_angle > 360)
  263.         status = GIMP_PDB_CALLING_ERROR;
  264.       else if (svals.density < 0.0 || svals.density > 1.0)
  265.         status = GIMP_PDB_CALLING_ERROR;
  266.       else if (svals.opacity < 0.0 || svals.opacity > 1.0)
  267.         status = GIMP_PDB_CALLING_ERROR;
  268.       else if (svals.random_hue < 0.0 || svals.random_hue > 1.0)
  269.         status = GIMP_PDB_CALLING_ERROR;
  270.       else if (svals.random_saturation < 0.0 ||
  271.            svals.random_saturation > 1.0)
  272.         status = GIMP_PDB_CALLING_ERROR;
  273.       else if (svals.colortype < NATURAL || svals.colortype > BACKGROUND)
  274.         status = GIMP_PDB_CALLING_ERROR;
  275.     }
  276.       break;
  277.  
  278.     case GIMP_RUN_WITH_LAST_VALS:
  279.       INIT_I18N();
  280.       /*  Possibly retrieve data  */
  281.       gimp_get_data ("plug_in_sparkle", &svals);
  282.       break;
  283.  
  284.     default:
  285.       break;
  286.     }
  287.  
  288.   /*  Get the specified drawable  */
  289.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  290.  
  291.   /*  Make sure that the drawable is gray or RGB color  */
  292.   if (gimp_drawable_is_rgb (drawable->id) ||
  293.       gimp_drawable_is_gray (drawable->id))
  294.     {
  295.       gimp_progress_init (_("Sparkling..."));
  296.       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  297.  
  298.       if (svals.border == FALSE)
  299.           /*  compute the luminosity which exceeds the luminosity threshold  */
  300.           threshold = compute_lum_threshold (drawable, svals.lum_threshold);
  301.       else
  302.         {
  303.           gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  304.           num_sparkles = 2 * (x2 - x1 + y2 - y1);
  305.           threshold = 255;
  306.          }
  307.           
  308.       sparkle (drawable, threshold);
  309.  
  310.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  311.     gimp_displays_flush ();
  312.  
  313.       /*  Store mvals data  */
  314.       if (run_mode == GIMP_RUN_INTERACTIVE)
  315.     gimp_set_data ("plug_in_sparkle", &svals, sizeof (SparkleVals));
  316.     }
  317.   else
  318.     {
  319.       /* gimp_message ("sparkle: cannot operate on indexed color images"); */
  320.       status = GIMP_PDB_EXECUTION_ERROR;
  321.     }
  322.  
  323.   values[0].data.d_status = status;
  324.  
  325.   gimp_drawable_detach (drawable);
  326. }
  327.  
  328. static gint
  329. sparkle_dialog (void)
  330. {
  331.   GtkWidget *dlg;
  332.   GtkWidget *main_vbox;
  333.   GtkWidget *vbox;
  334.   GtkWidget *hbox;
  335.   GtkWidget *frame;
  336.   GtkWidget *table;
  337.   GtkWidget *toggle;
  338.   GtkWidget *sep;
  339.   GtkWidget *r1, *r2, *r3;
  340.   GtkObject *scale_data;
  341.  
  342.   gimp_ui_init ("sparkle", FALSE);
  343.  
  344.   dlg = gimp_dialog_new (_("Sparkle"), "sparkle",
  345.              gimp_standard_help_func, "filters/sparkle.html",
  346.              GTK_WIN_POS_MOUSE,
  347.              FALSE, TRUE, FALSE,
  348.  
  349.              _("OK"), sparkle_ok_callback,
  350.              NULL, NULL, NULL, TRUE, FALSE,
  351.              _("Cancel"), gtk_widget_destroy,
  352.              NULL, 1, NULL, FALSE, TRUE,
  353.  
  354.              NULL);
  355.  
  356.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  357.               GTK_SIGNAL_FUNC (gtk_main_quit),
  358.               NULL);
  359.  
  360.   gimp_help_init ();
  361.  
  362.   /*  parameter settings  */
  363.   frame = gtk_frame_new (_("Parameter Settings"));
  364.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  365.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  366.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  367.   gtk_widget_show (frame);
  368.  
  369.   main_vbox = gtk_vbox_new (FALSE, 4);
  370.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4);
  371.   gtk_container_add (GTK_CONTAINER (frame), main_vbox);
  372.   gtk_widget_show (main_vbox);
  373.  
  374.   table = gtk_table_new (9, 3, FALSE);
  375.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  376.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  377.   gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
  378.   gtk_widget_show (table);
  379.  
  380.   scale_data =
  381.     gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  382.               _("Luminosity Threshold:"), SCALE_WIDTH, 0,
  383.               svals.lum_threshold, 0.0, 0.1, 0.001, 0.01, 3,
  384.               TRUE, 0, 0,
  385.               _("Adjust the Luminosity Threshold"), NULL);
  386.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  387.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  388.               &svals.lum_threshold);
  389.  
  390.   scale_data =
  391.     gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  392.               _("Flare Intensity:"), SCALE_WIDTH, 0,
  393.               svals.flare_inten, 0.0, 1.0, 0.01, 0.1, 2,
  394.               TRUE, 0, 0,
  395.               _("Adjust the Flare Intensity"), NULL);
  396.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  397.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  398.               &svals.flare_inten);
  399.  
  400.   scale_data =
  401.     gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  402.               _("Spike Length:"), SCALE_WIDTH, 0,
  403.               svals.spike_len, 1, 100, 1, 10, 0,
  404.               TRUE, 0, 0,
  405.               _("Adjust the Spike Length"), NULL);
  406.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  407.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  408.               &svals.spike_len);
  409.  
  410.   scale_data =
  411.     gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  412.               _("Spike Points:"), SCALE_WIDTH, 0,
  413.               svals.spike_pts, 0, 16, 1, 4, 0,
  414.               TRUE, 0, 0,
  415.               _("Adjust the Number of Spikes"), NULL);
  416.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  417.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  418.               &svals.spike_pts);
  419.  
  420.   scale_data =
  421.     gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
  422.               _("Spike Angle (-1: Random):"), SCALE_WIDTH, 0,
  423.               svals.spike_angle, -1, 360, 1, 15, 0,
  424.               TRUE, 0, 0,
  425.               _("Adjust the Spike Angle "
  426.                 "(-1 means a Random Angle is choosen)"), NULL);
  427.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  428.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  429.               &svals.spike_angle);
  430.  
  431.   scale_data =
  432.     gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
  433.               _("Spike Density:"), SCALE_WIDTH, 0,
  434.               svals.density, 0.0, 1.0, 0.01, 0.1, 2,
  435.               TRUE, 0, 0,
  436.               _("Adjust the Spike Density"), NULL);
  437.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  438.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  439.               &svals.density);
  440.  
  441.   scale_data =
  442.     gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
  443.               _("Opacity:"), SCALE_WIDTH, 0,
  444.               svals.opacity, 0.0, 1.0, 0.01, 0.1, 2,
  445.               TRUE, 0, 0,
  446.               _("Adjust the Opacity of the Spikes"), NULL);
  447.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  448.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  449.               &svals.opacity);
  450.  
  451.   scale_data =
  452.     gimp_scale_entry_new (GTK_TABLE (table), 0, 7,
  453.               _("Random Hue:"), SCALE_WIDTH, 0,
  454.               svals.random_hue, 0.0, 1.0, 0.01, 0.1, 2,
  455.               TRUE, 0, 0,
  456.               _("Adjust the Value how much the Hue should "
  457.                 "be changed randomly"), NULL);
  458.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  459.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  460.               &svals.random_hue);
  461.  
  462.   scale_data =
  463.     gimp_scale_entry_new (GTK_TABLE (table), 0, 8,
  464.               _("Random Saturation:"), SCALE_WIDTH, 0,
  465.               svals.random_saturation, 0.0, 1.0, 0.01, 0.1, 2,
  466.               TRUE, 0, 0,
  467.               _("Adjust the Value how much the Saturation should "
  468.                 "be changed randomly"), NULL);
  469.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  470.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  471.               &svals.random_saturation);
  472.  
  473.   sep = gtk_hseparator_new ();
  474.   gtk_box_pack_start (GTK_BOX (main_vbox), sep, FALSE, FALSE, 0);
  475.   gtk_widget_show (sep);
  476.  
  477.   hbox = gtk_hbox_new (FALSE, 4);
  478.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  479.   gtk_widget_show (hbox);
  480.  
  481.   vbox = gtk_vbox_new (FALSE, 1);
  482.   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
  483.   gtk_widget_show (vbox);
  484.  
  485.   toggle = gtk_check_button_new_with_label (_("Preserve Luminosity"));
  486.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  487.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
  488.                 svals.preserve_luminosity);
  489.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  490.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  491.               &svals.preserve_luminosity);
  492.   gtk_widget_show (toggle);
  493.   gimp_help_set_help_data (toggle, _("Should the Luminosity be preserved?"),
  494.                NULL);
  495.  
  496.   toggle = gtk_check_button_new_with_label (_("Inverse"));
  497.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  498.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), svals.invers);
  499.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  500.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  501.               &svals.invers);
  502.   gtk_widget_show (toggle);
  503.   gimp_help_set_help_data (toggle, _("Should an Inverse Effect be done?"), NULL);
  504.  
  505.   toggle = gtk_check_button_new_with_label (_("Add Border"));
  506.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  507.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), svals.border);
  508.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  509.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  510.               &svals.border);
  511.   gtk_widget_show (toggle);
  512.   gimp_help_set_help_data (toggle,
  513.                _("Draw a Border of Spikes around the Image"),
  514.                NULL);
  515.  
  516.   sep = gtk_vseparator_new ();
  517.   gtk_box_pack_start (GTK_BOX (hbox), sep, FALSE, FALSE, 0);
  518.   gtk_widget_show (sep);
  519.  
  520.   /*  colortype  */
  521.   vbox =
  522.     gimp_radio_group_new2 (FALSE, NULL,
  523.                gimp_radio_button_update,
  524.                &svals.colortype, (gpointer) svals.colortype,
  525.  
  526.                _("Natural Color"),    (gpointer) NATURAL, &r1,
  527.                _("Foreground Color"), (gpointer) FOREGROUND, &r2,
  528.                _("Background Color"), (gpointer) BACKGROUND, &r3,
  529.  
  530.                NULL);
  531.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
  532.   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
  533.   gtk_widget_show (vbox);
  534.  
  535.   gimp_help_set_help_data (r1, _("Use the Color of the Image"), NULL);
  536.   gimp_help_set_help_data (r2, _("Use the Foreground Color"), NULL);
  537.   gimp_help_set_help_data (r3, _("Use the Background Color"), NULL);
  538.  
  539.   gtk_widget_show (dlg);
  540.  
  541.   gtk_main ();
  542.   gdk_flush ();
  543.  
  544.   return sint.run;
  545. }
  546.  
  547. static gint
  548. compute_luminosity (guchar *pixel,
  549.             gint    gray,
  550.             gint    has_alpha)
  551. {
  552.   gint pixel0, pixel1, pixel2;
  553.  
  554.   if (svals.invers == FALSE)
  555.     {
  556.       pixel0 = pixel[0];
  557.       pixel1 = pixel[1];
  558.       pixel2 = pixel[2];
  559.     }
  560.   else
  561.     {
  562.       pixel0 = 255 - pixel[0];
  563.       pixel1 = 255 - pixel[1];
  564.       pixel2 = 255 - pixel[2];
  565.     }
  566.   if (gray)
  567.     {
  568.       if (has_alpha)
  569.     return (pixel0 * pixel1) / 255;
  570.       else
  571.     return (pixel0);
  572.     }
  573.   else
  574.     {
  575.       gint min, max;
  576.  
  577.       min = MIN (pixel0, pixel1);
  578.       min = MIN (min, pixel2);
  579.       max = MAX (pixel0, pixel1);
  580.       max = MAX (max, pixel2);
  581.  
  582.       if (has_alpha)
  583.     return ((min + max) * pixel[3]) / 510;
  584.       else
  585.     return (min + max) / 2;
  586.     }
  587. }
  588.  
  589. static gint
  590. compute_lum_threshold (GimpDrawable *drawable,
  591.                gdouble    percentile)
  592. {
  593.   GimpPixelRgn src_rgn;
  594.   gpointer  pr;
  595.   guchar   *data;
  596.   gint      values[256];
  597.   gint      total, sum;
  598.   gint      size;
  599.   gint      gray;
  600.   gint      has_alpha;
  601.   gint      i;
  602.   gint      x1, y1, x2, y2;
  603.  
  604.   /*  zero out the luminosity values array  */
  605.  
  606.   memset (values, 0, sizeof (gint) * 256);
  607.  
  608.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  609.   gray = gimp_drawable_is_gray (drawable->id);
  610.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  611.  
  612.   gimp_pixel_rgn_init (&src_rgn, drawable,
  613.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  614.  
  615.   for (pr = gimp_pixel_rgns_register (1, &src_rgn);
  616.        pr != NULL;
  617.        pr = gimp_pixel_rgns_process (pr))
  618.     {
  619.       data = src_rgn.data;
  620.       size = src_rgn.w * src_rgn.h;
  621.  
  622.       while (size--)
  623.     {
  624.       values [compute_luminosity (data, gray, has_alpha)]++;
  625.       data += src_rgn.bpp;
  626.     }
  627.     }
  628.  
  629.   total = (x2 - x1) * (y2 - y1);
  630.   sum = 0;
  631.  
  632.   for (i = 255; i >= 0; i--)
  633.     {
  634.       sum += values[i];
  635.       if ((gdouble) sum > percentile * (gdouble) total)
  636.     {
  637.       num_sparkles = sum;
  638.       return i;
  639.     }
  640.     }
  641.  
  642.   return 0;
  643. }
  644.  
  645. static void
  646. sparkle (GimpDrawable *drawable,
  647.      gint       threshold)
  648. {
  649.   GimpPixelRgn src_rgn, dest_rgn;
  650.   guchar   *src, *dest;
  651.   gdouble   nfrac, length, inten, spike_angle;
  652.   gint      cur_progress, max_progress;
  653.   gint      x1, y1, x2, y2;
  654.   gint      size, lum, x, y, b;
  655.   gint      gray;
  656.   gint      has_alpha, alpha;
  657.   gpointer  pr;
  658.   guchar   *tmp1;
  659.   gint      tile_width, tile_height;
  660.  
  661.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  662.   gray = gimp_drawable_is_gray (drawable->id);
  663.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  664.   alpha = (has_alpha) ? drawable->bpp - 1 : drawable->bpp;
  665.   tile_width  = gimp_tile_width();
  666.   tile_height = gimp_tile_height();
  667.  
  668.   /* initialize the progress dialog */
  669.   cur_progress = 0;
  670.   max_progress = num_sparkles;
  671.  
  672.   gimp_pixel_rgn_init (&src_rgn, drawable,
  673.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  674.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  675.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  676.  
  677.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  678.        pr != NULL;
  679.        pr = gimp_pixel_rgns_process (pr))
  680.     {
  681.       src = src_rgn.data;
  682.       dest = dest_rgn.data;
  683.  
  684.       size = src_rgn.w * src_rgn.h;
  685.  
  686.       while (size --)
  687.         {
  688.           if(has_alpha && src[alpha] == 0)
  689.              {
  690.                memset(dest, 0, alpha * sizeof(guchar));
  691.                dest += alpha;
  692.              }
  693.           else
  694.             {
  695.               for (b = 0, tmp1 = src; b < alpha; b++)
  696.                 {
  697.                   *dest++ = *tmp1++;
  698.                 }
  699.             }
  700.           if (has_alpha)
  701.             *dest++ = src[alpha];
  702.  
  703.           src += src_rgn.bpp;
  704.         }
  705.     }
  706.  
  707.   /* add effects to new image based on intensity of old pixels */
  708.   gimp_pixel_rgn_init (&src_rgn, drawable,
  709.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  710.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  711.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  712.  
  713.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  714.        pr != NULL;
  715.        pr = gimp_pixel_rgns_process (pr))
  716.     {
  717.       src = src_rgn.data;
  718.  
  719.       for (y = 0; y < src_rgn.h; y++)
  720.     for (x = 0; x < src_rgn.w; x++)
  721.       {
  722.         if (svals.border)
  723.           {
  724.             if (x + src_rgn.x == 0 || y + src_rgn.y == 0
  725.            || x + src_rgn.x == drawable->width - 1
  726.            || y + src_rgn.y == drawable->height - 1)
  727.            lum = 255;
  728.         else
  729.            lum = 0;
  730.           }
  731.         else
  732.             lum = compute_luminosity (src, gray, has_alpha);
  733.         if (lum >= threshold)
  734.           {
  735.         nfrac = fabs ((gdouble) (lum + 1 - threshold) /
  736.                   (gdouble) (256 - threshold));
  737.         length = (gdouble) svals.spike_len * (gdouble) pow (nfrac, 0.8);
  738.         inten = svals.flare_inten * nfrac /* pow (nfrac, 1.0) */;
  739.  
  740.         /* fspike im x,y intens rlength angle */
  741.         if (svals.spike_pts > 0)
  742.           {
  743.             /* major spikes */
  744.             if (svals.spike_angle == -1)
  745.                spike_angle = 360.0 * rand () / G_MAXRAND;
  746.             else
  747.             spike_angle = svals.spike_angle;
  748.             if (rand() <= G_MAXRAND * svals.density)
  749.               {
  750.             fspike (&src_rgn, &dest_rgn, gray, x1, y1, x2, y2,
  751.                 x + src_rgn.x, y + src_rgn.y,
  752.                 tile_width, tile_height,
  753.                 inten, length, spike_angle);
  754.                 /* minor spikes */
  755.             fspike (&src_rgn, &dest_rgn, gray, x1, y1, x2, y2,
  756.                 x + src_rgn.x, y + src_rgn.y,
  757.                 tile_width, tile_height,
  758.                 inten * 0.7, length * 0.7,
  759.                 ((gdouble) spike_angle + 180.0 / svals.spike_pts));
  760.               }
  761.           }
  762.         cur_progress ++;
  763.         if ((cur_progress % 5) == 0)
  764.           gimp_progress_update ((double) cur_progress /
  765.                     (double) max_progress);
  766.           }
  767.         src += src_rgn.bpp;
  768.       }
  769.     }
  770.  
  771.   gimp_progress_update (1.0);
  772.  
  773.   /*  update the blurred region  */
  774.   gimp_drawable_flush (drawable);
  775.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  776.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  777. }
  778.  
  779. static inline GimpTile *
  780. rpnt (GimpDrawable *drawable,
  781.       GimpTile     *tile,
  782.       gint       x1,
  783.       gint       y1,
  784.       gint       x2,
  785.       gint       y2,
  786.       gdouble    xr,
  787.       gdouble    yr,
  788.       gint       tile_width,
  789.       gint       tile_height,
  790.       gint      *row,
  791.       gint      *col,
  792.       gint       bytes,
  793.       gdouble    inten,
  794.       guchar     color[MAX_CHANNELS])
  795. {
  796.   gint    x, y, b;
  797.   gdouble dx, dy, rs, val;
  798.   guchar *pixel;
  799.   gdouble new;
  800.   gint    newcol, newrow;
  801.   gint    newcoloff, newrowoff;
  802.  
  803.   x = (int) (xr);    /* integer coord. to upper left of real point */
  804.   y = (int) (yr);
  805.  
  806.   if (x >= x1 && y >= y1 && x < x2 && y < y2)
  807.     {
  808.       newcol    = x / tile_width;
  809.       newcoloff = x % tile_width;
  810.       newrow    = y / tile_height;
  811.       newrowoff = y % tile_height;
  812.       
  813.       if ((newcol != *col) || (newrow != *row))
  814.     {
  815.       *col = newcol;
  816.       *row = newrow;
  817.       if (tile)
  818.         gimp_tile_unref (tile, TRUE);
  819.       tile = gimp_drawable_get_tile (drawable, TRUE, *row, *col);
  820.       gimp_tile_ref (tile);
  821.     }
  822.  
  823.       pixel = tile->data + tile->bpp * (tile->ewidth * newrowoff + newcoloff);
  824.       dx = xr - x; dy = yr - y;
  825.       rs = dx * dx + dy * dy;
  826.       val = inten * exp (-rs / PSV);
  827.  
  828.       for (b = 0; b < bytes; b++)
  829.     {
  830.       if (svals.invers == FALSE)
  831.           new = pixel[b];
  832.       else
  833.           new = 255 - pixel[b];
  834.  
  835.       if (svals.preserve_luminosity==TRUE)
  836.         {
  837.           if (new < color[b])
  838.           new *= (1.0 - val * (1.0 - svals.opacity));
  839.           else
  840.         {
  841.           new -= val * color[b] * (1.0 - svals.opacity);
  842.           if (new < 0.0)
  843.               new = 0.0;
  844.  
  845.         }
  846.         }
  847.       new *= 1.0 - val * svals.opacity;
  848.       new += val * color[b];
  849.  
  850.       if (new > 255) new = 255;
  851.  
  852.       if (svals.invers != FALSE)
  853.           pixel[b] = 255 - new;
  854.       else
  855.           pixel[b] = new;
  856.     }
  857.     }
  858.  
  859.   return tile;
  860. }
  861.  
  862. static void
  863. fspike (GimpPixelRgn *src_rgn,
  864.     GimpPixelRgn *dest_rgn,
  865.     gint       gray,
  866.     gint       x1,
  867.     gint       y1,
  868.     gint       x2,
  869.     gint       y2,
  870.     gint       xr,
  871.     gint       yr,
  872.     gint       tile_width,
  873.     gint       tile_height,
  874.     gdouble    inten,
  875.     gdouble    length,
  876.     gdouble    angle)
  877. {
  878.   const gdouble efac = 2.0;
  879.   gdouble xrt, yrt, dx, dy;
  880.   gdouble rpos;
  881.   gdouble in;
  882.   gdouble theta;
  883.   gdouble sfac;
  884.   gint    r, g, b;
  885.   GimpTile  *tile = NULL;
  886.   gint    row, col;
  887.   gint    i;
  888.   gint    bytes;
  889.   gint    ok;
  890.   guchar  pixel[MAX_CHANNELS];
  891.   guchar  color[MAX_CHANNELS];
  892.  
  893.   theta = angle;
  894.   bytes = dest_rgn->bpp;
  895.   row = -1;
  896.   col = -1;
  897.  
  898.   /* draw the major spikes */
  899.   for (i = 0; i < svals.spike_pts; i++)
  900.     {
  901.       gimp_pixel_rgn_get_pixel (dest_rgn, pixel, xr, yr);
  902.  
  903.       switch (svals.colortype)
  904.       {
  905.     case FOREGROUND:
  906.     gimp_palette_get_foreground (&color[0], &color[1], &color[2]);
  907.     break;
  908.     
  909.     case BACKGROUND:
  910.     gimp_palette_get_background (&color[0], &color[1], &color[2]);
  911.     break;
  912.     
  913.     default:
  914.     color[0] = pixel[0];
  915.     color[1] = pixel[1];
  916.     color[2] = pixel[2];
  917.     break;    
  918.       }
  919.       
  920.       if (svals.invers)
  921.     {
  922.       color[0] = 255 - color[0];
  923.       color[1] = 255 - color[1];
  924.       color[2] = 255 - color[2];
  925.         }
  926.       if (svals.random_hue > 0.0 || svals.random_saturation > 0.0)
  927.         {
  928.       r = 255 - color[0];
  929.       g = 255 - color[1];
  930.       b = 255 - color[2];             
  931.       gimp_rgb_to_hsv (&r, &g, &b);  
  932.       r+= (svals.random_hue * ((gdouble) rand() / (gdouble) RAND_MAX - 0.5))*255;
  933.       if (r >= 255)
  934.         r -= 255;
  935.       else if (r < 0) 
  936.         r += 255;
  937.       b+= (svals.random_saturation * (2.0 * (gdouble) rand() /
  938.                       (gdouble) RAND_MAX - 1.0))*255;
  939.       if (b > 255) b = 255;
  940.       gimp_hsv_to_rgb (&r, &g, &b);
  941.       color[0] = 255 - r;
  942.       color[1] = 255 - g;
  943.       color[2] = 255 - b;
  944.     }
  945.  
  946.       dx = 0.2 * cos (theta * G_PI / 180.0);
  947.       dy = 0.2 * sin (theta * G_PI / 180.0);
  948.       xrt = (gdouble) xr; /* (gdouble) is needed because some */
  949.       yrt = (gdouble) yr; /* compilers optimize too much otherwise */
  950.       rpos = 0.2;
  951.  
  952.       do
  953.     {
  954.       sfac = inten * exp (-pow (rpos / length, efac));
  955.       ok = FALSE;
  956.  
  957.       in = 0.2 * sfac;
  958.       if (in > 0.01)
  959.             ok = TRUE;
  960.  
  961.       tile = rpnt (dest_rgn->drawable, tile, x1, y1, x2, y2, xrt, yrt, tile_width, tile_height, &row, &col, bytes, in, color);
  962.       tile = rpnt (dest_rgn->drawable, tile, x1, y1, x2, y2, xrt + 1.0, yrt, tile_width, tile_height, &row, &col, bytes, in, color);
  963.       tile = rpnt (dest_rgn->drawable, tile, x1, y1, x2, y2, xrt + 1.0, yrt + 1.0, tile_width, tile_height, &row, &col, bytes, in, color);
  964.       tile = rpnt (dest_rgn->drawable, tile, x1, y1, x2, y2, xrt, yrt + 1.0, tile_width, tile_height, &row, &col, bytes, in, color);
  965.  
  966.       xrt += dx;
  967.       yrt += dy;
  968.       rpos += 0.2;
  969.     } while (ok);
  970.  
  971.       theta += 360.0 / svals.spike_pts;
  972.     }
  973.  
  974.   if (tile)
  975.     gimp_tile_unref (tile, TRUE);
  976. }
  977.  
  978. static void
  979. sparkle_ok_callback (GtkWidget *widget,
  980.              gpointer   data)
  981. {
  982.   sint.run = TRUE;
  983.  
  984.   gtk_widget_destroy (GTK_WIDGET (data));
  985. }
  986.