PTdecode/CImg-1.3.0/examples/gmic4gimp.cpp

Mon, 03 Aug 2009 23:39:53 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 03 Aug 2009 23:39:53 +0100
changeset 10
604c205d9163
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

add basic test routine for Ptouch library

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : gmic4gimp.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : G'MIC for GIMP - A plug-in to allow the use
philpem@5 7 # of G'MIC commands in GIMP.
philpem@5 8 # This file is a part of the CImg Library project.
philpem@5 9 # ( http://cimg.sourceforge.net )
philpem@5 10 #
philpem@5 11 # Copyright : David Tschumperle (GREYCstoration API)
philpem@5 12 #
philpem@5 13 # License : CeCILL v2.0
philpem@5 14 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
philpem@5 15 #
philpem@5 16 # This software is governed by the CeCILL license under French law and
philpem@5 17 # abiding by the rules of distribution of free software. You can use,
philpem@5 18 # modify and/ or redistribute the software under the terms of the CeCILL
philpem@5 19 # license as circulated by CEA, CNRS and INRIA at the following URL
philpem@5 20 # "http://www.cecill.info".
philpem@5 21 #
philpem@5 22 # As a counterpart to the access to the source code and rights to copy,
philpem@5 23 # modify and redistribute granted by the license, users are provided only
philpem@5 24 # with a limited warranty and the software's author, the holder of the
philpem@5 25 # economic rights, and the successive licensors have only limited
philpem@5 26 # liability.
philpem@5 27 #
philpem@5 28 # In this respect, the user's attention is drawn to the risks associated
philpem@5 29 # with loading, using, modifying and/or developing or reproducing the
philpem@5 30 # software by the user in light of its specific status of free software,
philpem@5 31 # that may mean that it is complicated to manipulate, and that also
philpem@5 32 # therefore means that it is reserved for developers and experienced
philpem@5 33 # professionals having in-depth computer knowledge. Users are therefore
philpem@5 34 # encouraged to load and test the software's suitability as regards their
philpem@5 35 # requirements in conditions enabling the security of their systems and/or
philpem@5 36 # data to be ensured and, more generally, to use and operate it in the
philpem@5 37 # same conditions as regards security.
philpem@5 38 #
philpem@5 39 # The fact that you are presently reading this means that you have had
philpem@5 40 # knowledge of the CeCILL license and that you accept its terms.
philpem@5 41 #
philpem@5 42 */
philpem@5 43
philpem@5 44 // Include necessary header files.
philpem@5 45 //--------------------------------
philpem@5 46 #define cimg_display_type 0
philpem@5 47 #include "gmic.h"
philpem@5 48 #include "gmic4gimp_def.h"
philpem@5 49 #include <pthread.h>
philpem@5 50 #include <locale>
philpem@5 51 #include <gtk/gtk.h>
philpem@5 52 #include <libgimp/gimp.h>
philpem@5 53 #include <libgimp/gimpui.h>
philpem@5 54 using namespace cimg_library;
philpem@5 55
philpem@5 56 // Define plug-in global variables.
philpem@5 57 //---------------------------------
philpem@5 58 CImgList<char> gmic_entries; // The list of recognized G'MIC menu entries (stored as 'char*' strings).
philpem@5 59 CImgList<char> gmic_commands; // The list of corresponding G'MIC commands to process the image.
philpem@5 60 CImgList<char> gmic_preview_commands; // The list of corresponding G'MIC commands to preview the image.
philpem@5 61 CImgList<char> gmic_arguments; // The list of corresponding needed filter arguments.
philpem@5 62 char *gmic_macros; // The array of customized G'MIC macros.
philpem@5 63 GtkTreeStore *filter_store; // A list of available filter entries (used by the GtkTreeView).
philpem@5 64 bool return_create_dialog; // Return value of the 'create_gui_dialog()' function (set by events handlers).
philpem@5 65 void **event_infos; // Infos that are passed to the GUI callback functions.
philpem@5 66 char *path_home; // The path where configuration files are looked for.
philpem@5 67
philpem@5 68 // Replace '[]' by '()' in a C-string.
philpem@5 69 //------------------------------------
philpem@5 70 void strparenthesis(char *const s) {
philpem@5 71 for (char *ns = s; *ns; ++ns) if (*ns=='[') *ns = '('; else if (*ns==']') *ns = ')';
philpem@5 72 }
philpem@5 73
philpem@5 74 // Set/get plug-in global variables in GIMP.
philpem@5 75 //------------------------------------------
philpem@5 76 void set_current_filter(const unsigned int current_filter) {
philpem@5 77 const unsigned int ncurrent_filter = current_filter>gmic_entries.size?0:current_filter;
philpem@5 78 gimp_set_data("gmic_current_filter",&ncurrent_filter,sizeof(unsigned int));
philpem@5 79 }
philpem@5 80
philpem@5 81 unsigned int get_current_filter() {
philpem@5 82 unsigned int current_filter = 0;
philpem@5 83 gimp_get_data("gmic_current_filter",&current_filter);
philpem@5 84 if (current_filter>gmic_entries.size) current_filter = 0;
philpem@5 85 return current_filter;
philpem@5 86 }
philpem@5 87
philpem@5 88 void set_filter_nbparams(const unsigned int filter, const unsigned int nbparams) {
philpem@5 89 char s_tmp[256] = { 0 };
philpem@5 90 std::sprintf(s_tmp,"gmic_filter%u_nbparams",filter);
philpem@5 91 gimp_set_data(s_tmp,&nbparams,sizeof(unsigned int));
philpem@5 92 }
philpem@5 93
philpem@5 94 unsigned int get_filter_nbparams(const unsigned int filter) {
philpem@5 95 char s_tmp[256] = { 0 };
philpem@5 96 std::sprintf(s_tmp,"gmic_filter%u_nbparams",filter);
philpem@5 97 unsigned int nbparams = 0;
philpem@5 98 gimp_get_data(s_tmp,&nbparams);
philpem@5 99 return nbparams;
philpem@5 100 }
philpem@5 101
philpem@5 102 void set_filter_parameter(const unsigned int filter, const unsigned int n, const char *const param) {
philpem@5 103 char s_tmp[256] = { 0 };
philpem@5 104 std::sprintf(s_tmp,"gmic_filter%u_parameter%u",filter,n);
philpem@5 105 gimp_set_data(s_tmp,param,cimg::strlen(param)+1);
philpem@5 106 }
philpem@5 107
philpem@5 108 const char *get_filter_parameter(const unsigned int filter, const unsigned int n) {
philpem@5 109 char s_tmp[256] = { 0 };
philpem@5 110 std::sprintf(s_tmp,"gmic_filter%u_parameter%u",filter,n);
philpem@5 111 static char res[4096] = { 0 };
philpem@5 112 res[0] = 0;
philpem@5 113 gimp_get_data(s_tmp,res);
philpem@5 114 return res;
philpem@5 115 }
philpem@5 116
philpem@5 117 unsigned int get_verbosity_level() {
philpem@5 118 unsigned int verbosity = 0;
philpem@5 119 gimp_get_data("gmic_verbosity",&verbosity);
philpem@5 120 return verbosity;
philpem@5 121 }
philpem@5 122
philpem@5 123 void set_verbosity_level(const unsigned int verbosity) {
philpem@5 124 gimp_set_data("gmic_verbosity",&verbosity,sizeof(unsigned int));
philpem@5 125 }
philpem@5 126
philpem@5 127 // Return G'MIC command line needed to run the selected filter.
philpem@5 128 //--------------------------------------------------------------
philpem@5 129 const char* get_commandline(const bool preview) {
philpem@5 130 const unsigned int
philpem@5 131 verbosity_level = get_verbosity_level(),
philpem@5 132 filter = get_current_filter(),
philpem@5 133 nbparams = get_filter_nbparams(filter);
philpem@5 134 if (!filter) return 0;
philpem@5 135
philpem@5 136 static CImg<char> res;
philpem@5 137
philpem@5 138 CImgList<char> lres;
philpem@5 139 switch (verbosity_level) {
philpem@5 140 case 0: lres.insert(CImg<char>("-v- -",5)); break;
philpem@5 141 case 1: lres.insert(CImg<char>("-",1)); break;
philpem@5 142 default: lres.insert(CImg<char>("-v+ -debug -",12));
philpem@5 143 }
philpem@5 144
philpem@5 145 const unsigned int N = filter - 1;
philpem@5 146 const CImg<char> &command_item = (preview?gmic_preview_commands[N]:gmic_commands[N]);
philpem@5 147 if (command_item) {
philpem@5 148 lres.insert(command_item);
philpem@5 149 if (nbparams) {
philpem@5 150 lres[1].last() = ' ';
philpem@5 151 for (unsigned int p = 0; p<nbparams; ++p) {
philpem@5 152 const char *const param = get_filter_parameter(filter,p);
philpem@5 153 if (param) lres.insert(CImg<char>(param,cimg::strlen(param)+1)).last().last() = ',';
philpem@5 154 }
philpem@5 155 }
philpem@5 156 (res = lres.get_append('x')).last() = 0;
philpem@5 157 }
philpem@5 158 return res.ptr();
philpem@5 159 }
philpem@5 160
philpem@5 161 // Process image region with G'MIC.
philpem@5 162 //---------------------------------
philpem@5 163
philpem@5 164 // Define structure to store the arguments needed by the processing thread.
philpem@5 165 struct st_process_thread {
philpem@5 166 pthread_t thread;
philpem@5 167 CImgList<float> images;
philpem@5 168 const char *commandline;
philpem@5 169 unsigned int verbosity_level;
philpem@5 170 pthread_mutex_t is_running;
philpem@5 171 };
philpem@5 172
philpem@5 173 // Thread that does the image processing part (call the G'MIC library).
philpem@5 174 void *process_thread(void *arg) {
philpem@5 175 st_process_thread &spt = *(st_process_thread*)arg;
philpem@5 176 try {
philpem@5 177 if (spt.verbosity_level>0)
philpem@5 178 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running G'MIC to process the image, with command : %s\n",spt.commandline);
philpem@5 179 std::setlocale(LC_NUMERIC,"C");
philpem@5 180 gmic(spt.commandline,spt.images,gmic_macros,false);
philpem@5 181 if (spt.verbosity_level>0)
philpem@5 182 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : G'MIC successfully returned !\n");
philpem@5 183 } catch (gmic_exception &e) {
philpem@5 184 if (spt.verbosity_level>0)
philpem@5 185 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Error encountered when running G'MIC :\n*** %s\n",e.message);
philpem@5 186 spt.images.assign();
philpem@5 187 }
philpem@5 188 pthread_mutex_unlock(&spt.is_running);
philpem@5 189 pthread_exit(0);
philpem@5 190 return 0;
philpem@5 191 }
philpem@5 192
philpem@5 193 // Routine called to process the current GIMP image.
philpem@5 194 void process_image(GimpDrawable *drawable, const char *last_commandline) {
philpem@5 195 const unsigned int filter = get_current_filter();
philpem@5 196 if (!last_commandline && !filter) return;
philpem@5 197 const char *commandline = last_commandline?last_commandline:get_commandline(false);
philpem@5 198 if (!commandline || !cimg::strcmp(commandline,"-v- -nop")) return;
philpem@5 199 gimp_progress_init_printf(" G'MIC Toolbox : %s...",gmic_entries[filter-1].ptr());
philpem@5 200
philpem@5 201 // Read GIMP image region data and make a CImg<float> instance from it.
philpem@5 202 GimpPixelRgn src_region;
philpem@5 203 gint x1, y1, x2, y2;
philpem@5 204 gimp_drawable_mask_bounds(drawable->drawable_id,&x1,&y1,&x2,&y2); // Get coordinates of the current layer selection.
philpem@5 205 const gint width = x2 - x1, height = y2 - y1, channels = drawable->bpp;
philpem@5 206 gimp_pixel_rgn_init(&src_region,drawable,x1,y1,width,height,false,false);
philpem@5 207 guchar *const src_row = g_new(guchar,width*channels);
philpem@5 208 CImg<float> img(width,height,1,channels);
philpem@5 209 cimg_forY(img,y) {
philpem@5 210 gimp_pixel_rgn_get_row(&src_region,src_row,x1,y1+y,width);
philpem@5 211 const guchar *ptrs = src_row;
philpem@5 212 cimg_forX(img,x) cimg_forV(img,k) img(x,y,k) = (float)*(ptrs++);
philpem@5 213 }
philpem@5 214 g_free(src_row);
philpem@5 215
philpem@5 216 // Call G'MIC interpreter on the CImg<float> image in a new thread.
philpem@5 217 st_process_thread spt;
philpem@5 218 spt.images.assign(1);
philpem@5 219 img.transfer_to(spt.images[0]);
philpem@5 220 spt.commandline = commandline;
philpem@5 221 spt.verbosity_level = get_verbosity_level();
philpem@5 222 pthread_mutex_init(&spt.is_running,0);
philpem@5 223 pthread_mutex_lock(&spt.is_running);
philpem@5 224 pthread_create(&(spt.thread),0,process_thread,(void*)&spt);
philpem@5 225
philpem@5 226 // Do a small animation with the progress bar, while waiting for
philpem@5 227 // the termination of the processing thread.
philpem@5 228 while (pthread_mutex_trylock(&spt.is_running)) { gimp_progress_pulse(); cimg::wait(500); }
philpem@5 229 gimp_progress_update(1.0);
philpem@5 230 pthread_join(spt.thread,0);
philpem@5 231 pthread_mutex_unlock(&spt.is_running);
philpem@5 232 pthread_mutex_destroy(&spt.is_running);
philpem@5 233
philpem@5 234 // Force the resulting images to have all the same 2D GRAY, GRAYA, RGB or RGBA format.
philpem@5 235 if (!spt.images) { gimp_progress_end(); return; }
philpem@5 236 unsigned int max_width = 0, max_height = 0, max_channels = 0;
philpem@5 237 cimglist_for(spt.images,p) {
philpem@5 238 const CImg<float>& img = spt.images[p];
philpem@5 239 if (img.width>max_width) max_width = img.width;
philpem@5 240 if (img.height>max_height) max_height = img.height;
philpem@5 241 if (img.dim>max_channels) max_channels = img.dim;
philpem@5 242 }
philpem@5 243 if (max_channels>4) max_channels = 4;
philpem@5 244 cimglist_apply(spt.images,resize)(-100,-100,1,max_channels);
philpem@5 245
philpem@5 246 // Transfer the result image back into GIMP.
philpem@5 247 if (spt.images.size==1 && (int)max_width==width && (int)max_height==height && (int)max_channels==channels) {
philpem@5 248
philpem@5 249 // When the result image has same dimensions than the source :
philpem@5 250 // Replace the selected region of the original GIMP image.
philpem@5 251 CImg<float> &res = spt.images[0];
philpem@5 252 GimpPixelRgn dest_region;
philpem@5 253 guchar *const dest_row = g_new(guchar,res.dimx()*res.dimv());
philpem@5 254 gimp_pixel_rgn_init(&dest_region,drawable,0,0,drawable->width,drawable->height,true,true);
philpem@5 255 cimg_forY(res,y) {
philpem@5 256 guchar *ptrd = dest_row;
philpem@5 257 cimg_forX(res,x) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k);
philpem@5 258 gimp_pixel_rgn_set_row(&dest_region,dest_row,x1,y1+y,width);
philpem@5 259 }
philpem@5 260 g_free(dest_row);
philpem@5 261 spt.images.assign();
philpem@5 262 gimp_drawable_flush(drawable);
philpem@5 263 gimp_drawable_merge_shadow(drawable->drawable_id,true);
philpem@5 264 gimp_drawable_update(drawable->drawable_id,x1,y1,x2-x1,y2-y1);
philpem@5 265 gimp_displays_flush();
philpem@5 266 } else {
philpem@5 267
philpem@5 268 // When the result image has different dimensions than the source :
philpem@5 269 // Returns a new GIMP image.
philpem@5 270 gint id_img = gimp_image_new(max_width,max_height,max_channels<=2?GIMP_GRAY:GIMP_RGB);
philpem@5 271 gimp_image_undo_group_start(id_img);
philpem@5 272
philpem@5 273 cimglist_for(spt.images,p) {
philpem@5 274 CImg<float> &res = spt.images[p];
philpem@5 275 gint id_layer = gimp_layer_new(id_img,"image",res.dimx(),res.dimy(),
philpem@5 276 res.dimv()==1?GIMP_GRAY_IMAGE:
philpem@5 277 res.dimv()==2?GIMP_GRAYA_IMAGE:
philpem@5 278 res.dimv()==3?GIMP_RGB_IMAGE:
philpem@5 279 GIMP_RGBA_IMAGE,
philpem@5 280 100.0,GIMP_NORMAL_MODE);
philpem@5 281 gimp_image_add_layer(id_img,id_layer,0);
philpem@5 282 GimpDrawable *ndrawable = gimp_drawable_get(id_layer);
philpem@5 283
philpem@5 284 GimpPixelRgn dest_region;
philpem@5 285 guchar *const dest_row = g_new(guchar,res.dimx()*res.dimv());
philpem@5 286 gimp_pixel_rgn_init(&dest_region,ndrawable,0,0,ndrawable->width,ndrawable->height,true,true);
philpem@5 287 cimg_forY(res,y) {
philpem@5 288 guchar *ptrd = dest_row;
philpem@5 289 cimg_forX(res,x) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k);
philpem@5 290 gimp_pixel_rgn_set_row(&dest_region,dest_row,0,y,res.dimx());
philpem@5 291 }
philpem@5 292 g_free(dest_row);
philpem@5 293 res.assign();
philpem@5 294 gimp_drawable_flush(ndrawable);
philpem@5 295 gimp_drawable_merge_shadow(ndrawable->drawable_id,true);
philpem@5 296 gimp_drawable_update(ndrawable->drawable_id,0,0,ndrawable->width,ndrawable->height);
philpem@5 297 gimp_drawable_detach(ndrawable);
philpem@5 298 }
philpem@5 299 gimp_display_new(id_img);
philpem@5 300 gimp_image_undo_group_end(id_img);
philpem@5 301 gimp_displays_flush();
philpem@5 302 }
philpem@5 303 gimp_progress_end();
philpem@5 304 }
philpem@5 305
philpem@5 306 // Process preview with G'MIC.
philpem@5 307 //-----------------------------
philpem@5 308 void process_preview(GimpPreview *preview) {
philpem@5 309 const unsigned int filter = get_current_filter();
philpem@5 310 if (!filter) return;
philpem@5 311 const char *const commandline = get_commandline(true);
philpem@5 312 if (!commandline || !cimg::strcmp(commandline,"-v- -nop")) return;
philpem@5 313
philpem@5 314 // Read GIMP image preview and make a CImg<float> instance from it.
philpem@5 315 gint width, height, channels;
philpem@5 316 guchar *const ptr0 = gimp_zoom_preview_get_source(GIMP_ZOOM_PREVIEW(preview),&width,&height,&channels), *ptrs = ptr0;
philpem@5 317 CImg<float> img(width,height,1,channels);
philpem@5 318 cimg_forXY(img,x,y) cimg_forV(img,k) img(x,y,k) = (float)*(ptrs++);
philpem@5 319
philpem@5 320 // Call G'MIC interpreter on the preview image.
philpem@5 321 CImgList<float> gmic_images(1);
philpem@5 322 img.transfer_to(gmic_images[0]);
philpem@5 323 try {
philpem@5 324 if (get_verbosity_level()>0)
philpem@5 325 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running G'MIC to process the preview, with command : %s\n",commandline);
philpem@5 326 std::setlocale(LC_NUMERIC,"C");
philpem@5 327 gmic(commandline,gmic_images,gmic_macros,false);
philpem@5 328 if (get_verbosity_level()>0)
philpem@5 329 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : G'MIC successfully returned !\n");
philpem@5 330 } catch (gmic_exception &e) {
philpem@5 331 if (get_verbosity_level()>0)
philpem@5 332 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Error encountered when running G'MIC :\n*** %s\n",e.message);
philpem@5 333 gmic_images.assign();
philpem@5 334 }
philpem@5 335
philpem@5 336 // Get current image preview from the processed data.
philpem@5 337 if (gmic_images.size && gmic_images[0]) {
philpem@5 338 CImg<float>& res = gmic_images[0];
philpem@5 339 if (res.width>res.height) {
philpem@5 340 const unsigned int _nheight = res.height*width/res.width, nheight = _nheight?_nheight:1;
philpem@5 341 res.resize(width,nheight,1,-100,2);
philpem@5 342 } else {
philpem@5 343 const unsigned int _nwidth = res.width*height/res.height, nwidth = _nwidth?_nwidth:1;
philpem@5 344 res.resize(nwidth,height,1,-100,2);
philpem@5 345 }
philpem@5 346 if (res.dimx()!=width || res.dimy()!=height) res.resize(width,height,1,-100,0,0,1);
philpem@5 347 switch (channels) {
philpem@5 348 case 1:
philpem@5 349 switch (res.dim) {
philpem@5 350 case 1: break;
philpem@5 351 case 2: res.channel(0); break;
philpem@5 352 case 3: res.channel(0); break;
philpem@5 353 case 4: res.channel(0); break;
philpem@5 354 default: res.channel(0);
philpem@5 355 } break;
philpem@5 356 case 2:
philpem@5 357 switch (res.dim) {
philpem@5 358 case 1: res.resize(-100,-100,1,2,0).get_shared_channel(1).fill(255); break;
philpem@5 359 case 2: break;
philpem@5 360 case 3: res.channels(0,1).get_shared_channel(1).fill(255); break;
philpem@5 361 case 4: res.get_shared_channel(1) = res.get_shared_channel(3); res.channels(0,1); break;
philpem@5 362 default: res.channels(0,1).get_shared_channel(1).fill(255);
philpem@5 363 } break;
philpem@5 364 case 3:
philpem@5 365 switch (res.dim) {
philpem@5 366 case 1: res.resize(-100,-100,1,3); break;
philpem@5 367 case 2: res.channel(0).resize(-100,-100,1,3); break;
philpem@5 368 case 3: break;
philpem@5 369 case 4: res.channels(0,2); break;
philpem@5 370 default: res.channels(0,2);
philpem@5 371 } break;
philpem@5 372 case 4:
philpem@5 373 switch (res.dim) {
philpem@5 374 case 1: res.resize(-100,-100,1,4).get_shared_channel(3).fill(255); break;
philpem@5 375 case 2:
philpem@5 376 res.resize(-100,-100,1,4,0);
philpem@5 377 res.get_shared_channel(3) = res.get_shared_channel(1);
philpem@5 378 res.get_shared_channel(1) = res.get_shared_channel(0);
philpem@5 379 res.get_shared_channel(2) = res.get_shared_channel(0);
philpem@5 380 break;
philpem@5 381 case 3: res.resize(-100,-100,1,4,0).get_shared_channel(3).fill(255); break;
philpem@5 382 case 4: break;
philpem@5 383 default: res.resize(-100,-100,1,4,0);
philpem@5 384 } break;
philpem@5 385 }
philpem@5 386 guchar *ptrd = ptr0;
philpem@5 387 cimg_forXY(res,x,y) cimg_forV(res,k) *(ptrd++) = (guchar)res(x,y,k);
philpem@5 388 gimp_preview_draw_buffer(preview,ptr0,width*channels);
philpem@5 389 g_free(ptr0);
philpem@5 390 }
philpem@5 391 }
philpem@5 392
philpem@5 393 // Define event functions for GUI.
philpem@5 394 //--------------------------------
philpem@5 395
philpem@5 396 // Handle responses to the parameter widgets.
philpem@5 397 void on_float_parameter_changed(GtkAdjustment *scale, gpointer user_data) {
philpem@5 398 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 399 double value = 0;
philpem@5 400 gimp_double_adjustment_update(scale,&value);
philpem@5 401 char s_value[1024] = { 0 };
philpem@5 402 std::sprintf(s_value,"%g",value);
philpem@5 403 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 404 return_create_dialog = true;
philpem@5 405 }
philpem@5 406
philpem@5 407 void on_int_parameter_changed(GtkAdjustment *scale, gpointer user_data) {
philpem@5 408 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 409 int value = 0;
philpem@5 410 gimp_int_adjustment_update(scale,&value);
philpem@5 411 char s_value[1024] = { 0 };
philpem@5 412 std::sprintf(s_value,"%d",value);
philpem@5 413 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 414 return_create_dialog = true;
philpem@5 415 }
philpem@5 416
philpem@5 417 void on_bool_parameter_changed(GtkCheckButton *checkbutton, gpointer user_data) {
philpem@5 418 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 419 int value = 0;
philpem@5 420 g_object_get(checkbutton,"active",&value,NULL);
philpem@5 421 char s_value[1024] = { 0 };
philpem@5 422 std::sprintf(s_value,"%d",value?1:0);
philpem@5 423 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 424 return_create_dialog = true;
philpem@5 425 }
philpem@5 426
philpem@5 427 void on_list_parameter_changed(GtkComboBox *combobox, gpointer user_data) {
philpem@5 428 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 429 int value = 0;
philpem@5 430 g_object_get(combobox,"active",&value,NULL);
philpem@5 431 char s_value[1024] = { 0 };
philpem@5 432 std::sprintf(s_value,"%d",value);
philpem@5 433 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 434 return_create_dialog = true;
philpem@5 435 }
philpem@5 436
philpem@5 437 void on_text_parameter_changed(GtkButton *button, gpointer user_data) {
philpem@5 438 button = 0;
philpem@5 439 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 440 GtkWidget *entry = *((GtkWidget**)user_data+1);
philpem@5 441 const char *s_value = gtk_entry_get_text(GTK_ENTRY(entry));
philpem@5 442 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 443 return_create_dialog = true;
philpem@5 444 }
philpem@5 445
philpem@5 446 void on_file_parameter_changed(GtkFileChooserButton *widget, gpointer user_data){
philpem@5 447 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 448 const char
philpem@5 449 *const filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)),
philpem@5 450 *s_value = filename?filename:"";
philpem@5 451 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 452 return_create_dialog = true;
philpem@5 453 }
philpem@5 454
philpem@5 455 void on_color_parameter_changed(GtkColorButton *widget, gpointer user_data){
philpem@5 456 const unsigned int arg = *(unsigned int*)user_data;
philpem@5 457 GdkColor color;
philpem@5 458 gtk_color_button_get_color(GTK_COLOR_BUTTON(widget),&color);
philpem@5 459 char s_value[1024] = { 0 };
philpem@5 460 if (gtk_color_button_get_use_alpha(GTK_COLOR_BUTTON(widget)))
philpem@5 461 std::sprintf(s_value,"%d,%d,%d,%d",
philpem@5 462 color.red>>8,color.green>>8,color.blue>>8,gtk_color_button_get_alpha(GTK_COLOR_BUTTON(widget))>>8);
philpem@5 463 else std::sprintf(s_value,"%d,%d,%d",
philpem@5 464 color.red>>8,color.green>>8,color.blue>>8);
philpem@5 465 set_filter_parameter(get_current_filter(),arg,s_value);
philpem@5 466 return_create_dialog = true;
philpem@5 467 }
philpem@5 468
philpem@5 469 // Create parameter GUI for specific chosen filter.
philpem@5 470 //--------------------------------------------------
philpem@5 471 void create_parameters_gui(const bool reset) {
philpem@5 472 const unsigned int filter = get_current_filter();
philpem@5 473
philpem@5 474 // Remove widget in the current frame if necessary.
philpem@5 475 GtkWidget *frame = 0;
philpem@5 476 gimp_get_data("gmic_gui_frame",&frame);
philpem@5 477 if (frame) {
philpem@5 478 GtkWidget *child = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(frame)));
philpem@5 479 if (child) gtk_container_remove(GTK_CONTAINER(frame),child);
philpem@5 480 }
philpem@5 481
philpem@5 482 GtkWidget *table = 0;
philpem@5 483 if (!filter) { // No filter selected -> Default message.
philpem@5 484 table = gtk_table_new(1,1,false);
philpem@5 485 gtk_widget_show(table);
philpem@5 486 GtkWidget *label = gtk_label_new(NULL);
philpem@5 487 gtk_label_set_markup(GTK_LABEL(label),"<i>Select a filter...</i>");
philpem@5 488 gtk_widget_show(label);
philpem@5 489 gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
philpem@5 490 (GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0);
philpem@5 491 gtk_misc_set_alignment (GTK_MISC(label),0,0.5);
philpem@5 492 gtk_frame_set_label(GTK_FRAME(frame),NULL);
philpem@5 493 } else { // Filter selected -> Build parameter table.
philpem@5 494 GtkWidget *preview = 0;
philpem@5 495 gimp_get_data("gmic_gui_preview",&preview);
philpem@5 496 const unsigned int N = filter - 1;
philpem@5 497 char nlabel[4096] = { 0 };
philpem@5 498 std::sprintf(nlabel,"<b> %s : </b>",gmic_entries[N].ptr());
philpem@5 499 GtkWidget *frame_title = gtk_label_new(NULL);
philpem@5 500 gtk_widget_show(frame_title);
philpem@5 501 gtk_label_set_markup(GTK_LABEL(frame_title),nlabel);
philpem@5 502 gtk_frame_set_label_widget(GTK_FRAME(frame),frame_title);
philpem@5 503
philpem@5 504 char argname[4096] = { 0 }, argtype[4096] = { 0 }, argarg[4096] = { 0 };
philpem@5 505 unsigned int nb_arguments = 0;
philpem@5 506 for (const char *argument = gmic_arguments[N].ptr(); *argument; ) {
philpem@5 507 if (std::sscanf(argument,"%4095[^=]=%4095[^(](%4095[^)]",argname,argtype,&(argarg[0]=0))>=2) {
philpem@5 508 argument += cimg::strlen(argname) + cimg::strlen(argtype) + cimg::strlen(argarg) + 3;
philpem@5 509 if (*argument) ++argument;
philpem@5 510 ++nb_arguments;
philpem@5 511 } else break;
philpem@5 512 }
philpem@5 513
philpem@5 514 if (!nb_arguments) { // Selected filter has no parameters -> Default message.
philpem@5 515 table = gtk_table_new(1,1,false);
philpem@5 516 gtk_widget_show(table);
philpem@5 517 GtkWidget *label = gtk_label_new(NULL);
philpem@5 518 gtk_label_set_markup(GTK_LABEL(label),"<i>No parameters to set...</i>");
philpem@5 519 gtk_widget_show(label);
philpem@5 520 gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
philpem@5 521 (GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0);
philpem@5 522 gtk_misc_set_alignment (GTK_MISC(label),0,0.5);
philpem@5 523 } else { // Selected filter has parameters -> Create parameter table.
philpem@5 524
philpem@5 525 // Create new table for putting parameters inside.
philpem@5 526 table = gtk_table_new(3,nb_arguments,false);
philpem@5 527 gtk_widget_show(table);
philpem@5 528 gtk_table_set_row_spacings(GTK_TABLE(table),6);
philpem@5 529 gtk_table_set_col_spacings(GTK_TABLE(table),6);
philpem@5 530 gtk_container_set_border_width(GTK_CONTAINER(table),8);
philpem@5 531
philpem@5 532 // Parse arguments list and add recognized one to the table.
philpem@5 533 event_infos = new void*[2*nb_arguments];
philpem@5 534 int current_parameter = 0, current_line = 0;
philpem@5 535 for (const char *argument = gmic_arguments[N].ptr(); *argument; ) {
philpem@5 536 if (std::sscanf(argument,"%4095[^=]=%4095[^(](%4095[^)]",argname,argtype,&(argarg[0]=0))>=2) {
philpem@5 537 argument += cimg::strlen(argname) + cimg::strlen(argtype) + cimg::strlen(argarg) + 3;
philpem@5 538 if (*argument) ++argument;
philpem@5 539 cimg::strclean(argname);
philpem@5 540 cimg::strclean(argtype);
philpem@5 541 const char *const s_value = get_filter_parameter(filter,current_parameter);
philpem@5 542
philpem@5 543 // Check for a float-valued parameter -> Create GtkAdjustment.
philpem@5 544 bool found_valid_item = false;
philpem@5 545 if (!found_valid_item && !cimg::strcasecmp(argtype,"float")) {
philpem@5 546 float initial_value = 0, min_value = 0, max_value = 100;
philpem@5 547 std::setlocale(LC_NUMERIC,"C");
philpem@5 548 std::sscanf(argarg,"%f%*c%f%*c%f",&initial_value,&min_value,&max_value);
philpem@5 549 if (!reset && std::sscanf(s_value,"%f",&initial_value)) {}
philpem@5 550 GtkObject *scale = gimp_scale_entry_new(GTK_TABLE(table),0,current_line,argname,100,6,
philpem@5 551 (gdouble)initial_value,(gdouble)min_value,(gdouble)max_value,
philpem@5 552 0.1,0.1,2,true,0,0,0,0);
philpem@5 553 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 554 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 555 on_float_parameter_changed(GTK_ADJUSTMENT(scale),(void*)(event_infos+2*current_parameter));
philpem@5 556 g_signal_connect(scale,"value_changed",G_CALLBACK(on_float_parameter_changed),
philpem@5 557 (void*)(event_infos+2*current_parameter));
philpem@5 558 g_signal_connect_swapped(scale,"value_changed",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 559 found_valid_item = true;
philpem@5 560 ++current_parameter;
philpem@5 561 }
philpem@5 562
philpem@5 563 // Check for an int-valued parameter -> Create GtkAdjustment.
philpem@5 564 if (!found_valid_item && !cimg::strcasecmp(argtype,"int")) {
philpem@5 565 float initial_value = 0, min_value = 0, max_value = 100;
philpem@5 566 std::setlocale(LC_NUMERIC,"C");
philpem@5 567 std::sscanf(argarg,"%f%*c%f%*c%f",&initial_value,&min_value,&max_value);
philpem@5 568 if (!reset && std::sscanf(s_value,"%f",&initial_value)) {}
philpem@5 569 GtkObject *scale = gimp_scale_entry_new(GTK_TABLE(table),0,current_line,argname,100,6,
philpem@5 570 (gdouble)(int)initial_value,(gdouble)(int)min_value,
philpem@5 571 (gdouble)(int)max_value,
philpem@5 572 1,1,0,true,0,0,0,0);
philpem@5 573 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 574 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 575 on_int_parameter_changed(GTK_ADJUSTMENT(scale),(void*)(event_infos+2*current_parameter));
philpem@5 576 g_signal_connect(scale,"value_changed",G_CALLBACK(on_int_parameter_changed),
philpem@5 577 (void*)(event_infos+2*current_parameter));
philpem@5 578 g_signal_connect_swapped(scale,"value_changed",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 579 found_valid_item = true;
philpem@5 580 ++current_parameter;
philpem@5 581 }
philpem@5 582
philpem@5 583 // Check for a bool-valued parameter -> Create GtkCheckButton.
philpem@5 584 if (!found_valid_item && !cimg::strcasecmp(argtype,"bool")) {
philpem@5 585 unsigned int initial_value = 0;
philpem@5 586 std::sscanf(argarg,"%u",&initial_value);
philpem@5 587 if (!reset && std::sscanf(s_value,"%u",&initial_value)) {}
philpem@5 588 GtkWidget *checkbutton = gtk_check_button_new_with_label(argname);
philpem@5 589 gtk_widget_show(checkbutton);
philpem@5 590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton),initial_value?true:false);
philpem@5 591 gtk_table_attach(GTK_TABLE(table),checkbutton,0,2,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 592 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 593 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 594 on_bool_parameter_changed(GTK_CHECK_BUTTON(checkbutton),(void*)(event_infos+2*current_parameter));
philpem@5 595 g_signal_connect(checkbutton,"toggled",G_CALLBACK(on_bool_parameter_changed),
philpem@5 596 (void*)(event_infos+2*current_parameter));
philpem@5 597 g_signal_connect_swapped(checkbutton,"toggled",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 598 found_valid_item = true;
philpem@5 599 ++current_parameter;
philpem@5 600 }
philpem@5 601
philpem@5 602 // Check for a list-valued parameter -> Create GtkComboBox.
philpem@5 603 if (!found_valid_item && !cimg::strcasecmp(argtype,"choice")) {
philpem@5 604 GtkWidget *label = gtk_label_new(argname);
philpem@5 605 gtk_widget_show(label);
philpem@5 606 gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 607 gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
philpem@5 608 GtkWidget *combobox = gtk_combo_box_new_text();
philpem@5 609 gtk_widget_show(combobox);
philpem@5 610 char s_entry[4096] = { 0 }, end = 0; int err2 = 0;
philpem@5 611 unsigned int initial_value = 0;
philpem@5 612 const char *entries = argarg;
philpem@5 613 if (std::sscanf(entries,"%u",&initial_value)==1) entries+=std::sprintf(s_entry,"%u",initial_value) + 1;
philpem@5 614 while (*entries) {
philpem@5 615 if ((err2 = std::sscanf(entries,"%4095[^,]%c",s_entry,&end))>0) {
philpem@5 616 entries += cimg::strlen(s_entry) + (err2==2?1:0);
philpem@5 617 cimg::strclean(s_entry);
philpem@5 618 strparenthesis(s_entry);
philpem@5 619 gtk_combo_box_append_text(GTK_COMBO_BOX(combobox),s_entry);
philpem@5 620 } else break;
philpem@5 621 }
philpem@5 622 if (!reset && std::sscanf(s_value,"%u",&initial_value)) {}
philpem@5 623 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox),initial_value);
philpem@5 624 gtk_table_attach(GTK_TABLE(table),combobox,1,3,current_line,current_line+1,
philpem@5 625 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)(GTK_FILL),0,0);
philpem@5 626 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 627 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 628 on_list_parameter_changed(GTK_COMBO_BOX(combobox),(void*)(event_infos+2*current_parameter));
philpem@5 629 g_signal_connect(combobox,"changed",G_CALLBACK(on_list_parameter_changed),
philpem@5 630 (void*)(event_infos+2*current_parameter));
philpem@5 631 g_signal_connect_swapped(combobox,"changed",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 632 found_valid_item = true;
philpem@5 633 ++current_parameter;
philpem@5 634 }
philpem@5 635
philpem@5 636 // Check for a text-valued parameter -> Create GtkEntry.
philpem@5 637 if (!found_valid_item && !cimg::strcasecmp(argtype,"text")) {
philpem@5 638 GtkWidget *label = gtk_label_new(argname);
philpem@5 639 gtk_widget_show(label);
philpem@5 640 gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 641 gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
philpem@5 642 GtkWidget *entry = gtk_entry_new_with_max_length(4095);
philpem@5 643 gtk_widget_show(entry);
philpem@5 644 cimg::strclean(argarg);
philpem@5 645 if (!reset && *s_value) gtk_entry_set_text(GTK_ENTRY(entry),s_value);
philpem@5 646 else gtk_entry_set_text(GTK_ENTRY(entry),argarg);
philpem@5 647 gtk_table_attach(GTK_TABLE(table),entry,1,2,current_line,current_line+1,
philpem@5 648 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0);
philpem@5 649 GtkWidget *button = gtk_button_new_with_label("Update");
philpem@5 650 gtk_widget_show(button);
philpem@5 651 gtk_table_attach(GTK_TABLE(table),button,2,3,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 652 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 653 event_infos[2*current_parameter+1] = (void*)entry;
philpem@5 654 on_text_parameter_changed(GTK_BUTTON(button),(void*)(event_infos+2*current_parameter));
philpem@5 655 g_signal_connect(button,"clicked",G_CALLBACK(on_text_parameter_changed),
philpem@5 656 (void*)(event_infos+2*current_parameter));
philpem@5 657 g_signal_connect_swapped(button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 658 found_valid_item = true;
philpem@5 659 ++current_parameter;
philpem@5 660 }
philpem@5 661
philpem@5 662 // Check for a filename parameter -> Create GtkFileChooserButton.
philpem@5 663 if (!found_valid_item && !cimg::strcasecmp(argtype,"file")) {
philpem@5 664 GtkWidget *label = gtk_label_new(argname);
philpem@5 665 gtk_widget_show(label);
philpem@5 666 gtk_table_attach(GTK_TABLE(table),label,0,1,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 667 gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
philpem@5 668 GtkWidget *filechooser = gtk_file_chooser_button_new(argname,GTK_FILE_CHOOSER_ACTION_OPEN);
philpem@5 669 gtk_widget_show(filechooser);
philpem@5 670 cimg::strclean(argarg);
philpem@5 671 if (!reset && *s_value) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filechooser),s_value);
philpem@5 672 else gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filechooser),argarg);
philpem@5 673 gtk_table_attach(GTK_TABLE(table),filechooser,1,3,current_line,current_line+1,
philpem@5 674 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0);
philpem@5 675 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 676 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 677 on_file_parameter_changed(GTK_FILE_CHOOSER_BUTTON(filechooser),(void*)(event_infos+2*current_parameter));
philpem@5 678 g_signal_connect(filechooser,"file-set",G_CALLBACK(on_file_parameter_changed),
philpem@5 679 (void*)(event_infos+2*current_parameter));
philpem@5 680 g_signal_connect_swapped(filechooser,"file-set",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 681 found_valid_item = true;
philpem@5 682 ++current_parameter;
philpem@5 683 }
philpem@5 684
philpem@5 685 // Check for a color -> Create GtkColorButton.
philpem@5 686 if (!found_valid_item && !cimg::strcasecmp(argtype,"color")) {
philpem@5 687 GtkWidget *hbox = gtk_hbox_new(false,6);
philpem@5 688 gtk_widget_show(hbox);
philpem@5 689 gtk_table_attach(GTK_TABLE(table),hbox,0,2,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 690 GtkWidget *label = gtk_label_new(argname);
philpem@5 691 gtk_widget_show(label);
philpem@5 692 gtk_box_pack_start(GTK_BOX(hbox),label,false,false,0);
philpem@5 693 GtkWidget *colorchooser = gtk_color_button_new();
philpem@5 694 gtk_widget_show(colorchooser);
philpem@5 695 gtk_color_button_set_title(GTK_COLOR_BUTTON(colorchooser),argname);
philpem@5 696 gtk_box_pack_start(GTK_BOX(hbox),colorchooser,false,false,0);
philpem@5 697 event_infos[2*current_parameter] = (void*)current_parameter;
philpem@5 698 event_infos[2*current_parameter+1] = (void*)0;
philpem@5 699 cimg::strclean(argarg);
philpem@5 700 unsigned int red = 0, green = 0, blue = 0, alpha = 255;
philpem@5 701 const int err = std::sscanf(argarg,"%u%*c%u%*c%u%*c%u",&red,&green,&blue,&alpha);
philpem@5 702 if (!reset && std::sscanf(s_value,"%u%*c%u%*c%u%*c%u",&red,&green,&blue,&alpha)==err) {}
philpem@5 703 GdkColor col;
philpem@5 704 col.pixel = 0; col.red = red<<8; col.green = green<<8; col.blue = blue<<8;
philpem@5 705 gtk_color_button_set_color(GTK_COLOR_BUTTON(colorchooser),&col);
philpem@5 706 if (err==4) {
philpem@5 707 gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorchooser),true);
philpem@5 708 gtk_color_button_set_alpha(GTK_COLOR_BUTTON(colorchooser),alpha<<8);
philpem@5 709 } else gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(colorchooser),false);
philpem@5 710 on_color_parameter_changed(GTK_COLOR_BUTTON(colorchooser),(void*)(event_infos+2*current_parameter));
philpem@5 711 g_signal_connect(colorchooser,"color-set",G_CALLBACK(on_color_parameter_changed),
philpem@5 712 (void*)(event_infos+2*current_parameter));
philpem@5 713 g_signal_connect_swapped(colorchooser,"color-set",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 714 found_valid_item = true;
philpem@5 715 ++current_parameter;
philpem@5 716 }
philpem@5 717
philpem@5 718 // Check for a note -> Create GtkLabel.
philpem@5 719 if (!found_valid_item && !cimg::strcasecmp(argtype,"note")) {
philpem@5 720 cimg::strclean(argarg);
philpem@5 721 GtkWidget *label = gtk_label_new(NULL);
philpem@5 722 cimg::strescape(argarg);
philpem@5 723 strparenthesis(argarg);
philpem@5 724 gtk_label_set_markup(GTK_LABEL(label),argarg);
philpem@5 725 gtk_label_set_line_wrap(GTK_LABEL(label),true);
philpem@5 726 gtk_widget_show(label);
philpem@5 727 gtk_table_attach(GTK_TABLE(table),label,0,3,current_line,current_line+1,GTK_FILL,GTK_SHRINK,0,0);
philpem@5 728 gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
philpem@5 729 found_valid_item = true;
philpem@5 730 }
philpem@5 731
philpem@5 732 if (!found_valid_item) {
philpem@5 733 if (get_verbosity_level()>0)
philpem@5 734 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Found invalid parameter type '%s' for argument '%s'.\n",argtype,argname);
philpem@5 735 } else ++current_line;
philpem@5 736 } else break;
philpem@5 737 }
philpem@5 738 set_filter_nbparams(filter,current_parameter);
philpem@5 739 }
philpem@5 740 }
philpem@5 741 gtk_container_add(GTK_CONTAINER(frame),table);
philpem@5 742 }
philpem@5 743
philpem@5 744 // Called when the selected filter changed (in the combo-box).
philpem@5 745 void on_filter_changed(GtkTreeSelection *selection, gpointer user_data) {
philpem@5 746 user_data = 0;
philpem@5 747 GtkTreeIter iter;
philpem@5 748 GtkTreeModel *model;
philpem@5 749 unsigned int choice = 0;
philpem@5 750 if (gtk_tree_selection_get_selected(selection,&model,&iter))
philpem@5 751 gtk_tree_model_get(model,&iter,0,&choice,-1);
philpem@5 752 set_current_filter(choice);
philpem@5 753 create_parameters_gui(false);
philpem@5 754 return_create_dialog = true;
philpem@5 755 }
philpem@5 756
philpem@5 757 // Handle responses to the dialog window buttons.
philpem@5 758 void on_verbosity_level_changed(GtkComboBox *combobox, gpointer user_data) {
philpem@5 759 user_data = 0;
philpem@5 760 int value = 0;
philpem@5 761 g_object_get(combobox,"active",&value,NULL);
philpem@5 762 set_verbosity_level(value);
philpem@5 763 }
philpem@5 764
philpem@5 765 void on_dialog_reset_clicked(GtkButton *widget, gpointer data) {
philpem@5 766 widget = 0; data = 0;
philpem@5 767 create_parameters_gui(true);
philpem@5 768 return_create_dialog = true;
philpem@5 769 }
philpem@5 770
philpem@5 771 void on_dialog_cancel_clicked(GtkButton *widget, gpointer data) {
philpem@5 772 widget = 0; data = 0;
philpem@5 773 return_create_dialog = false;
philpem@5 774 gtk_main_quit();
philpem@5 775 }
philpem@5 776
philpem@5 777 void on_dialog_apply_clicked(GtkButton *widget, gpointer data) {
philpem@5 778 widget = 0;
philpem@5 779 GimpDrawable *drawable = (GimpDrawable*)data;
philpem@5 780 process_image(drawable,0);
philpem@5 781 return_create_dialog = false;
philpem@5 782 }
philpem@5 783
philpem@5 784 void on_dialog_ok_clicked(GtkButton *widget, gpointer data) {
philpem@5 785 widget = 0; data = 0;
philpem@5 786 gtk_main_quit();
philpem@5 787 }
philpem@5 788
philpem@5 789 void on_update_button_clicked(GtkButton *widget, gpointer data) {
philpem@5 790 widget = 0;
philpem@5 791 GtkWidget *dialog = (GtkWidget*)data;
philpem@5 792 char update_filename[1024] = { 0 }, update_command[1024] = { 0 }, src_filename[1024] = { 0 }, dest_filename[1024] = { 0 };
philpem@5 793 const char
philpem@5 794 *const update_url = "http://www.greyc.ensicaen.fr/~dtschump",
philpem@5 795 *const path_tmp = cimg::temporary_path();
philpem@5 796 std::sprintf(update_filename,"gmic4gimp_def.%d",gmic_version);
philpem@5 797 std::sprintf(src_filename,"%s/%s",path_tmp,update_filename);
philpem@5 798 std::sprintf(dest_filename,"%s/.%s",path_home,update_filename);
philpem@5 799 if (get_verbosity_level()>0) {
philpem@5 800 std::sprintf(update_command,"wget %s/%s -O %s",update_url,update_filename,src_filename);
philpem@5 801 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Running update procedure, with command : %s\n",update_command);
philpem@5 802 } else std::sprintf(update_command,"wget --quiet %s/%s -O %s",update_url,update_filename,src_filename);
philpem@5 803 int status = cimg::system(update_command);
philpem@5 804 status = 0;
philpem@5 805 std::FILE *file_s = std::fopen(src_filename,"r");
philpem@5 806 bool succeed = false;
philpem@5 807 if (file_s) {
philpem@5 808 unsigned int size_s = 0;
philpem@5 809 std::fseek(file_s,0,SEEK_END);
philpem@5 810 size_s = (unsigned int)std::ftell(file_s);
philpem@5 811 std::rewind(file_s);
philpem@5 812 if (size_s) {
philpem@5 813 std::FILE *file_d = std::fopen(dest_filename,"w");
philpem@5 814 char *buffer = new char[size_s], sep = 0;
philpem@5 815 if (file_d &&
philpem@5 816 std::fread(buffer,sizeof(char),size_s,file_s)==size_s &&
philpem@5 817 std::sscanf(buffer,"#@gim%c",&sep)==1 && sep=='p' &&
philpem@5 818 std::fwrite(buffer,sizeof(char),size_s,file_d)==size_s) { succeed = true; std::fclose(file_d); }
philpem@5 819 delete[] buffer;
philpem@5 820 }
philpem@5 821 std::fclose(file_s);
philpem@5 822 }
philpem@5 823 if (!succeed) {
philpem@5 824 GtkWidget *message = gtk_message_dialog_new_with_markup(GTK_WINDOW(dialog),GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,
philpem@5 825 "<b>Filters update failed !</b>\n\n"
philpem@5 826 "A valid version of the update file :\n\n"
philpem@5 827 "<i>%s/%s</i>\n\n"
philpem@5 828 " ...could not be retrieved from the G'MIC server.\n\n"
philpem@5 829 "Please check your Internet connection or\n"
philpem@5 830 "try a manual update instead.",update_url,update_filename);
philpem@5 831 gtk_widget_show(message);
philpem@5 832 gtk_dialog_run(GTK_DIALOG(message));
philpem@5 833 gtk_widget_destroy(message);
philpem@5 834 } else {
philpem@5 835 GtkWidget *message = gtk_message_dialog_new_with_markup(GTK_WINDOW(dialog),GTK_DIALOG_MODAL,GTK_MESSAGE_INFO,GTK_BUTTONS_OK,
philpem@5 836 "<b>Filters update succeed !</b>\n\n"
philpem@5 837 "The G'MIC Toolbox must be restarted now.");
philpem@5 838 gtk_widget_show(message);
philpem@5 839 gtk_dialog_run(GTK_DIALOG(message));
philpem@5 840 gtk_widget_destroy(message);
philpem@5 841 return_create_dialog = false;
philpem@5 842 set_current_filter(0);
philpem@5 843 gtk_main_quit();
philpem@5 844 }
philpem@5 845 }
philpem@5 846
philpem@5 847 // Create main plug-in dialog window and wait for a response.
philpem@5 848 //-----------------------------------------------------------
philpem@5 849 bool create_dialog_gui(GimpDrawable *drawable) {
philpem@5 850
philpem@5 851 // Init GUI_specific variables
philpem@5 852 gimp_ui_init("gmic",true);
philpem@5 853 event_infos = 0;
philpem@5 854
philpem@5 855 // Create main plug-in dialog window.
philpem@5 856 GtkWidget
philpem@5 857 *dialog = gimp_dialog_new("The G'MIC Toolbox","gmic",0,(GtkDialogFlags)0,gimp_standard_help_func,"gmic",NULL),
philpem@5 858 *cancel_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL),
philpem@5 859 *reset_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GIMP_STOCK_RESET,1),
philpem@5 860 *apply_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_APPLY,GTK_RESPONSE_APPLY),
philpem@5 861 *ok_button = gimp_dialog_add_button(GIMP_DIALOG(dialog),GTK_STOCK_OK,GTK_RESPONSE_OK);
philpem@5 862 gimp_window_set_transient(GTK_WINDOW(dialog));
philpem@5 863 g_signal_connect(dialog,"close",G_CALLBACK(on_dialog_cancel_clicked),0);
philpem@5 864 g_signal_connect(dialog,"delete-event",G_CALLBACK(on_dialog_cancel_clicked),0);
philpem@5 865 g_signal_connect(cancel_button,"clicked",G_CALLBACK(on_dialog_cancel_clicked),0);
philpem@5 866 g_signal_connect(apply_button,"clicked",G_CALLBACK(on_dialog_apply_clicked),drawable);
philpem@5 867 g_signal_connect(ok_button,"clicked",G_CALLBACK(on_dialog_ok_clicked),0);
philpem@5 868
philpem@5 869 GtkWidget *dialog_hbox = gtk_hbox_new(false,0);
philpem@5 870 gtk_widget_show(dialog_hbox);
philpem@5 871 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),dialog_hbox);
philpem@5 872
philpem@5 873 // Create the left pane, containing the preview, the show commmand and the update buttons and the author name.
philpem@5 874 GtkWidget *left_pane = gtk_vbox_new(false,4);
philpem@5 875 gtk_widget_show(left_pane);
philpem@5 876 gtk_box_pack_start(GTK_BOX(dialog_hbox),left_pane,true,true,0);
philpem@5 877
philpem@5 878 GtkWidget *preview = gimp_zoom_preview_new(drawable);
philpem@5 879 gtk_widget_show(preview);
philpem@5 880 gtk_box_pack_start(GTK_BOX(left_pane),preview,true,true,0);
philpem@5 881 gimp_set_data("gmic_gui_preview",&preview,sizeof(GtkWidget*));
philpem@5 882 g_signal_connect(preview,"invalidated",G_CALLBACK(process_preview),0);
philpem@5 883 g_signal_connect_swapped(apply_button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 884
philpem@5 885 GtkWidget *verbosity_hbuttonbox = gtk_hbutton_box_new();
philpem@5 886 gtk_widget_show(verbosity_hbuttonbox);
philpem@5 887 gtk_box_pack_start(GTK_BOX(left_pane),verbosity_hbuttonbox,false,false,0);
philpem@5 888
philpem@5 889 GtkWidget *verbosity_combobox = gtk_combo_box_new_text();
philpem@5 890 gtk_widget_show(verbosity_combobox);
philpem@5 891 gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Quiet mode");
philpem@5 892 gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Verbose mode");
philpem@5 893 gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"Debug mode");
philpem@5 894 gtk_combo_box_set_active(GTK_COMBO_BOX(verbosity_combobox),get_verbosity_level());
philpem@5 895 gtk_container_add(GTK_CONTAINER(verbosity_hbuttonbox),verbosity_combobox);
philpem@5 896 g_signal_connect(verbosity_combobox,"changed",G_CALLBACK(on_verbosity_level_changed),0);
philpem@5 897
philpem@5 898 GtkWidget *update_hbuttonbox = gtk_hbutton_box_new();
philpem@5 899 gtk_widget_show(update_hbuttonbox);
philpem@5 900 gtk_box_pack_start(GTK_BOX(left_pane),update_hbuttonbox,false,false,0);
philpem@5 901 GtkWidget
philpem@5 902 *tmp_button = gtk_button_new_from_stock(GTK_STOCK_REFRESH),
philpem@5 903 *update_image = gtk_button_get_image(GTK_BUTTON(tmp_button)),
philpem@5 904 *update_button = gtk_button_new_with_mnemonic("_Update filters");
philpem@5 905 gtk_button_set_image(GTK_BUTTON(update_button),update_image);
philpem@5 906 gtk_widget_show(update_button);
philpem@5 907 gtk_container_add(GTK_CONTAINER(update_hbuttonbox),update_button);
philpem@5 908 g_signal_connect(update_button,"clicked",G_CALLBACK(on_update_button_clicked),(void*)dialog);
philpem@5 909
philpem@5 910 GtkWidget *about_label = gtk_label_new(NULL);
philpem@5 911 gtk_label_set_markup(GTK_LABEL(about_label),
philpem@5 912 "\n<span color=\"#666666\"><small>"
philpem@5 913 "<b>G'MIC</b> is proposed to you\n"
philpem@5 914 " by <i>David Tschumperle</i>"
philpem@5 915 "</small></span>");
philpem@5 916 gtk_widget_show(about_label);
philpem@5 917 gtk_box_pack_start(GTK_BOX(left_pane),about_label,false,false,0);
philpem@5 918
philpem@5 919 const unsigned int logo_width = 102, logo_height = 22;
philpem@5 920 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(data_logo,GDK_COLORSPACE_RGB,false,8,
philpem@5 921 logo_width,logo_height,3*logo_width,0,0);
philpem@5 922 GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
philpem@5 923 gtk_widget_show(image);
philpem@5 924 gtk_box_pack_start(GTK_BOX(left_pane),image,false,false,0);
philpem@5 925
philpem@5 926 // Create the middle pane, which contains the filters treeview.
philpem@5 927 GtkWidget *middle_pane = gtk_frame_new(NULL);
philpem@5 928 gtk_widget_show(middle_pane);
philpem@5 929 gtk_container_set_border_width(GTK_CONTAINER(middle_pane),4);
philpem@5 930 gtk_widget_set_size_request(middle_pane,250,-1);
philpem@5 931 gtk_box_pack_start(GTK_BOX(dialog_hbox),middle_pane,false,false,0);
philpem@5 932
philpem@5 933 GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL,NULL);
philpem@5 934 gtk_widget_show(scrolledwindow);
philpem@5 935 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
philpem@5 936 gtk_container_add(GTK_CONTAINER(middle_pane),scrolledwindow);
philpem@5 937
philpem@5 938 GtkWidget *treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_store));
philpem@5 939 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
philpem@5 940 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(" Available filters :",renderer,"text",1,NULL);
philpem@5 941 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),column);
philpem@5 942
philpem@5 943 GtkTreeSelection *select = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
philpem@5 944 gtk_tree_selection_set_mode(select,GTK_SELECTION_SINGLE);
philpem@5 945 g_signal_connect(G_OBJECT(select),"changed",G_CALLBACK(on_filter_changed),0);
philpem@5 946 g_signal_connect_swapped(select,"changed",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 947 gtk_widget_show(treeview);
philpem@5 948 gtk_container_add(GTK_CONTAINER(scrolledwindow),treeview);
philpem@5 949 g_signal_connect(reset_button,"clicked",G_CALLBACK(on_dialog_reset_clicked),select);
philpem@5 950 g_signal_connect_swapped(reset_button,"clicked",G_CALLBACK(gimp_preview_invalidate),preview);
philpem@5 951
philpem@5 952 // Create the right pane which contains the parameters frame.
philpem@5 953 GtkWidget *parameters_frame = gtk_frame_new(NULL);
philpem@5 954 gtk_widget_show(parameters_frame);
philpem@5 955 gtk_container_set_border_width(GTK_CONTAINER(parameters_frame),4);
philpem@5 956 gtk_widget_set_size_request(parameters_frame,450,-1);
philpem@5 957 gtk_box_pack_start(GTK_BOX(dialog_hbox),parameters_frame,false,false,0);
philpem@5 958 gimp_set_data("gmic_gui_frame",&parameters_frame,sizeof(GtkWidget*));
philpem@5 959 create_parameters_gui(false);
philpem@5 960
philpem@5 961 // Show dialog window and wait for user response.
philpem@5 962 gtk_widget_show(dialog);
philpem@5 963 gtk_main();
philpem@5 964
philpem@5 965 // Destroy dialog box widget and free resources.
philpem@5 966 gtk_widget_destroy(dialog);
philpem@5 967 gtk_widget_destroy(tmp_button);
philpem@5 968 if (event_infos) delete[] event_infos;
philpem@5 969 return return_create_dialog;
philpem@5 970 }
philpem@5 971
philpem@5 972 // 'Run' function needed by GIMP plug-in API.
philpem@5 973 //-------------------------------------------
philpem@5 974 void gmic_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) {
philpem@5 975
philpem@5 976 // Init plug-in variables.
philpem@5 977 static GimpParam values[1];
philpem@5 978 values[0].type = GIMP_PDB_STATUS;
philpem@5 979 *return_vals = values;
philpem@5 980 *nreturn_vals = 1;
philpem@5 981 name = 0;
philpem@5 982 nparams = 0;
philpem@5 983 GimpRunMode run_mode;
philpem@5 984 run_mode = (GimpRunMode)param[0].data.d_int32;
philpem@5 985 if (run_mode==GIMP_RUN_NONINTERACTIVE) {
philpem@5 986 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : ERROR, this plug-in cannot be run in non-interactive mode.\n");
philpem@5 987 values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
philpem@5 988 return;
philpem@5 989 }
philpem@5 990 gmic_macros = 0;
philpem@5 991 filter_store = 0;
philpem@5 992 return_create_dialog = true;
philpem@5 993 path_home = getenv(cimg_OS!=2?"HOME":"APPDATA");
philpem@5 994
philpem@5 995 // Check that no instance of the plug-in is already running.
philpem@5 996 bool is_existing_instance = 0;
philpem@5 997 gimp_get_data("gmic_instance",&is_existing_instance);
philpem@5 998 if (is_existing_instance) {
philpem@5 999 std::fprintf(stderr,"\n*** Plug-in 'gmic4gimp' : Existing instance of the plug-in is already running.\n");
philpem@5 1000 return;
philpem@5 1001 }
philpem@5 1002 is_existing_instance = true;
philpem@5 1003 gimp_set_data("gmic_instance",&is_existing_instance,sizeof(bool));
philpem@5 1004
philpem@5 1005 // Read user-defined configuration files '.gmic_def' and '.gmic', when possible.
philpem@5 1006 unsigned size_update = 0, size_custom = 0, size_def = sizeof(data_gmic4gimp_def);
philpem@5 1007 char filename_update[1024] = { 0 }, filename_custom[1024] = { 0 };
philpem@5 1008 std::sprintf(filename_update,"%s/.gmic4gimp_def.%d",path_home,gmic_version);
philpem@5 1009 std::sprintf(filename_custom,"%s/.gmic4gimp",path_home);
philpem@5 1010 std::FILE
philpem@5 1011 *file_update = std::fopen(filename_update,"r"),
philpem@5 1012 *file_custom = std::fopen(filename_custom,"r");
philpem@5 1013 if (file_update) {
philpem@5 1014 std::fseek(file_update,0,SEEK_END);
philpem@5 1015 size_update = (unsigned int)std::ftell(file_update);
philpem@5 1016 std::rewind(file_update);
philpem@5 1017 }
philpem@5 1018 if (file_custom) {
philpem@5 1019 std::fseek(file_custom,0,SEEK_END);
philpem@5 1020 size_custom = (unsigned int)std::ftell(file_custom);
philpem@5 1021 std::rewind(file_custom);
philpem@5 1022 }
philpem@5 1023 const unsigned int size_final = size_update + size_custom + size_def + 1;
philpem@5 1024 char *ptrd = gmic_macros = new char[size_final];
philpem@5 1025 if (size_custom) { ptrd+=std::fread(ptrd,1,size_custom,file_custom); std::fclose(file_custom); }
philpem@5 1026 if (size_update) { ptrd+=std::fread(ptrd,1,size_update,file_update); std::fclose(file_update); }
philpem@5 1027 if (size_def) { std::memcpy(ptrd,data_gmic4gimp_def,size_def); ptrd+=size_def; }
philpem@5 1028 *ptrd = 0;
philpem@5 1029
philpem@5 1030 // Parse available G'MIC filters definitions.
philpem@5 1031 GtkTreeIter iter, parent[16];
philpem@5 1032 filter_store = gtk_tree_store_new(2,G_TYPE_UINT,G_TYPE_STRING);
philpem@5 1033 char line[256*1024] = { 0 }, entry[4096] = { 0 }, command[4096] = { 0 };
philpem@5 1034 char preview_command[4096] = { 0 }, arguments[4096] = { 0 };
philpem@5 1035 int level = 0;
philpem@5 1036 for (const char *data = gmic_macros; *data; ) {
philpem@5 1037 if (*data=='\n') ++data;
philpem@5 1038 else {
philpem@5 1039 if (std::sscanf(data,"%262143[^\n]\n",line)>0) data += cimg::strlen(line) + 1;
philpem@5 1040 arguments[0] = 0;
philpem@5 1041 if (line[0]=='#') {
philpem@5 1042 const int err = std::sscanf(line,"#@gimp %4095[^:]: %4095[^, ]%*c %4095[^, ]%*c %4095[^\n]",
philpem@5 1043 entry,command,preview_command,arguments);
philpem@5 1044 strparenthesis(entry);
philpem@5 1045 if (err==1) { // If entry is a menu folder.
philpem@5 1046 cimg::strclean(entry);
philpem@5 1047 char *nentry = entry;
philpem@5 1048 while (*nentry=='_') { ++nentry; --level; }
philpem@5 1049 if (level<0) level = 0;
philpem@5 1050 if (level>15) level = 15;
philpem@5 1051 cimg::strclean(nentry);
philpem@5 1052 if (*nentry) {
philpem@5 1053 gtk_tree_store_append(filter_store,&parent[level],level?&parent[level-1]:0);
philpem@5 1054 gtk_tree_store_set(filter_store,&parent[level],0,0,1,nentry,-1);
philpem@5 1055 ++level;
philpem@5 1056 }
philpem@5 1057 } else if (err>=2) { // If entry is a regular filter.
philpem@5 1058 cimg::strclean(entry);
philpem@5 1059 cimg::strclean(command);
philpem@5 1060 gmic_entries.insert(CImg<char>(entry,cimg::strlen(entry)+1));
philpem@5 1061 gmic_commands.insert(CImg<char>(command,cimg::strlen(command)+1));
philpem@5 1062 gmic_arguments.insert(CImg<char>(arguments,cimg::strlen(arguments)+1));
philpem@5 1063 if (err>=3) {
philpem@5 1064 cimg::strclean(preview_command);
philpem@5 1065 gmic_preview_commands.insert(CImg<char>(preview_command,cimg::strlen(preview_command)+1));
philpem@5 1066 }
philpem@5 1067 gtk_tree_store_append(filter_store,&iter,level?&parent[level-1]:0);
philpem@5 1068 gtk_tree_store_set(filter_store,&iter,0,gmic_entries.size,1,entry,-1);
philpem@5 1069 }
philpem@5 1070 }
philpem@5 1071 }
philpem@5 1072 }
philpem@5 1073
philpem@5 1074 // Get currenty selected drawable and run image filter on it.
philpem@5 1075 GimpDrawable *drawable = gimp_drawable_get(param[2].data.d_drawable);
philpem@5 1076 gimp_tile_cache_ntiles(2*(drawable->width/gimp_tile_width()+1));
philpem@5 1077 if (run_mode==GIMP_RUN_INTERACTIVE) {
philpem@5 1078 if (create_dialog_gui(drawable)) {
philpem@5 1079 process_image(drawable,0);
philpem@5 1080 const char *commandline = get_commandline(false);
philpem@5 1081 if (commandline) { // Remember command line for the next use of the filter.
philpem@5 1082 char s_tmp[256] = { 0 };
philpem@5 1083 std::sprintf(s_tmp,"gmic_commandline%u",get_current_filter());
philpem@5 1084 gimp_set_data(s_tmp,commandline,cimg::strlen(commandline));
philpem@5 1085 }
philpem@5 1086 }
philpem@5 1087 } else if (run_mode==GIMP_RUN_WITH_LAST_VALS) {
philpem@5 1088 const unsigned int filter = get_current_filter();
philpem@5 1089 if (filter) {
philpem@5 1090 char s_tmp[256] = { 0 };
philpem@5 1091 std::sprintf(s_tmp,"gmic_commandline%u",filter);
philpem@5 1092 char commandline[4096] = { 0 };
philpem@5 1093 gimp_get_data(s_tmp,&commandline);
philpem@5 1094 process_image(drawable,commandline);
philpem@5 1095 }
philpem@5 1096 }
philpem@5 1097
philpem@5 1098 // Free plug-in resources.
philpem@5 1099 delete[] gmic_macros;
philpem@5 1100 values[0].data.d_status = GIMP_PDB_SUCCESS;
philpem@5 1101 is_existing_instance = false;
philpem@5 1102 gimp_set_data("gmic_instance",&is_existing_instance,sizeof(bool));
philpem@5 1103 }
philpem@5 1104
philpem@5 1105 // 'Query' function needed by GIMP plug-in API.
philpem@5 1106 //---------------------------------------------
philpem@5 1107 void gmic_query() {
philpem@5 1108 static const GimpParamDef args[] = {
philpem@5 1109 {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
philpem@5 1110 {GIMP_PDB_IMAGE, "image", "(unused)"},
philpem@5 1111 {GIMP_PDB_DRAWABLE, "drawable", "Drawable to draw on"}
philpem@5 1112 };
philpem@5 1113
philpem@5 1114 gimp_install_procedure("gmic", // name
philpem@5 1115 "G'MIC Toolbox", // blurb
philpem@5 1116 "G'MIC Toolbox", // help
philpem@5 1117 "David Tschumperle", // author
philpem@5 1118 "David Tschumperle", // copyright
philpem@5 1119 "2008-12-02", // date
philpem@5 1120 "_G'MIC Toolbox...", // menu_path
philpem@5 1121 "RGB*, GRAY*", // image_types
philpem@5 1122 GIMP_PLUGIN, // type
philpem@5 1123 G_N_ELEMENTS(args), // nparams
philpem@5 1124 0, // nreturn_vals
philpem@5 1125 args, // params
philpem@5 1126 0); // return_vals
philpem@5 1127
philpem@5 1128 gimp_plugin_menu_register("gmic", "<Image>/Filters");
philpem@5 1129 }
philpem@5 1130
philpem@5 1131 GimpPlugInInfo PLUG_IN_INFO = { 0, 0, gmic_query, gmic_run };
philpem@5 1132 MAIN();