1 /*
2  * Sharpen filters for GIMP - The GNU Image Manipulation Program
3  *
4  * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
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 3 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, see <https://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <libgimp/gimp.h>
26 #include <libgimp/gimpui.h>
27 
28 #include "libgimp/stdplugins-intl.h"
29 
30 /*
31  * Constants...
32  */
33 
34 #define PLUG_IN_PROC    "plug-in-sharpen"
35 #define PLUG_IN_BINARY  "sharpen"
36 #define PLUG_IN_ROLE    "gimp-sharpen"
37 #define PLUG_IN_VERSION "1.4.2 - 3 June 1998"
38 #define SCALE_WIDTH     100
39 
40 /*
41  * Local functions...
42  */
43 
44 static void     query (void);
45 static void     run   (const gchar      *name,
46                        gint              nparams,
47                        const GimpParam  *param,
48                        gint             *nreturn_vals,
49                        GimpParam       **returm_vals);
50 
51 static void     compute_luts   (void);
52 static void     sharpen        (GimpDrawable *drawable);
53 
54 static gboolean sharpen_dialog (GimpDrawable *drawable);
55 
56 static void     preview_update (GimpPreview  *preview,
57                                 GimpDrawable *drawable);
58 
59 typedef gint32 intneg;
60 typedef gint32 intpos;
61 
62 static void     gray_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
63                               intneg *neg1, intneg *neg2);
64 static void     graya_filter (int width, guchar *src, guchar *dst, intneg *neg0,
65                               intneg *neg1, intneg *neg2);
66 static void     rgb_filter   (int width, guchar *src, guchar *dst, intneg *neg0,
67                               intneg *neg1, intneg *neg2);
68 static void     rgba_filter  (int width, guchar *src, guchar *dst, intneg *neg0,
69                               intneg *neg1, intneg *neg2);
70 
71 
72 /*
73  * Globals...
74  */
75 
76 const GimpPlugInInfo PLUG_IN_INFO =
77 {
78   NULL,  /* init_proc  */
79   NULL,  /* quit_proc  */
80   query, /* query_proc */
81   run    /* run_proc   */
82 };
83 
84 typedef struct
85 {
86   gint  sharpen_percent;
87 } SharpenParams;
88 
89 static SharpenParams sharpen_params =
90 {
91   10
92 };
93 
94 static intneg neg_lut[256];   /* Negative coefficient LUT */
95 static intpos pos_lut[256];   /* Positive coefficient LUT */
96 
97 
MAIN()98 MAIN ()
99 
100 static void
101 query (void)
102 {
103   static const GimpParamDef   args[] =
104   {
105     { GIMP_PDB_INT32,    "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"      },
106     { GIMP_PDB_IMAGE,    "image",    "Input image"                       },
107     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"                    },
108     { GIMP_PDB_INT32,    "percent",  "Percent sharpening (default = 10)" }
109   };
110 
111   gimp_install_procedure (PLUG_IN_PROC,
112                           N_("Make image sharper "
113                              "(less powerful than Unsharp Mask)"),
114                           "This plug-in selectively performs a convolution "
115                           "filter on an image.",
116                           "Michael Sweet <mike@easysw.com>",
117                           "Copyright 1997-1998 by Michael Sweet",
118                           PLUG_IN_VERSION,
119                           N_("_Sharpen..."),
120                           "RGB*, GRAY*",
121                           GIMP_PLUGIN,
122                           G_N_ELEMENTS (args), 0,
123                           args, NULL);
124 }
125 
126 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)127 run (const gchar      *name,
128      gint              nparams,
129      const GimpParam  *param,
130      gint             *nreturn_vals,
131      GimpParam       **return_vals)
132 {
133   static GimpParam   values[1]; /* Return values */
134   GimpRunMode        run_mode;  /* Current run mode */
135   GimpPDBStatusType  status;    /* Return status */
136   GimpDrawable      *drawable;  /* Current image */
137 
138   /*
139    * Initialize parameter data...
140    */
141 
142   status   = GIMP_PDB_SUCCESS;
143   run_mode = param[0].data.d_int32;
144 
145   INIT_I18N ();
146 
147   *nreturn_vals = 1;
148   *return_vals  = values;
149 
150   values[0].type          = GIMP_PDB_STATUS;
151   values[0].data.d_status = status;
152 
153   /*
154    * Get drawable information...
155    */
156 
157   drawable = gimp_drawable_get (param[2].data.d_drawable);
158   gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
159 
160 
161   /*
162    * See how we will run
163    */
164 
165   switch (run_mode)
166     {
167     case GIMP_RUN_INTERACTIVE:
168       /*
169        * Possibly retrieve data...
170        */
171       gimp_get_data (PLUG_IN_PROC, &sharpen_params);
172 
173       /*
174        * Get information from the dialog...
175        */
176       if (!sharpen_dialog (drawable))
177         return;
178       break;
179 
180     case GIMP_RUN_NONINTERACTIVE:
181       /*
182        * Make sure all the arguments are present...
183        */
184       if (nparams != 4)
185         status = GIMP_PDB_CALLING_ERROR;
186       else
187         sharpen_params.sharpen_percent = param[3].data.d_int32;
188       break;
189 
190     case GIMP_RUN_WITH_LAST_VALS:
191       /*
192        * Possibly retrieve data...
193        */
194       gimp_get_data (PLUG_IN_PROC, &sharpen_params);
195       break;
196 
197     default:
198       status = GIMP_PDB_CALLING_ERROR;
199       break;
200     }
201 
202   /*
203    * Sharpen the image...
204    */
205 
206   if (status == GIMP_PDB_SUCCESS)
207     {
208       if ((gimp_drawable_is_rgb (drawable->drawable_id) ||
209            gimp_drawable_is_gray (drawable->drawable_id)))
210         {
211           /*
212            * Run!
213            */
214           sharpen (drawable);
215 
216           /*
217            * If run mode is interactive, flush displays...
218            */
219           if (run_mode != GIMP_RUN_NONINTERACTIVE)
220             gimp_displays_flush ();
221 
222           /*
223            * Store data...
224            */
225           if (run_mode == GIMP_RUN_INTERACTIVE)
226             gimp_set_data (PLUG_IN_PROC,
227                            &sharpen_params, sizeof (SharpenParams));
228         }
229       else
230         status = GIMP_PDB_EXECUTION_ERROR;
231     }
232 
233   /*
234    * Reset the current run status...
235    */
236   values[0].data.d_status = status;
237 
238   /*
239    * Detach from the drawable...
240    */
241   gimp_drawable_detach (drawable);
242 }
243 
244 
245 static void
compute_luts(void)246 compute_luts (void)
247 {
248   gint i;       /* Looping var */
249   gint fact;    /* 1 - sharpness */
250 
251   fact = 100 - sharpen_params.sharpen_percent;
252   if (fact < 1)
253     fact = 1;
254 
255   for (i = 0; i < 256; i ++)
256     {
257       pos_lut[i] = 800 * i / fact;
258       neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
259     }
260 }
261 
262 /*
263  * 'sharpen()' - Sharpen an image using a convolution filter.
264  */
265 
266 static void
sharpen(GimpDrawable * drawable)267 sharpen (GimpDrawable *drawable)
268 {
269   GimpPixelRgn  src_rgn;        /* Source image region */
270   GimpPixelRgn  dst_rgn;        /* Destination image region */
271   guchar       *src_rows[4];    /* Source pixel rows */
272   guchar       *src_ptr;        /* Current source pixel */
273   guchar       *dst_row;        /* Destination pixel row */
274   intneg       *neg_rows[4];    /* Negative coefficient rows */
275   intneg       *neg_ptr;        /* Current negative coefficient */
276   gint          i;              /* Looping vars */
277   gint          y;              /* Current location in image */
278   gint          row;            /* Current row in src_rows */
279   gint          count;          /* Current number of filled src_rows */
280   gint          width;          /* Byte width of the image */
281   gint          x1;             /* Selection bounds */
282   gint          y1;
283   gint          y2;
284   gint          sel_width;      /* Selection width */
285   gint          sel_height;     /* Selection height */
286   gint          img_bpp;        /* Bytes-per-pixel in image */
287   void          (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
288 
289   filter = NULL;
290 
291   if (! gimp_drawable_mask_intersect (drawable->drawable_id,
292                                       &x1, &y1, &sel_width, &sel_height))
293     return;
294 
295   y2 = y1 + sel_height;
296 
297   img_bpp = gimp_drawable_bpp (drawable->drawable_id);
298 
299   /*
300    * Let the user know what we're doing...
301    */
302   gimp_progress_init (_("Sharpening"));
303 
304   /*
305    * Setup for filter...
306    */
307 
308   gimp_pixel_rgn_init (&src_rgn, drawable,
309                        x1, y1, sel_width, sel_height, FALSE, FALSE);
310   gimp_pixel_rgn_init (&dst_rgn, drawable,
311                        x1, y1, sel_width, sel_height, TRUE, TRUE);
312 
313   compute_luts ();
314 
315   width = sel_width * img_bpp;
316 
317   for (row = 0; row < 4; row ++)
318     {
319       src_rows[row] = g_new (guchar, width);
320       neg_rows[row] = g_new (intneg, width);
321     }
322 
323   dst_row = g_new (guchar, width);
324 
325   /*
326    * Pre-load the first row for the filter...
327    */
328 
329   gimp_pixel_rgn_get_row (&src_rgn, src_rows[0], x1, y1, sel_width);
330 
331   for (i = width, src_ptr = src_rows[0], neg_ptr = neg_rows[0];
332        i > 0;
333        i --, src_ptr ++, neg_ptr ++)
334     *neg_ptr = neg_lut[*src_ptr];
335 
336   row   = 1;
337   count = 1;
338 
339   /*
340    * Select the filter...
341    */
342 
343   switch (img_bpp)
344     {
345     case 1 :
346       filter = gray_filter;
347       break;
348     case 2 :
349       filter = graya_filter;
350       break;
351     case 3 :
352       filter = rgb_filter;
353       break;
354     case 4 :
355       filter = rgba_filter;
356       break;
357     };
358 
359   /*
360    * Sharpen...
361    */
362 
363   for (y = y1; y < y2; y ++)
364     {
365       /*
366        * Load the next pixel row...
367        */
368 
369       if ((y + 1) < y2)
370         {
371           /*
372            * Check to see if our src_rows[] array is overflowing yet...
373            */
374 
375           if (count >= 3)
376             count --;
377 
378           /*
379            * Grab the next row...
380            */
381 
382           gimp_pixel_rgn_get_row (&src_rgn, src_rows[row],
383                                   x1, y + 1, sel_width);
384           for (i = width, src_ptr = src_rows[row], neg_ptr = neg_rows[row];
385                i > 0;
386                i --, src_ptr ++, neg_ptr ++)
387             *neg_ptr = neg_lut[*src_ptr];
388 
389           count ++;
390           row = (row + 1) & 3;
391         }
392       else
393         {
394           /*
395            * No more pixels at the bottom...  Drop the oldest samples...
396            */
397 
398           count --;
399         }
400 
401       /*
402        * Now sharpen pixels and save the results...
403        */
404 
405       if (count == 3)
406         {
407           (* filter) (sel_width, src_rows[(row + 2) & 3], dst_row,
408                       neg_rows[(row + 1) & 3] + img_bpp,
409                       neg_rows[(row + 2) & 3] + img_bpp,
410                       neg_rows[(row + 3) & 3] + img_bpp);
411 
412           /*
413            * Set the row...
414            */
415 
416           gimp_pixel_rgn_set_row (&dst_rgn, dst_row, x1, y, sel_width);
417         }
418       else if (count == 2)
419         {
420           if (y == y1)      /* first row */
421             gimp_pixel_rgn_set_row (&dst_rgn, src_rows[0],
422                                     x1, y, sel_width);
423           else                  /* last row  */
424             gimp_pixel_rgn_set_row (&dst_rgn, src_rows[(sel_height - 1) & 3],
425                                     x1, y, sel_width);
426         }
427 
428       if ((y & 15) == 0)
429         gimp_progress_update ((gdouble) (y - y1) / (gdouble) sel_height);
430     }
431 
432   /*
433    * OK, we're done.  Free all memory used...
434    */
435 
436   for (row = 0; row < 4; row ++)
437     {
438       g_free (src_rows[row]);
439       g_free (neg_rows[row]);
440     }
441 
442   g_free (dst_row);
443 
444   /*
445    * Update the screen...
446    */
447 
448   gimp_progress_update (1.0);
449   gimp_drawable_flush (drawable);
450   gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
451   gimp_drawable_update (drawable->drawable_id,
452                         x1, y1, sel_width, sel_height);
453 }
454 
455 
456 /*
457  * 'sharpen_dialog()' - Popup a dialog window for the filter box size...
458  */
459 
460 static gboolean
sharpen_dialog(GimpDrawable * drawable)461 sharpen_dialog (GimpDrawable *drawable)
462 {
463   GtkWidget *dialog;
464   GtkWidget *main_vbox;
465   GtkWidget *preview;
466   GtkWidget *table;
467   GtkObject *adj;
468   gboolean   run;
469 
470   gimp_ui_init (PLUG_IN_BINARY, TRUE);
471 
472   dialog = gimp_dialog_new (_("Sharpen"), PLUG_IN_ROLE,
473                             NULL, 0,
474                             gimp_standard_help_func, PLUG_IN_PROC,
475 
476                             _("_Cancel"), GTK_RESPONSE_CANCEL,
477                             _("_OK"),     GTK_RESPONSE_OK,
478 
479                             NULL);
480 
481   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
482                                            GTK_RESPONSE_OK,
483                                            GTK_RESPONSE_CANCEL,
484                                            -1);
485 
486   gimp_window_set_transient (GTK_WINDOW (dialog));
487 
488   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
489   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
490   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
491                       main_vbox, TRUE, TRUE, 0);
492   gtk_widget_show (main_vbox);
493 
494   preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
495   gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
496   gtk_widget_show (preview);
497 
498   g_signal_connect (preview, "invalidated",
499                     G_CALLBACK (preview_update),
500                     drawable);
501 
502   table = gtk_table_new (1, 3, FALSE);
503   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
504   gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
505   gtk_widget_show (table);
506 
507   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
508                               _("_Sharpness:"), SCALE_WIDTH, 0,
509                               sharpen_params.sharpen_percent,
510                               1, 99, 1, 10, 0,
511                               TRUE, 0, 0,
512                               NULL, NULL);
513   g_signal_connect (adj, "value-changed",
514                     G_CALLBACK (gimp_int_adjustment_update),
515                     &sharpen_params.sharpen_percent);
516   g_signal_connect_swapped (adj, "value-changed",
517                             G_CALLBACK (gimp_preview_invalidate),
518                             preview);
519 
520   gtk_widget_show (dialog);
521 
522   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
523 
524   gtk_widget_destroy (dialog);
525 
526   return run;
527 }
528 
529 static void
preview_update(GimpPreview * preview,GimpDrawable * drawable)530 preview_update (GimpPreview  *preview,
531                 GimpDrawable *drawable)
532 {
533   GimpPixelRgn  src_rgn;        /* Source image region */
534   guchar       *src_ptr;        /* Current source pixel */
535   guchar       *dst_ptr;        /* Current destination pixel */
536   intneg       *neg_ptr;        /* Current negative pixel */
537   gint          i;              /* Looping var */
538   gint          y;              /* Current location in image */
539   gint          width;          /* Byte width of the image */
540   gint          x1, y1;
541   gint          preview_width, preview_height;
542   guchar       *preview_src, *preview_dst;
543   intneg       *preview_neg;
544   gint          img_bpp;        /* Bytes-per-pixel in image */
545 
546   void          (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
547 
548   filter = NULL;
549 
550   compute_luts();
551 
552   gimp_preview_get_position (preview, &x1, &y1);
553   gimp_preview_get_size (preview, &preview_width, &preview_height);
554 
555   img_bpp = gimp_drawable_bpp (drawable->drawable_id);
556 
557 
558   preview_src = g_new (guchar, preview_width * preview_height * img_bpp);
559   preview_neg = g_new (intneg, preview_width * preview_height * img_bpp);
560   preview_dst = g_new (guchar, preview_width * preview_height * img_bpp);
561 
562   gimp_pixel_rgn_init (&src_rgn, drawable,
563                        x1, y1, preview_width, preview_height,
564                        FALSE, FALSE);
565 
566   width = preview_width * img_bpp;
567 
568   /*
569    * Load the preview area...
570    */
571 
572   gimp_pixel_rgn_get_rect (&src_rgn, preview_src, x1, y1,
573                            preview_width, preview_height);
574 
575   for (i = width * preview_height, src_ptr = preview_src, neg_ptr = preview_neg;
576        i > 0;
577        i --)
578     *neg_ptr++ = neg_lut[*src_ptr++];
579 
580   /*
581    * Select the filter...
582    */
583 
584   switch (img_bpp)
585     {
586     case 1:
587       filter = gray_filter;
588       break;
589     case 2:
590       filter = graya_filter;
591       break;
592     case 3:
593       filter = rgb_filter;
594       break;
595     case 4:
596       filter = rgba_filter;
597       break;
598     default:
599       g_error ("Programmer stupidity error: img_bpp is %d\n",
600                img_bpp);
601     }
602 
603   /*
604    * Sharpen...
605    */
606 
607   memcpy (preview_dst, preview_src, width);
608   memcpy (preview_dst + width * (preview_height - 1),
609           preview_src + width * (preview_height - 1),
610           width);
611 
612   for (y = preview_height - 2, src_ptr = preview_src + width,
613            neg_ptr = preview_neg + width + img_bpp,
614            dst_ptr = preview_dst + width;
615        y > 0;
616        y --, src_ptr += width, neg_ptr += width, dst_ptr += width)
617     (*filter)(preview_width, src_ptr, dst_ptr, neg_ptr - width,
618               neg_ptr, neg_ptr + width);
619 
620   gimp_preview_draw_buffer (preview, preview_dst, preview_width * img_bpp);
621 
622   g_free (preview_src);
623   g_free (preview_neg);
624   g_free (preview_dst);
625 }
626 
627 /*
628  * 'gray_filter()' - Sharpen grayscale pixels.
629  */
630 
631 static void
gray_filter(gint width,guchar * src,guchar * dst,intneg * neg0,intneg * neg1,intneg * neg2)632 gray_filter (gint    width,     /* I - Width of line in pixels */
633              guchar *src,       /* I - Source line */
634              guchar *dst,       /* O - Destination line */
635              intneg *neg0,      /* I - Top negative coefficient line */
636              intneg *neg1,      /* I - Middle negative coefficient line */
637              intneg *neg2)      /* I - Bottom negative coefficient line */
638 {
639   intpos pixel;         /* New pixel value */
640 
641   *dst++ = *src++;
642   width -= 2;
643 
644   while (width > 0)
645     {
646       pixel = (pos_lut[*src++] - neg0[-1] - neg0[0] - neg0[1] -
647                neg1[-1] - neg1[1] -
648                neg2[-1] - neg2[0] - neg2[1]);
649       pixel = (pixel + 4) >> 3;
650       *dst++ = CLAMP0255 (pixel);
651 
652       neg0 ++;
653       neg1 ++;
654       neg2 ++;
655       width --;
656     }
657 
658   *dst++ = *src++;
659 }
660 
661 /*
662  * 'graya_filter()' - Sharpen grayscale+alpha pixels.
663  */
664 
665 static void
graya_filter(gint width,guchar * src,guchar * dst,intneg * neg0,intneg * neg1,intneg * neg2)666 graya_filter (gint   width,     /* I - Width of line in pixels */
667               guchar *src,      /* I - Source line */
668               guchar *dst,      /* O - Destination line */
669               intneg *neg0,     /* I - Top negative coefficient line */
670               intneg *neg1,     /* I - Middle negative coefficient line */
671               intneg *neg2)     /* I - Bottom negative coefficient line */
672 {
673   intpos pixel;         /* New pixel value */
674 
675   *dst++ = *src++;
676   *dst++ = *src++;
677   width -= 2;
678 
679   while (width > 0)
680     {
681       pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
682                neg1[-2] - neg1[2] -
683                neg2[-2] - neg2[0] - neg2[2]);
684       pixel = (pixel + 4) >> 3;
685       *dst++ = CLAMP0255 (pixel);
686 
687       *dst++ = *src++;
688       neg0 += 2;
689       neg1 += 2;
690       neg2 += 2;
691       width --;
692     }
693 
694   *dst++ = *src++;
695   *dst++ = *src++;
696 }
697 
698 /*
699  * 'rgb_filter()' - Sharpen RGB pixels.
700  */
701 
702 static void
rgb_filter(gint width,guchar * src,guchar * dst,intneg * neg0,intneg * neg1,intneg * neg2)703 rgb_filter (gint    width,      /* I - Width of line in pixels */
704             guchar *src,        /* I - Source line */
705             guchar *dst,        /* O - Destination line */
706             intneg *neg0,       /* I - Top negative coefficient line */
707             intneg *neg1,       /* I - Middle negative coefficient line */
708             intneg *neg2)       /* I - Bottom negative coefficient line */
709 {
710   intpos pixel;         /* New pixel value */
711 
712   *dst++ = *src++;
713   *dst++ = *src++;
714   *dst++ = *src++;
715   width -= 2;
716 
717   while (width > 0)
718     {
719       pixel = (pos_lut[*src++] - neg0[-3] - neg0[0] - neg0[3] -
720                neg1[-3] - neg1[3] -
721                neg2[-3] - neg2[0] - neg2[3]);
722       pixel = (pixel + 4) >> 3;
723       *dst++ = CLAMP0255 (pixel);
724 
725       pixel = (pos_lut[*src++] - neg0[-2] - neg0[1] - neg0[4] -
726                neg1[-2] - neg1[4] -
727                neg2[-2] - neg2[1] - neg2[4]);
728       pixel = (pixel + 4) >> 3;
729       *dst++ = CLAMP0255 (pixel);
730 
731       pixel = (pos_lut[*src++] - neg0[-1] - neg0[2] - neg0[5] -
732                neg1[-1] - neg1[5] -
733                neg2[-1] - neg2[2] - neg2[5]);
734       pixel = (pixel + 4) >> 3;
735       *dst++ = CLAMP0255 (pixel);
736 
737       neg0 += 3;
738       neg1 += 3;
739       neg2 += 3;
740       width --;
741     }
742 
743   *dst++ = *src++;
744   *dst++ = *src++;
745   *dst++ = *src++;
746 }
747 
748 /*
749  * 'rgba_filter()' - Sharpen RGBA pixels.
750  */
751 
752 static void
rgba_filter(gint width,guchar * src,guchar * dst,intneg * neg0,intneg * neg1,intneg * neg2)753 rgba_filter (gint   width,      /* I - Width of line in pixels */
754              guchar *src,       /* I - Source line */
755              guchar *dst,       /* O - Destination line */
756              intneg *neg0,      /* I - Top negative coefficient line */
757              intneg *neg1,      /* I - Middle negative coefficient line */
758              intneg *neg2)      /* I - Bottom negative coefficient line */
759 {
760   intpos pixel;         /* New pixel value */
761 
762   *dst++ = *src++;
763   *dst++ = *src++;
764   *dst++ = *src++;
765   *dst++ = *src++;
766   width -= 2;
767 
768   while (width > 0)
769     {
770       pixel = (pos_lut[*src++] - neg0[-4] - neg0[0] - neg0[4] -
771                neg1[-4] - neg1[4] -
772                neg2[-4] - neg2[0] - neg2[4]);
773       pixel = (pixel + 4) >> 3;
774       *dst++ = CLAMP0255 (pixel);
775 
776       pixel = (pos_lut[*src++] - neg0[-3] - neg0[1] - neg0[5] -
777                neg1[-3] - neg1[5] -
778                neg2[-3] - neg2[1] - neg2[5]);
779       pixel = (pixel + 4) >> 3;
780       *dst++ = CLAMP0255 (pixel);
781 
782       pixel = (pos_lut[*src++] - neg0[-2] - neg0[2] - neg0[6] -
783                neg1[-2] - neg1[6] -
784                neg2[-2] - neg2[2] - neg2[6]);
785       pixel = (pixel + 4) >> 3;
786       *dst++ = CLAMP0255 (pixel);
787 
788       *dst++ = *src++;
789 
790       neg0 += 4;
791       neg1 += 4;
792       neg2 += 4;
793       width --;
794     }
795 
796   *dst++ = *src++;
797   *dst++ = *src++;
798   *dst++ = *src++;
799   *dst++ = *src++;
800 }
801