1
/* GIMP - The GNU Image Manipulation Program
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
4
* Lens plug-in - adjust for lens distortion
5
* Copyright (C) 2001-2005 David Hodson hodsond@acm.org
6
* Many thanks for Lars Clausen for the original inspiration,
7
* useful discussion, optimisation and improvements.
8
* Framework borrowed from many similar plugins...
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
#include <libgimp/gimp.h>
30
#include <libgimp/gimpui.h>
32
#include "libgimp/stdplugins-intl.h"
35
#define PLUG_IN_PROC "plug-in-lens-distortion"
36
#define PLUG_IN_BINARY "lens"
38
#define RESPONSE_RESET 1
40
#define LENS_MAX_PIXEL_DEPTH 4
55
gdouble normallise_radius_sq;
65
/* Declare local functions. */
67
static void query (void);
68
static void run (const gchar *name,
70
const GimpParam *param,
72
GimpParam **return_vals);
74
static void lens_distort (GimpDrawable *drawable);
75
static gboolean lens_dialog (GimpDrawable *drawable);
78
static LensValues vals = { 0.0, 0.0, 0.0, 0.0 };
79
static LensCalcValues calc_vals;
81
static gint drawable_width, drawable_height;
82
static guchar background_color[4];
85
const GimpPlugInInfo PLUG_IN_INFO =
89
query, /* query_proc */
99
static GimpParamDef args[] =
101
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
102
{ GIMP_PDB_IMAGE, "image", "Input image (unused)" },
103
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
104
{ GIMP_PDB_FLOAT, "offset-x", "Effect centre offset in X" },
105
{ GIMP_PDB_FLOAT, "offset-y", "Effect centre offset in Y" },
106
{ GIMP_PDB_FLOAT, "main-adjust",
107
"Amount of second-order distortion" },
108
{ GIMP_PDB_FLOAT, "edge-adjust",
109
"Amount of fourth-order distortion" },
110
{ GIMP_PDB_FLOAT, "rescale", "Rescale overall image size" },
111
{ GIMP_PDB_FLOAT, "brighten", "Adjust brightness in corners" }
114
gimp_install_procedure (PLUG_IN_PROC,
115
N_("Corrects lens distortion"),
116
"Corrects barrel or pincushion lens distortion.",
117
"David Hodson, Aurimas Juška",
120
N_("Lens Distortion..."),
123
G_N_ELEMENTS (args), 0,
126
gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
130
run (const gchar *name,
132
const GimpParam *param,
134
GimpParam **return_vals)
136
static GimpParam values[1];
137
GimpDrawable *drawable;
140
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
141
GimpRunMode run_mode;
143
run_mode = param[0].data.d_int32;
144
image_ID = param[1].data.d_int32;
146
values[0].type = GIMP_PDB_STATUS;
147
values[0].data.d_status = status;
151
drawable = gimp_drawable_get (param[2].data.d_drawable);
153
drawable_width = drawable->width;
154
drawable_height = drawable->height;
156
/* Get background color */
157
gimp_context_get_background (&background);
158
gimp_rgb_set_alpha (&background, 0.0);
159
gimp_drawable_get_color_uchar (drawable->drawable_id, &background,
162
/* Set the tile cache size */
163
gimp_tile_cache_ntiles (2 * MAX (drawable->ntile_rows, drawable->ntile_cols));
166
*return_vals = values;
169
case GIMP_RUN_INTERACTIVE:
170
gimp_get_data (PLUG_IN_PROC, &vals);
171
if (! lens_dialog (drawable))
175
case GIMP_RUN_NONINTERACTIVE:
177
status = GIMP_PDB_CALLING_ERROR;
179
if (status == GIMP_PDB_SUCCESS)
181
vals.centre_x = param[3].data.d_float;
182
vals.centre_y = param[4].data.d_float;
183
vals.square_a = param[5].data.d_float;
184
vals.quad_a = param[6].data.d_float;
185
vals.scale_a = param[7].data.d_float;
186
vals.brighten = param[8].data.d_float;
191
case GIMP_RUN_WITH_LAST_VALS:
192
gimp_get_data (PLUG_IN_PROC, &vals);
199
if ( status == GIMP_PDB_SUCCESS )
201
lens_distort (drawable);
203
if (run_mode != GIMP_RUN_NONINTERACTIVE)
204
gimp_displays_flush ();
206
if (run_mode == GIMP_RUN_INTERACTIVE)
207
gimp_set_data (PLUG_IN_PROC, &vals, sizeof (LensValues));
209
gimp_drawable_detach (drawable);
212
values[0].data.d_status = status;
216
lens_get_source_coords (gdouble i,
229
off_x = i - calc_vals.centre_x;
230
off_y = j - calc_vals.centre_y;
231
radius_sq = (off_x * off_x) + (off_y * off_y);
233
radius_sq *= calc_vals.normallise_radius_sq;
235
radius_mult = radius_sq * calc_vals.mult_sq + radius_sq * radius_sq *
238
radius_mult = calc_vals.rescale * (1.0 + radius_mult);
240
*x = calc_vals.centre_x + radius_mult * off_x;
241
*y = calc_vals.centre_y + radius_mult * off_y;
245
lens_setup_calc (gint width, gint height)
247
calc_vals.normallise_radius_sq =
248
4.0 / (width * width + height * height);
250
calc_vals.centre_x = width * (100.0 + vals.centre_x) / 200.0;
251
calc_vals.centre_y = height * (100.0 + vals.centre_y) / 200.0;
252
calc_vals.mult_sq = vals.square_a / 200.0;
253
calc_vals.mult_qd = vals.quad_a / 200.0;
254
calc_vals.rescale = pow(2.0, - vals.scale_a / 100.0);
255
calc_vals.brighten = - vals.brighten / 10.0;
259
* Catmull-Rom cubic interpolation
261
* equally spaced points p0, p1, p2, p3
262
* interpolate 0 <= u < 1 between p1 and p2
264
* (1 u u^2 u^3) ( 0.0 1.0 0.0 0.0 ) (p0)
265
* ( -0.5 0.0 0.5 0.0 ) (p1)
266
* ( 1.0 -2.5 2.0 -0.5 ) (p2)
267
* ( -0.5 1.5 -1.5 0.5 ) (p3)
272
lens_cubic_interpolate (const guchar *src,
281
gfloat um1, u, up1, up2;
282
gfloat vm1, v, vp1, vp2;
284
gfloat verts[4 * LENS_MAX_PIXEL_DEPTH];
286
um1 = ((-0.5 * dx + 1.0) * dx - 0.5) * dx;
287
u = (1.5 * dx - 2.5) * dx * dx + 1.0;
288
up1 = ((-1.5 * dx + 2.0) * dx + 0.5) * dx;
289
up2 = (0.5 * dx - 0.5) * dx * dx;
291
vm1 = ((-0.5 * dy + 1.0) * dy - 0.5) * dy;
292
v = (1.5 * dy - 2.5) * dy * dy + 1.0;
293
vp1 = ((-1.5 * dy + 2.0) * dy + 0.5) * dy;
294
vp2 = (0.5 * dy - 0.5) * dy * dy;
296
/* Note: if dst_depth < src_depth, we calculate unneeded pixels here */
297
/* later - select or create index array */
298
for (c = 0; c < 4 * src_depth; ++c)
300
verts[c] = vm1 * src[c] + v * src[c+row_stride] +
301
vp1 * src[c+row_stride*2] + vp2 * src[c+row_stride*3];
304
for (c = 0; c < dst_depth; ++c)
308
result = um1 * verts[c] + u * verts[c+src_depth] +
309
up1 * verts[c+src_depth*2] + up2 * verts[c+src_depth*3];
313
dst[c] = CLAMP (result, 0, 255);
318
lens_distort_func (gint ix,
322
GimpPixelFetcher *pft)
324
gdouble src_x, src_y, mag;
326
guchar pixel_buffer[16 * LENS_MAX_PIXEL_DEPTH];
332
lens_get_source_coords (ix, iy, &src_x, &src_y, &mag);
334
brighten = 1.0 + mag * calc_vals.brighten;
335
x_int = floor (src_x);
338
y_int = floor (src_y);
341
pixel = pixel_buffer;
342
for (y = y_int - 1; y <= y_int + 2; y++)
344
for (x = x_int -1; x <= x_int + 2; x++)
346
if (x >= 0 && y >= 0 &&
347
x < drawable_width && y < drawable_height)
349
gimp_pixel_fetcher_get_pixel (pft, x, y, pixel);
355
for (i = 0; i < bpp; i++)
356
pixel[i] = background_color[i];
363
lens_cubic_interpolate (pixel_buffer, bpp * 4, bpp,
364
dest, bpp, dx, dy, brighten);
368
lens_distort (GimpDrawable *drawable)
370
GimpRgnIterator *iter;
371
GimpPixelFetcher *pft;
374
lens_setup_calc (drawable->width, drawable->height);
376
pft = gimp_pixel_fetcher_new (drawable, FALSE);
378
gimp_context_get_background (&background);
379
gimp_rgb_set_alpha (&background, 0.0);
380
gimp_pixel_fetcher_set_bg_color (pft, &background);
381
gimp_pixel_fetcher_set_edge_mode (pft, GIMP_PIXEL_FETCHER_EDGE_BACKGROUND);
383
gimp_progress_init (_("Lens distortion"));
385
iter = gimp_rgn_iterator_new (drawable, 0);
386
gimp_rgn_iterator_dest (iter, (GimpRgnFuncDest) lens_distort_func, pft);
387
gimp_rgn_iterator_free (iter);
389
gimp_pixel_fetcher_destroy (pft);
393
lens_distort_preview (GimpDrawable *drawable,
394
GimpPreview *preview)
398
gint width, height, bpp;
400
GimpPixelFetcher *pft;
403
pft = gimp_pixel_fetcher_new (drawable, FALSE);
405
gimp_context_get_background (&background);
406
gimp_rgb_set_alpha (&background, 0.0);
407
gimp_pixel_fetcher_set_bg_color (pft, &background);
408
gimp_pixel_fetcher_set_edge_mode (pft, GIMP_PIXEL_FETCHER_EDGE_BACKGROUND);
410
lens_setup_calc (drawable->width, drawable->height);
412
dest = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
413
&width, &height, &bpp);
416
for (y = 0; y < height; y++)
418
for (x = 0; x < width; x++)
422
gimp_preview_untransform (preview, x, y, &sx, &sy);
424
lens_distort_func (sx, sy, pixel, bpp, pft);
430
gimp_pixel_fetcher_destroy (pft);
432
gimp_preview_draw_buffer (preview, dest, width * bpp);
436
/* UI callback functions */
438
static GSList *adjustments = NULL;
441
lens_dialog_reset (void)
445
for (list = adjustments; list; list = list->next)
446
gtk_adjustment_set_value (GTK_ADJUSTMENT (list->data), 0.0);
450
lens_response (GtkWidget *widget,
457
lens_dialog_reset ();
460
case GTK_RESPONSE_OK:
465
gtk_widget_destroy (GTK_WIDGET (widget));
471
lens_dialog (GimpDrawable *drawable)
474
GtkWidget *main_vbox;
479
gboolean run = FALSE;
481
gimp_ui_init (PLUG_IN_BINARY, FALSE);
483
dialog = gimp_dialog_new (_("Lens Distortion"), PLUG_IN_BINARY,
485
gimp_standard_help_func, PLUG_IN_PROC,
487
GIMP_STOCK_RESET, RESPONSE_RESET,
488
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
489
GTK_STOCK_OK, GTK_RESPONSE_OK,
493
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
499
gimp_window_set_transient (GTK_WINDOW (dialog));
501
main_vbox = gtk_vbox_new (FALSE, 12);
502
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
503
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
504
gtk_widget_show (main_vbox);
506
preview = gimp_zoom_preview_new (drawable);
507
gtk_box_pack_start_defaults (GTK_BOX (main_vbox), preview);
508
gtk_widget_show (preview);
509
g_signal_connect_swapped (preview, "invalidated",
510
G_CALLBACK (lens_distort_preview),
513
table = gtk_table_new (6, 3, FALSE);
514
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
515
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
516
gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
517
gtk_widget_show (table);
519
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
521
vals.square_a, -100.0, 100.0, 0.1, 1.0, 3,
524
adjustments = g_slist_append (adjustments, adj);
526
g_signal_connect (adj, "value-changed",
527
G_CALLBACK (gimp_double_adjustment_update),
529
g_signal_connect_swapped (adj, "value-changed",
530
G_CALLBACK (gimp_preview_invalidate),
533
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
535
vals.quad_a, -100.0, 100.0, 0.1, 1.0, 3,
538
adjustments = g_slist_append (adjustments, adj);
540
g_signal_connect (adj, "value-changed",
541
G_CALLBACK (gimp_double_adjustment_update),
543
g_signal_connect_swapped (adj, "value-changed",
544
G_CALLBACK (gimp_preview_invalidate),
547
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
549
vals.scale_a, -100.0, 100.0, 0.1, 1.0, 3,
552
adjustments = g_slist_append (adjustments, adj);
554
g_signal_connect (adj, "value-changed",
555
G_CALLBACK (gimp_double_adjustment_update),
557
g_signal_connect_swapped (adj, "value-changed",
558
G_CALLBACK (gimp_preview_invalidate),
561
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
562
_("_Brighten:"), 120, 6,
563
vals.brighten, -100.0, 100.0, 0.1, 1.0, 3,
566
adjustments = g_slist_append (adjustments, adj);
568
g_signal_connect (adj, "value-changed",
569
G_CALLBACK (gimp_double_adjustment_update),
571
g_signal_connect_swapped (adj, "value-changed",
572
G_CALLBACK (gimp_preview_invalidate),
575
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
576
_("_X shift:"), 120, 6,
577
vals.centre_x, -100.0, 100.0, 0.1, 1.0, 3,
580
adjustments = g_slist_append (adjustments, adj);
582
g_signal_connect (adj, "value-changed",
583
G_CALLBACK (gimp_double_adjustment_update),
585
g_signal_connect_swapped (adj, "value-changed",
586
G_CALLBACK (gimp_preview_invalidate),
589
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
590
_("_Y shift:"), 120, 6,
591
vals.centre_y, -100.0, 100.0, 0.1, 1.0, 3,
594
adjustments = g_slist_append (adjustments, adj);
596
g_signal_connect (adj, "value-changed",
597
G_CALLBACK (gimp_double_adjustment_update),
599
g_signal_connect_swapped (adj, "value-changed",
600
G_CALLBACK (gimp_preview_invalidate),
603
g_signal_connect (dialog, "response",
604
G_CALLBACK (lens_response),
606
g_signal_connect (dialog, "destroy",
607
G_CALLBACK (gtk_main_quit),
610
gtk_widget_show (dialog);
614
g_slist_free (adjustments);