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 / smooth_palette.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-01  |  11.5 KB  |  461 lines

  1. /*
  2.    smooth palette - derive smooth palette from image
  3.    Copyright (C) 1997  Scott Draves <spot@cs.cmu.edu>
  4.  
  5.    The GIMP -- an image manipulation program
  6.    Copyright (C) 1995 Spencer Kimball and Peter Mattis
  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. #include "config.h"
  24.  
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <time.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. /* Declare local functions. */
  39. static void query  (void);
  40. static void run    (gchar   *name,
  41.             gint     nparams,
  42.             GimpParam  *param,
  43.             gint    *nreturn_vals,
  44.             GimpParam **return_vals);
  45.  
  46. static gint   dialog (void);
  47.  
  48. static gint32 doit   (GimpDrawable *drawable,
  49.               gint32    *layer_id);
  50.  
  51. GimpPlugInInfo PLUG_IN_INFO =
  52. {
  53.   NULL,  /* init_proc  */
  54.   NULL,  /* quit_proc  */
  55.   query, /* query_proc */
  56.   run,   /* run_proc   */
  57. };
  58.  
  59. gboolean run_flag = FALSE;
  60.  
  61. MAIN ()
  62.  
  63. static void
  64. query (void)
  65. {
  66.   static GimpParamDef args[] =
  67.   {
  68.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  69.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  70.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  71.     { GIMP_PDB_INT32, "width", "Width" },
  72.     { GIMP_PDB_INT32, "height", "Height" },
  73.     { GIMP_PDB_INT32, "ntries", "Search Time" },
  74.     { GIMP_PDB_INT32, "show_image","Show Image?" }
  75.   };
  76.   static gint nargs = sizeof (args) / sizeof (args[0]);
  77.  
  78.   gimp_install_procedure ("plug_in_smooth_palette",
  79.               "derive smooth palette from image",
  80.               "help!",
  81.               "Scott Draves",
  82.               "Scott Draves",
  83.               "1997",
  84.               N_("<Image>/Filters/Colors/Smooth Palette..."),
  85.               "RGB*",
  86.               GIMP_PLUGIN,
  87.               nargs, 0,
  88.               args, NULL);
  89. }
  90.  
  91. static struct
  92. {
  93.   gint width;
  94.   gint height;
  95.   gint ntries;
  96.   gint try_size;
  97.   gint show_image;
  98. } config =
  99. {
  100.   256,
  101.   64,
  102.   50,
  103.   10000,
  104.   1
  105. };
  106.  
  107. static void
  108. run (gchar   *name,
  109.      gint     nparams,
  110.      GimpParam  *param,
  111.      gint    *nreturn_vals,
  112.      GimpParam **return_vals)
  113. {
  114.   static GimpParam values[3];
  115.   GimpRunModeType run_mode;
  116.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  117.   GimpDrawable *drawable;
  118.  
  119.   run_mode = param[0].data.d_int32;
  120.  
  121.   *nreturn_vals = 3;
  122.   *return_vals = values;
  123.  
  124.   values[0].type = GIMP_PDB_STATUS;
  125.   values[0].data.d_status = status;
  126.   values[1].type = GIMP_PDB_IMAGE;
  127.   values[2].type = GIMP_PDB_LAYER;
  128.  
  129.   switch (run_mode)
  130.     {
  131.     case GIMP_RUN_INTERACTIVE:
  132.       INIT_I18N_UI();
  133.       gimp_get_data ("plug_in_smooth_palette", &config);
  134.       if (! dialog ())
  135.     return;
  136.       break;
  137.  
  138.     case GIMP_RUN_NONINTERACTIVE:
  139.       INIT_I18N();
  140.       if (nparams != 7)
  141.     {
  142.       status = GIMP_PDB_CALLING_ERROR;
  143.     }
  144.       else
  145.     {
  146.       config.width      = param[3].data.d_int32;
  147.       config.height     = param[4].data.d_int32;
  148.       config.ntries     = param[5].data.d_int32;
  149.       config.show_image = param[6].data.d_int32 ? TRUE : FALSE;
  150.     }
  151.  
  152.       if (status == GIMP_PDB_SUCCESS && 
  153.       ((config.width <= 0) || (config.height <= 0) || config.ntries <= 0))
  154.     status = GIMP_PDB_CALLING_ERROR;
  155.  
  156.       break;
  157.  
  158.     case GIMP_RUN_WITH_LAST_VALS:
  159.       INIT_I18N();
  160.       /*  Possibly retrieve data  */
  161.       gimp_get_data ("plug_in_smooth_palette", &config);
  162.       break;
  163.  
  164.     default:
  165.       break;
  166.     }
  167.  
  168.   if (status == GIMP_PDB_SUCCESS)
  169.     {
  170.       drawable = gimp_drawable_get (param[2].data.d_drawable);
  171.       if (gimp_drawable_is_rgb (drawable->id))
  172.     {
  173.       gimp_progress_init (_("Deriving smooth palette..."));
  174.       gimp_tile_cache_ntiles (2 * (drawable->width + 1) /
  175.                   gimp_tile_width ());
  176.       values[1].data.d_image = doit (drawable, &values[2].data.d_layer);
  177.       if (run_mode == GIMP_RUN_INTERACTIVE)
  178.         gimp_set_data ("plug_in_smooth_palette", &config, sizeof (config));
  179.       if (config.show_image)
  180.         gimp_display_new (values[1].data.d_image);
  181.     }
  182.       else
  183.     status = GIMP_PDB_EXECUTION_ERROR;
  184.       gimp_drawable_detach (drawable);
  185.   }
  186.  
  187.   values[0].data.d_status = status;
  188. }
  189.  
  190. #define R (rand())
  191.  
  192. static long
  193. pix_diff (guchar *pal,
  194.       gint    bpp,
  195.       gint    i,
  196.       gint    j)
  197. {
  198.   glong r = 0;
  199.   gint k;
  200.  
  201.   for (k = 0; k < bpp; k++)
  202.     {
  203.       int p1 = pal[j * bpp + k];
  204.       int p2 = pal[i * bpp + k];
  205.       r += (p1 - p2) * (p1 - p2);
  206.     }
  207.  
  208.   return r;
  209. }
  210.  
  211. static void
  212. pix_swap (guchar *pal,
  213.       gint    bpp,
  214.       gint    i,
  215.       gint    j)
  216. {
  217.   gint k;
  218.  
  219.   for (k = 0; k < bpp; k++)
  220.     {
  221.       guchar t = pal[j * bpp + k];
  222.       pal[j * bpp + k] = pal[i * bpp + k];
  223.       pal[i * bpp + k] = t;
  224.     }
  225. }
  226.  
  227. static gint32
  228. doit (GimpDrawable *drawable,
  229.       gint32    *layer_id)
  230. {
  231.   gint32     new_image_id;
  232.   GimpDrawable *new_layer;
  233.   gint       psize, i, j;
  234.   guchar    *pal;
  235.   gint       bpp = drawable->bpp;
  236.   GimpPixelRgn  pr;
  237.  
  238.   srand(time(0));
  239.  
  240.   new_image_id = gimp_image_new (config.width, config.height, GIMP_RGB);
  241.   *layer_id = gimp_layer_new (new_image_id, _("Background"),
  242.                   config.width, config.height,
  243.                   gimp_drawable_type (drawable->id),
  244.                   100, GIMP_NORMAL_MODE);
  245.   gimp_image_add_layer (new_image_id, *layer_id, 0);
  246.   new_layer = gimp_drawable_get (*layer_id);
  247.  
  248.   psize = config.width;
  249.  
  250.   pal = g_malloc (psize * bpp);
  251.  
  252.   gimp_pixel_rgn_init (&pr, drawable, 0, 0, drawable->width,
  253.                drawable->height,
  254.                FALSE, FALSE);
  255.  
  256.   /* get initial palette */
  257.   for (i = 0; i < psize; i++)
  258.     {
  259.       gint x = R % drawable->width;
  260.       gint y = R % drawable->height;
  261.  
  262.       gimp_pixel_rgn_get_pixel (&pr, pal + bpp * i, x, y);
  263.     }
  264.  
  265.   /* reorder */
  266.   if (1)
  267.     {
  268.       guchar  *pal_best = g_malloc (psize * bpp);
  269.       guchar  *original = g_malloc (psize * bpp);
  270.       gdouble  len_best = 0;
  271.       gint     try;
  272.  
  273.       memcpy (pal_best, pal, bpp * psize);
  274.       memcpy (original, pal, bpp * psize);
  275.  
  276.       for (try = 0; try < config.ntries; try++)
  277.     {
  278.       gdouble len;
  279.  
  280.       if (!(try%5))
  281.         gimp_progress_update (try / (double) config.ntries);
  282.       memcpy (pal, original, bpp * psize);
  283.  
  284.       /* scramble */
  285.       for (i = 1; i < psize; i++)
  286.         pix_swap (pal, bpp, i, R % psize);
  287.  
  288.       /* measure */
  289.       len = 0.0;
  290.       for (i = 1; i < psize; i++)
  291.         len += pix_diff (pal, bpp, i, i-1);
  292.  
  293.       /* improve */
  294.       for (i = 0; i < config.try_size; i++)
  295.         {
  296.           gint  i0 = 1 + (R % (psize-2));
  297.           gint  i1 = 1 + (R % (psize-2));
  298.           glong as_is, swapd;
  299.  
  300.           if (1 == (i0 - i1))
  301.         {
  302.           as_is = (pix_diff (pal, bpp, i1 - 1, i1) +
  303.                pix_diff (pal, bpp, i0, i0 + 1));
  304.           swapd = (pix_diff (pal, bpp, i1 - 1, i0) +
  305.                pix_diff (pal, bpp, i1, i0 + 1));
  306.         }
  307.           else if (1 == (i1 - i0))
  308.         {
  309.           as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
  310.                pix_diff (pal, bpp, i1, i1 + 1));
  311.           swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
  312.                pix_diff (pal, bpp, i0, i1 + 1));
  313.         }
  314.           else
  315.         {
  316.           as_is = (pix_diff (pal, bpp, i0, i0 + 1) +
  317.                pix_diff (pal, bpp, i0, i0 - 1) +
  318.                pix_diff (pal, bpp, i1, i1 + 1) +
  319.                pix_diff (pal, bpp, i1, i1 - 1));
  320.           swapd = (pix_diff (pal, bpp, i1, i0 + 1) +
  321.                pix_diff (pal, bpp, i1, i0 - 1) +
  322.                pix_diff (pal, bpp, i0, i1 + 1) +
  323.                pix_diff (pal, bpp, i0, i1 - 1));
  324.         }
  325.           if (swapd < as_is)
  326.         {
  327.           pix_swap (pal, bpp, i0, i1);
  328.           len += swapd - as_is;
  329.         }
  330.         }
  331.       /* best? */
  332.       if (0 == try || len < len_best)
  333.         {
  334.           memcpy (pal_best, pal, bpp * psize);
  335.           len_best = len;
  336.         }
  337.     }
  338.       memcpy (pal, pal_best, bpp * psize);
  339.       g_free (pal_best);
  340.       g_free (original);
  341.       /* clean */
  342.       for (i = 1; i < 4 * psize; i++)
  343.     {
  344.       glong as_is, swapd;
  345.       gint i0 = 1 + R % (psize - 2);
  346.       gint i1 = i0 + 1;
  347.  
  348.       as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
  349.            pix_diff (pal, bpp, i1, i1 + 1));
  350.       swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
  351.            pix_diff (pal, bpp, i0, i1 + 1));
  352.       if (swapd < as_is)
  353.         {
  354.           pix_swap (pal, bpp, i0, i1);
  355.           len_best += swapd - as_is;
  356.         }
  357.     }
  358.     }
  359.  
  360.   /* store smooth palette */
  361.   gimp_pixel_rgn_init (&pr, new_layer, 0, 0,
  362.                config.width, config.height,
  363.                TRUE, TRUE);
  364.   for (j = 0; j < config.height; j++)
  365.     for (i = 0; i < config.width; i++)
  366.       gimp_pixel_rgn_set_pixel (&pr, pal + bpp * i, i, j);
  367.   g_free (pal);
  368.  
  369.   gimp_drawable_flush (new_layer);
  370.   gimp_drawable_merge_shadow (new_layer->id, TRUE);
  371.   gimp_drawable_update(new_layer->id, 0, 0,
  372.                config.width, config.height);
  373.  
  374.   return new_image_id;
  375. }
  376.  
  377.  
  378. static void
  379. ok_callback (GtkWidget *widget,
  380.          gpointer   data)
  381. {
  382.   run_flag = TRUE;
  383.  
  384.   gtk_widget_destroy (GTK_WIDGET (data));
  385. }
  386.  
  387. static gint
  388. dialog (void)
  389. {
  390.   GtkWidget *dlg;
  391.   GtkWidget *frame;
  392.   GtkWidget *table;
  393.   GtkWidget *spinbutton;
  394.   GtkObject *adj;
  395.  
  396.   gimp_ui_init ("smooth_palette", FALSE);
  397.  
  398.   dlg = gimp_dialog_new (_("Smooth Palette"), "smooth_palette",
  399.              gimp_standard_help_func, "filters/smooth_palette.html",
  400.              GTK_WIN_POS_MOUSE,
  401.              FALSE, TRUE, FALSE,
  402.  
  403.              _("OK"), ok_callback,
  404.              NULL, NULL, NULL, TRUE, FALSE,
  405.              _("Cancel"), gtk_widget_destroy,
  406.              NULL, 1, NULL, FALSE, TRUE,
  407.  
  408.              NULL);
  409.  
  410.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  411.               GTK_SIGNAL_FUNC (gtk_main_quit),
  412.               NULL);
  413.  
  414.   frame = gtk_frame_new (_("Parameter Settings"));
  415.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  416.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, FALSE, 0);
  417.   gtk_widget_show (frame);
  418.  
  419.   table = gtk_table_new (3, 2, FALSE);
  420.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  421.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  422.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  423.   gtk_container_add (GTK_CONTAINER (frame), table);
  424.   gtk_widget_show (table);
  425.  
  426.  
  427.   spinbutton = gimp_spin_button_new (&adj, config.width,
  428.                      1, GIMP_MAX_IMAGE_SIZE, 1, 10, 0, 1, 0);
  429.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  430.                  _("Width:"), 1.0, 0.5,
  431.                  spinbutton, 1, FALSE);
  432.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  433.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  434.               &config.width);
  435.  
  436.   spinbutton = gimp_spin_button_new (&adj, config.height,
  437.                      1, GIMP_MAX_IMAGE_SIZE, 1, 10, 0, 1, 0);
  438.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  439.                  _("Height:"), 1.0, 0.5,
  440.                  spinbutton, 1, FALSE);
  441.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  442.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  443.               &config.height);
  444.  
  445.   spinbutton = gimp_spin_button_new (&adj, config.ntries,
  446.                      1, 1024, 1, 10, 0, 1, 0);
  447.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
  448.                  _("Search Time:"), 1.0, 0.5,
  449.                  spinbutton, 1, FALSE);
  450.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  451.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  452.               &config.ntries);
  453.  
  454.   gtk_widget_show (dlg);
  455.  
  456.   gtk_main ();
  457.   gdk_flush ();
  458.  
  459.   return run_flag;
  460. }
  461.