1 /*
2  *
3  *
4  Copyright 2010-2011 Sebastian Kraft
5 
6  This file is part of GimpLensfun.
7 
8  GimpLensfun is free software: you can redistribute it and/or
9  modify it under the terms of the GNU General Public License
10  as published by the Free Software Foundation, either version
11  3 of the License, or (at your option) any later version.
12 
13  GimpLensfun is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied
15  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  PURPOSE. See the GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public
19  License along with GimpLensfun. If not, see
20  http://www.gnu.org/licenses/.
21 
22 */
23 
24 #include <stdio.h>
25 #include <math.h>
26 #include <string>
27 #include <vector>
28 #include <float.h>
29 
30 #include <lensfun/lensfun.h>
31 #include <libgimp/gimp.h>
32 #include <libgimp/gimpui.h>
33 
34 #include <exiv2/error.hpp>
35 #include <exiv2/image.hpp>
36 #include <exiv2/exif.hpp>
37 
38 #define VERSIONSTR "0.2.5-dev"
39 
40 
41 #ifndef DEBUG
42 #define DEBUG 0
43 #endif
44 
45 #include "LUT.hpp"
46 
47 using namespace std;
48 
49 //####################################################################
50 // Function declarations
51 static void query (void);
52 static void run   (const gchar      *name,
53                    gint              nparams,
54                    const GimpParam  *param,
55                    gint             *nreturn_vals,
56                    GimpParam       **return_vals);
57 
58 static gboolean create_dialog_window (GimpDrawable *drawable);
59 //--------------------------------------------------------------------
60 
61 
62 //####################################################################
63 // Global variables
64 GtkWidget *camera_combo, *maker_combo, *lens_combo;
65 GtkWidget *CorrVignetting, *CorrTCA, *CorrDistortion;
66 lfDatabase *ldb;
67 bool bComboBoxLock = false;
68 //--------------------------------------------------------------------
69 
70 
71 //####################################################################
72 // interpolation parameters
73 const int cLanczosWidth = 2;
74 const int cLanczosTableRes = 256;
75 LUT<float> LanczosLUT (cLanczosWidth * 2 * cLanczosTableRes + 1);
76 
77 typedef enum GL_INTERPOL {
78     GL_INTERPOL_NN,		// Nearest Neighbour
79     GL_INTERPOL_BL,		// Bilinear
80     GL_INTERPOL_LZ		// Lanczos
81 } glInterpolationType;
82 
83 
84 //####################################################################
85 // List of camera makers
86 const string    CameraMakers[] = {
87     "Canon",
88     "Casio",
89     "Fujifilm",
90     "GoPro",
91     "Kodak",
92     "Konica",
93     "Leica",
94     "Nikon",
95     "Olympus",
96     "Panasonic",
97     "Pentax",
98     "Ricoh",
99     "Samsung",
100     "Sigma",
101     "Sony",
102     "NULL"
103 };
104 //--------------------------------------------------------------------
105 
106 
107 //####################################################################
108 // struct for holding camera/lens info and parameters
109 typedef struct
110 {
111     int ModifyFlags;
112     bool Inverse;
113     std::string Camera;
114     std::string CamMaker;
115     std::string Lens;
116     float Scale;
117     float Crop;
118     float Focal;
119     float Aperture;
120     float Distance;
121     lfLensType TargetGeom;
122 } MyLensfunOpts;
123 //--------------------------------------------------------------------
124 static MyLensfunOpts sLensfunParameters =
125 {
126     LF_MODIFY_DISTORTION,
127     false,
128     "",
129     "",
130     "",
131     0.0,
132     0,
133     0,
134     0,
135     1.0,
136     LF_RECTILINEAR
137 };
138 //--------------------------------------------------------------------
139 
140 
141 //####################################################################
142 // struct for storing camera/lens info and parameters
143 typedef struct
144 {
145     int ModifyFlags;
146     bool Inverse;
147     char Camera[255];
148     char CamMaker[255];
149     char Lens[255];
150     float Scale;
151     float Crop;
152     float Focal;
153     float Aperture;
154     float Distance;
155     lfLensType TargetGeom;
156 } MyLensfunOptStorage;
157 //--------------------------------------------------------------------
158 static MyLensfunOptStorage sLensfunParameterStorage =
159 {
160     LF_MODIFY_DISTORTION,
161     false,
162     "",
163     "",
164     "",
165     0.0,
166     0,
167     0,
168     0,
169     1.0,
170     LF_RECTILINEAR
171 };
172 
173 
174 // GIMP Plugin
175 GimpPlugInInfo PLUG_IN_INFO =
176 {
177     NULL,
178     NULL,
179     query,
180     run
181 };
182 
MAIN()183 MAIN()
184 
185 
186 //####################################################################
187 // query() function
188 static void  query (void)
189 {
190     static GimpParamDef args[] =
191     {
192         {
193             GIMP_PDB_INT32,
194             (char *)"run-mode",
195             (char *)"Run mode"
196         },
197         {
198             GIMP_PDB_IMAGE,
199             (char *)"image",
200             (char *)"Input image"
201         },
202         {
203             GIMP_PDB_DRAWABLE,
204             (char *)"drawable",
205             (char *)"Input drawable"
206         }
207     };
208 
209     gimp_install_procedure (
210         "plug-in-lensfun",
211         "Correct lens distortion with lensfun",
212         "Correct lens distortion with lensfun",
213         "Sebastian Kraft",
214         "Copyright Sebastian Kraft",
215         "2010",
216         "_GimpLensfun...",
217         "RGB",
218         GIMP_PLUGIN,
219         G_N_ELEMENTS (args), 0,
220         args, NULL);
221 
222     gimp_plugin_menu_register ("plug-in-lensfun",
223                                "<Image>/Filters/Enhance");
224 }
225 //--------------------------------------------------------------------
226 
227 
228 //####################################################################
229 // Some helper functions
230 
231 // Round float to integer value
roundfloat2int(float d)232 int roundfloat2int(float d)
233 {
234     return d<0?d-.5:d+.5;
235 }
236 //--------------------------------------------------------------------
StrReplace(std::string & str,const std::string & old,const std::string & newstr)237 void StrReplace(std::string& str, const std::string& old, const std::string& newstr)
238 {
239     size_t pos = 0;
240     while ((pos = str.find(old, pos)) != std::string::npos)
241     {
242         str.replace(pos, old.length(), newstr);
243         pos += newstr.length();
244     }
245 }
246 //--------------------------------------------------------------------
StrCompare(const std::string & str1,const std::string & str2,bool CaseSensitive=false)247 int StrCompare(const std::string& str1, const std::string& str2, bool CaseSensitive = false)
248 {
249     string s1 = str1;
250     string s2 = str2;
251 
252     if (!CaseSensitive)
253     {
254         transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
255         transform(s2.begin(), s2.end(), s2.begin(), ::tolower);
256     }
257 
258     return s1.compare(s2);
259 
260 }
261 //--------------------------------------------------------------------
262 #ifdef POSIX
timespec2llu(struct timespec * ts)263 unsigned long long int timespec2llu(struct timespec *ts) {
264     return (unsigned long long int) ( ((unsigned long long int)ts->tv_sec * 1000000000) + ts->tv_nsec);
265 }
266 #endif
267 //--------------------------------------------------------------------
268 
269 
270 
271 //####################################################################
272 // Helper functions for printing debug output
273 #if DEBUG
PrintMount(const lfMount * mount)274 static void PrintMount (const lfMount *mount)
275 {
276     g_print ("Mount: %s\n", lf_mlstr_get (mount->Name));
277     if (mount->Compat)
278         for (int j = 0; mount->Compat [j]; j++)
279             g_print ("\tCompat: %s\n", mount->Compat [j]);
280 }
281 //--------------------------------------------------------------------
PrintCamera(const lfCamera * camera)282 static void PrintCamera (const lfCamera *camera)
283 {
284     g_print ("Camera: %s / %s %s%s%s\n",
285              lf_mlstr_get (camera->Maker),
286              lf_mlstr_get (camera->Model),
287              camera->Variant ? "(" : "",
288              camera->Variant ? lf_mlstr_get (camera->Variant) : "",
289              camera->Variant ? ")" : "");
290     g_print ("\tMount: %s\n", lf_db_mount_name (ldb, camera->Mount));
291     g_print ("\tCrop factor: %g\n", camera->CropFactor);
292 }
293 //--------------------------------------------------------------------
PrintLens(const lfLens * lens)294 static void PrintLens (const lfLens *lens)
295 {
296     g_print ("Lens: %s / %s\n",
297              lf_mlstr_get (lens->Maker),
298              lf_mlstr_get (lens->Model));
299     g_print ("\tCrop factor: %g\n", lens->CropFactor);
300     g_print ("\tFocal: %g-%g\n", lens->MinFocal, lens->MaxFocal);
301     g_print ("\tAperture: %g-%g\n", lens->MinAperture, lens->MaxAperture);
302     g_print ("\tCenter: %g,%g\n", lens->CenterX, lens->CenterY);
303     if (lens->Mounts)
304         for (int j = 0; lens->Mounts [j]; j++)
305             g_print ("\tMount: %s\n", lf_db_mount_name (ldb, lens->Mounts [j]));
306 }
307 //--------------------------------------------------------------------
PrintCameras(const lfCamera ** cameras)308 static void PrintCameras (const lfCamera **cameras)
309 {
310     if (cameras)
311         for (int i = 0; cameras [i]; i++)
312         {
313             g_print ("--- camera %d: ---\n", i + 1);
314             PrintCamera (cameras [i]);
315         }
316     else
317         g_print ("\t- failed\n");
318 }
319 //--------------------------------------------------------------------
PrintLenses(const lfLens ** lenses)320 static void PrintLenses (const lfLens **lenses)
321 {
322     if (lenses)
323         for (int i = 0; lenses [i]; i++)
324         {
325             g_print ("--- lens %d, score %d: ---\n", i + 1, lenses [i]->Score);
326             PrintLens (lenses [i]);
327         }
328     else
329         g_print ("\t- failed\n");
330 }
331 #endif
332 //--------------------------------------------------------------------
333 
334 
335 //####################################################################
336 // set dialog combo boxes to values
dialog_set_cboxes(string sNewMake,string sNewCamera,string sNewLens)337 static void dialog_set_cboxes( string sNewMake, string sNewCamera, string sNewLens) {
338 
339     vector<string> vCameraList;
340     vector<string> vLensList;
341 
342     const lfCamera**    cameras     = NULL;
343     const lfLens**  lenses      = NULL;
344     GtkTreeModel*   store       = NULL;
345 
346     int iCurrMakerID    = -1;
347     int iCurrCameraID   = -1;
348     int iCurrLensId     = -1;
349 
350     sLensfunParameters.CamMaker.clear();
351     sLensfunParameters.Camera.clear();
352     sLensfunParameters.Lens.clear();
353 
354     if (sNewMake.empty()==true)
355             return;
356 
357     // try to match maker with predefined list
358     int iNumMakers = 0;
359     for (int i = 0; CameraMakers[i].compare("NULL")!=0; i++)
360     {
361         if (StrCompare(CameraMakers[i], sNewMake)==0) {
362             gtk_combo_box_set_active(GTK_COMBO_BOX(maker_combo), i);
363             iCurrMakerID = i;
364         }
365         iNumMakers++;
366     }
367 
368     if (iCurrMakerID>=0)
369         sLensfunParameters.CamMaker = CameraMakers[iCurrMakerID];
370     else {
371         gtk_combo_box_append_text( GTK_COMBO_BOX(maker_combo), sNewMake.c_str());
372         gtk_combo_box_set_active(GTK_COMBO_BOX(maker_combo), iNumMakers);
373         iNumMakers++;
374         sLensfunParameters.CamMaker = sNewMake;
375     }
376 
377     // clear camera/lens combobox
378     store = gtk_combo_box_get_model( GTK_COMBO_BOX(camera_combo) );
379     gtk_list_store_clear( GTK_LIST_STORE( store ) );
380     store = gtk_combo_box_get_model( GTK_COMBO_BOX(lens_combo) );
381     gtk_list_store_clear( GTK_LIST_STORE( store ) );
382 
383     // get all cameras from maker out of database
384     cameras = ldb->FindCamerasExt (sLensfunParameters.CamMaker.c_str(), NULL, LF_SEARCH_LOOSE );
385     if (cameras) {
386         for (int i=0; cameras [i]; i++){
387             vCameraList.push_back(string(lf_mlstr_get(cameras[i]->Model)));
388         }
389         sort(vCameraList.begin(), vCameraList.end());
390     } else {
391         return;
392     }
393 
394     for (unsigned int i=0; i<vCameraList.size(); i++)
395     {
396         gtk_combo_box_append_text( GTK_COMBO_BOX( camera_combo ), vCameraList[i].c_str());
397         // set to active if model matches current camera
398         if ((!sNewCamera.empty()) && (StrCompare(sNewCamera, vCameraList[i])==0)) {
399             gtk_combo_box_set_active(GTK_COMBO_BOX(camera_combo), i);
400             sLensfunParameters.Camera = sNewCamera;
401         }
402 
403         if (StrCompare(string(lf_mlstr_get(cameras[i]->Model)), sNewCamera)==0)
404             iCurrCameraID = i;
405     }
406 
407     // return if camera is unidentified
408     if (iCurrCameraID == -1)
409     {
410         lf_free(cameras);
411         return;
412     }
413 
414     // find lenses for camera model
415     lenses = ldb->FindLenses (cameras[iCurrCameraID], NULL, NULL);
416     if (lenses) {
417         vLensList.clear();
418         for (int i = 0; lenses [i]; i++)
419             vLensList.push_back(string(lf_mlstr_get(lenses[i]->Model)));
420         sort(vLensList.begin(), vLensList.end());
421     } else {
422         lf_free(cameras);
423         return;
424     }
425 
426     for (unsigned int i = 0; i<vLensList.size(); i++)
427     {
428         gtk_combo_box_append_text( GTK_COMBO_BOX( lens_combo ), (vLensList[i]).c_str());
429 
430         // set active if lens matches current lens model
431         if ((!sNewLens.empty()) && (StrCompare(sNewLens, vLensList[i])==0)) {
432             gtk_combo_box_set_active(GTK_COMBO_BOX(lens_combo), i);
433             sLensfunParameters.Lens = sNewLens;
434         }
435 
436         if (StrCompare(string(lf_mlstr_get(lenses[i]->Model)), sNewLens)==0)
437             iCurrLensId = i;
438     }
439 
440     gtk_widget_set_sensitive(CorrTCA, false);
441     gtk_widget_set_sensitive(CorrVignetting, false);
442 
443     if (iCurrLensId >= 0)
444     {
445         if (lenses[iCurrLensId]->CalibTCA != NULL)
446             gtk_widget_set_sensitive(CorrTCA, true);
447         if (lenses[iCurrLensId]->CalibVignetting != NULL)
448             gtk_widget_set_sensitive(CorrVignetting, true);
449     }
450 
451     lf_free(lenses);
452     lf_free(cameras);
453 }
454 //--------------------------------------------------------------------
455 
456 //####################################################################
457 // dialog callback functions
458 static void
maker_cb_changed(GtkComboBox * combo,gpointer data)459 maker_cb_changed( GtkComboBox *combo,
460                   gpointer     data )
461 {
462     if (!bComboBoxLock) {
463         bComboBoxLock = true;
464         dialog_set_cboxes(gtk_combo_box_get_active_text(GTK_COMBO_BOX(maker_combo)),
465                           "",
466                           "");
467         bComboBoxLock = false;
468     }
469 }
470 //--------------------------------------------------------------------
471 static void
camera_cb_changed(GtkComboBox * combo,gpointer data)472 camera_cb_changed( GtkComboBox *combo,
473                    gpointer     data )
474 {
475     if (!bComboBoxLock) {
476         bComboBoxLock = true;
477         dialog_set_cboxes(string(gtk_combo_box_get_active_text(GTK_COMBO_BOX(maker_combo))),
478                           string(gtk_combo_box_get_active_text(GTK_COMBO_BOX(camera_combo))),
479                           "");
480         bComboBoxLock = false;
481     }
482 }
483 //--------------------------------------------------------------------
484 static void
lens_cb_changed(GtkComboBox * combo,gpointer data)485 lens_cb_changed( GtkComboBox *combo,
486                  gpointer     data )
487 {
488     if (!bComboBoxLock) {
489         bComboBoxLock = true;
490         dialog_set_cboxes(string(gtk_combo_box_get_active_text(GTK_COMBO_BOX(maker_combo))),
491                           string(gtk_combo_box_get_active_text(GTK_COMBO_BOX(camera_combo))),
492                           string(gtk_combo_box_get_active_text(GTK_COMBO_BOX(lens_combo))));
493         bComboBoxLock = false;
494     }
495 }
496 //--------------------------------------------------------------------
497 static void
focal_changed(GtkComboBox * combo,gpointer data)498 focal_changed( GtkComboBox *combo,
499                gpointer     data )
500 {
501     sLensfunParameters.Focal = (float) gtk_adjustment_get_value(GTK_ADJUSTMENT(data));
502 }
503 //--------------------------------------------------------------------
504 static void
aperture_changed(GtkComboBox * combo,gpointer data)505 aperture_changed( GtkComboBox *combo,
506                gpointer     data )
507 {
508     sLensfunParameters.Aperture = (float) gtk_adjustment_get_value(GTK_ADJUSTMENT(data));
509 }
510 //--------------------------------------------------------------------
511 static void
scalecheck_changed(GtkCheckButton * togglebutn,gpointer data)512 scalecheck_changed( GtkCheckButton *togglebutn,
513                     gpointer     data )
514 {
515     sLensfunParameters.Scale = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutn));
516 }
517 //--------------------------------------------------------------------
518 static void
modify_changed(GtkCheckButton * togglebutn,gpointer data)519 modify_changed( GtkCheckButton *togglebutn,
520                     gpointer     data )
521 {
522     sLensfunParameters.ModifyFlags = 0;
523     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CorrDistortion))
524         && GTK_WIDGET_SENSITIVE(CorrDistortion))
525         sLensfunParameters.ModifyFlags |= LF_MODIFY_DISTORTION;
526 
527     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CorrTCA))
528         && GTK_WIDGET_SENSITIVE(CorrTCA))
529         sLensfunParameters.ModifyFlags |= LF_MODIFY_TCA;
530 
531     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(CorrVignetting))
532         && GTK_WIDGET_SENSITIVE(CorrVignetting))
533         sLensfunParameters.ModifyFlags |= LF_MODIFY_VIGNETTING;
534 }//--------------------------------------------------------------------
535 
536 
537 //####################################################################
538 // Create gtk dialog window
create_dialog_window(GimpDrawable * drawable)539 static gboolean create_dialog_window (GimpDrawable *drawable)
540 {
541     GtkWidget *dialog;
542     GtkWidget *main_vbox;
543     GtkWidget *frame, *frame2;
544     GtkWidget *camera_label, *lens_label, *maker_label;
545     GtkWidget *focal_label, *aperture_label;
546     GtkWidget *scalecheck;
547 
548     GtkWidget *spinbutton;
549     GtkObject *spinbutton_adj;
550     GtkWidget *spinbutton_aperture;
551     GtkObject *spinbutton_aperture_adj;
552     GtkWidget *frame_label, *frame_label2;
553     GtkWidget *table, *table2;
554     gboolean   run;
555 
556     gint       iTableRow = 0;
557 
558     gimp_ui_init ("mylensfun", FALSE);
559 
560     dialog = gimp_dialog_new ("GIMP-Lensfun (v" VERSIONSTR ")", "mylensfun",
561                               NULL, GTK_DIALOG_MODAL ,
562                               gimp_standard_help_func, "plug-in-lensfun",
563                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
564                               GTK_STOCK_OK,     GTK_RESPONSE_OK,
565                               NULL);
566 
567     main_vbox = gtk_vbox_new (FALSE, 6);
568     gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
569     gtk_widget_show (main_vbox);
570 
571     frame = gtk_frame_new (NULL);
572     gtk_widget_show (frame);
573     gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
574     gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
575 
576     frame_label = gtk_label_new ("Camera/Lens Parameters");
577     gtk_widget_show (frame_label);
578     gtk_frame_set_label_widget (GTK_FRAME (frame), frame_label);
579     gtk_label_set_use_markup (GTK_LABEL (frame_label), TRUE);
580 
581     table = gtk_table_new(6, 2, TRUE);
582     gtk_table_set_homogeneous(GTK_TABLE(table), false);
583     gtk_table_set_row_spacings(GTK_TABLE(table), 2);
584     gtk_table_set_col_spacings(GTK_TABLE(table), 2);
585     gtk_container_set_border_width(GTK_CONTAINER(table), 10);
586 
587     // camera maker
588     maker_label = gtk_label_new ("Maker:");
589     gtk_misc_set_alignment(GTK_MISC(maker_label),0.0,0.5);
590     gtk_widget_show (maker_label);
591     gtk_table_attach(GTK_TABLE(table), maker_label, 0, 1, iTableRow, iTableRow+1, GTK_FILL, GTK_FILL, 0,0 );
592 
593     maker_combo = gtk_combo_box_new_text();
594     gtk_widget_show (maker_combo);
595 
596     for (int i = 0; StrCompare(CameraMakers[i], "NULL")!=0; i++)
597     {
598         gtk_combo_box_append_text( GTK_COMBO_BOX( maker_combo ), CameraMakers[i].c_str());
599     }
600 
601     gtk_table_attach_defaults(GTK_TABLE(table), maker_combo, 1, 2, iTableRow, iTableRow+1 );
602 
603     iTableRow++;
604 
605     // camera
606     camera_label = gtk_label_new ("Camera:");
607     gtk_misc_set_alignment(GTK_MISC(camera_label),0.0,0.5);
608     gtk_widget_show (camera_label);
609     gtk_table_attach(GTK_TABLE(table), camera_label, 0, 1, iTableRow, iTableRow+1, GTK_FILL, GTK_FILL, 0,0 );
610 
611     camera_combo = gtk_combo_box_new_text();
612     gtk_widget_show (camera_combo);
613 
614     gtk_table_attach_defaults(GTK_TABLE(table), camera_combo, 1,2, iTableRow, iTableRow+1 );
615 
616     iTableRow++;
617 
618     // lens
619     lens_label = gtk_label_new ("Lens:");
620     gtk_misc_set_alignment(GTK_MISC(lens_label),0.0,0.5);
621     gtk_widget_show (lens_label);
622     gtk_table_attach_defaults(GTK_TABLE(table), lens_label, 0,1,iTableRow, iTableRow+1 );
623 
624     lens_combo = gtk_combo_box_new_text();
625     gtk_widget_show (lens_combo);
626 
627     gtk_table_attach_defaults(GTK_TABLE(table), lens_combo, 1,2,iTableRow, iTableRow+1 );
628     iTableRow++;
629 
630     // focal length
631     focal_label = gtk_label_new("Focal length (mm):");
632     gtk_misc_set_alignment(GTK_MISC(focal_label),0.0,0.5);
633     gtk_widget_show (focal_label);
634     gtk_table_attach_defaults(GTK_TABLE(table), focal_label, 0,1,iTableRow, iTableRow+1 );
635 
636     spinbutton_adj = gtk_adjustment_new (sLensfunParameters.Focal, 0, 5000, 0.1, 0, 0);
637     spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_adj), 2, 1);
638     gtk_widget_show (spinbutton);
639     gtk_table_attach_defaults(GTK_TABLE(table), spinbutton, 1,2,iTableRow, iTableRow+1 );
640     iTableRow++;
641 
642     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
643 
644     // aperture
645     aperture_label = gtk_label_new("Aperture:");
646     gtk_misc_set_alignment(GTK_MISC(aperture_label),0.0,0.5);
647     gtk_widget_show (focal_label);
648     gtk_table_attach_defaults(GTK_TABLE(table), aperture_label, 0,1,iTableRow, iTableRow+1 );
649 
650     spinbutton_aperture_adj = gtk_adjustment_new (sLensfunParameters.Aperture, 0, 128, 0.1, 0, 0);
651     spinbutton_aperture = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton_aperture_adj), 2, 1);
652     gtk_widget_show (spinbutton_aperture);
653     gtk_table_attach_defaults(GTK_TABLE(table), spinbutton_aperture, 1,2,iTableRow, iTableRow+1 );
654     iTableRow++;
655 
656     gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton_aperture), TRUE);
657 
658     gtk_container_add (GTK_CONTAINER (frame), table);
659     gtk_widget_show_all(table);
660 
661     frame2 = gtk_frame_new (NULL);
662     gtk_widget_show (frame2);
663     gtk_box_pack_start (GTK_BOX (main_vbox), frame2, TRUE, TRUE, 0);
664     gtk_container_set_border_width (GTK_CONTAINER (frame2), 6);
665 
666     frame_label2 = gtk_label_new ("Processing Parameters");
667     gtk_widget_show (frame_label2);
668     gtk_frame_set_label_widget (GTK_FRAME (frame2), frame_label2);
669     gtk_label_set_use_markup (GTK_LABEL (frame_label2), TRUE);
670 
671     table2 = gtk_table_new(6, 2, TRUE);
672     gtk_table_set_homogeneous(GTK_TABLE(table2), false);
673     gtk_table_set_row_spacings(GTK_TABLE(table2), 2);
674     gtk_table_set_col_spacings(GTK_TABLE(table2), 2);
675     gtk_container_set_border_width(GTK_CONTAINER(table2), 10);
676 
677     iTableRow = 0;
678 
679     // scale to fit checkbox
680     scalecheck = gtk_check_button_new_with_label("Scale to fit");
681     //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
682     gtk_widget_show (scalecheck);
683     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scalecheck), !sLensfunParameters.Scale);
684     gtk_table_attach_defaults(GTK_TABLE(table2), scalecheck, 1,2,iTableRow, iTableRow+1 );
685     iTableRow++;
686 
687     // enable distortion correction
688     CorrDistortion = gtk_check_button_new_with_label("Distortion");
689     //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
690     gtk_widget_show (CorrDistortion);
691     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CorrDistortion), true);
692     gtk_table_attach_defaults(GTK_TABLE(table2), CorrDistortion, 1,2,iTableRow, iTableRow+1 );
693     iTableRow++;
694 
695     // enable vignetting correction
696     CorrVignetting = gtk_check_button_new_with_label("Vignetting");
697     //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
698     gtk_widget_show (CorrVignetting);
699     gtk_widget_set_sensitive(CorrVignetting, false);
700     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CorrVignetting), false);
701     gtk_table_attach_defaults(GTK_TABLE(table2), CorrVignetting, 1,2,iTableRow, iTableRow+1 );
702     iTableRow++;
703 
704     // enable TCA correction
705     CorrTCA = gtk_check_button_new_with_label("Chromatic Aberration");
706     //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
707     gtk_widget_show (CorrTCA);
708     gtk_widget_set_sensitive(CorrTCA, false);
709     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(CorrTCA), false);
710     gtk_table_attach_defaults(GTK_TABLE(table2), CorrTCA, 1,2,iTableRow, iTableRow+1 );
711     iTableRow++;
712 
713     gtk_container_add (GTK_CONTAINER (frame2), table2);
714     gtk_widget_show_all(table2);
715 
716     // try to set combo boxes to exif
717     dialog_set_cboxes(sLensfunParameters.CamMaker,
718                       sLensfunParameters.Camera,
719                       sLensfunParameters.Lens);
720 
721     // connect signals
722     g_signal_connect( G_OBJECT( maker_combo ), "changed",
723                       G_CALLBACK( maker_cb_changed ), NULL );
724     g_signal_connect( G_OBJECT( camera_combo ), "changed",
725                       G_CALLBACK( camera_cb_changed ), NULL );
726     g_signal_connect( G_OBJECT( lens_combo ), "changed",
727                       G_CALLBACK( lens_cb_changed ), NULL );
728     g_signal_connect (spinbutton_adj, "value_changed",
729                       G_CALLBACK (focal_changed), spinbutton_adj);
730     g_signal_connect (spinbutton_aperture_adj, "value_changed",
731                       G_CALLBACK (aperture_changed), spinbutton_aperture_adj);
732 
733     g_signal_connect( G_OBJECT( scalecheck ), "toggled",
734                       G_CALLBACK( scalecheck_changed ), NULL );
735     g_signal_connect( G_OBJECT( CorrDistortion ), "toggled",
736                       G_CALLBACK( modify_changed ), NULL );
737     g_signal_connect( G_OBJECT( CorrTCA ), "toggled",
738                       G_CALLBACK( modify_changed ), NULL );
739     g_signal_connect( G_OBJECT( CorrVignetting ), "toggled",
740                       G_CALLBACK( modify_changed ), NULL );
741 
742     // show and run
743     gtk_widget_show (dialog);
744     run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
745 
746     gtk_widget_destroy (dialog);
747     return run;
748 }
749 //--------------------------------------------------------------------
750 
751 
752 //####################################################################
753 // Interpolation functions
Lanczos(float x)754 inline float Lanczos(float x)
755 {
756     if ( (x<FLT_MIN) && (x>-FLT_MIN) )
757         return 1.0f;
758 
759     if ( (x >= cLanczosWidth) || (x <= (-1)*cLanczosWidth) )
760         return 0.0f;
761 
762     float xpi = x * static_cast<float>(M_PI);
763     return ( cLanczosWidth * sin(xpi) * sin(xpi/cLanczosWidth) ) / ( xpi*xpi );
764 }
765 //--------------------------------------------------------------------
InitInterpolation(glInterpolationType intType)766 void InitInterpolation(glInterpolationType intType)
767 {
768     switch(intType) {
769         case GL_INTERPOL_NN: break;
770         case GL_INTERPOL_BL: break;
771         case GL_INTERPOL_LZ:
772                 for (int i = -cLanczosWidth*cLanczosTableRes; i < cLanczosWidth*cLanczosTableRes; i++) {
773                     LanczosLUT[i + cLanczosWidth*cLanczosTableRes] = Lanczos(static_cast<float>(i)/static_cast<float>(cLanczosTableRes));
774                 }
775 
776                 break;
777     }
778 }
779 //--------------------------------------------------------------------
InterpolateLanczos(guchar * ImgBuffer,gint w,gint h,gint channels,float xpos,float ypos,int chan)780 inline int InterpolateLanczos(guchar *ImgBuffer, gint w, gint h, gint channels, float xpos, float ypos, int chan)
781 {
782 
783     int   xl   = int(xpos);
784     int   yl   = int(ypos);
785     float y    = 0.0f;
786     float norm = 0.0f;
787     float L    = 0.0f;
788 
789     // border checking
790     if ((xl-cLanczosWidth+1 < 0) ||
791         (xl+cLanczosWidth >= w)  ||
792         (yl-cLanczosWidth+1 < 0) ||
793         (yl+cLanczosWidth >= h))
794     {
795         return 0;
796     }
797 
798     // convolve with lanczos kernel
799     for (int i = xl-cLanczosWidth+1; i < xl+cLanczosWidth; i++) {
800         for (int j = yl-cLanczosWidth+1; j < yl+cLanczosWidth; j++) {
801             L = LanczosLUT[ (xpos - static_cast<float>(i))*static_cast<float>(cLanczosTableRes) + static_cast<float>(cLanczosWidth*cLanczosTableRes) ]
802                    * LanczosLUT[ (ypos - static_cast<float>(j))*static_cast<float>(cLanczosTableRes) + static_cast<float>(cLanczosWidth*cLanczosTableRes) ];
803             // L = Lanczos(xpos - static_cast<float>(i))
804             //                   * Lanczos(ypos - static_cast<float>(j));
805             y += static_cast<float>(ImgBuffer[ (channels*w*j) + (i*channels) + chan ]) * L;
806             norm += L;
807         }
808     }
809     // normalize
810     y = y / norm;
811 
812     // clip
813     if (y>255)
814         y = 255;
815     if (y<0)
816         y = 0;
817 
818     // round to integer and return
819     return roundfloat2int(y);
820 }
821 //--------------------------------------------------------------------
InterpolateLinear(guchar * ImgBuffer,gint w,gint h,gint channels,float xpos,float ypos,int chan)822 inline int InterpolateLinear(guchar *ImgBuffer, gint w, gint h, gint channels, float xpos, float ypos, int chan)
823 {
824     // interpolated values in x and y  direction
825     float   x1, x2, y;
826 
827     // surrounding integer rounded coordinates
828     int     xl, xr, yu, yl;
829 
830     xl = floor(xpos);
831     xr = ceil (xpos + 1e-10);
832     yu = floor(ypos);
833     yl = ceil (ypos + 1e-10);
834 
835     // border checking
836     if ((xl < 0)  ||
837         (xr >= w) ||
838         (yu < 0)  ||
839         (yl >= h))
840     {
841         return 0;
842     }
843 
844 
845     float px1y1 = (float) ImgBuffer[ (channels*w*yu) + (xl*channels) + chan ];
846     float px1y2 = (float) ImgBuffer[ (channels*w*yl) + (xl*channels) + chan ];
847     float px2y1 = (float) ImgBuffer[ (channels*w*yu) + (xr*channels) + chan ];
848     float px2y2 = (float) ImgBuffer[ (channels*w*yl) + (xr*channels) + chan ];
849 
850     x1 = (static_cast<float>(xr) - xpos)*px1y1 + (xpos - static_cast<float>(xl))*px2y1;
851     x2 = (static_cast<float>(xr) - xpos)*px1y2 + (xpos - static_cast<float>(xl))*px2y2;
852 
853     y  = (ypos - static_cast<float>(yu))*x2    + (static_cast<float>(yl) - ypos)*x1;
854 
855     return roundfloat2int(y);
856 }
857 //--------------------------------------------------------------------
InterpolateNearest(guchar * ImgBuffer,gint w,gint h,gint channels,float xpos,float ypos,int chan)858 inline int InterpolateNearest(guchar *ImgBuffer, gint w, gint h, gint channels, float xpos, float ypos, int chan)
859 {
860     int x = roundfloat2int(xpos);
861     int y = roundfloat2int(ypos);
862 
863 
864     // border checking
865     if ((x < 0)  ||
866         (x >= w) ||
867         (y < 0)  ||
868         (y >= h))
869     {
870         return 0;
871     }
872 
873     return ImgBuffer[ (channels*w*y) + (x*channels) + chan ];
874 }
875 //--------------------------------------------------------------------
876 
877 
878 //####################################################################
879 // Processing
process_image(GimpDrawable * drawable)880 static void process_image (GimpDrawable *drawable) {
881     gint         channels;
882     gint         x1, y1, x2, y2, imgwidth, imgheight;
883 
884     GimpPixelRgn rgn_in, rgn_out;
885     guchar *ImgBuffer;
886     guchar *ImgBufferOut;
887 
888     if ((sLensfunParameters.CamMaker.length()==0) ||
889         (sLensfunParameters.Camera.length()==0) ||
890         (sLensfunParameters.Lens.length()==0)) {
891             return;
892     }
893 
894     #ifdef POSIX
895     struct timespec profiling_start, profiling_stop;
896     #endif
897 
898     // get image size
899     gimp_drawable_mask_bounds (drawable->drawable_id,
900                                &x1, &y1,
901                                &x2, &y2);
902     imgwidth = x2-x1;
903     imgheight = y2-y1;
904 
905     // get number of channels
906     channels = gimp_drawable_bpp (drawable->drawable_id);
907 
908     gimp_pixel_rgn_init (&rgn_in,
909                          drawable,
910                          x1, y1,
911                          imgwidth, imgheight,
912                          FALSE, FALSE);
913     gimp_pixel_rgn_init (&rgn_out,
914                          drawable,
915                          x1, y1,
916                          imgwidth, imgheight,
917                          TRUE, TRUE);
918 
919     //Init input and output buffer
920     ImgBuffer = g_new (guchar, channels * (imgwidth+1) * (imgheight+1));
921     ImgBufferOut = g_new (guchar, channels * (imgwidth+1) * (imgheight+1));
922     InitInterpolation(GL_INTERPOL_LZ);
923 
924     // Copy pixel data from GIMP to internal buffer
925     gimp_pixel_rgn_get_rect (&rgn_in, ImgBuffer, x1, y1, imgwidth, imgheight);
926 
927     if (sLensfunParameters.Scale<1) {
928         sLensfunParameters.ModifyFlags |= LF_MODIFY_SCALE;
929     }
930 
931     const lfCamera **cameras = ldb->FindCamerasExt (sLensfunParameters.CamMaker.c_str(), sLensfunParameters.Camera.c_str());
932     sLensfunParameters.Crop = cameras[0]->CropFactor;
933 
934     const lfLens **lenses = ldb->FindLenses (cameras[0], NULL, sLensfunParameters.Lens.c_str());
935 
936     if (DEBUG) {
937         g_print("\nApplied settings:\n");
938         g_print("\tCamera: %s, %s\n", cameras[0]->Maker, cameras[0]->Model);
939         g_print("\tLens: %s\n", lenses[0]->Model);
940         g_print("\tFocal Length: %f\n", sLensfunParameters.Focal);
941         g_print("\tF-Stop: %f\n", sLensfunParameters.Aperture);
942         g_print("\tCrop Factor: %f\n", sLensfunParameters.Crop);
943         g_print("\tScale: %f\n", sLensfunParameters.Scale);
944 
945         #ifdef POSIX
946         clock_gettime(CLOCK_REALTIME, &profiling_start);
947         #endif
948     }
949 
950     //init lensfun modifier
951     lfModifier *mod = new lfModifier (lenses[0], sLensfunParameters.Crop, imgwidth, imgheight);
952     mod->Initialize (  lenses[0], LF_PF_U8, sLensfunParameters.Focal,
953                          sLensfunParameters.Aperture, sLensfunParameters.Distance, sLensfunParameters.Scale, sLensfunParameters.TargetGeom,
954                          sLensfunParameters.ModifyFlags, sLensfunParameters.Inverse);
955 
956     int iRowCount = 0;
957     #pragma omp parallel
958     {
959         // buffer containing undistorted coordinates for one row
960         float *UndistCoord = g_new (float, imgwidth*2*channels);
961 
962         //main loop for processing, iterate through rows
963         #pragma omp for
964         for (int i = 0; i < imgheight; i++)
965         {
966             mod->ApplyColorModification( &ImgBuffer[(channels*imgwidth*i)],
967                                         0, i, imgwidth, 1,
968                                         LF_CR_3(RED, GREEN, BLUE),
969                                         channels*imgwidth);
970 
971             mod->ApplySubpixelGeometryDistortion (0, i, imgwidth, 1, UndistCoord);
972 
973             float*  UndistIter = UndistCoord;
974             guchar *OutputBuffer = &ImgBufferOut[channels*imgwidth*i];
975             //iterate through subpixels in one row
976             for (int j = 0; j < imgwidth*channels; j += channels)
977             {
978                 *OutputBuffer = InterpolateLanczos(ImgBuffer, imgwidth, imgheight, channels, UndistIter [0], UndistIter [1], 0);
979                 OutputBuffer++;
980                 *OutputBuffer = InterpolateLanczos(ImgBuffer, imgwidth, imgheight, channels, UndistIter [2], UndistIter [3], 1);
981                 OutputBuffer++;
982                 *OutputBuffer = InterpolateLanczos(ImgBuffer, imgwidth, imgheight, channels, UndistIter [4], UndistIter [5], 2);
983                 OutputBuffer++;
984 
985                 // move pointer to next pixel
986                 UndistIter += 2 * 3;
987             }
988             #pragma omp atomic
989             iRowCount++;
990             //update progress bar only every N rows
991             if (iRowCount % 200 == 0) {
992                  #pragma omp critical
993                  {
994                  gimp_progress_update ((gdouble) (iRowCount - y1) / (gdouble) (imgheight));
995                  }
996             }
997         }
998         g_free(UndistCoord);
999     }
1000 
1001     delete mod;
1002 
1003     #ifdef POSIX
1004     if (DEBUG) {
1005         clock_gettime(CLOCK_REALTIME, &profiling_stop);
1006         unsigned long long int time_diff = timespec2llu(&profiling_stop) - timespec2llu(&profiling_start);
1007         g_print("\nPerformance: %12llu ns, %d pixel -> %llu ns/pixel\n", time_diff, imgwidth*imgheight, time_diff / (imgwidth*imgheight));
1008     }
1009     #endif
1010 
1011     //write data back to gimp
1012     gimp_pixel_rgn_set_rect (&rgn_out, ImgBufferOut, x1, y1, imgwidth, imgheight);
1013 
1014     gimp_drawable_flush (drawable);
1015     gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
1016     gimp_drawable_update (drawable->drawable_id,
1017                           x1, y1,
1018                           imgwidth, imgheight);
1019     gimp_displays_flush ();
1020     gimp_drawable_detach (drawable);
1021 
1022     // free memory
1023     g_free(ImgBufferOut);
1024     g_free(ImgBuffer);
1025 
1026     lf_free(lenses);
1027     lf_free(cameras);
1028 }
1029 //--------------------------------------------------------------------
1030 
1031 
1032 //####################################################################
1033 // Read camera and lens info from exif and try to find in database
1034 //
read_opts_from_exif(const char * filename)1035 static int read_opts_from_exif(const char *filename) {
1036 
1037     Exiv2::Image::AutoPtr Exiv2image;
1038     Exiv2::ExifData exifData;
1039 
1040     const lfCamera  **cameras    = 0;
1041     const lfCamera  *camera      = 0;
1042 
1043     const lfLens    **lenses     = 0;
1044     const lfLens    *lens        = 0;
1045     std::string LensNameMN;
1046 
1047     if (DEBUG) {
1048         g_print ("Reading exif data...");
1049     }
1050 
1051     try {
1052         // read exif from file
1053         Exiv2image = Exiv2::ImageFactory::open(string(filename));
1054         Exiv2image.get();
1055         Exiv2image->readMetadata();
1056         exifData = Exiv2image->exifData();
1057 
1058         if (exifData.empty()) {
1059             if (DEBUG) {
1060                 g_print ("no exif data found. \n");
1061             }
1062             return -1;
1063         }
1064     }
1065     catch (Exiv2::AnyError& e) {
1066         if (DEBUG) {
1067             g_print ("exception on reading data. \n");
1068         }
1069         return -1;
1070     }
1071 
1072     // search database for camera
1073     cameras = ldb->FindCameras (exifData["Exif.Image.Make"].toString().c_str(), exifData["Exif.Image.Model"].toString().c_str());
1074     if (cameras) {
1075         camera = cameras [0];
1076         sLensfunParameters.Crop = camera->CropFactor;
1077         sLensfunParameters.Camera = string(lf_mlstr_get(camera->Model));
1078         sLensfunParameters.CamMaker = string(lf_mlstr_get(camera->Maker));
1079     }  else {
1080         sLensfunParameters.CamMaker = exifData["Exif.Image.Make"].toString();
1081     }
1082     //PrintCameras(cameras, ldb);
1083 
1084     //Get lensID
1085     string CamMaker = exifData["Exif.Image.Make"].toString();
1086     transform(CamMaker.begin(), CamMaker.end(),CamMaker.begin(), ::tolower);
1087     string MakerNoteKey;
1088 
1089     //Select special MakerNote Tag for lens depending on Maker
1090     if ((CamMaker.find("pentax"))!=string::npos) {
1091         MakerNoteKey = "Exif.Pentax.LensType";
1092     }
1093     else if ((CamMaker.find("canon"))!=string::npos) {
1094         MakerNoteKey = "Exif.CanonCs.LensType";
1095     }
1096     else if ((CamMaker.find("minolta"))!=string::npos) {
1097         MakerNoteKey = "Exif.Minolta.LensID";
1098     }
1099     else if ((CamMaker.find("nikon"))!=string::npos) {
1100         MakerNoteKey = "Exif.NikonLd3.LensIDNumber";
1101         if (exifData[MakerNoteKey].toString().size()==0) {
1102             MakerNoteKey = "Exif.NikonLd2.LensIDNumber";
1103         }
1104         if (exifData[MakerNoteKey].toString().size()==0) {
1105             MakerNoteKey = "Exif.NikonLd1.LensIDNumber";
1106         }
1107     }
1108     else if ((CamMaker.find("olympus"))!=string::npos) {
1109         MakerNoteKey = "Exif.OlympusEq.LensType";
1110     }
1111     else {
1112         //Use default lens model tag for all other makers
1113         MakerNoteKey = "Exif.Photo.LensModel";
1114     }
1115 
1116     //Decode Lens ID
1117     if ((MakerNoteKey.size()>0) && (exifData[MakerNoteKey].toString().size()>0))  {
1118         Exiv2::ExifKey ek(MakerNoteKey);
1119         Exiv2::ExifData::const_iterator md = exifData.findKey(ek);
1120         if (md != exifData.end()) {
1121             LensNameMN = md->print(&exifData);
1122 
1123             //Modify some lens names for better searching in lfDatabase
1124             if ((CamMaker.find("nikon"))!=std::string::npos) {
1125                 StrReplace(LensNameMN, "Nikon", "");
1126                 StrReplace(LensNameMN, "Zoom-Nikkor", "");
1127             }
1128         }
1129     }
1130 
1131     if (camera) {
1132         if (LensNameMN.size()>8) {  // only take lens names with significant length
1133             lenses = ldb->FindLenses (camera, NULL, LensNameMN.c_str());
1134         } else {
1135             lenses = ldb->FindLenses (camera, NULL, NULL);
1136         }
1137         if (lenses) {
1138             lens = lenses[0];
1139             sLensfunParameters.Lens = string(lf_mlstr_get(lens->Model));
1140         }
1141         lf_free (lenses);
1142     }
1143     lf_free (cameras);
1144 
1145     sLensfunParameters.Focal = exifData["Exif.Photo.FocalLength"].toFloat();
1146     sLensfunParameters.Aperture = exifData["Exif.Photo.FNumber"].toFloat();
1147 
1148     if (DEBUG) {
1149         g_print("\nExif Data:\n");
1150         g_print("\tCamera: %s, %s\n", sLensfunParameters.CamMaker.c_str(), sLensfunParameters.Camera.c_str());
1151         g_print("\tLens: %s\n", sLensfunParameters.Lens.c_str());
1152         g_print("\tFocal Length: %f\n", sLensfunParameters.Focal);
1153         g_print("\tF-Stop: %f\n", sLensfunParameters.Aperture);
1154         g_print("\tCrop Factor: %f\n", sLensfunParameters.Crop);
1155         g_print("\tScale: %f\n", sLensfunParameters.Scale);
1156     }
1157 
1158     return 0;
1159 }
1160 //--------------------------------------------------------------------
1161 
1162 
1163 //####################################################################
1164 // store and load parameters and settings to/from gimp_data_storage
loadSettings()1165 static void loadSettings() {
1166 
1167     gimp_get_data ("plug-in-gimplensfun", &sLensfunParameterStorage);
1168 
1169     sLensfunParameters.ModifyFlags = sLensfunParameterStorage.ModifyFlags;
1170     sLensfunParameters.Inverse  = sLensfunParameterStorage.Inverse;
1171     if (strlen(sLensfunParameterStorage.Camera)>0)
1172         sLensfunParameters.Camera  = std::string(sLensfunParameterStorage.Camera);
1173     if (strlen(sLensfunParameterStorage.CamMaker)>0)
1174         sLensfunParameters.CamMaker  = std::string(sLensfunParameterStorage.CamMaker);
1175     if (strlen(sLensfunParameterStorage.Lens)>0)
1176         sLensfunParameters.Lens  = std::string(sLensfunParameterStorage.Lens);
1177     sLensfunParameters.Scale    = sLensfunParameterStorage.Scale;
1178     sLensfunParameters.Crop     = sLensfunParameterStorage.Crop;
1179     sLensfunParameters.Focal    = sLensfunParameterStorage.Focal;
1180     sLensfunParameters.Aperture = sLensfunParameterStorage.Aperture;
1181     sLensfunParameters.Distance = sLensfunParameterStorage.Distance;
1182     sLensfunParameters.TargetGeom = sLensfunParameterStorage.TargetGeom;
1183 }
1184 //--------------------------------------------------------------------
1185 
storeSettings()1186 static void storeSettings() {
1187 
1188     sLensfunParameterStorage.ModifyFlags = sLensfunParameters.ModifyFlags;
1189     sLensfunParameterStorage.Inverse  = sLensfunParameters.Inverse;
1190     strcpy( sLensfunParameterStorage.Camera, sLensfunParameters.Camera.c_str() );
1191     strcpy( sLensfunParameterStorage.CamMaker, sLensfunParameters.CamMaker.c_str() );
1192     strcpy( sLensfunParameterStorage.Lens, sLensfunParameters.Lens.c_str() );
1193     sLensfunParameterStorage.Scale    = sLensfunParameters.Scale;
1194     sLensfunParameterStorage.Crop     = sLensfunParameters.Crop;
1195     sLensfunParameterStorage.Focal    = sLensfunParameters.Focal;
1196     sLensfunParameterStorage.Aperture = sLensfunParameters.Aperture;
1197     sLensfunParameterStorage.Distance = sLensfunParameters.Distance;
1198     sLensfunParameterStorage.TargetGeom = sLensfunParameters.TargetGeom;
1199 
1200     gimp_set_data ("plug-in-gimplensfun", &sLensfunParameterStorage, sizeof (sLensfunParameterStorage));
1201 }
1202 //--------------------------------------------------------------------
1203 
1204 
1205 //####################################################################
1206 // Run()
1207 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)1208 run (const gchar*   name,
1209      gint           nparams,
1210      const GimpParam*   param,
1211      gint*          nreturn_vals,
1212      GimpParam**    return_vals)
1213 {
1214     static GimpParam    values[1];
1215     GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
1216     gint32              imageID;
1217     GimpRunMode         run_mode;
1218     GimpDrawable        *drawable;
1219 
1220     /* Setting mandatory output values */
1221     *nreturn_vals = 1;
1222     *return_vals  = values;
1223 
1224     values[0].type = GIMP_PDB_STATUS;
1225     values[0].data.d_status = status;
1226 
1227     drawable = gimp_drawable_get (param[2].data.d_drawable);
1228 
1229     gimp_progress_init ("Lensfun correction...");
1230 
1231     imageID = param[1].data.d_drawable;
1232 
1233     if (DEBUG) g_print ("Loading database...");
1234     //Load lensfun database
1235     ldb = new lfDatabase ();
1236     if (ldb->Load () != LF_NO_ERROR) {
1237         if (DEBUG) g_print ("failed!\n");
1238     } else {
1239         if (DEBUG) g_print ("OK\n");
1240     }
1241 
1242     // read exif data
1243     const gchar *filename = gimp_image_get_filename(imageID);
1244     if (DEBUG) g_print ("Image file path: %s\n", filename);
1245 
1246     if ((filename == NULL) || (read_opts_from_exif(filename) != 0)) {
1247 	    loadSettings();
1248     }
1249 
1250     run_mode = GimpRunMode(param[0].data.d_int32);
1251     if (run_mode == GIMP_RUN_INTERACTIVE)
1252     {
1253 	    if (DEBUG) g_print ("Creating dialog...\n");
1254 	    /* Display the dialog */
1255 	    if (create_dialog_window (drawable)) {
1256 		    process_image(drawable);
1257 	    }
1258     }
1259     else
1260     {
1261 	    /* If run_mode is non-interactive, we use the configuration
1262 	     * from read_opts_from_exif. If that fails, we use the stored settings
1263 	     * (loadSettings()), e.g. the settings that have been made in the last
1264 	     * interactive use of the plugin. One day, all settings should be
1265 	     * available as arguments in non-interactive mode.
1266 	     */
1267 	    process_image(drawable);
1268     }
1269 
1270     storeSettings();
1271 
1272     delete ldb;
1273 }
1274