2
* "$Id: sharpen.c,v 1.55 2004/12/23 23:58:35 weskaggs Exp $"
4
* Sharpen filters for The GIMP -- an image manipulation program
6
* Copyright 1997-1998 Michael Sweet (mike@easysw.com)
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.
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.
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.
30
#include <libgimp/gimp.h>
31
#include <libgimp/gimpui.h>
33
#include "libgimp/stdplugins-intl.h"
39
#define PLUG_IN_NAME "plug_in_sharpen"
40
#define PLUG_IN_VERSION "1.4.2 - 3 June 1998"
41
#define HELP_ID "plug-in-sharpen"
42
#define SCALE_WIDTH 100
48
static void query (void);
49
static void run (const gchar *name,
51
const GimpParam *param,
53
GimpParam **returm_vals);
55
static void compute_luts (void);
56
static void sharpen (GimpDrawable *drawable);
58
static gboolean sharpen_dialog (GimpDrawable *drawable);
60
static void preview_update (GimpPreview *preview);
62
typedef gint32 intneg;
63
typedef gint32 intpos;
65
static void gray_filter (int width, guchar *src, guchar *dst, intneg *neg0,
66
intneg *neg1, intneg *neg2);
67
static void graya_filter (int width, guchar *src, guchar *dst, intneg *neg0,
68
intneg *neg1, intneg *neg2);
69
static void rgb_filter (int width, guchar *src, guchar *dst, intneg *neg0,
70
intneg *neg1, intneg *neg2);
71
static void rgba_filter (int width, guchar *src, guchar *dst, intneg *neg0,
72
intneg *neg1, intneg *neg2);
79
GimpPlugInInfo PLUG_IN_INFO =
83
query, /* query_proc */
90
gboolean update_preview;
93
static SharpenParams sharpen_params =
99
static intneg neg_lut[256]; /* Negative coefficient LUT */
100
static intpos pos_lut[256]; /* Positive coefficient LUT */
108
static GimpParamDef args[] =
110
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
111
{ GIMP_PDB_IMAGE, "image", "Input image" },
112
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
113
{ GIMP_PDB_INT32, "percent", "Percent sharpening (default = 10)" }
116
gimp_install_procedure (PLUG_IN_NAME,
117
"Sharpen filter, typically used to 'sharpen' a "
118
"photographic image.",
119
"This plug-in selectively performs a convolution "
120
"filter on an image.",
121
"Michael Sweet <mike@easysw.com>",
122
"Copyright 1997-1998 by Michael Sweet",
127
G_N_ELEMENTS (args), 0,
130
gimp_plugin_menu_register (PLUG_IN_NAME, "<Image>/Filters/Enhance");
134
run (const gchar *name,
136
const GimpParam *param,
138
GimpParam **return_vals)
140
GimpRunMode run_mode; /* Current run mode */
141
GimpPDBStatusType status; /* Return status */
142
GimpParam *values; /* Return values */
143
GimpDrawable *drawable; /* Current image */
146
* Initialize parameter data...
149
status = GIMP_PDB_SUCCESS;
150
run_mode = param[0].data.d_int32;
154
values = g_new (GimpParam, 1);
157
*return_vals = values;
159
values[0].type = GIMP_PDB_STATUS;
160
values[0].data.d_status = status;
163
* Get drawable information...
166
drawable = gimp_drawable_get (param[2].data.d_drawable);
167
gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
171
* See how we will run
176
case GIMP_RUN_INTERACTIVE:
178
* Possibly retrieve data...
180
gimp_get_data (PLUG_IN_NAME, &sharpen_params);
183
* Get information from the dialog...
185
if (!sharpen_dialog (drawable))
189
case GIMP_RUN_NONINTERACTIVE:
191
* Make sure all the arguments are present...
194
status = GIMP_PDB_CALLING_ERROR;
196
sharpen_params.sharpen_percent = param[3].data.d_int32;
199
case GIMP_RUN_WITH_LAST_VALS:
201
* Possibly retrieve data...
203
gimp_get_data (PLUG_IN_NAME, &sharpen_params);
207
status = GIMP_PDB_CALLING_ERROR;
212
* Sharpen the image...
215
if (status == GIMP_PDB_SUCCESS)
217
if ((gimp_drawable_is_rgb (drawable->drawable_id) ||
218
gimp_drawable_is_gray (drawable->drawable_id)))
226
* If run mode is interactive, flush displays...
228
if (run_mode != GIMP_RUN_NONINTERACTIVE)
229
gimp_displays_flush ();
234
if (run_mode == GIMP_RUN_INTERACTIVE)
235
gimp_set_data (PLUG_IN_NAME,
236
&sharpen_params, sizeof (SharpenParams));
239
status = GIMP_PDB_EXECUTION_ERROR;
243
* Reset the current run status...
245
values[0].data.d_status = status;
248
* Detach from the drawable...
250
gimp_drawable_detach (drawable);
257
gint i; /* Looping var */
258
gint fact; /* 1 - sharpness */
260
fact = 100 - sharpen_params.sharpen_percent;
264
for (i = 0; i < 256; i ++)
266
pos_lut[i] = 800 * i / fact;
267
neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
272
* 'sharpen()' - Sharpen an image using a convolution filter.
276
sharpen (GimpDrawable *drawable)
278
GimpPixelRgn src_rgn; /* Source image region */
279
GimpPixelRgn dst_rgn; /* Destination image region */
280
guchar *src_rows[4]; /* Source pixel rows */
281
guchar *src_ptr; /* Current source pixel */
282
guchar *dst_row; /* Destination pixel row */
283
intneg *neg_rows[4]; /* Negative coefficient rows */
284
intneg *neg_ptr; /* Current negative coefficient */
285
gint i; /* Looping vars */
286
gint y; /* Current location in image */
287
gint row; /* Current row in src_rows */
288
gint count; /* Current number of filled src_rows */
289
gint width; /* Byte width of the image */
290
gint x1; /* Selection bounds */
294
gint sel_width; /* Selection width */
295
gint sel_height; /* Selection height */
296
gint img_bpp; /* Bytes-per-pixel in image */
297
void (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
301
gimp_drawable_mask_bounds (drawable->drawable_id,
305
sel_height = y2 - y1;
306
img_bpp = gimp_drawable_bpp (drawable->drawable_id);
309
* Let the user know what we're doing...
311
gimp_progress_init( _("Sharpening..."));
314
* Setup for filter...
317
gimp_pixel_rgn_init (&src_rgn, drawable,
318
x1, y1, sel_width, sel_height, FALSE, FALSE);
319
gimp_pixel_rgn_init (&dst_rgn, drawable,
320
x1, y1, sel_width, sel_height, TRUE, TRUE);
324
width = sel_width * img_bpp;
326
for (row = 0; row < 4; row ++)
328
src_rows[row] = g_new (guchar, width);
329
neg_rows[row] = g_new (intneg, width);
332
dst_row = g_new (guchar, width);
335
* Pre-load the first row for the filter...
338
gimp_pixel_rgn_get_row (&src_rgn, src_rows[0], x1, y1, sel_width);
340
for (i = width, src_ptr = src_rows[0], neg_ptr = neg_rows[0];
342
i --, src_ptr ++, neg_ptr ++)
343
*neg_ptr = neg_lut[*src_ptr];
349
* Select the filter...
355
filter = gray_filter;
358
filter = graya_filter;
364
filter = rgba_filter;
372
for (y = y1; y < y2; y ++)
375
* Load the next pixel row...
381
* Check to see if our src_rows[] array is overflowing yet...
388
* Grab the next row...
391
gimp_pixel_rgn_get_row (&src_rgn, src_rows[row],
392
x1, y + 1, sel_width);
393
for (i = width, src_ptr = src_rows[row], neg_ptr = neg_rows[row];
395
i --, src_ptr ++, neg_ptr ++)
396
*neg_ptr = neg_lut[*src_ptr];
404
* No more pixels at the bottom... Drop the oldest samples...
411
* Now sharpen pixels and save the results...
416
(* filter) (sel_width, src_rows[(row + 2) & 3], dst_row,
417
neg_rows[(row + 1) & 3] + img_bpp,
418
neg_rows[(row + 2) & 3] + img_bpp,
419
neg_rows[(row + 3) & 3] + img_bpp);
425
gimp_pixel_rgn_set_row (&dst_rgn, dst_row, x1, y, sel_width);
429
if (y == y1) /* first row */
430
gimp_pixel_rgn_set_row (&dst_rgn, src_rows[0],
433
gimp_pixel_rgn_set_row (&dst_rgn, src_rows[(sel_height - 1) & 3],
438
gimp_progress_update ((gdouble) (y - y1) / (gdouble) sel_height);
442
* OK, we're done. Free all memory used...
445
for (row = 0; row < 4; row ++)
447
g_free (src_rows[row]);
448
g_free (neg_rows[row]);
454
* Update the screen...
457
gimp_drawable_flush (drawable);
458
gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
459
gimp_drawable_update (drawable->drawable_id,
460
x1, y1, sel_width, sel_height);
465
* 'sharpen_dialog()' - Popup a dialog window for the filter box size...
469
sharpen_dialog (GimpDrawable *drawable)
472
GtkWidget *main_vbox;
478
gimp_ui_init ("sharpen", TRUE);
480
dialog = gimp_dialog_new (_("Sharpen"), "Sharpen",
482
gimp_standard_help_func, HELP_ID,
484
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
485
GTK_STOCK_OK, GTK_RESPONSE_OK,
489
main_vbox = gtk_vbox_new (FALSE, 12);
490
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
491
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
492
gtk_widget_show (main_vbox);
494
preview = gimp_drawable_preview_new (drawable,
495
&sharpen_params.update_preview);
496
gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
497
gtk_widget_show (preview);
499
g_signal_connect (preview, "invalidated",
500
G_CALLBACK (preview_update),
503
table = gtk_table_new (1, 3, FALSE);
504
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
505
gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
506
gtk_widget_show (table);
508
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
509
_("_Sharpness:"), SCALE_WIDTH, 0,
510
sharpen_params.sharpen_percent,
514
g_signal_connect (adj, "value_changed",
515
G_CALLBACK (gimp_int_adjustment_update),
516
&sharpen_params.sharpen_percent);
517
g_signal_connect_swapped (adj, "value_changed",
518
G_CALLBACK (gimp_preview_invalidate),
521
gtk_widget_show (dialog);
523
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
525
gtk_widget_destroy (dialog);
531
preview_update (GimpPreview *preview)
533
GimpDrawable *drawable;
534
GimpPixelRgn src_rgn; /* Source image region */
535
guchar *src_ptr; /* Current source pixel */
536
guchar *dst_ptr; /* Current destination pixel */
537
intneg *neg_ptr; /* Current negative pixel */
538
gint i; /* Looping var */
539
gint y; /* Current location in image */
540
gint width; /* Byte width of the image */
542
gint preview_width, preview_height;
543
guchar *preview_src, *preview_dst;
545
gint img_bpp; /* Bytes-per-pixel in image */
547
void (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
553
gimp_preview_get_position (preview, &x1, &y1);
554
gimp_preview_get_size (preview, &preview_width, &preview_height);
557
gimp_drawable_preview_get_drawable (GIMP_DRAWABLE_PREVIEW (preview));
559
img_bpp = gimp_drawable_bpp (drawable->drawable_id);
562
preview_src = g_new (guchar, preview_width * preview_height * img_bpp);
563
preview_neg = g_new (intneg, preview_width * preview_height * img_bpp);
564
preview_dst = g_new (guchar, preview_width * preview_height * img_bpp);
566
gimp_pixel_rgn_init (&src_rgn, drawable,
567
x1, y1, preview_width, preview_height,
570
width = preview_width * img_bpp;
573
* Load the preview area...
576
gimp_pixel_rgn_get_rect (&src_rgn, preview_src, x1, y1,
577
preview_width, preview_height);
579
for (i = width * preview_height, src_ptr = preview_src, neg_ptr = preview_neg;
582
*neg_ptr++ = neg_lut[*src_ptr++];
585
* Select the filter...
591
filter = gray_filter;
594
filter = graya_filter;
600
filter = rgba_filter;
608
memcpy (preview_dst, preview_src, width);
609
memcpy (preview_dst + width * (preview_height - 1),
610
preview_src + width * (preview_height - 1),
613
for (y = preview_height - 2, src_ptr = preview_src + width,
614
neg_ptr = preview_neg + width + img_bpp,
615
dst_ptr = preview_dst + width;
617
y --, src_ptr += width, neg_ptr += width, dst_ptr += width)
618
(*filter)(preview_width, src_ptr, dst_ptr, neg_ptr - width,
619
neg_ptr, neg_ptr + width);
621
gimp_preview_draw_buffer (preview, preview_dst, preview_width * img_bpp);
623
g_free (preview_src);
624
g_free (preview_neg);
625
g_free (preview_dst);
629
* 'gray_filter()' - Sharpen grayscale pixels.
633
gray_filter (gint width, /* I - Width of line in pixels */
634
guchar *src, /* I - Source line */
635
guchar *dst, /* O - Destination line */
636
intneg *neg0, /* I - Top negative coefficient line */
637
intneg *neg1, /* I - Middle negative coefficient line */
638
intneg *neg2) /* I - Bottom negative coefficient line */
640
intpos pixel; /* New pixel value */
647
pixel = (pos_lut[*src++] - neg0[-1] - neg0[0] - neg0[1] -
649
neg2[-1] - neg2[0] - neg2[1]);
650
pixel = (pixel + 4) >> 3;
651
*dst++ = CLAMP0255 (pixel);
663
* 'graya_filter()' - Sharpen grayscale+alpha pixels.
667
graya_filter (gint width, /* I - Width of line in pixels */
668
guchar *src, /* I - Source line */
669
guchar *dst, /* O - Destination line */
670
intneg *neg0, /* I - Top negative coefficient line */
671
intneg *neg1, /* I - Middle negative coefficient line */
672
intneg *neg2) /* I - Bottom negative coefficient line */
674
intpos pixel; /* New pixel value */
682
pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
684
neg2[-2] - neg2[0] - neg2[2]);
685
pixel = (pixel + 4) >> 3;
686
*dst++ = CLAMP0255 (pixel);
700
* 'rgb_filter()' - Sharpen RGB pixels.
704
rgb_filter (gint width, /* I - Width of line in pixels */
705
guchar *src, /* I - Source line */
706
guchar *dst, /* O - Destination line */
707
intneg *neg0, /* I - Top negative coefficient line */
708
intneg *neg1, /* I - Middle negative coefficient line */
709
intneg *neg2) /* I - Bottom negative coefficient line */
711
intpos pixel; /* New pixel value */
720
pixel = (pos_lut[*src++] - neg0[-3] - neg0[0] - neg0[3] -
722
neg2[-3] - neg2[0] - neg2[3]);
723
pixel = (pixel + 4) >> 3;
724
*dst++ = CLAMP0255 (pixel);
726
pixel = (pos_lut[*src++] - neg0[-2] - neg0[1] - neg0[4] -
728
neg2[-2] - neg2[1] - neg2[4]);
729
pixel = (pixel + 4) >> 3;
730
*dst++ = CLAMP0255 (pixel);
732
pixel = (pos_lut[*src++] - neg0[-1] - neg0[2] - neg0[5] -
734
neg2[-1] - neg2[2] - neg2[5]);
735
pixel = (pixel + 4) >> 3;
736
*dst++ = CLAMP0255 (pixel);
750
* 'rgba_filter()' - Sharpen RGBA pixels.
754
rgba_filter (gint width, /* I - Width of line in pixels */
755
guchar *src, /* I - Source line */
756
guchar *dst, /* O - Destination line */
757
intneg *neg0, /* I - Top negative coefficient line */
758
intneg *neg1, /* I - Middle negative coefficient line */
759
intneg *neg2) /* I - Bottom negative coefficient line */
761
intpos pixel; /* New pixel value */
771
pixel = (pos_lut[*src++] - neg0[-4] - neg0[0] - neg0[4] -
773
neg2[-4] - neg2[0] - neg2[4]);
774
pixel = (pixel + 4) >> 3;
775
*dst++ = CLAMP0255 (pixel);
777
pixel = (pos_lut[*src++] - neg0[-3] - neg0[1] - neg0[5] -
779
neg2[-3] - neg2[1] - neg2[5]);
780
pixel = (pixel + 4) >> 3;
781
*dst++ = CLAMP0255 (pixel);
783
pixel = (pos_lut[*src++] - neg0[-2] - neg0[2] - neg0[6] -
785
neg2[-2] - neg2[2] - neg2[6]);
786
pixel = (pixel + 4) >> 3;
787
*dst++ = CLAMP0255 (pixel);