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 / snoise.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  15.9 KB  |  613 lines

  1. /* Solid Noise plug-in -- creates solid noise textures
  2.  * Copyright (C) 1997, 1998 Marcelo de Gomensoro Malheiros
  3.  *
  4.  * The GIMP -- an image manipulation program
  5.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  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.  
  22. /* Solid Noise plug-in version 1.03, Apr 1998
  23.  *
  24.  * This plug-in generates solid noise textures based on the
  25.  * `Noise' and `Turbulence' functions described in the paper
  26.  *   
  27.  *    Perlin, K, and Hoffert, E. M., "Hypertexture",
  28.  *    Computer Graphics 23, 3 (August 1989)
  29.  *
  30.  * The algorithm implemented here also makes possible the
  31.  * creation of seamless tiles.
  32.  *
  33.  * You can contact me at <malheiro@dca.fee.unicamp.br>.
  34.  * Comments and improvements for this code are welcome.
  35.  *
  36.  * The overall plug-in structure is based on the Whirl plug-in,
  37.  * which is Copyright (C) 1997 Federico Mena Quintero
  38.  */
  39.  
  40. /* Version 1.03:
  41.  *
  42.  *  Added patch from Kevin Turner <kevint@poboxes.com> to use the
  43.  *  current time as the random seed. Thank you!
  44.  *  Incorporated some portability changes from the GIMP distribution.
  45.  *
  46.  * Version 1.02:
  47.  *
  48.  *  Fixed a stupid bug with the alpha channel.
  49.  *  Fixed a rounding bug for small tilable textures.
  50.  *  Now the dialog is more compact; using the settings from gtkrc.
  51.  *
  52.  * Version 1.01:
  53.  *
  54.  *  Quick fix for wrong pdb declaration. Also changed default seed to 1.
  55.  *  Thanks to Adrian Likins and Federico Mena for the patch!
  56.  *
  57.  * Version 1.0:
  58.  *
  59.  *  Initial release.
  60.  */
  61.  
  62. #include "config.h"
  63.  
  64. #include <time.h>
  65. #include <stdlib.h>
  66. #include <stdio.h>
  67.  
  68. #include <gtk/gtk.h>
  69.  
  70. #include <libgimp/gimp.h>
  71. #include <libgimp/gimpui.h>
  72.  
  73. #include "libgimp/stdplugins-intl.h"
  74.  
  75.  
  76. /*---- Defines ----*/
  77.  
  78. #define TABLE_SIZE 64
  79. #define WEIGHT(T) ((2.0*fabs(T)-3.0)*(T)*(T)+1.0)
  80.  
  81. #define SCALE_WIDTH 128
  82. #define SCALE_MAX    16.0
  83.  
  84.  
  85. /*---- Typedefs ----*/
  86.  
  87. typedef struct
  88. {
  89.   gint    tilable;
  90.   gint    turbulent;
  91.   gint    seed;
  92.   gint    detail;
  93.   gdouble xsize;
  94.   gdouble ysize;
  95.   /*  Interface only  */
  96.   gint    timeseed;
  97. } SolidNoiseValues;
  98.  
  99. typedef struct
  100. {
  101.   gint run;
  102. } SolidNoiseInterface;
  103.  
  104.  
  105. /*---- Prototypes ----*/
  106.  
  107. static void query (void);
  108. static void run   (gchar   *name,
  109.            gint     nparams,
  110.            GimpParam  *param,
  111.            gint    *nreturn_vals,
  112.            GimpParam **return_vals);
  113.  
  114. static void    solid_noise      (GimpDrawable *drawable);
  115. static void    solid_noise_init (void);
  116. static gdouble plain_noise      (gdouble    x,
  117.                  gdouble    y,
  118.                  guint      s);
  119. static gdouble noise            (gdouble    x,
  120.                  gdouble    y);
  121.  
  122. static gint    solid_noise_dialog    (void);
  123. static void    dialog_ok_callback    (GtkWidget *widget,
  124.                       gpointer   data);
  125.  
  126.  
  127. /*---- Variables ----*/
  128.  
  129. GimpPlugInInfo PLUG_IN_INFO =
  130. {
  131.   NULL,  /* init_proc  */
  132.   NULL,  /* quit_proc  */
  133.   query, /* query_proc */
  134.   run,   /* run_proc   */
  135. };
  136.  
  137. static SolidNoiseValues snvals =
  138. {
  139.   0,   /* tilable       */
  140.   0,   /* turbulent     */
  141.   1,   /* seed          */
  142.   1,   /* detail        */
  143.   4.0, /* xsize         */
  144.   4.0, /* ysize         */
  145.   0    /* use time seed */ 
  146. };
  147.  
  148. static SolidNoiseInterface snint =
  149. {
  150.   FALSE /* run */
  151. };
  152.  
  153. static gint        xclip, yclip;
  154. static gdouble     offset, factor;
  155. static gdouble     xsize, ysize;
  156. static gint        perm_tab[TABLE_SIZE];
  157. static GimpVector2 grad_tab[TABLE_SIZE];
  158.  
  159.  
  160. /*---- Functions ----*/
  161.  
  162. MAIN ()
  163.  
  164. static void
  165. query (void)
  166. {
  167.   static GimpParamDef args[] =
  168.   {
  169.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  170.     { GIMP_PDB_IMAGE, "image", "Input image" },
  171.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  172.     { GIMP_PDB_INT32, "tilable", "Create a tilable output (n=0/y=1)" },
  173.     { GIMP_PDB_INT32, "turbulent", "Make a turbulent noise (n=0/y=1)" },
  174.     { GIMP_PDB_INT32, "seed", "Random seed" },
  175.     { GIMP_PDB_INT32, "detail", "Detail level (0 - 15)" },
  176.     { GIMP_PDB_FLOAT, "xsize", "Horizontal texture size" },
  177.     { GIMP_PDB_FLOAT, "ysize", "Vertical texture size" }
  178.   };
  179.   static gint nargs = sizeof (args) / sizeof (args[0]);
  180.  
  181.   gimp_install_procedure ("plug_in_solid_noise",
  182.               "Creates a grayscale noise texture",
  183.               "Generates 2D textures using Perlin's classic solid noise function.",
  184.               "Marcelo de Gomensoro Malheiros",
  185.               "Marcelo de Gomensoro Malheiros",
  186.               "Apr 1998, v1.03",
  187.               N_("<Image>/Filters/Render/Clouds/Solid Noise..."),
  188.               "RGB*, GRAY*",
  189.               GIMP_PLUGIN,
  190.               nargs, 0,
  191.               args, NULL);
  192. }
  193.  
  194.  
  195. static void
  196. run (gchar   *name,
  197.      gint     nparams,
  198.      GimpParam  *param,
  199.      gint    *nreturn_vals,
  200.      GimpParam **return_vals)
  201. {
  202.   static GimpParam values[1];
  203.   
  204.   GimpDrawable *drawable;
  205.   GimpRunModeType run_mode;
  206.   GimpPDBStatusType status;
  207.   
  208.   status = GIMP_PDB_SUCCESS;
  209.   run_mode = param[0].data.d_int32;
  210.  
  211.   values[0].type = GIMP_PDB_STATUS;
  212.   values[0].data.d_status = status;
  213.  
  214.   *nreturn_vals = 1;
  215.   *return_vals  = values;
  216.   
  217.   /*  Get the specified drawable  */
  218.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  219.  
  220.   /*  See how we will run  */
  221.   switch (run_mode)
  222.     {
  223.     case GIMP_RUN_INTERACTIVE:
  224.       INIT_I18N_UI();
  225.       /*  Possibly retrieve data  */
  226.       gimp_get_data("plug_in_solid_noise", &snvals);
  227.  
  228.       /*  Get information from the dialog  */
  229.       if (!solid_noise_dialog ())
  230.     return;
  231.       break;
  232.  
  233.     case GIMP_RUN_NONINTERACTIVE:
  234.       INIT_I18N();
  235.       /*  Test number of arguments  */
  236.       if (nparams != 9)
  237.     {
  238.       status = GIMP_PDB_CALLING_ERROR;
  239.     }
  240.       else
  241.     {
  242.       snvals.tilable   = param[3].data.d_int32;
  243.       snvals.turbulent = param[4].data.d_int32;
  244.       snvals.seed      = param[5].data.d_int32;
  245.       snvals.detail    = param[6].data.d_int32;
  246.       snvals.xsize     = param[7].data.d_float;
  247.       snvals.ysize     = param[8].data.d_float;
  248.     }
  249.       break;
  250.  
  251.     case GIMP_RUN_WITH_LAST_VALS:
  252.       INIT_I18N();
  253.       /*  Possibly retrieve data  */
  254.       gimp_get_data ("plug_in_solid_noise", &snvals);
  255.       break;
  256.  
  257.     default:
  258.       break;
  259.     }
  260.   
  261.   /*  Create texture  */
  262.   if ((status == GIMP_PDB_SUCCESS) && (gimp_drawable_is_rgb (drawable->id) ||
  263.                      gimp_drawable_is_gray (drawable->id)))
  264.     {
  265.       /*  Set the tile cache size  */
  266.       gimp_tile_cache_ntiles ((drawable->width + gimp_tile_width () - 1) /
  267.                   gimp_tile_width ());
  268.  
  269.       /*  Run!  */
  270.       solid_noise (drawable);
  271.  
  272.       /*  If run mode is interactive, flush displays  */
  273.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  274.         gimp_displays_flush ();
  275.  
  276.       /*  Store data  */
  277.       if (run_mode == GIMP_RUN_INTERACTIVE || run_mode == GIMP_RUN_WITH_LAST_VALS)
  278.         gimp_set_data ("plug_in_solid_noise", &snvals,
  279.                sizeof (SolidNoiseValues));
  280.     }
  281.   else
  282.     {
  283.       /* gimp_message ("solid noise: cannot operate on indexed color images"); */
  284.       status = GIMP_PDB_EXECUTION_ERROR;
  285.     }  
  286.  
  287.   values[0].data.d_status = status;
  288.   
  289.   gimp_drawable_detach (drawable);
  290. }
  291.  
  292.  
  293. static void
  294. solid_noise (GimpDrawable *drawable)
  295. {
  296.   GimpPixelRgn dest_rgn;
  297.   gint      chns, i, has_alpha, row, col;
  298.   gint      sel_x1, sel_y1, sel_x2, sel_y2;
  299.   gint      sel_width, sel_height;
  300.   gint      progress, max_progress;
  301.   gpointer  pr;
  302.   guchar   *dest, *dest_row;
  303.   guchar    val;
  304.  
  305.   /*  Get selection area  */
  306.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  307.   sel_width = sel_x2 - sel_x1;
  308.   sel_height = sel_y2 - sel_y1;
  309.  
  310.   /*  Initialization  */
  311.   solid_noise_init ();
  312.   gimp_progress_init ( _("Solid Noise..."));
  313.   progress = 0;
  314.   max_progress = sel_width * sel_height;
  315.   chns = gimp_drawable_bpp (drawable->id);
  316.   has_alpha = 0;
  317.   if (gimp_drawable_has_alpha (drawable->id))
  318.     {
  319.       chns--;
  320.       has_alpha = 1;
  321.     }
  322.   gimp_pixel_rgn_init (&dest_rgn, drawable, sel_x1, sel_y1, sel_width,
  323.                        sel_height, TRUE, TRUE);
  324.  
  325.   /*  One, two, three, go!  */
  326.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn); pr != NULL;
  327.        pr = gimp_pixel_rgns_process (pr))
  328.     {
  329.       dest_row = dest_rgn.data;
  330.  
  331.       for (row = dest_rgn.y; row < (dest_rgn.y + dest_rgn.h); row++)
  332.         {
  333.           dest = dest_row;
  334.           
  335.           for (col = dest_rgn.x; col < (dest_rgn.x + dest_rgn.w); col++)
  336.             {
  337.               val = (guchar) floor (255.0 * noise ((double) (col - sel_x1) / sel_width, (double) (row - sel_y1) / sel_height));
  338.               for (i = 0; i < chns; i++)
  339.                 *dest++ = val;
  340.               if (has_alpha)
  341.                 *dest++ = 255;
  342.             }
  343.  
  344.           dest_row += dest_rgn.rowstride;;
  345.         }
  346.  
  347.       /*  Update progress  */
  348.       progress += dest_rgn.w * dest_rgn.h;
  349.       gimp_progress_update ((double) progress / (double) max_progress);
  350.     }
  351.  
  352.   /*  Update the drawable  */
  353.   gimp_drawable_flush (drawable);
  354.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  355.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  356. }
  357.  
  358.  
  359. static void
  360. solid_noise_init (void)
  361. {
  362.   gint    i, j, k, t;
  363.   gdouble m;
  364.  
  365.   /*  Force sane parameters  */
  366.   if (snvals.detail < 0)
  367.     snvals.detail = 0;
  368.   if (snvals.detail > 15)
  369.     snvals.detail = 15;
  370.   if (snvals.seed < 0)
  371.     snvals.seed = 0;
  372.   
  373.   /*  Define the pseudo-random number generator seed  */
  374.   if (snvals.timeseed)
  375.     snvals.seed = time(NULL);
  376.   srand (snvals.seed);
  377.  
  378.   /*  Set scaling factors  */
  379.   if (snvals.tilable)
  380.     {
  381.       xsize = ceil (snvals.xsize);
  382.       ysize = ceil (snvals.ysize);
  383.       xclip = (int) xsize;
  384.       yclip = (int) ysize;
  385.     }
  386.   else
  387.     {
  388.       xsize = snvals.xsize;
  389.       ysize = snvals.ysize;
  390.     }
  391.  
  392.   /*  Set totally empiric normalization values  */
  393.   if (snvals.turbulent)
  394.     {
  395.       offset=0.0;
  396.       factor=1.0;
  397.     }
  398.   else
  399.     {
  400.       offset=0.94;
  401.       factor=0.526;
  402.     }
  403.   
  404.   /*  Initialize the permutation table  */
  405.   for (i = 0; i < TABLE_SIZE; i++)
  406.     perm_tab[i] = i;
  407.   for (i = 0; i < (TABLE_SIZE >> 1); i++)
  408.     {
  409.       j = rand () % TABLE_SIZE;
  410.       k = rand () % TABLE_SIZE;
  411.       t = perm_tab[j];
  412.       perm_tab[j] = perm_tab[k];
  413.       perm_tab[k] = t;
  414.     }
  415.   
  416.   /*  Initialize the gradient table  */
  417.   for (i = 0; i < TABLE_SIZE; i++)
  418.     {
  419.       do
  420.     {
  421.       grad_tab[i].x =
  422.         (double)(rand () - (G_MAXRAND >> 1)) / (G_MAXRAND >> 1);
  423.       grad_tab[i].y =
  424.         (double)(rand () - (G_MAXRAND >> 1)) / (G_MAXRAND >> 1);
  425.       m = grad_tab[i].x * grad_tab[i].x + grad_tab[i].y * grad_tab[i].y;
  426.     }
  427.       while (m == 0.0 || m > 1.0);
  428.  
  429.       m = 1.0 / sqrt(m);
  430.       grad_tab[i].x *= m;
  431.       grad_tab[i].y *= m;
  432.     }
  433. }
  434.  
  435.  
  436. static gdouble
  437. plain_noise (gdouble x,
  438.          gdouble y,
  439.          guint   s)
  440. {
  441.   GimpVector2 v;
  442.   gint        a, b, i, j, n;
  443.   gdouble     sum;
  444.  
  445.   sum = 0.0;
  446.   x *= s;
  447.   y *= s;
  448.   a = (int) floor (x);
  449.   b = (int) floor (y);
  450.  
  451.   for (i = 0; i < 2; i++)
  452.     for (j = 0; j < 2; j++)
  453.       {
  454.     if (snvals.tilable) 
  455.       n = perm_tab[(((a + i) % (xclip * s)) + perm_tab[((b + j) % (yclip * s)) % TABLE_SIZE]) % TABLE_SIZE];
  456.     else
  457.       n = perm_tab[(a + i + perm_tab[(b + j) % TABLE_SIZE]) % TABLE_SIZE];
  458.     v.x = x - a - i;
  459.     v.y = y - b - j;
  460.     sum += WEIGHT(v.x) * WEIGHT(v.y) * (grad_tab[n].x * v.x + grad_tab[n].y * v.y);
  461.       }
  462.  
  463.   return sum / s;
  464. }
  465.  
  466.  
  467. static gdouble
  468. noise (gdouble x,
  469.        gdouble y)
  470. {
  471.   gint i;
  472.   guint s;
  473.   gdouble sum;
  474.  
  475.   s = 1;
  476.   sum = 0.0;
  477.   x *= xsize;
  478.   y *= ysize;
  479.   
  480.   for (i = 0; i <= snvals.detail; i++)
  481.     {
  482.       if (snvals.turbulent)
  483.     sum += fabs (plain_noise (x, y, s));
  484.       else
  485.     sum += plain_noise (x, y, s);
  486.       s <<= 1;
  487.     }
  488.  
  489.   return (sum+offset)*factor;
  490. }
  491.  
  492.  
  493. static gint
  494. solid_noise_dialog (void)
  495. {
  496.   GtkWidget *dlg;
  497.   GtkWidget *frame;
  498.   GtkWidget *toggle;
  499.   GtkWidget *table;
  500.   GtkWidget *seed_hbox;
  501.   GtkWidget *spinbutton;
  502.   GtkObject *adj;
  503.  
  504.   gimp_ui_init ("snoise", FALSE);
  505.  
  506.   /*  Dialog initialization  */
  507.   dlg = gimp_dialog_new (_("Solid Noise"), "snoise",
  508.              gimp_standard_help_func, "filters/snoise.html",
  509.              GTK_WIN_POS_MOUSE,
  510.              FALSE, TRUE, FALSE,
  511.  
  512.              _("OK"), dialog_ok_callback,
  513.              NULL, NULL, NULL, TRUE, FALSE,
  514.              _("Cancel"), gtk_widget_destroy,
  515.              NULL, 1, NULL, FALSE, TRUE,
  516.  
  517.              NULL);
  518.  
  519.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  520.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  521.               NULL);
  522.  
  523.   gimp_help_init ();
  524.  
  525.   frame = gtk_frame_new (_("Parameter Settings"));
  526.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  527.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, FALSE, 0);
  528.   gtk_widget_show (frame);
  529.  
  530.   /*  Table  */
  531.   table = gtk_table_new (4, 3, FALSE);
  532.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  533.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  534.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  535.   gtk_container_add (GTK_CONTAINER (frame), table);
  536.  
  537.   /*  Random Seed  */
  538.   seed_hbox = gimp_random_seed_new (&snvals.seed,
  539.                     &snvals.timeseed,
  540.                     TRUE, FALSE);
  541.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  542.                  _("Random Seed:"), 1.0, 0.5,
  543.                  seed_hbox, 1, TRUE);
  544.  
  545.   /*  Detail  */
  546.   spinbutton = gimp_spin_button_new (&adj, snvals.detail,
  547.                      1, 15, 1, 3, 0, 1, 0);
  548.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  549.                  _("Detail:"), 1.0, 0.5,
  550.                  spinbutton, 1, TRUE);
  551.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  552.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  553.               &snvals.detail);
  554.  
  555.   /*  Turbulent  */
  556.   toggle = gtk_check_button_new_with_label ( _("Turbulent"));
  557.   gtk_table_attach (GTK_TABLE (table), toggle, 2, 3, 0, 1,
  558.                     GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0);
  559.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), snvals.turbulent);
  560.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  561.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  562.               &snvals.turbulent);
  563.   gtk_widget_show (toggle);
  564.  
  565.   /*  Tilable  */
  566.   toggle = gtk_check_button_new_with_label ( _("Tilable"));
  567.   gtk_table_attach (GTK_TABLE (table), toggle, 2, 3, 1, 2,
  568.                     GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0);
  569.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), snvals.tilable);
  570.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  571.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  572.               &snvals.tilable);
  573.   gtk_widget_show (toggle);
  574.  
  575.   /*  X Size  */
  576.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  577.                   _("X Size:"), SCALE_WIDTH, 0,
  578.                   snvals.xsize, 0.1, SCALE_MAX, 0.1, 1.0, 1,
  579.                   TRUE, 0, 0,
  580.                   NULL, NULL);
  581.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  582.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  583.               &snvals.xsize);
  584.  
  585.   /*  Y Size  */
  586.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  587.                   _("Y Size:"), SCALE_WIDTH, 0,
  588.                   snvals.ysize, 0.1, SCALE_MAX, 0.1, 1.0, 1,
  589.                   TRUE, 0, 0,
  590.                   NULL, NULL);
  591.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  592.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  593.               &snvals.ysize);
  594.  
  595.   gtk_widget_show (table);
  596.   gtk_widget_show (dlg);
  597.  
  598.   gtk_main ();
  599.   gimp_help_free ();
  600.   gdk_flush ();
  601.  
  602.   return snint.run;
  603. }
  604.  
  605. static void
  606. dialog_ok_callback (GtkWidget *widget,
  607.             gpointer   data)
  608. {
  609.   snint.run = TRUE;
  610.  
  611.   gtk_widget_destroy (GTK_WIDGET (data));
  612. }
  613.