~ubuntu-branches/ubuntu/vivid/fotoxx/vivid

« back to all changes in this revision

Viewing changes to fotoxx-11.11.1.cc

  • Committer: Package Import Robot
  • Author(s): Santiago Torres Batan
  • Date: 2011-11-07 16:37:55 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20111107163755-2fdevt5hu7tcguhx
Tags: 11.11.1-1
* New upstream release
  
* debian/watch
  -Fix lintian warning: debian_watch_contains_dh_make_template

* debian/rules
  -Remove -XTRANLATIONS -XCHANGES params in dh_compress.
  -Fix lintian warning: debian_rules_missing_recommends_targets
  -Add symlink to avoid duplicate documentation at intalldocs rule.
  -Remove unnecessary files.
 
* debian/control
 -Bump Standars-Version to 3.9.2
 -Remov libfreeimage-dev, libimage-exiftool-perf from
   Build-Depends
 -Add xgd-utils and libimage-exiftool-perf to Recommends
 -Add brasero to Suggests

* debian/copyright
  -Update copyright to 2010, 2011
 
* debian/patches/ 
  -Fix lintian warning format-3.0-but-debian-changes-patch and
   (closes: #643119) by creating:
  -docs_dir.patch: changes upstream sources for the program to
   know where documentation is installed
  -makefile_changes.patch: edit makefile for installation
   Removes the depencies check by dependencies.sh. (closes: #622315)
   Fix FTBFS with binutils-gold. (closes: #610545)
   fotoxxfotoxx.desktop was created by mistake. (closes: #610544)
   Thanks to Mahyuddin Susanto.

* debian/doc-base
  -Register fotoxx user guide documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**************************************************************************
 
2
 
 
3
   Fotoxx      edit photos and manage collections
 
4
 
 
5
   Copyright 2007 2008 2009 2010 2011  Michael Cornelison
 
6
   Source URL: http://kornelix.squarespace.com/fotoxx
 
7
   Contact: kornelix2@googlemail.com
 
8
   
 
9
   This program is free software: you can redistribute it and/or modify
 
10
   it under the terms of the GNU General Public License as published by
 
11
   the Free Software Foundation, either version 3 of the License, or
 
12
   (at your option) any later version.
 
13
 
 
14
   This program is distributed in the hope that it will be useful,
 
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
   GNU General Public License for more details.
 
18
 
 
19
   You should have received a copy of the GNU General Public License
 
20
   along with this program. If not, see http://www.gnu.org/licenses/.
 
21
 
 
22
***************************************************************************/
 
23
 
 
24
#define EX                                                                 //  disable extern declarations
 
25
#include "fotoxx.h"
 
26
 
 
27
#define fversion "Fotoxx v.11.11.1"                                        //  this fotoxx version
 
28
#define flicense "Free software - GNU General Public License v.3"
 
29
#define fhomepage "http://kornelix.squarespace.com/fotoxx"
 
30
#define ftranslators                                                                \
 
31
      "Translators: Jie Luo, Eugenio Baldi, Justa, Peter Landgren, \n"              \
 
32
      "Alexander Krasnopolsky, Arthur Kalverboer, André Campos Rodovalho, \n"       \
 
33
      "Stanislas Zeller, Doriano Blengino"
 
34
#define fcredits "Software used: GTK, libtiff, ufraw-batch, exiftool"
 
35
#define fcontact "Bug reports: kornelix2@googlemail.com"
 
36
 
 
37
 
 
38
char     *initial_file = 0;                                                //  initial file on command line
 
39
 
 
40
 
 
41
//  fotoxx main program
 
42
 
 
43
int main(int argc, char *argv[])
 
44
{
 
45
   using namespace zfuncs;
 
46
 
 
47
   char           lang[8] = "", *pp;
 
48
   int            err;
 
49
   struct stat    statb;
 
50
   int            Fclone=0, cloxx=0, cloyy=0, cloww=0, clohh=0;
 
51
   GdkScreen      *screen;
 
52
   int            Fsyncfiles = 0;
 
53
   
 
54
   printf(fversion "\n");                                                  //  print fotoxx version
 
55
   if (argc > 1 && strEqu(argv[1],"-v")) return 0;                         //  no more is wanted
 
56
   
 
57
   //  initialize externals
 
58
 
 
59
   maxcolor = 0xffff;
 
60
   for (int ii = 0; ii < 8; ii++) wtnx[ii] = ii;
 
61
   strcpy(jpeg_quality,def_jpeg_quality);
 
62
   mwgeom[0] = mwgeom[1] = 100;                                            //  default main window geometry    v.11.07
 
63
   mwgeom[2] = 700; mwgeom[3] = 500;
 
64
   Mscale = 1.0;
 
65
   trimsize[0] = 1600;
 
66
   trimsize[1] = 1000;
 
67
   editresize[0] = 1600;
 
68
   editresize[1] = 1200;
 
69
   batchresize[0] = 1600;
 
70
   batchresize[1] = 1200;
 
71
   emailsize[0] = 1000;
 
72
   emailsize[1] = 800;
 
73
   gridcount[0] = gridcount[1] = 5;                                        //  set default grid line counts
 
74
   sa_pixRGB = &green;
 
75
   ss_funcs[0] = 1;
 
76
   tbar_style = "both";                                                    //  default toolbar style
 
77
   Ffirsttime = 1;                                                         //  first time startup           v.11.11
 
78
   fsync_lock = "fotoxx_syncfiles";
 
79
 
 
80
   //  command line args
 
81
 
 
82
   gtk_init(&argc,&argv);                                                  //  initz. GTK & strip GTK command line params
 
83
 
 
84
   for (int ii = 1; ii < argc; ii++)                                       //  command line parameters
 
85
   {
 
86
      pp = argv[ii];
 
87
      if (strcmpv(pp,"-debug","-d",null))                                  //  -debug
 
88
         Fdebug = 1;
 
89
      else if (strcmpv(pp,"-lang","-l",null) && argc > ii+1)               //  -lang lc_RC      (language/region code)
 
90
         strncpy0(lang,argv[++ii],7);
 
91
      else if (strcmpv(pp,"-clone1","-c1",null))                           //  -clone1       (clone1/2 not user options)
 
92
         Fclone = 1;
 
93
      else if (strcmpv(pp,"-clone2","-c2",null)) {                         //  -clone2 xpos ypos               v.11.07
 
94
         Fclone = 2;
 
95
         cloxx = atoi(argv[ii+1]);                                         //  window position and size
 
96
         cloyy = atoi(argv[ii+2]);                                         //    passed from parent instance
 
97
         cloww = atoi(argv[ii+3]);
 
98
         clohh = atoi(argv[ii+4]);
 
99
         ii += 4;
 
100
      }
 
101
      else if (strcmpv(pp,"-slideshow","-ss",null) && argc > ii+1)         //  -slideshow /image/file.jpg      v.11.04
 
102
         SS_imagefile = strdupz(argv[++ii]);
 
103
      else if (strcmpv(pp,"-music","-m",null) && argc > ii+1)              //  -music /music/file.ogg          v.11.04
 
104
         SS_musicfile = strdupz(argv[++ii]);
 
105
      else if (strcmpv(pp,"-recent","-r",null))                            //  recent files gallery            v.11.07
 
106
         Frecent = 1;
 
107
      else if (strcmpv(pp,"-previous","-prev","-p",null))                  //  open previous file              v.11.07
 
108
         Fprev = 1;
 
109
      else if (strcmpv(pp,"-syncfiles",null)) {                            //  spawned sync files process      v.11.11
 
110
         Fsyncfiles = 1;
 
111
         pp = argv[++ii];
 
112
         if (strEqu(pp,"auto")) Fautosync = 1;                             //  set auto or manual sync
 
113
         if (strEqu(pp,"manual")) Fmansync = 1;
 
114
      }
 
115
      else  initial_file = strdupz(pp);                                    //  must be initial file or directory
 
116
   }
 
117
 
 
118
   zinitapp("fotoxx",null);                                                //  get app directories
 
119
 
 
120
   ZTXinit(lang);                                                          //  setup locale and translations
 
121
 
 
122
   *topdirk_file = 0;
 
123
   strncatv(topdirk_file,199,get_zuserdir(),"/top_directory",null);        //  /home/user/.fotoxx/top_directory
 
124
 
 
125
   *search_index_file = 0;
 
126
   strncatv(search_index_file,199,get_zuserdir(),"/search_index",null);    //  /home/user/.fotoxx/search_index
 
127
 
 
128
   *tags_defined_file = 0;
 
129
   strncatv(tags_defined_file,199,get_zuserdir(),"/tags_defined",null);    //  /home/user/.fotoxx/tags_defined
 
130
 
 
131
   *recentfiles_file = 0;
 
132
   strncatv(recentfiles_file,199,get_zuserdir(),"/recent_files",null);     //  /home/user/.fotoxx/recent_files
 
133
 
 
134
   *saved_areas_dirk = 0;
 
135
   strncatv(saved_areas_dirk,199,get_zuserdir(),"/saved_areas/",null);     //  /home/user/.fotoxx/saved_areas/
 
136
   err = stat(saved_areas_dirk,&statb);
 
137
   if (err) mkdir(saved_areas_dirk,0750);
 
138
 
 
139
   *collections_dirk = 0;
 
140
   strncatv(collections_dirk,199,get_zuserdir(),"/collections/",null);     //  /home/user/.fotoxx/collections/
 
141
   err = stat(collections_dirk,&statb);
 
142
   if (err) mkdir(collections_dirk,0750);
 
143
 
 
144
   *saved_curves_dirk = 0;
 
145
   strncatv(saved_curves_dirk,199,get_zuserdir(),"/saved_curves/",null);   //  /home/user/.fotoxx/saved_curves/
 
146
   err = stat(saved_curves_dirk,&statb);
 
147
   if (err) mkdir(saved_curves_dirk,0750);
 
148
 
 
149
   *annotations_dirk = 0;
 
150
   strncatv(annotations_dirk,199,get_zuserdir(),"/annotations/",null);     //  /home/user/.fotoxx/annotations/
 
151
   err = stat(annotations_dirk,&statb);
 
152
   if (err) mkdir(annotations_dirk,0750);
 
153
 
 
154
   load_params();                                                          //  restore parameters from last session
 
155
   
 
156
   if (Fsyncfiles) {                                                       //  this is spawned sync process    v.11.11
 
157
      gtk_init_add((GtkFunction) syncfiles_func,0);                        //  do the sync function only
 
158
      gtk_main();
 
159
      return 0;
 
160
   }
 
161
 
 
162
   if (! Fsyncfiles && ! Fclone) {                                         //  this is the user GUI process
 
163
      printf("spawn sync files subprocess \n");
 
164
      err = system("fotoxx -syncfiles auto &");                            //  spawn process for sync files
 
165
      if (err) {
 
166
         printf("error: %s \n",wstrerror(err));                            //  quit if failed
 
167
         return 1;
 
168
      }
 
169
   }
 
170
   
 
171
   mWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);                             //  create main window
 
172
   gtk_window_set_title(MWIN,fversion);
 
173
   
 
174
   mVbox = gtk_vbox_new(0,0);                                              //  add vert. packing box
 
175
   gtk_container_add(GTK_CONTAINER(mWin),mVbox);
 
176
 
 
177
   mMbar = create_menubar(mVbox);                                          //  add menus / sub-menus
 
178
 
 
179
   GtkWidget *mFile = add_menubar_item(mMbar,ZTX("File"),topmenufunc);
 
180
      add_submenu_item(mFile,       ZTX("Image Gallery"),            m_gallery);
 
181
      add_submenu_item(mFile,       ZTX("Clone 50/50"),              m_clone1);
 
182
      add_submenu_item(mFile,       ZTX("Clone Overlay"),            m_clone2);
 
183
      add_submenu_item(mFile,       ZTX("Open Image File"),          m_open);
 
184
      add_submenu_item(mFile,       ZTX("Open in New Window"),       m_open_newin);
 
185
      add_submenu_item(mFile,       ZTX("Open Previous File"),       m_previous);
 
186
      add_submenu_item(mFile,       ZTX("Open Recent File"),         m_recent);
 
187
      add_submenu_item(mFile,       ZTX("Save to Same File"),        m_save);
 
188
      add_submenu_item(mFile,       ZTX("Save to New Version"),      m_savevers);
 
189
      add_submenu_item(mFile,       ZTX("Save to New File"),         m_saveas);
 
190
      add_submenu_item(mFile,       ZTX("Create Blank Image"),       m_create);
 
191
      add_submenu_item(mFile,       ZTX("Trash Image File"),         m_trash);
 
192
      add_submenu_item(mFile,       ZTX("Rename Image File"),        m_rename);
 
193
      add_submenu_item(mFile,       ZTX("Batch Rename Files"),       m_batchrename);
 
194
      add_submenu_item(mFile,       ZTX("Print Image File"),         m_print);
 
195
      add_submenu_item(mFile,       ZTX("Quit fotoxx"),              m_quit);
 
196
 
 
197
   GtkWidget *mTools = add_menubar_item(mMbar,ZTX("Tools"),topmenufunc);
 
198
      add_submenu_item(mTools,      ZTX("Manage Collections"),       m_manage_collections);
 
199
      add_submenu_item(mTools,      ZTX("Move Collections"),         m_move_collections);
 
200
      add_submenu_item(mTools,      ZTX("Check Monitor"),            m_moncheck);
 
201
      add_submenu_item(mTools,      ZTX("Monitor Gamma"),            m_mongamma);
 
202
      add_submenu_item(mTools,      ZTX("Brightness Graph"),         m_histogram);
 
203
      add_submenu_item(mTools,      ZTX("Slide Show"),               m_slideshow);
 
204
      add_submenu_item(mTools,      ZTX("Show RGB"),                 m_show_RGB);
 
205
      add_submenu_item(mTools,      ZTX("Grid Lines"),               m_gridlines);
 
206
      add_submenu_item(mTools,      ZTX("Lens Parameters"),          m_lensparms);
 
207
      add_submenu_item(mTools,      ZTX("Change Language"),          m_lang);
 
208
      add_submenu_item(mTools,          "Edit Translations",         m_translate);
 
209
      add_submenu_item(mTools,      ZTX("Add Menu and Launcher"),    m_menu_launcher);
 
210
      add_submenu_item(mTools,      ZTX("Convert RAW files"),        m_conv_raw);
 
211
      add_submenu_item(mTools,      ZTX("Burn Images to CD/DVD"),    m_burn);
 
212
      add_submenu_item(mTools,      ZTX("E-mail Images"),            m_email);
 
213
      add_submenu_item(mTools,      ZTX("Synchronize Files"),        m_syncfiles);
 
214
      add_submenu_item(mTools,      ZTX("Toolbar Style"),            m_tbar_style);
 
215
      add_submenu_item(mTools,      ZTX("Memory Usage"),             m_memory_usage);
 
216
 
 
217
   GtkWidget *mInfo = add_menubar_item(mMbar,"Info",topmenufunc);
 
218
      add_submenu_item(mInfo,       ZTX("Edit Caption/Comments"),    m_edit_cctext);
 
219
      add_submenu_item(mInfo,       ZTX("Edit Tags"),                m_edit_tags);
 
220
      add_submenu_item(mInfo,       ZTX("Manage Tags"),              m_manage_tags);
 
221
      add_submenu_item(mInfo,       ZTX("Batch Add Tags"),           m_batchAddTags);
 
222
      add_submenu_item(mInfo,       ZTX("Batch Delete Tag"),         m_batchDelTag);
 
223
      add_submenu_item(mInfo,       ZTX("View Info (short)"),        m_info_view_short);
 
224
      add_submenu_item(mInfo,       ZTX("View Info (long)"),         m_info_view_long);
 
225
      add_submenu_item(mInfo,       ZTX("Edit Info"),                m_info_edit);
 
226
      add_submenu_item(mInfo,       ZTX("Delete Info"),              m_info_delete);
 
227
      add_submenu_item(mInfo,       ZTX("Search Images"),            m_search_images);
 
228
 
 
229
   GtkWidget *mSelect = add_menubar_item(mMbar,ZTX("Select"),topmenufunc);
 
230
      add_submenu_item(mSelect,     ZTX("Select"),                   m_select);
 
231
      add_submenu_item(mSelect,     ZTX("Show"),                     m_select_show);
 
232
      add_submenu_item(mSelect,     ZTX("Hide"),                     m_select_hide);
 
233
      add_submenu_item(mSelect,     ZTX("Enable"),                   m_select_enable);
 
234
      add_submenu_item(mSelect,     ZTX("Disable"),                  m_select_disable);
 
235
      add_submenu_item(mSelect,     ZTX("Invert"),                   m_select_invert);
 
236
      add_submenu_item(mSelect,     ZTX("Unselect"),                 m_select_unselect);
 
237
      add_submenu_item(mSelect,     ZTX("Copy"),                     m_select_copy);
 
238
      add_submenu_item(mSelect,     ZTX("Paste"),                    m_select_paste);
 
239
      add_submenu_item(mSelect,     ZTX("Open"),                     m_select_open);
 
240
      add_submenu_item(mSelect,     ZTX("Save"),                     m_select_save);
 
241
      add_submenu_item(mSelect,     ZTX("Select Whole Image"),       m_select_whole_image);
 
242
      add_submenu_item(mSelect,     ZTX("Select and Edit"),          m_select_edit);
 
243
 
 
244
   GtkWidget *mTransf = add_menubar_item(mMbar,ZTX("Transform"),topmenufunc);
 
245
      add_submenu_item(mTransf,     ZTX("Rotate Image"),             m_rotate);
 
246
      add_submenu_item(mTransf,     ZTX("Trim Image"),               m_trim);
 
247
      add_submenu_item(mTransf,     ZTX("Resize Image"),             m_resize);
 
248
      add_submenu_item(mTransf,     ZTX("Batch Resize/Export"),      m_batchresize);
 
249
      add_submenu_item(mTransf,     ZTX("Annotate Image"),           m_annotate);
 
250
      add_submenu_item(mTransf,     ZTX("Flip Image"),               m_flip);
 
251
      add_submenu_item(mTransf,     ZTX("Make Negative"),            m_negate);
 
252
      add_submenu_item(mTransf,     ZTX("Unbend Image"),             m_unbend);
 
253
      add_submenu_item(mTransf,     ZTX("Keystone Correction"),      m_keystone);
 
254
      add_submenu_item(mTransf,     ZTX("Warp Image (area)"),        m_warp_area);
 
255
      add_submenu_item(mTransf,     ZTX("Warp Image (curved)"),      m_warp_curved);
 
256
      add_submenu_item(mTransf,     ZTX("Warp Image (linear)"),      m_warp_linear);
 
257
      add_submenu_item(mTransf,     ZTX("Warp Image (affine)"),      m_warp_affine);
 
258
 
 
259
   GtkWidget *mRetouch = add_menubar_item(mMbar,ZTX("Retouch"),topmenufunc);
 
260
      add_submenu_item(mRetouch,    ZTX("Brightness/Color"),         m_tune);
 
261
      add_submenu_item(mRetouch,    ZTX("Gamma Curves"),             m_gamma);
 
262
      add_submenu_item(mRetouch,    ZTX("Expand Brightness"),        m_xbrange);
 
263
      add_submenu_item(mRetouch,    ZTX("Flatten Brightness"),       m_flatten);
 
264
      add_submenu_item(mRetouch,    ZTX("Brightness Ramp"),          m_brightramp);
 
265
      add_submenu_item(mRetouch,    ZTX("Tone Mapping"),             m_tonemap);
 
266
      add_submenu_item(mRetouch,    ZTX("White Balance"),            m_whitebal);
 
267
      add_submenu_item(mRetouch,    ZTX("Match Colors"),             m_match_color);
 
268
      add_submenu_item(mRetouch,        "DRGB",                      m_DRGB);
 
269
      add_submenu_item(mRetouch,    ZTX("Revise RGB"),               m_revise_RGB);
 
270
      add_submenu_item(mRetouch,    ZTX("Red Eyes"),                 m_redeye);
 
271
      add_submenu_item(mRetouch,    ZTX("Blur Image"),               m_blur);
 
272
      add_submenu_item(mRetouch,    ZTX("Sharpen Image"),            m_sharpen);
 
273
      add_submenu_item(mRetouch,    ZTX("Reduce Noise"),             m_denoise);
 
274
      add_submenu_item(mRetouch,    ZTX("Smart Erase"),              m_smart_erase);
 
275
      add_submenu_item(mRetouch,    ZTX("Remove Dust"),              m_dust);
 
276
      add_submenu_item(mRetouch,    ZTX("Edit Pixels"),              m_pixedit);
 
277
 
 
278
   GtkWidget *mArt = add_menubar_item(mMbar,ZTX("Art"),topmenufunc);
 
279
      add_submenu_item(mArt,        ZTX("Color Depth"),              m_colordep);
 
280
      add_submenu_item(mArt,        ZTX("Drawing"),                  m_draw);
 
281
      add_submenu_item(mArt,        ZTX("Outlines"),                 m_outlines);
 
282
      add_submenu_item(mArt,        ZTX("Embossing"),                m_emboss);
 
283
      add_submenu_item(mArt,        ZTX("Tiles"),                    m_tiles);
 
284
      add_submenu_item(mArt,        ZTX("Dots"),                     m_dots);
 
285
      add_submenu_item(mArt,        ZTX("Painting"),                 m_painting);
 
286
 
 
287
   GtkWidget *mComb = add_menubar_item(mMbar,ZTX("Combine"),topmenufunc);
 
288
      add_submenu_item(mComb,       ZTX("High Dynamic Range"),       m_HDR);
 
289
      add_submenu_item(mComb,       ZTX("High Depth of Field"),      m_HDF);
 
290
      add_submenu_item(mComb,       ZTX("Stack / Paint"),            m_STP);
 
291
      add_submenu_item(mComb,       ZTX("Stack / Noise"),            m_STN);
 
292
      add_submenu_item(mComb,       ZTX("Panorama"),                 m_pano);
 
293
      add_submenu_item(mComb,       ZTX("Vertical Panorama"),        m_vpano);
 
294
 
 
295
   GtkWidget *mPlugins = add_menubar_item(mMbar,"Plugins",topmenufunc);    //  build plugin menu     v.11.03
 
296
      add_submenu_item(mPlugins,ZTX("Edit Plugins"),m_edit_plugins);
 
297
      for (int ii = 0; ii < Nplugins; ii++) {
 
298
         pp = strstr(plugins[ii]," = ");
 
299
         if (! pp) continue;
 
300
         *pp = 0;
 
301
         add_submenu_item(mPlugins,plugins[ii],m_run_plugin);
 
302
         *pp = ' ';
 
303
      }
 
304
 
 
305
   GtkWidget *mHelp = add_menubar_item(mMbar,ZTX("Help"),topmenufunc);
 
306
      add_submenu_item(mHelp,       ZTX("About"),                    m_help);
 
307
      add_submenu_item(mHelp,       ZTX("User Guide"),               m_help);
 
308
      add_submenu_item(mHelp,       ZTX("User Guide Changes"),       m_help);
 
309
      add_submenu_item(mHelp,       ZTX("Edit Functions Summary"),   m_help);
 
310
      add_submenu_item(mHelp,       ZTX("Change Log"),               m_help);
 
311
      add_submenu_item(mHelp,       ZTX("Translations"),             m_help);
 
312
      add_submenu_item(mHelp,       ZTX("Home Page"),                m_help);
 
313
      add_submenu_item(mHelp,       "README",                        m_help);
 
314
 
 
315
   mTbar = create_toolbar(mVbox);                                          //  toolbar buttons
 
316
      add_toolbar_button(mTbar,  ZTX("Gallery"),  ZTX("Image Gallery"),        "gallery.png",  m_gallery);
 
317
      add_toolbar_button(mTbar,  ZTX("Open"),     ZTX("Open Image File"),      "open.png",     m_open);
 
318
      add_toolbar_button(mTbar,  ZTX("Prev"),     ZTX("Open Previous File"),   "prev.png",     m_prev);
 
319
      add_toolbar_button(mTbar,  ZTX("Next"),     ZTX("Open Next File"),       "next.png",     m_next);
 
320
      add_toolbar_button(mTbar,  "Zoom+",         ZTX("Zoom-in (bigger)"),     "zoom+.png",    m_zoom);
 
321
      add_toolbar_button(mTbar,  "Zoom-",         ZTX("Zoom-out (smaller)"),   "zoom-.png",    m_zoom);
 
322
      add_toolbar_button(mTbar,  ZTX("Undo"),     ZTX("Undo One Edit"),        "undo.png",     m_undo);
 
323
      add_toolbar_button(mTbar,  ZTX("Redo"),     ZTX("Redo One Edit"),        "redo.png",     m_redo);
 
324
      add_toolbar_button(mTbar,  "separator",     null,                        null,           null);
 
325
      add_toolbar_button(mTbar,  "separator",     null,                        null,           null);
 
326
      add_toolbar_button(mTbar,  ZTX("Save"),     ZTX("Save to Same File"),    "save.png",     m_save);
 
327
      add_toolbar_button(mTbar,  ZTX("Save+V"),   ZTX("Save to New Version"),  "save+V.png",   m_savevers);
 
328
      add_toolbar_button(mTbar,  ZTX("Save+F"),   ZTX("Save to New File"),     "save+F.png",   m_saveas);
 
329
      add_toolbar_button(mTbar,  ZTX("Trash"),    ZTX("Move Image to Trash"),  "trash.png",    m_trash);
 
330
      add_toolbar_button(mTbar,  "separator",     null,                        null,           null);
 
331
      add_toolbar_button(mTbar,  "separator",     null,                        null,           null);
 
332
      add_toolbar_button(mTbar,  ZTX("Quit"),     ZTX("Quit fotoxx"),          "quit.png",     m_quit);
 
333
      add_toolbar_button(mTbar,  ZTX("Help"),     ZTX("Fotoxx Essentials"),    "help.png",     m_help);
 
334
   
 
335
   tbar_style_set(null);                                                   //  recall last toolbar style    v.11.06
 
336
 
 
337
   drWin = gtk_drawing_area_new();                                         //  add drawing window
 
338
   gtk_box_pack_start(GTK_BOX(mVbox),drWin,1,1,0);
 
339
 
 
340
   STbar = create_stbar(mVbox);                                            //  add status bar
 
341
 
 
342
   G_SIGNAL(mWin,"delete_event",delete_event,0)                            //  connect signals
 
343
   G_SIGNAL(mWin,"destroy",destroy_event,0)
 
344
   G_SIGNAL(drWin,"expose-event",mwpaint1,0)
 
345
 
 
346
   gtk_widget_add_events(drWin,GDK_BUTTON_PRESS_MASK);                     //  connect mouse events
 
347
   gtk_widget_add_events(drWin,GDK_BUTTON_RELEASE_MASK);
 
348
   gtk_widget_add_events(drWin,GDK_BUTTON_MOTION_MASK);                    //  motion with button pressed
 
349
   gtk_widget_add_events(drWin,GDK_POINTER_MOTION_MASK);                   //  pointer motion
 
350
   G_SIGNAL(drWin,"button-press-event",mouse_event,0)
 
351
   G_SIGNAL(drWin,"button-release-event",mouse_event,0)
 
352
   G_SIGNAL(drWin,"motion-notify-event",mouse_event,0)
 
353
 
 
354
   G_SIGNAL(mWin,"key-press-event",KBpress,0)                              //  connect KB events
 
355
   G_SIGNAL(mWin,"key-release-event",KBrelease,0)
 
356
   
 
357
   drag_drop_connect(drWin,m_open_drag);                                   //  connect drag-drop event
 
358
 
 
359
   gtk_window_move(MWIN,mwgeom[0],mwgeom[1]);                              //  main window geometry
 
360
   gtk_window_set_default_size(MWIN,mwgeom[2],mwgeom[3]);                  //  (defaults or last session params)
 
361
   gtk_widget_show_all(mWin);                                              //  show all widgets
 
362
 
 
363
   if (Fclone == 1) {                                                      //  clone1: left half of desktop    v.10.12
 
364
      screen = gdk_screen_get_default();
 
365
      cloww = gdk_screen_get_width(screen);
 
366
      clohh = gdk_screen_get_height(screen);
 
367
      cloww = cloww / 2 - 20;
 
368
      clohh = clohh - 50;
 
369
      gtk_window_move(MWIN,10,10);                                         //  v.11.07
 
370
      gtk_window_resize(MWIN,cloww,clohh);
 
371
   }
 
372
   
 
373
   if (Fclone == 2) {                                                      //  clone2: open new window         v.11.07
 
374
      gtk_window_move(MWIN,cloxx+20,cloyy+30);                             //    slightly offset from old window
 
375
      gtk_window_resize(MWIN,cloww,clohh);
 
376
   }
 
377
 
 
378
   gdkgc = gdk_gc_new(drWin->window);                                      //  initz. graphics context
 
379
 
 
380
   black.red = black.green = black.blue = 0;                               //  set up colors
 
381
   white.red = white.green = white.blue = maxcolor;
 
382
   lgray.red = lgray.green = lgray.blue = 0.9 * maxcolor;
 
383
   dgray.red = dgray.green = dgray.blue = 0.5 * maxcolor;
 
384
   red.red = maxcolor;  red.green = red.blue = 0;
 
385
   green.green = maxcolor; green.red = green.blue = 0;
 
386
 
 
387
   colormap = gtk_widget_get_colormap(drWin);
 
388
   gdk_rgb_find_color(colormap,&black);
 
389
   gdk_rgb_find_color(colormap,&white);
 
390
   gdk_rgb_find_color(colormap,&lgray);
 
391
   gdk_rgb_find_color(colormap,&dgray);
 
392
   gdk_rgb_find_color(colormap,&red);
 
393
   gdk_rgb_find_color(colormap,&green);
 
394
   
 
395
   gdk_gc_set_foreground(gdkgc,&black);
 
396
   gdk_gc_set_background(gdkgc,&lgray);
 
397
   gdk_window_set_background(drWin->window,&lgray);                        //  v.11.01
 
398
   fg_color = black;
 
399
 
 
400
   gdk_gc_set_line_attributes(gdkgc,1,LINEATTRIBUTES);
 
401
 
 
402
   arrowcursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);                       //  cursor for selection
 
403
   dragcursor = gdk_cursor_new(GDK_CROSSHAIR);                             //  cursor for dragging
 
404
   drawcursor = gdk_cursor_new(GDK_PENCIL);                                //  cursor for drawing lines
 
405
 
 
406
   gtk_init_add((GtkFunction) gtkinitfunc,0);                              //  set initz. call from gtk_main()
 
407
   gtk_main();                                                             //  start processing window events
 
408
   return 0;
 
409
}
 
410
 
 
411
 
 
412
/**************************************************************************/
 
413
 
 
414
//  initial function called from gtk_main() at startup
 
415
 
 
416
int gtkinitfunc(void *data)
 
417
{
 
418
   int            err, flag, npid;
 
419
   int            ftype, fdirk = 0;
 
420
   char           procfile[20], *ppv;
 
421
   cchar          *pp, *pp2;
 
422
   struct stat    statb;
 
423
 
 
424
   menulock(1);                                                            //  block menus until initz. done   v.11.04
 
425
 
 
426
   printf("install location: %s \n",get_zinstalloc());                     //  install location       v.11.05
 
427
   
 
428
   if (Ffirsttime) {                                                       //  first time startup           v.11.11
 
429
      printf("%s \n",ZTX("first time startup"));
 
430
      zmessageACK(mWin,"User Guide is available in Help menu");            //  inform user of user guide
 
431
      Ffirsttime = 0;
 
432
      save_params();
 
433
   }
 
434
 
 
435
   Nwt = sysconf(_SC_NPROCESSORS_ONLN);                                    //  get SMP CPU count
 
436
   if (Nwt <= 0) Nwt = 1;
 
437
   if (Nwt > max_threads) Nwt = max_threads;                               //  compile time limit
 
438
   printf("using %d threads \n",Nwt);
 
439
 
 
440
   err = system("which exiftool");                                         //  check for exiftool     v.11.05
 
441
   if (! err) Fexiftool = 1;
 
442
   err = system("which xdg-open");                                         //  check for xdg-open
 
443
   if (! err) Fxdgopen = 1;
 
444
   err = system("which ufraw-batch");                                      //  check for ufraw-batch
 
445
   if (! err) Fufraw = 1;
 
446
   
 
447
   undo_files = zmalloc(200);
 
448
   *undo_files = 0;                                                        //  look for orphaned undo files
 
449
   strncatv(undo_files,199,get_zuserdir(),"/*_undo_*",null);               //  /home/user/.fotoxx/*_undo_*
 
450
   flag = 1;
 
451
   while ((pp = SearchWild(undo_files,flag)))
 
452
   {
 
453
      pp2 = strstr(pp,".fotoxx/");
 
454
      if (! pp2) continue;
 
455
      npid = atoi(pp2+8);                                                  //  pid of file owner
 
456
      snprintf(procfile,19,"/proc/%d",npid);
 
457
      err = stat(procfile,&statb);
 
458
      if (! err) continue;                                                 //  pid is active, keep file
 
459
      printf("orphaned undo file deleted: %s \n",pp);
 
460
      err = remove(pp);                                                    //  delete file         v.11.03
 
461
   }
 
462
 
 
463
   *undo_files = 0;                                                        //  setup undo filespec template
 
464
   strncatv(undo_files,199,get_zuserdir(),"/",PIDstring,"_undo_nn",null);  //  /home/user/.fotoxx/pppppp_undo_nn
 
465
 
 
466
   editlog = pvlist_create(maxedits);                                      //  history log of image edits done
 
467
   for (int ii = 0; ii < maxedits; ii++)                                   //  pre-load all pvlist entries        v.10.2
 
468
      pvlist_append(editlog,"nothing");
 
469
 
 
470
   mutex_init(&Fpixmap_lock,0);                                            //  setup lock for edit pixmaps
 
471
   TIFFSetWarningHandler(tiffwarninghandler);                              //  intercept TIFF warning messages
 
472
   zdialog_positions("load");                                              //  load saved dialog positions        v.11.07
 
473
   
 
474
//  set up current file and directory from saved parameters or command line
 
475
 
 
476
   if (topdirk) curr_dirk = strdupz(topdirk);                              //  use top directory if defined       v.11.07
 
477
   else {
 
478
      ppv = getcwd(command,ccc);                                           //  or use current directory           v.11.09
 
479
      if (ppv) curr_dirk = strdupz(ppv);
 
480
   }
 
481
   
 
482
   if (initial_file) {                                                     //  from command line or previous session
 
483
      ppv = realpath(initial_file,0);                                      //  prepend directory if needed
 
484
      if (ppv) {
 
485
         zfree(initial_file);
 
486
         initial_file = strdupz(ppv);
 
487
         free(ppv);
 
488
      }
 
489
      else {
 
490
         printf("invalid file: %s \n",initial_file);                       //  invalid file path
 
491
         zfree(initial_file);
 
492
         initial_file = 0;
 
493
      }
 
494
   }
 
495
 
 
496
   if (initial_file) {
 
497
      ftype = image_file_type(initial_file);
 
498
      if (ftype == 0) {                                                    //  non-existent file
 
499
         printf("invalid file: %s \n",initial_file);
 
500
         zfree(initial_file);
 
501
         initial_file = 0;
 
502
      }
 
503
      else if (ftype == 1) {                                               //  is a directory
 
504
         if (curr_dirk) zfree(curr_dirk);
 
505
         curr_dirk = initial_file;
 
506
         initial_file = 0;
 
507
         fdirk = 1;
 
508
      }
 
509
      else if (ftype == 2) {                                               //  supported image file type
 
510
         if (curr_dirk) zfree(curr_dirk);
 
511
         curr_dirk = strdupz(initial_file);                                //  set current directory from file
 
512
         ppv = strrchr(curr_dirk,'/');
 
513
         if (ppv) *ppv = 0;
 
514
      }
 
515
      else {
 
516
         printf("unsupported file type: %s \n",initial_file);              //  unsupported file type
 
517
         zfree(initial_file);
 
518
         initial_file = 0;
 
519
      }
 
520
   }
 
521
 
 
522
   if (curr_dirk) err = chdir(curr_dirk);                                  //  set current directory
 
523
 
 
524
   menulock(0);                                                            //  enable menus                       v.11.04
 
525
 
 
526
   g_timeout_add(200,gtimefunc,0);                                         //  start periodic function (200 ms)   v.11.06
 
527
 
 
528
   if (SS_imagefile) {                                                     //  start slide show if wanted         v.11.04
 
529
      f_open(SS_imagefile,1);
 
530
      if (SS_musicfile) {
 
531
         sprintf(command,"xdg-open \"%s\" ",SS_musicfile);                 //  start music if wanted
 
532
         printf("command: %s \n",command);
 
533
         err = system(command);
 
534
      }
 
535
      m_slideshow(0,0);
 
536
      return 0;
 
537
   }
 
538
 
 
539
   if (initial_file)                                                       //  open initial file
 
540
      f_open(initial_file,1);
 
541
   
 
542
   else if (fdirk) {                                                       //  open gallery for initial directory
 
543
      image_navi::thumbsize = 128;
 
544
      m_gallery(0,0);                                                      //  v.11.08
 
545
   }
 
546
   
 
547
   else if (Fprev)                                                         //  start with previous file  v.11.08
 
548
      m_previous(0,0);
 
549
   
 
550
   else if (Frecent) {
 
551
      image_navi::thumbsize = 180;                                         //  start with gallery of recent files
 
552
      m_recent(0,0);                                                       //  v.11.07
 
553
   }
 
554
 
 
555
   return 0;                                                               //  start with blank window
 
556
}
 
557
 
 
558
 
 
559
/**************************************************************************/
 
560
 
 
561
//  Periodic function
 
562
//  Avoid any thread usage of gtk/gdk functions.
 
563
 
 
564
int gtimefunc(void *arg)
 
565
{
 
566
   static zdialog    *zdmessage = 0;
 
567
   int               syncfd;
 
568
 
 
569
   if (Wrepaint) {                                                         //  window update needed
 
570
      Wrepaint = 0;
 
571
      mwpaint1();
 
572
   }
 
573
 
 
574
   update_statusbar();                                                     //  update every cycle           v.11.03
 
575
   
 
576
   if (Fslideshow) slideshow_next("timer");                                //  show next image if time is up
 
577
 
 
578
   if (Ffuncbusy)
 
579
      paint_text(Dww/2-50,Dhh-20,"BUSY","monospace 10");                   //  write BUSY on window         v.11.07
 
580
 
 
581
   if (threadmessage && ! zdmessage)                                       //  display message for thread process
 
582
      zdmessage = zmessage_post(mWin,0,threadmessage);                     //  (avoid use of GTK in threads)      v.11.03
 
583
 
 
584
   if (zdmessage && ! threadmessage)                                       //  message terminated by thread
 
585
      zdialog_free(zdmessage);
 
586
   
 
587
   if (zdmessage && zdmessage->sentinel != zdsentinel) {                   //  popup window closed by user
 
588
      zdmessage = 0;
 
589
      threadmessage = 0;
 
590
   }
 
591
   
 
592
   syncfd = global_lock(fsync_lock);                                       //  check for sync files process
 
593
   if (syncfd >= 0) {                                                      //  not busy
 
594
      global_unlock(syncfd);
 
595
      if (Fsyncbusy) threadmessage = 0;                                    //  it was busy, now done     v.11.11
 
596
      Fsyncbusy = 0;
 
597
   }
 
598
   else Fsyncbusy = 1;                                                     //  sync files still busy
 
599
 
 
600
   return 1;
 
601
}
 
602
 
 
603
 
 
604
/**************************************************************************/
 
605
 
 
606
//  update status bar with image data and status
 
607
//  called from timer function
 
608
 
 
609
void update_statusbar()
 
610
{
 
611
   static double  time1 = 0, time2, cpu1 = 0, cpu2, cpuload;
 
612
   char           text1[300], text2[100];
 
613
   int            ww, hh, scale, bpc, done;
 
614
   double         file_MB = 1.0 / mega * curr_file_size;
 
615
   
 
616
   *text1 = *text2 = 0;
 
617
   
 
618
   time2 = get_seconds();                                                  //  compute process cpu load
 
619
   if (time2 - time1 > 1.0) {                                              //    at 1 second intervals
 
620
      cpu2 = jobtime();                                                    //  v.11.09
 
621
      cpuload = 100.0 * (cpu2 - cpu1) / (time2 - time1);
 
622
      time1 = time2;
 
623
      cpu1 = cpu2;
 
624
   }
 
625
 
 
626
   sprintf(text1,"CPU %03.0f%c",cpuload,'%');                              //  CPU 023%
 
627
   
 
628
   if (curr_file)                                                          //  v.11.04
 
629
   {
 
630
      if (E3pxm16) {
 
631
         ww = E3ww;
 
632
         hh = E3hh;
 
633
         bpc = 16;
 
634
      }
 
635
      else if (Fpxm16) {
 
636
         ww = Fww;
 
637
         hh = Fhh;
 
638
         bpc = 16;
 
639
      }
 
640
      else {
 
641
         ww = Fww;
 
642
         hh = Fhh;
 
643
         bpc = curr_file_bpc;
 
644
      }
 
645
 
 
646
      snprintf(text2,99,"  %dx%dx%d",ww,hh,bpc);                           //  2345x1234x16 (preview) 1.56MB 45%
 
647
      strcat(text1,text2);
 
648
      if (Fpreview) strcat(text1," (preview)");
 
649
      sprintf(text2," %.2fMB",file_MB);
 
650
      strcat(text1,text2);
 
651
      scale = Mscale * 100 + 0.5;
 
652
      sprintf(text2," %d%c",scale,'%');
 
653
      strcat(text1,text2);
 
654
   
 
655
      if (Pundo) {                                                         //  edit undo stack depth 
 
656
         snprintf(text2,99,"  edits: %d",Pundo);
 
657
         strcat(text1,text2);
 
658
      }
 
659
   }
 
660
 
 
661
   if (Fmenulock) strcat(text1,"  menu locked");
 
662
   if (Fsyncbusy) strcat(text1,"  file sync busy");                        //  v.11.11
 
663
   if (Factivearea) strcat(text1,"  area active");                         //  v.11.01
 
664
   if (zfuncs::zdialog_busy) strcat(text1,"  dialog open");                //  v.11.11
 
665
   
 
666
   if (SB_goal) {                                                          //  progress monitor       v.9.6
 
667
      done = 100.0 * SB_done / SB_goal;                                    //                         v.11.06
 
668
      snprintf(text2,99,"  progress %d%c",done,'%');
 
669
      strcat(text1,text2);
 
670
   }
 
671
 
 
672
   if (*SB_text) {                                                         //  application text       v.10.7
 
673
      strcat(text1,"  ");
 
674
      strcat(text1,SB_text);
 
675
   }
 
676
 
 
677
   stbar_message(STbar,text1);                                             //  write to status bar
 
678
 
 
679
   return;
 
680
}
 
681
 
 
682
 
 
683
/**************************************************************************/
 
684
 
 
685
//  functions for main window event signals
 
686
 
 
687
int delete_event()                                                         //  main window closed
 
688
{
 
689
   printf("main window delete event \n");
 
690
   if (mod_keep()) return 1;                                               //  allow user bailout
 
691
   Fshutdown++;                                                            //  shutdown in progress
 
692
   save_params();                                                          //  save parameters for next session
 
693
   zdialog_positions("save");                                              //  save dialog positions too    v.11.07
 
694
   free_resources();                                                       //  delete undo files
 
695
   return 0;
 
696
}
 
697
 
 
698
void destroy_event()                                                       //  main window destroyed
 
699
{
 
700
   printf("main window destroy event \n");
 
701
   exit(1);                                                                //  instead of gtk_main_quit();
 
702
   return;
 
703
}
 
704
 
 
705
 
 
706
//  process top-level menu entry
 
707
 
 
708
void topmenufunc(GtkWidget *, cchar *menu)
 
709
{
 
710
   topmenu = (char *) menu;                                                //  remember top-level menu in case
 
711
   return;                                                                 //    this is needed somewhere
 
712
}
 
713
 
 
714
 
 
715
/**************************************************************************/
 
716
 
 
717
//  Paint window when created, exposed, resized, or modified (edited).
 
718
//  This is a full window update. May NOT be called from threads.
 
719
 
 
720
int mwpaint1()
 
721
{
 
722
   GdkRectangle   wrect;
 
723
   int            incrx, incry;                                            //  mouse drag
 
724
   static int     pincrx = 0, pincry = 0;                                  //  prior mouse drag
 
725
   static int     pdww = 0, pdhh = 0;                                      //  prior window size
 
726
   static int     piorgx = 0, piorgy = 0;                                  //  prior image origin in window
 
727
   static double  pscale = 1;                                              //  prior scale
 
728
   double         wscale, hscale;
 
729
   int            refresh = 0;                                             //  flag, image refesh needed
 
730
   PXM            *pxmtemp;
 
731
   
 
732
   if (Fshutdown) return 1;                                                //  shutdown underway
 
733
   if (! Fpxm8) return 1;                                                  //  no image
 
734
 
 
735
   Dww = drWin->allocation.width;                                          //  (new) drawing window size
 
736
   Dhh = drWin->allocation.height;
 
737
   if (Dww < 20 || Dhh < 20) return 1;                                     //  too small
 
738
 
 
739
   if (mutex_trylock(&Fpixmap_lock) != 0) {                                //  lock pixmaps
 
740
      Wrepaint++;                                                          //  cannot, come back later
 
741
      return 1;
 
742
   }
 
743
   
 
744
   if (Frefresh) {                                                         //  image was updated
 
745
      refresh++;
 
746
      Frefresh = 0;
 
747
   }
 
748
 
 
749
   if (Dww != pdww || Dhh != pdhh) {                                       //  window size changed
 
750
      refresh++;                                                           //  image refresh needed
 
751
      pdww = Dww; 
 
752
      pdhh = Dhh;
 
753
   }
 
754
 
 
755
   if (E3pxm16) {                                                          //  get image size
 
756
      Iww = E3ww;
 
757
      Ihh = E3hh;                                                          //  edit in progress
 
758
   }
 
759
   else {
 
760
      Iww = Fww;                                                           //  no edit
 
761
      Ihh = Fhh;
 
762
   }
 
763
 
 
764
   if (Fzoom == 0) {                                                       //  scale to fit window
 
765
      wscale = 1.0 * Dww / Iww;
 
766
      hscale = 1.0 * Dhh / Ihh;
 
767
      if (wscale < hscale) Mscale = wscale;                                //  use greatest ww/hh ratio
 
768
      else  Mscale = hscale;
 
769
      if (Iww < Dww && Ihh < Dhh && ! Fblowup) Mscale = 1.0;               //  small image 1x unless Fblowup
 
770
      zoomx = zoomy = 0;
 
771
   }
 
772
   else Mscale = Fzoom;                                                    //  scale to Fzoom level
 
773
   
 
774
   if (Mscale != pscale) refresh++;                                        //  scale changed, image refresh needed
 
775
 
 
776
   if (Mscale > pscale) {                                                  //  zoom increased
 
777
      Iorgx += iww * 0.5 * (1.0 - pscale / Mscale);                        //  keep current image center
 
778
      Iorgy += ihh * 0.5 * (1.0 - pscale / Mscale);
 
779
   }
 
780
   pscale = Mscale;
 
781
   
 
782
   iww = Dww / Mscale;                                                     //  image space fitting in window
 
783
   if (iww > Iww) iww = Iww;
 
784
   ihh = Dhh / Mscale;
 
785
   if (ihh > Ihh) ihh = Ihh;
 
786
 
 
787
   if (zoomx || zoomy) {                                                   //  req. zoom center
 
788
      Iorgx = zoomx - 0.5 * iww;                                           //  corresp. image origin
 
789
      Iorgy = zoomy - 0.5 * ihh;
 
790
      zoomx = zoomy = 0;
 
791
   }
 
792
 
 
793
   if ((Mxdrag || Mydrag) && ! Mcapture) {                                 //  scroll via mouse drag
 
794
      incrx = (Mxdrag - Mxdown) * 1.3 * Iww / iww;                         //  scale
 
795
      incry = (Mydrag - Mydown) * 1.3 * Ihh / ihh;
 
796
      if (pincrx > 0 && incrx < 0) incrx = 0;                              //  stop bounce at extremes
 
797
      if (pincrx < 0 && incrx > 0) incrx = 0;
 
798
      pincrx = incrx;
 
799
      if (pincry > 0 && incry < 0) incry = 0;
 
800
      if (pincry < 0 && incry > 0) incry = 0;
 
801
      pincry = incry;
 
802
      Iorgx += incrx;                                                      //  new image origin after scroll
 
803
      Iorgy += incry;
 
804
      Mxdown = Mxdrag + incrx;                                             //  new drag origin
 
805
      Mydown = Mydrag + incry;
 
806
      Mxdrag = Mydrag = 0;
 
807
   }
 
808
 
 
809
   if (iww == Iww) {                                                       //  scaled image <= window width
 
810
      Iorgx = 0;                                                           //  center image in window
 
811
      Dorgx = 0.5 * (Dww - Iww * Mscale);
 
812
   }
 
813
   else Dorgx = 0;                                                         //  image > window, use entire window
 
814
 
 
815
   if (ihh == Ihh) {                                                       //  same for image height
 
816
      Iorgy = 0;
 
817
      Dorgy = 0.5 * (Dhh - Ihh * Mscale);
 
818
   }
 
819
   else Dorgy = 0;
 
820
   
 
821
   if (Iorgx + iww > Iww) Iorgx = Iww - iww;                               //  set limits
 
822
   if (Iorgy + ihh > Ihh) Iorgy = Ihh - ihh;
 
823
   if (Iorgx < 0) Iorgx = 0;
 
824
   if (Iorgy < 0) Iorgy = 0;
 
825
   
 
826
   if (Iorgx != piorgx || Iorgy != piorgy) {                               //  image origin changed
 
827
      refresh++;                                                           //  image refresh needed
 
828
      piorgx = Iorgx;
 
829
      piorgy = Iorgy;
 
830
   }
 
831
   
 
832
   if (refresh)                                                            //  image refresh is needed
 
833
   {
 
834
      if (E3pxm16) {                                                       //  edit in progress
 
835
         PXM_free(Dpxm16);
 
836
         Dpxm16 = PXM_copy_area(E3pxm16,Iorgx,Iorgy,iww,ihh);              //  copy E3 image area, PXM-16
 
837
         pxmtemp = PXM_convbpc(Dpxm16);                                    //  convert to PXM-8
 
838
      }
 
839
      else pxmtemp = PXM_copy_area(Fpxm8,Iorgx,Iorgy,iww,ihh);             //  no edit, copy PXM-8 image area
 
840
 
 
841
      dww = iww * Mscale;                                                  //  scale to window
 
842
      dhh = ihh * Mscale;
 
843
      PXM_free(Dpxm8);
 
844
      Dpxm8 = PXM_rescale(pxmtemp,dww,dhh);
 
845
      PXM_free(pxmtemp);
 
846
   }
 
847
   
 
848
   wrect.x = wrect.y = 0;                                                  //  stop flicker
 
849
   wrect.width = Dww;
 
850
   wrect.height = Dhh;
 
851
   gdk_window_begin_paint_rect(drWin->window,&wrect);
 
852
 
 
853
   gdk_draw_rgb_image(drWin->window, gdkgc, Dorgx, Dorgy, dww, dhh,        //  draw scaled image to window
 
854
                      NODITHER, (uint8 *) Dpxm8->bmp, dww*3);
 
855
 
 
856
   if (Ntoplines) paint_toplines(1);                                       //  draw line overlays
 
857
   if (Ftoparc) paint_toparc(1);                                           //  draw arc overlay
 
858
   if (Fgrid) paint_gridlines();                                           //  draw grid lines
 
859
   if (Ntoptext) paint_toptext();                                          //  draw text strings            v.11.07
 
860
   if (Fshowarea && ! Fpreview) sa_show(1);                                //  draw select area outline
 
861
 
 
862
   gdk_window_end_paint(drWin->window);                                    //  release all window updates
 
863
 
 
864
   mutex_unlock(&Fpixmap_lock);                                            //  unlock pixmaps
 
865
   Wpainted++;                                                             //  notify edit function of repaint
 
866
   histogram_paint();                                                      //  update brightness histogram if exists
 
867
   return 1;
 
868
}
 
869
 
 
870
 
 
871
/**************************************************************************/
 
872
 
 
873
//  Cause (modified) output image to get repainted soon.
 
874
//  The entire image is repainted (that part within the window).
 
875
//  MAY be called from threads.
 
876
 
 
877
void mwpaint2()
 
878
{
 
879
   Frefresh++;
 
880
   Wrepaint++;                                                             //  req. repaint by periodic function
 
881
   return;
 
882
}
 
883
 
 
884
 
 
885
//  Repaint a rectangular part of the image being edited.
 
886
//  px3, py3, ww3, hh3: modified area within E3pxm16 to be repainted
 
887
//  Dpxm16: 1x copy of E3pxm16 area currently visible in main window
 
888
//  px2, py2, ww2, hh2: E3pxm16 area copied into Dpxm16
 
889
//  Dpxm8: window image, Dpxm16 scaled to window, converted to 8 bits
 
890
//  May NOT be called from threads.
 
891
 
 
892
void mwpaint3(int px3, int py3, int ww3, int hh3)                          //  overhauled v.10.11
 
893
{
 
894
   int         px2, py2, ww2, hh2, dx, dy;
 
895
   uint16      *pix2, *pix3;
 
896
   uint8       *pxm8area;
 
897
   
 
898
   if (! Dpxm16) zappcrash("mwpaint3() no Dpxm16");                        //  v.10.12
 
899
   
 
900
   px2 = px3 - Iorgx;                                                      //  map modified area into Dpxm16
 
901
   py2 = py3 - Iorgy;
 
902
   ww2 = ww3;
 
903
   hh2 = hh3;
 
904
 
 
905
   if (px2 < 0) {                                                          //  if beyond Dpxm16 edge, reduce
 
906
      px3 -= px2;
 
907
      ww2 += px2;
 
908
      px2 = 0;
 
909
   }
 
910
   if (px2 + ww2 > iww) ww2 = iww - px2;
 
911
 
 
912
   if (py2 < 0) {
 
913
      py3 -= py2;
 
914
      hh2 += py2;
 
915
      py2 = 0;
 
916
   }
 
917
   if (py2 + hh2 > ihh) hh2 = ihh - py2;
 
918
 
 
919
   if (ww2 <= 0 || hh2 <= 0) return;                                       //  completely off Dpxm16 image
 
920
 
 
921
   for (dy = 0; dy < hh2; dy++)                                            //  copy pixels from E3pxm16 to Dpxm16
 
922
   for (dx = 0; dx < ww2; dx++)
 
923
   {
 
924
      pix3 = PXMpix(E3pxm16,px3+dx,py3+dy);
 
925
      pix2 = PXMpix(Dpxm16,px2+dx,py2+dy);
 
926
      pix2[0] = pix3[0];
 
927
      pix2[1] = pix3[1];
 
928
      pix2[2] = pix3[2];
 
929
   }
 
930
 
 
931
   PXM_update(Dpxm16,Dpxm8,px2,py2,ww2,hh2);                               //  copy/rescale Dpxm16 area into Dpxm8 area
 
932
 
 
933
   px2 = px2 * Mscale - 1;                                                 //  Dpxm8 area impacted   v.10.12
 
934
   py2 = py2 * Mscale - 1;
 
935
   ww2 = ww2 * Mscale + 1 / Mscale + 2;
 
936
   hh2 = hh2 * Mscale + 1 / Mscale + 2;
 
937
   
 
938
   if (px2 < 0) px2 = 0;                                                   //  stay within image edge
 
939
   if (py2 < 0) py2 = 0;
 
940
   if (px2 + ww2 > dww) ww2 = dww - px2;
 
941
   if (py2 + hh2 > dhh) hh2 = dhh - py2;
 
942
   if (ww2 <= 0 || hh2 <= 0) return;                                       //  completely off Dpxm8 image
 
943
 
 
944
   pxm8area = (uint8 *) Dpxm8->bmp + 3 * (dww * py2 + px2);                //  origin of area to copy
 
945
 
 
946
   gdk_draw_rgb_image(drWin->window, gdkgc, px2 + Dorgx, py2 + Dorgy,      //  copy into window
 
947
                                  ww2, hh2, NODITHER, pxm8area, dww*3);
 
948
   return;
 
949
}
 
950
 
 
951
 
 
952
/**************************************************************************/
 
953
 
 
954
//  mouse event function - capture buttons and drag movements
 
955
 
 
956
void mouse_event(GtkWidget *, GdkEventButton *event, void *)
 
957
{
 
958
   void mouse_convert(int &xpos, int &ypos);
 
959
 
 
960
   static int     bdtime = 0, butime = 0, mbusy = 0;
 
961
   int            button, time, type;
 
962
 
 
963
   type = event->type;
 
964
   button = event->button;                                                 //  button, 1/3 = left/right
 
965
   time = event->time;
 
966
   Mxposn = event->x;                                                      //  mouse position in window
 
967
   Myposn = event->y;
 
968
 
 
969
   mouse_convert(Mxposn,Myposn);                                           //  convert to image space
 
970
 
 
971
   if (type == GDK_MOTION_NOTIFY) {
 
972
      if (mbusy) return;                                                   //  discard excess motion events
 
973
      mbusy++;
 
974
      zmainloop();
 
975
      mbusy = 0;
 
976
   }
 
977
 
 
978
   if (type == GDK_BUTTON_PRESS) {                                         //  button down
 
979
      bdtime = time;                                                       //  time of button down
 
980
      Mxdown = Mxposn;                                                     //  position at button down time
 
981
      Mydown = Myposn;
 
982
      if (button) {
 
983
         Mdrag++;                                                          //  possible drag start
 
984
         Mbutton = button;
 
985
      }
 
986
      Mxdrag = Mydrag = 0;
 
987
   }
 
988
 
 
989
   if (type == GDK_BUTTON_RELEASE) {                                       //  button up
 
990
      Mxclick = Myclick  = 0;                                              //  reset click status
 
991
      butime = time;                                                       //  time of button up
 
992
      if (butime - bdtime < 400)                                           //  less than 0.4 secs down
 
993
         if (Mxposn == Mxdown && Myposn == Mydown) {                       //       and not moving
 
994
            if (Mbutton == 1) LMclick++;                                   //  left mouse click
 
995
            if (Mbutton == 3) RMclick++;                                   //  right mouse click
 
996
            Mxclick = Mxdown;                                              //  click = button down position
 
997
            Myclick = Mydown;
 
998
         }
 
999
      Mxdown = Mydown = Mxdrag = Mydrag = Mdrag = Mbutton = 0;             //  forget buttons and drag
 
1000
   }
 
1001
   
 
1002
   if (type == GDK_MOTION_NOTIFY && Mdrag) {                               //  drag underway
 
1003
      Mxdrag = Mxposn;
 
1004
      Mydrag = Myposn;
 
1005
   }
 
1006
   
 
1007
   if (mouseCBfunc) {                                                      //  pass to handler function
 
1008
      if (mbusy) return;
 
1009
      mbusy++;                                                             //  stop re-entrance       v.10.8
 
1010
      (* mouseCBfunc)();
 
1011
      mbusy = 0;
 
1012
      return;
 
1013
   }
 
1014
 
 
1015
   if (LMclick && ! Mcapture) {                                            //  left click = zoom request
 
1016
      LMclick = 0;
 
1017
      zoomx = Mxclick;                                                     //  zoom center = mouse
 
1018
      zoomy = Myclick;
 
1019
      m_zoom(null, (char *) "+");
 
1020
   }
 
1021
 
 
1022
   if (RMclick && ! Mcapture) {                                            //  right click
 
1023
      RMclick = 0;
 
1024
      if (Fzoom) {
 
1025
         zoomx = zoomy = 0;                                                //  reset zoomed image
 
1026
         m_zoom(null, (char *) "-");
 
1027
      }
 
1028
      else if (edit_coll_name && curr_file)                                //  pass file to edit collection
 
1029
         edit_coll_popmenu(null,curr_file);                                //  v.11.11
 
1030
   }
 
1031
 
 
1032
   if ((Mxdrag || Mydrag) && ! Mcapture) mwpaint1();                       //  drag = scroll
 
1033
   return;
 
1034
}
 
1035
 
 
1036
 
 
1037
//  convert mouse position from window space to image space
 
1038
 
 
1039
void mouse_convert(int &xpos, int &ypos)
 
1040
{
 
1041
   xpos = (xpos - Dorgx) / Mscale + Iorgx + 0.5;
 
1042
   ypos = (ypos - Dorgy) / Mscale + Iorgy + 0.5;
 
1043
 
 
1044
   if (xpos < 0) xpos = 0;                                                 //  if outside image put at edge
 
1045
   if (ypos < 0) ypos = 0;
 
1046
 
 
1047
   if (E3pxm16) { 
 
1048
      if (xpos >= E3ww) xpos = E3ww-1;
 
1049
      if (ypos >= E3hh) ypos = E3hh-1;
 
1050
   }
 
1051
   else {
 
1052
      if (xpos >= Fww) xpos = Fww-1;
 
1053
      if (ypos >= Fhh) ypos = Fhh-1;
 
1054
   }
 
1055
   
 
1056
   return;
 
1057
}
 
1058
 
 
1059
 
 
1060
/**************************************************************************/
 
1061
 
 
1062
//  keyboard event function - some toolbar buttons have KB equivalents
 
1063
//  GDK key symbols: /usr/include/gtk-2.0/gdk/gdkkeysyms.h
 
1064
 
 
1065
int      KBzmalloclog = 0;
 
1066
int      KBcontrolkey;
 
1067
int      KBshiftkey;
 
1068
int      KB_A_key;
 
1069
 
 
1070
int KBpress(GtkWidget *win, GdkEventKey *event, void *)                    //  prevent propagation of key-press
 
1071
{                                                                          //    events to toolbar buttons
 
1072
   KBkey = event->keyval;
 
1073
 
 
1074
   if (KBkey == GDK_Control_L   ||                                         //  Ctrl key is pressed    v.11.07
 
1075
       KBkey == GDK_Control_R) KBcontrolkey = 1;
 
1076
 
 
1077
   if (KBkey == GDK_Shift_L   ||                                           //  Shift key is pressed   v.11.07
 
1078
       KBkey == GDK_Shift_R) KBshiftkey = 1;
 
1079
   
 
1080
   if (KBkey == GDK_a   ||                                                 //  'A' key is pressed     v.11.11
 
1081
       KBkey == GDK_A) KB_A_key = 1;                                       //  (undo/redo --> undo/redo ALL)
 
1082
   
 
1083
   return 1;
 
1084
}
 
1085
 
 
1086
int KBrelease(GtkWidget *win, GdkEventKey *event, void *)
 
1087
{
 
1088
   int      angle = 0;
 
1089
   PXM      *pxm;
 
1090
   
 
1091
   KBkey = event->keyval;
 
1092
   
 
1093
   if (KBcapture) return 1;                                                //  let function handle it
 
1094
 
 
1095
   if (KBkey == GDK_Control_L   ||                                         //  Ctrl key released      v.11.07
 
1096
       KBkey == GDK_Control_R) KBcontrolkey = 0;
 
1097
   
 
1098
   if (KBkey == GDK_Shift_L   ||                                           //  Shift key released     v.11.07
 
1099
       KBkey == GDK_Shift_R) KBshiftkey = 0;
 
1100
 
 
1101
   if (KBkey == GDK_a   ||                                                 //  'A' key is released    v.11.11
 
1102
       KBkey == GDK_A) KB_A_key = 0;
 
1103
   
 
1104
   if (KBcontrolkey) {
 
1105
      if (KBkey == GDK_s) m_save(0,0);                                     //  Ctrl-* shortcuts
 
1106
      if (KBkey == GDK_S) m_saveas(0,0);
 
1107
      if (KBkey == GDK_v) m_savevers(0,0);
 
1108
      if (KBkey == GDK_V) m_savevers(0,0);
 
1109
      if (KBkey == GDK_q) m_quit(0,0);  
 
1110
      if (KBkey == GDK_Q) m_quit(0,0);
 
1111
   }
 
1112
 
 
1113
   if (KBkey == GDK_G || KBkey == GDK_g)                                   //  toggle grid lines      v.11.11
 
1114
      toggle_grid(2);
 
1115
 
 
1116
   if (Fslideshow) {                                                       //  v.11.10
 
1117
      if (KBkey == GDK_Left) slideshow_next("prev");                       //  arrow keys = prev/next image
 
1118
      if (KBkey == GDK_Right) slideshow_next("next");
 
1119
      if (KBkey == GDK_space) slideshow_next("pause");                     //  spacebar = pause/resume
 
1120
      if (KBkey == GDK_Escape) m_slideshow(0,0);                           //  escape = exit slideshow
 
1121
   }
 
1122
   else {
 
1123
      if (KBkey == GDK_Left) m_prev(0,0);                                  //  arrow keys = prev/next image
 
1124
      if (KBkey == GDK_Right) m_next(0,0);
 
1125
   }
 
1126
 
 
1127
   if (KBkey == GDK_plus) m_zoom(null, (char *) "+");                      //  +/- keys  >>  zoom in/out
 
1128
   if (KBkey == GDK_equal) m_zoom(null, (char *) "+");                     //  = key: same as +
 
1129
   if (KBkey == GDK_minus) m_zoom(null, (char *) "-");
 
1130
   if (KBkey == GDK_KP_Add) m_zoom(null, (char *) "+");                    //  keypad +
 
1131
   if (KBkey == GDK_KP_Subtract) m_zoom(null, (char *) "-");               //  keypad -
 
1132
   
 
1133
   if (KBkey == GDK_Delete) m_trash(0,0);                                  //  delete  >>  trash
 
1134
 
 
1135
   if (KBkey == GDK_F1)                                                    //  F1  >>  user guide
 
1136
      showz_userguide(zfuncs::F1_help_topic);                              //  show topic if there, or page 1
 
1137
 
 
1138
   if (! E1pxm16) {                                                        //  if no edit in progress,
 
1139
      if (KBkey == GDK_R || KBkey == GDK_r) angle = 90;                    //    allow manual rotations
 
1140
      if (KBkey == GDK_L || KBkey == GDK_l) angle = -90;
 
1141
      if (angle) {
 
1142
         mutex_lock(&Fpixmap_lock);                                        //  v.10.5
 
1143
         pxm = PXM_rotate(Fpxm8,angle);
 
1144
         PXM_free(Fpxm8);
 
1145
         Fpxm8 = pxm;
 
1146
         Fww = pxm->ww;
 
1147
         Fhh = pxm->hh;
 
1148
         mutex_unlock(&Fpixmap_lock);
 
1149
         mwpaint2();
 
1150
         m_save(0,"nowarn");                                               //  auto save uprighted image   v.10.12
 
1151
      }
 
1152
   }
 
1153
 
 
1154
   if (KBcontrolkey && KBkey == GDK_m) {                                   //  zmalloc() activity log
 
1155
      KBzmalloclog = 1 - KBzmalloclog;                                     //  Ctrl+M = toggle on/off
 
1156
      if (KBzmalloclog) zmalloc_log(999999);
 
1157
      else zmalloc_log(0);
 
1158
      zmalloc_report();                                                    //  v.10.8
 
1159
   }
 
1160
   
 
1161
   if (KBkey == GDK_T || KBkey == GDK_t) m_trim(0,0);                      //  Key T = Trim function        v.10.3.1
 
1162
   if (KBkey == GDK_B || KBkey == GDK_b) m_tune(0,0);                      //  Key B = brightness/color     v.10.3.1
 
1163
   if (KBkey == GDK_N || KBkey == GDK_n) m_rename(0,0);                    //  Key N = rename file          v.11.11
 
1164
   
 
1165
   if (KBkey == GDK_z || KBkey == GDK_Z) {                                 //  Z key
 
1166
      if (! KBcontrolkey) m_zoom(null, (char *) "Z");                      //  if no control key, toggle zoom 1x
 
1167
      else {                                                               //  if control key,              v.11.11
 
1168
         if (KBshiftkey) m_redo(0,0);                                      //  with shift: redo edit
 
1169
         else m_undo(0,0);                                                 //  without shift: undo edit
 
1170
      }
 
1171
   }
 
1172
   
 
1173
   KBkey = 0;
 
1174
   return 1;
 
1175
}
 
1176
 
 
1177
 
 
1178
/**************************************************************************/
 
1179
 
 
1180
//  paint a grid of horizontal and vertical lines
 
1181
 
 
1182
void paint_gridlines()                                                     //  revised  v.10.10.2
 
1183
{
 
1184
   int      px, py, stepx, stepy, startx, starty;
 
1185
 
 
1186
   if (! Fpxm8) return;                                                    //  no image
 
1187
   if (! Fgrid) return;                                                    //  grid lines off               v.11.11
 
1188
   
 
1189
   stepx = gridspace[0];                                                   //  space between grid lines
 
1190
   stepy = gridspace[1];
 
1191
 
 
1192
   if (gridcount[0]) stepx = dww / (1 + gridcount[0]);                     //  if line counts specified,    v.10.11
 
1193
   if (gridcount[1]) stepy = dhh / ( 1 + gridcount[1]);                    //    set spacing accordingly
 
1194
   
 
1195
   startx = stepx * gridoffset[0] / 100;                                   //  variable offsets             v.11.11
 
1196
   if (startx < 0) startx += stepx;
 
1197
 
 
1198
   starty = stepy * gridoffset[1] / 100;
 
1199
   if (starty < 0) starty += stepy;
 
1200
   
 
1201
   gdk_gc_set_foreground(gdkgc,&white);                                    //  white lines
 
1202
 
 
1203
   if (gridon[0] && stepx)
 
1204
      for (px = Dorgx+startx; px < Dorgx+dww; px += stepx)
 
1205
         gdk_draw_line(drWin->window,gdkgc,px,Dorgy,px,Dorgy+dhh);
 
1206
 
 
1207
   if (gridon[1] && stepy)
 
1208
      for (py = Dorgy+starty; py < Dorgy+dhh; py += stepy)
 
1209
         gdk_draw_line(drWin->window,gdkgc,Dorgx,py,Dorgx+dww,py);
 
1210
 
 
1211
   gdk_gc_set_foreground(gdkgc,&black);                                    //  adjacent black lines
 
1212
   fg_color = black;                                                       //  v.10.12
 
1213
 
 
1214
   if (gridon[0] && stepx) 
 
1215
      for (px = Dorgx+startx+1; px < Dorgx+dww; px += stepx)
 
1216
         gdk_draw_line(drWin->window,gdkgc,px,Dorgy,px,Dorgy+dhh);
 
1217
 
 
1218
   if (gridon[1] && stepy)
 
1219
      for (py = Dorgy+starty+1; py < Dorgy+dhh; py += stepy)
 
1220
         gdk_draw_line(drWin->window,gdkgc,Dorgx,py,Dorgx+dww,py);
 
1221
   
 
1222
   return;
 
1223
}
 
1224
 
 
1225
 
 
1226
/**************************************************************************/
 
1227
 
 
1228
//  refresh overlay lines on top of image
 
1229
//  arg = 1:   paint lines only (because window repainted)
 
1230
//        2:   erase lines and forget them
 
1231
//        3:   erase old lines, paint new lines, save new in old
 
1232
 
 
1233
void paint_toplines(int arg)
 
1234
{
 
1235
   int      ii;
 
1236
 
 
1237
   if (arg == 2 || arg == 3)                                               //  erase old lines
 
1238
      for (ii = 0; ii < Nptoplines; ii++)
 
1239
         erase_line(ptoplinex1[ii],ptopliney1[ii],ptoplinex2[ii],ptopliney2[ii]);
 
1240
   
 
1241
   if (arg == 1 || arg == 3)                                               //  draw new lines
 
1242
      for (ii = 0; ii < Ntoplines; ii++)
 
1243
         draw_line(toplinex1[ii],topliney1[ii],toplinex2[ii],topliney2[ii]);
 
1244
 
 
1245
   if (arg == 2) {
 
1246
      Nptoplines = Ntoplines = 0;                                          //  forget lines
 
1247
      return;
 
1248
   }
 
1249
 
 
1250
   for (ii = 0; ii < Ntoplines; ii++)                                      //  save for future erase
 
1251
   {
 
1252
      ptoplinex1[ii] = toplinex1[ii];
 
1253
      ptopliney1[ii] = topliney1[ii];
 
1254
      ptoplinex2[ii] = toplinex2[ii];
 
1255
      ptopliney2[ii] = topliney2[ii];
 
1256
   }
 
1257
 
 
1258
   Nptoplines = Ntoplines;
 
1259
 
 
1260
   return;
 
1261
}
 
1262
 
 
1263
 
 
1264
/**************************************************************************/
 
1265
 
 
1266
//  refresh overlay arc (circle/ellipse) on top of image
 
1267
//  arg = 1:   paint arc only (because window repainted)
 
1268
//        2:   erase arc and forget it
 
1269
//        3:   erase old arc, paint new arc, save new in old
 
1270
 
 
1271
void paint_toparc(int arg)
 
1272
{
 
1273
   int      arcx, arcy, arcw, arch;
 
1274
 
 
1275
   if (ptoparc && (arg == 2 || arg == 3)) {                                //  erase old arc
 
1276
      arcx = (ptoparcx-Iorgx) * Mscale + Dorgx + 0.5;                      //  image to window space
 
1277
      arcy = (ptoparcy-Iorgy) * Mscale + Dorgy + 0.5;
 
1278
      arcw = ptoparcw * Mscale;
 
1279
      arch = ptoparch * Mscale;
 
1280
 
 
1281
      gdk_gc_set_function(gdkgc,GDK_INVERT);                               //  invert pixels
 
1282
      gdk_draw_arc(drWin->window,gdkgc,0,arcx,arcy,arcw,arch,0,64*360);    //  draw arc
 
1283
      gdk_gc_set_function(gdkgc,GDK_COPY);
 
1284
   }
 
1285
   
 
1286
   if (Ftoparc && (arg == 1 || arg == 3)) {                                //  draw new arc
 
1287
      arcx = (toparcx-Iorgx) * Mscale + Dorgx + 0.5;                       //  image to window space
 
1288
      arcy = (toparcy-Iorgy) * Mscale + Dorgy + 0.5;
 
1289
      arcw = toparcw * Mscale;
 
1290
      arch = toparch * Mscale;
 
1291
 
 
1292
      gdk_gc_set_function(gdkgc,GDK_INVERT);                               //  invert pixels
 
1293
      gdk_draw_arc(drWin->window,gdkgc,0,arcx,arcy,arcw,arch,0,64*360);    //  draw arc
 
1294
      gdk_gc_set_function(gdkgc,GDK_COPY);
 
1295
   }
 
1296
 
 
1297
   if (arg == 2) {
 
1298
      Ftoparc = ptoparc = 0;                                               //  forget arcs
 
1299
      return;
 
1300
   }
 
1301
   
 
1302
   ptoparc = Ftoparc;                                                      //  save for future erase
 
1303
   ptoparcx = toparcx;
 
1304
   ptoparcy = toparcy;
 
1305
   ptoparcw = toparcw;
 
1306
   ptoparch = toparch;
 
1307
 
 
1308
   return;
 
1309
}
 
1310
 
 
1311
 
 
1312
/**************************************************************************/
 
1313
 
 
1314
//  draw dotted line. coordinates are in image space.
 
1315
 
 
1316
void draw_line(int ix1, int iy1, int ix2, int iy2)
 
1317
{
 
1318
   void draw_line_pixel(double pxm, double pym);
 
1319
 
 
1320
   double      x1, y1, x2, y2;   
 
1321
   double      pxm, pym, slope;
 
1322
   
 
1323
   x1 = Mscale * (ix1-Iorgx);                                              //  image to window space
 
1324
   y1 = Mscale * (iy1-Iorgy);
 
1325
   x2 = Mscale * (ix2-Iorgx);
 
1326
   y2 = Mscale * (iy2-Iorgy);
 
1327
   
 
1328
   if (abs(y2 - y1) > abs(x2 - x1)) {
 
1329
      slope = 1.0 * (x2 - x1) / (y2 - y1);
 
1330
      if (y2 > y1) {
 
1331
         for (pym = y1; pym <= y2; pym++) {
 
1332
            pxm = round(x1 + slope * (pym - y1));
 
1333
            draw_line_pixel(pxm,pym);
 
1334
         }
 
1335
      }
 
1336
      else {
 
1337
         for (pym = y1; pym >= y2; pym--) {
 
1338
            pxm = round(x1 + slope * (pym - y1));
 
1339
            draw_line_pixel(pxm,pym);
 
1340
         }
 
1341
      }
 
1342
   }
 
1343
   else {
 
1344
      slope = 1.0 * (y2 - y1) / (x2 - x1);
 
1345
      if (x2 > x1) {
 
1346
         for (pxm = x1; pxm <= x2; pxm++) {
 
1347
            pym = round(y1 + slope * (pxm - x1));
 
1348
            draw_line_pixel(pxm,pym);
 
1349
         }
 
1350
      }
 
1351
      else {
 
1352
         for (pxm = x1; pxm >= x2; pxm--) {
 
1353
            pym = round(y1 + slope * (pxm - x1));
 
1354
            draw_line_pixel(pxm,pym);
 
1355
         }
 
1356
      }
 
1357
   }
 
1358
 
 
1359
   gdk_gc_set_foreground(gdkgc,&black);
 
1360
   fg_color = black;
 
1361
 
 
1362
   return;
 
1363
}
 
1364
 
 
1365
void draw_line_pixel(double px, double py)
 
1366
{
 
1367
   int            pxn, pyn;
 
1368
   static int     flip = 0;
 
1369
   
 
1370
   pxn = px;
 
1371
   pyn = py;
 
1372
   
 
1373
   if (pxn < 0 || pxn > dww-1) return;
 
1374
   if (pyn < 0 || pyn > dhh-1) return;
 
1375
   
 
1376
   if (++flip > 4) flip = -5;                                              //  black/white line     v.10.10
 
1377
   if (flip < 0) gdk_gc_set_foreground(gdkgc,&black);
 
1378
   else gdk_gc_set_foreground(gdkgc,&white);
 
1379
   gdk_draw_point(drWin->window, gdkgc, pxn + Dorgx, pyn + Dorgy);
 
1380
   
 
1381
   return;
 
1382
}
 
1383
 
 
1384
 
 
1385
//  erase line. refresh line path from Dpxm8 pixels.
 
1386
 
 
1387
void erase_line(int ix1, int iy1, int ix2, int iy2)
 
1388
{
 
1389
   void erase_line_pixel(double pxm, double pym);
 
1390
 
 
1391
   double      x1, y1, x2, y2;   
 
1392
   double      pxm, pym, slope;
 
1393
   
 
1394
   if (! Dpxm8) zappcrash("Dpxm8 = 0");                                    //  v.10.3
 
1395
 
 
1396
   x1 = Mscale * (ix1-Iorgx);
 
1397
   y1 = Mscale * (iy1-Iorgy);
 
1398
   x2 = Mscale * (ix2-Iorgx);
 
1399
   y2 = Mscale * (iy2-Iorgy);
 
1400
   
 
1401
   if (abs(y2 - y1) > abs(x2 - x1)) {
 
1402
      slope = 1.0 * (x2 - x1) / (y2 - y1);
 
1403
      if (y2 > y1) {
 
1404
         for (pym = y1; pym <= y2; pym++) {
 
1405
            pxm = x1 + slope * (pym - y1);
 
1406
            erase_line_pixel(pxm,pym);
 
1407
         }
 
1408
      }
 
1409
      else {
 
1410
         for (pym = y1; pym >= y2; pym--) {
 
1411
            pxm = x1 + slope * (pym - y1);
 
1412
            erase_line_pixel(pxm,pym);
 
1413
         }
 
1414
      }
 
1415
   }
 
1416
   else {
 
1417
      slope = 1.0 * (y2 - y1) / (x2 - x1);
 
1418
      if (x2 > x1) {
 
1419
         for (pxm = x1; pxm <= x2; pxm++) {
 
1420
            pym = y1 + slope * (pxm - x1);
 
1421
            erase_line_pixel(pxm,pym);
 
1422
         }
 
1423
      }
 
1424
      else {
 
1425
         for (pxm = x1; pxm >= x2; pxm--) {
 
1426
            pym = y1 + slope * (pxm - x1);
 
1427
            erase_line_pixel(pxm,pym);
 
1428
         }
 
1429
      }
 
1430
   }
 
1431
 
 
1432
   return;
 
1433
}
 
1434
 
 
1435
void erase_line_pixel(double px, double py)
 
1436
{
 
1437
   int            pxn, pyn;
 
1438
   
 
1439
   pxn = px;
 
1440
   pyn = py;
 
1441
   
 
1442
   if (pxn < 0 || pxn > dww-1) return;
 
1443
   if (pyn < 0 || pyn > dhh-1) return;
 
1444
 
 
1445
   uint8 *pixel = (uint8 *) Dpxm8->bmp + (pyn * dww + pxn) * 3;
 
1446
   gdk_draw_rgb_image(drWin->window, gdkgc, pxn + Dorgx, pyn + Dorgy, 
 
1447
                                 1, 1, NODITHER, pixel, dww * 3);         
 
1448
   return;
 
1449
}
 
1450
 
 
1451
 
 
1452
//  draw one pixel using given color, R/G/B = red/green/black
 
1453
 
 
1454
void draw_pixel(int px, int py, GdkColor *color)                           //  v.10.12
 
1455
{
 
1456
   int            qx, qy;
 
1457
   static int     pqx, pqy;
 
1458
   
 
1459
   qx = Mscale * (px-Iorgx) + 0.5;                                         //  image to window space
 
1460
   qy = Mscale * (py-Iorgy) + 0.5;
 
1461
 
 
1462
   if (qx == pqx && qy == pqy) return;                                     //  avoid redundant points   v.9.7
 
1463
 
 
1464
   pqx = qx;
 
1465
   pqy = qy;
 
1466
   
 
1467
   qx = qx + Dorgx;                                                        //  image origin in drawing window
 
1468
   qy = qy + Dorgy;
 
1469
 
 
1470
   if (qx < 0 || qx > Dww-1) return;                                       //  bugfix v.11.03.1
 
1471
   if (qy < 0 || qy > Dhh-1) return;
 
1472
 
 
1473
   if (color != &fg_color) {
 
1474
      fg_color = *color;
 
1475
      gdk_gc_set_foreground(gdkgc,&fg_color);
 
1476
   }
 
1477
   
 
1478
   gdk_draw_point(drWin->window,gdkgc,qx,qy);                              //  draw pixel
 
1479
 
 
1480
   return;
 
1481
}
 
1482
 
 
1483
 
 
1484
//  draw a fat pixel using given color, R/G/B = red/green/black
 
1485
 
 
1486
void draw_fat_pixel(int px, int py, GdkColor *color)                       //  speedup    v.11.04
 
1487
{
 
1488
   int            qx, qy;
 
1489
   static int     pqx, pqy;
 
1490
   
 
1491
   qx = Mscale * (px-Iorgx) + 0.5;                                         //  image to window space
 
1492
   qy = Mscale * (py-Iorgy) + 0.5;
 
1493
 
 
1494
   if (qx == pqx && qy == pqy) return;                                     //  avoid redundant points   v.9.7
 
1495
 
 
1496
   pqx = qx;
 
1497
   pqy = qy;
 
1498
   
 
1499
   qx = qx + Dorgx;                                                        //  image origin in drawing window
 
1500
   qy = qy + Dorgy;
 
1501
   
 
1502
   if (qx < 0 || qy < 0) return;
 
1503
   
 
1504
   if (color != &fg_color) {
 
1505
      fg_color = *color;
 
1506
      gdk_gc_set_foreground(gdkgc,&fg_color);
 
1507
   }
 
1508
   
 
1509
   if (qx < Dww-1 && qy < Dhh-1 && Mscale > 1.0)                     
 
1510
      gdk_draw_rectangle(drWin->window,gdkgc,0,qx,qy,2,2);                 //  draw fat pixel 2x2
 
1511
 
 
1512
   else if (qx < Dww && qy < Dhh)
 
1513
      gdk_draw_point(drWin->window,gdkgc,qx,qy);                           //  on edge, draw pixel
 
1514
 
 
1515
   return;
 
1516
}
 
1517
 
 
1518
 
 
1519
/**************************************************************************/
 
1520
 
 
1521
//  maintain a set of text strings written whenever the window is painted
 
1522
 
 
1523
//  add a new text string to the list
 
1524
//  multiple text strings can be added with the same ID
 
1525
//  px and py are image coordinates
 
1526
 
 
1527
void add_toptext(int ID, int px, int py, cchar *text, cchar *font)         //  new v.11.07
 
1528
{
 
1529
   if (Ntoptext == maxtoptext) {
 
1530
      printf("maxtoptext exceeded \n");
 
1531
      return;
 
1532
   }
 
1533
   
 
1534
   int ii = Ntoptext++;
 
1535
   toptext[ii].ID = ID;
 
1536
   toptext[ii].px = px;
 
1537
   toptext[ii].py = py;
 
1538
   toptext[ii].text = text;
 
1539
   toptext[ii].font = font;
 
1540
   
 
1541
   return;
 
1542
}
 
1543
 
 
1544
 
 
1545
//  delete text strings having the given ID from the list
 
1546
 
 
1547
void erase_toptext(int ID)                                                 //  new v.11.07
 
1548
{
 
1549
   int      ii, jj;
 
1550
 
 
1551
   for (ii = jj = 0; ii < Ntoptext; ii++)
 
1552
   {
 
1553
      if (toptext[ii].ID == ID) continue;
 
1554
      else toptext[jj++] = toptext[ii];
 
1555
   }
 
1556
   
 
1557
   Ntoptext = jj;
 
1558
 
 
1559
   return;
 
1560
}
 
1561
 
 
1562
 
 
1563
//  paint current text strings on window whenever it is repainted
 
1564
//  called from mwpaint()
 
1565
 
 
1566
void paint_toptext()                                                       //  new v.11.07
 
1567
{
 
1568
   int      ii, px, py;
 
1569
 
 
1570
   for (ii = 0; ii < Ntoptext; ii++) {
 
1571
      px = toptext[ii].px;
 
1572
      py = toptext[ii].py;
 
1573
      px = Mscale * (px - Iorgx) + Dorgx;                                  //  image to window space
 
1574
      py = Mscale * (py - Iorgy) + Dorgy;
 
1575
      paint_text(px,py,toptext[ii].text,toptext[ii].font);
 
1576
   }
 
1577
 
 
1578
   return;
 
1579
}
 
1580
 
 
1581
 
 
1582
//  paint text on window, black on white background
 
1583
//  px and py are window coordinates
 
1584
 
 
1585
void paint_text(int px, int py, cchar *text, cchar *font)                  //  new v.11.07
 
1586
{
 
1587
   static PangoFontDescription   *pangofont = null;
 
1588
   static PangoLayout            *pangolayout = null;
 
1589
   static char                   priorfont[40] = "";
 
1590
   
 
1591
   if (strNeq(font,priorfont)) {                                           //  change font
 
1592
      strncpy0(priorfont,font,40);
 
1593
      if (pangofont) pango_font_description_free(pangofont);
 
1594
      if (pangolayout) g_object_unref(pangolayout);
 
1595
      pangofont = pango_font_description_from_string(font);
 
1596
      pangolayout = gtk_widget_create_pango_layout(drWin,0);
 
1597
      pango_layout_set_font_description(pangolayout,pangofont);
 
1598
   }
 
1599
   
 
1600
   pango_layout_set_text(pangolayout,text,-1);
 
1601
   gdk_draw_layout_with_colors(drWin->window,gdkgc,px,py,pangolayout,&black,&white);
 
1602
 
 
1603
   return;
 
1604
}
 
1605
 
 
1606
 
 
1607
/**************************************************************************
 
1608
 
 
1609
   spline curve setup and edit functions
 
1610
 
 
1611
   Support multiple frames with curves in parallel                         //  v.11.01
 
1612
   (edit curve(s) and image mask curve)
 
1613
 
 
1614
   Usage:
 
1615
   Add frame widget to dialog or zdialog.
 
1616
   Set up drawing in frame: 
 
1617
      sd = curve_init(frame, callback_func)
 
1618
   Initialize no. of curves in frame (1-10): 
 
1619
      sd->Nspc = n
 
1620
   Initialize vert/horz flag for curve spc: 
 
1621
      sd->vert[spc] = hv
 
1622
   Initialize anchor points for curve spc: 
 
1623
      sd->nap[spc], sd->apx[spc][xx], sd->apy[spc][yy]
 
1624
   Generate data for curve spc:  
 
1625
      splcurve_generate(sd,spc)
 
1626
   Curves will now be shown and edited inside the frame when window is realized.
 
1627
   The callback_func(spc) will be called when curve spc is edited.
 
1628
   Change curve in program: 
 
1629
      set anchor points, call splcurve_generate(sd,spc)
 
1630
   Get y-value (0-1) for curve spc and given x-value (0-1):
 
1631
      yval = splcurve_yval(sd,spc,xval)
 
1632
   If faster access to curve is needed (no interpolation):
 
1633
      kk = 1000 * xval;
 
1634
      if (kk > 999) kk = 999;
 
1635
      yval = sd->yval[spc][kk];
 
1636
 
 
1637
***/
 
1638
 
 
1639
//  initialize for spline curve editing
 
1640
//  initial anchor points are pre-loaded into spldat before window is realized
 
1641
 
 
1642
spldat * splcurve_init(GtkWidget *frame, void func(int spc))
 
1643
{
 
1644
   int      cc = sizeof(spldat);                                           //  allocate spc curve data area
 
1645
   spldat * sd = (spldat *) zmalloc(cc,"curvedat");
 
1646
   memset(sd,0,cc);
 
1647
 
 
1648
   sd->drawarea = gtk_drawing_area_new();                                  //  drawing area for curves
 
1649
   gtk_container_add(GTK_CONTAINER(frame),sd->drawarea);
 
1650
   sd->spcfunc = func;                                                     //  user callback function
 
1651
 
 
1652
   gtk_widget_add_events(sd->drawarea,GDK_BUTTON_PRESS_MASK);              //  connect mouse events to drawing area
 
1653
   gtk_widget_add_events(sd->drawarea,GDK_BUTTON_RELEASE_MASK);
 
1654
   gtk_widget_add_events(sd->drawarea,GDK_BUTTON1_MOTION_MASK); 
 
1655
   G_SIGNAL(sd->drawarea,"motion-notify-event",splcurve_adjust,sd)
 
1656
   G_SIGNAL(sd->drawarea,"button-press-event",splcurve_adjust,sd)
 
1657
   G_SIGNAL(sd->drawarea,"expose-event",splcurve_draw,sd)
 
1658
   
 
1659
   return sd;
 
1660
}
 
1661
 
 
1662
 
 
1663
//  modify anchor points in curve using mouse
 
1664
 
 
1665
int splcurve_adjust(void *, GdkEventButton *event, spldat *sd)
 
1666
{
 
1667
   int            ww, hh, kk;
 
1668
   int            mx, my, button, evtype;
 
1669
   static int     spc, ap, mbusy = 0, Fdrag = 0;                           //  drag continuation logic   v.9.7
 
1670
   int            minspc, minap;
 
1671
   double         mxval, myval, cxval, cyval;
 
1672
   double         dist2, mindist2 = 0;
 
1673
   
 
1674
   mx = event->x;                                                          //  mouse position in drawing area
 
1675
   my = event->y;
 
1676
   evtype = event->type;
 
1677
   button = event->button;
 
1678
 
 
1679
   if (evtype == GDK_MOTION_NOTIFY) {
 
1680
      if (mbusy) return 0;                                                 //  discard excess motion events    v.11.01
 
1681
      mbusy++;
 
1682
      zmainloop();
 
1683
      mbusy = 0;
 
1684
   }
 
1685
 
 
1686
   if (evtype == GDK_BUTTON_RELEASE) {
 
1687
      Fdrag = 0;
 
1688
      return 0;
 
1689
   }
 
1690
   
 
1691
   ww = sd->drawarea->allocation.width;                                    //  drawing area size
 
1692
   hh = sd->drawarea->allocation.height;
 
1693
   
 
1694
   if (mx < 0) mx = 0;                                                     //  limit edge excursions
 
1695
   if (mx > ww) mx = ww;
 
1696
   if (my < 0) my = 0;
 
1697
   if (my > hh) my = hh;
 
1698
   
 
1699
   if (evtype == GDK_BUTTON_PRESS) Fdrag = 0;                              //  left or right click
 
1700
   
 
1701
   if (Fdrag)                                                              //  continuation of drag
 
1702
   {
 
1703
      if (sd->vert[spc]) {
 
1704
         mxval = 1.0 * my / hh;                                            //  mouse position in curve space
 
1705
         myval = 1.0 * mx / ww;
 
1706
      }
 
1707
      else {
 
1708
         mxval = 1.0 * mx / ww;
 
1709
         myval = 1.0 * (hh - my) / hh;
 
1710
      }
 
1711
 
 
1712
      if (ap < sd->nap[spc]-1 && sd->apx[spc][ap+1] - mxval < 0.05)        //  disallow < 0.05 from next
 
1713
            return 0;
 
1714
      if (ap > 0 && mxval - sd->apx[spc][ap-1] < 0.05) return 0;           //    or prior anchor point 
 
1715
   }
 
1716
   
 
1717
   else                                                                    //  mouse click or new drag begin
 
1718
   {
 
1719
      minspc = minap = -1;                                                 //  find closest curve/anchor point
 
1720
      mindist2 = 999999;
 
1721
 
 
1722
      for (spc = 0; spc < sd->Nspc; spc++)                                 //  loop curves
 
1723
      {
 
1724
         if (sd->vert[spc]) {
 
1725
            mxval = 1.0 * my / hh;                                         //  mouse position in curve space
 
1726
            myval = 1.0 * mx / ww;
 
1727
         }
 
1728
         else {
 
1729
            mxval = 1.0 * mx / ww;
 
1730
            myval = 1.0 * (hh - my) / hh;
 
1731
         }
 
1732
 
 
1733
         for (ap = 0; ap < sd->nap[spc]; ap++)                             //  loop anchor points
 
1734
         {
 
1735
            cxval = sd->apx[spc][ap];
 
1736
            cyval = sd->apy[spc][ap];
 
1737
            dist2 = (mxval-cxval)*(mxval-cxval) 
 
1738
                  + (myval-cyval)*(myval-cyval);
 
1739
            if (dist2 < mindist2) {
 
1740
               mindist2 = dist2;                                           //  remember closest anchor point
 
1741
               minspc = spc;
 
1742
               minap = ap;
 
1743
            }
 
1744
         }
 
1745
      }
 
1746
 
 
1747
      if (minspc < 0) return 0;                                            //  impossible
 
1748
      spc = minspc;
 
1749
      ap = minap;
 
1750
   }
 
1751
   
 
1752
   if (evtype == GDK_BUTTON_PRESS && button == 3)                          //  right click, remove anchor point
 
1753
   {
 
1754
      if (sqrt(mindist2) > 0.05) return 0;                                 //  not close enough
 
1755
      if (sd->nap[spc] < 3) return 0;                                      //  < 2 anchor points would remain
 
1756
      sd->nap[spc]--;                                                      //  decr. before loop      v.11.11
 
1757
      for (kk = ap; kk < sd->nap[spc]; kk++) {
 
1758
         if (! kk) printf("meaningless reference %d",kk);                  //  stop gcc optimization bug    v.11.11  /////
 
1759
         sd->apx[spc][kk] = sd->apx[spc][kk+1];
 
1760
         sd->apy[spc][kk] = sd->apy[spc][kk+1];
 
1761
      }
 
1762
      splcurve_generate(sd,spc);                                           //  regenerate data for modified curve
 
1763
      splcurve_draw(0,0,sd);                                               //  regen and redraw all curves
 
1764
      sd->spcfunc(spc);                                                    //  call user function
 
1765
      return 0;
 
1766
   }
 
1767
 
 
1768
   if (! Fdrag)                                                            //  new drag or left click
 
1769
   {
 
1770
      if (sd->vert[spc]) {
 
1771
         mxval = 1.0 * my / hh;                                            //  mouse position in curve space
 
1772
         myval = 1.0 * mx / ww;
 
1773
      }
 
1774
      else {
 
1775
         mxval = 1.0 * mx / ww;
 
1776
         myval = 1.0 * (hh - my) / hh;
 
1777
      }
 
1778
 
 
1779
      if (sqrt(mindist2) < 0.05)                                           //  existing point close enough,
 
1780
      {                                                                    //    move this anchor point to mouse
 
1781
         if (ap < sd->nap[spc]-1 && sd->apx[spc][ap+1] - mxval < 0.05)
 
1782
               return 0;                                                   //  disallow < 0.05 from next
 
1783
         if (ap > 0 && mxval - sd->apx[spc][ap-1] < 0.05) return 0;        //    or prior anchor point 
 
1784
      }
 
1785
   
 
1786
      else                                                                 //  none close, add new anchor point
 
1787
      {
 
1788
         minspc = -1;                                                      //  find closest curve to mouse
 
1789
         mindist2 = 999999;
 
1790
 
 
1791
         for (spc = 0; spc < sd->Nspc; spc++)                              //  loop curves
 
1792
         {
 
1793
            if (sd->vert[spc]) {
 
1794
               mxval = 1.0 * my / hh;                                      //  mouse position in curve space
 
1795
               myval = 1.0 * mx / ww;
 
1796
            }
 
1797
            else {
 
1798
               mxval = 1.0 * mx / ww;
 
1799
               myval = 1.0 * (hh - my) / hh;
 
1800
            }
 
1801
 
 
1802
            cyval = splcurve_yval(sd,spc,mxval);
 
1803
            dist2 = fabs(myval - cyval);
 
1804
            if (dist2 < mindist2) {
 
1805
               mindist2 = dist2;                                           //  remember closest curve
 
1806
               minspc = spc;
 
1807
            }
 
1808
         }
 
1809
 
 
1810
         if (minspc < 0) return 0;                                         //  impossible
 
1811
         if (mindist2 > 0.05) return 0;                                    //  not close enough to any curve
 
1812
         spc = minspc;
 
1813
 
 
1814
         if (sd->nap[spc] > 49) {
 
1815
            zmessageACK(mWin,ZTX("Exceed 50 anchor points"));
 
1816
            return 0;
 
1817
         }
 
1818
 
 
1819
         if (sd->vert[spc]) {
 
1820
            mxval = 1.0 * my / hh;                                         //  mouse position in curve space
 
1821
            myval = 1.0 * mx / ww;
 
1822
         }
 
1823
         else {
 
1824
            mxval = 1.0 * mx / ww;
 
1825
            myval = 1.0 * (hh - my) / hh;
 
1826
         }
 
1827
 
 
1828
         for (ap = 0; ap < sd->nap[spc]; ap++)                             //  find anchor point with next higher x
 
1829
            if (mxval <= sd->apx[spc][ap]) break;                          //    (ap may come out 0 or nap)
 
1830
 
 
1831
         if (ap < sd->nap[spc] && sd->apx[spc][ap] - mxval < 0.05)         //  disallow < 0.05 from next
 
1832
               return 0;                                                   //    or prior anchor point
 
1833
         if (ap > 0 && mxval - sd->apx[spc][ap-1] < 0.05) return 0;
 
1834
 
 
1835
         for (kk = sd->nap[spc]; kk > ap; kk--) {                          //  make hole for new point
 
1836
            sd->apx[spc][kk] = sd->apx[spc][kk-1];
 
1837
            sd->apy[spc][kk] = sd->apy[spc][kk-1];
 
1838
         }
 
1839
 
 
1840
         sd->nap[spc]++;                                                   //  up point count
 
1841
      }
 
1842
   }
 
1843
 
 
1844
   sd->apx[spc][ap] = mxval;                                               //  new or moved anchor point
 
1845
   sd->apy[spc][ap] = myval;                                               //    at mouse position
 
1846
 
 
1847
   splcurve_generate(sd,spc);                                              //  regenerate data for modified curve
 
1848
   splcurve_draw(0,0,sd);                                                  //  regen and redraw all curves
 
1849
   sd->spcfunc(spc);                                                       //  call user function
 
1850
   
 
1851
   if (evtype == GDK_MOTION_NOTIFY) Fdrag = 1;                             //  remember drag is underway
 
1852
   return 0;
 
1853
}
 
1854
 
 
1855
 
 
1856
//  for expose event or when a curve is changed
 
1857
//  draw all curves based on current anchor points
 
1858
 
 
1859
int splcurve_draw(void *, void *, spldat *sd)
 
1860
{
 
1861
   int         ww, hh, px, py, qx, qy, spc, ap;
 
1862
   double      xval, yval;
 
1863
   
 
1864
   ww = sd->drawarea->allocation.width;                                    //  drawing area size
 
1865
   hh = sd->drawarea->allocation.height;
 
1866
   if (ww < 50 || hh < 50) return 0;
 
1867
 
 
1868
   gdk_window_clear(sd->drawarea->window);                                 //  clear window
 
1869
   
 
1870
   gdk_gc_set_foreground(gdkgc,&dgray);
 
1871
 
 
1872
   for (int ii = 0; ii < sd->Nscale; ii++)                                 //  draw y-scale lines if any    v.11.07
 
1873
   {
 
1874
      px = ww * sd->xscale[0][ii] + 0.5;                                   //  any line, not only horizontal   v.11.10
 
1875
      py = hh - hh * sd->yscale[0][ii] + 0.5;
 
1876
      qx = ww * sd->xscale[1][ii] + 0.5;
 
1877
      qy = hh - hh * sd->yscale[1][ii] + 0.5;
 
1878
      gdk_draw_line(sd->drawarea->window,gdkgc,px,py,qx,qy);
 
1879
   }
 
1880
 
 
1881
   gdk_gc_set_foreground(gdkgc,&black);
 
1882
   fg_color = black;
 
1883
 
 
1884
   for (spc = 0; spc < sd->Nspc; spc++)
 
1885
   {
 
1886
      if (sd->vert[spc])                                                   //  vert. curve
 
1887
      {
 
1888
         for (py = 0; py < hh; py++)                                       //  generate all points for curve
 
1889
         {
 
1890
            xval = 1.0 * py / hh;                                          //  remove anchor point limits   v.10.9
 
1891
            yval = splcurve_yval(sd,spc,xval);
 
1892
            px = ww * yval + 0.49;                                         //  "almost" round - erratic floating point
 
1893
            gdk_draw_point(sd->drawarea->window,gdkgc,px,py);              //    causes "bumps" in a flat curve
 
1894
         }
 
1895
         
 
1896
         for (ap = 0; ap < sd->nap[spc]; ap++)                             //  draw boxes at anchor points
 
1897
         {
 
1898
            xval = sd->apx[spc][ap];
 
1899
            yval = sd->apy[spc][ap];
 
1900
            px = ww * yval;
 
1901
            py = hh * xval;
 
1902
            for (qx = -2; qx < 3; qx++)
 
1903
            for (qy = -2; qy < 3; qy++) {
 
1904
               if (px+qx < 0 || px+qx >= ww) continue;
 
1905
               if (py+qy < 0 || py+qy >= hh) continue;
 
1906
               gdk_draw_point(sd->drawarea->window,gdkgc,px+qx,py+qy);
 
1907
            }
 
1908
         }
 
1909
      }
 
1910
      else                                                                 //  horz. curve
 
1911
      {
 
1912
         for (px = 0; px < ww; px++)                                       //  generate all points for curve
 
1913
         {
 
1914
            xval = 1.0 * px / ww;                                          //  remove anchor point limits   v.10.9
 
1915
            yval = splcurve_yval(sd,spc,xval);
 
1916
            py = hh - hh * yval + 0.49;                                    //  almost round - erratic FP in Intel CPUs
 
1917
            gdk_draw_point(sd->drawarea->window,gdkgc,px,py);              //    causes "bumps" in a flat curve
 
1918
         }
 
1919
         
 
1920
         for (ap = 0; ap < sd->nap[spc]; ap++)                             //  draw boxes at anchor points
 
1921
         {
 
1922
            xval = sd->apx[spc][ap];
 
1923
            yval = sd->apy[spc][ap];
 
1924
            px = ww * xval;
 
1925
            py = hh - hh * yval;
 
1926
            for (qx = -2; qx < 3; qx++)
 
1927
            for (qy = -2; qy < 3; qy++) {
 
1928
               if (px+qx < 0 || px+qx >= ww) continue;
 
1929
               if (py+qy < 0 || py+qy >= hh) continue;
 
1930
               gdk_draw_point(sd->drawarea->window,gdkgc,px+qx,py+qy);
 
1931
            }
 
1932
         }
 
1933
      }
 
1934
   }
 
1935
 
 
1936
   return 0;
 
1937
}
 
1938
 
 
1939
 
 
1940
//  generate all curve data points when anchor points are modified
 
1941
 
 
1942
int splcurve_generate(spldat *sd, int spc)
 
1943
{
 
1944
   int      kk, kklo, kkhi;
 
1945
   double   xval, yvalx;
 
1946
 
 
1947
   spline1(sd->nap[spc],sd->apx[spc],sd->apy[spc]);                        //  compute curve fitting anchor points
 
1948
 
 
1949
   kklo = 1000 * sd->apx[spc][0] - 30;                                     //  xval range = anchor point range
 
1950
   if (kklo < 0) kklo = 0;                                                 //    + 0.03 extra below/above      v.9.5
 
1951
   kkhi = 1000 * sd->apx[spc][sd->nap[spc]-1] + 30;
 
1952
   if (kkhi > 1000) kkhi = 1000;
 
1953
 
 
1954
   for (kk = 0; kk < 1000; kk++)                                           //  generate all points for curve
 
1955
   {
 
1956
      xval = 0.001 * kk;                                                   //  remove anchor point limits   v.10.9
 
1957
      yvalx = spline2(xval);
 
1958
      if (yvalx < 0) yvalx = 0;                                            //  yval < 0 not allowed, > 1 OK    v.9.5
 
1959
      sd->yval[spc][kk] = yvalx;
 
1960
   }
 
1961
 
 
1962
   return 0;
 
1963
}
 
1964
 
 
1965
 
 
1966
//  Retrieve curve data using interpolation of saved table of values
 
1967
 
 
1968
double splcurve_yval(spldat *sd, int spc, double xval)
 
1969
{
 
1970
   int      ii;
 
1971
   double   x1, x2, y1, y2, y3;
 
1972
   
 
1973
   if (xval <= 0) return sd->yval[spc][0];
 
1974
   if (xval >= 0.999) return sd->yval[spc][999];
 
1975
   
 
1976
   x2 = 1000.0 * xval;
 
1977
   ii = x2;
 
1978
   x1 = ii;
 
1979
   y1 = sd->yval[spc][ii];
 
1980
   y2 = sd->yval[spc][ii+1];
 
1981
   y3 = y1 + (y2 - y1) * (x2 - x1);
 
1982
   return y3;
 
1983
}
 
1984
 
 
1985
 
 
1986
//  load curve data from a file
 
1987
//  returns 0 if fail (invalid file data), sd not modified
 
1988
//  returns 1 if succcess, sd is initialized from file data
 
1989
 
 
1990
int splcurve_load(spldat *sd)                                              //  v.11.02
 
1991
{
 
1992
   char        *pfile;
 
1993
   int         nn, ii, jj;
 
1994
   FILE        *fid = 0;
 
1995
   int         Nspc, vert[10], nap[10];
 
1996
   double      apx[10][50], apy[10][50];
 
1997
 
 
1998
   zfuncs::F1_help_topic = "curve_edit";
 
1999
 
 
2000
   pfile = zgetfile1(ZTX("load curve from a file"),"open",saved_curves_dirk);
 
2001
   if (! pfile) return 0;
 
2002
 
 
2003
   fid = fopen(pfile,"r");
 
2004
   zfree(pfile);
 
2005
   if (! fid) goto fail;
 
2006
   
 
2007
   nn = fscanf(fid,"%d ",&Nspc);                                           //  no. of curves
 
2008
   if (nn != 1) goto fail;
 
2009
   if (Nspc < 1 || Nspc > 10) goto fail;
 
2010
   if (Nspc != sd->Nspc) goto fail2;
 
2011
 
 
2012
   for (ii = 0; ii < Nspc; ii++)                                           //  loop each curve
 
2013
   {
 
2014
      nn = fscanf(fid,"%d  %d ",&vert[ii],&nap[ii]);                       //  vertical flag, no. anchor points
 
2015
      if (nn != 2) goto fail;
 
2016
      if (vert[ii] < 0 || vert[ii] > 1) goto fail;
 
2017
      if (nap[ii] < 2 || nap[ii] > 50) goto fail;
 
2018
 
 
2019
      for (jj = 0; jj < nap[ii]; jj++)                                     //  anchor point values 
 
2020
      {
 
2021
         nn = fscanf(fid,"%lf/%lf  ",&apx[ii][jj],&apy[ii][jj]);
 
2022
         if (nn != 2) goto fail;
 
2023
         if (apx[ii][jj] < 0 || apx[ii][jj] > 1) goto fail;
 
2024
         if (apy[ii][jj] < 0 || apy[ii][jj] > 1) goto fail;
 
2025
      }
 
2026
   }
 
2027
   
 
2028
   fclose(fid);
 
2029
   
 
2030
   sd->Nspc = Nspc;                                                        //  copy curve data to caller's arg
 
2031
 
 
2032
   for (ii = 0; ii < Nspc; ii++)
 
2033
   {
 
2034
      sd->vert[ii] = vert[ii];
 
2035
      sd->nap[ii] = nap[ii];
 
2036
      
 
2037
      for (jj = 0; jj < nap[ii]; jj++)
 
2038
      {
 
2039
         sd->apx[ii][jj] = apx[ii][jj];
 
2040
         sd->apy[ii][jj] = apy[ii][jj];
 
2041
      }
 
2042
   }
 
2043
 
 
2044
   for (ii = 0; ii < Nspc; ii++)                                           //  generate curve data from anchor points
 
2045
      splcurve_generate(sd,ii);
 
2046
   
 
2047
   if (sd->drawarea) splcurve_draw(0,0,sd);                                //  regen and redraw all curves
 
2048
 
 
2049
   return 1;
 
2050
   
 
2051
fail:
 
2052
   if (fid) fclose(fid);
 
2053
   zmessageACK(mWin,ZTX("curve file is invalid"));
 
2054
   return 0;
 
2055
 
 
2056
fail2:
 
2057
   if (fid) fclose(fid);
 
2058
   zmessageACK(mWin,ZTX("curve file has different no. of curves"));
 
2059
   return 0;
 
2060
}
 
2061
 
 
2062
 
 
2063
//  save curve data to a file
 
2064
 
 
2065
int splcurve_save(spldat *sd)                                              //  v.11.02
 
2066
{
 
2067
   char        *pfile, *pp;
 
2068
   int         ii, jj;
 
2069
   FILE        *fid;
 
2070
 
 
2071
   zfuncs::F1_help_topic = "curve_edit";
 
2072
 
 
2073
   pp = zgetfile1(ZTX("save curve to a file"),"save",saved_curves_dirk);
 
2074
   if (! pp) return 0;
 
2075
 
 
2076
   pfile = strdupz(pp,8);
 
2077
   zfree(pp);
 
2078
 
 
2079
   pp = strrchr(pfile,'/');                                                //  force .curve extension
 
2080
   if (pp) pp = strrchr(pp,'.');
 
2081
   if (pp) strcpy(pp,".curve");
 
2082
   else strcat(pfile,".curve");
 
2083
 
 
2084
   fid = fopen(pfile,"w");
 
2085
   zfree(pfile);
 
2086
   if (! fid) return 0;
 
2087
   
 
2088
   fprintf(fid,"%d \n",sd->Nspc);                                          //  no. of curves
 
2089
 
 
2090
   for (ii = 0; ii < sd->Nspc; ii++)                                       //  loop each curve
 
2091
   {
 
2092
      fprintf(fid,"%d  %d \n",sd->vert[ii],sd->nap[ii]);                   //  vertical flag, no. anchor points
 
2093
      for (jj = 0; jj < sd->nap[ii]; jj++)                                 //  anchor point values 
 
2094
         fprintf(fid,"%.4f/%.4f  ",sd->apx[ii][jj],sd->apy[ii][jj]);
 
2095
      fprintf(fid,"\n");
 
2096
   }
 
2097
   
 
2098
   fclose(fid);
 
2099
   return 0;
 
2100
}
 
2101
 
 
2102
 
 
2103
/**************************************************************************
 
2104
      zdialog mouse capture and release
 
2105
***************************************************************************/
 
2106
 
 
2107
void takeMouse(zdialog *zd, CBfunc func, GdkCursor *cursor)                //  capture mouse for dialog     v.11.03
 
2108
{
 
2109
   freeMouse();
 
2110
   if (zd && zdialog_widget(zd,"mymouse"))
 
2111
      zdialog_stuff(zd,"mymouse",1);                                       //  set zdialog radio button on
 
2112
   mouse_zd = zd;
 
2113
   mouseCBfunc = func;
 
2114
   Mcapture = 1;
 
2115
   gdk_window_set_cursor(drWin->window,cursor);                            //  v.11.03
 
2116
   return;
 
2117
}
 
2118
 
 
2119
 
 
2120
void freeMouse()                                                           //  free mouse for main window   v.10.12
 
2121
{
 
2122
   if (mouse_zd && zdialog_widget(mouse_zd,"mymouse")) 
 
2123
      zdialog_stuff(mouse_zd,"mymouse",0);                                 //  set radio button off
 
2124
   mouse_zd = 0;
 
2125
   mouseCBfunc = 0;
 
2126
   Mcapture = 0;
 
2127
   paint_toparc(2);                                                        //  remove mouse circle          v.11.04
 
2128
   gdk_window_set_cursor(drWin->window,0);                                 //  set normal cursor            v.11.03
 
2129
   return;
 
2130
}
 
2131
 
 
2132
 
 
2133
/**************************************************************************
 
2134
      file menu functions
 
2135
***************************************************************************/
 
2136
 
 
2137
//  display image gallery (thumbnails) in a separate window
 
2138
 
 
2139
void m_gallery(GtkWidget *, cchar *)
 
2140
{
 
2141
   zfuncs::F1_help_topic = "navigation";
 
2142
   
 
2143
   if (curr_file)
 
2144
      image_gallery(0,"paint1",curr_file_posn,m_gallery2,mWin);            //  overlay main window    v.10.9
 
2145
   else {
 
2146
      char *pp = getcwd(command,ccc);                                      //  v.11.09
 
2147
      if (pp) {
 
2148
         image_gallery(pp,"init",0,m_gallery2,mWin);                       //  use current directory   v.11.04
 
2149
         image_gallery(0,"paint1");
 
2150
      }
 
2151
      curr_file_posn = 0;                                                  //  v.11.05
 
2152
   }
 
2153
 
 
2154
   return;
 
2155
}
 
2156
 
 
2157
 
 
2158
//  clicked thumbnail will call this function
 
2159
 
 
2160
void m_gallery2(int Nth, int button)
 
2161
{
 
2162
   char     *file;
 
2163
 
 
2164
   zfuncs::F1_help_topic = "navigation";                                   //  v.11.08
 
2165
 
 
2166
   if (Nth == -1) return;                                                  //  gallery window closed
 
2167
 
 
2168
   file = image_gallery(0,"find",Nth);
 
2169
   if (! file) return;
 
2170
   
 
2171
   if (edit_coll_name && button == 3)                                      //  right-click, pass to edit collection
 
2172
   {                                                                       //  v.11.11
 
2173
      edit_coll_popmenu(null,file);
 
2174
      zfree(file);
 
2175
      return;
 
2176
   }
 
2177
 
 
2178
   f_open(file,1,Nth);                                                     //  clicked file = current file
 
2179
   zfree(file);
 
2180
 
 
2181
   gtk_window_present(MWIN);                                               //  bring main window to front   v.10.12
 
2182
   return;
 
2183
}
 
2184
 
 
2185
 
 
2186
/**************************************************************************/
 
2187
 
 
2188
//  start a new parallel instance of fotoxx
 
2189
//  move my window to right half of desktop
 
2190
//  start new instance to open in left half
 
2191
 
 
2192
void m_clone1(GtkWidget *, cchar *)
 
2193
{
 
2194
   GdkScreen   *screen;
 
2195
   int         ww, hh, err;
 
2196
 
 
2197
   zfuncs::F1_help_topic = "clone";                                        //  v.10.8
 
2198
 
 
2199
   screen = gdk_screen_get_default();                                      //  get desktop screen size
 
2200
   ww = gdk_screen_get_width(screen);
 
2201
   hh = gdk_screen_get_height(screen);
 
2202
   ww = ww / 2 - 20;
 
2203
   hh = hh - 50;
 
2204
   gdk_window_move_resize(mWin->window,ww+20,10,ww,hh);                    //  move my window to right half
 
2205
   
 
2206
   snprintf(command,ccc,"fotoxx -clone1 -lang %s",zfuncs::zlanguage);      //  start new instance
 
2207
   if (curr_file) strncatv(command,ccc," \"",curr_file,"\"",null);
 
2208
   strcat(command," &");
 
2209
   err = system(command);
 
2210
   if (err) printf("error: %s \n",wstrerror(err));
 
2211
   return;
 
2212
}
 
2213
 
 
2214
 
 
2215
//  start a new parallel instance of fotoxx
 
2216
//  new window is slightly down and right from old window
 
2217
 
 
2218
void m_clone2(GtkWidget *, cchar *)                                        //  new  v.11.07
 
2219
{
 
2220
   int         xx, yy, ww, hh, err;
 
2221
 
 
2222
   zfuncs::F1_help_topic = "clone";
 
2223
 
 
2224
   gtk_window_get_position(MWIN,&xx,&yy);                                  //  get window position and size
 
2225
   gtk_window_get_size(MWIN,&ww,&hh);
 
2226
 
 
2227
   snprintf(command,ccc,"fotoxx -clone2 %d %d %d %d -lang %s",xx,yy,ww,hh,zfuncs::zlanguage);
 
2228
   if (curr_file) strncatv(command,ccc," \"",curr_file,"\"",null);
 
2229
   strcat(command," &");                                                   //  start new instance and pass
 
2230
   err = system(command);                                                  //    my window posn and size
 
2231
   if (err) printf("error: %s \n",wstrerror(err));
 
2232
   return;
 
2233
}
 
2234
 
 
2235
 
 
2236
/**************************************************************************/
 
2237
 
 
2238
//  open file menu function
 
2239
 
 
2240
void m_open(GtkWidget *, cchar *)
 
2241
{
 
2242
   zfuncs::F1_help_topic = "open_image_file";
 
2243
   f_open(null,1);
 
2244
   return;
 
2245
}
 
2246
 
 
2247
 
 
2248
/**************************************************************************/
 
2249
 
 
2250
//  open drag-drop file
 
2251
 
 
2252
void m_open_drag(int x, int y, char *file)
 
2253
{
 
2254
   zfuncs::F1_help_topic = "open_image_file";
 
2255
   f_open(file,1);
 
2256
   return;
 
2257
}
 
2258
 
 
2259
 
 
2260
/**************************************************************************/
 
2261
 
 
2262
//  open a new file in a new parallel instance of fotoxx
 
2263
//  new window is slightly down and right from old window
 
2264
 
 
2265
void m_open_newin(GtkWidget *, cchar *)                                    //  new  v.11.07
 
2266
{
 
2267
   int         xx, yy, ww, hh, err;
 
2268
   char        *file;
 
2269
 
 
2270
   zfuncs::F1_help_topic = "open_image_file";
 
2271
 
 
2272
   file = zgetfile1(ZTX("Open Image File"),"open",curr_file);              //  dialog to get filespec
 
2273
   if (! file) return;                                                     //  canceled
 
2274
   if (image_file_type(file) != 2) return;                                 //  not a supported image file      v.11.05
 
2275
 
 
2276
   gtk_window_get_position(MWIN,&xx,&yy);                                  //  get window position and size
 
2277
   gtk_window_get_size(MWIN,&ww,&hh);
 
2278
 
 
2279
   snprintf(command,ccc,"fotoxx -c2 %d %d %d %d -l %s",xx,yy,ww,hh,zfuncs::zlanguage);
 
2280
   strncatv(command,ccc," \"",file,"\"",null);
 
2281
   strcat(command," &");                                                   //  start new instance and pass
 
2282
   err = system(command);                                                  //    my window posn and size
 
2283
   if (err) printf("error: %s \n",wstrerror(err));
 
2284
   return;
 
2285
}
 
2286
 
 
2287
 
 
2288
/**************************************************************************/
 
2289
 
 
2290
//  open the previous file opened (not the same as toolbar [prev] button)
 
2291
//  repeated use will cycle back and forth between two most recent files
 
2292
 
 
2293
void m_previous(GtkWidget *, cchar *)
 
2294
{
 
2295
   int      ii, jj, err;
 
2296
   
 
2297
   zfuncs::F1_help_topic = "open_previous_file";
 
2298
 
 
2299
   if (! menulock(1)) return;                                              //  v.11.11
 
2300
   menulock(0);
 
2301
   if (mod_keep()) return;                                                 //  v.11.06
 
2302
   
 
2303
   if (curr_file) jj = 1;                                                  //  2nd most recent file   v.11.08
 
2304
   else jj = 0;                                                            //  no current file, use most recent
 
2305
 
 
2306
   for (ii = jj; ii < Nrecentfiles; ii++)
 
2307
   {
 
2308
      err = f_open(recentfiles[ii],1);                                     //  get first one that is still there
 
2309
      if (! err) return;
 
2310
   }
 
2311
 
 
2312
   return;
 
2313
}
 
2314
 
 
2315
 
 
2316
/**************************************************************************/
 
2317
 
 
2318
//  open an image file from the list of recent image files
 
2319
 
 
2320
void m_recent(GtkWidget *, cchar *)                                        //  overhauled    v.11.01
 
2321
{
 
2322
   void recentfile2(int Nth, int button);
 
2323
 
 
2324
   int            ii, jj, typ;
 
2325
   FILE           *fid;
 
2326
 
 
2327
   zfuncs::F1_help_topic = "open_recent_file";
 
2328
 
 
2329
   if (! menulock(1)) return;
 
2330
   menulock(0);
 
2331
   if (mod_keep()) return;                                                 //  v.11.11
 
2332
 
 
2333
   fid = fopen(recentfiles_file,"w");                                      //  revalidate list of recent files
 
2334
   if (! fid) return;                                                      //  and update disk file
 
2335
   
 
2336
   for (ii = jj = 0; ii < Nrecentfiles; ii++)
 
2337
   {
 
2338
      if (! recentfiles[ii]) continue;                                     //  improved      v.11.11
 
2339
      typ = image_file_type(recentfiles[ii]);
 
2340
      if (typ != 2) {
 
2341
         zfree(recentfiles[ii]);
 
2342
         recentfiles[ii] = 0;
 
2343
         continue;
 
2344
      }
 
2345
      if (jj < ii) {
 
2346
         recentfiles[jj] = recentfiles[ii];
 
2347
         recentfiles[ii] = 0;                                              //  bugfix        v.11.11.1
 
2348
      }
 
2349
      fprintf(fid,"%s \n",recentfiles[jj]);
 
2350
      jj++;
 
2351
   }
 
2352
 
 
2353
   fclose(fid);
 
2354
   
 
2355
   if (! jj) return;                                                       //  no files found
 
2356
   
 
2357
   image_gallery(recentfiles_file,"initF",0,recentfile2,mWin);             //  generate gallery of recent files
 
2358
   image_gallery(0,"paint1",0);                                            //  show new image gallery window
 
2359
 
 
2360
   return;
 
2361
}
 
2362
 
 
2363
 
 
2364
//  clicked thumbnail will call this function
 
2365
 
 
2366
void recentfile2(int Nth, int button)
 
2367
{
 
2368
   char     *file;
 
2369
 
 
2370
   if (Nth == -1) {                                                        //  gallery window cancelled
 
2371
      if (curr_file) image_gallery(curr_file,"init");                      //  reset gallery from current file    v.11.05
 
2372
      return;
 
2373
   }
 
2374
   
 
2375
   file = image_gallery(0,"find",Nth);
 
2376
   if (! file) return;
 
2377
   image_gallery(file,"init");                                             //  initz. gallery from chosen file    v.11.05
 
2378
   image_gallery(0,"close");                                               //  close the gallery
 
2379
   f_open(file,1);                                                         //  open the file
 
2380
   zfree(file);
 
2381
   return;
 
2382
}
 
2383
 
 
2384
 
 
2385
/**************************************************************************/
 
2386
 
 
2387
//  add a file to the list of recent files
 
2388
 
 
2389
void add_recent_file(cchar *file)
 
2390
{
 
2391
   int      ii;
 
2392
 
 
2393
   for (ii = 0; ii < Nrecentfiles-1 && recentfiles[ii]; ii++)              //  find file in recent list
 
2394
      if (strEqu(file,recentfiles[ii])) break;                             //    (or find last entry in list)
 
2395
   if (recentfiles[ii]) zfree(recentfiles[ii]);                            //  free this slot in list
 
2396
   for (; ii > 0; ii--) recentfiles[ii] = recentfiles[ii-1];               //  move list UP to fill hole
 
2397
   recentfiles[0] = strdupz(file);                                         //  current file >> first in list
 
2398
   return;
 
2399
}
 
2400
 
 
2401
 
 
2402
/**************************************************************************/
 
2403
 
 
2404
//  Open a file and initialize PXM bitmap.
 
2405
//  If flock and menu is locked, do nothing and return.
 
2406
//  Otherwise open the file and display in main window.
 
2407
//  If Nth matches the file position in current file set, curr_file_posn 
 
2408
//  will be set to Nth, otherwise it is searched and set correctly
 
2409
//  (a file can be present multiple times in a collection set).
 
2410
//  If fkeepundo is ON, the edit undo image stack is not purged: current
 
2411
//  edits are kept after opening the new file (used by m_saveas()).
 
2412
//  Returns: 0 = OK, +N = error.
 
2413
 
 
2414
int f_open(cchar *filespec, int flock, int Nth, int fkeepundo)
 
2415
{
 
2416
   PXM         *temp8;
 
2417
   int         err, cc, yn, fposn, nfiles, nimages;
 
2418
   char        *pp, *file, *rawfile, titlebar[250];
 
2419
   char        fname[100], fdirk[100];
 
2420
   cchar       *discard = ZTX("Discard special gallery list? \n %s");
 
2421
   
 
2422
   int      Gtype = image_navi::gallerytype;
 
2423
   char     *Gname = image_navi::galleryname;
 
2424
   int      Grf = 0;
 
2425
   int      Gnew = 0;
 
2426
   
 
2427
   if (Gname) Grf = strEqu(Gname,recentfiles_file);                        //  flag, gallery is recent files list
 
2428
 
 
2429
   if (flock && Fmenulock) {
 
2430
      zmessageACK(mWin,ZTX("prior function still active"));                //  v.11.06
 
2431
      return 1;
 
2432
   }
 
2433
 
 
2434
   if (mod_keep()) return 2;                                               //  unsaved edits
 
2435
 
 
2436
   if (filespec) 
 
2437
      file = strdupz(filespec,0,"f_open");                                 //  use passed filespec
 
2438
   else {
 
2439
      if (Gtype == 2 && ! Grf) {                                           //  if named collection or search results,
 
2440
         yn = zmessageYN(mWin,discard,image_navi::galleryname);            //    warn user, gallery will be discarded
 
2441
         if (! yn) return 6;                                               //  do not discard                  v.11.09
 
2442
         Gnew = 1;                                                         //  flag, new gallery needed
 
2443
      }
 
2444
      file = zgetfile1(ZTX("Open Image File"),"open",curr_file);           //  dialog to get filespec
 
2445
      if (! file) return 3;                                                //  canceled
 
2446
   }
 
2447
 
 
2448
   if (image_file_type(file) != 2)                                         //  not a supported image file type
 
2449
   {
 
2450
      pp = strrchr(file,'.');
 
2451
      if (! pp || strlen(pp) != 4 || ! strcasestr(RAWfiles,pp)) {          //  check if a RAW file extension
 
2452
         zfree(file);                                                      //  v.11.09
 
2453
         return 3;                                                         //  no
 
2454
      }
 
2455
      
 
2456
      rawfile = file;                                                      //  v.11.09
 
2457
      file = strdupz(rawfile,5,"f_open");                                  //  filename.raw >> filename.tiff
 
2458
      pp = file + (pp - rawfile);
 
2459
      strcpy(pp,".tiff");
 
2460
      snprintf(command,ccc,"ufraw-batch --wb=camera --out-type=tiff "      //  convert RAW file to tiff-16     v.11.09
 
2461
                           "--out-depth=16 --overwrite "
 
2462
                           "--output=\"%s\" \"%s\" ",file,rawfile);
 
2463
      err = system(command);
 
2464
      if (err) {
 
2465
         zmessageACK(mWin,wstrerror(err));                                 //  failed, clean up
 
2466
         zfree(file);
 
2467
         zfree(rawfile);
 
2468
         return 3;
 
2469
      }
 
2470
      zfree(rawfile);                                                      //  tiff file will be opened
 
2471
   }
 
2472
 
 
2473
   temp8 = f_load(file,8);                                                 //  load image as PXM-8 pixmap
 
2474
   if (! temp8) {
 
2475
      zfree(file);                                                         //  bad image
 
2476
      return 5;
 
2477
   }
 
2478
                                                                           //  menulock() removed     v.11.11
 
2479
 
 
2480
   free_resources(fkeepundo);                                              //  free resources for old image file
 
2481
 
 
2482
   if (curr_file) zfree(curr_file);                                        //  current image filespec
 
2483
   curr_file = strdupz(file,8,"curr_file");
 
2484
   zfree(file);
 
2485
 
 
2486
   if (curr_dirk) zfree(curr_dirk);                                        //  set current directory
 
2487
   curr_dirk = strdupz(curr_file,0,"curr_dirk");                           //    for new current file
 
2488
   pp = strrchr(curr_dirk,'/');
 
2489
   *pp = 0;
 
2490
   err = chdir(curr_dirk);
 
2491
   
 
2492
   Fpxm8 = temp8;                                                          //  pixmap for current image
 
2493
   Fww = Fpxm8->ww;
 
2494
   Fhh = Fpxm8->hh;
 
2495
   
 
2496
   strcpy(curr_file_type,f_load_type);                                     //  set curr_file_xxx from f_load_xxx
 
2497
   curr_file_bpc = f_load_bpc;
 
2498
   curr_file_size = f_load_size;
 
2499
 
 
2500
   fposn = image_gallery_position(curr_file,Nth);                          //  file position in gallery list
 
2501
   if (fposn < 0 || Gnew) {                                                //  not there or break current gallery    v.11.09
 
2502
      image_gallery(curr_file,"init");                                     //  generate new gallery list
 
2503
      fposn = image_gallery_position(curr_file,0);                         //  position and count in gallery list
 
2504
      image_gallery(0,"paint2",fposn);                                     //  refresh gallery window if active      v.11.07
 
2505
   }
 
2506
 
 
2507
   nfiles = image_navi::nfiles;                                            //  total gallery files (incl. directories)
 
2508
   nimages = image_navi::nimages;                                          //  total image files               v.11.05
 
2509
 
 
2510
   curr_file_posn = fposn;                                                 //  keep track of file position
 
2511
   curr_file_count = image_navi::nimages;                                  //  and image gallery count
 
2512
 
 
2513
   add_recent_file(curr_file);                                             //  first in recent files list   /////////
 
2514
 
 
2515
   Fzoom = 0;                                                              //  zoom level = fit window
 
2516
   zoomx = zoomy = 0;                                                      //  no zoom center
 
2517
 
 
2518
   pp = (char *) strrchr(curr_file,'/');
 
2519
   strncpy0(fname,pp+1,99);                                                //  file name   //////
 
2520
   cc = pp - curr_file;
 
2521
   if (cc < 99) strncpy0(fdirk,curr_file,cc+2);                            //  get dirk/path/ if short enough
 
2522
   else {
 
2523
      strncpy(fdirk,curr_file,96);                                         //  or use /dirk/path...
 
2524
      strcpy(fdirk+95,"...");
 
2525
   }
 
2526
 
 
2527
   fposn = fposn + 1 - nfiles + nimages;                                   //  position among images, 1-based
 
2528
   snprintf(titlebar,250,"%s   %d/%d  %s  %s",                             //  window title bar
 
2529
                  fversion,fposn,curr_file_count,fname,fdirk);
 
2530
   gtk_window_set_title(MWIN,titlebar);
 
2531
 
 
2532
   mwpaint1();                                                             //  immediate paint              v.11.11
 
2533
   gtk_window_present(MWIN);                                               //  bring main window to front   v.11.04
 
2534
   zmainloop();             
 
2535
 
 
2536
   if (zdrename) m_rename(0,0);                                            //  update active rename dialog
 
2537
   if (zdexifview) info_view(0);                                           //    "  EXIF/IPTC view window        v.10.2
 
2538
   if (zdexifedit) m_info_edit(0,0);                                       //    "  EXIF/IPTC edit window        v.10.11
 
2539
   if (zdedittags) m_edit_tags(0,0);                                       //    "  edit tags dialog
 
2540
   if (zdeditcctext) m_edit_cctext(0,0);                                   //    "  edit comments dialog    v.10.10
 
2541
   
 
2542
   curr_image_time = get_seconds();                                        //  mark time of file load       v.11.07
 
2543
 
 
2544
   return 0;
 
2545
}
 
2546
 
 
2547
 
 
2548
/**************************************************************************/
 
2549
 
 
2550
//  open previous or next file in current gallery list
 
2551
 
 
2552
void m_prev(GtkWidget *, cchar *menu)
 
2553
{
 
2554
   int      err, Nth;
 
2555
 
 
2556
   if (menu) zfuncs::F1_help_topic = "open_image_file";
 
2557
 
 
2558
   if (Fmenulock) return;
 
2559
   if (mod_keep()) return;
 
2560
   if (! curr_file) return;
 
2561
 
 
2562
   for (Nth = curr_file_posn-1; Nth >= 0; Nth--)                           //  v.11.05
 
2563
   {
 
2564
      char *pp = image_gallery(0,"find",Nth);
 
2565
      if (! pp) continue;
 
2566
      err = f_open(pp,1,Nth);
 
2567
      zfree(pp);
 
2568
      if (! err) break;
 
2569
   }
 
2570
      
 
2571
   return;
 
2572
}
 
2573
 
 
2574
void m_next(GtkWidget *, cchar *menu)
 
2575
{
 
2576
   int      err, Nth;
 
2577
   int      nfiles = image_navi::nfiles;
 
2578
 
 
2579
   if (menu) zfuncs::F1_help_topic = "open_image_file";
 
2580
 
 
2581
   if (Fmenulock) return;
 
2582
   if (mod_keep()) return;
 
2583
   if (! curr_file) return;
 
2584
 
 
2585
   for (Nth = curr_file_posn+1; Nth < nfiles; Nth++)                       //  v.11.05
 
2586
   {
 
2587
      char *pp = image_gallery(0,"find",Nth);
 
2588
      if (! pp) continue;
 
2589
      err = f_open(pp,1,Nth);
 
2590
      zfree(pp);
 
2591
      if (! err) break;
 
2592
   }
 
2593
      
 
2594
   return;
 
2595
}
 
2596
 
 
2597
 
 
2598
/**************************************************************************/
 
2599
 
 
2600
//  save (modified) image to same file
 
2601
 
 
2602
void m_save(GtkWidget *, cchar *menu)
 
2603
{
 
2604
   int         Fwarn = 1, zstat, suppress;
 
2605
   char        *pp;
 
2606
   zdialog     *zd;
 
2607
   cchar       *warn_message = ZTX("Overwrite original file?");
 
2608
   cchar       *suppress_message = ZTX("Do not warn again");
 
2609
 
 
2610
   if (! curr_file) return;
 
2611
   if (is_syncbusy()) return;                                              //  v.11.11
 
2612
 
 
2613
   if (menu && strNeq(menu,"nowarn"))                                      //  don't change help topic      v.11.11
 
2614
      zfuncs::F1_help_topic = "save_file";
 
2615
 
 
2616
   if (Fwarnoverwrite) {                                                   //  warn if overwrite original   v.11.10
 
2617
      pp = strrchr(curr_file,'/');
 
2618
      if (! pp) return;
 
2619
      pp = strstr(pp,".v");                                                //  look for version notation .vNN 
 
2620
      if (pp && pp[2] >= '0' && pp[2] <= '9'
 
2621
          && pp[3] >= '0' && pp[3] <= '9') Fwarn = 0;                      //  found, file not original
 
2622
   
 
2623
      if (Fwarn && ! (menu && strEqu(menu,"nowarn"))) {                    //  no warn if KB rotate save    v.11.11
 
2624
         zd = zdialog_new(ZTX("Warning"),mWin,Bproceed,Bcancel,null);
 
2625
         zdialog_add_widget(zd,"label","lab1","dialog",warn_message,"space=5");
 
2626
         zdialog_add_widget(zd,"check","suppress","dialog",suppress_message,"space=5");
 
2627
         zdialog_run(zd);
 
2628
         zstat = zdialog_wait(zd);
 
2629
         zdialog_fetch(zd,"suppress",suppress);
 
2630
         zdialog_free(zd);
 
2631
         if (suppress) {
 
2632
            Fwarnoverwrite = 0;
 
2633
            save_params();
 
2634
         }
 
2635
         if (zstat != 1) return;
 
2636
      }
 
2637
   }
 
2638
   
 
2639
   strcpy(jpeg_quality,def_jpeg_quality);                                  //  default jpeg save quality
 
2640
 
 
2641
   if (strEqu(curr_file_type,"other"))                                     //  if gif, bmp, etc. use jpg    v.11.03
 
2642
      strcpy(curr_file_type,"jpg");
 
2643
 
 
2644
   f_save(curr_file,curr_file_type,curr_file_bpc);                         //  save file
 
2645
   
 
2646
   strcpy(curr_file_type,f_save_type);                                     //  update curr_file_xxx from f_save_xxx
 
2647
   curr_file_size = f_save_size;
 
2648
   
 
2649
   return;
 
2650
}
 
2651
 
 
2652
 
 
2653
/**************************************************************************/
 
2654
 
 
2655
//  save (modified) image to new version of same file
 
2656
//   - no confirmation of overwrite.
 
2657
 
 
2658
void m_savevers(GtkWidget *, cchar *)                                      //  new  v.11.07
 
2659
{   
 
2660
   char           *outfile, *pext, *pvers;
 
2661
   cchar          *delim;
 
2662
   int            ii, err, nvers;
 
2663
   struct stat    fstat;
 
2664
 
 
2665
   if (! curr_file) return;
 
2666
   if (is_syncbusy()) return;                                              //  v.11.11
 
2667
 
 
2668
   zfuncs::F1_help_topic = "save_file";
 
2669
 
 
2670
   strcpy(jpeg_quality,def_jpeg_quality);                                  //  default jpeg save quality
 
2671
 
 
2672
   outfile = strdupz(curr_file,12,"curr_file");                            //  output file name TBD
 
2673
 
 
2674
   pext = strrchr(outfile,'/');                                            //  find file .ext
 
2675
   if (pext) pext = strrchr(pext,'.');
 
2676
   if (pext && strlen(pext) > 5) pext = 0;
 
2677
   if (! pext) pext = outfile + strlen(outfile);                           //  unknown, none
 
2678
 
 
2679
   pvers = pext - 4;                                                       //  find curr. version: filename.vNN.ext
 
2680
   if (! strnEqu(pvers,".v",2)) nvers = 0;
 
2681
   else {
 
2682
      err = convSI(pvers+2,nvers,1,98,&delim);                             //  convert NN to number 1-98
 
2683
      if (err > 1) nvers = 0;                                              //  conversion error
 
2684
      if (delim != pext) nvers = 0;                                        //  check format is .vNN
 
2685
   }
 
2686
   if (nvers == 0) {                                                       //  no version in file name
 
2687
      pvers = pext;
 
2688
      pext += 4;
 
2689
      memmove(pext,pvers,6);                                               //  make space for .vNN before .ext
 
2690
      strncpy(pvers,".vNN",4);
 
2691
   }
 
2692
 
 
2693
   for (ii = 98; ii > nvers; ii--)                                         //  look for higher file versions
 
2694
   {
 
2695
      pvers[2] = ii/10 + '0';                                              //  build filename.vNN.ext
 
2696
      pvers[3] = ii - 10 * (ii/10) + '0';
 
2697
      err = stat(outfile,&fstat);
 
2698
      if (! err) break;
 
2699
   }
 
2700
 
 
2701
   ii++;                                                                   //  use next version 1-99
 
2702
   nvers = ii;
 
2703
   pvers[2] = ii/10 + '0';                                                 //  build filename.vNN.ext
 
2704
   pvers[3] = ii - 10 * (ii/10) + '0';
 
2705
 
 
2706
   f_save(outfile,curr_file_type,curr_file_bpc);                           //  save file (fails at 99 versions)
 
2707
 
 
2708
   load_fileinfo(outfile);                                                 //  update search index file
 
2709
   update_search_index(outfile);
 
2710
 
 
2711
   image_gallery(outfile,"init");                                          //  update gallery
 
2712
   image_gallery(outfile,"paint2",-1);                                     //  refresh gallery window if active
 
2713
 
 
2714
   f_open(outfile,1,0,1);                                                  //  new version = current file      v.11.11.1
 
2715
 
 
2716
   zfree(outfile);
 
2717
   return;
 
2718
}
 
2719
 
 
2720
 
 
2721
/**************************************************************************/
 
2722
 
 
2723
//  save (modified) image to new file
 
2724
//  confirm if overwrite existing file
 
2725
 
 
2726
GtkWidget   *saveas_fchooser;
 
2727
 
 
2728
void m_saveas(GtkWidget *, cchar *menu)
 
2729
{
 
2730
   void  saveas_radiobutt(void *, int button);
 
2731
   void  saveas_kbkey(void *, GdkEventKey *event);
 
2732
 
 
2733
   GtkWidget      *fdialog, *hbox;
 
2734
   GtkWidget      *tiff8, *tiff16, *jpeg, *png, *jqlab, *jqval;
 
2735
   GtkWidget      *makecurrent;
 
2736
   char           *outfile = 0, *outfile2 = 0, *pext;
 
2737
   int            ii, err, yn, bpc, status, mkcurr = 0;
 
2738
   struct stat    fstat;
 
2739
   cchar          *type;
 
2740
   cchar          *exts = ".jpg.JPG.jpeg.JPEG.tif.TIF.tiff.TIFF.png.PNG";
 
2741
 
 
2742
   if (! curr_file) return;
 
2743
   if (is_syncbusy()) return;                                              //  v.11.11
 
2744
 
 
2745
   if (menu) zfuncs::F1_help_topic = "save_file";
 
2746
 
 
2747
   fdialog = gtk_dialog_new_with_buttons(ZTX("Save File"),                 //  build file save dialog
 
2748
                           MWIN, GTK_DIALOG_MODAL,
 
2749
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 
 
2750
                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, null);
 
2751
   gtk_window_set_default_size(GTK_WINDOW(fdialog),600,500);
 
2752
 
 
2753
   saveas_fchooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_SAVE);
 
2754
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(fdialog)->vbox),saveas_fchooser);
 
2755
 
 
2756
   //  set_filename() should select the file but does nothing      GTK bug ?     v.10.9.1       /////
 
2757
   //  set_current_name() puts file name in input box, but does not select the file
 
2758
   gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(saveas_fchooser),curr_file);
 
2759
   char *fname = strrchr(curr_file,'/') + 1;
 
2760
   gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),fname);
 
2761
   
 
2762
   hbox = gtk_hbox_new(0,0);
 
2763
   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(fdialog)->vbox),hbox);
 
2764
   gtk_box_set_child_packing(GTK_BOX(GTK_DIALOG(fdialog)->vbox),hbox,0,0,10,GTK_PACK_END);
 
2765
 
 
2766
   tiff8 = gtk_radio_button_new_with_label(null,"tiff-8");
 
2767
   tiff16 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(tiff8),"tiff-16");
 
2768
   png = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(tiff8),"png");
 
2769
   jpeg = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(tiff8),"jpeg");
 
2770
   jqlab = gtk_label_new(ZTX("quality"));
 
2771
   jqval = gtk_entry_new();
 
2772
   makecurrent = gtk_check_button_new_with_label(ZTX("make current"));
 
2773
 
 
2774
   gtk_entry_set_width_chars(GTK_ENTRY(jqval),2);
 
2775
   gtk_box_pack_start(GTK_BOX(hbox),tiff8,0,0,5);                          //  (o) tiff8  (o) tiff16  (o) png
 
2776
   gtk_box_pack_start(GTK_BOX(hbox),tiff16,0,0,5);
 
2777
   gtk_box_pack_start(GTK_BOX(hbox),png,0,0,5);
 
2778
   gtk_box_pack_start(GTK_BOX(hbox),jpeg,0,0,5);                           //  (o) jpeg  jpeg quality [__]
 
2779
   gtk_box_pack_start(GTK_BOX(hbox),jqlab,0,0,0);
 
2780
   gtk_box_pack_start(GTK_BOX(hbox),jqval,0,0,3);
 
2781
   gtk_box_pack_end(GTK_BOX(hbox),makecurrent,0,0,3);                      //  [x] make current
 
2782
   
 
2783
   G_SIGNAL(tiff8,"pressed",saveas_radiobutt,0)                            //  connect file type radio buttons
 
2784
   G_SIGNAL(tiff16,"pressed",saveas_radiobutt,1)                           //    to handler function
 
2785
   G_SIGNAL(png,"pressed",saveas_radiobutt,2)
 
2786
   G_SIGNAL(jpeg,"pressed",saveas_radiobutt,3)
 
2787
   G_SIGNAL(fdialog,"key-release-event",saveas_kbkey,0)
 
2788
 
 
2789
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(jpeg),1);                //  set default file type = jpeg
 
2790
   gtk_entry_set_text(GTK_ENTRY(jqval),def_jpeg_quality);                  //  default jpeg save quality
 
2791
   
 
2792
   if (strEqu(curr_file_type,"png"))                                       //  default matches file type
 
2793
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(png),1);              //    if png or tiff
 
2794
   if (strEqu(curr_file_type,"tiff"))
 
2795
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tiff8),1);
 
2796
   if (curr_file_bpc == 16)
 
2797
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tiff16),1);
 
2798
 
 
2799
dialog_run:
 
2800
 
 
2801
   gtk_widget_show_all(fdialog);                                           //  run dialog
 
2802
 
 
2803
   status = gtk_dialog_run(GTK_DIALOG(fdialog));
 
2804
   if (status != GTK_RESPONSE_ACCEPT) {                                    //  user cancelled
 
2805
      gtk_widget_destroy(fdialog);
 
2806
      return;
 
2807
   }
 
2808
 
 
2809
   outfile2 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveas_fchooser));
 
2810
   if (! outfile2) goto dialog_run;
 
2811
   outfile = strdupz(outfile2,12,"curr_file");                             //  add space for possible .vNN and .ext
 
2812
   g_free(outfile2);
 
2813
 
 
2814
   type = "jpg";                                                           //  default output type
 
2815
   bpc = 8;
 
2816
 
 
2817
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(png)))
 
2818
      type = "png";
 
2819
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tiff8)))
 
2820
      type = "tif";
 
2821
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tiff16))) {
 
2822
      type = "tif";
 
2823
      bpc = 16;
 
2824
   }
 
2825
 
 
2826
   if (strEqu(type,"jpg")) {                                               //  set jpeg save quality
 
2827
      ii = atoi(gtk_entry_get_text(GTK_ENTRY(jqval)));
 
2828
      if (ii < 1 || ii > 100) {
 
2829
         zmessageACK(mWin,ZTX("jpeg quality must be 1-100"));
 
2830
         goto dialog_run;
 
2831
      }
 
2832
      sprintf(jpeg_quality,"%d",ii);
 
2833
   }
 
2834
   
 
2835
   pext = strrchr(outfile,'/');                                            //  locate file .ext
 
2836
   if (pext) pext = strrchr(pext,'.');
 
2837
   if (pext && ! strstr(exts,pext)) pext = 0;                              //  keep .ext and append new    v.10.12.1
 
2838
   if (! pext) {
 
2839
      pext = outfile + strlen(outfile);                                    //  missing, add one
 
2840
      strcpy(pext,".");
 
2841
      strcpy(pext+1,type);
 
2842
   }
 
2843
 
 
2844
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(makecurrent)))       //  make saved file the current file?
 
2845
      mkcurr = 1;
 
2846
   
 
2847
   gtk_widget_destroy(fdialog);                                            //  kill dialog    /// gtk bug, can crash
 
2848
 
 
2849
   err = stat(outfile,&fstat);                                             //  check if file exists
 
2850
   if (! err) {
 
2851
      yn = zmessageYN(mWin,ZTX("Overwrite file? \n %s"),outfile);          //  confirm overwrite
 
2852
      if (! yn) {
 
2853
         zfree(outfile);
 
2854
         return;
 
2855
      }
 
2856
   }
 
2857
 
 
2858
   f_save(outfile,type,bpc);                                               //  save the file
 
2859
   
 
2860
   load_fileinfo(outfile);                                                 //  update search index file        v.11.07
 
2861
   update_search_index(outfile);
 
2862
 
 
2863
   if (mkcurr)                                                             //  use the saved file as new current file
 
2864
      f_open(outfile,1,0,1);                                               //    and retain edit history       v.11.07
 
2865
   else if (samedirk(outfile,curr_file)) {                                 //  if same directory, update gallery
 
2866
      image_gallery(outfile,"init");                                       //  add new file to gallery
 
2867
      image_gallery(0,"paint2",curr_file_posn);                            //  refresh if active, keep position
 
2868
   }
 
2869
 
 
2870
   zfree(outfile);
 
2871
   return;
 
2872
}
 
2873
 
 
2874
 
 
2875
//  set dialog file type from user selection of file type radio button
 
2876
 
 
2877
void saveas_radiobutt(void *, int button)                                  //  v.9.4
 
2878
{
 
2879
   cchar    *filetypes[4] = { ".tif", ".tif", ".png", ".jpg" };            //  v.10.9
 
2880
   char     *filespec;
 
2881
   char     *filename, *pp;
 
2882
 
 
2883
   filespec = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveas_fchooser));
 
2884
   if (! filespec) return;
 
2885
   filename = strrchr(filespec,'/');
 
2886
   if (! filename) return;
 
2887
   filename = strdupz(filename+1,6,"saveas");
 
2888
   pp = strrchr(filename,'.');
 
2889
   if (! pp || strlen(pp) > 5) pp = filename + strlen(filename);           //  bugfix        v.10.9.1
 
2890
   strcpy(pp,filetypes[button]);
 
2891
   gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),filename);
 
2892
   gtk_widget_show_all(saveas_fchooser);
 
2893
   zfree(filename);
 
2894
   g_free(filespec);                                                       //  bugfix, leak    v.10.9.1
 
2895
   return;
 
2896
}
 
2897
 
 
2898
 
 
2899
//  response function for KB key release
 
2900
 
 
2901
void saveas_kbkey(void *, GdkEventKey *event)                              //  v.10.5
 
2902
{
 
2903
   if (event->keyval == GDK_F1) 
 
2904
      showz_userguide(zfuncs::F1_help_topic);
 
2905
   return;
 
2906
}
 
2907
 
 
2908
 
 
2909
//  set new image zoom level or magnification
 
2910
 
 
2911
void m_zoom(GtkWidget *, cchar *menu)
 
2912
{
 
2913
   int      ii, iww, ihh, Dww, Dhh;
 
2914
   char     zoom;
 
2915
   double   scalew, scaleh, fitscale;
 
2916
   double   scales[11] = { 0.125, 0.176, 0.25, 0.354, 0.5, 0.71, 1.0, 1.41, 2.0, 2.83, 4.0 };
 
2917
   
 
2918
   if (strnEqu(menu,"Zoom",4)) zoom = menu[4];                             //  get + or -
 
2919
   else  zoom = *menu;
 
2920
   
 
2921
   Dww = drWin->allocation.width;                                          //  drawing window size
 
2922
   Dhh = drWin->allocation.height;
 
2923
   
 
2924
   if (E3pxm16) {                                                          //  bugfix
 
2925
      iww = E3ww;
 
2926
      ihh = E3hh;
 
2927
   }
 
2928
   else  {
 
2929
      iww = Fww;
 
2930
      ihh = Fhh;
 
2931
   }
 
2932
 
 
2933
   if (iww > Dww || ihh > Dhh) {                                           //  get window fit scale
 
2934
      scalew = 1.0 * Dww / iww;
 
2935
      scaleh = 1.0 * Dhh / ihh;
 
2936
      if (scalew < scaleh) fitscale = scalew;
 
2937
      else fitscale = scaleh;
 
2938
   }
 
2939
   else fitscale = 1.0;                                                    //  if image < window use 100%
 
2940
   
 
2941
   if (zoom == '+') {                                                      //  zoom bigger
 
2942
      if (! Fzoom) Fzoom = fitscale / 1.2;
 
2943
      Fzoom = Fzoom * sqrt(2.0);                                           //  new scale: 41% bigger
 
2944
      for (ii = 0; ii < 11; ii++)
 
2945
         if (Fzoom < 1.01 * scales[ii]) break;                             //  next higher scale in table
 
2946
      if (ii == 11) ii = 10;
 
2947
      Fzoom = scales[ii];
 
2948
      if (Fzoom < fitscale) Fzoom = 0;                                     //  image < window
 
2949
   }
 
2950
 
 
2951
   if (zoom == '-') Fzoom = 0;                                             //  zoom to fit window
 
2952
 
 
2953
   if (zoom == 'Z') {
 
2954
      if (Fzoom != 0) Fzoom = 0;                                           //  toggle 100% and fit window
 
2955
      else  Fzoom = 1;
 
2956
   }
 
2957
   
 
2958
   if (! Fzoom) zoomx = zoomy = 0;                                         //  no req. zoom center
 
2959
   
 
2960
   mwpaint2();                                                             //  refresh window
 
2961
   curr_image_time = get_seconds();                                        //  mark time of image change    v.11.07
 
2962
   return;
 
2963
}
 
2964
 
 
2965
 
 
2966
/**************************************************************************/
 
2967
 
 
2968
//  create a new blank image with desired background color
 
2969
 
 
2970
void m_create(GtkWidget *, cchar *)                                        //  v.11.01
 
2971
{
 
2972
   int   create_dialog_event(zdialog *zd, cchar *event);
 
2973
 
 
2974
   zdialog     *zd;
 
2975
   int         zstat;
 
2976
   char        *prev_file = 0;
 
2977
 
 
2978
   zfuncs::F1_help_topic = "create";
 
2979
   
 
2980
   if (is_syncbusy()) return;                                              //  v.11.11
 
2981
   if (mod_keep()) return;                                                 //  unsaved edits
 
2982
   if (! menulock(1)) return;                                              //  lock menus
 
2983
 
 
2984
   if (curr_file) prev_file = strdupz(curr_file);
 
2985
 
 
2986
//    file name [___________________________] .jpg                         //  v.11.05
 
2987
//    width [____]  height [____] (pixels)
 
2988
//    color [____]
 
2989
   
 
2990
   zd = zdialog_new(ZTX("Create Blank Image"),mWin,Bdone,Bcancel,null);
 
2991
   zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5");
 
2992
   zdialog_add_widget(zd,"label","labf","hbf",ZTX("file name"),"space=3");
 
2993
   zdialog_add_widget(zd,"entry","file","hbf","no-name","space=3|expand");
 
2994
   zdialog_add_widget(zd,"label","ftype","hbf",".jpg  ");
 
2995
   zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=5");
 
2996
   zdialog_add_widget(zd,"label","space","hbz",0,"space=3");
 
2997
   zdialog_add_widget(zd,"label","labw","hbz",ZTX("width"));
 
2998
   zdialog_add_widget(zd,"spin","width","hbz","100|9999|1|1600");
 
2999
   zdialog_add_widget(zd,"label","space","hbz",0,"space=5");
 
3000
   zdialog_add_widget(zd,"label","labh","hbz",ZTX("height"));
 
3001
   zdialog_add_widget(zd,"spin","height","hbz","100|9999|1|1000");
 
3002
   zdialog_add_widget(zd,"label","space","hbz",0,"space=3");
 
3003
   zdialog_add_widget(zd,"label","labp","hbz","(pixels) ");
 
3004
   zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=5");
 
3005
   zdialog_add_widget(zd,"label","space","hbc",0,"space=3");
 
3006
   zdialog_add_widget(zd,"label","labc","hbc",ZTX("color"));
 
3007
   zdialog_add_widget(zd,"colorbutt","color","hbc","200|200|200");
 
3008
   
 
3009
   create_dialog_event(zd,"init");
 
3010
 
 
3011
   zdialog_help(zd,"create");                                              //  zdialog help topic     v.11.08
 
3012
   zdialog_run(zd,create_dialog_event);
 
3013
   zstat = zdialog_wait(zd);
 
3014
   zdialog_free(zd);
 
3015
 
 
3016
   if (zstat != 1) {                                                       //  cancel
 
3017
      if (prev_file) {
 
3018
         f_open(prev_file,0);                                              //  back to previous file
 
3019
         zfree(prev_file);
 
3020
         mwpaint2();
 
3021
      }
 
3022
   }
 
3023
 
 
3024
   menulock(0);
 
3025
   return;
 
3026
}
 
3027
 
 
3028
 
 
3029
//  dialog event and completion function
 
3030
 
 
3031
int create_dialog_event(zdialog *zd, cchar *event)                         //  overhauled    v.11.05
 
3032
{
 
3033
   char           color[20], fname[100], *filespec;
 
3034
   cchar          *pp;
 
3035
   int            fncc, ww, hh, err;
 
3036
   static int     red, green, blue;
 
3037
   uint8          *pixel;
 
3038
   struct stat    statb;
 
3039
   
 
3040
   if (zd->zstat != 1) return 0;                                           //  [done]
 
3041
   
 
3042
   zdialog_fetch(zd,"file",fname,100);                                     //  get new file name
 
3043
   strTrim2(fname);
 
3044
   if (*fname <= ' ') strcpy(fname,"no-name");
 
3045
   fncc = strlen(fname);
 
3046
   
 
3047
   filespec = strdupz(curr_dirk,fncc+8,"create");                          //  make full filespec
 
3048
   strcat(filespec,"/");
 
3049
   strcat(filespec,fname);
 
3050
   strcat(filespec,".jpg");
 
3051
   
 
3052
   err = stat(filespec,&statb);                                            //  make sure it does not exist
 
3053
   if (! err) {
 
3054
      zmessageACK(mWin,"file already exists");
 
3055
      zfree(filespec);
 
3056
      zd->zstat = 0;                                                       //  keep dialog alive
 
3057
      return 0;
 
3058
   }
 
3059
   
 
3060
   if (curr_file) zfree(curr_file);                                        //  stop copy of metadata from old file
 
3061
   curr_file = 0;                                                          //  bugfix     v.11.08
 
3062
 
 
3063
   zdialog_fetch(zd,"width",ww);                                           //  get image dimensions
 
3064
   zdialog_fetch(zd,"height",hh);
 
3065
 
 
3066
   zdialog_fetch(zd,"color",color,19);                                     //  get image color
 
3067
   pp = strField(color,"|",1);
 
3068
   if (pp) red = atoi(pp);
 
3069
   pp = strField(color,"|",2);
 
3070
   if (pp) green = atoi(pp);
 
3071
   pp = strField(color,"|",3);
 
3072
   if (pp) blue = atoi(pp);
 
3073
   
 
3074
   mutex_lock(&Fpixmap_lock);                                              //  lock pixmaps
 
3075
 
 
3076
   PXM_free(Fpxm8);                                                        //  create new PXM image
 
3077
   Fpxm8 = PXM_make(ww,hh,8);
 
3078
   Fww = Fpxm8->ww;
 
3079
   Fhh = Fpxm8->hh;
 
3080
   pixel = (uint8 *) Fpxm8->bmp;
 
3081
 
 
3082
   for (int ii = 0; ii < ww * hh * 3; ii += 3) {
 
3083
      pixel[ii] = red;
 
3084
      pixel[ii+1] = green;
 
3085
      pixel[ii+2] = blue;
 
3086
   }
 
3087
 
 
3088
   mutex_unlock(&Fpixmap_lock);
 
3089
 
 
3090
   strcpy(jpeg_quality,def_jpeg_quality);
 
3091
 
 
3092
   err = f_save(filespec,"jpg",8);                                         //  save to disk
 
3093
   if (err) {
 
3094
      zmessageACK(mWin,"cannot save file");
 
3095
      zfree(filespec);
 
3096
      return 0;
 
3097
   }
 
3098
 
 
3099
   f_open(filespec,0);                                                     //  make it the current file
 
3100
   zfree(filespec);
 
3101
 
 
3102
   return 0;
 
3103
}
 
3104
 
 
3105
 
 
3106
/**************************************************************************/
 
3107
 
 
3108
//  Delete image file - move curr_file to trash.
 
3109
//  Use new Linux standard trash function.                                 //  v.10.8
 
3110
//  If not available, revert to Desktop folder for fotoxx trash.
 
3111
 
 
3112
void m_trash(GtkWidget *, cchar *)
 
3113
{
 
3114
   int            err, yn;
 
3115
   char           *pp, trashdir[200];
 
3116
   struct stat    trstat;
 
3117
   static int     gstat, trashworks = 1;                                   //  assume it works until otherwise
 
3118
   GError         *gerror = 0;
 
3119
   GFile          *gfile = 0;
 
3120
   cchar          *gerrmess = ZTX("Linux standard trash is not supported. \n"
 
3121
                                  "Desktop trash folder will be created.");
 
3122
   
 
3123
   zfuncs::F1_help_topic = "trash";                                        //  v.10.8
 
3124
 
 
3125
   if (! curr_file) return;                                                //  nothing to trash
 
3126
 
 
3127
   if (is_syncbusy()) return;                                              //  v.11.11
 
3128
   if (! menulock(1)) return;                                              //  check lock but leave unlocked
 
3129
   menulock(0);
 
3130
 
 
3131
   err = stat(curr_file,&trstat);                                          //  get file status
 
3132
   if (err) {
 
3133
      zmessLogACK(mWin,strerror(errno));
 
3134
      return;
 
3135
   }
 
3136
 
 
3137
   if (! (trstat.st_mode & S_IWUSR)) {                                     //  check permission
 
3138
      yn = zmessageYN(mWin,ZTX("Move read-only file to trash?"));
 
3139
      if (! yn) return;
 
3140
      trstat.st_mode |= S_IWUSR;
 
3141
      chmod(curr_file,trstat.st_mode);
 
3142
   }
 
3143
   
 
3144
   if (trashworks)                                                         //  try Linux standard trash
 
3145
   {
 
3146
      gfile = g_file_new_for_path(curr_file);
 
3147
      gstat = g_file_trash(gfile,0,&gerror);
 
3148
      if (! gstat) {
 
3149
         printf("g_file_trash() error: %s \n",gerror->message);
 
3150
         zmessageACK(mWin,gerrmess);
 
3151
         trashworks = 0;                                                   //  did not work
 
3152
      }
 
3153
   }
 
3154
 
 
3155
   if (! trashworks)
 
3156
   {
 
3157
      snprintf(trashdir,199,"%s/%s",getenv("HOME"),ftrash);                //  use fotoxx trash filespec
 
3158
 
 
3159
      trstat.st_mode = 0;
 
3160
      err = stat(trashdir,&trstat);
 
3161
      if (! S_ISDIR(trstat.st_mode)) {
 
3162
         err = mkdir(trashdir,0750);                                       //  v.11.03
 
3163
         if (err) {
 
3164
            zmessLogACK(mWin,ZTX("Cannot create trash folder: %s"),wstrerror(err));
 
3165
            return;
 
3166
         }
 
3167
      }
 
3168
 
 
3169
      snprintf(command,ccc,"cp \"%s\" \"%s\" ",curr_file,trashdir);        //  copy image file to trash
 
3170
      err = system(command);
 
3171
      if (err) {
 
3172
         zmessLogACK(mWin,ZTX("error: %s"),wstrerror(err));
 
3173
         return;
 
3174
      }
 
3175
 
 
3176
      err = remove(curr_file);                                             //  remove original file      v.11.03
 
3177
      if (err) {
 
3178
         zmessLogACK(mWin,ZTX("error: %s"),wstrerror(err));
 
3179
         return;
 
3180
      }
 
3181
   }
 
3182
   
 
3183
   delete_search_index(curr_file);                                         //  delete in search index file
 
3184
   image_gallery(0,"delete",curr_file_posn);                               //  delete in gallery list
 
3185
   pp = image_gallery(0,"find",curr_file_posn);                            //  open next file (now current position)
 
3186
   if (pp) f_open(pp,1);                                                   //                                     v.11.05
 
3187
   if (pp) zfree(pp);
 
3188
   image_gallery(0,"paint2",curr_file_posn);                               //  refresh gallery window if active   v.11.07
 
3189
 
 
3190
   return;
 
3191
}
 
3192
 
 
3193
 
 
3194
/**************************************************************************/
 
3195
 
 
3196
//  rename menu function
 
3197
//  activate rename dialog, stuff data from current file
 
3198
//  dialog remains active when new file is opened
 
3199
 
 
3200
char     rename_old[100] = "";
 
3201
char     rename_new[100] = "";
 
3202
 
 
3203
void m_rename(GtkWidget *, cchar *menu)
 
3204
{
 
3205
   int rename_dialog_event(zdialog *zd, cchar *event);
 
3206
   
 
3207
   char     *pdir, *pfile, *pext;
 
3208
 
 
3209
   if (menu) zfuncs::F1_help_topic = "rename";
 
3210
 
 
3211
   if (! curr_file) return;
 
3212
   if (is_syncbusy()) return;                                              //  v.11.11
 
3213
 
 
3214
   if (! zdrename)                                                         //  restart dialog
 
3215
   {
 
3216
      zdrename = zdialog_new(ZTX("Rename Image File"),mWin,Bcancel,null);
 
3217
      zdialog_add_widget(zdrename,"hbox","hb1","dialog",0,"space=10");
 
3218
      zdialog_add_widget(zdrename,"vbox","vb1","hb1",0,"homog|space=5");
 
3219
      zdialog_add_widget(zdrename,"vbox","vb2","hb1",0,"homog|expand");
 
3220
 
 
3221
      zdialog_add_widget(zdrename,"button","Bold","vb1",ZTX("old name"));
 
3222
      zdialog_add_widget(zdrename,"button","Bnew","vb1",ZTX("rename to"));
 
3223
      zdialog_add_widget(zdrename,"button","Bprev","vb1",ZTX("previous"));
 
3224
 
 
3225
      zdialog_add_widget(zdrename,"hbox","hb21","vb2",0);                  //  [ old name ] [ oldname  ]
 
3226
      zdialog_add_widget(zdrename,"hbox","hb22","vb2",0);                  //  [ new name ] [ newname  ] [+1]
 
3227
      zdialog_add_widget(zdrename,"hbox","hb23","vb2",0);                  //  [ previous ] [ prevname ]
 
3228
 
 
3229
      zdialog_add_widget(zdrename,"label","Lold","hb21");
 
3230
      zdialog_add_widget(zdrename,"entry","Enew","hb22",0,"expand|scc=30");
 
3231
      zdialog_add_widget(zdrename,"button","B+1","hb22"," +1 ","space=5");
 
3232
      zdialog_add_widget(zdrename,"label","Lprev","hb23");
 
3233
 
 
3234
      zdialog_help(zdrename,"rename");                                     //  zdialog help topic     v.11.08
 
3235
      zdialog_run(zdrename,rename_dialog_event);                           //  run dialog
 
3236
   }
 
3237
 
 
3238
   parsefile(curr_file,&pdir,&pfile,&pext);
 
3239
   strncpy0(rename_old,pfile,99);
 
3240
   strncpy0(rename_new,pfile,99);
 
3241
   zdialog_stuff(zdrename,"Lold",rename_old);                              //  current file name
 
3242
   zdialog_stuff(zdrename,"Enew",rename_new);                              //  entered file name
 
3243
 
 
3244
   return;
 
3245
}
 
3246
 
 
3247
 
 
3248
//  dialog event and completion callback function
 
3249
 
 
3250
int rename_dialog_event(zdialog *zd, cchar *event)
 
3251
{
 
3252
   char           *pp, *pdir, *pfile, *pext, *pnew, *pold;
 
3253
   int            nseq, digits, ccp, ccn, ccx, err;
 
3254
   struct stat    statb;
 
3255
   
 
3256
   if (zd->zstat) {                                                        //  complete
 
3257
      zdialog_free(zdrename);                                              //  kill dialog
 
3258
      return 0;
 
3259
   }
 
3260
   
 
3261
   if (strEqu(event,"Bold"))                                               //  reset to current file name
 
3262
      zdialog_stuff(zd,"Enew",rename_old);
 
3263
 
 
3264
   if (strEqu(event,"Bprev")) {                                            //  previous name >> new name
 
3265
      zdialog_fetch(zd,"Lprev",rename_new,99);
 
3266
      zdialog_stuff(zd,"Enew",rename_new);
 
3267
   }
 
3268
 
 
3269
   if (strEqu(event,"B+1"))                                                //  increment sequence number
 
3270
   {
 
3271
      zdialog_fetch(zd,"Enew",rename_new,94);                              //  get entered filename
 
3272
      pp = rename_new + strlen(rename_new);
 
3273
      digits = 0;
 
3274
      while (pp[-1] >= '0' && pp[-1] <= '9') {
 
3275
         pp--;                                                             //  look for NNN in filenameNNN
 
3276
         digits++;
 
3277
      }
 
3278
      nseq = 1 + atoi(pp);                                                 //  NNN + 1
 
3279
      if (nseq > 9999) nseq = 0;
 
3280
      if (digits < 2) digits = 2;                                          //  keep digit count if enough
 
3281
      if (nseq > 99 && digits < 3) digits = 3;                             //  use leading zeros
 
3282
      if (nseq > 999 && digits < 4) digits = 4;
 
3283
      snprintf(pp,digits+1,"%0*d",digits,nseq);
 
3284
      zdialog_stuff(zd,"Enew",rename_new);
 
3285
   }
 
3286
 
 
3287
   if (strEqu(event,"Bnew"))                                               //  [rename to] button
 
3288
   {
 
3289
      if (is_syncbusy()) return 0;                                         //  v.11.11
 
3290
      if (! menulock(1)) return 0;                                         //  check lock but leave unlocked
 
3291
      menulock(0);
 
3292
 
 
3293
      parsefile(curr_file,&pdir,&pfile,&pext);                             //  existing /directories/file.ext
 
3294
 
 
3295
      zdialog_fetch(zd,"Enew",rename_new,94);                              //  new file name from user
 
3296
 
 
3297
      ccp = strlen(pdir);                                                  //  length of /directories/
 
3298
      ccn = strlen(rename_new);                                            //  length of file
 
3299
      if (pext) ccx = strlen(pext);                                        //  length of .ext
 
3300
      else ccx = 0;
 
3301
 
 
3302
      pnew = zmalloc(ccp + ccn + ccx + 1,"rename");                        //  put it all together
 
3303
      strncpy(pnew,curr_file,ccp);                                         //   /directories/file.ext
 
3304
      strcpy(pnew+ccp,rename_new);
 
3305
      if (ccx) strcpy(pnew+ccp+ccn,pext);
 
3306
      
 
3307
      err = stat(pnew,&statb);                                             //  check if new name exists
 
3308
      if (! err) {
 
3309
         zmessageACK(mWin,ZTX("The target file already exists"));
 
3310
         zfree(pnew);
 
3311
         return 0;
 
3312
      }
 
3313
      
 
3314
      snprintf(command,ccc,"cp -p \"%s\" \"%s\"",curr_file,pnew);          //  copy to new file   -p v.10.3
 
3315
      err = system(command);
 
3316
      if (err) {
 
3317
         zmessageACK(mWin,ZTX("Rename failed: \n %s"),wstrerror(err));
 
3318
         printf("command: %s \n",command);
 
3319
         zfree(pnew);
 
3320
         return 0;
 
3321
      }
 
3322
 
 
3323
      zdialog_stuff(zd,"Lprev",rename_new);                                //  set previous name in dialog
 
3324
 
 
3325
      load_fileinfo(pnew);                                                 //  update search index file
 
3326
      update_search_index(pnew);
 
3327
 
 
3328
      pold = strdupz(curr_file,0,"curr_file");                             //  save file name to be deleted
 
3329
      delete_search_index(pold);                                           //  delete in search index       v.9.7
 
3330
      err = remove(pold);                                                  //  delete file                  v.11.03
 
3331
 
 
3332
      err = f_open(pnew,1);                                                //  no automatic "next"          v.11.08
 
3333
      image_gallery(curr_file,"init");                                     //  update gallery               v.11.05
 
3334
      image_gallery(0,"paint2",curr_file_posn);                            //  refresh gallery window if active
 
3335
 
 
3336
      zfree(pnew);
 
3337
      zfree(pold);
 
3338
   }
 
3339
   
 
3340
   return 0;
 
3341
}
 
3342
 
 
3343
 
 
3344
/**************************************************************************/
 
3345
 
 
3346
//  menu function - batch rename files
 
3347
 
 
3348
char     **batchrename_filelist = 0;
 
3349
int      batchrename_filecount = 0;
 
3350
 
 
3351
void m_batchrename(GtkWidget *, cchar *)                                   //  new v.9.7
 
3352
{
 
3353
   int   batchrename_dialog_event(zdialog *zd, cchar *event);
 
3354
   
 
3355
   zdialog  *zd;
 
3356
 
 
3357
   if (zdrename) return;                                                   //  interactive rename is active
 
3358
 
 
3359
   if (is_syncbusy()) return;                                              //  v.11.11
 
3360
   if (mod_keep()) return;                                                 //  unsaved edits
 
3361
   if (! menulock(1)) return;                                              //  lock menus       v.10.8
 
3362
 
 
3363
   zfuncs::F1_help_topic = "batch_rename";                                 //  v.10.8
 
3364
 
 
3365
   zd = zdialog_new(ZTX("Batch Rename"),mWin,Bproceed,Bcancel,null);
 
3366
   zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5");
 
3367
   zdialog_add_widget(zd,"button","files","hb1",Bselectfiles,"space=10");
 
3368
   zdialog_add_widget(zd,"label","labcount","hb1",ZTX("%d files selected"),"space=10");
 
3369
   zdialog_add_widget(zd,"hbox","hb2","dialog","space=5");
 
3370
   zdialog_add_widget(zd,"label","lab2","hb2",ZTX("new base name"),"space=10");
 
3371
   zdialog_add_widget(zd,"entry","basename","hb2");
 
3372
   zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5");
 
3373
   zdialog_add_widget(zd,"label","lab31","hb3",ZTX("starting sequence"),"space=10");
 
3374
   zdialog_add_widget(zd,"entry","sequence","hb3","100","scc=5");
 
3375
   zdialog_add_widget(zd,"label","lab32","hb3",ZTX("increment"),"space=10");
 
3376
   zdialog_add_widget(zd,"entry","increment","hb3","01","scc=3");
 
3377
 
 
3378
   batchrename_filelist = 0;
 
3379
   batchrename_filecount = 0;
 
3380
   
 
3381
   zdialog_help(zd,"batch_rename");                                        //  zdialog help topic     v.11.08
 
3382
   zdialog_run(zd,batchrename_dialog_event);                               //  run dialog
 
3383
   zdialog_wait(zd);                                                       //  wait for completion
 
3384
   
 
3385
   zdialog_free(zd);
 
3386
   menulock(0);
 
3387
   return;
 
3388
}
 
3389
 
 
3390
 
 
3391
//  dialog event and completion callback function
 
3392
 
 
3393
int batchrename_dialog_event(zdialog *zd, cchar *event)
 
3394
{
 
3395
   char           **flist = batchrename_filelist, countmess[50];
 
3396
   cchar          *selectmess = ZTX("select files to rename");
 
3397
   int            ii, err, cc, ccp, ccn, ccx;
 
3398
   int            sequence, increment, adder;
 
3399
   char           basename[100], filename[120], *oldfile, *newfile;
 
3400
   char           *pdir, *pfile, *pext;
 
3401
   cchar          *errmess = ZTX("base name / sequence / increment not reasonable");
 
3402
   struct stat    statb;
 
3403
   
 
3404
   if (strEqu(event,"files"))                                              //  select images to rename
 
3405
   {
 
3406
      if (flist) {                                                         //  free prior list
 
3407
         for (ii = 0; flist[ii]; ii++) 
 
3408
            zfree(flist[ii]);
 
3409
         zfree(flist);
 
3410
      }
 
3411
 
 
3412
      flist = zgetfileN(selectmess,"openN",curr_file);                     //  get file list from user
 
3413
      batchrename_filelist = flist;
 
3414
 
 
3415
      if (flist)                                                           //  count files in list
 
3416
         for (ii = 0; flist[ii]; ii++);
 
3417
      else ii = 0;
 
3418
      batchrename_filecount = ii;
 
3419
 
 
3420
      snprintf(countmess,50,ZTX("%d files selected"),batchrename_filecount);
 
3421
      zdialog_stuff(zd,"labcount",countmess);
 
3422
   }
 
3423
   
 
3424
   if (! zd->zstat) return 0;                                              //  dialog active
 
3425
 
 
3426
   if (zd->zstat != 1) goto cleanup;                                       //  dialog canceled
 
3427
   if (! batchrename_filecount) goto cleanup;                              //  no files selected
 
3428
 
 
3429
   zdialog_fetch(zd,"basename",basename,99);
 
3430
   zdialog_fetch(zd,"sequence",sequence);
 
3431
   zdialog_fetch(zd,"increment",increment);
 
3432
 
 
3433
   if (strlen(basename) < 1 || sequence < 1 || increment < 1) {
 
3434
      zd->zstat = 0;                                                       //  keep dialog alive      bugfix v.10.7
 
3435
      zmessageACK(mWin,errmess);
 
3436
      return 0;
 
3437
   }
 
3438
   
 
3439
   write_popup_text("open","Renaming files",500,200,mWin);                 //  status monitor popup window    v.10.3
 
3440
 
 
3441
   for (ii = 0; flist[ii]; ii++)
 
3442
   {
 
3443
      oldfile = flist[ii];
 
3444
      parsefile(oldfile,&pdir,&pfile,&pext);
 
3445
      ccp = strlen(pdir);
 
3446
      if (pext) ccx = strlen(pext);
 
3447
      else ccx = 0;
 
3448
 
 
3449
      adder = sequence + ii * increment;
 
3450
      snprintf(filename,119,"%s%d",basename,adder);                        //  removed "-" between        v.10.7
 
3451
      ccn = strlen(filename);
 
3452
 
 
3453
      newfile = zmalloc(ccp + ccn + ccx + 1,"rename");                     //  construct /path/filename.ext
 
3454
      strcpy(newfile,pdir);
 
3455
      strcpy(newfile+ccp,filename);
 
3456
      if (ccx) strcpy(newfile+ccp+ccn,pext);
 
3457
 
 
3458
      err = stat(newfile,&statb);
 
3459
      if (! err) {
 
3460
         snprintf(command,ccc,"%s %s",ZTX("new file already exists:"),newfile);
 
3461
         write_popup_text("write",command);
 
3462
         zfree(newfile);
 
3463
         break;
 
3464
      }
 
3465
 
 
3466
      cc = snprintf(command,ccc,"cp -p \"%s\" \"%s\"",oldfile,newfile);    //  copy to new file   -p v.10.3
 
3467
      if (cc >= maxfcc*2) {
 
3468
         snprintf(command,ccc,"%s %s",ZTX("filespec too long:"),oldfile);
 
3469
         write_popup_text("write",command);
 
3470
         zfree(newfile);
 
3471
         break;
 
3472
      }
 
3473
 
 
3474
      write_popup_text("write",command);                                   //  report progress
 
3475
      zmainloop();
 
3476
 
 
3477
      err = system(command);
 
3478
      if (err) {
 
3479
         snprintf(command,ccc,"%s %s",ZTX("Rename failed:"),wstrerror(err));
 
3480
         write_popup_text("write",command);
 
3481
         zfree(newfile);
 
3482
         break;
 
3483
      }
 
3484
 
 
3485
      load_fileinfo(newfile);                                              //  update search index file
 
3486
      update_search_index(newfile);
 
3487
      zfree(newfile);
 
3488
 
 
3489
      err = remove(oldfile);                                               //  delete old file           v.11.03
 
3490
      delete_search_index(oldfile);                                        //  remove from search index
 
3491
      zmainloop();
 
3492
   }
 
3493
 
 
3494
   write_popup_text("write","COMPLETED");
 
3495
   write_popup_text("close",0);
 
3496
 
 
3497
   image_gallery(curr_file,"init");                                        //  update gallery file list
 
3498
   image_gallery(0,"paint2",curr_file_posn);                               //  refresh gallery window if active
 
3499
 
 
3500
cleanup:
 
3501
 
 
3502
   if (batchrename_filecount) {
 
3503
      for (ii = 0; flist[ii]; ii++) 
 
3504
         zfree(flist[ii]);
 
3505
      zfree(flist);
 
3506
   }
 
3507
 
 
3508
   return 0;
 
3509
}
 
3510
 
 
3511
 
 
3512
/**************************************************************************/
 
3513
 
 
3514
//  print current image file
 
3515
 
 
3516
double print_size[2] = { 29.7, 21.0 };                                     //  default A4 landscape
 
3517
double print_margins[4] = { 0.5, 0.5, 0.5, 0.5 };                          //  top, bottom, left, right
 
3518
 
 
3519
void m_print(GtkWidget *, cchar *)                                         //  use GTK print   v.9.4
 
3520
{
 
3521
   PXM * print_addgrid(PXM *printimage);                                   //  v.11.01
 
3522
 
 
3523
   int      err;
 
3524
   char     *printfile;
 
3525
   PXM      *printimage, *pxmtemp;
 
3526
   
 
3527
   zfuncs::F1_help_topic = "print";
 
3528
   
 
3529
   if (! curr_file) return;                                                //  no image file
 
3530
   
 
3531
   printfile = strdupz(get_zuserdir(),20,"printfile");                     //  make temp print file:
 
3532
   strcat(printfile,"/printfile.tif");                                     //    ~/.fotoxx/printfile.tif    v.11.03
 
3533
   
 
3534
   if (Fpxm16) printimage = PXM_convbpc(Fpxm16);
 
3535
   else printimage = PXM_copy(Fpxm8);
 
3536
   
 
3537
   pxmtemp = print_addgrid(printimage);                                    //  add grid lines if wanted     v.11.01
 
3538
   if (pxmtemp) {
 
3539
      PXM_free(printimage);
 
3540
      printimage = pxmtemp;
 
3541
   }
 
3542
   
 
3543
   err = PXBwrite(printimage,printfile);
 
3544
   PXM_free(printimage);
 
3545
 
 
3546
   if (err) {
 
3547
      zfree(printfile);                                                    //  v.10.3
 
3548
      return;
 
3549
   }
 
3550
   
 
3551
   print_image_paper_setup();                                              //  new v.11.10
 
3552
   print_image_margins_setup();
 
3553
   print_image_file(printfile);
 
3554
   
 
3555
   zfree(printfile);
 
3556
   return;
 
3557
}
 
3558
 
 
3559
 
 
3560
//  add grid lines to print image if wanted
 
3561
 
 
3562
PXM * print_addgrid(PXM *printimage)                                       //  v.11.01
 
3563
{
 
3564
   PXM      *temp8;
 
3565
   uint8    *pixel;
 
3566
   int      px, py, ww, hh, row;
 
3567
   int      startx, starty, stepx, stepy;
 
3568
   
 
3569
   if (! Fgrid) return 0;                                                  //  grid lines off
 
3570
 
 
3571
   temp8 = f_load(curr_file,8);
 
3572
   if (! temp8) return 0;
 
3573
   
 
3574
   ww = temp8->ww;
 
3575
   hh = temp8->hh;
 
3576
   row = ww * 3;
 
3577
 
 
3578
   stepx = gridspace[0];                                                   //  space between grid lines
 
3579
   stepy = gridspace[1];
 
3580
   
 
3581
   stepx = stepx / Mscale;                                                 //  window scale to image scale
 
3582
   stepy = stepy / Mscale;
 
3583
   
 
3584
   if (gridcount[0]) stepx = ww / (1 + gridcount[0]);                      //  if line counts specified,
 
3585
   if (gridcount[1]) stepy = hh / ( 1 + gridcount[1]);                     //    set spacing accordingly
 
3586
   
 
3587
   startx = stepx * gridoffset[0] / 100;                                   //  variable offsets             v.11.11
 
3588
   if (startx < 0) startx += stepx;
 
3589
 
 
3590
   starty = stepy * gridoffset[1] / 100;
 
3591
   if (starty < 0) starty += stepy;
 
3592
 
 
3593
   if (gridon[0]) {
 
3594
      for (px = startx; px < ww-1; px += stepx)
 
3595
      for (py = 0; py < hh; py++)
 
3596
      {
 
3597
         pixel = PXMpix8(temp8,px,py);                                     //  adjoining white and black lines
 
3598
         pixel[0] = pixel[1] = pixel[2] = 255;
 
3599
         pixel[3] = pixel[4] = pixel[5] = 0;
 
3600
      }
 
3601
   }
 
3602
 
 
3603
   if (gridon[1]) {
 
3604
      for (py = starty; py < hh-1; py += stepy)
 
3605
      for (px = 0; px < ww; px++)
 
3606
      {
 
3607
         pixel = PXMpix8(temp8,px,py);
 
3608
         pixel[0] = pixel[1] = pixel[2] = 255;
 
3609
         pixel[row] = pixel[row+1] = pixel[row+2] = 0;
 
3610
      }
 
3611
   }
 
3612
 
 
3613
   return temp8;
 
3614
}
 
3615
 
 
3616
 
 
3617
/**************************************************************************/
 
3618
 
 
3619
//  normal quit menu function
 
3620
 
 
3621
void m_quit(GtkWidget *, cchar *)
 
3622
{
 
3623
   if (mod_keep()) return;                                                 //  unsaved edits
 
3624
   printf("quit \n");
 
3625
   Fshutdown++;                                                            //  bugfix  v.10.11
 
3626
 
 
3627
   for (int ii = 0; ii < 100; ii++)                                        //  wait up to a second if something running
 
3628
      if (Ffuncbusy) {                                                     //  v.11.01
 
3629
         zmainloop();
 
3630
         zsleep(0.01);
 
3631
      }
 
3632
   
 
3633
   if (Ffuncbusy) printf("busy function killed");                          //  v.11.05
 
3634
   
 
3635
   gtk_window_get_position(MWIN,&mwgeom[0],&mwgeom[1]);                    //  get last window position     v.11.07
 
3636
   gtk_window_get_size(MWIN,&mwgeom[2],&mwgeom[3]);                        //    and size for next session
 
3637
   save_params();                                                          //  save state for next session
 
3638
   zdialog_positions("save");                                              //  save dialog positions too    v.11.07
 
3639
   free_resources();                                                       //  delete temp files
 
3640
   if (KBzmalloclog) zmalloc_report();                                     //  report memory v.10.8
 
3641
   fflush(null);                                                           //  flush stdout, stderr      v.11.05
 
3642
   gtk_main_quit();                                                        //  gone forever
 
3643
   return;
 
3644
}
 
3645
 
 
3646
 
 
3647
/**************************************************************************
 
3648
   plugin menu functions
 
3649
**************************************************************************/
 
3650
 
 
3651
//  process plugin menu selection
 
3652
//  execute correspinding command using current image file
 
3653
 
 
3654
editfunc    EFplugin;
 
3655
 
 
3656
void  m_run_plugin(GtkWidget *, cchar *menu)                               //  v.11.03
 
3657
{
 
3658
   int         ii, jj, err;
 
3659
   char        *pp = 0, pluginfile[200];
 
3660
   PXM         *pxmtemp;
 
3661
   
 
3662
   zfuncs::F1_help_topic = "plugins";
 
3663
   
 
3664
   for (ii = 0; ii < Nplugins; ii++)                                       //  search plugins for menu name
 
3665
   {
 
3666
      pp = strstr(plugins[ii]," = ");
 
3667
      if (! pp) continue;
 
3668
      *pp = 0;
 
3669
      jj = strEqu(plugins[ii],menu);
 
3670
      *pp = ' ';
 
3671
      if (jj) break;
 
3672
   }
 
3673
 
 
3674
   if (ii == Nplugins) {
 
3675
      zmessageACK(mWin,"plugin menu not found %s",menu);
 
3676
      return;
 
3677
   }
 
3678
 
 
3679
   pp += 3;
 
3680
   if (strlen(pp) < 3) {
 
3681
      zmessageACK(mWin,"no plugin command");
 
3682
      return;
 
3683
   }
 
3684
 
 
3685
   strncpy0(command,pp,ccc);                                               //  corresp. command
 
3686
   strTrim2(command);
 
3687
 
 
3688
   EFplugin.funcname = menu;
 
3689
   if (! edit_setup(EFplugin)) return;                                     //  setup edit
 
3690
 
 
3691
   snprintf(pluginfile,199,"%s/plugfile.tif",get_zuserdir());              //  /home/user/.fotoxx/plugfile.tif
 
3692
   
 
3693
   TIFFwrite(E1pxm16,pluginfile);                                          //  E1 >> plugin_file
 
3694
 
 
3695
   strcat(command," ");                                                    //  construct: command /.../plugin_file &
 
3696
   strcat(command,pluginfile);
 
3697
   printf("plugin: %s \n",command);
 
3698
 
 
3699
   err = system(command);                                                  //  execute plugin command
 
3700
   if (err) {
 
3701
      zmessageACK(mWin,"plugin command error");
 
3702
      edit_cancel(EFplugin);
 
3703
      return;
 
3704
   }
 
3705
   
 
3706
   pxmtemp = TIFFread(pluginfile);                                         //  read command output file
 
3707
   if (! pxmtemp) {
 
3708
      zmessageACK(mWin,"plugin failed");
 
3709
      edit_cancel(EFplugin);
 
3710
      return;
 
3711
   }
 
3712
   
 
3713
   PXM_free(E3pxm16);                                                      //  plugin_file >> E3
 
3714
   if (pxmtemp->bpc == 16) E3pxm16 = pxmtemp;
 
3715
   else {
 
3716
      E3pxm16 = PXM_convbpc(pxmtemp);
 
3717
      PXM_free(pxmtemp);
 
3718
   }
 
3719
 
 
3720
   EFplugin.Fmod = 1;                                                      //  assume image was modified
 
3721
   edit_done(EFplugin);
 
3722
 
 
3723
   return;
 
3724
}   
 
3725
 
 
3726
 
 
3727
//  edit plugins menu
 
3728
 
 
3729
void  m_edit_plugins(GtkWidget *, cchar *)                                 //  v.11.03
 
3730
{
 
3731
   int   edit_plugins_event(zdialog *zd, cchar *event);
 
3732
 
 
3733
   int         ii;
 
3734
   char        *pp;
 
3735
   zdialog     *zd;
 
3736
   
 
3737
   zfuncs::F1_help_topic = "plugins";
 
3738
   
 
3739
   zd = zdialog_new("Edit Plugins",mWin,ZTX("Add"),ZTX("Remove"),Bdone,null);
 
3740
   zdialog_add_widget(zd,"hbox","hbm","dialog",0,"space=5");
 
3741
   zdialog_add_widget(zd,"label","labm","hbm",ZTX("menu name"),"space=5");
 
3742
   zdialog_add_widget(zd,"comboE","menu","hbm",0,"space=5");
 
3743
   zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=5");
 
3744
   zdialog_add_widget(zd,"label","labc","hbc","command","space=5");
 
3745
   zdialog_add_widget(zd,"entry","command","hbc",0,"space=5|expand");
 
3746
 
 
3747
   for (ii = 0; ii < Nplugins; ii++)                                       //  stuff combo box with available
 
3748
   {                                                                       //     plugin menu names
 
3749
      pp = strstr(plugins[ii]," = ");
 
3750
      if (! pp) continue;
 
3751
      *pp = 0;
 
3752
      zdialog_cb_app(zd,"menu",plugins[ii]);
 
3753
      *pp = ' ';
 
3754
   }
 
3755
   
 
3756
   zdialog_help(zd,"plugins");                                             //  zdialog help topic     v.11.08
 
3757
   zdialog_run(zd,edit_plugins_event);
 
3758
   return;
 
3759
}
 
3760
 
 
3761
 
 
3762
//  dialog event function
 
3763
 
 
3764
int  edit_plugins_event(zdialog *zd, cchar *event)
 
3765
{
 
3766
   int      ii, jj, cc, zstat;
 
3767
   char     menu[40], *pp = 0;
 
3768
   
 
3769
   zdialog_fetch(zd,"menu",menu,40);                                       //  selected menu
 
3770
   zdialog_fetch(zd,"command",command,ccc);
 
3771
   
 
3772
   if (strEqu(event,"menu")) 
 
3773
   {
 
3774
      for (ii = 0; ii < Nplugins; ii++)
 
3775
      {
 
3776
         pp = strstr(plugins[ii]," = ");                                   //  find corresp. plugin record
 
3777
         if (! pp) continue;
 
3778
         *pp = 0;
 
3779
         jj = strEqu(plugins[ii],menu);
 
3780
         *pp = ' ';
 
3781
         if (jj) break;
 
3782
      }
 
3783
      
 
3784
      if (ii == Nplugins) return 0;
 
3785
 
 
3786
      pp += 3;
 
3787
      if (strlen(pp) < 3) return 0;
 
3788
      strncpy0(command,pp,ccc);
 
3789
      zdialog_stuff(zd,"command",command);                                 //  stuff corresp. command in dialog
 
3790
      
 
3791
      return 0;
 
3792
   }
 
3793
   
 
3794
   zstat = zd->zstat;
 
3795
   if (! zstat) return 0;
 
3796
   
 
3797
   if (zstat == 1)                                                         //  add new plugin
 
3798
   {
 
3799
      if (strlen(menu) < 3 || strlen(command) < 3) return 0;
 
3800
      if (Nplugins == maxplugins) {
 
3801
         zmessageACK(mWin,"too many plugins");
 
3802
         return 0;
 
3803
      }
 
3804
      ii = Nplugins;                                                       //  add plugin record
 
3805
      cc = strlen(menu) + strlen(command) + 5;
 
3806
      plugins[ii] = zmalloc(cc,"plugins");                                 //  format: menu = command
 
3807
      strcpy(plugins[ii],menu);
 
3808
      strcat(plugins[ii]," = ");
 
3809
      strcat(plugins[ii],command);
 
3810
      Nplugins++;
 
3811
 
 
3812
      zmessageACK(mWin,ZTX("Restart Fotoxx to update plugin menu"));
 
3813
   }
 
3814
   
 
3815
   if (zstat == 2)                                                         //  remove current plugin
 
3816
   {
 
3817
      for (ii = 0; ii < Nplugins; ii++)
 
3818
      {
 
3819
         pp = strstr(plugins[ii]," = ");                                   //  find corresp. plugin record
 
3820
         if (! pp) continue;
 
3821
         *pp = 0;
 
3822
         jj = strEqu(plugins[ii],menu);
 
3823
         *pp = ' ';
 
3824
         if (jj) break;
 
3825
      }
 
3826
      
 
3827
      if (ii == Nplugins) return 0;
 
3828
      
 
3829
      Nplugins--;                                                          //  remove plugin record
 
3830
      for (jj = ii; jj < Nplugins; jj++)
 
3831
         plugins[jj] = plugins[jj+1];
 
3832
      
 
3833
      zmessageACK(mWin,ZTX("Restart Fotoxx to update plugin menu"));
 
3834
   }
 
3835
   
 
3836
   if (zstat == 3) {                                                       //  done
 
3837
      zdialog_free(zd);
 
3838
      return 0;
 
3839
   }
 
3840
   
 
3841
   return 0;
 
3842
}
 
3843
 
 
3844
 
 
3845
/**************************************************************************
 
3846
 
 
3847
   edit transaction and thread support functions
 
3848
 
 
3849
   edit transaction management
 
3850
      edit_setup()                     start new edit - copy E3 > E1
 
3851
      edit_cancel()                    cancel edit - E1 > E3, delete E1
 
3852
      edit_done()                      commit edit - add to undo stack
 
3853
      edit_undo()                      undo edit - E1 > E3
 
3854
      edit_redo()                      redo edit - run thread again
 
3855
      edit_fullsize()                  convert preview to full-size pixmaps
 
3856
 
 
3857
   main level thread management
 
3858
      start_thread(func,arg)           start thread running
 
3859
      signal_thread()                  signal thread that work is pending
 
3860
      wait_thread_idle()               wait for pending work complete
 
3861
      wrapup_thread(command)           wait for exit or command thread exit
 
3862
 
 
3863
   thread function
 
3864
      thread_idle_loop()               wait for pending work, exit if commanded
 
3865
      thread_exit()                    exit thread unconditionally
 
3866
 
 
3867
   thread_status (thread ownership
 
3868
      0     no thread is running
 
3869
      1     thread is running and idle (no work)
 
3870
      2     thread is working
 
3871
      0     thread has exited
 
3872
 
 
3873
   thread_command (main program ownership)
 
3874
      0     idle, no work pending
 
3875
      8     exit when pending work is done
 
3876
      9     exit now, unconditionally
 
3877
 
 
3878
   thread_pend       work requested counter
 
3879
   thread_done       work done counter
 
3880
   thread_hiwater    high water mark
 
3881
   edit_action       done/cancel/undo/redo in progress
 
3882
 
 
3883
***************************************************************************/
 
3884
 
 
3885
int      thread_command = 0, thread_status = 0;
 
3886
int      thread_pend = 0, thread_done = 0, thread_hiwater = 0;
 
3887
int      edit_action = 0;
 
3888
 
 
3889
 
 
3890
/**************************************************************************
 
3891
 
 
3892
  Setup for a new edit transaction
 
3893
  Create E1 (edit input) and E3 (edit output) pixmaps from
 
3894
  previous edit output (Fpxm16) or image file (new Fpxm16).
 
3895
 
 
3896
  Fprev      0     edit full-size image
 
3897
             1     edit preview image unless select area exists
 
3898
 
 
3899
  Farea      0     select_area is invalid and will be deleted (e.g. rotate)
 
3900
             1     select_area not used but remains valid (e.g. red-eye)
 
3901
             2     select_area can be used and remains valid (e.g. flatten)
 
3902
 
 
3903
***************************************************************************/
 
3904
 
 
3905
int edit_setup(editfunc &EF)                                               //  support parallel edits          v.11.07
 
3906
{
 
3907
   int      yn;
 
3908
   PXM      *pxmtemp = 0;
 
3909
 
 
3910
   if (! curr_file) return 0;                                              //  no image file
 
3911
   
 
3912
   if (is_syncbusy()) return 0;                                            //  must wait for file sync         v.11.11
 
3913
   
 
3914
   if (! Fpxm16) {                                                         //  no 16-bit image from prior edit
 
3915
      pxmtemp = f_load(curr_file,16);                                      //  check that file can be loaded
 
3916
      if (! pxmtemp) {
 
3917
         printf("f_load (16) failed: %s \n",curr_file);
 
3918
         return 0;
 
3919
      }
 
3920
   }
 
3921
 
 
3922
   if (CEF) {                                                              //  prior edit function active      v.11.07
 
3923
      if (&EF == CEF) return 0;                                            //  2nd instance of one function
 
3924
      if (EF.Fpara && CEF->Fpara)                                          //  check if parallel edits are OK
 
3925
         edit_suspend();                                                   //  suspend prior edit
 
3926
      else {
 
3927
         zmessageACK(mWin,ZTX("cannot parallel edit"));
 
3928
         return 0;
 
3929
      }
 
3930
   }
 
3931
   else {                                                                  //  if this is 1st active edit
 
3932
      if (! menulock(1)) return 0;                                         //    test menu lock is allowed
 
3933
      menulock(0);                                                         //      but don't lock yet          v.11.07
 
3934
   }
 
3935
 
 
3936
   if (! Fexiftool && ! Fexifwarned) {
 
3937
      zmessageACK(mWin,ZTX("exiftool is not installed \n"                  //  warn if starting to edit
 
3938
                           "edited images will lose EXIF data"));          //    and exiftool is missing
 
3939
      Fexifwarned = 1;
 
3940
   }
 
3941
 
 
3942
   if (Pundo > maxedits-2) {                                               //  undo capacity reached
 
3943
      zmessageACK(mWin,ZTX("Too many edits, please save image"));
 
3944
      return 0;
 
3945
   }
 
3946
 
 
3947
   if (EF.Farea == 0 && sa_stat) {                                         //  select area will be lost, warn user
 
3948
      yn = zmessageYN(mWin,ZTX("Select area cannot be kept.\n"
 
3949
                               "Continue?"));
 
3950
      if (! yn) return 0;
 
3951
      sa_unselect();                                                       //  unselect area
 
3952
      zdialog_free(zdsela);
 
3953
   }
 
3954
   
 
3955
   if (EF.Farea == 2 && sa_stat && ! Factivearea) {                        //  select area exists and can be used,
 
3956
      yn = zmessageYN(mWin,ZTX("Select area not active.\n"                 //    but not active, ask user
 
3957
                               "Continue?"));
 
3958
      if (! yn) return 0;
 
3959
   }
 
3960
 
 
3961
   if (! menulock(1)) return 0;                                            //  can now commit, lock menu       v.11.07
 
3962
 
 
3963
   Fpreview = 0;                                                           //  use preview image if supported
 
3964
   if (EF.Fprev && ! (EF.Farea == 2 && Factivearea)) {                     //    and select area will not be used
 
3965
      Fpreview = 1;                                                        //  use preview image (smaller)
 
3966
      sa_show(0);                                                          //  hide area if present
 
3967
      curr_image_time = get_seconds();                                     //  mark time of file load          v.11.07
 
3968
      if (Fzoom) {
 
3969
         Fzoom = 0;                                                        //  bugfix, mwpaint() req.          v.11.01
 
3970
         mwpaint1();
 
3971
      }
 
3972
   }
 
3973
 
 
3974
   mutex_lock(&Fpixmap_lock);                                              //  lock pixmaps
 
3975
 
 
3976
   if (! Fpxm16) Fpxm16 = pxmtemp;                                         //  create Fpxm16 if not already
 
3977
 
 
3978
   PXM_fixblue(Fpxm16);                                                    //  blue=0 >> blue=2 for vpixel()   v.11.07
 
3979
 
 
3980
   if (Fpreview)                                                           //  edit pixmaps are window-size
 
3981
      E1pxm16 = PXM_rescale(Fpxm16,dww,dhh);                               //  E1pxm16 = Fpxm16 scaled to window
 
3982
   else E1pxm16 = PXM_copy(Fpxm16);                                        //  edit pixmaps are full-size
 
3983
   E3pxm16 = PXM_copy(E1pxm16);                                            //  E1 >> E3
 
3984
   
 
3985
   E1ww = E3ww = E1pxm16->ww;                                              //  E1 and E3 pixmap dimensions
 
3986
   E1hh = E3hh = E1pxm16->hh;
 
3987
 
 
3988
   mutex_unlock(&Fpixmap_lock);
 
3989
   
 
3990
   if (Pundo == 0) save_undo("initial");                                   //  initial image >> undo stack
 
3991
 
 
3992
   CEF = &EF;                                                              //  set current edit function
 
3993
   CEF->Fmod = 0;                                                          //  image not modified yet
 
3994
 
 
3995
   thread_command = thread_status = 0;                                     //  no thread running
 
3996
   thread_pend = thread_done = thread_hiwater = 0;                         //  no work pending or done
 
3997
   if (EF.threadfunc) start_thread(EF.threadfunc,0);                       //  start edit thread
 
3998
   
 
3999
   Frefresh++;
 
4000
   mwpaint1();                                                             //  mwpaint1() not mwpaint2()
 
4001
   return 1;
 
4002
}
 
4003
 
 
4004
 
 
4005
/**************************************************************************/
 
4006
 
 
4007
//  suspend a parallel edit function so it can be resumed
 
4008
//  edit dialog and curve edit are left active
 
4009
//  logically the equivalent of edit_done()
 
4010
 
 
4011
void edit_suspend()
 
4012
{
 
4013
   wait_thread_idle();                                                     //  wait for thread to finish
 
4014
   
 
4015
   if (Fpreview && CEF->Fmod) {                                            //  preview image was edited
 
4016
      Fzoom = 0;
 
4017
      edit_fullsize();                                                     //  update full image
 
4018
   }
 
4019
 
 
4020
   wrapup_thread(8);                                                       //  tell thread to finish and exit
 
4021
 
 
4022
   mutex_lock(&Fpixmap_lock);                                              //  lock pixmaps
 
4023
 
 
4024
   if (CEF->Fmod) {                                                        //  image was modified
 
4025
      Fmodified++;                                                         //  overall mod counter
 
4026
      PXM_free(Fpxm16);
 
4027
      Fpxm16 = E3pxm16;                                                    //  E3 >> Fpxm16           v.11.07
 
4028
      E3pxm16 = 0;
 
4029
      PXM_free(Fpxm8);
 
4030
      Fpxm8 = PXM_convbpc(Fpxm16);                                         //  Fpxm16 >> Fpxm8
 
4031
      Fww = Fpxm8->ww;
 
4032
      Fhh = Fpxm8->hh;
 
4033
      Pundo++;                                                             //  save next undo state
 
4034
      Pumax = Pundo;
 
4035
      save_undo(CEF->funcname);
 
4036
   }
 
4037
 
 
4038
   PXM_free(E1pxm16);                                                      //  free edit pixmaps
 
4039
   PXM_free(E3pxm16);
 
4040
   PXM_free(ERpxm16);                                                      //  free redo copy
 
4041
   E1ww = E3ww = ERww = 0;
 
4042
 
 
4043
   mutex_unlock(&Fpixmap_lock);
 
4044
   
 
4045
   CEF = 0;                                                                //  no current edit func
 
4046
   menulock(0);
 
4047
   mwpaint2();
 
4048
   return;
 
4049
}
 
4050
 
 
4051
 
 
4052
/**************************************************************************/
 
4053
 
 
4054
//  process edit cancel
 
4055
 
 
4056
void edit_cancel(editfunc &EF)
 
4057
{
 
4058
   if (edit_action) return;
 
4059
   edit_action++;                                                          //  stop reentry
 
4060
   
 
4061
   if (&EF == CEF)                                                         //  current edit is canceled     v.11.07
 
4062
      wrapup_thread(9);                                                    //  tell thread to quit, wait
 
4063
   
 
4064
   if (EF.mousefunc == mouseCBfunc)                                        //  if my mouse, free mouse
 
4065
      freeMouse();
 
4066
   if (EF.zd) zdialog_free(EF.zd);                                         //  kill dialog
 
4067
   if (EF.curves) zfree(EF.curves);                                        //  free curves data
 
4068
   EF.zd = 0;
 
4069
   EF.curves = 0;
 
4070
 
 
4071
   if (&EF != CEF) {                                                       //  inactive edit canceled       v.11.07
 
4072
      edit_action = 0;
 
4073
      return;
 
4074
   }
 
4075
   
 
4076
   mutex_lock(&Fpixmap_lock);
 
4077
   PXM_free(E1pxm16);                                                      //  free edit pixmaps E1, E3
 
4078
   PXM_free(E3pxm16);
 
4079
   PXM_free(ERpxm16);                                                      //  free redo copy    v.10.3
 
4080
   E1ww = E3ww = ERww = 0;
 
4081
   mutex_unlock(&Fpixmap_lock);
 
4082
   
 
4083
   if (Fpreview) curr_image_time = get_seconds();                          //  mark time of file load       v.11.07
 
4084
   Fzoom = 0;                                                              //  v.11.07
 
4085
   CEF = 0;                                                                //  no current edit func
 
4086
   Fpreview = 0;                                                           //  no preview mode
 
4087
   menulock(0);                                                            //  unlock menu
 
4088
   mwpaint2();                                                             //  refresh window
 
4089
   edit_action = 0;
 
4090
   return;
 
4091
}   
 
4092
 
 
4093
 
 
4094
/**************************************************************************/
 
4095
 
 
4096
//  process edit dialog [done]  
 
4097
//  E3pxm16 >> Fpxm16 >> Fpxm8
 
4098
 
 
4099
void edit_done(editfunc &EF)
 
4100
{
 
4101
   if (edit_action) return;
 
4102
   edit_action++;                                                          //  stop reentry
 
4103
 
 
4104
   if (&EF == CEF) {                                                       //  current edit is done      v.11.07
 
4105
      wait_thread_idle();                                                  //  wait for thread to finish
 
4106
      if (Fpreview && CEF->Fmod) {                                         //  preview image was edited
 
4107
         Fzoom = 0;
 
4108
         edit_fullsize();                                                  //  update full image
 
4109
      }
 
4110
      wrapup_thread(8);                                                    //  tell thread to finish and exit
 
4111
   }
 
4112
 
 
4113
   if (EF.mousefunc == mouseCBfunc)                                        //  if my mouse, free mouse
 
4114
      freeMouse();
 
4115
   if (EF.zd) zdialog_free(EF.zd);                                         //  kill dialog
 
4116
   if (EF.curves) zfree(EF.curves);                                        //  free curves data
 
4117
   EF.zd = 0;
 
4118
   EF.curves = 0;
 
4119
 
 
4120
   if (&EF != CEF) {                                                       //  inactive edit is done     v.11.07
 
4121
      edit_action = 0;
 
4122
      return;
 
4123
   }
 
4124
 
 
4125
   mutex_lock(&Fpixmap_lock);
 
4126
 
 
4127
   if (CEF->Fmod) {                                                        //  image was modified
 
4128
      Fmodified++;                                                         //  overall mod counter
 
4129
      PXM_free(Fpxm16);
 
4130
      Fpxm16 = E3pxm16;                                                    //  E3 >> Fpxm16              v.11.07
 
4131
      E3pxm16 = 0;
 
4132
      PXM_free(Fpxm8);
 
4133
      Fpxm8 = PXM_convbpc(Fpxm16);                                         //  Fpxm16 >> Fpxm8
 
4134
      Fww = Fpxm8->ww;
 
4135
      Fhh = Fpxm8->hh;
 
4136
      Pundo++;
 
4137
      Pumax = Pundo;
 
4138
      save_undo(CEF->funcname);                                            //  save next undo state
 
4139
   }
 
4140
 
 
4141
   PXM_free(E1pxm16);                                                      //  free edit pixmaps
 
4142
   PXM_free(E3pxm16);
 
4143
   PXM_free(ERpxm16);                                                      //  free redo copy    v.10.3
 
4144
   E1ww = E3ww = ERww = 0;
 
4145
 
 
4146
   mutex_unlock(&Fpixmap_lock);
 
4147
   
 
4148
   CEF = 0;                                                                //  no current edit func
 
4149
   Fpreview = 0;                                                           //  no preview mode
 
4150
   menulock(0);                                                            //  unlock menu
 
4151
   mwpaint2();                                                             //  update window
 
4152
   edit_action = 0;
 
4153
   return;
 
4154
}
 
4155
 
 
4156
 
 
4157
/**************************************************************************/
 
4158
 
 
4159
//  Setup for edit dialog event or curve edit.
 
4160
//  Call this function before performing any image edit
 
4161
//  (switch current edit function to caller's edit function).
 
4162
 
 
4163
void edit_takeover(editfunc &EF)                                           //  v.11.07
 
4164
{
 
4165
   static int  busy = 0;
 
4166
   
 
4167
   if (busy++) return;                                                     //  KB key bounce
 
4168
   if (CEF && &EF != CEF) {                                                //  edit function changed
 
4169
      edit_setup(EF);                                                      //  close old and setup new edit
 
4170
      if (EF.zd) zdialog_send_event(EF.zd,"reset");                        //  reset dialog controls           v.11.08
 
4171
      freeMouse();                                                         //  free mouse from old dialog      v.11.08
 
4172
   }
 
4173
   busy = 0;
 
4174
   return;
 
4175
}
 
4176
 
 
4177
 
 
4178
/**************************************************************************/
 
4179
 
 
4180
//  edit undo, redo, reset functions
 
4181
//  these apply within an active edit function
 
4182
 
 
4183
void edit_undo()
 
4184
{
 
4185
   if (thread_status == 2) return;                                         //  thread busy
 
4186
   if (! CEF->Fmod) return;                                                //  not modified
 
4187
   if (edit_action) return;
 
4188
   edit_action++;                                                          //  stop reentry
 
4189
   
 
4190
   mutex_lock(&Fpixmap_lock);
 
4191
   PXM_free(ERpxm16);                                                      //  E3 >> redo copy
 
4192
   ERpxm16 = E3pxm16;
 
4193
   ERww = E3ww;
 
4194
   ERhh = E3hh;
 
4195
   E3pxm16 = PXM_copy(E1pxm16);                                            //  E1 >> E3
 
4196
   E3ww = E1ww;
 
4197
   E3hh = E1hh;
 
4198
   mutex_unlock(&Fpixmap_lock);
 
4199
 
 
4200
   CEF->Fmod = 0;                                                          //  reset image modified status
 
4201
   mwpaint2();                                                             //  refresh window
 
4202
   edit_action = 0;
 
4203
   return;
 
4204
}
 
4205
 
 
4206
 
 
4207
void edit_redo()
 
4208
{
 
4209
   if (thread_status == 2) return;                                         //  thread busy
 
4210
   if (! ERpxm16) return;                                                  //  no prior undo
 
4211
   if (edit_action) return;
 
4212
   edit_action++;                                                          //  stop reentry
 
4213
 
 
4214
   mutex_lock(&Fpixmap_lock);
 
4215
   PXM_free(E3pxm16);                                                      //  redo copy >> E3
 
4216
   E3pxm16 = ERpxm16;
 
4217
   E3ww = ERww;
 
4218
   E3hh = ERhh;
 
4219
   ERpxm16 = 0;
 
4220
   mutex_unlock(&Fpixmap_lock);
 
4221
 
 
4222
   CEF->Fmod = 1;                                                          //  image modified
 
4223
   Fmodified++;                                                            //  overall mod counter
 
4224
   mwpaint2();
 
4225
   edit_action = 0;
 
4226
   return;
 
4227
}
 
4228
 
 
4229
 
 
4230
void edit_reset()
 
4231
{
 
4232
   if (thread_status == 2) return;                                         //  thread busy
 
4233
   if (! CEF->Fmod) return;                                                //  not modified
 
4234
   if (edit_action) return;
 
4235
   edit_action++;                                                          //  stop reentry
 
4236
 
 
4237
   mutex_lock(&Fpixmap_lock);
 
4238
   PXM_free(ERpxm16);                                                      //  delete redo copy
 
4239
   PXM_free(E3pxm16);
 
4240
   E3pxm16 = PXM_copy(E1pxm16);                                            //  E1 >> E3
 
4241
   E3ww = E1ww;
 
4242
   E3hh = E1hh;
 
4243
   mutex_unlock(&Fpixmap_lock);
 
4244
 
 
4245
   CEF->Fmod = 0;                                                          //  reset image modified status
 
4246
   mwpaint2();                                                             //  refresh window
 
4247
   edit_action = 0;
 
4248
   return;
 
4249
}
 
4250
 
 
4251
 
 
4252
void edit_zapredo()
 
4253
{
 
4254
   PXM_free(ERpxm16);                                                      //  no redo copy
 
4255
   return;
 
4256
}
 
4257
 
 
4258
 
 
4259
/**************************************************************************/
 
4260
 
 
4261
//  Convert from preview mode (window-size pixmaps) to full-size pixmaps.
 
4262
//  Can only be used when edit function is in an edit thread with idle_loop
 
4263
//  This function is called directly from some edit functions
 
4264
 
 
4265
void edit_fullsize()
 
4266
{
 
4267
   mutex_lock(&Fpixmap_lock);
 
4268
   PXM_free(E1pxm16);                                                      //  free preview pixmaps
 
4269
   PXM_free(E3pxm16);
 
4270
   E1pxm16 = PXM_copy(Fpxm16);                                             //  make full-size pixmaps
 
4271
   E3pxm16 = PXM_copy(Fpxm16);
 
4272
   E1ww = E3ww = Fww;
 
4273
   E1hh = E3hh = Fhh;
 
4274
   mutex_unlock(&Fpixmap_lock);
 
4275
 
 
4276
   Fpreview = 0;
 
4277
   Fzoom = 0;                                                              //  v.11.07
 
4278
   curr_image_time = get_seconds();                                        //  mark time of file load    v.11.07
 
4279
 
 
4280
   if (! CEF->Fmod) return;                                                //  no change
 
4281
   signal_thread();                                                        //  signal thread, repeat edit
 
4282
   wait_thread_idle();
 
4283
   return;
 
4284
}
 
4285
 
 
4286
 
 
4287
/**************************************************************************
 
4288
      undo / redo toolbar buttons
 
4289
***************************************************************************/
 
4290
 
 
4291
//  [undo] menu function - reinstate previous edit in undo/redo stack
 
4292
 
 
4293
void m_undo(GtkWidget *, cchar *)
 
4294
{
 
4295
   if (CEF) {                                                              //  undo active edit
 
4296
      edit_undo();                                                         //  v.11.08
 
4297
      return;
 
4298
   }
 
4299
 
 
4300
   if (Pundo == 0) return;                                                 //  undo past edit
 
4301
   if (! menulock(1)) return;
 
4302
   Pundo--;
 
4303
   if (KB_A_key) Pundo = 0;                                                //  if KB 'A' key, undo all      v.11.11
 
4304
   load_undo();
 
4305
   Fmodified = Pundo;                                                      //  v.11.08
 
4306
   menulock(0);
 
4307
   return;
 
4308
}
 
4309
 
 
4310
 
 
4311
//  [redo] menu function - reinstate next edit in undo/redo stack
 
4312
 
 
4313
void m_redo(GtkWidget *, cchar *)
 
4314
{
 
4315
   if (CEF) {                                                              //  redo active edit
 
4316
      edit_redo();                                                         //  v.11.08
 
4317
      return;
 
4318
   }
 
4319
 
 
4320
   if (Pundo == Pumax) return;
 
4321
   if (! menulock(1)) return;
 
4322
   Pundo++;
 
4323
   if (KB_A_key) Pundo = Pumax;                                            //  if KB 'A' key, redo all      v.11.11
 
4324
   load_undo();
 
4325
   Fmodified = Pundo;                                                      //  v.11.08
 
4326
   menulock(0);
 
4327
   return;
 
4328
}
 
4329
 
 
4330
 
 
4331
//  undo all edits of the current image
 
4332
//  (discard modifications)
 
4333
 
 
4334
void undo_all()                                                            //  v.11.04
 
4335
{
 
4336
   if (! menulock(1)) return;
 
4337
   Pundo = 0;                                                              //  v.11.08
 
4338
   load_undo();
 
4339
   Fmodified = 0;                                                          //  v.11.07
 
4340
   menulock(0);
 
4341
   return;
 
4342
}
 
4343
 
 
4344
 
 
4345
//  Save Fpxm16 to undo/redo file stack
 
4346
//  stack position = Pundo
 
4347
 
 
4348
void save_undo(cchar *funcname)
 
4349
{
 
4350
   char     *pp, buff[24];
 
4351
   int      fid, cc, cc2;
 
4352
   
 
4353
   pp = strstr(undo_files,"_undo_");
 
4354
   if (! pp) zappcrash("undo/redo stack corrupted 1");
 
4355
   snprintf(pp+6,3,"%02d",Pundo);
 
4356
   
 
4357
   fid = open(undo_files,O_WRONLY|O_CREAT|O_TRUNC,0640);
 
4358
   if (! fid) zappcrash("undo/redo stack corrupted 2");
 
4359
 
 
4360
   snprintf(buff,24," %05d %05d fotoxx ",Fww,Fhh);
 
4361
   cc = write(fid,buff,20);
 
4362
   if (cc != 20) zappcrash("undo/redo stack corrupted 3");
 
4363
   
 
4364
   cc = Fww * Fhh * 6;
 
4365
   cc2 = write(fid,Fpxm16->bmp,cc);
 
4366
   if (cc2 != cc) zappcrash("undo/redo stack corrupted 4");
 
4367
 
 
4368
   close(fid);
 
4369
 
 
4370
   pvlist_replace(editlog,Pundo,funcname);                                 //  save log of edits done
 
4371
   return;
 
4372
}
 
4373
 
 
4374
 
 
4375
//  Load Fpxm16 from undo/redo file stack
 
4376
//  stack position = Pundo
 
4377
 
 
4378
void load_undo()
 
4379
{
 
4380
   char     *pp, buff[24], fotoxx[8];
 
4381
   int      fid, ww, hh, cc, cc2;
 
4382
 
 
4383
   pp = strstr(undo_files,"_undo_");
 
4384
   if (! pp) zappcrash("undo/redo stack corrupted 1");
 
4385
   snprintf(pp+6,3,"%02d",Pundo);
 
4386
   
 
4387
   fid = open(undo_files,O_RDONLY);
 
4388
   if (! fid) zappcrash("undo/redo stack corrupted 2");
 
4389
   
 
4390
   *fotoxx = 0;
 
4391
   cc = read(fid,buff,20);
 
4392
   sscanf(buff," %d %d %8s ",&ww, &hh, fotoxx);
 
4393
   if (! strEqu(fotoxx,"fotoxx")) zappcrash("undo/redo stack corrupted 4");
 
4394
 
 
4395
   mutex_lock(&Fpixmap_lock);
 
4396
 
 
4397
   PXM_free(Fpxm16);
 
4398
   Fpxm16 = PXM_make(ww,hh,16);
 
4399
   cc = ww * hh * 6;
 
4400
   cc2 = read(fid,Fpxm16->bmp,cc);
 
4401
   if (cc2 != cc) zappcrash("undo/redo stack corrupted 5");
 
4402
   close(fid);
 
4403
   
 
4404
   PXM_free(Fpxm8);
 
4405
   Fpxm8 = PXM_convbpc(Fpxm16);
 
4406
 
 
4407
   Fww = ww;
 
4408
   Fhh = hh;
 
4409
   
 
4410
   if (sa_fww != Fww || sa_fhh != Fhh)                                     //  disable area if image size changes
 
4411
      sa_disable();                                                        //  bugfix  v.11.08
 
4412
 
 
4413
   mutex_unlock(&Fpixmap_lock);
 
4414
   mwpaint2();
 
4415
   return;
 
4416
}
 
4417
 
 
4418
 
 
4419
/**************************************************************************/
 
4420
 
 
4421
//  ask user if modified image should be kept or discarded
 
4422
//  returns: 0 = mods discarded,  1 = do not discard mods
 
4423
 
 
4424
int mod_keep()
 
4425
{
 
4426
   int      keep = 0, choice = 0;
 
4427
   cchar    *title = ZTX("Discard edits?");
 
4428
   cchar    *message = ZTX("This action will discard current edits.\n"     //  more clarity  v.11.10
 
4429
                           "Continue to discard edits.\n"
 
4430
                           "Go Back to keep edits.");
 
4431
   cchar    *continu = ZTX("Continue");
 
4432
   cchar    *goback = ZTX("Go Back");
 
4433
 
 
4434
   if (Fsaved < Pundo) keep = 1;                                           //  curr. edits not saved
 
4435
   if (keep == 0) return 0;                                                //  no mods
 
4436
   choice = zdialog_choose(title,mWin,message,continu,goback,null);
 
4437
   if (choice == 2) return 1;                                              //  keep mods
 
4438
   undo_all();                                                             //  discard mods
 
4439
   return 0;
 
4440
}
 
4441
 
 
4442
 
 
4443
/**************************************************************************/
 
4444
 
 
4445
//  menu lock/unlock - some functions must not run concurrently
 
4446
//  returns 1 if success, else 0
 
4447
 
 
4448
int menulock(int lock)
 
4449
{
 
4450
   if (! lock && ! Fmenulock) zappcrash("menu lock error");
 
4451
   if (lock && Fmenulock) {
 
4452
      zmessageACK(mWin,ZTX("prior function still active"));                //  v.11.06
 
4453
      return 0;
 
4454
   }
 
4455
   if (lock) Fmenulock++;
 
4456
   else Fmenulock--;
 
4457
   return 1;
 
4458
}
 
4459
 
 
4460
 
 
4461
/**************************************************************************/
 
4462
 
 
4463
//  check if Fsyncbusy is active, return 0 if not,
 
4464
//  otherwise message user and return 1
 
4465
 
 
4466
int is_syncbusy()
 
4467
{
 
4468
   if (! Fsyncbusy) return 0;
 
4469
   threadmessage = "Synchronize files is running. Edits are blocked. \n"
 
4470
                   "You can still navigate and view images normally.";
 
4471
   return 1;
 
4472
}
 
4473
 
 
4474
 
 
4475
/**************************************************************************/
 
4476
 
 
4477
//  start thread that does the edit work
 
4478
 
 
4479
void start_thread(threadfunc func, void *arg)
 
4480
{
 
4481
   thread_status = 1;                                                      //  thread is running
 
4482
   thread_command = thread_pend = thread_done = thread_hiwater = 0;        //  nothing pending
 
4483
   start_detached_thread(func,arg);
 
4484
   return;
 
4485
}
 
4486
 
 
4487
 
 
4488
//  signal thread that work is pending
 
4489
 
 
4490
void signal_thread()
 
4491
{
 
4492
   edit_zapredo();                                                         //  reset redo copy
 
4493
   if (thread_status > 0) thread_pend++;
 
4494
   return;
 
4495
}
 
4496
 
 
4497
 
 
4498
//  wait for edit thread to complete pending work and become idle
 
4499
 
 
4500
void wait_thread_idle()
 
4501
{
 
4502
   while (thread_status && thread_pend > thread_done)
 
4503
   {
 
4504
      zmainloop();
 
4505
      zsleep(0.01);
 
4506
   }
 
4507
   
 
4508
   return;
 
4509
}
 
4510
 
 
4511
 
 
4512
//  wait for thread exit or command thread exit
 
4513
//  command = 0    wait for normal completion
 
4514
//            8    finish pending work and exit
 
4515
//            9    quit, exit now
 
4516
 
 
4517
void wrapup_thread(int command)
 
4518
{
 
4519
   thread_command = command;                                               //  tell thread to quit or finish
 
4520
 
 
4521
   while (thread_status > 0)                                               //  wait for thread to finish
 
4522
   {                                                                       //    pending work and exit
 
4523
      zmainloop();
 
4524
      zsleep(0.01);
 
4525
   }
 
4526
 
 
4527
   return;
 
4528
}
 
4529
 
 
4530
 
 
4531
//  called only from edit threads
 
4532
//  idle loop - wait for work request or exit command
 
4533
 
 
4534
void thread_idle_loop()
 
4535
{
 
4536
   if (thread_status == 2) Ffuncbusy--;                                    //  v.11.05
 
4537
   thread_status = 1;                                                      //  status = idle
 
4538
   thread_done = thread_hiwater;                                           //  work done = high-water mark
 
4539
 
 
4540
   while (true)
 
4541
   {
 
4542
      if (thread_command == 9) thread_exit();                              //  quit now command
 
4543
      if (thread_command == 8)                                             //  finish work and exit
 
4544
         if (thread_pend <= thread_done) thread_exit();
 
4545
      if (thread_pend > thread_done) break;                                //  wait for work request
 
4546
      zsleep(0.01);
 
4547
   }
 
4548
   
 
4549
   thread_hiwater = thread_pend;                                           //  set high-water mark
 
4550
   thread_status = 2;                                                      //  thread is working
 
4551
   Ffuncbusy++;                                                            //  v.11.05
 
4552
   return;                                                                 //  perform edit
 
4553
}
 
4554
 
 
4555
 
 
4556
//  exit thread unconditionally, called only from edit threads
 
4557
 
 
4558
void thread_exit()
 
4559
{
 
4560
   if (thread_status == 2) Ffuncbusy--;                                    //  v.11.05
 
4561
   thread_pend = thread_done = thread_hiwater = 0;
 
4562
   thread_status = 0;
 
4563
   pthread_exit(0);                                                        //  "return" cannot be used here
 
4564
}
 
4565
 
 
4566
 
 
4567
/**************************************************************************/
 
4568
 
 
4569
//  edit support functions for working threads (per processor core)
 
4570
 
 
4571
void start_wthread(threadfunc func, void *arg)                             //  start and increment busy count
 
4572
{
 
4573
   zadd_locked(wthreads_busy,+1);
 
4574
   zadd_locked(Ffuncbusy,+1);                                              //  v.11.01
 
4575
   start_detached_thread(func,arg);
 
4576
   return;
 
4577
}
 
4578
 
 
4579
 
 
4580
void exit_wthread()                                                        //  decrement busy count and exit
 
4581
{
 
4582
   zadd_locked(Ffuncbusy,-1);                                              //  v.11.01
 
4583
   zadd_locked(wthreads_busy,-1);
 
4584
   pthread_exit(0);                                                        //  "return" cannot be used here  v.9.4
 
4585
}
 
4586
 
 
4587
 
 
4588
void wait_wthreads()                                                       //  wait for all working threads done
 
4589
{                                                                          //  caller can be main() or a thread
 
4590
   while (wthreads_busy) {
 
4591
      zsleep(0.01);
 
4592
      zmainloop();                                                         //  v.11.11.1
 
4593
   }
 
4594
   return;
 
4595
}
 
4596
 
 
4597
 
 
4598
/**************************************************************************
 
4599
      other support functions
 
4600
***************************************************************************/
 
4601
 
 
4602
//  help menu function
 
4603
 
 
4604
void m_help(GtkWidget *, cchar *menu)
 
4605
{
 
4606
   if (strEqu(menu,ZTX("About"))) 
 
4607
      zmessageACK(mWin,"%s \n%s \n%s \n%s \n\n%s \n\n%s",
 
4608
        fversion,flicense,fhomepage,fcontact,fcredits,ftranslators);
 
4609
      
 
4610
   if (strEqu(menu,ZTX("User Guide"))) 
 
4611
      showz_userguide();
 
4612
   
 
4613
   if (strEqu(menu,ZTX("User Guide Changes")))
 
4614
      showz_userguide("changes");
 
4615
 
 
4616
   if (strEqu(menu,ZTX("Help")))                                           //  toolbar button
 
4617
      showz_userguide(zfuncs::F1_help_topic);                              //  show topic if there, or page 1
 
4618
 
 
4619
   if (strEqu(menu,"README"))
 
4620
      showz_readme();
 
4621
 
 
4622
   if (strEqu(menu,ZTX("Edit Functions Summary")))                         //  v.11.11
 
4623
      showz_doctext("edit-menus");
 
4624
 
 
4625
   if (strEqu(menu,ZTX("Change Log")))
 
4626
      showz_changelog();
 
4627
 
 
4628
   if (strEqu(menu,ZTX("Translations")))
 
4629
      showz_translations();
 
4630
      
 
4631
   if (strEqu(menu,ZTX("Home Page")))
 
4632
      showz_html(fhomepage);
 
4633
 
 
4634
   return;
 
4635
}
 
4636
 
 
4637
 
 
4638
/**************************************************************************/
 
4639
 
 
4640
//  table for loading and saving adjustable parameters between sessions
 
4641
 
 
4642
typedef struct {
 
4643
   char     name[20];
 
4644
   char     type[12];
 
4645
   int      count;
 
4646
   void     *location;
 
4647
}  param;
 
4648
 
 
4649
#define Nparms 32
 
4650
 
 
4651
param paramTab[Nparms] = {
 
4652
//     name                 type        count      location
 
4653
{     "fotoxx version",    "char",        1,       &pversion               },
 
4654
{     "last session",      "char",        1,       &last_session           },
 
4655
{     "first time",        "int",         1,       &Ffirsttime             },
 
4656
{     "new files found",   "int",         1,       &newfiles               },
 
4657
{     "window geometry",   "int",         4,       &mwgeom                 },
 
4658
{     "toolbar style",     "char",        1,       &tbar_style             },
 
4659
{     "warn overwrite",    "int",         1,       &Fwarnoverwrite         },
 
4660
{     "lens name",         "char",        4,       &lens4_name             },
 
4661
{     "lens mm",           "double",      4,       &lens4_mm               },
 
4662
{     "lens bow",          "double",      4,       &lens4_bow              },
 
4663
{     "current lens",      "int",         1,       &curr_lens              },
 
4664
{     "trim size",         "int",         2,       &trimsize               },
 
4665
{     "trim buttons",      "char",        6,       &trimbuttons            },
 
4666
{     "trim ratios",       "char",        6,       &trimratios             },
 
4667
{     "edit resize",       "int",         2,       &editresize             },
 
4668
{     "batch resize",      "int",         2,       &batchresize            },
 
4669
{     "e-mail resize",     "int",         2,       &emailsize              },
 
4670
{     "annotate font",     "char",        1,       &annotate_font          },
 
4671
{     "annotate text",     "char",        1,       &annotate_text          },
 
4672
{     "annotate color",    "char",        3,       &annotate_color         },
 
4673
{     "annotate trans",    "int",         3,       &annotate_trans         },
 
4674
{     "annotate outline",  "int",         1,       &annotate_outline       },
 
4675
{     "annotate angle",    "double",      1,       &annotate_angle         },
 
4676
{     "grid spacing",      "int",         2,       &gridspace              },
 
4677
{     "grid counts",       "int",         2,       &gridcount              },
 
4678
{     "rotate grid",       "int",         8,       &rotate_grid            },
 
4679
{     "unbend grid",       "int",         8,       &unbend_grid            },
 
4680
{     "warp-curved grid",  "int",         8,       &warpC_grid             },
 
4681
{     "warp-linear grid",  "int",         8,       &warpL_grid             },
 
4682
{     "warp-affine grid",  "int",         8,       &warpF_grid             },
 
4683
{     "slideshow trans",   "int",         SSNF,    &ss_funcs               },
 
4684
{     "slideshow music",   "char",        1,       &ss_musicfile           }   };
 
4685
 
 
4686
 
 
4687
//  save parameters to file /home/<user>/.fotoxx/parameters
 
4688
 
 
4689
void save_params()                                                         //  new v.10.9
 
4690
{
 
4691
   FILE        *fid;
 
4692
   char        buff[1050], text[1000];                                     //  limit for character data cc
 
4693
   char        *pp, *name, *type;
 
4694
   int         count;
 
4695
   void        *location;
 
4696
   char        **charloc;
 
4697
   int         *intloc;
 
4698
   double      *doubleloc;
 
4699
   time_t      currtime;
 
4700
   
 
4701
   pversion = strdupz(fversion);                                           //  this fotoxx version       v.11.10
 
4702
   
 
4703
   currtime = time(0);
 
4704
   last_session = ctime(&currtime) + 4;                                    //  fotoxx session exit time  v.11.11
 
4705
   if (last_session[4] == ' ') last_session[4] = '0';                      //  format  mmm dd hh:mm:ss yyyy
 
4706
   pp = strchr(last_session,'\n');
 
4707
   if (pp) *pp = 0;
 
4708
 
 
4709
   snprintf(buff,199,"%s/parameters",get_zuserdir());                      //  open output file
 
4710
   fid = fopen(buff,"w");
 
4711
   if (! fid) return;
 
4712
   
 
4713
   for (int ii = 0; ii < Nparms; ii++)                                     //  write table of state data
 
4714
   {
 
4715
      name = paramTab[ii].name;
 
4716
      type = paramTab[ii].type;
 
4717
      count = paramTab[ii].count;
 
4718
      location = paramTab[ii].location;
 
4719
      charloc = (char **) location;
 
4720
      intloc = (int *) location;
 
4721
      doubleloc = (double *) location;
 
4722
 
 
4723
      fprintf(fid,"%-20s  %-8s  %02d  ",name,type,count);                  //  write parm name, type, count
 
4724
 
 
4725
      for (int kk = 0; kk < count; kk++)                                   //  write "value" "value" ...
 
4726
      {
 
4727
         if (strEqu(type,"char")) {
 
4728
            if (! *charloc) printf("bad param data %s %d \n",name,kk);     //  bugfix v.10.11.1
 
4729
            if (! *charloc) break;
 
4730
            repl_1str(*charloc++,text,"\n","\\n");                         //  replace newlines with "\n"   v.10.11
 
4731
            fprintf(fid,"  \"%s\"",text);
 
4732
         }
 
4733
         if (strEqu(type,"int"))
 
4734
            fprintf(fid,"  \"%d\"",*intloc++);
 
4735
 
 
4736
         if (strEqu(type,"double"))
 
4737
            fprintf(fid,"  \"%.2f\"",*doubleloc++);
 
4738
      }
 
4739
 
 
4740
      fprintf(fid,"\n");                                                   //  write EOR
 
4741
   }
 
4742
   
 
4743
   fprintf(fid,"\n");
 
4744
   fclose(fid);                                                            //  close file
 
4745
 
 
4746
   fid = fopen(recentfiles_file,"w");                                      //  recent files file
 
4747
   if (! fid) return;
 
4748
   
 
4749
   for (int ii = 0; ii < Nrecentfiles; ii++)                               //  save list of recent files
 
4750
      if (recentfiles[ii])
 
4751
         fprintf(fid,"%s \n",recentfiles[ii]);
 
4752
   
 
4753
   fclose(fid);
 
4754
 
 
4755
   snprintf(buff,199,"%s/plugins",get_zuserdir());                         //  open file for plugins     v.11.03
 
4756
   fid = fopen(buff,"w");
 
4757
   if (! fid) return;
 
4758
   
 
4759
   for (int ii = 0; ii < Nplugins; ii++)                                   //  save plugins
 
4760
      if (plugins[ii])
 
4761
         fprintf(fid,"%s \n",plugins[ii]);
 
4762
   
 
4763
   fclose(fid);
 
4764
 
 
4765
   return;
 
4766
}
 
4767
 
 
4768
 
 
4769
//  load parameters from file /home/<user>/.fotoxx/parameters
 
4770
 
 
4771
void load_params()                                                         //  new v.10.9
 
4772
{
 
4773
   FILE        *fid;
 
4774
   int         ii, err, pcount, Idata;
 
4775
   double      Rdata;
 
4776
   char        buff[1000], text[1000], *pp;
 
4777
   char        name[20], type[12], count[8], data[1000];
 
4778
   void        *location;
 
4779
   char        **charloc;
 
4780
   int         *intloc;
 
4781
   double      *doubleloc;
 
4782
   
 
4783
   for (ii = 0; ii < Nparms; ii++)                                         //  set string parameters to "undefined"
 
4784
   {                                                                       //  v.10.11
 
4785
      if (strNeq(paramTab[ii].type,"char")) continue;
 
4786
      charloc = (char **) paramTab[ii].location;
 
4787
      pcount = paramTab[ii].count;
 
4788
      for (int jj = 0; jj < pcount; jj++)
 
4789
         *charloc++ = strdupz("undefined",20);
 
4790
   }
 
4791
   
 
4792
   snprintf(buff,499,"%s/parameters",get_zuserdir());                      //  open parameters file
 
4793
   fid = fopen(buff,"r");
 
4794
   if (! fid) return;
 
4795
 
 
4796
   while (true)                                                            //  read parameters
 
4797
   {
 
4798
      pp = fgets_trim(buff,999,fid,1);
 
4799
      if (! pp) break;
 
4800
      if (*pp == '#') continue;                                            //  comment          v.10.10
 
4801
      if (strlen(pp) < 40) continue;                                       //  rubbish          v.10.10
 
4802
 
 
4803
      err = 0;
 
4804
 
 
4805
      strncpy0(name,pp,20);                                                //  parm name
 
4806
      strTrim2(name);
 
4807
      
 
4808
      strncpy0(type,pp+22,8);                                              //  parm type
 
4809
      strTrim2(type);
 
4810
      
 
4811
      strncpy0(count,pp+32,4);                                             //  parm count
 
4812
      strTrim2(count);
 
4813
      err = convSI(count,pcount);
 
4814
      
 
4815
      strncpy0(data,pp+38,1000);                                           //  parm value(s)
 
4816
      strTrim2(data);
 
4817
      
 
4818
      for (ii = 0; ii < Nparms; ii++)                                      //  match file record to param table
 
4819
      {
 
4820
         if (strNeq(name,paramTab[ii].name)) continue;                     //  parm name
 
4821
         if (strNeq(type,paramTab[ii].type)) continue;                     //  parm type
 
4822
         if (paramTab[ii].count != pcount) continue;                       //  parm count
 
4823
 
 
4824
         location = paramTab[ii].location;
 
4825
         charloc = (char **) location;
 
4826
         intloc = (int *) location;
 
4827
         doubleloc = (double *) location;
 
4828
         
 
4829
         for (int kk = 1; kk <= pcount; kk++)
 
4830
         {
 
4831
            pp = (char *) strField(data,' ',kk);
 
4832
            if (! pp) break;
 
4833
 
 
4834
            if (strEqu(type,"char")) {
 
4835
               repl_1str(pp,text,"\\n","\n");                              //  replace "\n" with real newlines  v.10.11
 
4836
               *charloc++ = strdupz(text,0,"parameters");                  //  v.11.04
 
4837
            }
 
4838
 
 
4839
            if (strEqu(type,"int")) {
 
4840
               err = convSI(pp,Idata);
 
4841
               if (err) continue;
 
4842
               *intloc++ = Idata;
 
4843
            }
 
4844
 
 
4845
            if (strEqu(type,"double")) {
 
4846
               err = convSD(pp,Rdata);
 
4847
               if (err) continue;
 
4848
               *doubleloc++ = Rdata;
 
4849
            }
 
4850
         }
 
4851
      }
 
4852
   }
 
4853
 
 
4854
   fclose(fid);
 
4855
   
 
4856
   if (strNeq(pversion,fversion))                                          //  fotoxx version change        v.11.10
 
4857
      printf("new Fotoxx version %s %s \n",pversion,fversion);
 
4858
   
 
4859
   printf("last Fotoxx session: %s \n",last_session);                      //  last session exit time       v.11.11
 
4860
 
 
4861
   fid = fopen(topdirk_file,"r");                                          //  set top directory from       v.11.11
 
4862
   if (fid) {                                                              //    from top directory file
 
4863
      pp = fgets_trim(buff,499,fid,1);
 
4864
      if (pp && *pp == '/') 
 
4865
         topdirk = strdupz(buff,0,"top_dirk");
 
4866
      fclose(fid);
 
4867
   }
 
4868
 
 
4869
   printf("top image directory: %s \n",topdirk);
 
4870
 
 
4871
   for (ii = 0; ii < Nrecentfiles; ii++)                                   //  recent image file list = empty
 
4872
      recentfiles[ii] = 0;
 
4873
 
 
4874
   fid = fopen(recentfiles_file,"r");                                      //  open recent files file
 
4875
   if (fid) {
 
4876
      for (ii = 0; ii < Nrecentfiles; ii++)                                //  read list of recent files
 
4877
      {
 
4878
         pp = fgets_trim(buff,499,fid,1);
 
4879
         if (! pp) break;
 
4880
         if (*pp == '/') 
 
4881
            recentfiles[ii] = strdupz(buff,0,"recentfile");
 
4882
         else ii--;                                                        //  v.11.11
 
4883
      }
 
4884
 
 
4885
      fclose(fid);
 
4886
   }
 
4887
 
 
4888
   for (ii = 0; ii < maxplugins; ii++)                                     //  plugins list = empty      v.11.03
 
4889
      plugins[ii] = 0;
 
4890
   
 
4891
   plugins[0] = strdupz("Gimp = gimp");                                    //  if empty, default Gimp plugin
 
4892
   Nplugins = 1;
 
4893
 
 
4894
   snprintf(buff,499,"%s/plugins",get_zuserdir());                         //  open plugins file         v.11.03
 
4895
   fid = fopen(buff,"r");
 
4896
   if (fid)
 
4897
   {   
 
4898
      for (ii = 0; ii < maxplugins; ii++)                                  //  read list of plugins
 
4899
      {
 
4900
         pp = fgets_trim(buff,499,fid,1);
 
4901
         if (! pp) break;
 
4902
         plugins[ii] = strdupz(buff,0,"plugins");
 
4903
      }
 
4904
 
 
4905
      fclose(fid);
 
4906
      Nplugins = ii;
 
4907
   }
 
4908
 
 
4909
   return;
 
4910
}
 
4911
 
 
4912
 
 
4913
/**************************************************************************/
 
4914
 
 
4915
//  Compute the mean brightness of all pixel neighborhoods,                //  new v.9.6
 
4916
//  using a Guassian or a flat distribution for the weightings. 
 
4917
//  If a select area is active, only inside pixels are calculated.
 
4918
//  The flat method is 10-100x faster.
 
4919
 
 
4920
int         brhood_radius;
 
4921
double      brhood_kernel[200][200];                                       //  up to radius = 99
 
4922
float       *brhood_brightness = 0;                                        //  neighborhood brightness per pixel
 
4923
char        brhood_method;                                                 //  g = gaussian, f = flat distribution
 
4924
 
 
4925
 
 
4926
void brhood_calc(int radius, char method)
 
4927
{
 
4928
   void * brhood_wthread(void *arg);
 
4929
 
 
4930
   int      rad, rad2, dx, dy, cc, ii;
 
4931
   double   kern;
 
4932
   
 
4933
   brhood_radius = radius;
 
4934
   brhood_method = method;
 
4935
 
 
4936
   if (brhood_method == 'g')                                               //  compute Gaussian kernel
 
4937
   {                                                                       //  (not currently used   v.11.02)
 
4938
      rad = brhood_radius;
 
4939
      rad2 = rad * rad;
 
4940
 
 
4941
      for (dy = -rad; dy <= rad; dy++)
 
4942
      for (dx = -rad; dx <= rad; dx++)
 
4943
      {
 
4944
         if (dx*dx + dy*dy <= rad2)                                        //  cells within radius
 
4945
            kern = exp( - (dx*dx + dy*dy) / rad2);
 
4946
         else kern = 0;                                                    //  outside radius
 
4947
         brhood_kernel[dy+rad][dx+rad] = kern;
 
4948
      }
 
4949
   }
 
4950
 
 
4951
   if (brhood_brightness) zfree(brhood_brightness);                        //  allocate memory for pixel map
 
4952
   cc = E1ww * E1hh * sizeof(float);
 
4953
   brhood_brightness = (float *) zmalloc(cc,"brhood");
 
4954
   memset(brhood_brightness,0,cc);
 
4955
 
 
4956
   if (Factivearea) SB_goal = sa_Npixel;                                   //  set up progress tracking
 
4957
   else SB_goal = E1ww * E1hh;
 
4958
   SB_done = 0;                                                            //  v.11.06
 
4959
 
 
4960
   for (ii = 0; ii < Nwt; ii++)                                            //  start worker threads
 
4961
      start_wthread(brhood_wthread,&wtnx[ii]);
 
4962
   wait_wthreads();                                                        //  wait for completion
 
4963
 
 
4964
   SB_goal = 0;
 
4965
   return;
 
4966
}
 
4967
 
 
4968
 
 
4969
//  worker thread function
 
4970
 
 
4971
void * brhood_wthread(void *arg)
 
4972
{
 
4973
   int      index = *((int *) arg);
 
4974
   int      rad = brhood_radius;
 
4975
   int      ii, px, py, qx, qy, Fstart;
 
4976
   double   kern, bsum, bsamp, bmean;
 
4977
   uint16   *pixel;
 
4978
 
 
4979
   if (brhood_method == 'g')                                               //  use round gaussian distribution
 
4980
   {
 
4981
      for (py = index; py < E1hh; py += Nwt)
 
4982
      for (px = 0; px < E1ww; px++)
 
4983
      {
 
4984
         if (Factivearea && sa_mode != 7) {                                //  select area, not whole image    v.11.02
 
4985
            ii = py * E1ww + px;                                           //    use only inside pixels
 
4986
            if (! sa_pixmap[ii]) continue;
 
4987
         }
 
4988
         
 
4989
         bsum = bsamp = 0;
 
4990
 
 
4991
         for (qy = py-rad; qy <= py+rad; qy++)                             //  computed weighted sum of brightness
 
4992
         for (qx = px-rad; qx <= px+rad; qx++)                             //    for pixels in neighborhood
 
4993
         {
 
4994
            if (qy < 0 || qy > E1hh-1) continue;
 
4995
            if (qx < 0 || qx > E1ww-1) continue;
 
4996
            kern = brhood_kernel[qy+rad-py][qx+rad-px];
 
4997
            pixel = PXMpix(E1pxm16,qx,qy);
 
4998
            bsum += pixbright(pixel) * kern;                               //  sum brightness * weight
 
4999
            bsamp += kern;                                                 //  sum weights
 
5000
         }
 
5001
 
 
5002
         bmean = bsum / bsamp;                                             //  mean brightness
 
5003
         ii = py * E1ww + px;
 
5004
         brhood_brightness[ii] = bmean;                                    //  pixel value
 
5005
 
 
5006
         SB_done++;                                                        //  track progress                  v.11.06
 
5007
         if (drandz() < 0.0001) zsleep(0.001);                             //  trigger sorry kernel scheduler
 
5008
      }
 
5009
   }
 
5010
 
 
5011
   if (brhood_method == 'f')                                               //  use square flat distribution
 
5012
   {
 
5013
      Fstart = 1;
 
5014
      bsum = bsamp = 0;
 
5015
 
 
5016
      for (py = index; py < E1hh; py += Nwt)
 
5017
      for (px = 0; px < E1ww; px++)
 
5018
      {
 
5019
         if (Factivearea && sa_mode != 7) {                                //  select area, not whole image    v.11.02
 
5020
            ii = py * E1ww + px;                                           //     compute only inside pixels
 
5021
            if (! sa_pixmap[ii]) {
 
5022
               Fstart = 1;
 
5023
               continue;
 
5024
            }
 
5025
         }
 
5026
         
 
5027
         if (px == 0) Fstart = 1;
 
5028
         
 
5029
         if (Fstart) 
 
5030
         {
 
5031
            Fstart = 0;
 
5032
            bsum = bsamp = 0;
 
5033
 
 
5034
            for (qy = py-rad; qy <= py+rad; qy++)                          //  add up all columns
 
5035
            for (qx = px-rad; qx <= px+rad; qx++)
 
5036
            {
 
5037
               if (qy < 0 || qy > E1hh-1) continue;
 
5038
               if (qx < 0 || qx > E1ww-1) continue;
 
5039
               pixel = PXMpix(E1pxm16,qx,qy);
 
5040
               bsum += pixbright(pixel);
 
5041
               bsamp += 1;
 
5042
            }
 
5043
         }
 
5044
         else
 
5045
         {
 
5046
            qx = px-rad-1;                                                 //  subtract first-1 column
 
5047
            if (qx >= 0) {
 
5048
               for (qy = py-rad; qy <= py+rad; qy++)
 
5049
               {
 
5050
                  if (qy < 0 || qy > E1hh-1) continue;
 
5051
                  pixel = PXMpix(E1pxm16,qx,qy);
 
5052
                  bsum -= pixbright(pixel);
 
5053
                  bsamp -= 1;
 
5054
               }
 
5055
            }
 
5056
            qx = px+rad;                                                   //  add last column
 
5057
            if (qx < E1ww) {
 
5058
               for (qy = py-rad; qy <= py+rad; qy++)
 
5059
               {
 
5060
                  if (qy < 0 || qy > E1hh-1) continue;
 
5061
                  pixel = PXMpix(E1pxm16,qx,qy);
 
5062
                  bsum += pixbright(pixel);
 
5063
                  bsamp += 1;
 
5064
               }
 
5065
            }
 
5066
         }
 
5067
 
 
5068
         bmean = bsum / bsamp;                                             //  mean brightness
 
5069
         ii = py * E1ww + px;
 
5070
         brhood_brightness[ii] = bmean;
 
5071
 
 
5072
         SB_done++;                                                        //  track progress                  v.11.06
 
5073
         if (drandz() < 0.0001) zsleep(0.001);                             //  trigger sorry kernel scheduler
 
5074
      }
 
5075
   }
 
5076
         
 
5077
   exit_wthread();
 
5078
   return 0;                                                               //  not executed, avoid gcc warning
 
5079
}
 
5080
 
 
5081
 
 
5082
//  get the neighborhood brightness for given pixel
 
5083
 
 
5084
float get_brhood(int px, int py)
 
5085
{
 
5086
   int ii = py * E1ww + px;
 
5087
   return brhood_brightness[ii];
 
5088
}
 
5089
 
 
5090
 
 
5091
/**************************************************************************/
 
5092
 
 
5093
//  free all resources associated with the current image file
 
5094
 
 
5095
void free_resources(int fkeepundo)
 
5096
{
 
5097
   char        *pp;
 
5098
   int         err;
 
5099
 
 
5100
   mutex_lock(&Fpixmap_lock);                                              //  lock pixmaps
 
5101
   
 
5102
   if (! fkeepundo) {
 
5103
      strcpy(command,"rm -f ");                                            //  delete all undo files
 
5104
      strcat(command,undo_files);
 
5105
      pp = strstr(command,"_undo_");                                       //  /home/user/.fotoxx/pppppp_undo_*
 
5106
      strcpy(pp + 6,"*");
 
5107
      err = system(command);
 
5108
      if (err) printf("error: %s \n",wstrerror(err));
 
5109
      Fmodified = Pundo = Pumax = Fsaved = 0;                              //  reset undo/redo stack
 
5110
   }
 
5111
 
 
5112
   Ntoplines = Nptoplines;                                                 //  no image overlay lines
 
5113
   freeMouse();                                                            //  free mouse                v.11.04
 
5114
   
 
5115
   if (Fshutdown) {                                                        //  stop here if shutdown mode
 
5116
      mutex_unlock(&Fpixmap_lock);
 
5117
      return;
 
5118
   }
 
5119
   
 
5120
   if (curr_file) {
 
5121
      sa_unselect();                                                       //  unselect select area
 
5122
      zdialog_free(zdsela);                                                //  kill dialogs if active
 
5123
      zfree(curr_file);                                                    //  free image file
 
5124
      curr_file = 0;
 
5125
      SB_goal = 0;                                                         //  v.9.2
 
5126
      *SB_text = 0;                                                        //  v.10.7
 
5127
   }
 
5128
 
 
5129
   if (brhood_brightness) zfree(brhood_brightness);                        //  free brightness map       v.9.6
 
5130
   brhood_brightness = 0;
 
5131
 
 
5132
   PXM_free(Fpxm8);
 
5133
   PXM_free(Dpxm8);
 
5134
   PXM_free(Fpxm16);
 
5135
   PXM_free(E1pxm16);
 
5136
   PXM_free(E3pxm16);
 
5137
   PXM_free(ERpxm16);                                                      //  v.10.3
 
5138
 
 
5139
   Fww = E1ww = E3ww = Dww = 0;                                            //  make unusable (crash)
 
5140
 
 
5141
   mutex_unlock(&Fpixmap_lock);
 
5142
   return;
 
5143
}
 
5144
 
 
5145
 
 
5146
//  Get a virtual pixel at location (px,py) (real) in an PXM-16 pixmap.
 
5147
//  Get the overlapping real pixels and build a composite.
 
5148
 
 
5149
int vpixel(PXM *pxm, double px, double py, uint16 *vpix)
 
5150
{
 
5151
   int            ww, hh, px0, py0;
 
5152
   uint16         *ppix, *pix0, *pix1, *pix2, *pix3;
 
5153
   double         f0, f1, f2, f3;
 
5154
   double         red, green, blue;
 
5155
   
 
5156
   ww = pxm->ww;
 
5157
   hh = pxm->hh;
 
5158
   ppix = (uint16 *) pxm->bmp;
 
5159
 
 
5160
   px0 = px;                                                               //  pixel containing (px,py)
 
5161
   py0 = py;
 
5162
 
 
5163
   if (px0 < 1 || py0 < 1) return 0;                                       //  void edge pixels
 
5164
   if (px0 > ww-3 || py0 > hh-3) return 0;
 
5165
   
 
5166
   pix0 = ppix + (py0 * ww + px0) * 3;                                     //  4 pixels based at (px0,py0)
 
5167
   pix1 = pix0 + ww * 3;
 
5168
   pix2 = pix0 + 3;
 
5169
   pix3 = pix0 + ww * 3 + 3;
 
5170
 
 
5171
   f0 = (px0+1 - px) * (py0+1 - py);                                       //  overlap of (px,py)
 
5172
   f1 = (px0+1 - px) * (py - py0);                                         //   in each of the 4 pixels
 
5173
   f2 = (px - px0) * (py0+1 - py);
 
5174
   f3 = (px - px0) * (py - py0);
 
5175
   
 
5176
   red =   f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0];      //  sum the weighted inputs
 
5177
   green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1];
 
5178
   blue =  f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2];
 
5179
   
 
5180
   vpix[0] = red;
 
5181
   vpix[1] = green;
 
5182
   vpix[2] = blue;
 
5183
   
 
5184
   if (blue < 1) {                                                         //  v.10.7 
 
5185
      if (blue < 0.9) return 0;                                            //  near edge or voided area
 
5186
      vpix[2] = 1;                                                         //  avoid 0.999 to integer 0
 
5187
   }
 
5188
 
 
5189
   return 1;
 
5190
}
 
5191
 
 
5192
 
 
5193
//  compare two doubles for significant difference
 
5194
//  return:  0  difference not significant
 
5195
//          +1  d1 > d2
 
5196
//          -1  d1 < d2
 
5197
 
 
5198
int sigdiff(double d1, double d2, double signf)
 
5199
{
 
5200
   double diff = fabs(d1-d2);
 
5201
   if (diff == 0.0) return 0;
 
5202
   diff = diff / (fabs(d1) + fabs(d2));
 
5203
   if (diff < signf) return 0;
 
5204
   if (d1 > d2) return 1;
 
5205
   else return -1;
 
5206
}
 
5207
 
 
5208
 
 
5209
/**************************************************************************
 
5210
   file read and write utilities
 
5211
   PXM pixmap <--> file on disk
 
5212
***************************************************************************/
 
5213
 
 
5214
//  Load an image file into an PXM pixmap of 8 or 16 bits/color
 
5215
//  Also sets the following file scope variables:
 
5216
//    f_load_type = "jpg" "tif" "png" or "other"
 
5217
//    f_load_bpc = disk file bits per color = 1/8/16
 
5218
//    f_load_size = disk file size
 
5219
 
 
5220
PXM * f_load(cchar *filespec, int bpc)                                     //  use pixbuf or tiff library   v.9.8
 
5221
{
 
5222
   int            err;
 
5223
   cchar          *pext;
 
5224
   PXM            *pxm1, *pxm2;
 
5225
   struct stat    fstat;
 
5226
 
 
5227
   if (bpc != 8 && bpc != 16)                                              //  requested bpc must be 8 or 16
 
5228
      zappcrash("image_load bpc: %d",bpc);
 
5229
 
 
5230
   err = stat(filespec,&fstat);
 
5231
   if (err) return 0;                                                      //  file not found
 
5232
   if (! S_ISREG(fstat.st_mode)) return 0;                                 //  not a regular file
 
5233
   if (image_file_type(filespec) != 2) return 0;                           //  not a supported image type
 
5234
   f_load_size = fstat.st_size;                                            //  disk file bytes
 
5235
   
 
5236
   pext = strrchr(filespec,'/');
 
5237
   if (! pext) pext = filespec;
 
5238
 
 
5239
   if (pext > filespec+12 && strnEqu(pext-12,"/.thumbnails/",13)) {        //  refuse thumbnail files    v.10.0
 
5240
      zmessageACK(mWin,ZTX("cannot open thumbnail file"));
 
5241
      return 0;
 
5242
   }
 
5243
 
 
5244
   pext = strrchr(pext,'.');
 
5245
   if (! pext) pext = "";
 
5246
   
 
5247
   if (strstr(".jpg .jpeg .JPG .JPEG",pext)) strcpy(f_load_type,"jpg");
 
5248
   else if (strstr(".tif .tiff .TIF .TIFF",pext)) strcpy(f_load_type,"tif");
 
5249
   else if (strstr(".png .PNG",pext)) strcpy(f_load_type,"png");
 
5250
   else strcpy(f_load_type,"other");
 
5251
   
 
5252
   if (strEqu(f_load_type,"tif"))                                          //  use tiff lib to read tiff file
 
5253
      pxm1 = TIFFread(filespec);
 
5254
   else  pxm1 = PXBread(filespec);                                         //  use pixbuf lib for others
 
5255
   if (! pxm1) return 0;                                                   //  (both set f_load_bpc = file bpc)
 
5256
   
 
5257
   if (pxm1->bpc != bpc) {
 
5258
      pxm2 = PXM_convbpc(pxm1);                                            //  convert to requested bpc
 
5259
      PXM_free(pxm1);                                                      //    8 <--> 16
 
5260
      pxm1 = pxm2;
 
5261
   }
 
5262
                                                                           //  auto upright image removed    v.10.12
 
5263
   return pxm1;
 
5264
}
 
5265
 
 
5266
 
 
5267
/**************************************************************************/
 
5268
 
 
5269
//  save current image to specified disk file (same or new).
 
5270
//  set f_save_type, f_save_bpc, f_save_size
 
5271
//  update search index file
 
5272
//  return 0 if OK, else +N
 
5273
 
 
5274
int f_save(char *outfile, cchar *type, int bpc)                            //  use pixbuf or tiff library   v.9.8
 
5275
{
 
5276
   PXM            *pxm16;
 
5277
   cchar          *exifkey[2] = { exif_orientation_key, iptc_editlog_key };
 
5278
   cchar          *exifdata[2];
 
5279
   char           **ppv, funcslist[1000];
 
5280
   char           *pp, *tempfile, *pext;
 
5281
   int            nkeys, err = 1, cc1, cc2;
 
5282
   struct stat    fstat;
 
5283
   
 
5284
   if ((bpc != 8 && bpc != 16) || ! strstr("jpg tif png",type))            //  check args
 
5285
      zappcrash("f_save: %s %d",type,bpc);
 
5286
   
 
5287
   Ffuncbusy++;                                                            //  v.11.01
 
5288
 
 
5289
   pext = strrchr(outfile,'/');                                            //  force compatible file extension
 
5290
   if (pext) pext = strrchr(pext,'.');                                     //    if not already
 
5291
   if (! pext) pext = outfile + strlen(outfile);
 
5292
   if (strEqu(type,"jpg") && ! strstr(".jpg .JPG .jpeg .JPEG",pext))
 
5293
      strcpy(pext,".jpg");
 
5294
   if (strEqu(type,"png") && ! strstr(".png .PNG",pext))
 
5295
      strcpy(pext,".png");
 
5296
   if (strEqu(type,"tif") && ! strstr(".tif .TIF .tiff .TIFF",pext))
 
5297
      strcpy(pext,".tif");
 
5298
 
 
5299
   tempfile = strdupz(get_zuserdir(),24,"f_save");                         //  use temp output file
 
5300
   strcat(tempfile,"/temp_");                                              //    ~/.fotoxx/temp_ppppp.ext
 
5301
   strcat(tempfile,PIDstring);
 
5302
   strcat(tempfile,".");
 
5303
   strcat(tempfile,type);
 
5304
 
 
5305
   if (strEqu(type,"png")) {                                               //  save as PNG file (bpc = 8 always)
 
5306
      if (Fpxm16) err = PXBwrite(Fpxm16,tempfile);
 
5307
      else  err = PXBwrite(Fpxm8,tempfile);
 
5308
      bpc = 8;
 
5309
   }
 
5310
 
 
5311
   else if (strEqu(type,"tif")) {                                          //  save as tiff file
 
5312
      if (bpc == 8) {
 
5313
         if (Fpxm16) {
 
5314
            PXM_free(Fpxm8);                                               //  bugfix     v.10.8
 
5315
            Fpxm8 = PXM_convbpc(Fpxm16);
 
5316
         }
 
5317
         err = TIFFwrite(Fpxm8,tempfile);
 
5318
      }
 
5319
      else if (bpc == 16) {
 
5320
         if (Fpxm16) err = TIFFwrite(Fpxm16,tempfile);                     //  edit file exists, use it
 
5321
         else {
 
5322
            if (curr_file_bpc == 16) pxm16 = TIFFread(curr_file);          //  use original 16 bpc file
 
5323
            else  pxm16 = PXM_convbpc(Fpxm8);                              //  or convert 8 to 16 bpc
 
5324
            if (! pxm16) err = 1;
 
5325
            else {
 
5326
               err = TIFFwrite(pxm16,tempfile);
 
5327
               PXM_free(pxm16);
 
5328
            }
 
5329
         }
 
5330
      }
 
5331
   }
 
5332
 
 
5333
   else {                                                                  //  save as JPEG file (bpc = 8 always)
 
5334
      if (Fpxm16) err = PXBwrite(Fpxm16,tempfile);
 
5335
      else  err = PXBwrite(Fpxm8,tempfile);
 
5336
      bpc = 8;
 
5337
   }
 
5338
 
 
5339
   if (err) {
 
5340
      remove(tempfile);                                                    //  failure, clean up   v.11.02
 
5341
      zfree(tempfile);
 
5342
      Ffuncbusy--;
 
5343
      return 1;
 
5344
   }
 
5345
 
 
5346
   exifdata[0] = "";                                                       //  EXIF orientation = upright
 
5347
   nkeys = 1;                                                              //  remove old width, height        v.10.8
 
5348
 
 
5349
   if (Pundo > 0)                                                          //  if edits were made,
 
5350
   {                                                                       //    update relevant EXIF key      v.10.2
 
5351
      *funcslist = 0;
 
5352
      ppv = info_get(curr_file,&exifkey[1],1);                             //  get existing list of edits
 
5353
      if (ppv[0]) {
 
5354
         strncpy0(funcslist,ppv[0],998);
 
5355
         zfree(ppv[0]);
 
5356
      }
 
5357
      cc1 = strlen(funcslist);                                             //  add blank
 
5358
      if (cc1 && funcslist[cc1-1] != ' ') {
 
5359
         strcpy(funcslist+cc1," ");
 
5360
         cc1++;
 
5361
      }
 
5362
 
 
5363
      for (int ii = 1; ii <= Pundo; ii++)                                  //  append new list of edits
 
5364
      {                                                                    //  (omit index 0 = initial)
 
5365
         pp = pvlist_get(editlog,ii);
 
5366
         cc2 = strlen(pp);
 
5367
         if (cc1 + cc2 > 998) break;
 
5368
         strcpy(funcslist+cc1,pp);
 
5369
         strcpy(funcslist+cc1+cc2," ");
 
5370
         cc1 += cc2 + 1;
 
5371
      }
 
5372
      
 
5373
      exifdata[1] = funcslist;                                             //  IPTC edit history key, fotoxx edits done
 
5374
      nkeys = 2;
 
5375
   }
 
5376
 
 
5377
   err = stat(curr_file,&fstat);
 
5378
   if (! err && S_ISREG(fstat.st_mode)) {                                  //  if current file exists,         v.11.05
 
5379
      err = info_copy(curr_file,tempfile,exifkey,exifdata,nkeys);          //    copy all EXIF/IPTC data to
 
5380
      if (err) zmessageACK(mWin,"Unable to copy EXIF/IPTC");               //      temp file with above revisions
 
5381
   }
 
5382
   
 
5383
   snprintf(command,ccc,"cp -f \"%s\" \"%s\" ",tempfile,outfile);          //  copy temp file to output file
 
5384
   err = system(command);
 
5385
   if (err) zmessageACK(mWin,"Unable to save image: %s",wstrerror(err));
 
5386
 
 
5387
   remove(tempfile);                                                       //  delete temp file                v.11.02
 
5388
   zfree(tempfile);
 
5389
   
 
5390
   save_fileinfo(outfile);                                                 //  save tag changes if any
 
5391
   if (! curr_file || strNeq(outfile,curr_file))                           //  if save to new file,            v.11.08
 
5392
      update_search_index(outfile);                                        //    update search index file
 
5393
 
 
5394
   Fsaved = Pundo;                                                         //  update which mods are saved to disk
 
5395
 
 
5396
   add_recent_file(outfile);                                               //  first in recent files list
 
5397
   
 
5398
   strcpy(f_save_type,type);                                               //  update f_save_xxx data
 
5399
   f_save_bpc = bpc;
 
5400
 
 
5401
   err = stat(outfile,&fstat);
 
5402
   if (err) {
 
5403
      Ffuncbusy--;
 
5404
      return 1;
 
5405
   }
 
5406
 
 
5407
   f_save_size = fstat.st_size;
 
5408
   
 
5409
   Ffuncbusy--;
 
5410
   mwpaint2();                                                             //  v.11.03
 
5411
   return 0;
 
5412
}
 
5413
 
 
5414
 
 
5415
/**************************************************************************/
 
5416
 
 
5417
//  Read from TIFF file using TIFF library. 
 
5418
//  Use native TIFF file bits/pixel.
 
5419
 
 
5420
PXM * TIFFread(cchar *filespec)                                            //  overhauled  v.10.8.1
 
5421
{
 
5422
   TIFF        *tiff;
 
5423
   PXM         *pxm;
 
5424
   char        *tiffbuff;
 
5425
   uint8       *tiff8, *pxm8;
 
5426
   uint16      *tiff16, *pxm16;
 
5427
   uint16      bpc, nch, fmt; 
 
5428
   int         ww, hh, rps, stb, nst;                                      //  int not uint        v.11.03
 
5429
   int         tiffstat, row, col, strip, cc;
 
5430
   
 
5431
   tiff = TIFFOpen(filespec,"r");
 
5432
   if (! tiff) {
 
5433
      zmessageACK(mWin,ZTX("TIFF open failure"));
 
5434
      return 0;
 
5435
   }
 
5436
 
 
5437
   TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &ww);                            //  width
 
5438
   TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &hh);                           //  height
 
5439
   TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpc);                        //  bits per color, 1/8/16
 
5440
   TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &nch);                      //  no. channels (colors), 1/3/4
 
5441
   TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rps);                         //  rows per strip
 
5442
   TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &fmt);                         //  data format
 
5443
   stb = TIFFStripSize(tiff);                                              //  strip size
 
5444
   nst = TIFFNumberOfStrips(tiff);                                         //  number of strips
 
5445
 
 
5446
// printf("ww %d  hh %d  nch %d  bpc %d  rps %d  stb %d  nst %d  fmt %d \n",ww,hh,nch,bpc,rps,stb,nst,fmt);
 
5447
   
 
5448
   if (! (bpc <= 8 || bpc == 16)) {                                        //  check for supported bits/color
 
5449
      zmessageACK(mWin,ZTX("TIFF bits/color=%d not supported"),bpc);
 
5450
      TIFFClose(tiff);
 
5451
      return 0;
 
5452
   }
 
5453
   
 
5454
   f_load_bpc = bpc;                                                       //  for f_load(), file bpc 1/8/16   v.10.12
 
5455
 
 
5456
   if (bpc <= 8)                                                           //  use universal TIFF reader
 
5457
   {                                                                       //    if bits/color <= 8
 
5458
      tiffbuff = zmalloc(ww*hh*4,"tiffbuff");
 
5459
 
 
5460
      tiffstat = TIFFReadRGBAImage(tiff, ww, hh, (uint *) tiffbuff, 0);
 
5461
      TIFFClose(tiff);
 
5462
 
 
5463
      if (tiffstat != 1) {
 
5464
         zmessageACK(mWin,ZTX("TIFF read failure"));
 
5465
         zfree(tiffbuff);
 
5466
         return 0;
 
5467
      }
 
5468
 
 
5469
      pxm = PXM_make(ww,hh,8);
 
5470
 
 
5471
      tiff8 = (uint8 *) tiffbuff;
 
5472
 
 
5473
      for (row = 0; row < hh; row++)                                       //  convert RGBA to RGB
 
5474
      {
 
5475
         pxm8 = (uint8 *) pxm->bmp;
 
5476
         pxm8 += (hh-1 - row) * ww * 3;
 
5477
 
 
5478
         for (col = 0; col < ww; col++)
 
5479
         {
 
5480
            pxm8[0] = tiff8[0];
 
5481
            pxm8[1] = tiff8[1];
 
5482
            pxm8[2] = tiff8[2];
 
5483
            pxm8 += 3;
 
5484
            tiff8 += 4;
 
5485
         }
 
5486
      }
 
5487
 
 
5488
      zfree(tiffbuff);
 
5489
      return pxm;
 
5490
   }
 
5491
                                                                           //  16 bits per color
 
5492
 
 
5493
   stb += 1000000;                                                         //  reduce risk of crash   v.10.8.2
 
5494
   tiffbuff = zmalloc(stb,"tiffbuff");                                     //  read encoded strips 
 
5495
 
 
5496
   pxm = PXM_make(ww,hh,16);
 
5497
   
 
5498
   for (strip = 0; strip < nst; strip++)
 
5499
   {
 
5500
      cc = TIFFReadEncodedStrip(tiff,strip,tiffbuff,stb);
 
5501
      if (cc < 0) {
 
5502
         zmessageACK(mWin,ZTX("TIFF read failure"));
 
5503
         TIFFClose(tiff);
 
5504
         zfree(tiffbuff);
 
5505
         PXM_free(pxm);
 
5506
         return 0;
 
5507
      }
 
5508
      
 
5509
      if (cc == 0) break;
 
5510
      
 
5511
      tiff16 = (uint16 *) tiffbuff;
 
5512
      pxm16 = (uint16 *) pxm->bmp;
 
5513
      row = strip * rps;
 
5514
      pxm16 += row * ww * 3;
 
5515
 
 
5516
      while (cc >= 6)                                                      //  bugfix  v.11.03
 
5517
      {
 
5518
         pxm16[0] = tiff16[0];
 
5519
         pxm16[1] = tiff16[1];
 
5520
         pxm16[2] = tiff16[2];
 
5521
         pxm16 += 3;
 
5522
         tiff16 += nch;
 
5523
         cc -= nch * 2;
 
5524
      }
 
5525
   }
 
5526
 
 
5527
   TIFFClose(tiff);
 
5528
   zfree(tiffbuff);
 
5529
   return pxm;
 
5530
}
 
5531
 
 
5532
 
 
5533
//  Write to TIFF file using TIFF library. 
 
5534
//  File bpc is taken from PXM (8 or 16).
 
5535
//  returns 0 if OK, +N if error.
 
5536
 
 
5537
int TIFFwrite(PXM *pxm, cchar *filespec)                                   //  new v.9.8
 
5538
{
 
5539
   TIFF        *tiff;
 
5540
   uint8       *tiff8, *pxm8;
 
5541
   uint16      *tiff16, *pxm16;
 
5542
   int         tiffstat = 0;
 
5543
   int         ww, hh, row, col, rowcc;                                    //  int not uint        v.11.03
 
5544
   int         bpc, nch, pm = 2, pc = 1, comp = 5; 
 
5545
   char        *tiffbuff;
 
5546
   
 
5547
   tiff = TIFFOpen(filespec,"w");
 
5548
   if (! tiff) {
 
5549
      zmessageACK(mWin,ZTX("TIFF open failure"));
 
5550
      return 1;
 
5551
   }
 
5552
   
 
5553
   ww = pxm->ww;
 
5554
   hh = pxm->hh;
 
5555
   bpc = pxm->bpc;
 
5556
   nch = 3;                                                                //  alpha channel removed
 
5557
 
 
5558
   TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, ww);
 
5559
   TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bpc);
 
5560
   TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, nch);
 
5561
   TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, pm);                            //  RGB
 
5562
   TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, pc);
 
5563
   TIFFSetField(tiff, TIFFTAG_COMPRESSION, comp);                          //  LZW
 
5564
   
 
5565
   rowcc = TIFFScanlineSize(tiff);
 
5566
   tiffbuff = (char*) zmalloc(rowcc,"tiffbuff");
 
5567
 
 
5568
   for (row = 0; row < hh; row++)
 
5569
   {
 
5570
      if (bpc == 8)
 
5571
      {
 
5572
         tiff8 = (uint8 *) tiffbuff;
 
5573
         pxm8 = (uint8 *) pxm->bmp + row * ww * 3;
 
5574
 
 
5575
         for (col = 0; col < ww; col++)
 
5576
         {
 
5577
            tiff8[0] = pxm8[0];
 
5578
            tiff8[1] = pxm8[1];
 
5579
            tiff8[2] = pxm8[2];
 
5580
            pxm8 += 3;
 
5581
            tiff8 += nch;
 
5582
         }
 
5583
      }
 
5584
 
 
5585
      if (bpc == 16) 
 
5586
      {
 
5587
         tiff16 = (uint16 *) tiffbuff;
 
5588
         pxm16 = (uint16 *) pxm->bmp + row * ww * 3;
 
5589
 
 
5590
         for (col = 0; col < ww; col++)
 
5591
         {
 
5592
            tiff16[0] = pxm16[0];
 
5593
            tiff16[1] = pxm16[1];
 
5594
            tiff16[2] = pxm16[2];
 
5595
            pxm16 += 3;
 
5596
            tiff16 += nch;
 
5597
         }
 
5598
      }
 
5599
 
 
5600
      tiffstat = TIFFWriteScanline(tiff,tiffbuff,row,0);
 
5601
      if (tiffstat != 1) break;
 
5602
   }
 
5603
 
 
5604
   TIFFClose(tiff);
 
5605
   zfree(tiffbuff);
 
5606
   
 
5607
   if (tiffstat == 1) return 0;
 
5608
   zmessageACK(mWin,ZTX("TIFF write failure"));
 
5609
   return 2;
 
5610
}
 
5611
 
 
5612
 
 
5613
//  intercept TIFF warning messages (many)                                 //  new v.9.8
 
5614
 
 
5615
void tiffwarninghandler(cchar *module, cchar *format, va_list ap)
 
5616
{
 
5617
   return;                                                                 //  stop flood of crap
 
5618
   char  message[200];
 
5619
   vsnprintf(message,199,format,ap);
 
5620
   printf("TIFF warning: %s %s \n",module,message);
 
5621
   return;
 
5622
}
 
5623
 
 
5624
 
 
5625
//  Read from jpeg/png file using pixbuf library. bpc = 8.
 
5626
 
 
5627
PXM * PXBread(cchar *filespec)                                             //  new v.9.8
 
5628
{
 
5629
   GError      *gerror = 0;
 
5630
   PXB         *pxb;
 
5631
   PXM         *pxm;
 
5632
   int         ww, hh, px, py, nch, rowst;
 
5633
   uint8       *bmp1, *bmp2, *pix1, *pix2;
 
5634
   
 
5635
   pxb = gdk_pixbuf_new_from_file(filespec,&gerror);
 
5636
   if (! pxb) {
 
5637
      printf("%s \n",gerror->message);                                     //  v.10.8
 
5638
      zmessageACK(mWin,ZTX("file type not supported"));
 
5639
      return 0;
 
5640
   }
 
5641
 
 
5642
   ww = gdk_pixbuf_get_width(pxb);
 
5643
   hh = gdk_pixbuf_get_height(pxb);
 
5644
   nch = gdk_pixbuf_get_n_channels(pxb);
 
5645
   rowst = gdk_pixbuf_get_rowstride(pxb);
 
5646
   bmp1 = gdk_pixbuf_get_pixels(pxb);
 
5647
   
 
5648
   pxm = PXM_make(ww,hh,8);
 
5649
   bmp2 = (uint8 *) pxm->bmp;
 
5650
 
 
5651
   for (py = 0; py < hh; py++)
 
5652
   {
 
5653
      pix1 = bmp1 + rowst * py;
 
5654
      pix2 = bmp2 + py * ww * 3;
 
5655
 
 
5656
      for (px = 0; px < ww; px++)
 
5657
      {
 
5658
         pix2[0] = pix1[0];
 
5659
         pix2[1] = pix1[1];
 
5660
         pix2[2] = pix1[2];
 
5661
         pix1 += nch;
 
5662
         pix2 += 3;
 
5663
      }
 
5664
   }
 
5665
   
 
5666
   f_load_bpc = 8;                                                         //  file bits per color for f_load()
 
5667
   g_object_unref(pxb);                                                    //  (any value on disk becomes 8)
 
5668
   return pxm;
 
5669
}
 
5670
 
 
5671
 
 
5672
//  Write to jpeg/png file using pixbuf library. bpc = 8.
 
5673
//  returns 0 if OK, +N if error.
 
5674
 
 
5675
int PXBwrite(PXM *pxm, cchar *filespec)                                    //  new v.9.8
 
5676
{
 
5677
   int         ww, hh, bpc, px, py, rowst;
 
5678
   uint8       *bmp8, *pix8, *bmp2, *pix2;
 
5679
   uint16      *bmp16, *pix16;
 
5680
   PXB         *pxb;
 
5681
   cchar       *pext;
 
5682
   GError      *gerror = 0;
 
5683
   int         pxbstat;
 
5684
 
 
5685
   ww = pxm->ww;
 
5686
   hh = pxm->hh;
 
5687
   bpc = pxm->bpc;
 
5688
   
 
5689
   pxb = gdk_pixbuf_new(RGBCOLOR,0,8,ww,hh);
 
5690
   if (! pxb) zappcrash("pixbuf allocation failure");
 
5691
 
 
5692
   bmp8 = (uint8 *) pxm->bmp;
 
5693
   bmp16 = (uint16 *) pxm->bmp;
 
5694
   bmp2 = gdk_pixbuf_get_pixels(pxb);
 
5695
   rowst = gdk_pixbuf_get_rowstride(pxb);
 
5696
 
 
5697
   if (bpc == 8)
 
5698
   {
 
5699
      for (py = 0; py < hh; py++)
 
5700
      {
 
5701
         pix8 = bmp8 + py * ww * 3;
 
5702
         pix2 = bmp2 + rowst * py;
 
5703
 
 
5704
         for (px = 0; px < ww; px++)
 
5705
         {
 
5706
            pix2[0] = pix8[0];
 
5707
            pix2[1] = pix8[1];
 
5708
            pix2[2] = pix8[2];
 
5709
            pix2 += 3;
 
5710
            pix8 += 3;
 
5711
         }
 
5712
      }
 
5713
   }
 
5714
   
 
5715
   if (bpc == 16)
 
5716
   {
 
5717
      for (py = 0; py < hh; py++)
 
5718
      {
 
5719
         pix16 = bmp16 + py * ww * 3;
 
5720
         pix2 = bmp2 + rowst * py;
 
5721
 
 
5722
         for (px = 0; px < ww; px++)
 
5723
         {
 
5724
            pix2[0] = pix16[0] >> 8;
 
5725
            pix2[1] = pix16[1] >> 8;
 
5726
            pix2[2] = pix16[2] >> 8;
 
5727
            pix2 += 3;
 
5728
            pix16 += 3;
 
5729
         }
 
5730
      }
 
5731
   }
 
5732
   
 
5733
   pext = strrchr(filespec,'/');
 
5734
   if (! pext) pext = filespec;
 
5735
   pext = strrchr(pext,'.');
 
5736
   if (! pext) pext = "";
 
5737
   
 
5738
   if (strstr(".png .PNG",pext))
 
5739
      pxbstat = gdk_pixbuf_save(pxb,filespec,"png",&gerror,null);
 
5740
   else pxbstat = gdk_pixbuf_save(pxb,filespec,"jpeg",&gerror,"quality",jpeg_quality,null);
 
5741
   g_object_unref(pxb);
 
5742
   if (pxbstat) return 0;
 
5743
 
 
5744
   printf("%s \n",gerror->message);                                        //  v.10.8
 
5745
   zmessageACK(mWin,ZTX("pixbuf write failure"));
 
5746
   return 1;
 
5747
}
 
5748
 
 
5749
 
 
5750
/**************************************************************************
 
5751
      PXM pixmap conversion and rescale functions
 
5752
***************************************************************************/
 
5753
 
 
5754
//  initialize PXM pixmap - allocate memory
 
5755
 
 
5756
PXM * PXM_make(int ww, int hh, int bpc)
 
5757
{
 
5758
   if (ww < 1 || hh < 1 || (bpc != 8 && bpc != 16))
 
5759
      zappcrash("PXM_make() %d %d %d",ww,hh,bpc);
 
5760
   
 
5761
   PXM *pxm = (PXM *) zmalloc(sizeof(PXM),"PXM");
 
5762
   pxm->ww = ww;
 
5763
   pxm->hh = hh;
 
5764
   pxm->bpc = bpc;
 
5765
   if (bpc == 8) pxm->bmp = zmalloc(ww*hh*3,"PXM.bmp");
 
5766
   if (bpc == 16) pxm->bmp = zmalloc(ww*hh*6,"PXM.bmp");
 
5767
   strcpy(pxm->wmi,"rgbrgb");
 
5768
   return pxm;
 
5769
}
 
5770
 
 
5771
 
 
5772
//  free PXM pixmap
 
5773
 
 
5774
void PXM_free(PXM *&pxm)
 
5775
{
 
5776
   if (! pxm) return;
 
5777
   if (! strEqu(pxm->wmi,"rgbrgb")) 
 
5778
      zappcrash("PXM_free(), bad PXM");
 
5779
   strcpy(pxm->wmi,"xxxxxx");
 
5780
   zfree(pxm->bmp);
 
5781
   zfree(pxm);
 
5782
   pxm = 0;                                                                //  v.11.01
 
5783
   return;
 
5784
}
 
5785
 
 
5786
 
 
5787
//  create a copy of an PXM pixmap
 
5788
 
 
5789
PXM * PXM_copy(PXM *pxm1)
 
5790
{
 
5791
   int      cc;
 
5792
   PXM      *pxm2;
 
5793
 
 
5794
   pxm2 = PXM_make(pxm1->ww, pxm1->hh, pxm1->bpc);
 
5795
   cc = pxm1->ww * pxm1->hh * (pxm1->bpc / 8 * 3);
 
5796
   memcpy(pxm2->bmp,pxm1->bmp,cc);
 
5797
   return pxm2;
 
5798
}
 
5799
 
 
5800
 
 
5801
//  create a copy of an PXM area
 
5802
 
 
5803
PXM * PXM_copy_area(PXM *pxm1, int orgx, int orgy, int ww2, int hh2)
 
5804
{
 
5805
   uint8          *bmp1, *pix1, *bmp2, *pix2;
 
5806
   uint16         *bmp3, *pix3, *bmp4, *pix4;
 
5807
   PXM            *pxm2 = 0;
 
5808
   int            ww1, bpc, px1, py1, px2, py2;
 
5809
 
 
5810
   ww1 = pxm1->ww;
 
5811
   bpc = pxm1->bpc;
 
5812
 
 
5813
   if (bpc == 8)
 
5814
   {
 
5815
      pxm2 = PXM_make(ww2,hh2,8);
 
5816
      bmp1 = (uint8 *) pxm1->bmp;
 
5817
      bmp2 = (uint8 *) pxm2->bmp;
 
5818
     
 
5819
      for (py1 = orgy, py2 = 0; py2 < hh2; py1++, py2++) 
 
5820
      {
 
5821
         for (px1 = orgx, px2 = 0; px2 < ww2; px1++, px2++)
 
5822
         {
 
5823
            pix1 = bmp1 + (py1 * ww1 + px1) * 3;
 
5824
            pix2 = bmp2 + (py2 * ww2 + px2) * 3;
 
5825
 
 
5826
            pix2[0] = pix1[0];
 
5827
            pix2[1] = pix1[1];
 
5828
            pix2[2] = pix1[2];
 
5829
            pix1 += 3;
 
5830
            pix2 += 3;
 
5831
         }
 
5832
      }
 
5833
   }
 
5834
 
 
5835
   if (bpc == 16)
 
5836
   {
 
5837
      pxm2 = PXM_make(ww2,hh2,16);
 
5838
      bmp3 = (uint16 *) pxm1->bmp;
 
5839
      bmp4 = (uint16 *) pxm2->bmp;
 
5840
     
 
5841
      for (py1 = orgy, py2 = 0; py2 < hh2; py1++, py2++) 
 
5842
      {
 
5843
         for (px1 = orgx, px2 = 0; px2 < ww2; px1++, px2++)
 
5844
         {
 
5845
            pix3 = bmp3 + (py1 * ww1 + px1) * 3;
 
5846
            pix4 = bmp4 + (py2 * ww2 + px2) * 3;
 
5847
 
 
5848
            pix4[0] = pix3[0];
 
5849
            pix4[1] = pix3[1];
 
5850
            pix4[2] = pix3[2];
 
5851
            pix3 += 3;
 
5852
            pix4 += 3;
 
5853
         }
 
5854
      }
 
5855
   }
 
5856
   
 
5857
   return pxm2;
 
5858
}
 
5859
 
 
5860
 
 
5861
//   convert PXM pixmap from 8/16 to 16/8 bits per color
 
5862
 
 
5863
PXM * PXM_convbpc(PXM *pxm1)
 
5864
{
 
5865
   uint8          *bmp8, *pix8;
 
5866
   uint16         *bmp16, *pix16;
 
5867
   PXM            *pxm2 = 0;
 
5868
   int            ww, hh, bpc, px, py;
 
5869
 
 
5870
   ww = pxm1->ww;
 
5871
   hh = pxm1->hh;
 
5872
   bpc = pxm1->bpc;
 
5873
 
 
5874
   if (bpc == 8)                                                           //  8 > 16
 
5875
   {
 
5876
      pxm2 = PXM_make(ww,hh,16);
 
5877
      bmp8 = (uint8 *) pxm1->bmp;
 
5878
      bmp16 = (uint16 *) pxm2->bmp;
 
5879
     
 
5880
      for (py = 0; py < hh; py++) 
 
5881
      {
 
5882
         pix8 = bmp8 + py * ww * 3;
 
5883
         pix16 = bmp16 + py * ww * 3;
 
5884
 
 
5885
         for (px = 0; px < ww; px++)
 
5886
         {
 
5887
            pix16[0] = pix8[0] << 8;
 
5888
            pix16[1] = pix8[1] << 8;
 
5889
            pix16[2] = pix8[2] << 8;
 
5890
            pix8 += 3;
 
5891
            pix16 += 3;
 
5892
         }
 
5893
      }
 
5894
   }
 
5895
 
 
5896
   if (bpc == 16)                                                          //  16 > 8
 
5897
   {
 
5898
      pxm2 = PXM_make(ww,hh,8);
 
5899
      bmp8 = (uint8 *) pxm2->bmp;
 
5900
      bmp16 = (uint16 *) pxm1->bmp;
 
5901
     
 
5902
      for (py = 0; py < hh; py++) 
 
5903
      {
 
5904
         pix8 = bmp8 + py * ww * 3;
 
5905
         pix16 = bmp16 + py * ww * 3;
 
5906
 
 
5907
         for (px = 0; px < ww; px++)
 
5908
         {
 
5909
            pix8[0] = pix16[0] >> 8;
 
5910
            pix8[1] = pix16[1] >> 8;
 
5911
            pix8[2] = pix16[2] >> 8;
 
5912
            pix8 += 3;
 
5913
            pix16 += 3;
 
5914
         }
 
5915
      }
 
5916
   }
 
5917
 
 
5918
   return pxm2;
 
5919
}
 
5920
 
 
5921
 
 
5922
//  rescale PXM pixmap to size ww2 x hh2
 
5923
 
 
5924
PXM * PXM_rescale(PXM *pxm1, int ww2, int hh2)
 
5925
{
 
5926
   void bmp8_rescale(uint8*, uint8*, int, int, int, int);
 
5927
   void bmp16_rescale(uint16*, uint16*, int, int, int, int);
 
5928
 
 
5929
   PXM      *pxm2;
 
5930
   int      ww1, hh1, bpc;
 
5931
   uint8    *bmp1, *bmp2;
 
5932
   uint16   *bmp3, *bmp4;
 
5933
 
 
5934
   ww1 = pxm1->ww;
 
5935
   hh1 = pxm1->hh;
 
5936
   bpc = pxm1->bpc;
 
5937
   
 
5938
   pxm2 = PXM_make(ww2,hh2,bpc);
 
5939
   
 
5940
   if (bpc == 8) {
 
5941
      bmp1 = (uint8 *) pxm1->bmp;
 
5942
      bmp2 = (uint8 *) pxm2->bmp;
 
5943
      bmp8_rescale(bmp1,bmp2,ww1,hh1,ww2,hh2);
 
5944
   }
 
5945
 
 
5946
   if (bpc == 16) {
 
5947
      bmp3 = (uint16 *) pxm1->bmp;
 
5948
      bmp4 = (uint16 *) pxm2->bmp;
 
5949
      bmp16_rescale(bmp3,bmp4,ww1,hh1,ww2,hh2);
 
5950
   }
 
5951
   
 
5952
   return pxm2;
 
5953
}
 
5954
 
 
5955
 
 
5956
//  Copy and rescale a modified area within a PXM-16 image into the
 
5957
//  corresponding area of a PXM-8 image previously rescaled from the 
 
5958
//  PXM-16 image. Keep the same mapping of input to output pixels, so 
 
5959
//  that the modified area fits seamlessly into the PXM-8 image. Used 
 
5960
//  when a section of an image is edited and the window image is updated.
 
5961
//   
 
5962
//  pxm1           PXM-16 image with changed area to rescale and copy
 
5963
//  pxm2           existing rescaled PXM-8 copy of pxm1
 
5964
//  org1x, org1y   pxm1 origin of area to copy (top left corner)
 
5965
//  ww1a, hh1a     width and height of area to copy                        //  new v.10.11
 
5966
 
 
5967
void PXM_update(PXM *pxm1, PXM *pxm2, int org1x, int org1y, int ww1a, int hh1a)
 
5968
{
 
5969
   uint16      *bmp1, *pix1;
 
5970
   uint8       *bmp2, *pix2;
 
5971
   int         ww1, hh1, ww2, hh2;
 
5972
   int         px1, py1, px2, py2;
 
5973
   int         pxl, pyl, pxm, pym, ii;
 
5974
   int         *px1L, *py1L;
 
5975
   int         maxmapx, maxmapy;
 
5976
   int         org2x, end2x, org2y, end2y;
 
5977
   float       scalex, scaley;
 
5978
   float       px1a, py1a, px1b, py1b;
 
5979
   float       fx, fy, ftot;
 
5980
   float       red, green, blue;
 
5981
   float       *pxmap, *pymap;
 
5982
 
 
5983
   bmp1 = (uint16 *) pxm1->bmp;   
 
5984
   bmp2 = (uint8 *) pxm2->bmp;   
 
5985
   ww1 = pxm1->ww;
 
5986
   hh1 = pxm1->hh;
 
5987
   ww2 = pxm2->ww;
 
5988
   hh2 = pxm2->hh;
 
5989
 
 
5990
   scalex = 1.0 * ww1 / ww2;                                               //  compute x and y scales
 
5991
   scaley = 1.0 * hh1 / hh2;
 
5992
   
 
5993
   if (scalex <= 1) maxmapx = 2;                                           //  compute max input pixels
 
5994
   else maxmapx = scalex + 2;                                              //    mapping into output pixels
 
5995
   maxmapx += 1;                                                           //      for both dimensions
 
5996
   if (scaley <= 1) maxmapy = 2;                                           //  (pixels may not be square)
 
5997
   else maxmapy = scaley + 2;
 
5998
   maxmapy += 1;
 
5999
   
 
6000
   pymap = (float *) zmalloc(hh2 * maxmapy * sizeof(float));               //  maps overlap of < maxmap input
 
6001
   pxmap = (float *) zmalloc(ww2 * maxmapx * sizeof(float));               //    pixels per output pixel
 
6002
 
 
6003
   py1L = (int *) zmalloc(hh2 * sizeof(int));                              //  maps first (lowest) input pixel
 
6004
   px1L = (int *) zmalloc(ww2 * sizeof(int));                              //    per output pixel
 
6005
 
 
6006
   for (py2 = 0; py2 < hh2; py2++)                                         //  loop output y-pixels
 
6007
   {
 
6008
      py1a = py2 * scaley;                                                 //  corresponding input y-pixels
 
6009
      py1b = py1a + scaley;
 
6010
      if (py1b >= hh1) py1b = hh1 - 0.001;                                 //  fix precision limitation
 
6011
      pyl = py1a;
 
6012
      py1L[py2] = pyl;                                                     //  1st overlapping input pixel
 
6013
 
 
6014
      for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++)                   //  loop overlapping input pixels
 
6015
      {
 
6016
         if (py1 < py1a) {                                                 //  compute amount of overlap
 
6017
            if (py1+1 < py1b) fy = py1+1 - py1a;                           //    0.0 to 1.0 
 
6018
            else fy = scaley;
 
6019
         }
 
6020
         else if (py1+1 > py1b) fy = py1b - py1;
 
6021
         else fy = 1;
 
6022
 
 
6023
         ii = py2 * maxmapy + pym;                                         //  save it
 
6024
         pymap[ii] = 0.9999 * fy / scaley;
 
6025
      }
 
6026
      ii = py2 * maxmapy + pym;                                            //  set an end marker after
 
6027
      pymap[ii] = -1;                                                      //    last overlapping pixel
 
6028
   }
 
6029
 
 
6030
   for (px2 = 0; px2 < ww2; px2++)                                         //  do same for x-pixels
 
6031
   {
 
6032
      px1a = px2 * scalex;
 
6033
      px1b = px1a + scalex;
 
6034
      if (px1b >= ww1) px1b = ww1 - 0.001;
 
6035
      pxl = px1a;
 
6036
      px1L[px2] = pxl;
 
6037
 
 
6038
      for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++)
 
6039
      {
 
6040
         if (px1 < px1a) {
 
6041
            if (px1+1 < px1b) fx = px1+1 - px1a;
 
6042
            else fx = scalex;
 
6043
         }
 
6044
         else if (px1+1 > px1b) fx = px1b - px1;
 
6045
         else fx = 1;
 
6046
 
 
6047
         ii = px2 * maxmapx + pxm;
 
6048
         pxmap[ii] = 0.9999 * fx / scalex;
 
6049
      }
 
6050
      ii = px2 * maxmapx + pxm;
 
6051
      pxmap[ii] = -1;
 
6052
   }
 
6053
 
 
6054
   org2x = org1x / scalex;                                                 //  compute output image rectangle
 
6055
   end2x = (org1x + ww1a) / scalex + 1;                                    //    containing any part of input area
 
6056
   if (org2x < 0) org2x = 0;                                               //       revised  v.10.12
 
6057
   if (end2x > ww2) end2x = ww2;
 
6058
 
 
6059
   org2y = org1y / scaley;
 
6060
   end2y = (org1y + hh1a) / scaley + 1;
 
6061
   if (org2y < 0) org2y = 0;
 
6062
   if (end2y > hh2) end2y = hh2;
 
6063
 
 
6064
   for (py2 = org2y; py2 < end2y; py2++)                                   //  loop output y-pixels
 
6065
   {
 
6066
      pyl = py1L[py2];                                                     //  corresp. 1st input y-pixel
 
6067
 
 
6068
      for (px2 = org2x; px2 < end2x; px2++)                                //  loop output x-pixels
 
6069
      {
 
6070
         pxl = px1L[px2];                                                  //  corresp. 1st input x-pixel
 
6071
 
 
6072
         red = green = blue = 0;                                           //  initz. output pixel
 
6073
 
 
6074
         for (py1 = pyl, pym = 0; ; py1++, pym++)                          //  loop overlapping input y-pixels
 
6075
         {
 
6076
            ii = py2 * maxmapy + pym;                                      //  get y-overlap
 
6077
            fy = pymap[ii];
 
6078
            if (fy < 0) break;                                             //  no more pixels
 
6079
 
 
6080
            for (px1 = pxl, pxm = 0; ; px1++, pxm++)                       //  loop overlapping input x-pixels
 
6081
            {
 
6082
               ii = px2 * maxmapx + pxm;                                   //  get x-overlap
 
6083
               fx = pxmap[ii];
 
6084
               if (fx < 0) break;                                          //  no more pixels
 
6085
 
 
6086
               ftot = fx * fy;                                             //  area overlap = x * y overlap
 
6087
               pix1 = bmp1 + (py1 * ww1 + px1) * 3;
 
6088
               red += pix1[0] * ftot;                                      //  add input pixel * overlap
 
6089
               green += pix1[1] * ftot;
 
6090
               blue += pix1[2] * ftot;
 
6091
            }
 
6092
 
 
6093
            pix2 = bmp2 + (py2 * ww2 + px2) * 3;                           //  save output pixel
 
6094
            pix2[0] = int(red) >> 8;
 
6095
            pix2[1] = int(green) >> 8;
 
6096
            pix2[2] = int(blue) >> 8;
 
6097
         }
 
6098
      }
 
6099
   }
 
6100
 
 
6101
   zfree(px1L);
 
6102
   zfree(py1L);
 
6103
   zfree(pxmap);
 
6104
   zfree(pymap);
 
6105
   return;
 
6106
}
 
6107
 
 
6108
 
 
6109
//  rotate PXM pixmap through given angle in degrees (+ = clockwise)
 
6110
 
 
6111
PXM * PXM_rotate(PXM *pxm1, double angle)
 
6112
{
 
6113
   PXM * PXM_rotate8(PXM *, double);
 
6114
   PXM * PXM_rotate16(PXM *, double);
 
6115
 
 
6116
   PXM      *pxm2 = 0;
 
6117
   int      bpc;
 
6118
   
 
6119
   bpc = pxm1->bpc;
 
6120
   if (bpc == 8) pxm2 = PXM_rotate8(pxm1,angle);
 
6121
   if (bpc == 16) pxm2 = PXM_rotate16(pxm1,angle);
 
6122
   return pxm2;
 
6123
}
 
6124
 
 
6125
 
 
6126
/**************************************************************************
 
6127
 
 
6128
   Rescale 8 bpc image (3 x 8 bits per color) to new width and height.
 
6129
   The scale ratios may be different for width and height.
 
6130
 
 
6131
   Method: 
 
6132
   The input and output images are overlayed, stretching or shrinking the
 
6133
   output pixels as needed. The contribution of each input pixel overlapping
 
6134
   an output pixel is proportional to the area of the output pixel covered by
 
6135
   the input pixel. The contributions of all overlaping input pixels are added.
 
6136
   The work is spread among Nwt threads to reduce the elapsed time on modern 
 
6137
   computers having multiple SMP processors.
 
6138
 
 
6139
   Example: if the output image is 40% of the input image, then:
 
6140
     outpix[0,0] = 0.16 * inpix[0,0] + 0.16 * inpix[1,0] + 0.08 * inpix[2,0]
 
6141
                 + 0.16 * inpix[0,1] + 0.16 * inpix[1,1] + 0.08 * inpix[2,1]
 
6142
                 + 0.08 * inpix[0,2] + 0.08 * inpix[1,2] + 0.04 * inpix[2,2]
 
6143
 
 
6144
*********/
 
6145
 
 
6146
namespace bmp8rescale {                                                    //  data for threads
 
6147
   uint8    *pixmap1;
 
6148
   uint8    *pixmap2;
 
6149
   int      ww1;
 
6150
   int      hh1;
 
6151
   int      ww2;
 
6152
   int      hh2;
 
6153
   int      *px1L;
 
6154
   int      *py1L;
 
6155
   double   *pxmap;
 
6156
   double   *pymap;
 
6157
   int      maxmapx;
 
6158
   int      maxmapy;
 
6159
   int      busy[max_threads];
 
6160
}
 
6161
 
 
6162
 
 
6163
void bmp8_rescale(uint8 *pixmap1x, uint8 *pixmap2x, int ww1x, int hh1x, int ww2x, int hh2x)
 
6164
{
 
6165
   using namespace bmp8rescale;
 
6166
 
 
6167
   void * bmp8_rescale_thread(void *arg);
 
6168
 
 
6169
   int         px1, py1, px2, py2;
 
6170
   int         pxl, pyl, pxm, pym, ii;
 
6171
   double      scalex, scaley;
 
6172
   double      px1a, py1a, px1b, py1b;
 
6173
   double      fx, fy;
 
6174
   
 
6175
   pixmap1 = pixmap1x;
 
6176
   pixmap2 = pixmap2x;
 
6177
   ww1 = ww1x;
 
6178
   hh1 = hh1x;
 
6179
   ww2 = ww2x;
 
6180
   hh2 = hh2x;
 
6181
 
 
6182
   memset(pixmap2, 0, ww2 * hh2 * 3 * sizeof(uint8));                      //  clear output pixmap
 
6183
 
 
6184
   scalex = 1.0 * ww1 / ww2;                                               //  compute x and y scales
 
6185
   scaley = 1.0 * hh1 / hh2;
 
6186
   
 
6187
   if (scalex <= 1) maxmapx = 2;                                           //  compute max input pixels
 
6188
   else maxmapx = scalex + 2;                                              //    mapping into output pixels
 
6189
   maxmapx += 1;                                                           //      for both dimensions
 
6190
   if (scaley <= 1) maxmapy = 2;                                           //  (pixels may not be square)
 
6191
   else maxmapy = scaley + 2;
 
6192
   maxmapy += 1;
 
6193
   
 
6194
   pymap = (double *) zmalloc(hh2 * maxmapy * sizeof(double));             //  maps overlap of < maxmap input
 
6195
   pxmap = (double *) zmalloc(ww2 * maxmapx * sizeof(double));             //    pixels per output pixel
 
6196
 
 
6197
   py1L = (int *) zmalloc(hh2 * sizeof(int));                              //  maps first (lowest) input pixel
 
6198
   px1L = (int *) zmalloc(ww2 * sizeof(int));                              //    per output pixel
 
6199
 
 
6200
   for (py2 = 0; py2 < hh2; py2++)                                         //  loop output y-pixels
 
6201
   {
 
6202
      py1a = py2 * scaley;                                                 //  corresponding input y-pixels
 
6203
      py1b = py1a + scaley;
 
6204
      if (py1b >= hh1) py1b = hh1 - 0.001;                                 //  fix precision limitation
 
6205
      pyl = py1a;
 
6206
      py1L[py2] = pyl;                                                     //  1st overlapping input pixel
 
6207
 
 
6208
      for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++)                   //  loop overlapping input pixels
 
6209
      {
 
6210
         if (py1 < py1a) {                                                 //  compute amount of overlap
 
6211
            if (py1+1 < py1b) fy = py1+1 - py1a;                           //    0.0 to 1.0 
 
6212
            else fy = scaley;
 
6213
         }
 
6214
         else if (py1+1 > py1b) fy = py1b - py1;
 
6215
         else fy = 1;
 
6216
 
 
6217
         ii = py2 * maxmapy + pym;                                         //  save it
 
6218
         pymap[ii] = 0.9999 * fy / scaley;
 
6219
      }
 
6220
      ii = py2 * maxmapy + pym;                                            //  set an end marker after
 
6221
      pymap[ii] = -1;                                                      //    last overlapping pixel
 
6222
   }
 
6223
   
 
6224
   for (px2 = 0; px2 < ww2; px2++)                                         //  do same for x-pixels
 
6225
   {
 
6226
      px1a = px2 * scalex;
 
6227
      px1b = px1a + scalex;
 
6228
      if (px1b >= ww1) px1b = ww1 - 0.001;
 
6229
      pxl = px1a;
 
6230
      px1L[px2] = pxl;
 
6231
 
 
6232
      for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++)
 
6233
      {
 
6234
         if (px1 < px1a) {
 
6235
            if (px1+1 < px1b) fx = px1+1 - px1a;
 
6236
            else fx = scalex;
 
6237
         }
 
6238
         else if (px1+1 > px1b) fx = px1b - px1;
 
6239
         else fx = 1;
 
6240
 
 
6241
         ii = px2 * maxmapx + pxm;
 
6242
         pxmap[ii] = 0.9999 * fx / scalex;
 
6243
      }
 
6244
      ii = px2 * maxmapx + pxm;
 
6245
      pxmap[ii] = -1;
 
6246
   }
 
6247
 
 
6248
   for (ii = 0; ii < Nwt; ii++) {                                          //  start working threads
 
6249
      busy[ii] = 1;
 
6250
      start_detached_thread(bmp8_rescale_thread,&wtnx[ii]);
 
6251
   }
 
6252
   
 
6253
   for (ii = 0; ii < Nwt; ii++)                                            //  wait for all done
 
6254
      while (busy[ii]) zsleep(0.004);
 
6255
 
 
6256
   zfree(px1L);
 
6257
   zfree(py1L);
 
6258
   zfree(pxmap);
 
6259
   zfree(pymap);
 
6260
   return;
 
6261
}
 
6262
 
 
6263
 
 
6264
void * bmp8_rescale_thread(void *arg)                                      //  worker thread function
 
6265
{
 
6266
   using namespace bmp8rescale;
 
6267
 
 
6268
   int         index = *((int *) arg);
 
6269
   int         px1, py1, px2, py2;
 
6270
   int         pxl, pyl, pxm, pym, ii;
 
6271
   uint8       *pixel1, *pixel2;
 
6272
   double      fx, fy, ftot;
 
6273
   double      red, green, blue;
 
6274
 
 
6275
   for (py2 = index; py2 < hh2; py2 += Nwt)                                //  loop output y-pixels
 
6276
   {
 
6277
      pyl = py1L[py2];                                                     //  corresp. 1st input y-pixel
 
6278
 
 
6279
      for (px2 = 0; px2 < ww2; px2++)                                      //  loop output x-pixels
 
6280
      {
 
6281
         pxl = px1L[px2];                                                  //  corresp. 1st input x-pixel
 
6282
 
 
6283
         red = green = blue = 0;                                           //  initz. output pixel
 
6284
 
 
6285
         for (py1 = pyl, pym = 0; ; py1++, pym++)                          //  loop overlapping input y-pixels
 
6286
         {
 
6287
            ii = py2 * maxmapy + pym;                                      //  get y-overlap
 
6288
            fy = pymap[ii];
 
6289
            if (fy < 0) break;                                             //  no more pixels
 
6290
 
 
6291
            for (px1 = pxl, pxm = 0; ; px1++, pxm++)                       //  loop overlapping input x-pixels
 
6292
            {
 
6293
               ii = px2 * maxmapx + pxm;                                   //  get x-overlap
 
6294
               fx = pxmap[ii];
 
6295
               if (fx < 0) break;                                          //  no more pixels
 
6296
 
 
6297
               ftot = fx * fy;                                             //  area overlap = x * y overlap
 
6298
               pixel1 = pixmap1 + (py1 * ww1 + px1) * 3;
 
6299
               red += pixel1[0] * ftot;                                    //  add input pixel * overlap
 
6300
               green += pixel1[1] * ftot;
 
6301
               blue += pixel1[2] * ftot;
 
6302
            }
 
6303
 
 
6304
            pixel2 = pixmap2 + (py2 * ww2 + px2) * 3;                      //  save output pixel
 
6305
            pixel2[0] = red;
 
6306
            pixel2[1] = green;
 
6307
            pixel2[2] = blue;
 
6308
         }
 
6309
      }
 
6310
   }
 
6311
 
 
6312
   busy[index] = 0;
 
6313
   return 0;
 
6314
}
 
6315
 
 
6316
 
 
6317
/**************************************************************************
 
6318
 
 
6319
   Rescale 16 bpc image (3 x 16 bits per color) to new width and height.
 
6320
   Identical to bmp8_rescale except for the following: 
 
6321
      uint8 >> uint16
 
6322
      xxx8 >> xxx16
 
6323
 
 
6324
*******/
 
6325
 
 
6326
namespace bmp16rescale {                                                   //  data for threads
 
6327
   uint16   *pixmap1;
 
6328
   uint16   *pixmap2;
 
6329
   int      ww1;
 
6330
   int      hh1;
 
6331
   int      ww2;
 
6332
   int      hh2;
 
6333
   int      *px1L;
 
6334
   int      *py1L;
 
6335
   double   *pxmap;
 
6336
   double   *pymap;
 
6337
   int      maxmapx;
 
6338
   int      maxmapy;
 
6339
   int      busy[max_threads];
 
6340
}
 
6341
 
 
6342
 
 
6343
void bmp16_rescale(uint16 *pixmap1x, uint16 *pixmap2x, int ww1x, int hh1x, int ww2x, int hh2x)
 
6344
{
 
6345
   using namespace bmp16rescale;
 
6346
 
 
6347
   void * bmp16_rescale_thread(void *arg);
 
6348
 
 
6349
   int         px1, py1, px2, py2;
 
6350
   int         pxl, pyl, pxm, pym, ii;
 
6351
   double      scalex, scaley;
 
6352
   double      px1a, py1a, px1b, py1b;
 
6353
   double      fx, fy;
 
6354
   
 
6355
   pixmap1 = pixmap1x;
 
6356
   pixmap2 = pixmap2x;
 
6357
   ww1 = ww1x;
 
6358
   hh1 = hh1x;
 
6359
   ww2 = ww2x;
 
6360
   hh2 = hh2x;
 
6361
 
 
6362
   memset(pixmap2, 0, ww2 * hh2 * 3 * sizeof(uint16));                     //  clear output pixmap
 
6363
 
 
6364
   scalex = 1.0 * ww1 / ww2;                                               //  compute x and y scales
 
6365
   scaley = 1.0 * hh1 / hh2;
 
6366
   
 
6367
   if (scalex <= 1) maxmapx = 2;                                           //  compute max input pixels
 
6368
   else maxmapx = scalex + 2;                                              //    mapping into output pixels
 
6369
   maxmapx += 1;                                                           //      for both dimensions
 
6370
   if (scaley <= 1) maxmapy = 2;                                           //  (pixels may not be square)
 
6371
   else maxmapy = scaley + 2;
 
6372
   maxmapy += 1;
 
6373
   
 
6374
   pymap = (double *) zmalloc(hh2 * maxmapy * sizeof(double));             //  maps overlap of < maxmap input
 
6375
   pxmap = (double *) zmalloc(ww2 * maxmapx * sizeof(double));             //    pixels per output pixel
 
6376
 
 
6377
   py1L = (int *) zmalloc(hh2 * sizeof(int));                              //  maps first (lowest) input pixel
 
6378
   px1L = (int *) zmalloc(ww2 * sizeof(int));                              //    per output pixel
 
6379
 
 
6380
   for (py2 = 0; py2 < hh2; py2++)                                         //  loop output y-pixels
 
6381
   {
 
6382
      py1a = py2 * scaley;                                                 //  corresponding input y-pixels
 
6383
      py1b = py1a + scaley;
 
6384
      if (py1b >= hh1) py1b = hh1 - 0.001;                                 //  fix precision limitation
 
6385
      pyl = py1a;
 
6386
      py1L[py2] = pyl;                                                     //  1st overlapping input pixel
 
6387
 
 
6388
      for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++)                   //  loop overlapping input pixels
 
6389
      {
 
6390
         if (py1 < py1a) {                                                 //  compute amount of overlap
 
6391
            if (py1+1 < py1b) fy = py1+1 - py1a;                           //    0.0 to 1.0 
 
6392
            else fy = scaley;
 
6393
         }
 
6394
         else if (py1+1 > py1b) fy = py1b - py1;
 
6395
         else fy = 1;
 
6396
 
 
6397
         ii = py2 * maxmapy + pym;                                         //  save it
 
6398
         pymap[ii] = 0.9999 * fy / scaley;
 
6399
      }
 
6400
      ii = py2 * maxmapy + pym;                                            //  set an end marker after
 
6401
      pymap[ii] = -1;                                                      //    last overlapping pixel
 
6402
   }
 
6403
   
 
6404
   for (px2 = 0; px2 < ww2; px2++)                                         //  do same for x-pixels
 
6405
   {
 
6406
      px1a = px2 * scalex;
 
6407
      px1b = px1a + scalex;
 
6408
      if (px1b >= ww1) px1b = ww1 - 0.001;
 
6409
      pxl = px1a;
 
6410
      px1L[px2] = pxl;
 
6411
 
 
6412
      for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++)
 
6413
      {
 
6414
         if (px1 < px1a) {
 
6415
            if (px1+1 < px1b) fx = px1+1 - px1a;
 
6416
            else fx = scalex;
 
6417
         }
 
6418
         else if (px1+1 > px1b) fx = px1b - px1;
 
6419
         else fx = 1;
 
6420
 
 
6421
         ii = px2 * maxmapx + pxm;
 
6422
         pxmap[ii] = 0.9999 * fx / scalex;
 
6423
      }
 
6424
      ii = px2 * maxmapx + pxm;
 
6425
      pxmap[ii] = -1;
 
6426
   }
 
6427
 
 
6428
   for (ii = 0; ii < Nwt; ii++) {                                          //  start working threads
 
6429
      busy[ii] = 1;
 
6430
      start_detached_thread(bmp16_rescale_thread,&wtnx[ii]);
 
6431
   }
 
6432
   
 
6433
   for (ii = 0; ii < Nwt; ii++)                                            //  wait for all done
 
6434
      while (busy[ii]) zsleep(0.004);
 
6435
 
 
6436
   zfree(px1L);
 
6437
   zfree(py1L);
 
6438
   zfree(pxmap);
 
6439
   zfree(pymap);
 
6440
   return;
 
6441
}
 
6442
 
 
6443
 
 
6444
void * bmp16_rescale_thread(void *arg)                                     //  worker thread function
 
6445
{
 
6446
   using namespace bmp16rescale;
 
6447
 
 
6448
   int         index = *((int *) arg);
 
6449
   int         px1, py1, px2, py2;
 
6450
   int         pxl, pyl, pxm, pym, ii;
 
6451
   uint16      *pixel1, *pixel2;
 
6452
   double      fx, fy, ftot;
 
6453
   double      red, green, blue;
 
6454
 
 
6455
   for (py2 = index; py2 < hh2; py2 += Nwt)                                //  loop output y-pixels
 
6456
   {
 
6457
      pyl = py1L[py2];                                                     //  corresp. 1st input y-pixel
 
6458
 
 
6459
      for (px2 = 0; px2 < ww2; px2++)                                      //  loop output x-pixels
 
6460
      {
 
6461
         pxl = px1L[px2];                                                  //  corresp. 1st input x-pixel
 
6462
 
 
6463
         red = green = blue = 0;                                           //  initz. output pixel
 
6464
 
 
6465
         for (py1 = pyl, pym = 0; ; py1++, pym++)                          //  loop overlapping input y-pixels
 
6466
         {
 
6467
            ii = py2 * maxmapy + pym;                                      //  get y-overlap
 
6468
            fy = pymap[ii];
 
6469
            if (fy < 0) break;                                             //  no more pixels
 
6470
 
 
6471
            for (px1 = pxl, pxm = 0; ; px1++, pxm++)                       //  loop overlapping input x-pixels
 
6472
            {
 
6473
               ii = px2 * maxmapx + pxm;                                   //  get x-overlap
 
6474
               fx = pxmap[ii];
 
6475
               if (fx < 0) break;                                          //  no more pixels
 
6476
 
 
6477
               ftot = fx * fy;                                             //  area overlap = x * y overlap
 
6478
               pixel1 = pixmap1 + (py1 * ww1 + px1) * 3;
 
6479
               red += pixel1[0] * ftot;                                    //  add input pixel * overlap
 
6480
               green += pixel1[1] * ftot;
 
6481
               blue += pixel1[2] * ftot;
 
6482
            }
 
6483
 
 
6484
            pixel2 = pixmap2 + (py2 * ww2 + px2) * 3;                      //  save output pixel
 
6485
            pixel2[0] = red;
 
6486
            pixel2[1] = green;
 
6487
            pixel2[2] = blue;
 
6488
         }
 
6489
      }
 
6490
   }
 
6491
 
 
6492
   busy[index] = 0;
 
6493
   return 0;
 
6494
}
 
6495
 
 
6496
 
 
6497
/**************************************************************************
 
6498
 
 
6499
      PXM *pxm2 = PXM_rotate8(PXM *pxm1, double angle)
 
6500
 
 
6501
      Rotate PXM-8 pixmap through an arbitrary angle (degrees).
 
6502
 
 
6503
      The returned image has the same size as the original, but the
 
6504
      pixmap size is increased to accomodate the rotated image.
 
6505
      (e.g. a 100x100 image rotated 45 deg. needs a 142x142 pixmap).
 
6506
      The parameters ww and hh are the dimensions of the input
 
6507
      pixmap, and are updated to the dimensions of the output pixmap.
 
6508
 
 
6509
      The space added around the rotated image is black (RGB 0,0,0).
 
6510
      Angle is in degrees. Positive direction is clockwise.
 
6511
      Speed is about 3 million pixels/sec/thread for a 2.4 GHz CPU.
 
6512
      Loss of resolution is less than 1 pixel.
 
6513
      
 
6514
      Work is divided among Nwt threads to gain speed.
 
6515
      
 
6516
      v.9.3: affine transform instead of trig functions, for speed
 
6517
 
 
6518
***************************************************************************/
 
6519
 
 
6520
namespace rotpxm8 {
 
6521
   int      busy = 0;
 
6522
   uint8    *pixmap1;
 
6523
   uint8    *pixmap2;
 
6524
   int      ww1;
 
6525
   int      hh1;
 
6526
   int      ww2;
 
6527
   int      hh2;
 
6528
   double   angle;
 
6529
}
 
6530
 
 
6531
 
 
6532
PXM * PXM_rotate8(PXM *pxm1, double anglex)
 
6533
{
 
6534
   using namespace rotpxm8;
 
6535
 
 
6536
   void     *PXM_rotate8_thread(void *);
 
6537
 
 
6538
   int      cc, ii;
 
6539
   PXM      *pxm2;
 
6540
   
 
6541
   ww1 = pxm1->ww;
 
6542
   hh1 = pxm1->hh;
 
6543
   pixmap1 = (uint8 *) pxm1->bmp;
 
6544
   angle = anglex;
 
6545
 
 
6546
   while (angle < -180) angle += 360;                                      //  normalize, -180 to +180
 
6547
   while (angle > 180) angle -= 360;
 
6548
   angle = angle * pi / 180;                                               //  radians, -pi to +pi
 
6549
   
 
6550
   if (fabs(angle) < 0.001) {                                              //  angle = 0 within my precision
 
6551
      pxm2 = PXM_make(ww1,hh1,8);                                          //  return a copy of the input
 
6552
      pixmap2 = (uint8 *) pxm2->bmp;
 
6553
      cc = ww1 * hh1 * 3 * sizeof(uint8);
 
6554
      memcpy(pixmap2,pixmap1,cc);
 
6555
      return pxm2;
 
6556
   }
 
6557
 
 
6558
   ww2 = ww1*fabs(cos(angle)) + hh1*fabs(sin(angle));                      //  rectangle containing rotated image
 
6559
   hh2 = ww1*fabs(sin(angle)) + hh1*fabs(cos(angle));
 
6560
   
 
6561
   pxm2 = PXM_make(ww2,hh2,8);
 
6562
   pixmap2 = (uint8 *) pxm2->bmp;
 
6563
 
 
6564
   for (ii = 0; ii < Nwt; ii++)                                            //  start worker threads
 
6565
      start_detached_thread(PXM_rotate8_thread,&wtnx[ii]);
 
6566
   zadd_locked(busy,+Nwt);
 
6567
 
 
6568
   while (busy) zsleep(0.004);                                             //  wait for completion
 
6569
   return pxm2;
 
6570
}
 
6571
 
 
6572
 
 
6573
void * PXM_rotate8_thread(void *arg)
 
6574
{
 
6575
   using namespace rotpxm8;
 
6576
 
 
6577
   int      index = *((int *) (arg));
 
6578
   int      px2, py2, px0, py0;
 
6579
   uint8    *pix0, *pix1, *pix2, *pix3;
 
6580
   double   px1, py1;
 
6581
   double   f0, f1, f2, f3, red, green, blue;
 
6582
   double   a, b, d, e, ww15, hh15, ww25, hh25;
 
6583
 
 
6584
   ww15 = 0.5 * ww1;
 
6585
   hh15 = 0.5 * hh1;
 
6586
   ww25 = 0.5 * ww2;
 
6587
   hh25 = 0.5 * hh2;
 
6588
 
 
6589
   a = cos(angle);
 
6590
   b = sin(angle);
 
6591
   d = - sin(angle);
 
6592
   e = cos(angle);
 
6593
   
 
6594
   for (py2 = index; py2 < hh2; py2 += Nwt)                                //  loop through output pixels
 
6595
   for (px2 = 0; px2 < ww2; px2++)                                         //  outer loop y
 
6596
   {
 
6597
      px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15;                    //  (px1,py1) = corresponding    v.9.3
 
6598
      py1 = d * (px2 - ww25) + e * (py2 - hh25) + hh15;                    //    point within input pixels
 
6599
 
 
6600
      px0 = px1;                                                           //  pixel containing (px1,py1)
 
6601
      py0 = py1;
 
6602
      
 
6603
      if (px1 < 0 || px0 >= ww1-1 || py1 < 0 || py0 >= hh1-1) {            //  if outside input pixel array
 
6604
         pix2 = pixmap2 + (py2 * ww2 + px2) * 3;                           //    output is black
 
6605
         pix2[0] = pix2[1] = pix2[2] = 0;
 
6606
         continue;
 
6607
      }
 
6608
 
 
6609
      pix0 = pixmap1 + (py0 * ww1 + px0) * 3;                              //  4 input pixels based at (px0,py0)
 
6610
      pix1 = pix0 + ww1 * 3;
 
6611
      pix2 = pix0 + 3;
 
6612
      pix3 = pix1 + 3;
 
6613
 
 
6614
      f0 = (px0+1 - px1) * (py0+1 - py1);                                  //  overlap of (px1,py1)
 
6615
      f1 = (px0+1 - px1) * (py1 - py0);                                    //    in each of the 4 pixels
 
6616
      f2 = (px1 - px0) * (py0+1 - py1);
 
6617
      f3 = (px1 - px0) * (py1 - py0);
 
6618
   
 
6619
      red =   f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0];   //  sum the weighted inputs
 
6620
      green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1];
 
6621
      blue =  f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2];
 
6622
      
 
6623
      pix2 = pixmap2 + (py2 * ww2 + px2) * 3;                              //  output pixel
 
6624
      pix2[0] = red;
 
6625
      pix2[1] = green;
 
6626
      pix2[2] = blue;
 
6627
   }
 
6628
   
 
6629
   zadd_locked(busy,-1);
 
6630
   return 0;
 
6631
}
 
6632
 
 
6633
 
 
6634
/**************************************************************************
 
6635
 
 
6636
   PXM *pxm2 = PXM_rotate16(PXM *pxm1, double angle)
 
6637
   Rotate PXM-16 pixmap through an arbitrary angle (degrees).
 
6638
   Identical to PXM_rotate8() except for:
 
6639
      uint8 >> uint16
 
6640
      rotpxm8 >> rotpxm16
 
6641
      8 >> 16   
 
6642
 
 
6643
**********/
 
6644
 
 
6645
namespace rotpxm16 {
 
6646
   int      busy = 0;
 
6647
   uint16   *pixmap1;
 
6648
   uint16   *pixmap2;
 
6649
   int      ww1;
 
6650
   int      hh1;
 
6651
   int      ww2;
 
6652
   int      hh2;
 
6653
   double   angle;
 
6654
}
 
6655
 
 
6656
 
 
6657
PXM * PXM_rotate16(PXM *pxm1, double anglex)
 
6658
{
 
6659
   using namespace rotpxm16;
 
6660
 
 
6661
   void     *PXM_rotate16_thread(void *);
 
6662
 
 
6663
   int      cc, ii;
 
6664
   PXM      *pxm2;
 
6665
   
 
6666
   ww1 = pxm1->ww;
 
6667
   hh1 = pxm1->hh;
 
6668
   pixmap1 = (uint16 *) pxm1->bmp;
 
6669
   angle = anglex;
 
6670
 
 
6671
   while (angle < -180) angle += 360;                                      //  normalize, -180 to +180
 
6672
   while (angle > 180) angle -= 360;
 
6673
   angle = angle * pi / 180;                                               //  radians, -pi to +pi
 
6674
   
 
6675
   if (fabs(angle) < 0.001) {                                              //  angle = 0 within my precision
 
6676
      pxm2 = PXM_make(ww1,hh1,16);                                         //  return a copy of the input
 
6677
      pixmap2 = (uint16 *) pxm2->bmp;
 
6678
      cc = ww1 * hh1 * 3 * sizeof(uint16);
 
6679
      memcpy(pixmap2,pixmap1,cc);
 
6680
      return pxm2;
 
6681
   }
 
6682
 
 
6683
   ww2 = ww1*fabs(cos(angle)) + hh1*fabs(sin(angle));                      //  rectangle containing rotated image
 
6684
   hh2 = ww1*fabs(sin(angle)) + hh1*fabs(cos(angle));
 
6685
   
 
6686
   pxm2 = PXM_make(ww2,hh2,16);
 
6687
   pixmap2 = (uint16 *) pxm2->bmp;
 
6688
 
 
6689
   for (ii = 0; ii < Nwt; ii++)                                            //  start worker threads
 
6690
      start_detached_thread(PXM_rotate16_thread,&wtnx[ii]);
 
6691
   zadd_locked(busy,+Nwt);
 
6692
 
 
6693
   while (busy) zsleep(0.004);                                             //  wait for completion
 
6694
   return pxm2;
 
6695
}
 
6696
 
 
6697
 
 
6698
void * PXM_rotate16_thread(void *arg)
 
6699
{
 
6700
   using namespace rotpxm16;
 
6701
 
 
6702
   int      index = *((int *) (arg));
 
6703
   int      px2, py2, px0, py0;
 
6704
   uint16   *pix0, *pix1, *pix2, *pix3;
 
6705
   double   px1, py1;
 
6706
   double   f0, f1, f2, f3, red, green, blue;
 
6707
   double   a, b, d, e, ww15, hh15, ww25, hh25;
 
6708
 
 
6709
   ww15 = 0.5 * ww1;
 
6710
   hh15 = 0.5 * hh1;
 
6711
   ww25 = 0.5 * ww2;
 
6712
   hh25 = 0.5 * hh2;
 
6713
 
 
6714
   a = cos(angle);
 
6715
   b = sin(angle);
 
6716
   d = - sin(angle);
 
6717
   e = cos(angle);
 
6718
   
 
6719
   for (py2 = index; py2 < hh2; py2 += Nwt)                                //  loop through output pixels
 
6720
   for (px2 = 0; px2 < ww2; px2++)                                         //  outer loop y
 
6721
   {
 
6722
      px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15;                    //  (px1,py1) = corresponding    v.9.3
 
6723
      py1 = d * (px2 - ww25) + e * (py2 - hh25) + hh15;                    //    point within input pixels
 
6724
 
 
6725
      px0 = px1;                                                           //  pixel containing (px1,py1)
 
6726
      py0 = py1;
 
6727
      
 
6728
      if (px1 < 0 || px0 >= ww1-1 || py1 < 0 || py0 >= hh1-1) {            //  if outside input pixel array
 
6729
         pix2 = pixmap2 + (py2 * ww2 + px2) * 3;                           //    output is black
 
6730
         pix2[0] = pix2[1] = pix2[2] = 0;
 
6731
         continue;
 
6732
      }
 
6733
 
 
6734
      pix0 = pixmap1 + (py0 * ww1 + px0) * 3;                              //  4 input pixels based at (px0,py0)
 
6735
      pix1 = pix0 + ww1 * 3;
 
6736
      pix2 = pix0 + 3;
 
6737
      pix3 = pix1 + 3;
 
6738
 
 
6739
      f0 = (px0+1 - px1) * (py0+1 - py1);                                  //  overlap of (px1,py1)
 
6740
      f1 = (px0+1 - px1) * (py1 - py0);                                    //    in each of the 4 pixels
 
6741
      f2 = (px1 - px0) * (py0+1 - py1);
 
6742
      f3 = (px1 - px0) * (py1 - py0);
 
6743
   
 
6744
      red =   f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0];   //  sum the weighted inputs
 
6745
      green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1];
 
6746
      blue =  f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2];
 
6747
      
 
6748
      pix2 = pixmap2 + (py2 * ww2 + px2) * 3;                              //  output pixel
 
6749
      pix2[0] = red;
 
6750
      pix2[1] = green;
 
6751
      pix2[2] = blue;
 
6752
   }
 
6753
   
 
6754
   zadd_locked(busy,-1);
 
6755
   return 0;
 
6756
}
 
6757
 
 
6758
 
 
6759
//  replace blue = 0 pixels with blue = 2 in a 16-bit pixmap
 
6760
//  (blue = 0 reserved for pixels voided by warp or overlay offsets)
 
6761
//  all callers of vpixel() need to do this
 
6762
//  needs about 0.013 seconds for 10 megapixel image and 3.3 GHz processor
 
6763
 
 
6764
void PXM_fixblue(PXM *pxm)                                                 //  v.11.07
 
6765
{
 
6766
   int      size;
 
6767
   uint16   *pixel, *pixel0, *pixelN;
 
6768
 
 
6769
   size = pxm->ww * pxm->hh * 3;
 
6770
   pixel0 = (uint16 *) pxm->bmp + 2;
 
6771
   pixelN = (uint16 *) pixel0 + size;
 
6772
 
 
6773
   for (pixel = pixel0; pixel < pixelN; pixel += 3)                        //  +3 is +6 bytes
 
6774
      if (! *pixel) *pixel = 2;
 
6775
 
 
6776
   return;
 
6777
}
 
6778
 
 
6779
 
 
6780