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

  1. /*
  2.  * This is a plug-in for the GIMP.
  3.  *
  4.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  5.  * Copyright (C) 1996 Torsten Martinsen
  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.  * $Id: oilify.c,v 1.24 2000/08/22 01:26:55 neo Exp $
  22.  */
  23.  
  24. #include "config.h"
  25.  
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29.  
  30. #include <gtk/gtk.h>
  31.  
  32. #include <libgimp/gimp.h>
  33. #include <libgimp/gimpui.h>
  34.  
  35. #include "libgimp/stdplugins-intl.h"
  36.  
  37.  
  38. #define SCALE_WIDTH  125
  39. #define HISTSIZE     256
  40.  
  41. #define MODE_RGB       0
  42. #define MODE_INTEN     1
  43.  
  44. #undef INTENSITY
  45. #define INTENSITY(p)   ((guint) (p[0]*77+p[1]*150+p[2]*29) >> 8)
  46.  
  47. typedef struct
  48. {
  49.   gdouble mask_size;
  50.   gint    mode;
  51. } OilifyVals;
  52.  
  53. typedef struct
  54. {
  55.   gint run;
  56. } OilifyInterface;
  57.  
  58. /* Declare local functions.
  59.  */
  60. static void      query  (void);
  61. static void      run    (gchar     *name,
  62.                          gint       nparams,
  63.                          GimpParam    *param,
  64.                          gint      *nreturn_vals,
  65.                          GimpParam   **return_vals);
  66.  
  67. static void      oilify_rgb         (GimpDrawable *drawable);
  68. static void      oilify_intensity   (GimpDrawable *drawable);
  69.  
  70. static gint      oilify_dialog      (void);
  71.  
  72. static void      oilify_ok_callback (GtkWidget *widget,
  73.                      gpointer   data);
  74.  
  75. GimpPlugInInfo PLUG_IN_INFO =
  76. {
  77.   NULL,  /* init_proc  */
  78.   NULL,  /* quit_proc  */
  79.   query, /* query_proc */
  80.   run,   /* run_proc   */
  81. };
  82.  
  83. static OilifyVals ovals =
  84. {
  85.   7.0,     /* mask size */
  86.   0        /* mode      */
  87. };
  88.  
  89. static OilifyInterface oint =
  90. {
  91.   FALSE     /*  run  */
  92. };
  93.  
  94.  
  95. MAIN ()
  96.  
  97. static void
  98. query (void)
  99. {
  100.   static GimpParamDef args[] =
  101.   {
  102.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  103.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  104.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  105.     { GIMP_PDB_INT32, "mask_size", "Oil paint mask size" },
  106.     { GIMP_PDB_INT32, "mode", "Algorithm {RGB (0), INTENSITY (1)}" }
  107.   };
  108.   static gint nargs = sizeof (args) / sizeof (args[0]);
  109.  
  110.   gimp_install_procedure ("plug_in_oilify",
  111.               "Modify the specified drawable to resemble an oil "
  112.               "painting",
  113.               "This function performs the well-known oil-paint "
  114.               "effect on the specified drawable.  The size of the "
  115.               "input mask is specified by 'mask_size'.",
  116.               "Torsten Martinsen",
  117.               "Torsten Martinsen",
  118.               "1996",
  119.               N_("<Image>/Filters/Artistic/Oilify..."),
  120.               "RGB*, GRAY*",
  121.               GIMP_PLUGIN,
  122.               nargs, 0,
  123.               args, NULL);
  124. }
  125.  
  126. static void
  127. run (gchar   *name,
  128.      gint     nparams,
  129.      GimpParam  *param,
  130.      gint    *nreturn_vals,
  131.      GimpParam **return_vals)
  132. {
  133.   static GimpParam values[1];
  134.   GimpDrawable *drawable;
  135.   GimpRunModeType run_mode;
  136.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  137.  
  138.   INIT_I18N_UI();
  139.  
  140.   run_mode = param[0].data.d_int32;
  141.  
  142.   *nreturn_vals = 2;
  143.   *return_vals = values;
  144.  
  145.   values[0].type = GIMP_PDB_STATUS;
  146.   values[0].data.d_status = status;
  147.  
  148.   switch (run_mode)
  149.     {
  150.     case GIMP_RUN_INTERACTIVE:
  151.       /*  Possibly retrieve data  */
  152.       gimp_get_data ("plug_in_oilify", &ovals);
  153.  
  154.       /*  First acquire information with a dialog  */
  155.       if (! oilify_dialog ())
  156.         return;
  157.       break;
  158.  
  159.     case GIMP_RUN_NONINTERACTIVE:
  160.       /*  Make sure all the arguments are there!  */
  161.       if (nparams != 5)
  162.     {
  163.       status = GIMP_PDB_CALLING_ERROR;
  164.     }
  165.       else
  166.         {
  167.           ovals.mask_size = (gdouble) param[3].data.d_int32;
  168.           ovals.mode = (gint) param[4].data.d_int32;
  169.  
  170.       if ((ovals.mask_size < 1.0) ||
  171.           ((ovals.mode != MODE_INTEN) &&
  172.            (ovals.mode != MODE_RGB)))
  173.         status = GIMP_PDB_CALLING_ERROR;
  174.     }
  175.       break;
  176.  
  177.     case GIMP_RUN_WITH_LAST_VALS:
  178.       /*  Possibly retrieve data  */
  179.       gimp_get_data ("plug_in_oilify", &ovals);
  180.       break;
  181.  
  182.     default:
  183.       break;
  184.     }
  185.  
  186.   /*  Get the specified drawable  */
  187.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  188.  
  189.   /*  Make sure that the drawable is gray or RGB color  */
  190.   if ((status == GIMP_PDB_SUCCESS) &&
  191.       (gimp_drawable_is_rgb (drawable->id) ||
  192.        gimp_drawable_is_gray (drawable->id)))
  193.     {
  194.       gimp_progress_init (_("Oil Painting..."));
  195.       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  196.  
  197.       if (gimp_drawable_is_rgb (drawable->id) && (ovals.mode == MODE_INTEN))
  198.         oilify_intensity (drawable);
  199.       else
  200.         oilify_rgb (drawable);
  201.  
  202.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  203.         gimp_displays_flush ();
  204.  
  205.       /*  Store data  */
  206.       if (run_mode == GIMP_RUN_INTERACTIVE)
  207.         gimp_set_data ("plug_in_oilify", &ovals, sizeof (OilifyVals));
  208.     }
  209.   else
  210.     {
  211.       /* gimp_message ("oilify: cannot operate on indexed color images"); */
  212.       status = GIMP_PDB_EXECUTION_ERROR;
  213.     }
  214.  
  215.   values[0].data.d_status = status;
  216.  
  217.   gimp_drawable_detach (drawable);
  218. }
  219.  
  220. /*
  221.  * For each RGB channel, replace the pixel at (x,y) with the
  222.  * value that occurs most often in the n x n chunk centered
  223.  * at (x,y).
  224.  */
  225. static void
  226. oilify_rgb (GimpDrawable *drawable)
  227. {
  228.   GimpPixelRgn src_rgn, dest_rgn;
  229.   gint bytes;
  230.   gint width, height;
  231.   guchar *src_row, *src;
  232.   guchar *dest_row, *dest;
  233.   gint x, y, c, b, xx, yy, n;
  234.   gint x1, y1, x2, y2;
  235.   gint x3, y3, x4, y4;
  236.   gint Val[4];
  237.   gint Cnt[4];
  238.   gint Hist[4][HISTSIZE];
  239.   gpointer pr1, pr2;
  240.   gint progress, max_progress;
  241.   gint *tmp1, *tmp2;
  242.   guchar *guc_tmp1;
  243.  
  244.   /*  get the selection bounds  */
  245.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  246.   progress = 0;
  247.   max_progress = (x2 - x1) * (y2 - y1);
  248.  
  249.   width = drawable->width;
  250.   height = drawable->height;
  251.   bytes = drawable->bpp;
  252.  
  253.   n = (int) ovals.mask_size / 2;
  254.  
  255.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  256.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  257.  
  258.   for (pr1 = gimp_pixel_rgns_register (1, &dest_rgn);
  259.        pr1 != NULL;
  260.        pr1 = gimp_pixel_rgns_process (pr1))
  261.     {
  262.       dest_row = dest_rgn.data;
  263.  
  264.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  265.     {
  266.       dest = dest_row;
  267.  
  268.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  269.         {
  270.           memset(Cnt, 0, sizeof(Cnt));
  271.           memset(Val, 0, sizeof(Val));
  272.           memset(Hist, 0, sizeof(Hist));
  273.  
  274.           x3 = CLAMP ((x - n), x1, x2);
  275.           y3 = CLAMP ((y - n), y1, y2);
  276.           x4 = CLAMP ((x + n + 1), x1, x2);
  277.           y4 = CLAMP ((y + n + 1), y1, y2);
  278.  
  279.           gimp_pixel_rgn_init (&src_rgn, drawable,
  280.                    x3, y3, (x4 - x3), (y4 - y3), FALSE, FALSE);
  281.  
  282.           for (pr2 = gimp_pixel_rgns_register (1, &src_rgn);
  283.            pr2 != NULL;
  284.            pr2 = gimp_pixel_rgns_process (pr2))
  285.         {
  286.           src_row = src_rgn.data;
  287.           
  288.           for (yy = 0; yy < src_rgn.h; yy++)
  289.             {
  290.               src = src_row;
  291.               
  292.               for (xx = 0; xx < src_rgn.w; xx++)
  293.             {
  294.               for (b = 0,
  295.                  tmp1 = Val,
  296.                  tmp2 = Cnt,
  297.                  guc_tmp1 = src;
  298.                    b < bytes;
  299.                    b++, tmp1++, tmp2++, guc_tmp1++)
  300.                 {
  301.                   if ((c = ++Hist[b][*guc_tmp1]) > *tmp2)
  302.                 {
  303.                   *tmp1 = *guc_tmp1;
  304.                   *tmp2 = c;
  305.                 }
  306.                 }
  307.               
  308.               src += src_rgn.bpp;
  309.             }
  310.               
  311.               src_row += src_rgn.rowstride;
  312.             }
  313.         }
  314.           
  315.           for (b = 0, tmp1 = Val; b < bytes; b++)
  316.         *dest++ = *tmp1++;
  317.         }
  318.       
  319.       dest_row += dest_rgn.rowstride;
  320.     }
  321.       
  322.       progress += dest_rgn.w * dest_rgn.h;
  323.       gimp_progress_update ((double) progress / (double) max_progress);
  324.     }
  325.   
  326.   /*  update the oil-painted region  */
  327.   gimp_drawable_flush (drawable);
  328.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  329.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  330. }
  331.  
  332. /*
  333.  * For each RGB channel, replace the pixel at (x,y) with the
  334.  * value that occurs most often in the n x n chunk centered
  335.  * at (x,y). Histogram is based on intensity.
  336.  */
  337. static void
  338. oilify_intensity (GimpDrawable *drawable)
  339. {
  340.   GimpPixelRgn src_rgn, dest_rgn;
  341.   gint bytes;
  342.   gint width, height;
  343.   guchar *src_row, *src;
  344.   guchar *dest_row, *dest;
  345.   gint x, y, c, b, xx, yy, n;
  346.   gint x1, y1, x2, y2;
  347.   gint x3, y3, x4, y4;
  348.   gint Val[4];
  349.   gint Cnt;
  350.   gint Hist[HISTSIZE];
  351.   gpointer pr1, pr2;
  352.   gint progress, max_progress;
  353.   gint *tmp1;
  354.   guchar *guc_tmp1;
  355.  
  356.   /*  get the selection bounds  */
  357.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  358.   progress = 0;
  359.   max_progress = (x2 - x1) * (y2 - y1);
  360.   
  361.   width = drawable->width;
  362.   height = drawable->height;
  363.   bytes = drawable->bpp;
  364.   
  365.   n = (int) ovals.mask_size / 2;
  366.   
  367.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  368.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  369.   
  370.   for (pr1 = gimp_pixel_rgns_register (1, &dest_rgn);
  371.        pr1 != NULL;
  372.        pr1 = gimp_pixel_rgns_process (pr1))
  373.     {
  374.       dest_row = dest_rgn.data;
  375.       
  376.       for (y = dest_rgn.y; y < (dest_rgn.y + dest_rgn.h); y++)
  377.     {
  378.       dest = dest_row;
  379.       
  380.       for (x = dest_rgn.x; x < (dest_rgn.x + dest_rgn.w); x++)
  381.         {
  382.           Cnt = 0;
  383.           memset(Val, 0, sizeof(Val));
  384.           memset(Hist, 0, sizeof(Hist));
  385.  
  386.           x3 = CLAMP ((x - n), x1, x2);
  387.           y3 = CLAMP ((y - n), y1, y2);
  388.           x4 = CLAMP ((x + n + 1), x1, x2);
  389.           y4 = CLAMP ((y + n + 1), y1, y2);
  390.           
  391.           gimp_pixel_rgn_init (&src_rgn, drawable,
  392.                    x3, y3, (x4 - x3), (y4 - y3), FALSE, FALSE);
  393.           
  394.           for (pr2 = gimp_pixel_rgns_register (1, &src_rgn);
  395.            pr2 != NULL;
  396.            pr2 = gimp_pixel_rgns_process (pr2))
  397.         {
  398.           src_row = src_rgn.data;
  399.           
  400.           for (yy = 0; yy < src_rgn.h; yy++)
  401.             {
  402.               src = src_row;
  403.               
  404.               for (xx = 0; xx < src_rgn.w; xx++)
  405.             {
  406.               if ((c = ++Hist[INTENSITY(src)]) > Cnt)
  407.                 {
  408.                   Cnt = c;
  409.                   for (b = 0,
  410.                      tmp1 = Val,
  411.                      guc_tmp1 = src;
  412.                    b < bytes;
  413.                    b++, tmp1++, guc_tmp1++)
  414.                 *tmp1 = *guc_tmp1;
  415.                 }
  416.               
  417.               src += src_rgn.bpp;
  418.             }
  419.               
  420.               src_row += src_rgn.rowstride;
  421.             }
  422.         }
  423.           
  424.           for (b = 0, tmp1 = Val; b < bytes; b++)
  425.         *dest++ = *tmp1++;
  426.         }
  427.       
  428.       dest_row += dest_rgn.rowstride;
  429.     }
  430.       
  431.       progress += dest_rgn.w * dest_rgn.h;
  432.       if ((progress % 5) == 0)
  433.     gimp_progress_update ((double) progress / (double) max_progress);
  434.     }
  435.   
  436.   /*  update the oil-painted region  */
  437.   gimp_drawable_flush (drawable);
  438.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  439.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  440. }
  441.  
  442. static gint
  443. oilify_dialog (void)
  444. {
  445.   GtkWidget *dlg;
  446.   GtkWidget *frame;
  447.   GtkWidget *table;
  448.   GtkWidget *toggle;
  449.   GtkObject *adj;
  450.  
  451.   gimp_ui_init ("oilify", FALSE);
  452.  
  453.   dlg = gimp_dialog_new (_("Oilify"), "oilify",
  454.              gimp_standard_help_func, "filters/oilify.html",
  455.              GTK_WIN_POS_MOUSE,
  456.              FALSE, TRUE, FALSE,
  457.  
  458.              _("OK"), oilify_ok_callback,
  459.              NULL, NULL, NULL, TRUE, FALSE,
  460.              _("Cancel"), gtk_widget_destroy,
  461.              NULL, 1, NULL, FALSE, TRUE,
  462.  
  463.              NULL);
  464.  
  465.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  466.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  467.                       NULL);
  468.  
  469.   /*  parameter settings  */
  470.   frame = gtk_frame_new (_("Parameter Settings"));
  471.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  472.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  473.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  474.  
  475.   table = gtk_table_new (2, 3, FALSE);
  476.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  477.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  478.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  479.   gtk_container_add (GTK_CONTAINER (frame), table);
  480.  
  481.   toggle = gtk_check_button_new_with_label (_("Use Intensity Algorithm"));
  482.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 3, 0, 1, GTK_FILL, 0, 0, 0);
  483.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  484.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  485.                       &ovals.mode);
  486.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), ovals.mode);
  487.   gtk_widget_show (toggle);
  488.  
  489.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  490.                   _("Mask Size:"), SCALE_WIDTH, 0,
  491.                   ovals.mask_size, 3.0, 50.0, 1.0, 5.0, 0,
  492.                   TRUE, 0, 0,
  493.                   NULL, NULL);
  494.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  495.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  496.               &ovals.mask_size);
  497.  
  498.   gtk_widget_show (frame);
  499.   gtk_widget_show (table);
  500.   gtk_widget_show (dlg);
  501.  
  502.   gtk_main ();
  503.   gdk_flush ();
  504.  
  505.   return oint.run;
  506. }
  507.  
  508. static void
  509. oilify_ok_callback (GtkWidget *widget,
  510.                     gpointer   data)
  511. {
  512.   oint.run = TRUE;
  513.  
  514.   gtk_widget_destroy (GTK_WIDGET (data));
  515. }
  516.