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

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Eiichi Takamori
  4.  * Copyright (C) 1996, 1997 Torsten Martinsen
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  */
  20.  
  21. /*
  22.  * This plug-in creates a black-and-white 'engraved' version of an image.
  23.  * Much of the code is stolen from the Pixelize plug-in.
  24.  */
  25.  
  26. #include "config.h"
  27.  
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30.  
  31. #include <gtk/gtk.h>
  32.  
  33. #include <libgimp/gimp.h>
  34. #include <libgimp/gimpui.h>
  35.  
  36. #include "libgimp/stdplugins-intl.h"
  37.  
  38.  
  39. /* Some useful macros */
  40.  
  41. #define SCALE_WIDTH     125
  42. #define TILE_CACHE_SIZE  16
  43.  
  44. typedef struct
  45. {
  46.   gint height;
  47.   gint limit;
  48. } EngraveValues;
  49.  
  50. typedef struct
  51. {
  52.   gint run;
  53. } EngraveInterface;
  54.  
  55. static void query (void);
  56. static void run   (gchar   *name,
  57.            gint     nparams,
  58.            GimpParam  *param,
  59.            gint    *nreturn_vals,
  60.            GimpParam **return_vals);
  61.  
  62. static gint engrave_dialog      (void);
  63. static void engrave_ok_callback (GtkWidget *widget,
  64.                  gpointer   data);
  65.  
  66. static void engrave       (GimpDrawable *drawable);
  67. static void engrave_large (GimpDrawable *drawable,
  68.                gint       height,
  69.                gint       limit);
  70. static void engrave_small (GimpDrawable *drawable,
  71.                gint       height,
  72.                gint       limit,
  73.                gint       tile_width);
  74. static void engrave_sub   (gint       height,
  75.                gint       limit,
  76.                gint       bpp,
  77.                gint       color_n);
  78.  
  79. GimpPlugInInfo PLUG_IN_INFO =
  80. {
  81.   NULL,  /* init_proc  */
  82.   NULL,  /* quit_proc  */
  83.   query, /* query_proc */
  84.   run,   /* run_proc   */
  85. };
  86.  
  87. static EngraveValues pvals =
  88. {
  89.   10
  90. };
  91.  
  92. static EngraveInterface pint =
  93. {
  94.   FALSE            /* run */
  95. };
  96.  
  97.  
  98. MAIN ()
  99.  
  100. static void
  101. query (void)
  102. {
  103.   static GimpParamDef args[] =
  104.   {
  105.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  106.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  107.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  108.     { GIMP_PDB_INT32, "height", "Resolution in pixels" },
  109.     { GIMP_PDB_INT32, "limit", "If true, limit line width" }
  110.   };
  111.   static gint nargs = sizeof (args) / sizeof (args[0]);
  112.  
  113.   gimp_install_procedure ("plug_in_engrave",
  114.               "Engrave the contents of the specified drawable",
  115.               "Creates a black-and-white 'engraved' version of an image as seen in old illustrations",
  116.               "Spencer Kimball & Peter Mattis, Eiichi Takamori, Torsten Martinsen",
  117.               "Spencer Kimball & Peter Mattis, Eiichi Takamori, Torsten Martinsen",
  118.               "1995,1996,1997",
  119.               N_("<Image>/Filters/Distorts/Engrave..."),
  120.               "RGBA, GRAYA",
  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.   run_mode = param[0].data.d_int32;
  139.  
  140.   *nreturn_vals = 1;
  141.   *return_vals = values;
  142.  
  143.   values[0].type = GIMP_PDB_STATUS;
  144.   values[0].data.d_status = status;
  145.  
  146.   /*  Get the specified drawable  */
  147.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  148.  
  149.   switch (run_mode)
  150.     {
  151.     case GIMP_RUN_INTERACTIVE:
  152.       INIT_I18N_UI();
  153.       /*  Possibly retrieve data  */
  154.       gimp_get_data ("plug_in_engrave", &pvals);
  155.  
  156.       /*  First acquire information with a dialog  */
  157.       if (!engrave_dialog ())
  158.     {
  159.       gimp_drawable_detach (drawable);
  160.       return;
  161.     }
  162.       break;
  163.  
  164.     case GIMP_RUN_NONINTERACTIVE:
  165.       INIT_I18N();
  166.       /*  Make sure all the arguments are there!  */
  167.       if (nparams != 5)
  168.     status = GIMP_PDB_CALLING_ERROR;
  169.       if (status == GIMP_PDB_SUCCESS)
  170.     {
  171.       pvals.height = param[3].data.d_int32;
  172.       pvals.limit  = (param[4].data.d_int32) ? TRUE : FALSE;
  173.     }
  174.       if ((status == GIMP_PDB_SUCCESS) &&
  175.       pvals.height < 0)
  176.     status = GIMP_PDB_CALLING_ERROR;
  177.       break;
  178.  
  179.     case GIMP_RUN_WITH_LAST_VALS:
  180.       INIT_I18N();
  181.       /*  Possibly retrieve data  */
  182.       gimp_get_data ("plug_in_engrave", &pvals);
  183.       break;
  184.  
  185.     default:
  186.       break;
  187.     }
  188.  
  189.   if (status == GIMP_PDB_SUCCESS)
  190.     {
  191.       gimp_progress_init (_("Engraving..."));
  192.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  193.  
  194.       engrave (drawable);
  195.  
  196.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  197.     gimp_displays_flush ();
  198.  
  199.       /*  Store data  */
  200.       if (run_mode == GIMP_RUN_INTERACTIVE)
  201.     gimp_set_data ("plug_in_engrave", &pvals, sizeof (EngraveValues));
  202.     }
  203.   values[0].data.d_status = status;
  204.  
  205.   gimp_drawable_detach (drawable);
  206. }
  207.  
  208. static gint
  209. engrave_dialog (void)
  210. {
  211.   GtkWidget *dlg;
  212.   GtkWidget *frame;
  213.   GtkWidget *table;
  214.   GtkWidget *toggle;
  215.   GtkObject *adj;
  216.  
  217.   gimp_ui_init ("engrave", FALSE);
  218.  
  219.   dlg = gimp_dialog_new (_("Engrave"), "engrave",
  220.              gimp_standard_help_func, "filters/engrave.html",
  221.              GTK_WIN_POS_MOUSE,
  222.              FALSE, TRUE, FALSE,
  223.  
  224.              _("OK"), engrave_ok_callback,
  225.              NULL, NULL, NULL, TRUE, FALSE,
  226.              _("Cancel"), gtk_widget_destroy,
  227.              NULL, 1, NULL, FALSE, TRUE,
  228.  
  229.              NULL);
  230.  
  231.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  232.               GTK_SIGNAL_FUNC (gtk_main_quit),
  233.               NULL);
  234.  
  235.   /*  parameter settings  */
  236.   frame = gtk_frame_new (_("Parameter Settings"));
  237.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  238.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  239.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  240.  
  241.   table = gtk_table_new (2, 3, FALSE);
  242.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  243.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  244.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  245.   gtk_container_add (GTK_CONTAINER (frame), table);
  246.  
  247.   toggle = gtk_check_button_new_with_label (_("Limit Line Width"));
  248.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 3, 0, 1, GTK_FILL, 0, 0, 0);
  249.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  250.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  251.               &pvals.limit);
  252.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pvals.limit);
  253.   gtk_widget_show (toggle);
  254.  
  255.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  256.                   _("Height:"), SCALE_WIDTH, 0,
  257.                   pvals.height, 2.0, 16.0, 1.0, 4.0, 0,
  258.                   TRUE, 0, 0,
  259.                   NULL, NULL);
  260.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  261.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  262.               &pvals.height);
  263.  
  264.   gtk_widget_show (frame);
  265.   gtk_widget_show (table);
  266.   gtk_widget_show (dlg);
  267.  
  268.   gtk_main ();
  269.   gdk_flush ();
  270.  
  271.   return pint.run;
  272. }
  273.  
  274. /*  Engrave interface functions  */
  275.  
  276. static void
  277. engrave_ok_callback (GtkWidget *widget,
  278.              gpointer   data)
  279. {
  280.   pint.run = TRUE;
  281.  
  282.   gtk_widget_destroy (GTK_WIDGET (data));
  283. }
  284.  
  285. static void
  286. engrave (GimpDrawable *drawable)
  287. {
  288.   gint tile_width;
  289.   gint height;
  290.   gint limit;
  291.  
  292.   tile_width = gimp_tile_width();
  293.   height = (gint) pvals.height;
  294.   limit = (gint) pvals.limit;
  295.   if (height >= tile_width)
  296.     engrave_large (drawable, height, limit);
  297.   else
  298.     engrave_small (drawable, height, limit, tile_width);
  299. }
  300.  
  301. static void
  302. engrave_large (GimpDrawable *drawable,
  303.            gint       height,
  304.            gint       limit)
  305. {
  306.   GimpPixelRgn src_rgn, dest_rgn;
  307.   guchar *src_row, *dest_row;
  308.   guchar *src, *dest;
  309.   gulong *average;
  310.   gint row, col, b, bpp;
  311.   gint x, y, y_step, inten, v;
  312.   gulong count;
  313.   gint x1, y1, x2, y2;
  314.   gint progress, max_progress;
  315.   gpointer pr;
  316.  
  317.   gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2);
  318.  
  319.   if (gimp_drawable_is_rgb(drawable->id))
  320.     bpp = 3;
  321.   else
  322.     bpp = 1;
  323.   average = g_new(gulong, bpp);
  324.  
  325.   /* Initialize progress */
  326.   progress = 0;
  327.   max_progress = 2 * (x2 - x1) * (y2 - y1);
  328.  
  329.   for (y = y1; y < y2; y += height - (y % height))
  330.     {
  331.       for (x = x1; x < x2; ++x)
  332.     {
  333.       y_step = height - (y % height);
  334.       y_step = MIN(y_step, x2 - x);
  335.  
  336.       gimp_pixel_rgn_init(&src_rgn, drawable, x, y, 1, y_step, FALSE, FALSE);
  337.       for (b = 0; b < bpp; b++)
  338.         average[b] = 0;
  339.       count = 0;
  340.  
  341.       for (pr = gimp_pixel_rgns_register(1, &src_rgn);
  342.            pr != NULL;
  343.            pr = gimp_pixel_rgns_process(pr))
  344.         {
  345.           src_row = src_rgn.data;
  346.           for (row = 0; row < src_rgn.h; row++)
  347.         {
  348.           src = src_row;
  349.           for (col = 0; col < src_rgn.w; col++)
  350.             {
  351.               for (b = 0; b < bpp; b++)
  352.             average[b] += src[b];
  353.               src += src_rgn.bpp;
  354.               count += 1;
  355.             }
  356.           src_row += src_rgn.rowstride;
  357.         }
  358.           /* Update progress */
  359.           progress += src_rgn.w * src_rgn.h;
  360.           gimp_progress_update((double) progress / (double) max_progress);
  361.         }
  362.  
  363.       if (count > 0)
  364.         for (b = 0; b < bpp; b++)
  365.           average[b] = (guchar) (average[b] / count);
  366.  
  367.       if (bpp < 3)
  368.         inten = average[0]/254.0*height;
  369.       else
  370.         inten = INTENSITY(average[0],
  371.                   average[1],
  372.                   average[2])/254.0*height;
  373.  
  374.       gimp_pixel_rgn_init(&dest_rgn, drawable, x, y, 1, y_step, TRUE, TRUE);
  375.       for (pr = gimp_pixel_rgns_register(1, &dest_rgn);
  376.            pr != NULL;
  377.            pr = gimp_pixel_rgns_process(pr))
  378.         {
  379.           dest_row = dest_rgn.data;
  380.           for (row = 0; row < dest_rgn.h; row++)
  381.         {
  382.           dest = dest_row;
  383.           v = inten > row ? 255 : 0;
  384.           if (limit)
  385.             {
  386.               if (row == 0)
  387.             v = 255;
  388.               else if (row == height-1)
  389.             v = 0;
  390.             }
  391.           for (b = 0; b < bpp; b++)
  392.             dest[b] = v;
  393.           dest_row += dest_rgn.rowstride;
  394.         }
  395.           /* Update progress */
  396.           progress += dest_rgn.w * dest_rgn.h;
  397.           gimp_progress_update((double) progress / (double) max_progress);
  398.         }
  399.     }
  400.     }
  401.  
  402.   g_free(average);
  403.  
  404.   /*  update the engraved region  */
  405.   gimp_drawable_flush(drawable);
  406.   gimp_drawable_merge_shadow(drawable->id, TRUE);
  407.   gimp_drawable_update(drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  408. }
  409.  
  410. typedef struct
  411. {
  412.   gint    x, y, h;
  413.   gint    width;
  414.   guchar *data;
  415. } PixelArea;
  416.  
  417. PixelArea area;
  418.  
  419. static void
  420. engrave_small (GimpDrawable *drawable,
  421.            gint       height,
  422.            gint       limit,
  423.            gint       tile_width)
  424. {
  425.   GimpPixelRgn src_rgn, dest_rgn;
  426.   gint bpp, color_n;
  427.   gint x1, y1, x2, y2;
  428.   gint progress, max_progress;
  429.  
  430.   /*
  431.     For speed efficiency, operates on PixelAreas, whose each width and
  432.     height are less than tile size.
  433.  
  434.     If both ends of area cannot be divided by height ( as
  435.     x1%height != 0 etc.), operates on the remainder pixels.
  436.   */
  437.  
  438.   gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2);
  439.   gimp_pixel_rgn_init(&src_rgn, drawable,
  440.               x1, y1, x2 - x1, y2 - y1, FALSE, FALSE);
  441.   gimp_pixel_rgn_init(&dest_rgn, drawable,
  442.               x1, y1, x2 - x1, y2 - y1, TRUE, TRUE);
  443.  
  444.   /* Initialize progress */
  445.   progress = 0;
  446.   max_progress = (x2 - x1) * (y2 - y1);
  447.  
  448.   bpp = drawable->bpp;
  449.   if (gimp_drawable_is_rgb(drawable->id))
  450.     color_n = 3;
  451.   else
  452.     color_n = 1;
  453.  
  454.   area.width = (tile_width / height) * height;
  455.   area.data = g_new(guchar, (glong) bpp * area.width * area.width);
  456.  
  457.   for (area.y = y1; area.y < y2;
  458.        area.y += area.width - (area.y % area.width))
  459.     {
  460.       area.h = area.width - (area.y % area.width);
  461.       area.h = MIN(area.h, y2 - area.y);
  462.       for (area.x = x1; area.x < x2; ++area.x)
  463.     {
  464.       gimp_pixel_rgn_get_rect(&src_rgn, area.data, area.x, area.y, 1, area.h);
  465.  
  466.       engrave_sub(height, limit, bpp, color_n);
  467.  
  468.       gimp_pixel_rgn_set_rect(&dest_rgn, area.data, area.x, area.y, 1, area.h);
  469.  
  470.       /* Update progress */
  471.       progress += area.h;
  472.       gimp_progress_update((double) progress / (double) max_progress);
  473.     }
  474.     }
  475.  
  476.   g_free(area.data);
  477.  
  478.   /*  update the engraved region  */
  479.   gimp_drawable_flush(drawable);
  480.   gimp_drawable_merge_shadow(drawable->id, TRUE);
  481.   gimp_drawable_update(drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  482. }
  483.  
  484. static void
  485. engrave_sub (gint height,
  486.          gint limit,
  487.          gint bpp,
  488.          gint color_n)
  489. {
  490.   glong average[3];        /* color_n <= 3 */
  491.   gint y, h, inten, v;
  492.   guchar *buf_row, *buf;
  493.   gint row;
  494.   gint rowstride;
  495.   gint count;
  496.   gint i;
  497.  
  498.   /*
  499.     Since there's so many nested FOR's,
  500.     put a few of them here...
  501.   */
  502.  
  503.   rowstride = bpp;
  504.  
  505.   for (y = area.y; y < area.y + area.h; y += height - (y % height))
  506.     {
  507.       h = height - (y % height);
  508.       h = MIN(h, area.y + area.h - y);
  509.  
  510.       for (i = 0; i < color_n; i++)
  511.     average[i] = 0;
  512.       count = 0;
  513.  
  514.       /* Read */
  515.       buf_row = area.data + (y - area.y) * rowstride;
  516.  
  517.       for (row = 0; row < h; row++)
  518.     {
  519.       buf = buf_row;
  520.       for (i = 0; i < color_n; i++)
  521.         average[i] += buf[i];
  522.       count++;
  523.       buf_row += rowstride;
  524.     }
  525.  
  526.       /* Average */
  527.       if (count > 0) 
  528.     for (i = 0; i < color_n; i++)
  529.       average[i] /= count;
  530.  
  531.       if (bpp < 3)
  532.     inten = average[0]/254.0*height;
  533.       else
  534.     inten = INTENSITY(average[0],
  535.               average[1],
  536.               average[2])/254.0*height;
  537.  
  538.       /* Write */
  539.       buf_row = area.data + (y - area.y) * rowstride;
  540.  
  541.       for (row = 0; row < h; row++)
  542.     {
  543.       buf = buf_row;
  544.       v = inten > row ? 255 : 0;
  545.       if (limit)
  546.         {
  547.           if (row == 0)
  548.         v = 255;
  549.           else if (row == height-1)
  550.         v = 0;
  551.         }
  552.       for (i = 0; i < color_n; i++)
  553.         buf[i] = v;
  554.       buf_row += rowstride;
  555.     }
  556.     }
  557. }
  558.