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 / nova.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-01  |  32.5 KB  |  1,131 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * SuperNova plug-in
  5.  * Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>,
  6.  *                    Spencer Kimball, Federico Mena Quintero
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. /*
  24.  * version 1.200
  25.  * This plug-in requires GIMP v0.99.10 or above.
  26.  *
  27.  * This plug-in produces an effect like a supernova burst.
  28.  *
  29.  *      Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  30.  *      http://ha1.seikyou.ne.jp/home/taka/gimp/
  31.  *
  32.  *      Preview render mode by timecop@japan.co.jp
  33.  *      http://www.ne.jp/asahi/linux/timecop
  34.  *
  35.  * Changes from version 1.122 to version 1.200 by tim copperfield:
  36.  * - preview mode now previews the nova with scale;
  37.  * - toggle for cursor show/hide during preview
  38.  * 
  39.  * Changes from version 1.1115 to version 1.122 by Martin Weber:
  40.  * - Little bug fixes
  41.  * - Added random hue
  42.  * - Freeing memory
  43.  *
  44.  * Changes from version 1.1114 to version 1.1115:
  45.  * - Added gtk_rc_parse
  46.  * - Fixed bug that drawing preview of small height image
  47.  *   (maybe image height < PREVIEW_SIZE) caused core dump.
  48.  * - Changed default value.
  49.  * - Moved to <Image>/Filters/Effects.  right?
  50.  * - etc...
  51.  *
  52.  * Changes from version 1.1112 to version 1.1114:
  53.  * - Modified proc args to GIMP_PDB_COLOR, also included nspoke.
  54.  * - nova_int_entryscale_new(): Fixed caption was guchar -> gchar, etc.
  55.  * - Now nova renders properly with alpha channel.
  56.  *   (Very thanks to Spencer Kimball and Federico Mena !)
  57.  *
  58.  * TODO:
  59.  * - clean up the code more
  60.  * - add notebook interface and so on
  61.  */
  62.  
  63. #include "config.h"
  64.  
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <string.h>
  68. #include <time.h>
  69.  
  70. #include <gtk/gtk.h>
  71.  
  72. #include <libgimp/gimp.h>
  73. #include <libgimp/gimpui.h>
  74.  
  75. #include "libgimp/stdplugins-intl.h"
  76.  
  77.  
  78. #ifdef RCSID
  79. static char rcsid[] = "$Id: nova.c,v 1.29 2000/09/30 20:13:06 nicklamb Exp $";
  80. #endif
  81.  
  82. /* Some useful macros */
  83.  
  84. #if 0
  85. #define DEBUG1 printf
  86. #else
  87. #define DEBUG1 dummy_printf
  88. static void dummy_printf(char *fmt, ...) {}
  89. #endif
  90.  
  91. #define ENTRY_WIDTH      50
  92. #define SCALE_WIDTH     125
  93. #define TILE_CACHE_SIZE  16
  94. #define PREVIEW_SIZE    128 
  95.  
  96.  
  97. #define PREVIEW   0x1
  98. #define CURSOR    0x2
  99. #define ALL       0xf
  100.  
  101. #define PREVIEW_MASK (GDK_EXPOSURE_MASK | \
  102.                       GDK_BUTTON_PRESS_MASK | \
  103.                       GDK_BUTTON1_MOTION_MASK)
  104.  
  105. static  guchar    *preview_bits;
  106. static  GtkWidget *preview;
  107. static  gdouble    preview_scale_x;
  108. static  gdouble    preview_scale_y;
  109. static  gboolean   show_cursor = FALSE;
  110.  
  111. typedef struct
  112. {
  113.   gint    xcenter, ycenter;
  114.   guchar  color[3];
  115.   gint    radius;
  116.   gint    nspoke;
  117.   gint    randomhue;
  118. } NovaValues;
  119.  
  120. typedef struct
  121. {
  122.   gint run;
  123. } NovaInterface;
  124.  
  125. typedef struct
  126. {
  127.   GimpDrawable *drawable;
  128.   gint       dwidth;
  129.   gint       dheight;
  130.   gint       bpp;
  131.   GtkObject *xadj;
  132.   GtkObject *yadj;
  133.   gint       cursor;
  134.   gint       curx, cury;              /* x,y of cursor in preview */
  135.   gint       oldx, oldy;
  136.   gint       in_call;
  137. } NovaCenter;
  138.  
  139.  
  140. /* Declare a local function.
  141.  */
  142. static void        query (void);
  143. static void        run   (gchar    *name,
  144.               gint      nparams,
  145.               GimpParam   *param,
  146.               gint     *nreturn_vals,
  147.               GimpParam  **return_vals);
  148.  
  149. static void        fill_preview_with_thumb (GtkWidget *preview_widget, 
  150.                         gint32     drawable_ID);
  151. static GtkWidget  *preview_widget          (GimpDrawable *drawable);
  152.  
  153. static void        nova                    (GimpDrawable *drawable, 
  154.                         gboolean   preview_mode);
  155.  
  156. static gint        nova_dialog             (GimpDrawable *drawable);
  157. static void        nova_ok_callback        (GtkWidget *widget,
  158.                             gpointer   data);
  159.  
  160. static GtkWidget * nova_center_create            (GimpDrawable     *drawable);
  161. static void        nova_center_destroy           (GtkWidget     *widget,
  162.                           gpointer       data);
  163. static void        nova_center_draw              (NovaCenter    *center,
  164.                           gint           update);
  165. static void        nova_center_adjustment_update (GtkAdjustment *widget,
  166.                           gpointer       data);
  167. static void        nova_center_cursor_update     (NovaCenter    *center);
  168. static gint        nova_center_preview_expose    (GtkWidget     *widget,
  169.                           GdkEvent      *event,
  170.                           gpointer       data);
  171. static gint        nova_center_preview_events    (GtkWidget     *widget,
  172.                           GdkEvent      *event,
  173.                           gpointer       data);
  174.  
  175. GimpPlugInInfo PLUG_IN_INFO =
  176. {
  177.   NULL,  /* init_proc  */
  178.   NULL,  /* quit_proc  */
  179.   query, /* query_proc */
  180.   run,   /* run_proc   */
  181. };
  182.  
  183. static NovaValues pvals =
  184. {
  185.   128, 128,             /* xcenter, ycenter */
  186.   { 90, 100, 255 },     /* color */
  187.   20,                   /* radius */
  188.   100,                  /* nspoke */
  189.   0                     /* random hue */
  190. };
  191.  
  192. static NovaInterface pint =
  193. {
  194.   FALSE     /* run */
  195. };
  196.  
  197.  
  198. MAIN ()
  199.  
  200. static void
  201. query (void)
  202. {
  203.   static GimpParamDef args[]=
  204.   {
  205.     { GIMP_PDB_INT32,    "run_mode",  "Interactive, non-interactive" },
  206.     { GIMP_PDB_IMAGE,    "image",     "Input image (unused)" },
  207.     { GIMP_PDB_DRAWABLE, "drawable",  "Input drawable" },
  208.     { GIMP_PDB_INT32,    "xcenter",   "X coordinates of the center of supernova" },
  209.     { GIMP_PDB_INT32,    "ycenter",   "Y coordinates of the center of supernova" },
  210.     { GIMP_PDB_COLOR,    "color",     "Color of supernova" },
  211.     { GIMP_PDB_INT32,    "radius",    "Radius of supernova" },
  212.     { GIMP_PDB_INT32,    "nspoke",    "Number of spokes" },
  213.     { GIMP_PDB_INT32,    "randomhue", "Random hue" }
  214.   };
  215.   static gint nargs = sizeof (args) / sizeof (args[0]);
  216.  
  217.   gimp_install_procedure ("plug_in_nova",
  218.                           "Produce Supernova effect to the specified drawable",
  219.                           "This plug-in produces an effect like a supernova "
  220.               "burst. The amount of the light effect is "
  221.               "approximately in proportion to 1/r, where r is the "
  222.               "distance from the center of the star. It works with "
  223.               "RGB*, GRAY* image.",
  224.                           "Eiichi Takamori",
  225.                           "Eiichi Takamori",
  226.                           "May 2000",
  227.                           N_("<Image>/Filters/Light Effects/SuperNova..."),
  228.                           "RGB*, GRAY*",
  229.                           GIMP_PLUGIN,
  230.                           nargs, 0,
  231.                           args, NULL);
  232. }
  233.  
  234. static void
  235. run (gchar    *name,
  236.      gint      nparams,
  237.      GimpParam   *param,
  238.      gint     *nreturn_vals,
  239.      GimpParam  **return_vals)
  240. {
  241.   static GimpParam values[1];
  242.   GimpDrawable *drawable;
  243.   GimpRunModeType run_mode;
  244.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  245.  
  246.   INIT_I18N_UI();
  247.  
  248.   run_mode = param[0].data.d_int32;
  249.  
  250.   *nreturn_vals = 1;
  251.   *return_vals = values;
  252.  
  253.   values[0].type = GIMP_PDB_STATUS;
  254.   values[0].data.d_status = status;
  255.  
  256.   /*  Get the specified drawable  */
  257.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  258.  
  259.   switch (run_mode)
  260.     {
  261.     case GIMP_RUN_INTERACTIVE:
  262.       /*  Possibly retrieve data  */
  263.       gimp_get_data ("plug_in_nova", &pvals);
  264.  
  265.       /*  First acquire information with a dialog  */
  266.       if (! nova_dialog (drawable))
  267.         {
  268.           gimp_drawable_detach (drawable);
  269.           return;
  270.         }
  271.       break;
  272.  
  273.     case GIMP_RUN_NONINTERACTIVE:
  274.       /*  Make sure all the arguments are there!  */
  275.       if (nparams != 9)
  276.         status = GIMP_PDB_CALLING_ERROR;
  277.       if (status == GIMP_PDB_SUCCESS)
  278.         {
  279.           pvals.xcenter   = param[3].data.d_int32;
  280.           pvals.ycenter   = param[4].data.d_int32;
  281.           pvals.color[0]  = param[5].data.d_color.red;
  282.           pvals.color[1]  = param[5].data.d_color.green;
  283.           pvals.color[2]  = param[5].data.d_color.blue;
  284.           pvals.radius    = param[6].data.d_int32;
  285.           pvals.nspoke    = param[7].data.d_int32;
  286.       pvals.randomhue = param[8].data.d_int32;
  287.         }
  288.       if ((status == GIMP_PDB_SUCCESS) &&
  289.       pvals.radius <= 0)
  290.         status = GIMP_PDB_CALLING_ERROR;
  291.       break;
  292.  
  293.     case GIMP_RUN_WITH_LAST_VALS:
  294.       /*  Possibly retrieve data  */
  295.       gimp_get_data ("plug_in_nova", &pvals);
  296.       break;
  297.  
  298.     default:
  299.       break;
  300.     }
  301.  
  302.   if (status == GIMP_PDB_SUCCESS)
  303.     {
  304.       /*  Make sure that the drawable is gray or RGB color  */
  305.       if (gimp_drawable_is_rgb (drawable->id) ||
  306.       gimp_drawable_is_gray (drawable->id))
  307.         {
  308.           gimp_progress_init (_("Rendering SuperNova..."));
  309.           gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  310.  
  311.           nova (drawable, 0);
  312.  
  313.           if (run_mode != GIMP_RUN_NONINTERACTIVE)
  314.             gimp_displays_flush ();
  315.  
  316.           /*  Store data  */
  317.           if (run_mode == GIMP_RUN_INTERACTIVE)
  318.             gimp_set_data ("plug_in_nova", &pvals, sizeof (NovaValues));
  319.         g_free(preview_bits); /* this is allocated during preview render */
  320.         }
  321.       else
  322.         {
  323.           /* gimp_message ("nova: cannot operate on indexed color images"); */
  324.           status = GIMP_PDB_EXECUTION_ERROR;
  325.         }
  326.     }
  327.  
  328.   values[0].data.d_status = status;
  329.  
  330.   gimp_drawable_detach (drawable);
  331. }
  332.  
  333. static GtkWidget *
  334. preview_widget (GimpDrawable *drawable)
  335. {
  336.   gint       size;
  337.   GtkWidget *preview;
  338.  
  339.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  340.   fill_preview_with_thumb (preview, drawable->id);
  341.   size = GTK_PREVIEW (preview)->rowstride * GTK_PREVIEW (preview)->buffer_height;
  342.   preview_bits = g_malloc (size);
  343.   memcpy (preview_bits, GTK_PREVIEW (preview)->buffer, size);
  344.  
  345.   return preview;
  346. }
  347.  
  348. static void
  349. fill_preview_with_thumb (GtkWidget *widget, 
  350.              gint32     drawable_ID)
  351. {
  352.   guchar  *drawable_data;
  353.   gint     bpp;
  354.   gint     x,y;
  355.   gint     width  = PREVIEW_SIZE;
  356.   gint     height = PREVIEW_SIZE;
  357.   guchar  *src;
  358.   gdouble  r, g, b, a;
  359.   gdouble  c0, c1;
  360.   guchar  *p0, *p1;
  361.   guchar  *even, *odd;
  362.  
  363.   bpp = 0; /* Only returned */
  364.   
  365.   drawable_data = 
  366.     gimp_drawable_get_thumbnail_data (drawable_ID, &width, &height, &bpp);
  367.  
  368.   if (width < 1 || height < 1)
  369.     return;
  370.  
  371.   gtk_preview_size (GTK_PREVIEW (widget), width, height);
  372.   preview_scale_x = (gdouble)width  / (gdouble)gimp_drawable_width  (drawable_ID);
  373.   preview_scale_y = (gdouble)height / (gdouble)gimp_drawable_height (drawable_ID);
  374.  
  375.   even = g_malloc (width * 3);
  376.   odd  = g_malloc (width * 3);
  377.   src = drawable_data;
  378.  
  379.   for (y = 0; y < height; y++)
  380.     {
  381.       p0 = even;
  382.       p1 = odd;
  383.       
  384.       for (x = 0; x < width; x++) 
  385.     {
  386.       if(bpp == 4)
  387.         {
  388.           r =  ((gdouble)src[x*4+0])/255.0;
  389.           g = ((gdouble)src[x*4+1])/255.0;
  390.           b = ((gdouble)src[x*4+2])/255.0;
  391.           a = ((gdouble)src[x*4+3])/255.0;
  392.         }
  393.       else if(bpp == 3)
  394.         {
  395.           r =  ((gdouble)src[x*3+0])/255.0;
  396.           g = ((gdouble)src[x*3+1])/255.0;
  397.           b = ((gdouble)src[x*3+2])/255.0;
  398.           a = 1.0;
  399.         }
  400.       else
  401.         {
  402.           r = ((gdouble)src[x*bpp+0])/255.0;
  403.           g = b = r;
  404.           if(bpp == 2)
  405.         a = ((gdouble)src[x*bpp+1])/255.0;
  406.           else
  407.         a = 1.0;
  408.         }
  409.       
  410.       if ((x / GIMP_CHECK_SIZE_SM) & 1) 
  411.         {
  412.           c0 = GIMP_CHECK_LIGHT;
  413.           c1 = GIMP_CHECK_DARK;
  414.         } 
  415.       else 
  416.         {
  417.           c0 = GIMP_CHECK_DARK;
  418.           c1 = GIMP_CHECK_LIGHT;
  419.         }
  420.       
  421.     *p0++ = (c0 + (r - c0) * a) * 255.0;
  422.     *p0++ = (c0 + (g - c0) * a) * 255.0;
  423.     *p0++ = (c0 + (b - c0) * a) * 255.0;
  424.     
  425.     *p1++ = (c1 + (r - c1) * a) * 255.0;
  426.     *p1++ = (c1 + (g - c1) * a) * 255.0;
  427.     *p1++ = (c1 + (b - c1) * a) * 255.0;
  428.     
  429.       } /* for */
  430.       
  431.       if ((y / GIMP_CHECK_SIZE_SM) & 1)
  432.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)odd,  0, y, width);
  433.       else
  434.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)even, 0, y, width);
  435.  
  436.       src += width * bpp;
  437.     }
  438.  
  439.   g_free (even);
  440.   g_free (odd);
  441.   g_free (drawable_data);
  442. }
  443.  
  444.  
  445. /*******************/
  446. /*   Main Dialog   */
  447. /*******************/
  448.  
  449. static gint
  450. nova_dialog (GimpDrawable *drawable)
  451. {
  452.   GtkWidget *dlg;
  453.   GtkWidget *frame;
  454.   GtkWidget *table;
  455.   GtkWidget *button;
  456.   GtkWidget *center_frame;
  457.   GtkObject *adj;
  458.  
  459.   gimp_ui_init ("nova", TRUE);
  460.  
  461.   dlg = gimp_dialog_new (_("SuperNova"), "nova",
  462.              gimp_standard_help_func, "filters/nova.html",
  463.              GTK_WIN_POS_MOUSE,
  464.              FALSE, TRUE, FALSE,
  465.  
  466.              _("OK"), nova_ok_callback,
  467.              NULL, NULL, NULL, TRUE, FALSE,
  468.              _("Cancel"), gtk_widget_destroy,
  469.              NULL, 1, NULL, FALSE, TRUE,
  470.  
  471.              NULL);
  472.  
  473.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  474.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  475.                       NULL);
  476.  
  477.   /*  parameter settings  */
  478.   frame = gtk_frame_new (_("Parameter Settings"));
  479.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  480.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  481.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  482.  
  483.   table = gtk_table_new (5, 3, FALSE);
  484.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  485.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  486.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  487.   gtk_container_add (GTK_CONTAINER (frame), table);
  488.  
  489.   center_frame = nova_center_create (drawable);
  490.   gtk_table_attach (GTK_TABLE (table), center_frame, 0, 3, 0, 1,
  491.                     0, 0, 0, 0);
  492.  
  493.   button = gimp_color_button_new (_("SuperNova Color Picker"), 
  494.                   SCALE_WIDTH - 8, 16, 
  495.                   pvals.color, 3);
  496.   gtk_signal_connect_object (GTK_OBJECT (button), "color_changed",
  497.                  GTK_SIGNAL_FUNC (nova),
  498.                  (gpointer)drawable);
  499.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
  500.                  _("Color:"), 1.0, 0.5,
  501.                  button, 1, TRUE);
  502.  
  503.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  504.                   _("Radius:"), SCALE_WIDTH, 0,
  505.                   pvals.radius, 1, 100, 1, 10, 0,
  506.                   FALSE, 1, GIMP_MAX_IMAGE_SIZE,
  507.                   NULL, NULL);
  508.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  509.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  510.               &pvals.radius);
  511.   gtk_signal_connect_object (GTK_OBJECT (adj), "value_changed",
  512.                  GTK_SIGNAL_FUNC (nova),
  513.                  (gpointer)drawable);
  514.  
  515.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
  516.                   _("Spokes:"), SCALE_WIDTH, 0,
  517.                   pvals.nspoke, 1, 1024, 1, 16, 0,
  518.                   TRUE, 0, 0,
  519.                   NULL, NULL);
  520.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  521.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  522.               &pvals.nspoke);
  523.   gtk_signal_connect_object (GTK_OBJECT (adj), "value_changed",
  524.                  GTK_SIGNAL_FUNC (nova),
  525.                  (gpointer)drawable);
  526.  
  527.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
  528.                   _("Random Hue:"), SCALE_WIDTH, 0,
  529.                   pvals.randomhue, 0, 360, 1, 15, 0,
  530.                   TRUE, 0, 0,
  531.                   NULL, NULL);
  532.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  533.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  534.               &pvals.randomhue);
  535.   gtk_signal_connect_object (GTK_OBJECT (adj), "value_changed",
  536.                  GTK_SIGNAL_FUNC (nova),
  537.                  (gpointer)drawable);
  538.  
  539.   gtk_widget_show (frame);
  540.   gtk_widget_show (table);
  541.   gtk_widget_show (dlg);
  542.  
  543.   gtk_main ();
  544.   gdk_flush ();
  545.  
  546.   return pint.run;
  547. }
  548.  
  549. static void
  550. nova_ok_callback (GtkWidget *widget,
  551.           gpointer   data)
  552. {
  553.   pint.run = TRUE;
  554.  
  555.   gtk_widget_destroy (GTK_WIDGET (data));
  556. }
  557.  
  558.  
  559. /*=================================================================
  560.     CenterFrame
  561.  
  562.     A frame that contains one preview and 2 entrys, used for positioning
  563.     of the center of Nova.
  564. ==================================================================*/
  565.  
  566. /*
  567.  * Create new CenterFrame, and return it (GtkFrame).
  568.  */
  569. static GtkWidget *
  570. nova_center_create (GimpDrawable *drawable)
  571. {
  572.   NovaCenter *center;
  573.   GtkWidget  *frame;
  574.   GtkWidget  *table;
  575.   GtkWidget  *label;
  576.   GtkWidget  *pframe;
  577.   GtkWidget  *spinbutton;
  578.   GtkWidget  *check;
  579.  
  580.   center = g_new (NovaCenter, 1);
  581.   center->drawable = drawable;
  582.   center->dwidth   = gimp_drawable_width (drawable->id);
  583.   center->dheight  = gimp_drawable_height (drawable->id);
  584.   center->bpp      = gimp_drawable_bpp (drawable->id);
  585.  
  586.   if (gimp_drawable_has_alpha (drawable->id))
  587.     center->bpp--;
  588.  
  589.   center->cursor   = FALSE;
  590.   center->curx     = 0;
  591.   center->cury     = 0;
  592.   center->oldx     = 0;
  593.   center->oldy     = 0;
  594.   center->in_call  = TRUE;  /* to avoid side effects while initialization */
  595.  
  596.   frame = gtk_frame_new (_("Center of SuperNova"));
  597.   gtk_signal_connect (GTK_OBJECT (frame), "destroy",
  598.                       GTK_SIGNAL_FUNC (nova_center_destroy),
  599.                       center);
  600.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  601.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  602.  
  603.   table = gtk_table_new (3, 4, FALSE);
  604.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  605.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  606.   gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6);
  607.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  608.   gtk_container_add (GTK_CONTAINER (frame), table);
  609.  
  610.   label = gtk_label_new (_("X:"));
  611.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  612.   gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
  613.            GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  614.   gtk_widget_show (label);
  615.  
  616.   spinbutton =
  617.     gimp_spin_button_new (¢er->xadj,
  618.               pvals.xcenter, G_MININT, G_MAXINT,
  619.               1, 10, 10, 0, 0);
  620.   gtk_object_set_user_data (GTK_OBJECT (center->xadj), center);
  621.   gtk_signal_connect (GTK_OBJECT (center->xadj), "value_changed",
  622.               GTK_SIGNAL_FUNC (nova_center_adjustment_update),
  623.               &pvals.xcenter);
  624.   gtk_table_attach (GTK_TABLE (table), spinbutton, 1, 2, 0, 1,
  625.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  626.   gtk_widget_show (spinbutton);
  627.  
  628.   label = gtk_label_new (_("Y:"));
  629.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  630.   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
  631.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  632.   gtk_widget_show (label);
  633.  
  634.   spinbutton =
  635.     gimp_spin_button_new (¢er->yadj,
  636.               pvals.ycenter, G_MININT, G_MAXINT,
  637.               1, 10, 10, 0, 0);
  638.   gtk_object_set_user_data (GTK_OBJECT (center->yadj), center);
  639.   gtk_signal_connect (GTK_OBJECT (center->yadj), "value_changed",
  640.               GTK_SIGNAL_FUNC (nova_center_adjustment_update),
  641.               &pvals.ycenter);
  642.   gtk_table_attach (GTK_TABLE (table), spinbutton, 3, 4, 0, 1,
  643.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  644.   gtk_widget_show (spinbutton);
  645.  
  646.   /* frame that contains preview */
  647.   pframe = gtk_frame_new (NULL);
  648.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  649.   gtk_table_attach (GTK_TABLE (table), pframe, 0, 4, 1, 2,
  650.             0, 0, 0, 0);
  651.  
  652.   /* PREVIEW */
  653.   preview = preview_widget (center->drawable);
  654.   gtk_widget_set_events (GTK_WIDGET (preview), PREVIEW_MASK);
  655.   gtk_signal_connect_after (GTK_OBJECT (preview), "expose_event",
  656.                 GTK_SIGNAL_FUNC (nova_center_preview_expose),
  657.                 center);
  658.   gtk_signal_connect (GTK_OBJECT (preview), "event",
  659.                       GTK_SIGNAL_FUNC (nova_center_preview_events),
  660.                       center);
  661.   gtk_container_add (GTK_CONTAINER (pframe), preview);
  662.   gtk_widget_show (preview);
  663.  
  664.   gtk_widget_show (pframe);
  665.   gtk_widget_show (table);
  666.   gtk_object_set_user_data (GTK_OBJECT (frame), center);
  667.   gtk_widget_show (frame);
  668.  
  669.   check = gtk_check_button_new_with_label (_("Show Cursor"));
  670.   gtk_table_attach (GTK_TABLE (table), check, 0, 4, 2, 3,
  671.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  672.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), show_cursor);
  673.   gtk_signal_connect (GTK_OBJECT (check), "toggled",
  674.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  675.                       &show_cursor);
  676.   gtk_signal_connect_object (GTK_OBJECT (check), "toggled",
  677.                  GTK_SIGNAL_FUNC (nova),
  678.                  (gpointer)drawable);
  679.   gtk_widget_show (check);
  680.   
  681.   nova_center_cursor_update (center);
  682.  
  683.   center->cursor  = FALSE;   /* Make sure that the cursor has not been drawn */
  684.   center->in_call = FALSE;   /* End of initialization */
  685.   nova (drawable, TRUE); 
  686.  
  687.   DEBUG1("pvals center=%d,%d\n", pvals.xcenter, pvals.ycenter);
  688.   DEBUG1("center cur=%d,%d\n", center->curx, center->cury);
  689.  
  690.   return frame;
  691. }
  692.  
  693. static void
  694. nova_center_destroy (GtkWidget *widget,
  695.              gpointer   data)
  696. {
  697.   NovaCenter *center = data;
  698.   g_free (center);
  699. }
  700.  
  701.  
  702. /*
  703.  *   Drawing CenterFrame
  704.  *   if update & PREVIEW, draw preview
  705.  *   if update & CURSOR,  draw cross cursor
  706.  */
  707.  
  708. static void
  709. nova_center_draw (NovaCenter *center, 
  710.           gint        update)
  711. {
  712.   if (update & PREVIEW)
  713.     {
  714.       center->cursor = FALSE;
  715.       DEBUG1 ("draw-preview\n");
  716.     }
  717.  
  718.   if (update & CURSOR)
  719.     {
  720.       DEBUG1 ("draw-cursor %d old=%d,%d cur=%d,%d\n",
  721.           center->cursor, center->oldx, center->oldy,
  722.           center->curx, center->cury);
  723.       gdk_gc_set_function (preview->style->black_gc, GDK_INVERT);
  724.       if (show_cursor) 
  725.     {
  726.       if (center->cursor)
  727.         {
  728.           gdk_draw_line (preview->window,
  729.                  preview->style->black_gc,
  730.                  center->oldx, 1, center->oldx, 
  731.                  (GTK_PREVIEW (preview)->buffer_height) - 1);
  732.           gdk_draw_line (preview->window,
  733.                  preview->style->black_gc,
  734.                  1, center->oldy, 
  735.                  (GTK_PREVIEW (preview)->buffer_width)-1, center->oldy);
  736.         }
  737.  
  738.       gdk_draw_line (preview->window,
  739.              preview->style->black_gc,
  740.              center->curx, 1, center->curx, 
  741.              (GTK_PREVIEW (preview)->buffer_height) - 1);
  742.       gdk_draw_line (preview->window,
  743.              preview->style->black_gc,
  744.              1, center->cury, 
  745.              (GTK_PREVIEW (preview)->buffer_width)-1, center->cury);
  746.     }
  747.       /* current position of cursor is updated */
  748.       center->oldx = center->curx;
  749.       center->oldy = center->cury;
  750.       center->cursor = TRUE;
  751.       gdk_gc_set_function (preview->style->black_gc, GDK_COPY);
  752.     }
  753. }
  754.  
  755. /*
  756.  *  CenterFrame entry callback
  757.  */
  758.  
  759. static void
  760. nova_center_adjustment_update (GtkAdjustment *adjustment,
  761.                    gpointer       data)
  762. {
  763.   NovaCenter *center;
  764.  
  765.   gimp_int_adjustment_update (adjustment, data);
  766.  
  767.   center = gtk_object_get_user_data (GTK_OBJECT (adjustment));
  768.  
  769.   if (!center->in_call)
  770.     {
  771.       nova_center_cursor_update (center);
  772.       nova_center_draw (center, CURSOR);
  773.       nova (center->drawable, TRUE);
  774.     }
  775. }
  776.  
  777. /*
  778.  *  Update the cross cursor's  coordinates accoding to pvals.[xy]center
  779.  *  but not redraw it
  780.  */
  781.  
  782. static void
  783. nova_center_cursor_update (NovaCenter *center)
  784. {
  785.   center->curx = pvals.xcenter * GTK_PREVIEW (preview)->buffer_width / center->dwidth;
  786.   center->cury = pvals.ycenter * GTK_PREVIEW (preview)->buffer_height / center->dheight;
  787.  
  788.   if (center->curx < 0)
  789.     center->curx = 0;
  790.   else if (center->curx >= GTK_PREVIEW (preview)->buffer_width)
  791.     center->curx = GTK_PREVIEW (preview)->buffer_width - 1;
  792.  
  793.   if (center->cury < 0)
  794.     center->cury = 0;
  795.   else if (center->cury >= GTK_PREVIEW (preview)->buffer_height)
  796.     center->cury = GTK_PREVIEW (preview)->buffer_height - 1;
  797. }
  798.  
  799. /*
  800.  *    Handle the expose event on the preview
  801.  */
  802. static gint
  803. nova_center_preview_expose (GtkWidget *widget,
  804.                             GdkEvent  *event,
  805.                 gpointer   data)
  806. {
  807.   NovaCenter *center;
  808.  
  809.   center = (NovaCenter *) data;
  810.   nova_center_draw (center, ALL);
  811.  
  812.   return FALSE;
  813. }
  814.  
  815. /*
  816.  *    Handle other events on the preview
  817.  */
  818.  
  819. static gint
  820. nova_center_preview_events (GtkWidget *widget,
  821.                 GdkEvent  *event,
  822.                 gpointer   data)
  823. {
  824.   NovaCenter *center;
  825.   GdkEventButton *bevent;
  826.   GdkEventMotion *mevent;
  827.  
  828.   center = (NovaCenter *) data;
  829.  
  830.   switch (event->type)
  831.     {
  832.     case GDK_EXPOSE:
  833.       break;
  834.  
  835.     case GDK_BUTTON_PRESS:
  836.       bevent = (GdkEventButton *) event;
  837.       center->curx = bevent->x;
  838.       center->cury = bevent->y;
  839.       goto mouse;
  840.  
  841.     case GDK_MOTION_NOTIFY:
  842.       mevent = (GdkEventMotion *) event;
  843.       if (!mevent->state)
  844.     break;
  845.       center->curx = mevent->x;
  846.       center->cury = mevent->y;
  847.     mouse:
  848.       nova_center_draw (center, CURSOR);
  849.       center->in_call = TRUE;
  850.       gtk_adjustment_set_value (GTK_ADJUSTMENT (center->xadj),
  851.                 center->curx * center->dwidth /
  852.                 GTK_PREVIEW (preview)->buffer_width);
  853.       gtk_adjustment_set_value (GTK_ADJUSTMENT (center->yadj),
  854.                 center->cury * center->dheight /
  855.                 GTK_PREVIEW (preview)->buffer_height);
  856.       center->in_call = FALSE;
  857.       nova (center->drawable, 1);
  858.       break;
  859.  
  860.     default:
  861.       break;
  862.     }
  863.  
  864.   return FALSE;
  865. }
  866.  
  867. /*
  868.   ################################################################
  869.   ##                                                            ##
  870.   ##                   Main Calculation                         ##
  871.   ##                                                            ##
  872.   ################################################################
  873. */
  874.  
  875. static gdouble
  876. gauss (void)
  877. {
  878.   gdouble sum = 0;
  879.   gint i;
  880.  
  881.   for (i = 0; i < 6; i++)
  882.     sum += (gdouble) rand () / G_MAXRAND;
  883.  
  884.   return sum / 6;
  885. }
  886.  
  887. static void
  888. nova (GimpDrawable *drawable, 
  889.       gboolean   preview_mode)
  890. {
  891.    GimpPixelRgn src_rgn, dest_rgn;
  892.    gpointer pr;
  893.    guchar *src_row, *dest_row;
  894.    guchar *src, *dest, *save_dest;
  895.    gint x1, y1, x2, y2;
  896.    gint row, col;
  897.    gint x, y;
  898.    gint alpha, has_alpha, bpp;
  899.    gint progress, max_progress;
  900.    /****************/
  901.    gint xc, yc; /* center of image */
  902.    gdouble u, v;
  903.    gdouble l, l0;
  904.    gdouble w, w1, c;
  905.    gdouble *spoke;
  906.    gdouble nova_alpha;
  907.    gdouble src_alpha;
  908.    gdouble new_alpha;
  909.    gdouble compl_ratio;
  910.    gdouble ratio;
  911.    gdouble h, s;
  912.    gdouble spokecol;
  913.    gint    i, j;
  914.    gint    color[4];
  915.    guchar  *spokecolor;
  916.  
  917.    /* initialize */
  918.  
  919.    new_alpha = 0.0;
  920.  
  921.    srand (time (NULL));
  922.    spoke = g_new (gdouble, pvals.nspoke);
  923.    spokecolor = g_new (guchar, 3 * pvals.nspoke);
  924.  
  925.    gimp_rgb_to_hsv4 (pvals.color, &h, &s, &v);
  926.  
  927.    for (i = 0; i < pvals.nspoke; i++)
  928.      {
  929.        spoke[i] = gauss();
  930.        h += ((gdouble) pvals.randomhue / 360.0 *
  931.          ((gdouble) rand() / (gdouble) G_MAXRAND - 0.5));
  932.        if (h < 0)
  933.      h += 1.0;
  934.        else if (h >= 1.0)
  935.      h -= 1.0;
  936.        gimp_hsv_to_rgb4 (&spokecolor[3*i], h, s, v);       
  937.      }
  938.  
  939.    has_alpha = gimp_drawable_has_alpha (drawable->id);
  940.    if (preview_mode) 
  941.      {
  942.        xc = (gdouble)pvals.xcenter * preview_scale_x;
  943.        yc = (gdouble)pvals.ycenter * preview_scale_y;
  944.  
  945.        x1 = y1 = 0;
  946.        x2 = GTK_PREVIEW (preview)->buffer_width;
  947.        y2 = GTK_PREVIEW (preview)->buffer_height;
  948.        bpp = GTK_PREVIEW (preview)->bpp;
  949.        has_alpha = 0;
  950.        alpha = bpp;
  951.      } 
  952.    else 
  953.      {
  954.        gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  955.        bpp = gimp_drawable_bpp (drawable->id);
  956.        alpha = (has_alpha) ? bpp - 1 : bpp;
  957.        xc = pvals.xcenter;
  958.        yc = pvals.ycenter;
  959.  
  960.        gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, x2-x1, y2-y1, FALSE, FALSE);
  961.        gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, x2-x1, y2-y1, TRUE, TRUE);
  962.      }
  963.  
  964.    l0 = (x2-xc)/4+1;   /* standard length */
  965.  
  966.    /* Initialize progress */
  967.    progress = 0;
  968.    max_progress = (x2 - x1) * (y2 - y1);
  969.  
  970.    if (preview_mode) 
  971.      {
  972.        /* thanks to this pixel rgn register
  973.       stuff, i have to duplicate the
  974.       entire loop.  why not just use
  975.       get_row??? */
  976.        src_row   = g_malloc (y2 * GTK_PREVIEW (preview)->rowstride);
  977.        memcpy (src_row, preview_bits, y2 * GTK_PREVIEW (preview)->rowstride);
  978.        dest_row  = g_malloc (y2 * GTK_PREVIEW (preview)->rowstride);
  979.        save_dest = dest_row;
  980.  
  981.        for (row = 0, y = 0; row < y2; row++, y++)
  982.          {
  983.            src = src_row;
  984.            dest = dest_row;
  985.  
  986.            for (col = 0, x = 0; col < x2; col++, x++)
  987.              {
  988.                u = (gdouble) (x-xc) / (pvals.radius * preview_scale_x);
  989.                v = (gdouble) (y-yc) / (pvals.radius * preview_scale_y);
  990.                l = sqrt(u*u + v*v);
  991.  
  992.                /* This algorithm is still under construction. */
  993.                c = (atan2 (u, v) / (2 * G_PI) + .51) * pvals.nspoke;
  994.                i = (int) floor (c);
  995.                c -= i;
  996.                i %= pvals.nspoke;
  997.                w1 = spoke[i] * (1 - c) + spoke[(i + 1) % pvals.nspoke] * c;
  998.                w1 = w1 * w1;
  999.  
  1000.                w = 1/(l+0.001)*0.9;
  1001.  
  1002.                nova_alpha = CLAMP (w, 0.0, 1.0);
  1003.  
  1004.                if (has_alpha)
  1005.                  {
  1006.                    src_alpha = (gdouble) src[alpha] / 255.0;
  1007.                    new_alpha = src_alpha + (1.0 - src_alpha) * nova_alpha;
  1008.  
  1009.                    if (new_alpha != 0.0)
  1010.                      ratio = nova_alpha / new_alpha;
  1011.                    else
  1012.                      ratio = 0.0;
  1013.                  }
  1014.                else
  1015.                  ratio = nova_alpha;
  1016.  
  1017.                compl_ratio = 1.0 - ratio;
  1018.  
  1019.                for (j = 0; j < alpha; j++)
  1020.                  {
  1021.            spokecol = (gdouble)spokecolor[3*i+j]*(1.0-c) + 
  1022.                       (gdouble)spokecolor[3*((i + 1) % pvals.nspoke)+j]*c;
  1023.                    if (w > 1.0)
  1024.                      color[j] = CLAMP (spokecol * w, 0, 255);
  1025.                    else
  1026.                      color[j] = src[j] * compl_ratio + spokecol * ratio;
  1027.  
  1028.                    c = CLAMP (w1 * w, 0, 1);
  1029.                    color[j] = color[j] + 255 * c;
  1030.  
  1031.                    dest[j]= CLAMP (color[j], 0, 255);
  1032.                  }
  1033.  
  1034.                if (has_alpha)
  1035.                  dest[alpha] = new_alpha * 255.0;
  1036.  
  1037.                src += bpp;
  1038.                dest += bpp;
  1039.              }
  1040.            src_row  += GTK_PREVIEW (preview)->rowstride;
  1041.            dest_row += GTK_PREVIEW (preview)->rowstride;
  1042.          }
  1043.  
  1044.        memcpy (GTK_PREVIEW(preview)->buffer, save_dest, y2 * GTK_PREVIEW (preview)->rowstride);
  1045.        gtk_widget_queue_draw (preview);
  1046.      } 
  1047.    else 
  1048.      { /* normal mode */
  1049.        for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  1050.         pr != NULL; pr = gimp_pixel_rgns_process (pr))
  1051.      {
  1052.        src_row = src_rgn.data;
  1053.        dest_row = dest_rgn.data;
  1054.        
  1055.        for (row = 0, y = src_rgn.y; row < src_rgn.h; row++, y++)
  1056.          {
  1057.            src = src_row;
  1058.            dest = dest_row;
  1059.            
  1060.            for (col = 0, x = src_rgn.x; col < src_rgn.w; col++, x++)
  1061.          {
  1062.            u = (gdouble) (x-xc) / pvals.radius;
  1063.            v = (gdouble) (y-yc) / pvals.radius;
  1064.            l = sqrt(u*u + v*v);
  1065.            
  1066.            /* This algorithm is still under construction. */
  1067.            c = (atan2 (u, v) / (2 * G_PI) + .51) * pvals.nspoke;
  1068.            i = (int) floor (c);
  1069.            c -= i;
  1070.            i %= pvals.nspoke;
  1071.            w1 = spoke[i] * (1 - c) + spoke[(i + 1) % pvals.nspoke] * c;
  1072.            w1 = w1 * w1;
  1073.            
  1074.            w = 1/(l+0.001)*0.9;
  1075.            
  1076.            nova_alpha = CLAMP (w, 0.0, 1.0);
  1077.            
  1078.            if (has_alpha)
  1079.              {
  1080.                src_alpha = (gdouble) src[alpha] / 255.0;
  1081.                new_alpha = src_alpha + (1.0 - src_alpha) * nova_alpha;
  1082.                
  1083.                if (new_alpha != 0.0)
  1084.              ratio = nova_alpha / new_alpha;
  1085.                else
  1086.              ratio = 0.0;
  1087.              }
  1088.            else
  1089.              ratio = nova_alpha;
  1090.            
  1091.            compl_ratio = 1.0 - ratio;
  1092.            
  1093.            for (j = 0; j < alpha; j++)
  1094.              {
  1095.                spokecol = (gdouble)spokecolor[3*i+j]*(1.0-c) + 
  1096.                       (gdouble)spokecolor[3*((i + 1) % pvals.nspoke)+j]*c;
  1097.                if (w > 1.0)
  1098.              color[j] = CLAMP (spokecol * w, 0, 255);
  1099.                else
  1100.              color[j] = src[j] * compl_ratio + spokecol * ratio;
  1101.                
  1102.                c = CLAMP (w1 * w, 0, 1);
  1103.                color[j] = color[j] + 255 * c;
  1104.                
  1105.                dest[j]= CLAMP (color[j], 0, 255);
  1106.              }
  1107.            
  1108.            if (has_alpha)
  1109.              dest[alpha] = new_alpha * 255.0;
  1110.            
  1111.            src += src_rgn.bpp;
  1112.            dest += dest_rgn.bpp;
  1113.          }
  1114.            src_row += src_rgn.rowstride;
  1115.            dest_row += dest_rgn.rowstride;
  1116.          }
  1117.  
  1118.        /* Update progress */
  1119.        progress += src_rgn.w * src_rgn.h;
  1120.        gimp_progress_update ((double) progress / (double) max_progress);
  1121.      }
  1122.  
  1123.        gimp_drawable_flush (drawable);
  1124.        gimp_drawable_merge_shadow (drawable->id, TRUE);
  1125.        gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  1126.      }
  1127.  
  1128.    g_free (spoke);
  1129.    g_free (spokecolor);
  1130. }
  1131.