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 / sharpen.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  27.4 KB  |  1,091 lines

  1. /*
  2.  * "$Id: sharpen.c,v 1.23 2000/08/22 01:26:56 neo Exp $"
  3.  *
  4.  *   Sharpen filters for The GIMP -- an image manipulation program
  5.  *
  6.  *   Copyright 1997-1998 Michael Sweet (mike@easysw.com)
  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.  * Contents:
  23.  *
  24.  *   main()                    - Main entry - just call gimp_main()...
  25.  *   query()                   - Respond to a plug-in query...
  26.  *   run()                     - Run the filter...
  27.  *   sharpen()                 - Sharpen an image using a median filter.
  28.  *   sharpen_dialog()          - Popup a dialog window for the filter box size...
  29.  *   preview_init()            - Initialize the preview window...
  30.  *   preview_scroll_callback() - Update the preview when a scrollbar is moved.
  31.  *   preview_update()          - Update the preview window.
  32.  *   preview_exit()            - Free all memory used by the preview window...
  33.  *   dialog_iscale_update()    - Update the value field using the scale.
  34.  *   dialog_ok_callback()      - Start the filter...
  35.  *   gray_filter()             - Sharpen grayscale pixels.
  36.  *   graya_filter()            - Sharpen grayscale+alpha pixels.
  37.  *   rgb_filter()              - Sharpen RGB pixels.
  38.  *   rgba_filter()             - Sharpen RGBA pixels.
  39.  *
  40.  * Revision History:
  41.  *
  42.  *   See ChangeLog
  43.  */
  44.  
  45. #include "config.h"
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <signal.h>
  51.  
  52. #include <gtk/gtk.h>
  53.  
  54. #include <libgimp/gimp.h>
  55. #include <libgimp/gimpui.h>
  56.  
  57. #include "libgimp/stdplugins-intl.h"
  58.  
  59.  
  60. /*
  61.  * Constants...
  62.  */
  63.  
  64. #define PLUG_IN_NAME        "plug_in_sharpen"
  65. #define PLUG_IN_VERSION        "1.4.2 - 3 June 1998"
  66. #define PREVIEW_SIZE        128
  67. #define SCALE_WIDTH        100
  68.  
  69. /*
  70.  * Local functions...
  71.  */
  72.  
  73. static void    query (void);
  74. static void    run   (gchar   *name,
  75.                gint     nparams,
  76.                GimpParam  *param,
  77.                gint    *nreturn_vals,
  78.                GimpParam **returm_vals);
  79.  
  80. static void    compute_luts   (void);
  81. static void    sharpen        (void);
  82.  
  83. static gint    sharpen_dialog (void);
  84.  
  85. static void    dialog_iscale_update (GtkAdjustment *, gint *);
  86. static void    dialog_ok_callback   (GtkWidget *, gpointer);
  87.  
  88. static void    preview_init            (void);
  89. static void    preview_exit            (void);
  90. static void    preview_update          (void);
  91. static void    preview_scroll_callback (void);
  92.  
  93. typedef gint32 intneg;
  94. typedef gint32 intpos;
  95.  
  96. static void    gray_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
  97.                   intneg *neg1, intneg *neg2);
  98. static void    graya_filter (int width, guchar *src, guchar *dst, intneg *neg0,
  99.                   intneg *neg1, intneg *neg2);
  100. static void    rgb_filter   (int width, guchar *src, guchar *dst, intneg *neg0,
  101.                   intneg *neg1, intneg *neg2);
  102. static void    rgba_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
  103.                   intneg *neg1, intneg *neg2);
  104.  
  105.  
  106. /*
  107.  * Globals...
  108.  */
  109.  
  110. GimpPlugInInfo PLUG_IN_INFO =
  111. {
  112.   NULL,  /* init_proc  */
  113.   NULL,  /* quit_proc  */
  114.   query, /* query_proc */
  115.   run    /* run_proc   */
  116. };
  117.  
  118. static GtkWidget *preview;        /* Preview widget */
  119. static gint       preview_width;    /* Width of preview widget */
  120. static gint       preview_height;    /* Height of preview widget */
  121. static gint       preview_x1;        /* Upper-left X of preview */
  122. static gint       preview_y1;        /* Upper-left Y of preview */
  123. static gint       preview_x2;        /* Lower-right X of preview */
  124. static gint       preview_y2;        /* Lower-right Y of preview */
  125. static guchar    *preview_src;        /* Source pixel image */
  126. static intneg    *preview_neg;        /* Negative coefficient pixels */
  127. static guchar    *preview_dst;        /* Destination pixel image */
  128. static guchar    *preview_image;    /* Preview RGB image */
  129. static GtkObject *hscroll_data;        /* Horizontal scrollbar data */
  130. static GtkObject *vscroll_data;        /* Vertical scrollbar data */
  131.  
  132. static GimpDrawable *drawable = NULL;    /* Current image */
  133. static gint       sel_x1;              /* Selection bounds */
  134. static gint       sel_y1;
  135. static gint       sel_x2;
  136. static gint       sel_y2;
  137. static gint       sel_width;        /* Selection width */
  138. static gint       sel_height;        /* Selection height */
  139. static gint       img_bpp;        /* Bytes-per-pixel in image */
  140. static gint       sharpen_percent = 10;    /* Percent of sharpening */
  141. static gint       run_filter = FALSE;    /* True if we should run the filter */
  142.  
  143. static intneg     neg_lut[256];        /* Negative coefficient LUT */
  144. static intpos     pos_lut[256];        /* Positive coefficient LUT */
  145.  
  146.  
  147. MAIN ()
  148.  
  149. static void
  150. query (void)
  151. {
  152.   static GimpParamDef    args[] =
  153.   {
  154.     { GIMP_PDB_INT32,    "run_mode",    "Interactive, non-interactive" },
  155.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  156.     { GIMP_PDB_DRAWABLE,    "drawable",    "Input drawable" },
  157.     { GIMP_PDB_INT32,    "percent",    "Percent sharpening (default = 10)" }
  158.   };
  159.   static gint nargs = sizeof (args) / sizeof (args[0]);
  160.  
  161.   gimp_install_procedure (PLUG_IN_NAME,
  162.               "Sharpen filter, typically used to 'sharpen' a photographic image.",
  163.               "This plug-in selectively performs a convolution filter on an image.",
  164.               "Michael Sweet <mike@easysw.com>",
  165.               "Copyright 1997-1998 by Michael Sweet",
  166.               PLUG_IN_VERSION,
  167.               N_("<Image>/Filters/Enhance/Sharpen..."),
  168.               "RGB*, GRAY*",
  169.               GIMP_PLUGIN,
  170.               nargs, 0,
  171.               args, NULL);
  172. }
  173.  
  174. static void
  175. run (gchar   *name,
  176.      gint     nparams,
  177.      GimpParam  *param,
  178.      gint    *nreturn_vals,
  179.      GimpParam **return_vals)
  180. {
  181.   GimpRunModeType    run_mode;    /* Current run mode */
  182.   GimpPDBStatusType    status;        /* Return status */
  183.   GimpParam    *values;    /* Return values */
  184.  
  185.   /*
  186.    * Initialize parameter data...
  187.    */
  188.  
  189.   status   = GIMP_PDB_SUCCESS;
  190.   run_mode = param[0].data.d_int32;
  191.  
  192.   values = g_new (GimpParam, 1);
  193.  
  194.   values[0].type          = GIMP_PDB_STATUS;
  195.   values[0].data.d_status = status;
  196.  
  197.   *nreturn_vals = 1;
  198.   *return_vals  = values;
  199.  
  200.   /*
  201.    * Get drawable information...
  202.    */
  203.  
  204.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  205.  
  206.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  207.  
  208.   sel_width     = sel_x2 - sel_x1;
  209.   sel_height    = sel_y2 - sel_y1;
  210.   img_bpp       = gimp_drawable_bpp (drawable->id);
  211.  
  212.   /*
  213.    * See how we will run
  214.    */
  215.  
  216.   switch (run_mode)
  217.     {
  218.     case GIMP_RUN_INTERACTIVE:
  219.       INIT_I18N_UI();
  220.       /*
  221.        * Possibly retrieve data...
  222.        */
  223.       gimp_get_data (PLUG_IN_NAME, &sharpen_percent);
  224.  
  225.       /*
  226.        * Get information from the dialog...
  227.        */
  228.       if (!sharpen_dialog ())
  229.     return;
  230.       break;
  231.  
  232.     case GIMP_RUN_NONINTERACTIVE:
  233.       INIT_I18N();
  234.       /*
  235.        * Make sure all the arguments are present...
  236.        */
  237.       if (nparams != 4)
  238.     status = GIMP_PDB_CALLING_ERROR;
  239.       else
  240.     sharpen_percent = param[3].data.d_int32;
  241.       break;
  242.  
  243.     case GIMP_RUN_WITH_LAST_VALS:
  244.       INIT_I18N();
  245.       /*
  246.        * Possibly retrieve data...
  247.        */
  248.       gimp_get_data (PLUG_IN_NAME, &sharpen_percent);
  249.       break;
  250.  
  251.     default:
  252.       status = GIMP_PDB_CALLING_ERROR;
  253.       break;;
  254.     };
  255.  
  256.   /*
  257.    * Sharpen the image...
  258.    */
  259.  
  260.   if (status == GIMP_PDB_SUCCESS)
  261.     {
  262.       if ((gimp_drawable_is_rgb (drawable->id) ||
  263.        gimp_drawable_is_gray (drawable->id)))
  264.     {
  265.       /*
  266.        * Set the tile cache size...
  267.        */
  268.       gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width() - 1) /
  269.                   gimp_tile_width() + 1);
  270.  
  271.       /*
  272.        * Run!
  273.        */
  274.       sharpen ();
  275.  
  276.       /*
  277.        * If run mode is interactive, flush displays...
  278.        */
  279.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  280.         gimp_displays_flush ();
  281.  
  282.       /*
  283.        * Store data...
  284.        */
  285.       if (run_mode == GIMP_RUN_INTERACTIVE)
  286.         gimp_set_data (PLUG_IN_NAME,
  287.                &sharpen_percent, sizeof (sharpen_percent));
  288.     }
  289.       else
  290.     status = GIMP_PDB_EXECUTION_ERROR;
  291.     };
  292.  
  293.   /*
  294.    * Reset the current run status...
  295.    */
  296.   values[0].data.d_status = status;
  297.  
  298.   /*
  299.    * Detach from the drawable...
  300.    */
  301.   gimp_drawable_detach (drawable);
  302. }
  303.  
  304.  
  305. static void
  306. compute_luts (void)
  307. {
  308.   gint i;    /* Looping var */
  309.   gint fact;    /* 1 - sharpness */
  310.  
  311.   fact = 100 - sharpen_percent;
  312.   if (fact < 1)
  313.     fact = 1;
  314.  
  315.   for (i = 0; i < 256; i ++)
  316.     {
  317.       pos_lut[i] = 800 * i / fact;
  318.       neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
  319.     };
  320. }
  321.  
  322. /*
  323.  * 'sharpen()' - Sharpen an image using a convolution filter.
  324.  */
  325.  
  326. static void
  327. sharpen (void)
  328. {
  329.   GimpPixelRgn    src_rgn,    /* Source image region */
  330.         dst_rgn;    /* Destination image region */
  331.   guchar    *src_rows[4],    /* Source pixel rows */
  332.         *src_ptr,    /* Current source pixel */
  333.         *dst_row;    /* Destination pixel row */
  334.   intneg    *neg_rows[4],    /* Negative coefficient rows */
  335.         *neg_ptr;    /* Current negative coefficient */
  336.   gint        i,        /* Looping vars */
  337.         y,        /* Current location in image */
  338.         row,        /* Current row in src_rows */
  339.         count,        /* Current number of filled src_rows */
  340.         width;        /* Byte width of the image */
  341.   void        (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
  342.  
  343.   filter = NULL;
  344.  
  345.   /*
  346.    * Let the user know what we're doing...
  347.    */
  348.   gimp_progress_init( _("Sharpening..."));
  349.  
  350.   /*
  351.    * Setup for filter...
  352.    */
  353.  
  354.   gimp_pixel_rgn_init (&src_rgn, drawable,
  355.                sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE);
  356.   gimp_pixel_rgn_init (&dst_rgn, drawable,
  357.                sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  358.  
  359.   compute_luts ();
  360.  
  361.   width = sel_width * img_bpp;
  362.  
  363.   for (row = 0; row < 4; row ++)
  364.     {
  365.       src_rows[row] = g_new (guchar, width);
  366.       neg_rows[row] = g_new (intneg, width);
  367.     };
  368.  
  369.   dst_row = g_new (guchar, width);
  370.  
  371.   /*
  372.    * Pre-load the first row for the filter...
  373.    */
  374.  
  375.   gimp_pixel_rgn_get_row (&src_rgn, src_rows[0], sel_x1, sel_y1, sel_width);
  376.   for (i = width, src_ptr = src_rows[0], neg_ptr = neg_rows[0];
  377.        i > 0;
  378.        i --, src_ptr ++, neg_ptr ++)
  379.     *neg_ptr = neg_lut[*src_ptr];
  380.  
  381.   row   = 1;
  382.   count = 1;
  383.  
  384.   /*
  385.    * Select the filter...
  386.    */
  387.  
  388.   switch (img_bpp)
  389.     {
  390.     case 1 :
  391.       filter = gray_filter;
  392.       break;
  393.     case 2 :
  394.       filter = graya_filter;
  395.       break;
  396.     case 3 :
  397.       filter = rgb_filter;
  398.       break;
  399.     case 4 :
  400.       filter = rgba_filter;
  401.       break;
  402.     };
  403.  
  404.   /*
  405.    * Sharpen...
  406.    */
  407.  
  408.   for (y = sel_y1; y < sel_y2; y ++)
  409.     {
  410.       /*
  411.        * Load the next pixel row...
  412.        */
  413.  
  414.       if ((y + 1) < sel_y2)
  415.     {
  416.       /*
  417.        * Check to see if our src_rows[] array is overflowing yet...
  418.        */
  419.  
  420.       if (count >= 3)
  421.         count --;
  422.  
  423.       /*
  424.        * Grab the next row...
  425.        */
  426.  
  427.       gimp_pixel_rgn_get_row (&src_rgn, src_rows[row],
  428.                   sel_x1, y + 1, sel_width);
  429.       for (i = width, src_ptr = src_rows[row], neg_ptr = neg_rows[row];
  430.            i > 0;
  431.            i --, src_ptr ++, neg_ptr ++)
  432.         *neg_ptr = neg_lut[*src_ptr];
  433.  
  434.       count ++;
  435.       row = (row + 1) & 3;
  436.     }
  437.       else
  438.     {
  439.       /*
  440.        * No more pixels at the bottom...  Drop the oldest samples...
  441.        */
  442.  
  443.       count --;
  444.     };
  445.  
  446.       /*
  447.        * Now sharpen pixels and save the results...
  448.        */
  449.  
  450.       if (count == 3)
  451.     {
  452.       (* filter) (sel_width, src_rows[(row + 2) & 3], dst_row,
  453.               neg_rows[(row + 1) & 3] + img_bpp,
  454.               neg_rows[(row + 2) & 3] + img_bpp,
  455.               neg_rows[(row + 3) & 3] + img_bpp);
  456.  
  457.       /*
  458.        * Set the row...
  459.        */
  460.  
  461.       gimp_pixel_rgn_set_row (&dst_rgn, dst_row, sel_x1, y, sel_width);
  462.     }
  463.       else if (count == 2)
  464.     {
  465.       if (y == sel_y1)
  466.         gimp_pixel_rgn_set_row (&dst_rgn, src_rows[0], sel_x1, y, sel_width);
  467.       else
  468.         gimp_pixel_rgn_set_row (&dst_rgn, src_rows[2], sel_x1, y, sel_width);
  469.     };
  470.  
  471.       if ((y & 15) == 0)
  472.     gimp_progress_update ((gdouble) (y - sel_y1) / (gdouble) sel_height);
  473.     };
  474.  
  475.   /*
  476.    * OK, we're done.  Free all memory used...
  477.    */
  478.  
  479.   for (row = 0; row < 4; row ++)
  480.     {
  481.       g_free (src_rows[row]);
  482.       g_free (neg_rows[row]);
  483.     };
  484.  
  485.   g_free (dst_row);
  486.  
  487.   /*
  488.    * Update the screen...
  489.    */
  490.  
  491.   gimp_drawable_flush (drawable);
  492.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  493.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height);
  494. }
  495.  
  496.  
  497. /*
  498.  * 'sharpen_dialog()' - Popup a dialog window for the filter box size...
  499.  */
  500.  
  501. static gint
  502. sharpen_dialog (void)
  503. {
  504.   GtkWidget *dialog;
  505.   GtkWidget *vbox;
  506.   GtkWidget *table;
  507.   GtkWidget *abox;
  508.   GtkWidget *ptable;
  509.   GtkWidget *frame;
  510.   GtkWidget *scrollbar;
  511.   GtkObject *adj;
  512.   gchar *title;
  513.  
  514.   gimp_ui_init ("sharpen", TRUE);
  515.  
  516.   title = g_strdup_printf (_("Sharpen - %s"), PLUG_IN_VERSION);
  517.  
  518.   dialog = gimp_dialog_new (title, "sharpen",
  519.                 gimp_standard_help_func, "filters/sharpen.html",
  520.                 GTK_WIN_POS_MOUSE,
  521.                 FALSE, TRUE, FALSE,
  522.  
  523.                 _("OK"), dialog_ok_callback,
  524.                 NULL, NULL, NULL, TRUE, FALSE,
  525.                 _("Cancel"), gtk_widget_destroy,
  526.                 NULL, 1, NULL, FALSE, TRUE,
  527.  
  528.                 NULL);
  529.  
  530.   g_free (title);
  531.  
  532.   gtk_signal_connect (GTK_OBJECT(dialog), "destroy",
  533.               GTK_SIGNAL_FUNC (gtk_main_quit),
  534.               NULL);
  535.  
  536.   /*
  537.    * Top-level table for dialog...
  538.    */
  539.  
  540.   vbox = gtk_vbox_new (FALSE, 4);
  541.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  542.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
  543.                       FALSE, FALSE, 0);
  544.   gtk_widget_show (vbox);
  545.  
  546.   /*
  547.    * Preview window...
  548.    */
  549.  
  550.   frame = gtk_frame_new (_("Preview"));
  551.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  552.   gtk_widget_show (frame);
  553.  
  554.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  555.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  556.   gtk_container_add (GTK_CONTAINER (frame), abox);
  557.   gtk_widget_show (abox);
  558.  
  559.   ptable = gtk_table_new (2, 2, FALSE);
  560.   gtk_container_add (GTK_CONTAINER (abox), ptable);
  561.   gtk_widget_show (ptable);
  562.  
  563.   frame = gtk_frame_new (NULL);
  564.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  565.   gtk_table_attach (GTK_TABLE (ptable), frame, 0, 1, 0, 1, 0, 0, 0, 0);
  566.   gtk_widget_show (frame);
  567.  
  568.   preview_width  = MIN (sel_width, PREVIEW_SIZE);
  569.   preview_height = MIN (sel_height, PREVIEW_SIZE);
  570.  
  571.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  572.   gtk_preview_size (GTK_PREVIEW (preview), preview_width, preview_height);
  573.   gtk_container_add (GTK_CONTAINER (frame), preview);
  574.   gtk_widget_show (preview);
  575.  
  576.   hscroll_data = gtk_adjustment_new (0, 0, sel_width - 1, 1.0,
  577.                      MIN (preview_width, sel_width),
  578.                      MIN (preview_width, sel_width));
  579.  
  580.   gtk_signal_connect (hscroll_data, "value_changed",
  581.               (GtkSignalFunc) preview_scroll_callback,
  582.               NULL);
  583.  
  584.   scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (hscroll_data));
  585.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  586.   gtk_table_attach (GTK_TABLE (ptable), scrollbar, 0, 1, 1, 2,
  587.             GTK_FILL, 0, 0, 0);
  588.   gtk_widget_show (scrollbar);
  589.  
  590.   vscroll_data = gtk_adjustment_new (0, 0, sel_height - 1, 1.0,
  591.                      MIN (preview_height, sel_height),
  592.                      MIN (preview_height, sel_height));
  593.  
  594.   gtk_signal_connect (vscroll_data, "value_changed",
  595.               GTK_SIGNAL_FUNC (preview_scroll_callback),
  596.               NULL);
  597.  
  598.   scrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (vscroll_data));
  599.   gtk_range_set_update_policy (GTK_RANGE (scrollbar), GTK_UPDATE_CONTINUOUS);
  600.   gtk_table_attach (GTK_TABLE (ptable), scrollbar, 1, 2, 0, 1,
  601.             0, GTK_FILL, 0, 0);
  602.   gtk_widget_show (scrollbar);
  603.  
  604.   preview_init ();
  605.  
  606.   /*
  607.    * Sharpness control...
  608.    */
  609.  
  610.   frame = gtk_frame_new (_("Parameter Settings"));
  611.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  612.   gtk_widget_show (frame);
  613.  
  614.   table = gtk_table_new (1, 3, FALSE);
  615.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  616.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  617.   gtk_container_add (GTK_CONTAINER (frame), table);
  618.   gtk_widget_show (table);
  619.  
  620.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  621.                   _("Sharpness:"), SCALE_WIDTH, 0,
  622.                   sharpen_percent, 1, 99, 1, 10, 0,
  623.                   TRUE, 0, 0,
  624.                   NULL, NULL);
  625.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  626.               GTK_SIGNAL_FUNC (dialog_iscale_update),
  627.               &sharpen_percent);
  628.  
  629.   gtk_widget_show (dialog);
  630.  
  631.   preview_update ();
  632.  
  633.   gtk_main ();
  634.   gdk_flush ();
  635.  
  636.   preview_exit ();
  637.  
  638.   return run_filter;
  639. }
  640.  
  641. /*  preview functions  */
  642.  
  643. static void
  644. preview_init (void)
  645. {
  646.   gint width;  /* Byte width of the image */
  647.  
  648.   /*
  649.    * Setup for preview filter...
  650.    */
  651.  
  652.   compute_luts();
  653.  
  654.   width = preview_width * img_bpp;
  655.  
  656.   preview_src   = g_new (guchar, width * preview_height);
  657.   preview_neg   = g_new (intneg, width * preview_height);
  658.   preview_dst   = g_new (guchar, width * preview_height);
  659.   preview_image = g_new (guchar, preview_width * preview_height * 3);
  660.  
  661.   preview_x1 = sel_x1;
  662.   preview_y1 = sel_y1;
  663.   preview_x2 = preview_x1 + preview_width;
  664.   preview_y2 = preview_y1 + preview_height;
  665. }
  666.  
  667. static void
  668. preview_scroll_callback (void)
  669. {
  670.   preview_x1 = sel_x1 + GTK_ADJUSTMENT (hscroll_data)->value;
  671.   preview_y1 = sel_y1 + GTK_ADJUSTMENT (vscroll_data)->value;
  672.   preview_x2 = preview_x1 + MIN (preview_width, sel_width);
  673.   preview_y2 = preview_y1 + MIN (preview_height, sel_height);
  674.  
  675.   preview_update ();
  676. }
  677.  
  678. static void
  679. preview_update (void)
  680. {
  681.   GimpPixelRgn    src_rgn;    /* Source image region */
  682.   guchar    *src_ptr,    /* Current source pixel */
  683.         *dst_ptr,    /* Current destination pixel */
  684.           *image_ptr;    /* Current image pixel */
  685.   intneg    *neg_ptr;    /* Current negative pixel */
  686.   guchar    check;        /* Current check mark pixel */
  687.   gint        i,          /* Looping var */
  688.         x, y,        /* Current location in image */
  689.         width;        /* Byte width of the image */
  690.   void        (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
  691.  
  692.   filter = NULL;
  693.  
  694.   /*
  695.    * Setup for filter...
  696.    */
  697.  
  698.   gimp_pixel_rgn_init (&src_rgn, drawable,
  699.                preview_x1, preview_y1, preview_width, preview_height,
  700.                FALSE, FALSE);
  701.  
  702.   width = preview_width * img_bpp;
  703.  
  704.   /*
  705.    * Load the preview area...
  706.    */
  707.  
  708.   gimp_pixel_rgn_get_rect (&src_rgn, preview_src, preview_x1, preview_y1,
  709.                preview_width, preview_height);
  710.  
  711.   for (i = width * preview_height, src_ptr = preview_src, neg_ptr = preview_neg;
  712.        i > 0;
  713.        i --)
  714.     *neg_ptr++ = neg_lut[*src_ptr++];
  715.  
  716.   /*
  717.    * Select the filter...
  718.    */
  719.  
  720.   switch (img_bpp)
  721.     {
  722.     case 1:
  723.       filter = gray_filter;
  724.       break;
  725.     case 2:
  726.       filter = graya_filter;
  727.       break;
  728.     case 3:
  729.       filter = rgb_filter;
  730.       break;
  731.     case 4:
  732.       filter = rgba_filter;
  733.       break;
  734.     };
  735.  
  736.   /*
  737.    * Sharpen...
  738.    */
  739.  
  740.   memcpy (preview_dst, preview_src, width);
  741.   memcpy (preview_dst + width * (preview_height - 1),
  742.       preview_src + width * (preview_height - 1),
  743.       width);
  744.  
  745.   for (y = preview_height - 2, src_ptr = preview_src + width,
  746.        neg_ptr = preview_neg + width + img_bpp,
  747.        dst_ptr = preview_dst + width;
  748.        y > 0;
  749.        y --, src_ptr += width, neg_ptr += width, dst_ptr += width)
  750.     (*filter)(preview_width, src_ptr, dst_ptr, neg_ptr - width,
  751.               neg_ptr, neg_ptr + width);
  752.  
  753.   /*
  754.    * Fill the preview image buffer...
  755.    */
  756.  
  757.   switch (img_bpp)
  758.     {
  759.     case 1:
  760.       for (x = preview_width * preview_height, dst_ptr = preview_dst,
  761.          image_ptr = preview_image;
  762.        x > 0;
  763.        x --, dst_ptr ++, image_ptr += 3)
  764.     image_ptr[0] = image_ptr[1] = image_ptr[2] = *dst_ptr;
  765.       break;
  766.  
  767.     case 2:
  768.       for (y = preview_height, dst_ptr = preview_dst,
  769.          image_ptr = preview_image;
  770.        y > 0;
  771.        y --)
  772.     for (x = preview_width;
  773.          x > 0;
  774.          x --, dst_ptr += 2, image_ptr += 3)
  775.       if (dst_ptr[1] == 255)
  776.         image_ptr[0] = image_ptr[1] = image_ptr[2] = *dst_ptr;
  777.       else
  778.         {
  779.               if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  780.                 check = GIMP_CHECK_LIGHT * 255;
  781.               else
  782.                 check = GIMP_CHECK_DARK * 255;
  783.  
  784.               if (dst_ptr[1] == 0)
  785.                 image_ptr[0] = image_ptr[1] = image_ptr[2] = check;
  786.               else
  787.                 image_ptr[0] = image_ptr[1] = image_ptr[2] =
  788.           check + ((dst_ptr[0] - check) * dst_ptr[1]) / 255;
  789.         };
  790.       break;
  791.  
  792.     case 3:
  793.       memcpy (preview_image, preview_dst, preview_width * preview_height * 3);
  794.       break;
  795.  
  796.     case 4:
  797.       for (y = preview_height, dst_ptr = preview_dst,
  798.          image_ptr = preview_image;
  799.        y > 0;
  800.        y --)
  801.     for (x = preview_width;
  802.          x > 0;
  803.          x --, dst_ptr += 4, image_ptr += 3)
  804.       if (dst_ptr[3] == 255)
  805.         {
  806.           image_ptr[0] = dst_ptr[0];
  807.           image_ptr[1] = dst_ptr[1];
  808.           image_ptr[2] = dst_ptr[2];
  809.         }
  810.       else
  811.         {
  812.               if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
  813.                 check = GIMP_CHECK_LIGHT * 255;
  814.               else
  815.                 check = GIMP_CHECK_DARK * 255;
  816.  
  817.               if (dst_ptr[3] == 0)
  818.                 image_ptr[0] = image_ptr[1] = image_ptr[2] = check;
  819.               else
  820.         {
  821.           image_ptr[0] =
  822.             check + ((dst_ptr[0] - check) * dst_ptr[3]) / 255;
  823.           image_ptr[1] =
  824.             check + ((dst_ptr[1] - check) * dst_ptr[3]) / 255;
  825.           image_ptr[2] =
  826.             check + ((dst_ptr[2] - check) * dst_ptr[3]) / 255;
  827.         };
  828.         };
  829.       break;
  830.     };
  831.  
  832.   /*
  833.    * Draw the preview image on the screen...
  834.    */
  835.  
  836.   for (y = 0, image_ptr = preview_image;
  837.        y < preview_height;
  838.        y ++, image_ptr += preview_width * 3)
  839.     gtk_preview_draw_row (GTK_PREVIEW (preview), image_ptr, 0, y,
  840.               preview_width);
  841.  
  842.   gtk_widget_draw (preview, NULL);
  843.   gdk_flush ();
  844. }
  845.  
  846. static void
  847. preview_exit (void)
  848. {
  849.   g_free (preview_src);
  850.   g_free (preview_neg);
  851.   g_free (preview_dst);
  852.   g_free (preview_image);
  853. }
  854.  
  855. /*  dialog callbacks  */
  856.  
  857. static void
  858. dialog_iscale_update (GtkAdjustment *adjustment,
  859.               gint          *value)
  860. {
  861.   gimp_int_adjustment_update (adjustment, value);
  862.  
  863.   compute_luts ();
  864.   preview_update ();
  865. }
  866.  
  867. static void
  868. dialog_ok_callback (GtkWidget *widget,
  869.             gpointer   data)
  870. {
  871.   run_filter = TRUE;
  872.  
  873.   gtk_widget_destroy (GTK_WIDGET (data));
  874. }
  875.  
  876.  
  877. /*
  878.  * 'gray_filter()' - Sharpen grayscale pixels.
  879.  */
  880.  
  881. static void
  882. gray_filter (gint    width,    /* I - Width of line in pixels */
  883.          guchar *src,    /* I - Source line */
  884.          guchar *dst,    /* O - Destination line */
  885.          intneg *neg0,    /* I - Top negative coefficient line */
  886.          intneg *neg1,    /* I - Middle negative coefficient line */
  887.          intneg *neg2)    /* I - Bottom negative coefficient line */
  888. {
  889.   intpos pixel;        /* New pixel value */
  890.  
  891.   *dst++ = *src++;
  892.   width -= 2;
  893.  
  894.   while (width > 0)
  895.     {
  896.       pixel = (pos_lut[*src++] - neg0[-1] - neg0[0] - neg0[1] -
  897.            neg1[-1] - neg1[1] -
  898.            neg2[-1] - neg2[0] - neg2[1]);
  899.       pixel = (pixel + 4) >> 3;
  900.       if (pixel < 0)
  901.     *dst++ = 0;
  902.       else if (pixel < 255)
  903.     *dst++ = pixel;
  904.       else
  905.     *dst++ = 255;
  906.  
  907.       neg0 ++;
  908.       neg1 ++;
  909.       neg2 ++;
  910.       width --;
  911.     };
  912.  
  913.   *dst++ = *src++;
  914. }
  915.  
  916. /*
  917.  * 'graya_filter()' - Sharpen grayscale+alpha pixels.
  918.  */
  919.  
  920. static void
  921. graya_filter (gint   width,    /* I - Width of line in pixels */
  922.           guchar *src,    /* I - Source line */
  923.           guchar *dst,    /* O - Destination line */
  924.           intneg *neg0,    /* I - Top negative coefficient line */
  925.           intneg *neg1,    /* I - Middle negative coefficient line */
  926.           intneg *neg2)    /* I - Bottom negative coefficient line */
  927. {
  928.   intpos pixel;        /* New pixel value */
  929.  
  930.   *dst++ = *src++;
  931.   *dst++ = *src++;
  932.   width -= 2;
  933.  
  934.   while (width > 0)
  935.     {
  936.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
  937.            neg1[-2] - neg1[2] -
  938.            neg2[-2] - neg2[0] - neg2[2]);
  939.       pixel = (pixel + 4) >> 3;
  940.       if (pixel < 0)
  941.     *dst++ = 0;
  942.       else if (pixel < 255)
  943.     *dst++ = pixel;
  944.       else
  945.     *dst++ = 255;
  946.  
  947.       *dst++ = *src++;
  948.       neg0 += 2;
  949.       neg1 += 2;
  950.       neg2 += 2;
  951.       width --;
  952.     };
  953.  
  954.   *dst++ = *src++;
  955.   *dst++ = *src++;
  956. }
  957.  
  958. /*
  959.  * 'rgb_filter()' - Sharpen RGB pixels.
  960.  */
  961.  
  962. static void
  963. rgb_filter (gint    width,    /* I - Width of line in pixels */
  964.         guchar *src,    /* I - Source line */
  965.         guchar *dst,    /* O - Destination line */
  966.         intneg *neg0,    /* I - Top negative coefficient line */
  967.         intneg *neg1,    /* I - Middle negative coefficient line */
  968.         intneg *neg2)    /* I - Bottom negative coefficient line */
  969. {
  970.   intpos pixel;        /* New pixel value */
  971.  
  972.   *dst++ = *src++;
  973.   *dst++ = *src++;
  974.   *dst++ = *src++;
  975.   width -= 2;
  976.  
  977.   while (width > 0)
  978.     {
  979.       pixel = (pos_lut[*src++] - neg0[-3] - neg0[0] - neg0[3] -
  980.            neg1[-3] - neg1[3] -
  981.            neg2[-3] - neg2[0] - neg2[3]);
  982.       pixel = (pixel + 4) >> 3;
  983.       if (pixel < 0)
  984.     *dst++ = 0;
  985.       else if (pixel < 255)
  986.     *dst++ = pixel;
  987.       else
  988.     *dst++ = 255;
  989.  
  990.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[1] - neg0[4] -
  991.            neg1[-2] - neg1[4] -
  992.            neg2[-2] - neg2[1] - neg2[4]);
  993.       pixel = (pixel + 4) >> 3;
  994.       if (pixel < 0)
  995.     *dst++ = 0;
  996.       else if (pixel < 255)
  997.     *dst++ = pixel;
  998.       else
  999.     *dst++ = 255;
  1000.  
  1001.       pixel = (pos_lut[*src++] - neg0[-1] - neg0[2] - neg0[5] -
  1002.            neg1[-1] - neg1[5] -
  1003.            neg2[-1] - neg2[2] - neg2[5]);
  1004.       pixel = (pixel + 4) >> 3;
  1005.       if (pixel < 0)
  1006.     *dst++ = 0;
  1007.       else if (pixel < 255)
  1008.     *dst++ = pixel;
  1009.       else
  1010.     *dst++ = 255;
  1011.  
  1012.       neg0 += 3;
  1013.       neg1 += 3;
  1014.       neg2 += 3;
  1015.       width --;
  1016.     };
  1017.  
  1018.   *dst++ = *src++;
  1019.   *dst++ = *src++;
  1020.   *dst++ = *src++;
  1021. }
  1022.  
  1023. /*
  1024.  * 'rgba_filter()' - Sharpen RGBA pixels.
  1025.  */
  1026.  
  1027. static void
  1028. rgba_filter (gint   width,    /* I - Width of line in pixels */
  1029.          guchar *src,    /* I - Source line */
  1030.          guchar *dst,    /* O - Destination line */
  1031.          intneg *neg0,    /* I - Top negative coefficient line */
  1032.          intneg *neg1,    /* I - Middle negative coefficient line */
  1033.          intneg *neg2)    /* I - Bottom negative coefficient line */
  1034. {
  1035.   intpos pixel;        /* New pixel value */
  1036.  
  1037.   *dst++ = *src++;
  1038.   *dst++ = *src++;
  1039.   *dst++ = *src++;
  1040.   *dst++ = *src++;
  1041.   width -= 2;
  1042.  
  1043.   while (width > 0)
  1044.     {
  1045.       pixel = (pos_lut[*src++] - neg0[-4] - neg0[0] - neg0[4] -
  1046.            neg1[-4] - neg1[4] -
  1047.            neg2[-4] - neg2[0] - neg2[4]);
  1048.       pixel = (pixel + 4) >> 3;
  1049.       if (pixel < 0)
  1050.     *dst++ = 0;
  1051.       else if (pixel < 255)
  1052.     *dst++ = pixel;
  1053.       else
  1054.     *dst++ = 255;
  1055.  
  1056.       pixel = (pos_lut[*src++] - neg0[-3] - neg0[1] - neg0[5] -
  1057.            neg1[-3] - neg1[5] -
  1058.            neg2[-3] - neg2[1] - neg2[5]);
  1059.       pixel = (pixel + 4) >> 3;
  1060.       if (pixel < 0)
  1061.     *dst++ = 0;
  1062.       else if (pixel < 255)
  1063.     *dst++ = pixel;
  1064.       else
  1065.     *dst++ = 255;
  1066.  
  1067.       pixel = (pos_lut[*src++] - neg0[-2] - neg0[2] - neg0[6] -
  1068.            neg1[-2] - neg1[6] -
  1069.            neg2[-2] - neg2[2] - neg2[6]);
  1070.       pixel = (pixel + 4) >> 3;
  1071.       if (pixel < 0)
  1072.     *dst++ = 0;
  1073.       else if (pixel < 255)
  1074.     *dst++ = pixel;
  1075.       else
  1076.     *dst++ = 255;
  1077.  
  1078.       *dst++ = *src++;
  1079.  
  1080.       neg0 += 4;
  1081.       neg1 += 4;
  1082.       neg2 += 4;
  1083.       width --;
  1084.     };
  1085.  
  1086.   *dst++ = *src++;
  1087.   *dst++ = *src++;
  1088.   *dst++ = *src++;
  1089.   *dst++ = *src++;
  1090. }
  1091.