1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/PTdecode/CImg-1.3.0/examples/gmic4gimp.cpp Mon Aug 03 14:09:20 2009 +0100 1.3 @@ -0,0 +1,1132 @@ 1.4 +/* 1.5 + # 1.6 + # File : gmic4gimp.cpp 1.7 + # ( C++ source file ) 1.8 + # 1.9 + # Description : G'MIC for GIMP - A plug-in to allow the use 1.10 + # of G'MIC commands in GIMP. 1.11 + # This file is a part of the CImg Library project. 1.12 + # ( http://cimg.sourceforge.net ) 1.13 + # 1.14 + # Copyright : David Tschumperle (GREYCstoration API) 1.15 + # 1.16 + # License : CeCILL v2.0 1.17 + # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) 1.18 + # 1.19 + # This software is governed by the CeCILL license under French law and 1.20 + # abiding by the rules of distribution of free software. You can use, 1.21 + # modify and/ or redistribute the software under the terms of the CeCILL 1.22 + # license as circulated by CEA, CNRS and INRIA at the following URL 1.23 + # "http://www.cecill.info". 1.24 + # 1.25 + # As a counterpart to the access to the source code and rights to copy, 1.26 + # modify and redistribute granted by the license, users are provided only 1.27 + # with a limited warranty and the software's author, the holder of the 1.28 + # economic rights, and the successive licensors have only limited 1.29 + # liability. 1.30 + # 1.31 + # In this respect, the user's attention is drawn to the risks associated 1.32 + # with loading, using, modifying and/or developing or reproducing the 1.33 + # software by the user in light of its specific status of free software, 1.34 + # that may mean that it is complicated to manipulate, and that also 1.35 + # therefore means that it is reserved for developers and experienced 1.36 + # professionals having in-depth computer knowledge. Users are therefore 1.37 + # encouraged to load and test the software's suitability as regards their 1.38 + # requirements in conditions enabling the security of their systems and/or 1.39 + # data to be ensured and, more generally, to use and operate it in the 1.40 + # same conditions as regards security. 1.41 + # 1.42 + # The fact that you are presently reading this means that you have had 1.43 + # knowledge of the CeCILL license and that you accept its terms. 1.44 + # 1.45 +*/ 1.46 + 1.47 +// Include necessary header files. 1.48 +//-------------------------------- 1.49 +#define cimg_display_type 0 1.50 +#include "gmic.h" 1.51 +#include "gmic4gimp_def.h" 1.52 +#include <pthread.h> 1.53 +#include <locale> 1.54 +#include <gtk/gtk.h> 1.55 +#include <libgimp/gimp.h> 1.56 +#include <libgimp/gimpui.h> 1.57 +using namespace cimg_library; 1.58 + 1.59 +// Define plug-in global variables. 1.60 +//--------------------------------- 1.61 +CImgList<char> gmic_entries; // The list of recognized G'MIC menu entries (stored as 'char*' strings). 1.62 +CImgList<char> gmic_commands; // The list of corresponding G'MIC commands to process the image. 1.63 +CImgList<char> gmic_preview_commands; // The list of corresponding G'MIC commands to preview the image. 1.64 +CImgList<char> gmic_arguments; // The list of corresponding needed filter arguments. 1.65 +char *gmic_macros; // The array of customized G'MIC macros. 1.66 +GtkTreeStore *filter_store; // A list of available filter entries (used by the GtkTreeView). 1.67 +bool return_create_dialog; // Return value of the 'create_gui_dialog()' function (set by events handlers). 1.68 +void **event_infos; // Infos that are passed to the GUI callback functions. 1.69 +char *path_home; // The path where configuration files are looked for. 1.70 + 1.71 +// Replace '[]' by '()' in a C-string. 1.72 +//------------------------------------ 1.73 +void strparenthesis(char *const s) { 1.74 + for (char *ns = s; *ns; ++ns) if (*ns=='[') *ns = '('; else if (*ns==']') *ns = ')'; 1.75 +} 1.76 + 1.77 +// Set/get plug-in global variables in GIMP. 1.78 +//------------------------------------------ 1.79 +void set_current_filter(const unsigned int current_filter) { 1.80 + const unsigned int ncurrent_filter = current_filter>gmic_entries.size?0:current_filter; 1.81 + gimp_set_data("gmic_current_filter",&ncurrent_filter,sizeof(unsigned int)); 1.82 +} 1.83 + 1.84 +unsigned int get_current_filter() { 1.85 + unsigned int current_filter = 0; 1.86 + gimp_get_data("gmic_current_filter",¤t_filter); 1.87 + if (current_filter>gmic_entries.size) current_filter = 0; 1.88 + return current_filter; 1.89 +} 1.90 + 1.91 +void set_filter_nbparams(const unsigned int filter, const unsigned int nbparams) { 1.92 + char s_tmp[256] = { 0 }; 1.93 + std::sprintf(s_tmp,"gmic_filter%u_nbparams",filter); 1.94 + gimp_set_data(s_tmp,&nbparams,sizeof(unsigned int)); 1.95 +} 1.96 + 1.97 +unsigned int get_filter_nbparams(const unsigned int filter) { 1.98 + char s_tmp[256] = { 0 }; 1.99 + std::sprintf(s_tmp,"gmic_filter%u_nbparams",filter); 1.100 + unsigned int nbparams = 0; 1.101 + gimp_get_data(s_tmp,&nbparams); 1.102 + return nbparams; 1.103 +} 1.104 + 1.105 +void set_filter_parameter(const unsigned int filter, const unsigned int n, const char *const param) { 1.106 + char s_tmp[256] = { 0 }; 1.107 + std::sprintf(s_tmp,"gmic_filter%u_parameter%u",filter,n); 1.108 + gimp_set_data(s_tmp,param,cimg::strlen(param)+1); 1.109 +} 1.110 + 1.111 +const char *get_filter_parameter(const unsigned int filter, const unsigned int n) { 1.112 + char s_tmp[256] = { 0 }; 1.113 + std::sprintf(s_tmp,"gmic_filter%u_parameter%u",filter,n); 1.114 + static char res[4096] = { 0 }; 1.115 + res[0] = 0; 1.116 + gimp_get_data(s_tmp,res); 1.117 + return res; 1.118 +} 1.119 + 1.120 +unsigned int get_verbosity_level() { 1.121 + unsigned int verbosity = 0; 1.122 + gimp_get_data("gmic_verbosity",&verbosity); 1.123 + return verbosity; 1.124 +} 1.125 + 1.126 +void set_verbosity_level(const unsigned int verbosity) { 1.127 + gimp_set_data("gmic_verbosity",&verbosity,sizeof(unsigned int)); 1.128 +} 1.129 + 1.130 +// Return G'MIC command line needed to run the selected filter. 1.131 +//-------------------------------------------------------------- 1.132 +const char* get_commandline(const bool preview) { 1.133 + const unsigned int 1.134 + verbosity_level = get_verbosity_level(), 1.135 + filter = get_current_filter(), 1.136 + nbparams = get_filter_nbparams(filter); 1.137 + if (!filter) return 0; 1.138 + 1.139 + static CImg<char> res; 1.140 + 1.141 + CImgList<char> lres; 1.142 + switch (verbosity_level) { 1.143 + case 0: lres.insert(CImg<char>("-v- -",5)); break; 1.144 + case 1: lres.insert(CImg<char>("-",1)); break; 1.145 + default: lres.insert(CImg<char>("-v+ -debug -",12)); 1.146 + } 1.147 + 1.148 + const unsigned int N = filter - 1; 1.149 + const CImg<char> &command_item = (preview?gmic_preview_commands[N]:gmic_commands[N]); 1.150 + if (command_item) { 1.151 + lres.insert(command_item); 1.152 + if (nbparams) { 1.153 + lres[1].last() = ' '; 1.154 + for (unsigned int p = 0; p<nbparams; ++p) { 1.155 + const char *const param = get_filter_parameter(filter,p); 1.156 + if (param) lres.insert(CImg<char>(param,cimg::strlen(param)+1)).last().last() = ','; 1.157 + } 1.158 + } 1.159 + (res = lres.get_append('x')).last() = 0; 1.160 + } 1.161 + return res.ptr(); 1.162 +} 1.163 + 1.164 +// Process image region with G'MIC. 1.165 +//--------------------------------- 1.166 + 1.167 +// Define structure to store the arguments needed by the processing thread. 1.168 +struct st_process_thread { 1.169 + pthread_t thread; 1.170 + CImgList<float> images; 1.171 + const char *commandline; 1.172 + unsigned int verbosity_level; 1.173 + pthread_mutex_t is_running; 1.174 +}; 1.175 + 1.176 +// Thread that does the image processing part (call the G'MIC library). 1.177 +void *process_thread(void *arg) { 1.178 + st_process_thread &spt = *(st_process_thread*)arg; 1.179 + try { 1.180 + if (spt.verbosity_level>0) 1.181 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running G'MIC to process the image, with command : %s\n",spt.commandline); 1.182 + std::setlocale(LC_NUMERIC,"C"); 1.183 + gmic(spt.commandline,spt.images,gmic_macros,false); 1.184 + if (spt.verbosity_level>0) 1.185 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : G'MIC successfully returned !\n"); 1.186 + } catch (gmic_exception &e) { 1.187 + if (spt.verbosity_level>0) 1.188 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Error encountered when running G'MIC :\n*** %s\n",e.message); 1.189 + spt.images.assign(); 1.190 + } 1.191 + pthread_mutex_unlock(&spt.is_running); 1.192 + pthread_exit(0); 1.193 + return 0; 1.194 +} 1.195 + 1.196 +// Routine called to process the current GIMP image. 1.197 +void process_image(GimpDrawable *drawable, const char *last_commandline) { 1.198 + const unsigned int filter = get_current_filter(); 1.199 + if (!last_commandline && !filter) return; 1.200 + const char *commandline = last_commandline?last_commandline:get_commandline(false); 1.201 + if (!commandline || !cimg::strcmp(commandline,"-v- -nop")) return; 1.202 + gimp_progress_init_printf(" G'MIC Toolbox : %s...",gmic_entries[filter-1].ptr()); 1.203 + 1.204 + // Read GIMP image region data and make a CImg<float> instance from it. 1.205 + GimpPixelRgn src_region; 1.206 + gint x1, y1, x2, y2; 1.207 + gimp_drawable_mask_bounds(drawable->drawable_id,&x1,&y1,&x2,&y2); // Get coordinates of the current layer selection. 1.208 + const gint width = x2 - x1, height = y2 - y1, channels = drawable->bpp; 1.209 + gimp_pixel_rgn_init(&src_region,drawable,x1,y1,width,height,false,false); 1.210 + guchar *const src_row = g_new(guchar,width*channels); 1.211 + CImg<float> img(width,height,1,channels); 1.212 + cimg_forY(img,y) { 1.213 + gimp_pixel_rgn_get_row(&src_region,src_row,x1,y1+y,width); 1.214 + const guchar *ptrs = src_row; 1.215 + cimg_forX(img,x) cimg_forV(img,k) img(x,y,k) = (float)*(ptrs++); 1.216 + } 1.217 + g_free(src_row); 1.218 + 1.219 + // Call G'MIC interpreter on the CImg<float> image in a new thread. 1.220 + st_process_thread spt; 1.221 + spt.images.assign(1); 1.222 + img.transfer_to(spt.images[0]); 1.223 + spt.commandline = commandline; 1.224 + spt.verbosity_level = get_verbosity_level(); 1.225 + pthread_mutex_init(&spt.is_running,0); 1.226 + pthread_mutex_lock(&spt.is_running); 1.227 + pthread_create(&(spt.thread),0,process_thread,(void*)&spt); 1.228 + 1.229 + // Do a small animation with the progress bar, while waiting for 1.230 + // the termination of the processing thread. 1.231 + while (pthread_mutex_trylock(&spt.is_running)) { gimp_progress_pulse(); cimg::wait(500); } 1.232 + gimp_progress_update(1.0); 1.233 + pthread_join(spt.thread,0); 1.234 + pthread_mutex_unlock(&spt.is_running); 1.235 + pthread_mutex_destroy(&spt.is_running); 1.236 + 1.237 + // Force the resulting images to have all the same 2D GRAY, GRAYA, RGB or RGBA format. 1.238 + if (!spt.images) { gimp_progress_end(); return; } 1.239 + unsigned int max_width = 0, max_height = 0, max_channels = 0; 1.240 + cimglist_for(spt.images,p) { 1.241 + const CImg<float>& img = spt.images[p]; 1.242 + if (img.width>max_width) max_width = img.width; 1.243 + if (img.height>max_height) max_height = img.height; 1.244 + if (img.dim>max_channels) max_channels = img.dim; 1.245 + } 1.246 + if (max_channels>4) max_channels = 4; 1.247 + cimglist_apply(spt.images,resize)(-100,-100,1,max_channels); 1.248 + 1.249 + // Transfer the result image back into GIMP. 1.250 + if (spt.images.size==1 && (int)max_width==width && (int)max_height==height && (int)max_channels==channels) { 1.251 + 1.252 + // When the result image has same dimensions than the source : 1.253 + // Replace the selected region of the original GIMP image. 1.254 + CImg<float> &res = spt.images[0]; 1.255 + GimpPixelRgn dest_region; 1.256 + guchar *const dest_row = g_new(guchar,res.dimx()*res.dimv()); 1.257 + gimp_pixel_rgn_init(&dest_region,drawable,0,0,drawable->width,drawable->height,true,true); 1.258 + cimg_forY(res,y) { 1.259 + guchar *ptrd = dest_row; 1.260 + cimg_forX(res,x) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k); 1.261 + gimp_pixel_rgn_set_row(&dest_region,dest_row,x1,y1+y,width); 1.262 + } 1.263 + g_free(dest_row); 1.264 + spt.images.assign(); 1.265 + gimp_drawable_flush(drawable); 1.266 + gimp_drawable_merge_shadow(drawable->drawable_id,true); 1.267 + gimp_drawable_update(drawable->drawable_id,x1,y1,x2-x1,y2-y1); 1.268 + gimp_displays_flush(); 1.269 + } else { 1.270 + 1.271 + // When the result image has different dimensions than the source : 1.272 + // Returns a new GIMP image. 1.273 + gint id_img = gimp_image_new(max_width,max_height,max_channels<=2?GIMP_GRAY:GIMP_RGB); 1.274 + gimp_image_undo_group_start(id_img); 1.275 + 1.276 + cimglist_for(spt.images,p) { 1.277 + CImg<float> &res = spt.images[p]; 1.278 + gint id_layer = gimp_layer_new(id_img,"image",res.dimx(),res.dimy(), 1.279 + res.dimv()==1?GIMP_GRAY_IMAGE: 1.280 + res.dimv()==2?GIMP_GRAYA_IMAGE: 1.281 + res.dimv()==3?GIMP_RGB_IMAGE: 1.282 + GIMP_RGBA_IMAGE, 1.283 + 100.0,GIMP_NORMAL_MODE); 1.284 + gimp_image_add_layer(id_img,id_layer,0); 1.285 + GimpDrawable *ndrawable = gimp_drawable_get(id_layer); 1.286 + 1.287 + GimpPixelRgn dest_region; 1.288 + guchar *const dest_row = g_new(guchar,res.dimx()*res.dimv()); 1.289 + gimp_pixel_rgn_init(&dest_region,ndrawable,0,0,ndrawable->width,ndrawable->height,true,true); 1.290 + cimg_forY(res,y) { 1.291 + guchar *ptrd = dest_row; 1.292 + cimg_forX(res,x) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k); 1.293 + gimp_pixel_rgn_set_row(&dest_region,dest_row,0,y,res.dimx()); 1.294 + } 1.295 + g_free(dest_row); 1.296 + res.assign(); 1.297 + gimp_drawable_flush(ndrawable); 1.298 + gimp_drawable_merge_shadow(ndrawable->drawable_id,true); 1.299 + gimp_drawable_update(ndrawable->drawable_id,0,0,ndrawable->width,ndrawable->height); 1.300 + gimp_drawable_detach(ndrawable); 1.301 + } 1.302 + gimp_display_new(id_img); 1.303 + gimp_image_undo_group_end(id_img); 1.304 + gimp_displays_flush(); 1.305 + } 1.306 + gimp_progress_end(); 1.307 +} 1.308 + 1.309 +// Process preview with G'MIC. 1.310 +//----------------------------- 1.311 +void process_preview(GimpPreview *preview) { 1.312 + const unsigned int filter = get_current_filter(); 1.313 + if (!filter) return; 1.314 + const char *const commandline = get_commandline(true); 1.315 + if (!commandline || !cimg::strcmp(commandline,"-v- -nop")) return; 1.316 + 1.317 + // Read GIMP image preview and make a CImg<float> instance from it. 1.318 + gint width, height, channels; 1.319 + guchar *const ptr0 = gimp_zoom_preview_get_source(GIMP_ZOOM_PREVIEW(preview),&width,&height,&channels), *ptrs = ptr0; 1.320 + CImg<float> img(width,height,1,channels); 1.321 + cimg_forXY(img,x,y) cimg_forV(img,k) img(x,y,k) = (float)*(ptrs++); 1.322 + 1.323 + // Call G'MIC interpreter on the preview image. 1.324 + CImgList<float> gmic_images(1); 1.325 + img.transfer_to(gmic_images[0]); 1.326 + try { 1.327 + if (get_verbosity_level()>0) 1.328 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running G'MIC to process the preview, with command : %s\n",commandline); 1.329 + std::setlocale(LC_NUMERIC,"C"); 1.330 + gmic(commandline,gmic_images,gmic_macros,false); 1.331 + if (get_verbosity_level()>0) 1.332 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : G'MIC successfully returned !\n"); 1.333 + } catch (gmic_exception &e) { 1.334 + if (get_verbosity_level()>0) 1.335 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Error encountered when running G'MIC :\n*** %s\n",e.message); 1.336 + gmic_images.assign(); 1.337 + } 1.338 + 1.339 + // Get current image preview from the processed data. 1.340 + if (gmic_images.size && gmic_images[0]) { 1.341 + CImg<float>& res = gmic_images[0]; 1.342 + if (res.width>res.height) { 1.343 + const unsigned int _nheight = res.height*width/res.width, nheight = _nheight?_nheight:1; 1.344 + res.resize(width,nheight,1,-100,2); 1.345 + } else { 1.346 + const unsigned int _nwidth = res.width*height/res.height, nwidth = _nwidth?_nwidth:1; 1.347 + res.resize(nwidth,height,1,-100,2); 1.348 + } 1.349 + if (res.dimx()!=width || res.dimy()!=height) res.resize(width,height,1,-100,0,0,1); 1.350 + switch (channels) { 1.351 + case 1: 1.352 + switch (res.dim) { 1.353 + case 1: break; 1.354 + case 2: res.channel(0); break; 1.355 + case 3: res.channel(0); break; 1.356 + case 4: res.channel(0); break; 1.357 + default: res.channel(0); 1.358 + } break; 1.359 + case 2: 1.360 + switch (res.dim) { 1.361 + case 1: res.resize(-100,-100,1,2,0).get_shared_channel(1).fill(255); break; 1.362 + case 2: break; 1.363 + case 3: res.channels(0,1).get_shared_channel(1).fill(255); break; 1.364 + case 4: res.get_shared_channel(1) = res.get_shared_channel(3); res.channels(0,1); break; 1.365 + default: res.channels(0,1).get_shared_channel(1).fill(255); 1.366 + } break; 1.367 + case 3: 1.368 + switch (res.dim) { 1.369 + case 1: res.resize(-100,-100,1,3); break; 1.370 + case 2: res.channel(0).resize(-100,-100,1,3); break; 1.371 + case 3: break; 1.372 + case 4: res.channels(0,2); break; 1.373 + default: res.channels(0,2); 1.374 + } break; 1.375 + case 4: 1.376 + switch (res.dim) { 1.377 + case 1: res.resize(-100,-100,1,4).get_shared_channel(3).fill(255); break; 1.378 + case 2: 1.379 + res.resize(-100,-100,1,4,0); 1.380 + res.get_shared_channel(3) = res.get_shared_channel(1); 1.381 + res.get_shared_channel(1) = res.get_shared_channel(0); 1.382 + res.get_shared_channel(2) = res.get_shared_channel(0); 1.383 + break; 1.384 + case 3: res.resize(-100,-100,1,4,0).get_shared_channel(3).fill(255); break; 1.385 + case 4: break; 1.386 + default: res.resize(-100,-100,1,4,0); 1.387 + } break; 1.388 + } 1.389 + guchar *ptrd = ptr0; 1.390 + cimg_forXY(res,x,y) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k); 1.391 + gimp_preview_draw_buffer(preview,ptr0,width*channels); 1.392 + g_free(ptr0); 1.393 + } 1.394 +} 1.395 + 1.396 +// Define event functions for GUI. 1.397 +//-------------------------------- 1.398 + 1.399 +// Handle responses to the parameter widgets. 1.400 +void on_float_parameter_changed(GtkAdjustment *scale, gpointer user_data) { 1.401 + const unsigned int arg = *(unsigned int*)user_data; 1.402 + double value = 0; 1.403 + gimp_double_adjustment_update(scale,&value); 1.404 + char s_value[1024] = { 0 }; 1.405 + std::sprintf(s_value,"%g",value); 1.406 + set_filter_parameter(get_current_filter(),arg,s_value); 1.407 + return_create_dialog = true; 1.408 +} 1.409 + 1.410 +void on_int_parameter_changed(GtkAdjustment *scale, gpointer user_data) { 1.411 + const unsigned int arg = *(unsigned int*)user_data; 1.412 + int value = 0; 1.413 + gimp_int_adjustment_update(scale,&value); 1.414 + char s_value[1024] = { 0 }; 1.415 + std::sprintf(s_value,"%d",value); 1.416 + set_filter_parameter(get_current_filter(),arg,s_value); 1.417 + return_create_dialog = true; 1.418 +} 1.419 + 1.420 +void on_bool_parameter_changed(GtkCheckButton *checkbutton, gpointer user_data) { 1.421 + const unsigned int arg = *(unsigned int*)user_data; 1.422 + int value = 0; 1.423 + g_object_get(checkbutton,"active",&value,NULL); 1.424 + char s_value[1024] = { 0 }; 1.425 + std::sprintf(s_value,"%d",value?1:0); 1.426 + set_filter_parameter(get_current_filter(),arg,s_value); 1.427 + return_create_dialog = true; 1.428 +} 1.429 + 1.430 +void on_list_parameter_changed(GtkComboBox *combobox, gpointer user_data) { 1.431 + const unsigned int arg = *(unsigned int*)user_data; 1.432 + int value = 0; 1.433 + g_object_get(combobox,"active",&value,NULL); 1.434 + char s_value[1024] = { 0 }; 1.435 + std::sprintf(s_value,"%d",value); 1.436 + set_filter_parameter(get_current_filter(),arg,s_value); 1.437 + return_create_dialog = true; 1.438 +} 1.439 + 1.440 +void on_text_parameter_changed(GtkButton *button, gpointer user_data) { 1.441 + button = 0; 1.442 + const unsigned int arg = *(unsigned int*)user_data; 1.443 + GtkWidget *entry = *((GtkWidget**)user_data+1); 1.444 + const char *s_value = gtk_entry_get_text(GTK_ENTRY(entry)); 1.445 + set_filter_parameter(get_current_filter(),arg,s_value); 1.446 + return_create_dialog = true; 1.447 +} 1.448 + 1.449 +void on_file_parameter_changed(GtkFileChooserButton *widget, gpointer user_data){ 1.450 + const unsigned int arg = *(unsigned int*)user_data; 1.451 + const char 1.452 + *const filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)), 1.453 + *s_value = filename?filename:""; 1.454 + set_filter_parameter(get_current_filter(),arg,s_value); 1.455 + return_create_dialog = true; 1.456 +} 1.457 + 1.458 +void on_color_parameter_changed(GtkColorButton *widget, gpointer user_data){ 1.459 + const unsigned int arg = *(unsigned int*)user_data; 1.460 + GdkColor color; 1.461 + gtk_color_button_get_color(GTK_COLOR_BUTTON(widget),&color); 1.462 + char s_value[1024] = { 0 }; 1.463 + if (gtk_color_button_get_use_alpha(GTK_COLOR_BUTTON(widget))) 1.464 + std::sprintf(s_value,"%d,%d,%d,%d", 1.465 + color.red>>8,color.green>>8,color.blue>>8,gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget))>>8); 1.466 + else std::sprintf(s_value,"%d,%d,%d", 1.467 + color.red>>8,color.green>>8,color.blue>>8); 1.468 + set_filter_parameter(get_current_filter(),arg,s_value); 1.469 + return_create_dialog = true; 1.470 +} 1.471 + 1.472 +// Create parameter GUI for specific chosen filter. 1.473 +//-------------------------------------------------- 1.474 +void create_parameters_gui(const bool reset) { 1.475 + const unsigned int filter = get_current_filter(); 1.476 + 1.477 + // Remove widget in the current frame if necessary. 1.478 + GtkWidget *frame = 0; 1.479 + gimp_get_data("gmic_gui_frame",&frame); 1.480 + if (frame) { 1.481 + GtkWidget *child = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(frame))); 1.482 + if (child) gtk_container_remove(GTK_CONTAINER(frame),child); 1.483 + } 1.484 + 1.485 + GtkWidget *table = 0; 1.486 + if (!filter) { // No filter selected -> Default message. 1.487 + table = gtk_table_new(1,1,false); 1.488 + gtk_widget_show(table); 1.489 + GtkWidget *label = gtk_label_new(NULL); 1.490 + gtk_label_set_markup(GTK_LABEL(label),"<i>Select a filter...</i>"); 1.491 + gtk_widget_show(label); 1.492 + gtk_table_attach(GTK_TABLE(table),label,0,1,0,1, 1.493 + (GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0); 1.494 + gtk_misc_set_alignment (GTK_MISC(label),0,0.5); 1.495 + gtk_frame_set_label(GTK_FRAME(frame),NULL); 1.496 + } else { // Filter selected -> Build parameter table. 1.497 + GtkWidget *preview = 0; 1.498 + gimp_get_data("gmic_gui_preview",&preview); 1.499 + const unsigned int N = filter - 1; 1.500 + char nlabel[4096] = { 0 }; 1.501 + std::sprintf(nlabel,"<b> %s : </b>",gmic_entries[N].ptr()); 1.502 + GtkWidget *frame_title = gtk_label_new(NULL); 1.503 + gtk_widget_show(frame_title); 1.504 + gtk_label_set_markup(GTK_LABEL(frame_title),nlabel); 1.505 + gtk_frame_set_label_widget(GTK_FRAME(frame),frame_title); 1.506 + 1.507 + char argname[4096] = { 0 }, argtype[4096] = { 0 }, argarg[4096] = { 0 }; 1.508 + unsigned int nb_arguments = 0; 1.509 + for (const char *argument = gmic_arguments[N].ptr(); *argument; ) { 1.510 + if (std::sscanf(argument,"%4095[^=]=%4095[^(](%4095[^)]",argname,argtype,&(argarg[0]=0))>=2) { 1.511 + argument += cimg::strlen(argname) + cimg::strlen(argtype) + cimg::strlen(argarg) + 3; 1.512 + if (*argument) ++argument; 1.513 + ++nb_arguments; 1.514 + } else break; 1.515 + } 1.516 + 1.517 + if (!nb_arguments) { // Selected filter has no parameters -> Default message. 1.518 + table = gtk_table_new(1,1,false); 1.519 + gtk_widget_show(table); 1.520 + GtkWidget *label = gtk_label_new(NULL); 1.521 + gtk_label_set_markup(GTK_LABEL(label),"<i>No parameters to set...</i>"); 1.522 + gtk_widget_show(label); 1.523 + gtk_table_attach(GTK_TABLE(table),label,0,1,0,1, 1.524 + (GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0); 1.525 + gtk_misc_set_alignment (GTK_MISC(label),0,0.5); 1.526 + } else { // Selected filter has parameters -> Create parameter table. 1.527 + 1.528 + // Create new table for putting parameters inside. 1.529 + table = gtk_table_new(3,nb_arguments,false); 1.530 + gtk_widget_show(table); 1.531 + gtk_table_set_row_spacings(GTK_TABLE(table),6); 1.532 + gtk_table_set_col_spacings(GTK_TABLE(table),6); 1.533 + gtk_container_set_border_width(GTK_CONTAINER(table),8); 1.534 + 1.535 + // Parse arguments list and add recognized one to the table. 1.536 + event_infos = new void*[2*nb_arguments]; 1.537 + int current_parameter = 0, current_line = 0; 1.538 + for (const char *argument = gmic_arguments[N].ptr(); *argument; ) { 1.539 + if (std::sscanf(argument,"%4095[^=]=%4095[^(](%4095[^)]",argname,argtype,&(argarg[0]=0))>=2) { 1.540 + argument += cimg::strlen(argname) + cimg::strlen(argtype) + cimg::strlen(argarg) + 3; 1.541 + if (*argument) ++argument; 1.542 + cimg::strclean(argname); 1.543 + cimg::strclean(argtype); 1.544 + const char *const s_value = get_filter_parameter(filter,current_parameter); 1.545 + 1.546 + // Check for a float-valued parameter -> Create GtkAdjustment. 1.547 + bool found_valid_item = false; 1.548 + if (!found_valid_item && !cimg::strcasecmp(argtype,"float")) { 1.549 + float initial_value = 0, min_value = 0, max_value = 100; 1.550 + std::setlocale(LC_NUMERIC,"C"); 1.551 + std::sscanf(argarg,"%f%*c%f%*c%f",&initial_value,&min_value,&max_value); 1.552 + if (!reset && std::sscanf(s_value,"%f",&initial_value)) {} 1.553 + GtkObject *scale = gimp_scale_entry_new(GTK_TABLE(table),0,current_line,argname,100,6, 1.554 + (gdouble)initial_value,(gdouble)min_value,(gdouble)max_value, 1.555 + 0.1,0.1,2,true,0,0,0,0); 1.556 + event_infos[2*current_parameter] = (void*)current_parameter; 1.557 + event_infos[2*current_parameter+1] = (void*)0; 1.558 + on_float_parameter_changed(GTK_ADJUSTMENT(scale),(void*)(event_infos+2*current_parameter)); 1.559 + g_signal_connect(scale,"value_changed",G_CALLBACK(on_float_parameter_changed), 1.560 + (void*)(event_infos+2*current_parameter)); 1.561 + g_signal_connect_swapped(scale,"value_changed",G_CALLBACK(gimp_preview_invalidate),preview); 1.562 + found_valid_item = true; 1.563 + ++current_parameter; 1.564 + } 1.565 + 1.566 + // Check for an int-valued parameter -> Create GtkAdjustment. 1.567 + if (!found_valid_item && !cimg::strcasecmp(argtype,"int")) { 1.568 + float initial_value = 0, min_value = 0, max_value = 100; 1.569 + std::setlocale(LC_NUMERIC,"C"); 1.570 + std::sscanf(argarg,"%f%*c%f%*c%f",&initial_value,&min_value,&max_value); 1.571 + if (!reset && std::sscanf(s_value,"%f",&initial_value)) {} 1.572 + GtkObject *scale = gimp_scale_entry_new(GTK_TABLE(table),0,current_line,argname,100,6, 1.573 + (gdouble)(int)initial_value,(gdouble)(int)min_value, 1.574 + (gdouble)(int)max_value, 1.575 + 1,1,0,true,0,0,0,0); 1.576 + event_infos[2*current_parameter] = (void*)current_parameter; 1.577 + event_infos[2*current_parameter+1] = (void*)0; 1.578 + on_int_parameter_changed(GTK_ADJUSTMENT(scale),(void*)(event_infos+2*current_parameter)); 1.579 + g_signal_connect(scale,"value_changed",G_CALLBACK(on_int_parameter_changed), 1.580 + (void*)(event_infos+2*current_parameter)); 1.581 + g_signal_connect_swapped(scale,"value_changed",G_CALLBACK(gimp_preview_invalidate),preview); 1.582 + found_valid_item = true; 1.583 + ++current_parameter; 1.584 + } 1.585 + 1.586 + // Check for a bool-valued parameter -> Create GtkCheckButton. 1.587 + if (!found_valid_item && !cimg::strcasecmp(argtype,"bool")) { 1.588 + unsigned int initial_value = 0; 1.589 + std::sscanf(argarg,"%u",&initial_value); 1.590 + if (!reset && std::sscanf(s_value,"%u",&initial_value)) {} 1.591 + GtkWidget *checkbutton = gtk_check_button_new_with_label(argname); 1.592 + gtk_widget_show(checkbutton); 1.593 + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton),initial_value?true:false); 1.594 + gtk_table_attach(GTK_TABLE(table),checkbutton,0,2,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.595 + event_infos[2*current_parameter] = (void*)current_parameter; 1.596 + event_infos[2*current_parameter+1] = (void*)0; 1.597 + on_bool_parameter_changed(GTK_CHECK_BUTTON(checkbutton),(void*)(event_infos+2*current_parameter)); 1.598 + g_signal_connect(checkbutton,"toggled",G_CALLBACK(on_bool_parameter_changed), 1.599 + (void*)(event_infos+2*current_parameter)); 1.600 + g_signal_connect_swapped(checkbutton,"toggled",G_CALLBACK(gimp_preview_invalidate),preview); 1.601 + found_valid_item = true; 1.602 + ++current_parameter; 1.603 + } 1.604 + 1.605 + // Check for a list-valued parameter -> Create GtkComboBox. 1.606 + if (!found_valid_item && !cimg::strcasecmp(argtype,"choice")) { 1.607 + GtkWidget *label = gtk_label_new(argname); 1.608 + gtk_widget_show(label); 1.609 + gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.610 + gtk_misc_set_alignment(GTK_MISC(label),0,0.5); 1.611 + GtkWidget *combobox = gtk_combo_box_new_text(); 1.612 + gtk_widget_show(combobox); 1.613 + char s_entry[4096] = { 0 }, end = 0; int err2 = 0; 1.614 + unsigned int initial_value = 0; 1.615 + const char *entries = argarg; 1.616 + if (std::sscanf(entries,"%u",&initial_value)==1) entries+=std::sprintf(s_entry,"%u",initial_value) + 1; 1.617 + while (*entries) { 1.618 + if ((err2 = std::sscanf(entries,"%4095[^,]%c",s_entry,&end))>0) { 1.619 + entries += cimg::strlen(s_entry) + (err2==2?1:0); 1.620 + cimg::strclean(s_entry); 1.621 + strparenthesis(s_entry); 1.622 + gtk_combo_box_append_text(GTK_COMBO_BOX(combobox),s_entry); 1.623 + } else break; 1.624 + } 1.625 + if (!reset && std::sscanf(s_value,"%u",&initial_value)) {} 1.626 + gtk_combo_box_set_active(GTK_COMBO_BOX(combobox),initial_value); 1.627 + gtk_table_attach(GTK_TABLE(table),combobox,1,3,current_line,current_line+1, 1.628 + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)(GTK_FILL),0,0); 1.629 + event_infos[2*current_parameter] = (void*)current_parameter; 1.630 + event_infos[2*current_parameter+1] = (void*)0; 1.631 + on_list_parameter_changed(GTK_COMBO_BOX(combobox),(void*)(event_infos+2*current_parameter)); 1.632 + g_signal_connect(combobox,"changed",G_CALLBACK(on_list_parameter_changed), 1.633 + (void*)(event_infos+2*current_parameter)); 1.634 + g_signal_connect_swapped(combobox,"changed",G_CALLBACK(gimp_preview_invalidate),preview); 1.635 + found_valid_item = true; 1.636 + ++current_parameter; 1.637 + } 1.638 + 1.639 + // Check for a text-valued parameter -> Create GtkEntry. 1.640 + if (!found_valid_item && !cimg::strcasecmp(argtype,"text")) { 1.641 + GtkWidget *label = gtk_label_new(argname); 1.642 + gtk_widget_show(label); 1.643 + gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.644 + gtk_misc_set_alignment(GTK_MISC(label),0,0.5); 1.645 + GtkWidget *entry = gtk_entry_new_with_max_length(4095); 1.646 + gtk_widget_show(entry); 1.647 + cimg::strclean(argarg); 1.648 + if (!reset && *s_value) gtk_entry_set_text(GTK_ENTRY(entry),s_value); 1.649 + else gtk_entry_set_text(GTK_ENTRY(entry),argarg); 1.650 + gtk_table_attach(GTK_TABLE(table),entry,1,2,current_line,current_line+1, 1.651 + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0); 1.652 + GtkWidget *button = gtk_button_new_with_label("Update"); 1.653 + gtk_widget_show(button); 1.654 + gtk_table_attach(GTK_TABLE(table),button,2,3,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.655 + event_infos[2*current_parameter] = (void*)current_parameter; 1.656 + event_infos[2*current_parameter+1] = (void*)entry; 1.657 + on_text_parameter_changed(GTK_BUTTON(button),(void*)(event_infos+2*current_parameter)); 1.658 + g_signal_connect(button,"clicked",G_CALLBACK(on_text_parameter_changed), 1.659 + (void*)(event_infos+2*current_parameter)); 1.660 + g_signal_connect_swapped(button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview); 1.661 + found_valid_item = true; 1.662 + ++current_parameter; 1.663 + } 1.664 + 1.665 + // Check for a filename parameter -> Create GtkFileChooserButton. 1.666 + if (!found_valid_item && !cimg::strcasecmp(argtype,"file")) { 1.667 + GtkWidget *label = gtk_label_new(argname); 1.668 + gtk_widget_show(label); 1.669 + gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.670 + gtk_misc_set_alignment(GTK_MISC(label),0,0.5); 1.671 + GtkWidget *filechooser = gtk_file_chooser_button_new(argname,GTK_FILE_CHOOSER_ACTION_OPEN); 1.672 + gtk_widget_show(filechooser); 1.673 + cimg::strclean(argarg); 1.674 + if (!reset && *s_value) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filechooser),s_value); 1.675 + else gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filechooser),argarg); 1.676 + gtk_table_attach(GTK_TABLE(table),filechooser,1,3,current_line,current_line+1, 1.677 + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0); 1.678 + event_infos[2*current_parameter] = (void*)current_parameter; 1.679 + event_infos[2*current_parameter+1] = (void*)0; 1.680 + on_file_parameter_changed(GTK_FILE_CHOOSER_BUTTON(filechooser),(void*)(event_infos+2*current_parameter)); 1.681 + g_signal_connect(filechooser,"file-set",G_CALLBACK(on_file_parameter_changed), 1.682 + (void*)(event_infos+2*current_parameter)); 1.683 + g_signal_connect_swapped(filechooser,"file-set",G_CALLBACK(gimp_preview_invalidate),preview); 1.684 + found_valid_item = true; 1.685 + ++current_parameter; 1.686 + } 1.687 + 1.688 + // Check for a color -> Create GtkColorButton. 1.689 + if (!found_valid_item && !cimg::strcasecmp(argtype,"color")) { 1.690 + GtkWidget *hbox = gtk_hbox_new(false,6); 1.691 + gtk_widget_show(hbox); 1.692 + gtk_table_attach(GTK_TABLE(table),hbox,0,2,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.693 + GtkWidget *label = gtk_label_new(argname); 1.694 + gtk_widget_show(label); 1.695 + gtk_box_pack_start(GTK_BOX(hbox),label,false,false,0); 1.696 + GtkWidget *colorchooser = gtk_color_button_new(); 1.697 + gtk_widget_show(colorchooser); 1.698 + gtk_color_button_set_title(GTK_COLOR_BUTTON(colorchooser),argname); 1.699 + gtk_box_pack_start(GTK_BOX(hbox),colorchooser,false,false,0); 1.700 + event_infos[2*current_parameter] = (void*)current_parameter; 1.701 + event_infos[2*current_parameter+1] = (void*)0; 1.702 + cimg::strclean(argarg); 1.703 + unsigned int red = 0, green = 0, blue = 0, alpha = 255; 1.704 + const int err = std::sscanf(argarg,"%u%*c%u%*c%u%*c%u",&red,&green,&blue,&alpha); 1.705 + if (!reset && std::sscanf(s_value,"%u%*c%u%*c%u%*c%u",&red,&green,&blue,&alpha)==err) {} 1.706 + GdkColor col; 1.707 + col.pixel = 0; col.red = red<<8; col.green = green<<8; col.blue = blue<<8; 1.708 + gtk_color_button_set_color(GTK_COLOR_BUTTON(colorchooser),&col); 1.709 + if (err==4) { 1.710 + gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorchooser),true); 1.711 + gtk_color_button_set_alpha(GTK_COLOR_BUTTON(colorchooser),alpha<<8); 1.712 + } else gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorchooser),false); 1.713 + on_color_parameter_changed(GTK_COLOR_BUTTON(colorchooser),(void*)(event_infos+2*current_parameter)); 1.714 + g_signal_connect(colorchooser,"color-set",G_CALLBACK(on_color_parameter_changed), 1.715 + (void*)(event_infos+2*current_parameter)); 1.716 + g_signal_connect_swapped(colorchooser,"color-set",G_CALLBACK(gimp_preview_invalidate),preview); 1.717 + found_valid_item = true; 1.718 + ++current_parameter; 1.719 + } 1.720 + 1.721 + // Check for a note -> Create GtkLabel. 1.722 + if (!found_valid_item && !cimg::strcasecmp(argtype,"note")) { 1.723 + cimg::strclean(argarg); 1.724 + GtkWidget *label = gtk_label_new(NULL); 1.725 + cimg::strescape(argarg); 1.726 + strparenthesis(argarg); 1.727 + gtk_label_set_markup(GTK_LABEL(label),argarg); 1.728 + gtk_label_set_line_wrap(GTK_LABEL(label),true); 1.729 + gtk_widget_show(label); 1.730 + gtk_table_attach(GTK_TABLE(table),label,0,3,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0); 1.731 + gtk_misc_set_alignment(GTK_MISC(label),0,0.5); 1.732 + found_valid_item = true; 1.733 + } 1.734 + 1.735 + if (!found_valid_item) { 1.736 + if (get_verbosity_level()>0) 1.737 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Found invalid parameter type '%s' for argument '%s'.\n",argtype,argname); 1.738 + } else ++current_line; 1.739 + } else break; 1.740 + } 1.741 + set_filter_nbparams(filter,current_parameter); 1.742 + } 1.743 + } 1.744 + gtk_container_add(GTK_CONTAINER(frame),table); 1.745 +} 1.746 + 1.747 +// Called when the selected filter changed (in the combo-box). 1.748 +void on_filter_changed(GtkTreeSelection *selection, gpointer user_data) { 1.749 + user_data = 0; 1.750 + GtkTreeIter iter; 1.751 + GtkTreeModel *model; 1.752 + unsigned int choice = 0; 1.753 + if (gtk_tree_selection_get_selected(selection,&model,&iter)) 1.754 + gtk_tree_model_get(model,&iter,0,&choice,-1); 1.755 + set_current_filter(choice); 1.756 + create_parameters_gui(false); 1.757 + return_create_dialog = true; 1.758 +} 1.759 + 1.760 +// Handle responses to the dialog window buttons. 1.761 +void on_verbosity_level_changed(GtkComboBox *combobox, gpointer user_data) { 1.762 + user_data = 0; 1.763 + int value = 0; 1.764 + g_object_get(combobox,"active",&value,NULL); 1.765 + set_verbosity_level(value); 1.766 +} 1.767 + 1.768 +void on_dialog_reset_clicked(GtkButton *widget, gpointer data) { 1.769 + widget = 0; data = 0; 1.770 + create_parameters_gui(true); 1.771 + return_create_dialog = true; 1.772 +} 1.773 + 1.774 +void on_dialog_cancel_clicked(GtkButton *widget, gpointer data) { 1.775 + widget = 0; data = 0; 1.776 + return_create_dialog = false; 1.777 + gtk_main_quit(); 1.778 +} 1.779 + 1.780 +void on_dialog_apply_clicked(GtkButton *widget, gpointer data) { 1.781 + widget = 0; 1.782 + GimpDrawable *drawable = (GimpDrawable*)data; 1.783 + process_image(drawable,0); 1.784 + return_create_dialog = false; 1.785 +} 1.786 + 1.787 +void on_dialog_ok_clicked(GtkButton *widget, gpointer data) { 1.788 + widget = 0; data = 0; 1.789 + gtk_main_quit(); 1.790 +} 1.791 + 1.792 +void on_update_button_clicked(GtkButton *widget, gpointer data) { 1.793 + widget = 0; 1.794 + GtkWidget *dialog = (GtkWidget*)data; 1.795 + char update_filename[1024] = { 0 }, update_command[1024] = { 0 }, src_filename[1024] = { 0 }, dest_filename[1024] = { 0 }; 1.796 + const char 1.797 + *const update_url = "http://www.greyc.ensicaen.fr/~dtschump", 1.798 + *const path_tmp = cimg::temporary_path(); 1.799 + std::sprintf(update_filename,"gmic4gimp_def.%d",gmic_version); 1.800 + std::sprintf(src_filename,"%s/%s",path_tmp,update_filename); 1.801 + std::sprintf(dest_filename,"%s/.%s",path_home,update_filename); 1.802 + if (get_verbosity_level()>0) { 1.803 + std::sprintf(update_command,"wget %s/%s -O %s",update_url,update_filename,src_filename); 1.804 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running update procedure, with command : %s\n",update_command); 1.805 + } else std::sprintf(update_command,"wget --quiet %s/%s -O %s",update_url,update_filename,src_filename); 1.806 + int status = cimg::system(update_command); 1.807 + status = 0; 1.808 + std::FILE *file_s = std::fopen(src_filename,"r"); 1.809 + bool succeed = false; 1.810 + if (file_s) { 1.811 + unsigned int size_s = 0; 1.812 + std::fseek(file_s,0,SEEK_END); 1.813 + size_s = (unsigned int)std::ftell(file_s); 1.814 + std::rewind(file_s); 1.815 + if (size_s) { 1.816 + std::FILE *file_d = std::fopen(dest_filename,"w"); 1.817 + char *buffer = new char[size_s], sep = 0; 1.818 + if (file_d && 1.819 + std::fread(buffer,sizeof(char),size_s,file_s)==size_s && 1.820 + std::sscanf(buffer,"#@gim%c",&sep)==1 && sep=='p' && 1.821 + std::fwrite(buffer,sizeof(char),size_s,file_d)==size_s) { succeed = true; std::fclose(file_d); } 1.822 + delete[] buffer; 1.823 + } 1.824 + std::fclose(file_s); 1.825 + } 1.826 + if (!succeed) { 1.827 + GtkWidget *message = gtk_message_dialog_new_with_markup(GTK_WINDOW(dialog),GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK, 1.828 + "<b>Filters update failed !</b>\n\n" 1.829 + "A valid version of the update file :\n\n" 1.830 + "<i>%s/%s</i>\n\n" 1.831 + " ...could not be retrieved from the G'MIC server.\n\n" 1.832 + "Please check your Internet connection or\n" 1.833 + "try a manual update instead.",update_url,update_filename); 1.834 + gtk_widget_show(message); 1.835 + gtk_dialog_run(GTK_DIALOG(message)); 1.836 + gtk_widget_destroy(message); 1.837 + } else { 1.838 + GtkWidget *message = gtk_message_dialog_new_with_markup(GTK_WINDOW(dialog),GTK_DIALOG_MODAL,GTK_MESSAGE_INFO,GTK_BUTTONS_OK, 1.839 + "<b>Filters update succeed !</b>\n\n" 1.840 + "The G'MIC Toolbox must be restarted now."); 1.841 + gtk_widget_show(message); 1.842 + gtk_dialog_run(GTK_DIALOG(message)); 1.843 + gtk_widget_destroy(message); 1.844 + return_create_dialog = false; 1.845 + set_current_filter(0); 1.846 + gtk_main_quit(); 1.847 + } 1.848 +} 1.849 + 1.850 +// Create main plug-in dialog window and wait for a response. 1.851 +//----------------------------------------------------------- 1.852 +bool create_dialog_gui(GimpDrawable *drawable) { 1.853 + 1.854 + // Init GUI_specific variables 1.855 + gimp_ui_init("gmic",true); 1.856 + event_infos = 0; 1.857 + 1.858 + // Create main plug-in dialog window. 1.859 + GtkWidget 1.860 + *dialog = gimp_dialog_new("The G'MIC Toolbox","gmic",0,(GtkDialogFlags)0,gimp_standard_help_func,"gmic",NULL), 1.861 + *cancel_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL), 1.862 + *reset_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GIMP_STOCK_RESET,1), 1.863 + *apply_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_APPLY,GTK_RESPONSE_APPLY), 1.864 + *ok_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_OK,GTK_RESPONSE_OK); 1.865 + gimp_window_set_transient(GTK_WINDOW(dialog)); 1.866 + g_signal_connect(dialog,"close",G_CALLBACK(on_dialog_cancel_clicked),0); 1.867 + g_signal_connect(dialog,"delete-event",G_CALLBACK(on_dialog_cancel_clicked),0); 1.868 + g_signal_connect(cancel_button,"clicked",G_CALLBACK(on_dialog_cancel_clicked),0); 1.869 + g_signal_connect(apply_button,"clicked",G_CALLBACK(on_dialog_apply_clicked),drawable); 1.870 + g_signal_connect(ok_button,"clicked",G_CALLBACK(on_dialog_ok_clicked),0); 1.871 + 1.872 + GtkWidget *dialog_hbox = gtk_hbox_new(false,0); 1.873 + gtk_widget_show(dialog_hbox); 1.874 + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),dialog_hbox); 1.875 + 1.876 + // Create the left pane, containing the preview, the show commmand and the update buttons and the author name. 1.877 + GtkWidget *left_pane = gtk_vbox_new(false,4); 1.878 + gtk_widget_show(left_pane); 1.879 + gtk_box_pack_start(GTK_BOX(dialog_hbox),left_pane,true,true,0); 1.880 + 1.881 + GtkWidget *preview = gimp_zoom_preview_new(drawable); 1.882 + gtk_widget_show(preview); 1.883 + gtk_box_pack_start(GTK_BOX(left_pane),preview,true,true,0); 1.884 + gimp_set_data("gmic_gui_preview",&preview,sizeof(GtkWidget*)); 1.885 + g_signal_connect(preview,"invalidated",G_CALLBACK(process_preview),0); 1.886 + g_signal_connect_swapped(apply_button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview); 1.887 + 1.888 + GtkWidget *verbosity_hbuttonbox = gtk_hbutton_box_new(); 1.889 + gtk_widget_show(verbosity_hbuttonbox); 1.890 + gtk_box_pack_start(GTK_BOX(left_pane),verbosity_hbuttonbox,false,false,0); 1.891 + 1.892 + GtkWidget *verbosity_combobox = gtk_combo_box_new_text(); 1.893 + gtk_widget_show(verbosity_combobox); 1.894 + gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Quiet mode"); 1.895 + gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Verbose mode"); 1.896 + gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Debug mode"); 1.897 + gtk_combo_box_set_active(GTK_COMBO_BOX(verbosity_combobox),get_verbosity_level()); 1.898 + gtk_container_add(GTK_CONTAINER(verbosity_hbuttonbox),verbosity_combobox); 1.899 + g_signal_connect(verbosity_combobox,"changed",G_CALLBACK(on_verbosity_level_changed),0); 1.900 + 1.901 + GtkWidget *update_hbuttonbox = gtk_hbutton_box_new(); 1.902 + gtk_widget_show(update_hbuttonbox); 1.903 + gtk_box_pack_start(GTK_BOX(left_pane),update_hbuttonbox,false,false,0); 1.904 + GtkWidget 1.905 + *tmp_button = gtk_button_new_from_stock(GTK_STOCK_REFRESH), 1.906 + *update_image = gtk_button_get_image(GTK_BUTTON(tmp_button)), 1.907 + *update_button = gtk_button_new_with_mnemonic("_Update filters"); 1.908 + gtk_button_set_image(GTK_BUTTON(update_button),update_image); 1.909 + gtk_widget_show(update_button); 1.910 + gtk_container_add(GTK_CONTAINER(update_hbuttonbox),update_button); 1.911 + g_signal_connect(update_button,"clicked",G_CALLBACK(on_update_button_clicked),(void*)dialog); 1.912 + 1.913 + GtkWidget *about_label = gtk_label_new(NULL); 1.914 + gtk_label_set_markup(GTK_LABEL(about_label), 1.915 + "\n<span color=\"#666666\"><small>" 1.916 + "<b>G'MIC</b> is proposed to you\n" 1.917 + " by <i>David Tschumperle</i>" 1.918 + "</small></span>"); 1.919 + gtk_widget_show(about_label); 1.920 + gtk_box_pack_start(GTK_BOX(left_pane),about_label,false,false,0); 1.921 + 1.922 + const unsigned int logo_width = 102, logo_height = 22; 1.923 + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(data_logo,GDK_COLORSPACE_RGB,false,8, 1.924 + logo_width,logo_height,3*logo_width,0,0); 1.925 + GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); 1.926 + gtk_widget_show(image); 1.927 + gtk_box_pack_start(GTK_BOX(left_pane),image,false,false,0); 1.928 + 1.929 + // Create the middle pane, which contains the filters treeview. 1.930 + GtkWidget *middle_pane = gtk_frame_new(NULL); 1.931 + gtk_widget_show(middle_pane); 1.932 + gtk_container_set_border_width(GTK_CONTAINER(middle_pane),4); 1.933 + gtk_widget_set_size_request(middle_pane,250,-1); 1.934 + gtk_box_pack_start(GTK_BOX(dialog_hbox),middle_pane,false,false,0); 1.935 + 1.936 + GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL,NULL); 1.937 + gtk_widget_show(scrolledwindow); 1.938 + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); 1.939 + gtk_container_add(GTK_CONTAINER(middle_pane),scrolledwindow); 1.940 + 1.941 + GtkWidget *treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store)); 1.942 + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); 1.943 + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(" Available filters :",renderer,"text",1,NULL); 1.944 + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),column); 1.945 + 1.946 + GtkTreeSelection *select = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); 1.947 + gtk_tree_selection_set_mode(select,GTK_SELECTION_SINGLE); 1.948 + g_signal_connect(G_OBJECT(select),"changed",G_CALLBACK(on_filter_changed),0); 1.949 + g_signal_connect_swapped(select,"changed",G_CALLBACK(gimp_preview_invalidate),preview); 1.950 + gtk_widget_show(treeview); 1.951 + gtk_container_add(GTK_CONTAINER(scrolledwindow),treeview); 1.952 + g_signal_connect(reset_button,"clicked",G_CALLBACK(on_dialog_reset_clicked),select); 1.953 + g_signal_connect_swapped(reset_button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview); 1.954 + 1.955 + // Create the right pane which contains the parameters frame. 1.956 + GtkWidget *parameters_frame = gtk_frame_new(NULL); 1.957 + gtk_widget_show(parameters_frame); 1.958 + gtk_container_set_border_width(GTK_CONTAINER(parameters_frame),4); 1.959 + gtk_widget_set_size_request(parameters_frame,450,-1); 1.960 + gtk_box_pack_start(GTK_BOX(dialog_hbox),parameters_frame,false,false,0); 1.961 + gimp_set_data("gmic_gui_frame",¶meters_frame,sizeof(GtkWidget*)); 1.962 + create_parameters_gui(false); 1.963 + 1.964 + // Show dialog window and wait for user response. 1.965 + gtk_widget_show(dialog); 1.966 + gtk_main(); 1.967 + 1.968 + // Destroy dialog box widget and free resources. 1.969 + gtk_widget_destroy(dialog); 1.970 + gtk_widget_destroy(tmp_button); 1.971 + if (event_infos) delete[] event_infos; 1.972 + return return_create_dialog; 1.973 +} 1.974 + 1.975 +// 'Run' function needed by GIMP plug-in API. 1.976 +//------------------------------------------- 1.977 +void gmic_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { 1.978 + 1.979 + // Init plug-in variables. 1.980 + static GimpParam values[1]; 1.981 + values[0].type = GIMP_PDB_STATUS; 1.982 + *return_vals = values; 1.983 + *nreturn_vals = 1; 1.984 + name = 0; 1.985 + nparams = 0; 1.986 + GimpRunMode run_mode; 1.987 + run_mode = (GimpRunMode)param[0].data.d_int32; 1.988 + if (run_mode==GIMP_RUN_NONINTERACTIVE) { 1.989 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : ERROR, this plug-in cannot be run in non-interactive mode.\n"); 1.990 + values[0].data.d_status = GIMP_PDB_CALLING_ERROR; 1.991 + return; 1.992 + } 1.993 + gmic_macros = 0; 1.994 + filter_store = 0; 1.995 + return_create_dialog = true; 1.996 + path_home = getenv(cimg_OS!=2?"HOME":"APPDATA"); 1.997 + 1.998 + // Check that no instance of the plug-in is already running. 1.999 + bool is_existing_instance = 0; 1.1000 + gimp_get_data("gmic_instance",&is_existing_instance); 1.1001 + if (is_existing_instance) { 1.1002 + std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Existing instance of the plug-in is already running.\n"); 1.1003 + return; 1.1004 + } 1.1005 + is_existing_instance = true; 1.1006 + gimp_set_data("gmic_instance",&is_existing_instance,sizeof(bool)); 1.1007 + 1.1008 + // Read user-defined configuration files '.gmic_def' and '.gmic', when possible. 1.1009 + unsigned size_update = 0, size_custom = 0, size_def = sizeof(data_gmic4gimp_def); 1.1010 + char filename_update[1024] = { 0 }, filename_custom[1024] = { 0 }; 1.1011 + std::sprintf(filename_update,"%s/.gmic4gimp_def.%d",path_home,gmic_version); 1.1012 + std::sprintf(filename_custom,"%s/.gmic4gimp",path_home); 1.1013 + std::FILE 1.1014 + *file_update = std::fopen(filename_update,"r"), 1.1015 + *file_custom = std::fopen(filename_custom,"r"); 1.1016 + if (file_update) { 1.1017 + std::fseek(file_update,0,SEEK_END); 1.1018 + size_update = (unsigned int)std::ftell(file_update); 1.1019 + std::rewind(file_update); 1.1020 + } 1.1021 + if (file_custom) { 1.1022 + std::fseek(file_custom,0,SEEK_END); 1.1023 + size_custom = (unsigned int)std::ftell(file_custom); 1.1024 + std::rewind(file_custom); 1.1025 + } 1.1026 + const unsigned int size_final = size_update + size_custom + size_def + 1; 1.1027 + char *ptrd = gmic_macros = new char[size_final]; 1.1028 + if (size_custom) { ptrd+=std::fread(ptrd,1,size_custom,file_custom); std::fclose(file_custom); } 1.1029 + if (size_update) { ptrd+=std::fread(ptrd,1,size_update,file_update); std::fclose(file_update); } 1.1030 + if (size_def) { std::memcpy(ptrd,data_gmic4gimp_def,size_def); ptrd+=size_def; } 1.1031 + *ptrd = 0; 1.1032 + 1.1033 + // Parse available G'MIC filters definitions. 1.1034 + GtkTreeIter iter, parent[16]; 1.1035 + filter_store = gtk_tree_store_new(2,G_TYPE_UINT,G_TYPE_STRING); 1.1036 + char line[256*1024] = { 0 }, entry[4096] = { 0 }, command[4096] = { 0 }; 1.1037 + char preview_command[4096] = { 0 }, arguments[4096] = { 0 }; 1.1038 + int level = 0; 1.1039 + for (const char *data = gmic_macros; *data; ) { 1.1040 + if (*data=='\n') ++data; 1.1041 + else { 1.1042 + if (std::sscanf(data,"%262143[^\n]\n",line)>0) data += cimg::strlen(line) + 1; 1.1043 + arguments[0] = 0; 1.1044 + if (line[0]=='#') { 1.1045 + const int err = std::sscanf(line,"#@gimp %4095[^:]: %4095[^, ]%*c %4095[^, ]%*c %4095[^\n]", 1.1046 + entry,command,preview_command,arguments); 1.1047 + strparenthesis(entry); 1.1048 + if (err==1) { // If entry is a menu folder. 1.1049 + cimg::strclean(entry); 1.1050 + char *nentry = entry; 1.1051 + while (*nentry=='_') { ++nentry; --level; } 1.1052 + if (level<0) level = 0; 1.1053 + if (level>15) level = 15; 1.1054 + cimg::strclean(nentry); 1.1055 + if (*nentry) { 1.1056 + gtk_tree_store_append(filter_store,&parent[level],level?&parent[level-1]:0); 1.1057 + gtk_tree_store_set(filter_store,&parent[level],0,0,1,nentry,-1); 1.1058 + ++level; 1.1059 + } 1.1060 + } else if (err>=2) { // If entry is a regular filter. 1.1061 + cimg::strclean(entry); 1.1062 + cimg::strclean(command); 1.1063 + gmic_entries.insert(CImg<char>(entry,cimg::strlen(entry)+1)); 1.1064 + gmic_commands.insert(CImg<char>(command,cimg::strlen(command)+1)); 1.1065 + gmic_arguments.insert(CImg<char>(arguments,cimg::strlen(arguments)+1)); 1.1066 + if (err>=3) { 1.1067 + cimg::strclean(preview_command); 1.1068 + gmic_preview_commands.insert(CImg<char>(preview_command,cimg::strlen(preview_command)+1)); 1.1069 + } 1.1070 + gtk_tree_store_append(filter_store,&iter,level?&parent[level-1]:0); 1.1071 + gtk_tree_store_set(filter_store,&iter,0,gmic_entries.size,1,entry,-1); 1.1072 + } 1.1073 + } 1.1074 + } 1.1075 + } 1.1076 + 1.1077 + // Get currenty selected drawable and run image filter on it. 1.1078 + GimpDrawable *drawable = gimp_drawable_get(param[2].data.d_drawable); 1.1079 + gimp_tile_cache_ntiles(2*(drawable->width/gimp_tile_width()+1)); 1.1080 + if (run_mode==GIMP_RUN_INTERACTIVE) { 1.1081 + if (create_dialog_gui(drawable)) { 1.1082 + process_image(drawable,0); 1.1083 + const char *commandline = get_commandline(false); 1.1084 + if (commandline) { // Remember command line for the next use of the filter. 1.1085 + char s_tmp[256] = { 0 }; 1.1086 + std::sprintf(s_tmp,"gmic_commandline%u",get_current_filter()); 1.1087 + gimp_set_data(s_tmp,commandline,cimg::strlen(commandline)); 1.1088 + } 1.1089 + } 1.1090 + } else if (run_mode==GIMP_RUN_WITH_LAST_VALS) { 1.1091 + const unsigned int filter = get_current_filter(); 1.1092 + if (filter) { 1.1093 + char s_tmp[256] = { 0 }; 1.1094 + std::sprintf(s_tmp,"gmic_commandline%u",filter); 1.1095 + char commandline[4096] = { 0 }; 1.1096 + gimp_get_data(s_tmp,&commandline); 1.1097 + process_image(drawable,commandline); 1.1098 + } 1.1099 + } 1.1100 + 1.1101 + // Free plug-in resources. 1.1102 + delete[] gmic_macros; 1.1103 + values[0].data.d_status = GIMP_PDB_SUCCESS; 1.1104 + is_existing_instance = false; 1.1105 + gimp_set_data("gmic_instance",&is_existing_instance,sizeof(bool)); 1.1106 +} 1.1107 + 1.1108 +// 'Query' function needed by GIMP plug-in API. 1.1109 +//--------------------------------------------- 1.1110 +void gmic_query() { 1.1111 + static const GimpParamDef args[] = { 1.1112 + {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, 1.1113 + {GIMP_PDB_IMAGE, "image", "(unused)"}, 1.1114 + {GIMP_PDB_DRAWABLE, "drawable", "Drawable to draw on"} 1.1115 + }; 1.1116 + 1.1117 + gimp_install_procedure("gmic", // name 1.1118 + "G'MIC Toolbox", // blurb 1.1119 + "G'MIC Toolbox", // help 1.1120 + "David Tschumperle", // author 1.1121 + "David Tschumperle", // copyright 1.1122 + "2008-12-02", // date 1.1123 + "_G'MIC Toolbox...", // menu_path 1.1124 + "RGB*, GRAY*", // image_types 1.1125 + GIMP_PLUGIN, // type 1.1126 + G_N_ELEMENTS(args), // nparams 1.1127 + 0, // nreturn_vals 1.1128 + args, // params 1.1129 + 0); // return_vals 1.1130 + 1.1131 + gimp_plugin_menu_register("gmic", "<Image>/Filters"); 1.1132 +} 1.1133 + 1.1134 +GimpPlugInInfo PLUG_IN_INFO = { 0, 0, gmic_query, gmic_run }; 1.1135 +MAIN();