Skip to content

Instantly share code, notes, and snippets.

@aferrero2707
Created September 6, 2016 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aferrero2707/9123c7830130a7048aef9ba3ef0186d1 to your computer and use it in GitHub Desktop.
Save aferrero2707/9123c7830130a7048aef9ba3ef0186d1 to your computer and use it in GitHub Desktop.
Patched G'MIC plug-in that adds color management of the preview area
/*
#
# File : gmic_gimp.cpp
# ( C++ source file )
#
# Description : G'MIC for GIMP - A plug-in to allow the use
# of G'MIC commands in GIMP.
#
# Copyright : David Tschumperle
# ( http://tschumperle.users.greyc.fr/ )
#
# License : CeCILL v2.0
# ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
#
# This software is governed by the CeCILL license under French law and
# abiding by the rules of distribution of free software. You can use,
# modify and/ or redistribute the software under the terms of the CeCILL
# license as circulated by CEA, CNRS and INRIA at the following URL
# "http://www.cecill.info".
#
# As a counterpart to the access to the source code and rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty and the software's author, the holder of the
# economic rights, and the successive licensors have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading, using, modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also
# therefore means that it is reserved for developers and experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and, more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license and that you accept its terms.
#
*/
// Include necessary header files.
//--------------------------------
#define cimg_display_type 0
#include "gmic.h"
#include "gmic_stdlib.h"
#include <pthread.h>
#include <locale>
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#undef min
#undef max
// Define default pixel type.
#ifndef gmic_pixel_type
#define gmic_pixel_type float
#endif
#define __s_gmic_pixel_type(s) #s
#define _s_gmic_pixel_type(s) __s_gmic_pixel_type(s)
#define s_gmic_pixel_type _s_gmic_pixel_type(gmic_pixel_type)
// Manage different versions of the GIMP API.
#define _gimp_item_is_valid gimp_item_is_valid
#define _gimp_image_get_item_position gimp_image_get_item_position
#if GIMP_MINOR_VERSION<=8
#define _gimp_item_get_visible gimp_drawable_get_visible
#else
#define _gimp_item_get_visible gimp_item_get_visible
#endif
using namespace cimg_library;
// Define plug-in global variables.
//---------------------------------
CImgList<char> gmic_entries; // The list of recognized G'MIC menu entries.
CImgList<char> gmic_1stlevel_entries; // The treepath positions of 1st-level G'MIC menu entries.
CImgList<char> gmic_commands; // The list of corresponding G'MIC commands to process the image.
CImgList<char> gmic_preview_commands; // The list of corresponding G'MIC commands to preview the image.
CImgList<char> gmic_arguments; // The list of corresponding needed filter arguments.
CImgList<char> gmic_faves; // The list of favorites filters and their default parameters.
CImgList<double> gmic_preview_factors; // The list of default preview factors for each filter.
CImgList<unsigned int> gmic_button_parameters; // The list of button parameters for the current filter.
CImg<gmic_pixel_type> computed_preview; // The last computed preview image.
CImg<unsigned char> latest_preview_buffer; // The latest content of the preview buffer.
CImg<char> gmic_additional_commands; // The buffer of additional G'MIC command implementations.
bool _create_dialog_gui; // Return value for 'create_gui_dialog()' (set by events handlers).
bool is_block_preview = false; // Flag to block preview, when double-clicking on the filter tree.
void **event_infos; // Infos that are passed to the GUI callback functions.
int image_id = 0; // The image concerned by the plug-in execution.
int preview_image_id = 0; // The alternate preview image used if image is too small.
double preview_image_factor = 0; // If alternative preview image used, tell about the size factor (>1).
unsigned int indice_faves = 0; // The starting index of favorite filters.
unsigned int nb_available_filters = 0; // The number of available filters (non-testing).
std::FILE *logfile = 0; // The log file if any.
void *p_spt = 0; // A pointer to the current running G'MIC thread if any (or 0).
GimpRunMode run_mode; // Run-mode used to call the plug-in.
GtkTreeStore *tree_view_store = 0; // The list of the filters as a GtkTreeView model.
GtkWidget *dialog_window = 0; // The plug-in dialog window.
GtkWidget *gui_preview = 0; // The preview window.
GtkWidget *gui_preview_warning = 0; // Warning label displaying for unaccurate preview.
GtkWidget *gui_preview_align = 0; // Alignment widget that contains the preview.
GtkWidget *relabel_hbox = 0; // The entire widget to relabel filter.
GtkWidget *relabel_entry = 0; // The text entry to relabel filter.
GtkWidget *tree_view = 0; // The filter treeview.
GtkWidget *tree_mode_stock = 0; // A temporary stock button for the expand/collapse button.
GtkWidget *tree_mode_button = 0; // Expand/Collapse button for the treeview.
GtkWidget *refresh_stock = 0; // A temporary stock button for the refresh button.
GtkWidget *reset_zoom_stock = 0; // A temporary stock button for the reset zoom button.
GtkWidget *fave_stock = 0; // A temporary stock button for the fave button.
GtkWidget *delete_stock = 0; // A temporary stock button for the fave button 2.
GtkWidget *fave_add_button = 0; // Fave button.
GtkWidget *fave_delete_button = 0; // Fave delete button.
GtkWidget *right_frame = 0; // The right frame containing the filter parameters.
GtkWidget *right_pane = 0; // The right scrolled window, containing the right frame.
GtkWidget *markup2ascii = 0; // Used to convert markup to ascii strings.
GimpPDBStatusType status = GIMP_PDB_SUCCESS; // The plug-in return status.
const char *s_blendmode[] = { "alpha","dissolve","behind","multiply","screen","overlay","difference",
"add","subtract","darken","lighten","hue","saturation","color","value",
"divide","dodge","burn","hardlight","softlight","grainextract",
"grainmerge","colorerase" };
GimpColorProfile* img_profile = NULL;
GimpColorProfile* dpy_profile = NULL;
// Set/get plug-in persistent variables, using GIMP {get,set}_data() features.
//-----------------------------------------------------------------------------
// Set/get the number of available filters.
void set_nbfilters(const unsigned int nb_filters) {
gimp_set_data("gmic_nbfilters",&nb_filters,sizeof(unsigned int));
}
unsigned int get_nbfilters() {
unsigned int nb_filters = 0;
gimp_get_data("gmic_nbfilters",&nb_filters);
return nb_filters;
}
// Set/get the indice of the currently selected filter.
void set_current_filter(const unsigned int current_filter) {
const unsigned int ncurrent_filter = current_filter>=gmic_entries.size()?0:current_filter;
gimp_set_data("gmic_current_filter",&ncurrent_filter,sizeof(unsigned int));
}
unsigned int get_current_filter() {
unsigned int current_filter = 0;
gimp_get_data("gmic_current_filter",&current_filter);
if (current_filter>=gmic_entries.size()) current_filter = 0;
return current_filter;
}
// Set/get the number of parameters of the specified filter.
void set_filter_nbparams(const unsigned int filter, const unsigned int nbparams) {
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_filter%u_nbparams",filter);
gimp_set_data(s_tmp,&nbparams,sizeof(unsigned int));
}
unsigned int get_filter_nbparams(const unsigned int filter) {
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_filter%u_nbparams",filter);
unsigned int nbparams = 0;
gimp_get_data(s_tmp,&nbparams);
return nbparams;
}
// Set/get one particular parameter of a filter.
void set_filter_parameter(const unsigned int filter, const unsigned int n, const char *const param) {
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_filter%u_parameter%u",filter,n);
gimp_set_data(s_tmp,param,std::strlen(param) + 1);
}
CImg<char> get_filter_parameter(const unsigned int filter, const unsigned int n) {
CImg<char> s_param, s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_filter%u_parameter%u",filter,n);
const unsigned int siz = 1U + gimp_get_data_size(s_tmp);
s_param.assign(siz);
*s_param = 0;
gimp_get_data(s_tmp,s_param);
return s_param;
}
// Set/get one particular default parameter of a fave filter.
void set_fave_parameter(const unsigned int filter, const unsigned int n, const char *const param) {
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_fave%u_parameter%u",filter,n);
gimp_set_data(s_tmp,param,std::strlen(param) + 1);
}
CImg<char> get_fave_parameter(const unsigned int filter, const unsigned int n) {
CImg<char> s_param, s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_fave%u_parameter%u",filter,n);
const unsigned int siz = 1U + gimp_get_data_size(s_tmp);
s_param.assign(siz);
*s_param = 0;
gimp_get_data(s_tmp,s_param);
return s_param;
}
// Reset all parameters of all filters.
void reset_filters_parameters() {
const char *const empty = "";
for (unsigned int i = 1; i<gmic_entries.size(); ++i)
for (unsigned int j = 0; ; ++j) {
const CImg<char> val = get_filter_parameter(i,j);
if (*val) set_filter_parameter(i,j,empty); else break;
}
}
// Set/get the filter input, output, preview, verbosity modes and log file.
void set_input_mode(const unsigned int input_mode) {
gimp_set_data("gmic_input_mode",&input_mode,sizeof(unsigned int));
}
unsigned int get_input_mode(const bool normalized=true) {
unsigned int input_mode = 0;
gimp_get_data("gmic_input_mode",&input_mode);
return normalized?(input_mode<2?1:(input_mode - 2)):input_mode;
}
void set_output_mode(const unsigned int output_mode) {
gimp_set_data("gmic_output_mode",&output_mode,sizeof(unsigned int));
}
unsigned int get_output_mode(const bool normalized=true) {
unsigned int output_mode = 0;
gimp_get_data("gmic_output_mode",&output_mode);
return normalized?(output_mode<2?0:(output_mode - 2)):output_mode;
}
void set_preview_mode(const unsigned int preview_mode) {
gimp_set_data("gmic_preview_mode",&preview_mode,sizeof(unsigned int));
}
unsigned int get_preview_mode(const bool normalized=true) {
unsigned int preview_mode = 0;
gimp_get_data("gmic_preview_mode",&preview_mode);
return normalized?(preview_mode<2?0:(preview_mode - 2)):preview_mode;
}
void set_preview_size(const unsigned int preview_size) {
gimp_set_data("gmic_preview_size",&preview_size,sizeof(unsigned int));
}
unsigned int get_preview_size(const bool normalized=true) {
unsigned int preview_size = 0;
gimp_get_data("gmic_preview_size",&preview_size);
return normalized?(preview_size<2?0:(preview_size - 2)):preview_size;
}
void set_verbosity_mode(const unsigned int verbosity) {
gimp_set_data("gmic_verbosity_mode",&verbosity,sizeof(unsigned int));
}
unsigned int get_verbosity_mode(const bool normalized=true) {
unsigned int verbosity_mode = 0;
gimp_get_data("gmic_verbosity_mode",&verbosity_mode);
return normalized?(verbosity_mode<2?0:(verbosity_mode - 2)):verbosity_mode;
}
void set_logfile() {
const unsigned int verbosity = get_verbosity_mode();
if (verbosity==3 || verbosity==5 || verbosity==7) {
if (!logfile) {
CImg<char> filename(1024);
cimg_snprintf(filename,filename.width(),"%sgimp_log",
gmic::path_rc());
logfile = std::fopen(filename,"a");
}
cimg::output(logfile?logfile:stdout);
} else {
if (logfile) std::fclose(logfile);
logfile = 0;
cimg::output(stdout);
}
}
// Set/get the tree collapse/expand mode.
void set_tree_mode(const bool expand) {
gimp_set_data("gmic_tree_mode",&expand,sizeof(bool));
}
bool get_tree_mode() {
bool tree_mode = false;
gimp_get_data("gmic_tree_mode",&tree_mode);
return tree_mode;
}
// Set/get the net update activation state.
void set_net_update(const bool net_update) {
gimp_set_data("gmic_net_update",&net_update,sizeof(bool));
}
bool get_net_update() {
bool net_update = true;
gimp_get_data("gmic_net_update",&net_update);
return net_update;
}
// Set/get the current locale.
void set_locale() {
CImg<char> locale(16); *locale = 0;
const char *s_locale = gimp_gimprc_query("language");
if (!s_locale || std::strlen(s_locale)<2) s_locale = setlocale(LC_CTYPE,0);
if (!s_locale || std::strlen(s_locale)<2 || !cimg::strncasecmp("lc",s_locale,2)) s_locale = getenv("LANG");
if (!s_locale || std::strlen(s_locale)<2) s_locale = getenv("LANGUAGE");
if (!s_locale || std::strlen(s_locale)<2) s_locale = getenv("LC_ALL");
if (!s_locale || std::strlen(s_locale)<2) s_locale = getenv("LC_CTYPE");
if (!s_locale || std::strlen(s_locale)<2) s_locale = getenv("LC_TIME");
if (!s_locale || std::strlen(s_locale)<2) s_locale = getenv("LC_NAME");
if (!s_locale || std::strlen(s_locale)<2) s_locale = "en";
cimg_sscanf(s_locale,"%c%c",&(locale[0]),&(locale[1]));
locale[2] = 0;
cimg::lowercase(locale);
gimp_set_data("gmic_locale",locale,std::strlen(locale) + 1);
}
CImg<char> get_locale() {
CImg<char> locale(16);
*locale = 0;
gimp_get_data("gmic_locale",locale);
return locale;
}
// Get layer blending mode from string.
//-------------------------------------
void get_output_layer_props(const char *const s, GimpLayerModeEffects &blendmode, double &opacity,
int &posx, int &posy, CImg<char>& name) {
#define _get_output_layer_blendmode(in,out) \
if (S && !is_blendmode) { \
const unsigned int l = (unsigned int)std::strlen(in); \
if (!std::strncmp(S + 5,in,l) && S[5 + l]==')') { blendmode = out; is_blendmode = true; } \
}
if (!s || !*s) return;
// Read output blending mode.
const char *S = std::strstr(s,"mode(");
bool is_blendmode = false;
_get_output_layer_blendmode("alpha",GIMP_NORMAL_MODE);
_get_output_layer_blendmode("normal",GIMP_NORMAL_MODE);
_get_output_layer_blendmode("dissolve",GIMP_DISSOLVE_MODE);
_get_output_layer_blendmode("lighten",GIMP_LIGHTEN_ONLY_MODE);
_get_output_layer_blendmode("screen",GIMP_SCREEN_MODE);
_get_output_layer_blendmode("dodge",GIMP_DODGE_MODE);
_get_output_layer_blendmode("add",GIMP_ADDITION_MODE);
_get_output_layer_blendmode("darken",GIMP_DARKEN_ONLY_MODE);
_get_output_layer_blendmode("multiply",GIMP_MULTIPLY_MODE);
_get_output_layer_blendmode("burn",GIMP_BURN_MODE);
_get_output_layer_blendmode("overlay",GIMP_OVERLAY_MODE);
_get_output_layer_blendmode("softlight",GIMP_SOFTLIGHT_MODE);
_get_output_layer_blendmode("hardlight",GIMP_HARDLIGHT_MODE);
_get_output_layer_blendmode("difference",GIMP_DIFFERENCE_MODE);
_get_output_layer_blendmode("subtract",GIMP_SUBTRACT_MODE);
_get_output_layer_blendmode("grainextract",GIMP_GRAIN_EXTRACT_MODE);
_get_output_layer_blendmode("grainmerge",GIMP_GRAIN_MERGE_MODE);
_get_output_layer_blendmode("divide",GIMP_DIVIDE_MODE);
_get_output_layer_blendmode("hue",GIMP_HUE_MODE);
_get_output_layer_blendmode("saturation",GIMP_SATURATION_MODE);
_get_output_layer_blendmode("color",GIMP_COLOR_MODE);
_get_output_layer_blendmode("value",GIMP_VALUE_MODE);
// Read output opacity.
double _opacity = 0;
char sep = 0;
S = std::strstr(s,"opacity(");
if (S && cimg_sscanf(S + 8,"%lf%c",&_opacity,&sep)==2 && sep==')') opacity = _opacity;
if (opacity<0) opacity = 0; else if (opacity>100) opacity = 100;
// Read output positions.
int _posx = 0, _posy = 0;
sep = 0;
S = std::strstr(s,"pos(");
if (S && cimg_sscanf(S + 4,"%d%*c%d%c",&_posx,&_posy,&sep)==3 && sep==')') { posx = _posx; posy = _posy; }
// Read output name.
S = std::strstr(s,"name(");
if (S) {
const char *ps = S + 5;
unsigned int level = 1;
while (*ps && level) {
if (*ps=='(') ++level;
else if (*ps==')') --level;
++ps;
}
if (!level || *(ps - 1)==')') {
name.assign(S + 5,(unsigned int)(ps - S - 5)).back() = 0;
cimg_for(name,pn,char) if (*pn==21) *pn = '('; else if (*pn==22) *pn = ')';
}
}
}
// Translate string into the current locale.
//------------------------------------------
#define _t(source,dest) if (!std::strcmp(source,s)) { const char *const ns = dest; return ns; }
const char *t(const char *const s) {
const CImg<char> locale = get_locale();
// Catalan translation.
if (!std::strcmp(locale,"ca")) {
if (!s) {
const char *const ns = "No ha estat possible establir una connexi&#243; a Internet !\n\n"
"No es possible arribar a aquestes fonts de filtres :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Av\303\255s:</b> Previsualitzaci\303\263 pot ser inexacta\n"
"(factor de zoom s'ha modificat)</span>");
_t("G'MIC for GIMP","G'MIC per al GIMP");
_t("<i>Select a filter...</i>","<i>Selecciona un filtre...</i>");
_t("<i>No parameters to set...</i>","<i>Sense par\303\240metres...</i>");
_t("<b> Input / Output </b>","<b> Entrades / Sortides </b>");
_t("Input layers...","Capes d'entrada...");
_t("None","Cap");
_t("Active (default)","Actiu (predet.)");
_t("All","Tots");
_t("Active & below","L'activa i les de sota");
_t("Active & above","L'activa i les de sobre");
_t("All visibles","Totes les visibles");
_t("All invisibles","Totes les invisibles");
_t("All visibles (decr.)","Totes les visibles (decr.)");
_t("All invisibles (decr.)","Totes les invisibles (decr.)");
_t("All (decr.)","Totes (decr.)");
_t("Output mode...","Mode de sortida...");
_t("In place (default)","A la capa actual (predet.)");
_t("New layer(s)","Nova/es capa/es");
_t("New active layer(s)","Nova/es capa/es actius");
_t("New image","Nova imatge");
_t("Preview mode...","Previsualitzaci\303\263 de sortida...");
_t("1st output (default)","1era imatge (predet.)");
_t("2nd output","2ona imatge");
_t("3rd output","3era imatge");
_t("4th output","4rta imatge");
_t("1st -> 2nd","1era -> 2ona");
_t("1st -> 3rd","1era -> 3era");
_t("1st -> 4th","1era -> 4rta");
_t("All outputs","Totes les imatges");
_t("Output messages...","Missatges de sortida...");
_t("Quiet (default)","Sense missatges (predet.)");
_t("Verbose (layer name)","Verb\303\263s (nom de la capa)");
_t("Verbose (console)","Verb\303\263s (consola)");
_t("Verbose (logfile)","Verb\303\263s (arxiu)");
_t("Very verbose (console)","Molt verb\303\263s (consola)");
_t("Very verbose (logfile)","Molt verb\303\263s (arxiu)");
_t("Debug mode (console)","Depuraci\303\263 (consola)");
_t("Debug mode (logfile)","Depuraci\303\263 (arxiu)");
_t("Preview size...","Mida de previsualitzaci\303\263...");
_t("Tiny","Molt petita");
_t("Small","Petita");
_t("Normal","Normal");
_t("Large","Gran");
_t(" Available filters (%u)"," Filtres disponibles (%u)");
_t("Update","Actualitzaci\303\263");
_t("Rename","Canviar");
_t("Add filter to faves","Afegir filtre a favorits");
_t("Remove filter from faves","Remogui el filtre de favorits");
_t("Update filters","filtres Actualitzar");
_t("Enable Internet updates","Habilitar actualitzacions d'Internet");
_t("Expand/collapse","Expand/collapse");
_t("Reset zoom","Restablir zoom");
}
// Dutch translation.
else if (!std::strcmp(locale,"nl")) {
if (!s) {
const char *const ns = "Geen internet-update mogelijk !\n\n"
"Kan deze filters bronnen te bereiken :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Waarschuwing:</b> Voorbeeld mogelijk niet correct\n"
"(zoomratio veranderd)</span>");
_t("G'MIC for GIMP","G'MIC voor GIMP");
_t("<i>Select a filter...</i>","<i>Kies een filter...</i>");
_t("<i>No parameters to set...</i>","<i>Geen parameters nodig...</i>");
_t("<b> Input / Output </b>","<b> Input / Output </b>");
_t("Input layers...","Input lagen...");
_t("None","Geen");
_t("Active (default)","Actieve laag (standaard)");
_t("All","Alle");
_t("Active & below","Actieve & onderliggende");
_t("Active & above","Actieve & bovenliggende");
_t("All visibles","Alle zichtbare");
_t("All invisibles","Alle niet zichtbare");
_t("All visibles (decr.)","Alle zichtbare (afnemend)");
_t("All invisibles (decr.)","Alle niet zichtbare (afnemend)");
_t("All (decr.)","Alle (afnemend)");
_t("Output mode...","Output mode...");
_t("In place (default)","Vervang bestaande (standaard)");
_t("New layer(s)","Nieuwe laag/lagen");
_t("New active layer(s)","Nieuwe actieve laag/lagen");
_t("New image","Nieuwe afbeelding");
_t("Preview mode...","Output voorbeeld...");
_t("1st output (default)","1e Resultaat (standaard)");
_t("2nd output","2e Resultaat");
_t("3rd output","3e Resultaat");
_t("4th output","4e Resultaat");
_t("1st -> 2nd","1e -> 2e");
_t("1st -> 3rd","1e -> 3e");
_t("1st -> 4th","1e -> 4e");
_t("All outputs","Alle resultaten");
_t("Output messages...","Output berichten...");
_t("Quiet (default)","Geen melding (standaard)");
_t("Verbose (layer name)","Uitgebreid (laag naam)");
_t("Verbose (console)","Uitgebreid (console)");
_t("Verbose (logfile)","Uitgebreid (logfile)");
_t("Very verbose (console)","Heel uitgebreid (console)");
_t("Very verbose (logfile)","Heel uitgebreid (logfile)");
_t("Debug mode (console)","Debug mode (console)");
_t("Debug mode (logfile)","Debug mode (logfile)");
_t("Preview size...","Voorbeeldformaat...");
_t("Tiny","Zeer klein");
_t("Small","Klein");
_t("Normal","Normale");
_t("Large","Groot");
_t(" Available filters (%u)"," Beschikbare filters (%u)");
_t("Update","Ververs");
_t("Rename","Hernoemen");
_t("Add filter to faves","Filter toevoegen aan favorieten");
_t("Remove filter from faves","Verwijder filter uit favorieten");
_t("Update filters","Update filters");
_t("Enable Internet updates","Enable Internet updates");
_t("Expand/collapse","Expand/collapse");
_t("Reset zoom","Reset zoom");
}
// French translation.
else if (!std::strcmp(locale,"fr")) {
if (!s) {
const char *const ns = "Mise &#224; jour depuis Internet incompl&#232;te !\n\n"
"Acc&#232;s impossible aux sources de filtres :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Avertissement:</b> L'aper\303\247u est probablement inexact\n"
"(le facteur de zoom a \303\251t\303\251 modifi\303\251)</span>");
_t("G'MIC for GIMP","G'MIC pour GIMP");
_t("<i>Select a filter...</i>","<i>Choisissez un filtre...</i>");
_t("<i>No parameters to set...</i>","<i>Pas de param&#232;tres...</i>");
_t("<b> Input / Output </b>","<b> Entr&#233;es / Sorties </b>");
_t("Input layers...","Calques d'entr\303\251e...");
_t("None","Aucun");
_t("Active (default)","Actif (d\303\251faut)");
_t("All","Tous");
_t("Active & below","Actif & en dessous");
_t("Active & above","Actif & au dessus");
_t("All visibles","Tous les visibles");
_t("All invisibles","Tous les invisibles");
_t("All visibles (decr.)","Tous les visibles (d\303\251cr.)");
_t("All invisibles (decr.)","Tous les invisibles (d\303\251cr.)");
_t("All (decr.)","Tous (d\303\251cr.)");
_t("Output mode...","Mode de sortie...");
_t("In place (default)","Sur place (d\303\251faut)");
_t("New layer(s)","Nouveau(x) calque(s)");
_t("New active layer(s)","Nouveau(x) calque(s) actifs");
_t("New image","Nouvelle image");
_t("Preview mode...","Mode d'aper\303\247u...");
_t("1st output (default)","1\303\250re image (d\303\251faut)");
_t("2nd output","2\303\250me image");
_t("3rd output","3\303\250me image");
_t("4th output","4\303\250me image");
_t("1st -> 2nd","1\303\250re -> 2\303\250me");
_t("1st -> 3rd","1\303\250re -> 3\303\250me");
_t("1st -> 4th","1\303\250re -> 4\303\250me");
_t("All outputs","Toutes les images");
_t("Output messages...","Messages de sortie...");
_t("Quiet (default)","Aucun message (d\303\251faut)");
_t("Verbose (layer name)","Mode verbeux (nom de calque)");
_t("Verbose (console)","Mode verbeux (console)");
_t("Verbose (logfile)","Mode verbeux (fichier log)");
_t("Very verbose (console)","Mode tr\303\250s verbeux (console)");
_t("Very verbose (logfile)","Mode tr\303\250s verbeux (fichier log)");
_t("Debug mode (console)","Mode d\303\251bogage (console)");
_t("Debug mode (logfile)","Mode d\303\251bogage (fichier log)");
_t("Preview size...","Taille d'aper\303\247u...");
_t("Tiny","Minuscule");
_t("Small","Petit");
_t("Normal","Normal");
_t("Large","Grand");
_t(" Available filters (%u)"," Filtres disponibles (%u)");
_t("Update","Actualiser");
_t("Rename","Renommer");
_t("Add filter to faves","Ajouter le filtre aux favoris");
_t("Remove filter from faves","Retirer le filtre des favoris");
_t("Update filters","Actualiser les filtres");
_t("Enable Internet updates","Autoriser les mises \303\240 jour Internet");
_t("Expand/collapse","D\303\251plier/Replier");
_t("Reset zoom","R\303\251initialize le zoom");
}
// German translation.
else if (!std::strcmp(locale,"de")) {
if (!s) {
const char *const ns = "Kein Internet-Update m\303\266glich !\n\n"
"Kann diese Filter Quellen erreichen :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Warnung:</b> Vorschau kann ungenau sein\n"
"(Vergr\303\266\303\237erung wurde ver\303\244ndert)</span>");
_t("G'MIC for GIMP","G'MIC f\303\274r GIMP");
_t("<i>Select a filter...</i>","<i>W\303\244hlen Sie einen Filter...</i>");
_t("<i>No parameters to set...</i>","<i>Keine w\303\244hlbaren Parameter...</i>");
_t("<b> Input / Output </b>","<b> Eingabe / Ausgabe </b>");
_t("Input layers...","Eingabeebenen...");
_t("None","Keine");
_t("Active (default)","Aktive (Standard)");
_t("All","Alle");
_t("Active & below","Aktive & darunterliegende");
_t("Active & above","Aktive & dar\303\274berliegende");
_t("All visibles","Alle sichtbaren");
_t("All invisibles","Alle nicht sichtbaren");
_t("All visibles (decr.)","Alle sichtbaren (absteigend)");
_t("All invisibles (decr.)","Alle nicht sichtbaren (absteigend)");
_t("All (decr.)","Alle (absteigend)");
_t("Output mode...","Ausgabemodus...");
_t("In place (default)","Bestehende ersetzen (standard)");
_t("New layer(s)","Neue Ebene(n)");
_t("New active layer(s)","Neue aktive Ebene(n)");
_t("New image","Neues Bild");
_t("Preview mode...","Ausgabevorschau...");
_t("1st output (default)","1. Ausgabe (Standard)");
_t("2nd output","2. Ausgabe");
_t("3rd output","3. Ausgabe");
_t("4th output","4. Ausgabe");
_t("1st -> 2nd","1. -> 2.");
_t("1st -> 3rd","1. -> 3.");
_t("1st -> 4th","1. -> 4.");
_t("All outputs","Alle Ausgaben");
_t("Output messages...","Ausgabemeldungen...");
_t("Quiet (default)","Keine Meldung (Standard)");
_t("Verbose (layer name)","Ausf\303\274hrlich (Ebenennamen)");
_t("Verbose (console)","Ausf\303\274hrlich (Konsole)");
_t("Verbose (logfile)","Ausf\303\274hrlich (Logfile)");
_t("Very verbose (console)","Sehr ausf\303\274hrlich (Konsole)");
_t("Very verbose (logfile)","Sehr ausf\303\274hrlich (Logfile)");
_t("Debug mode (console)","Debug-Modus (Konsole)");
_t("Debug mode (logfile)","Debug-Modus (Logfile)");
_t("Preview size...","Vorschaugrosse...");
_t("Tiny","Winzig");
_t("Small","Klein");
_t("Normal","Normal");
_t("Large","Gross");
_t(" Available filters (%u)"," Verf\303\274gbare Filter (%u)");
_t("Update","Update");
_t("Rename","Umbenennen");
_t("Add filter to faves","Filter zu Favoriten hinzuf\303\274gen");
_t("Remove filter from faves","Filter aus Favoriten entfernen");
_t("Update filters","Filter updaten");
_t("Enable Internet updates","Internet-Updates aktivieren");
_t("Expand/collapse","Ein-/Ausklappen");
_t("Reset zoom","Reset Zoom");
}
// Italian translation.
else if (!std::strcmp(locale,"it")) {
if (!s) {
const char *const ns = "Impossibile aggiornare da Internet !\n\n"
"Impossibile raggiungere queste fonti filtri :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Attenzione:</b> L'anteprima pu\303\262 essere inaccurata\n"
"(il fattore di zoom \303\250 stato modificato)</span>");
_t("G'MIC for GIMP","G'MIC per GIMP");
_t("<i>Select a filter...</i>","<i>Sciegliete un Filtro...</i>");
_t("<i>No parameters to set...</i>","<i>Filtro senza Parametri...</i>");
_t("<b> Input / Output </b>","<b> Input / Output </b>");
_t("Input layers...","Input da Layers...");
_t("None","Nessuno");
_t("Active (default)","Layer Attivo (default)");
_t("All","Tutti");
_t("Active & below","Attivo & superiori");
_t("Active & above","Attivo & inferiori");
_t("All visibles","Tutti i Visibili");
_t("All invisibles","Tutti gli invisibili");
_t("All visibles (decr.)","Tutti i visibili (dal fondo)");
_t("All invisibles (decr.)","Tutti gli invisibili (dal fondo)");
_t("All (decr.)","Tutti (dal fondo)");
_t("Output mode...","Tipo di output...");
_t("In place (default)","Applica al Layer attivo (default) ");
_t("New layer(s)","Nuovo(i) Layer(s)");
_t("New active layer(s)","Attiva Nuovo(i) Layer(s)");
_t("New image","Nuova Immagine");
_t("Preview mode...","Anteprima...");
_t("1st output (default)","Primo Output (default)");
_t("2nd output","Secondo Output");
_t("3rd output","Terzo Output");
_t("4th output","Quarto Output");
_t("1st -> 2nd","1 -> 2");
_t("1st -> 3rd","1 -> 3");
_t("1st -> 4th","1 -> 4");
_t("All outputs","Tutti i layers");
_t("Output messages...","Messaggi di Output...");
_t("Quiet (default)","Nessun Messaggio (default)");
_t("Verbose (layer name)","Messagi (nome del Layer)");
_t("Verbose (console)","Messagi (console)");
_t("Verbose (logfile)","Messagi (logfile)");
_t("Very verbose (console)","Messaggi Dettagliati (console)");
_t("Very verbose (logfile)","Messaggi Dettagliati (logfile)");
_t("Debug mode (console)","Debug Mode (console)");
_t("Debug mode (logfile)","Debug Mode (logfile)");
_t("Preview size...","Anteprima dimensioni...");
_t("Tiny","Minuscolo");
_t("Small","Piccolo");
_t("Normal","Normale");
_t("Large","Grande");
_t(" Available filters (%u)"," Filtri disponibili (%u)");
_t("Update","Aggiornare");
_t("Rename","Rinominare");
_t("Add filter to faves","Aggiungi filtro ai favoriti");
_t("Remove filter from faves","Rimuovi filtro dai favoriti");
_t("Update filters","Aggiorna filtri");
_t("Enable Internet updates","Abilita aggiornamenti Internet");
_t("Expand/collapse","Espandi/comprimi");
_t("Reset zoom","Reimpostare lo zoom");
}
// Japanese translation.
else if (!std::strcmp(locale,"ja")) {
if (!s) {
const char *const ns = "\xE3\x82\xA4\xE3\x83\xB3\xE3\x82\xBF\xE3\x83\xBC\xE3\x83\x8D\xE3\x83\x83\xE3\x83"
"\x88\xE7\xB5\x8C\xE7\x94\xB1\xE3\x81\xA7\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\xAB\xE5\xA4\xB1\xE6\x95"
"\x97\xE3\x81\x97\xE3\x81\x9F\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x81\x8C\xE3\x81\x82\xE3\x82"
"\x8A\xE3\x81\xBE\xE3\x81\x99\xEF\xBC\x81\n\n"
"\xE4\xBB\xA5\xE4\xB8\x8B\xE3\x81\xAE\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x81\xAE\xE9\x85\x8D"
"\xE5\xB8\x83\xE5\x85\x83\xE3\x81\xAB\xE3\x82\xA2\xE3\x82\xAF\xE3\x82\xBB\xE3\x82\xB9\xE3\x81\xA7\xE3\x81\x8D"
"\xE3\x81\xBE\xE3\x81\x9B\xE3\x82\x93\xE3\x81\xA7\xE3\x81\x97\xE3\x81\x9F\xE3\x80\x82\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>\xE8\xAD\xA6\xE5\x91\x8A:</b> \xE3\x83\x97\xE3\x83\xAC\xE3\x83\x93\xE3\x83\xA5"
"\xE3\x83\xBC\xE3\x81\xAF\xE5\xAE\x9F\xE9\x9A\x9B\xE3\x81\xAE\xE5\xAE\x9F\xE8\xA1\x8C\xE7\xB5\x90\xE6\x9E\x9C"
"\xE3\x81\xA8\xE7\x95\xB0\xE3\x81\xAA\xE3\x82\x8B\xE5\xA0\xB4\xE5\x90\x88\xE3\x81\x8C\xE3\x81\x82\xE3\x82\x8A"
"\xE3\x81\xBE\xE3\x81\x99\n"
"(\xE6\x8B\xA1\xE5\xA4\xA7\xE7\x8E\x87\xE3\x81\x8C\xE5\xA4\x89\xE6\x9B\xB4\xE3\x81\x95\xE3\x82\x8C\xE3\x81\xBE"
"\xE3\x81\x97\xE3\x81\x9F)</span>");
_t("G'MIC for GIMP","G'MIC for GIMP");
_t("<i>Select a filter...</i>","<i>\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x92\xE9\x81\xB8\xE6\x8A"
"\x9E\xE3\x81\x97\xE3\x81\xA6\xE3\x81\x8F\xE3\x81\xA0\xE3\x81\x95\xE3\x81\x84...</i>");
_t("<i>No parameters to set...</i>","<i>\xE8\xA8\xAD\xE5\xAE\x9A\xE3\x81\x99\xE3\x82\x8B\xE3\x83\x91\xE3\x83\xA9"
"\xE3\x83\xA1\xE3\x83\xBC\xE3\x82\xBF\xE3\x81\x8C\xE3\x81\x82\xE3\x82\x8A\xE3\x81\xBE\xE3\x81\x9B\xE3\x82\x93"
"...</i>");
_t("<b> Input / Output </b>","<b> \xE5\x85\xA5\xE5\x8A\x9B\x2F\xE5\x87\xBA\xE5\x8A\x9B </b>");
_t("Input layers...","\xE5\x85\xA5\xE5\x8A\x9B\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC...");
_t("None","\xE3\x81\xAA\xE3\x81\x97");
_t("Active (default)","\xE3\x82\xA2\xE3\x82\xAF\xE3\x83\x86\xE3\x82\xA3\xE3\x83\x96\xE3\x81\xAA\xE3\x83\xAC\xE3\x82"
"\xA4\xE3\x83\xA4\xE3\x83\xBC (\xE3\x83\x87\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x88)");
_t("All","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6");
_t("Active & below","\xE3\x82\xA2\xE3\x82\xAF\xE3\x83\x86\xE3\x82\xA3\xE3\x83\x96 & \xE4\xB8\x80\xE3\x81\xA4\xE4"
"\xB8\x8A\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("Active & above","\xE3\x82\xA2\xE3\x82\xAF\xE3\x83\x86\xE3\x82\xA3\xE3\x83\x96 & \xE4\xB8\x80\xE3\x81\xA4\xE4"
"\xB8\x8B\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("All visibles","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6\xE3\x81\xAE\xE5\x8F\xAF\xE8\xA6\x96\xE3\x83\xAC\xE3\x82"
"\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("All invisibles","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6\xE3\x81\xAE\xE4\xB8\x8D\xE5\x8F\xAF\xE8\xA6\x96\xE3\x83"
"\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("All visibles (decr.)","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6\xE3\x81\xAE\xE5\x8F\xAF\xE8\xA6\x96\xE3\x83\xAC\xE3"
"\x82\xA4\xE3\x83\xA4\xE3\x83\xBC (\xE9\x80\x86\xE9\xA0\x86)");
_t("All invisibles (decr.)","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6\xE3\x81\xAE\xE4\xB8\x8D\xE5\x8F\xAF\xE8\xA6\x96"
"\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC (\xE9\x80\x86\xE9\xA0\x86)");
_t("All (decr.)","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6 (\xE9\x80\x86\xE9\xA0\x86)");
_t("Output mode...","\xE5\x87\xBA\xE5\x8A\x9B\xE3\x83\xA2\xE3\x83\xBC\xE3\x83\x89...");
_t("In place (default)","\xE7\x8F\xBE\xE5\x9C\xA8\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC "
"(\xE3\x83\x87\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x88)");
_t("New layer(s)","\xE6\x96\xB0\xE8\xA6\x8F\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("New active layer(s)","\xE3\x82\xA2\xE3\x82\xAF\xE3\x83\x86\xE3\x82\xA3\xE3\x83\x96\xE3\x81\xAA\xE6\x96\xB0\xE8"
"\xA6\x8F\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC");
_t("New image","\xE6\x96\xB0\xE8\xA6\x8F\xE7\x94\xBB\xE5\x83\x8F");
_t("Preview mode...","\xE3\x83\x97\xE3\x83\xAC\xE3\x83\x93\xE3\x83\xA5\xE3\x83\xBC\xE3\x83\xA2\xE3\x83\xBC\xE3\x83"
"\x89...");
_t("1st output (default)","1 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC"
"\xE3\x81\xAB\xE9\x81\xA9\xE7\x94\xA8 (\xE3\x83\x87\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x88)");
_t("2nd output","2 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81\xAB"
"\xE9\x81\xA9\xE7\x94\xA8");
_t("3rd output","3 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81\xAB"
"\xE9\x81\xA9\xE7\x94\xA8");
_t("4th output","4 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81\xAB"
"\xE9\x81\xA9\xE7\x94\xA8");
_t("1st -> 2nd","1 ~ 2 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81"
"\xAB\xE9\x81\xA9\xE7\x94\xA8");
_t("1st -> 3rd","1 ~ 3 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81"
"\xAB\xE9\x81\xA9\xE7\x94\xA8");
_t("1st -> 4th","1 ~ 4 \xE6\x9E\x9A\xE7\x9B\xAE\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE3\x81"
"\xAB\xE9\x81\xA9\xE7\x94\xA8");
_t("All outputs","\xE3\x81\x99\xE3\x81\xB9\xE3\x81\xA6\xE3\x81\xAE\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC"
"\xE3\x81\xAB\xE9\x81\xA9\xE7\x94\xA8");
_t("Output messages...","\xE5\x87\xBA\xE5\x8A\x9B\xE3\x83\xA1\xE3\x83\x83\xE3\x82\xBB\xE3\x83\xBC\xE3\x82\xB8...");
_t("Quiet (default)","\xE3\x81\xAA\xE3\x81\x97 (\xE3\x83\x87\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x88)");
_t("Verbose (layer name)","\xE6\x83\x85\xE5\xA0\xB1\xE3\x82\x92\xE5\x87\xBA\xE5\x8A\x9B "
"(\xE3\x83\xAC\xE3\x82\xA4\xE3\x83\xA4\xE3\x83\xBC\xE5\x90\x8D)");
_t("Verbose (console)","\xE6\x83\x85\xE5\xA0\xB1\xE3\x82\x92\xE5\x87\xBA\xE5\x8A\x9B "
"(\xE3\x82\xB3\xE3\x83\xB3\xE3\x82\xBD\xE3\x83\xBC\xE3\x83\xAB)");
_t("Verbose (logfile)","\xE6\x83\x85\xE5\xA0\xB1\xE3\x82\x92\xE5\x87\xBA\xE5\x8A\x9B "
"(\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB)");
_t("Very verbose (console)","\xE8\xA9\xB3\xE7\xB4\xB0\xE3\x81\xAA\xE6\x83\x85\xE5\xA0\xB1\xE3\x82\x92\xE5\x87\xBA"
"\xE5\x8A\x9B (\xE3\x82\xB3\xE3\x83\xB3\xE3\x82\xBD\xE3\x83\xBC\xE3\x83\xAB)");
_t("Very verbose (logfile)","\xE8\xA9\xB3\xE7\xB4\xB0\xE3\x81\xAA\xE6\x83\x85\xE5\xA0\xB1\xE3\x82\x92\xE5\x87\xBA"
"\xE5\x8A\x9B (\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB)");
_t("Debug mode (console)","\xE3\x83\x87\xE3\x83\x90\xE3\x83\x83\xE3\x82\xB0\xE3\x83\xA2\xE3\x83\xBC\xE3\x83\x89 "
"(\xE3\x82\xB3\xE3\x83\xB3\xE3\x82\xBD\xE3\x83\xBC\xE3\x83\xAB)");
_t("Debug mode (logfile)","\xE3\x83\x87\xE3\x83\x90\xE3\x83\x83\xE3\x82\xB0\xE3\x83\xA2\xE3\x83\xBC\xE3\x83\x89 "
"(\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB)");
_t("Preview size...","\xE3\x83\x97\xE3\x83\xAC\xE3\x83\x93\xE3\x83\xA5\xE3\x83\xBC\xE3\x82\xB5\xE3\x82\xA4\xE3\x82"
"\xBA...");
_t("Tiny","\xE6\xA5\xB5\xE5\xB0\x8F");
_t("Small","\xE5\xB0\x8F");
_t("Normal","\xE6\x99\xAE\xE9\x80\x9A");
_t("Large","\xE5\xA4\xA7");
_t(" Available filters (%u)"," \xE5\x88\xA9\xE7\x94\xA8\xE5\x8F\xAF\xE8\x83\xBD\xE3\x81\xAA\xE3\x83\x95\xE3\x82\xA3"
"\xE3\x83\xAB\xE3\x82\xBF (%u)");
_t("Update","\xE6\x9B\xB4\xE6\x96\xB0");
_t("Rename","\xE5\x90\x8D\xE5\x89\x8D\xE3\x82\x92\xE5\xA4\x89\xE6\x9B\xB4");
_t("Internet","\xE3\x82\xA4\xE3\x83\xB3\xE3\x82\xBF\xE3\x83\xBC\xE3\x83\x8D\xE3\x83\x83\xE3\x83\x88");
_t("Add filter to faves","\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x92\xE3\x81\x8A\xE6\xB0\x97\xE3"
"\x81\xAB\xE5\x85\xA5\xE3\x82\x8A\xE3\x81\xAB\xE8\xBF\xBD\xE5\x8A\xA0");
_t("Remove filter from faves","\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x92\xE3\x81\x8A\xE6\xB0\x97"
"\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x8A\xE3\x81\x8B\xE3\x82\x89\xE5\x89\x8A\xE9\x99\xA4");
_t("Update filters","\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x92\xE6\x9B\xB4\xE6\x96\xB0");
_t("Enable Internet updates","\xE3\x82\xA4\xE3\x83\xB3\xE3\x82\xBF\xE3\x83\xBC\xE3\x83\x8D\xE3\x83\x83\xE3\x83\x88"
"\xE7\xB5\x8C\xE7\x94\xB1\xE3\x81\xA7\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x82\x92\xE6\x9C\x89\xE5\x8A\xB9"
"\xE5\x8C\x96");
_t("Expand/collapse","\xE5\xB1\x95\xE9\x96\x8B\x2F\xE6\x8A\x98\xE3\x82\x8A\xE3\x81\x9F\xE3\x81\x9F\xE3\x82\x80");
_t("Reset zoom","\xE3\x82\xBA\xE3\x83\xBC\xE3\x83\xA0\xE3\x82\x92\xE3\x83\xAA\xE3\x82\xBB\xE3\x83\x83\xE3\x83\x88");
}
// Polish translation.
else if (!std::strcmp(locale,"pl")) {
if (!s) {
const char *const ns = "Aktualizacja filtr\303\263w przez internet (cz\304\231\305\233ciowo) nie "
"powiod\305\202a si\304\231 !\n\n"
"Brak dost\304\231pu do tych \305\272r\303\263de\305\202 filtr\303\263w :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Uwaga:</b> Podgl\304\205d mo\305\274e si\304\231 r\303\263\305\274ni\304\207 od "
"efektu ko\305\204cowego\n"
"ze wzgl\304\231du na zmian\304\231 przybli\305\274enia</span>");
_t("G'MIC for GIMP","G'MIC dla GIMP");
_t("<i>Select a filter...</i>","<i>Wybierz filtr...</i>");
_t("<i>No parameters to set...</i>","<i>Brak parametr\304\205w do ustawienia...</i>");
_t("<b> Input / Output </b>","<b> Wej\305\233cie / Wyj\305\233cie </b>");
_t("Input layers...","Warstwy wej\305\233cia...");
_t("None","Brak");
_t("Active (default)","Aktywna (domy\305\233lnie)");
_t("All","Wszystkie");
_t("Active & below","Aktywna & poni\305\274ej");
_t("Active & above","Aktywna & powy\305\274ej");
_t("All visibles","Wszystkie widoczne");
_t("All invisibles","Wszystkie niewidoczne");
_t("All visibles (decr.)","Wszystkie widoczne (od do\305\202u)");
_t("All invisibles (decr.)","Wszystkie niewidoczne (od do\305\202u)");
_t("All (decr.)","Wszystkie (od do\305\202u)");
_t("Output mode...","Tryb wyj\305\233cia...");
_t("In place (default)","Na miejscu (domy\305\233lnie)");
_t("New layer(s)","Nowa/e warstwa/y");
_t("New active layer(s)","Nowa/e aktywna/e warstwa/y");
_t("New image","Nowy obraz");
_t("Preview mode...","Podgl\304\205d wyj\305\233cia dla warstw...");
_t("1st output (default)","Pierwszej (domy\305\233lnie)");
_t("2nd output","Drugiej");
_t("3rd output","Trzeciej");
_t("4th output","Czwartej");
_t("1st -> 2nd","Od 1 do 2");
_t("1st -> 3rd","Od 1 do 3");
_t("1st -> 4th","Od 1 do 4");
_t("All outputs","Wszystkich");
_t("Output messages...","Komunikat wyj\305\233cia...");
_t("Quiet (default)","Brak (domy\305\233lnie)");
_t("Verbose (layer name)","Og\303\263lny (nazwa warstwy)");
_t("Verbose (console)","Og\303\263lny (konsola)");
_t("Verbose (logfile)","Og\303\263lny (plik log)");
_t("Very verbose (console)","Dok\305\202adny (konsola)");
_t("Very verbose (logfile)","Dok\305\202adny (plik log)");
_t("Debug mode (console)","Debugowanie (konsola)");
_t("Debug mode (logfile)","Debugowanie (plik log)");
_t("Preview size...","Rozmiar podgl\304\205d...");
_t("Tiny","Male");
_t("Small","Mala");
_t("Normal","Normalne");
_t("Large","Duza");
_t(" Available filters (%u)"," Dost\304\231pne filtry (%u)");
_t("Update","Uaktualnij");
_t("Rename","Zmiana nazwy");
_t("Add filter to faves","Dodaj filtr do ulubionych");
_t("Remove filter from faves","Usun filtr z Ulubionych");
_t("Update filters","Filtry aktualizacji");
_t("Enable Internet updates","Wlacz aktualizacje internetowe");
_t("Expand/collapse","Rozwin/Zwin");
_t("Reset zoom","Zresetowa\304\207 zoomu");
}
// Portuguese translation.
else if (!std::strcmp(locale,"pt")) {
if (!s) {
const char *const ns = "A atualiza\303\247\303\243o pela internet falhou !\n\n"
"Incapaz de chegar a essas fontes de filtros :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Aten\303\247\303\243o:</b> a pr\303\251-visualiza\303\247\303\243o pode "
"estar incorreta\n"
"(o fator amplia\303\247\303\243o foi modificado)</span>");
_t("G'MIC for GIMP","G'MIC para o GIMP");
_t("<i>Select a filter...</i>","<i>Escolha um filtro</i>");
_t("<i>No parameters to set...</i>","<i>Sem par\303\242metros para configurar...</i>");
_t("<b> Input / Output </b>","<b> Entrada / Saida </b>");
_t("Input layers...","Camadas de Entrada...");
_t("None","Nenhuma");
_t("Active (default)","Ativo (Padr\303\243o)");
_t("All","Todos");
_t("Active & below","Ativo & abaixo");
_t("Active & above","Ativo & acima");
_t("All visibles","Todos vis\303\255veis");
_t("All invisibles","Todos invis\303\255veis");
_t("All visibles (decr.)","Todos vis\303\255veis (decr.)");
_t("All invisibles (decr.)","Todos invis\303\255veis (decr.)");
_t("All (decr.)","Todos (decr.)");
_t("Output mode...","Modo de saida...");
_t("In place (default)","No lugar (Padr\303\243o)");
_t("New layer(s)","Nova(s) camada(s)");
_t("New active layer(s)","Nova(s) camadas(s) ativa");
_t("New image","Nova imagem");
_t("Preview mode...","Pr\303\251 Visualiza\303\247\303\243o");
_t("1st output (default)","Primeira pr\303\251via (Padr\303\243o)");
_t("2nd output","2 pr\303\251via imagem");
_t("3rd output","3 pr\303\251via imagem");
_t("4th output","4 pr\303\251via imagem");
_t("1st -> 2nd","1st -> 2nd");
_t("1st -> 3rd","1st -> 3rd");
_t("1st -> 4th","1st -> 4th");
_t("All outputs","Todas as imagens");
_t("Output messages...","Mensagens de saida...");
_t("Quiet (default)","Quieto (Padr\303\243o)");
_t("Verbose (layer name)","Mode verbose (nome da camada)");
_t("Verbose (console)","Mode verbose (console)");
_t("Verbose (logfile)","Mode verbose (arquivo)");
_t("Very verbose (console)","Modo verbose ampliada (console)");
_t("Very verbose (logfile)","Modo verbose ampliada (arquivo)");
_t("Debug mode (console)","Modo Debug (console)");
_t("Debug mode (logfile)","Modo Debug (arquivo)");
_t("Preview size...","O tamanho da visualiza\303\247\303\243o...");
_t("Tiny","Minusculo");
_t("Small","Pequeno");
_t("Normal","Normal");
_t("Large","Grande");
_t(" Available filters (%u)"," Filtros dispon\303\255veis (%u)");
_t("Update","Atualizar");
_t("Rename","Renomear");
_t("Add filter to faves","Adicionar a favoritos filtro");
_t("Remove filter from faves","Remover filtro de favoritos");
_t("Update filters","Filtros de atualizacao");
_t("Enable Internet updates","Ativar atualizacoes Internet");
_t("Expand/collapse","Expandir/recolher");
_t("Reset zoom","Redefinir zoom");
}
// Serbian translation.
else if (!std::strcmp(locale,"sr")) {
if (!s) {
const char *const ns = "A\305\276uriranje filtera sa interneta (delimi\304\215no) neuspe\305\241no !\n\n"
"Nije mogu\304\207e dospeti do izvorne lokacije ovih filtera :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Upozorenje :</b> Pregled mo\305\276e biti neta\304\215na\n"
"( zum faktor je modifikovan )</span>");
_t("G'MIC for GIMP","G'MIC za GIMP");
_t("<i>Select a filter...</i>","<i>Izaberite filter...</i>");
_t("<i>No parameters to set...</i>","<i>Nema parametara za pode\305\241avanje...</i>");
_t("<b> Input / Output </b>","<b> Ulazni podaci / Rezultati </b>");
_t("Input layers...","Ulazni slojevi...");
_t("None","Nijedan");
_t("Active (default)","Aktivan (podrazumevana opcija)");
_t("All","Svi");
_t("Active & below","Aktivni & ispod");
_t("Active & above","Aktivni & iznad");
_t("All visibles","Svi vidljivi");
_t("All invisibles","Svi nevidljivi");
_t("All visibles (decr.)","Svi vidljivi (po opadaju\304\207em nizu)");
_t("All invisibles (decr.)","Svi nevidljivi (po opadaju\304\207em nizu)");
_t("All (decr.)","Svi (po opadaju\304\207em nizu)");
_t("Output mode...","Izlazni mod...");
_t("In place (default)","Umesto (podrazumevana opcija)");
_t("New layer(s)","Novi sloj(evi)");
_t("New active layer(s)","Novi aktivni sloj(evi)");
_t("New image","Nova slika");
_t("Preview mode...","Pregled rezultata...");
_t("1st output (default)","prvi rezultat (podrazumevana opcija)");
_t("2nd output","drugi rezultat");
_t("3rd output","tre\304\207i rezultat");
_t("4th output","\304\215etvrti rezultat");
_t("1st -> 2nd","prvi -> drugi");
_t("1st -> 3rd","prvi -> tre\304\207i");
_t("1st -> 4th","prvi -> \304\215etvrti");
_t("All outputs","Svi rezultati");
_t("Output messages...","Izlazne poruke...");
_t("Quiet (default)","Tiho (podrazumevana opcija)");
_t("Verbose (layer name)","Op\305\241irnije (slojevi)");
_t("Verbose (console)","Op\305\241irnije (konzola)");
_t("Verbose (logfile)","Op\305\241irnije (log fajl)");
_t("Very verbose (console)","Vrlo op\305\241irno (konzola)");
_t("Very verbose (logfile)","Vrlo op\305\241irno (log fajl)");
_t("Debug mode (console)","Mod za otklanjanje programskih gre\305\241aka (konzola)");
_t("Debug mode (logfile)","Mod za otklanjanje programskih gre\305\241aka (log fajl)");
_t("Preview size...","Pregled velicine...");
_t("Tiny","Veoma mali");
_t("Small","Mali");
_t("Normal","Normalno");
_t("Large","Veliki");
_t(" Available filters (%u)"," Raspolo\305\276ivi filteri (%u)");
_t("Update","Azuriranje");
_t("Rename","Preimenovati");
_t("Add filter to faves","Dodaj filter to faves");
_t("Remove filter from faves","Izvadite filter iz faves");
_t("Update filters","Update filteri");
_t("Enable Internet updates","Omoguci Internet ispravke");
_t("Expand/collapse","Razvij/skupi");
_t("Reset zoom","Reset zum");
}
// Spanish translation (Castillan).
else if (!std::strcmp(locale,"es")) {
if (!s) {
const char *const ns = "No es posible establecer conexión a Internet !\n\n"
"No es posible acceder a estas fuentes de filtros :\n";
return ns;
}
_t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>",
"\n<span color=\"#AA0000\"><b>Advertencia:</b> La vista previa puede ser inexacta\n"
"(el factor de zoom ha sido modificado)</span>");
_t("G'MIC for GIMP","G'MIC para GIMP");
_t("<i>Select a filter...</i>","<i>Selecciona un filtro...</i>");
_t("<i>No parameters to set...</i>","<i>Sin par\303\241metros...</i>");
_t("<b> Input / Output </b>","<b> Entrada / Salida </b>");
_t("Input layers...","Capas de entrada...");
_t("None","Ninguna");
_t("Active (default)","Activa (predet.)");
_t("All","Todas");
_t("Active & below","Activa e inferior");
_t("Active & above","Activa y superior");
_t("All visibles","Todas las visibles");
_t("All invisibles","Todas las invisibles");
_t("All visibles (decr.)","Todas las visibles (decr.)");
_t("All invisibles (decr.)","Todas las invisibles (decr.)");
_t("All (decr.)","Todas (decr.)");
_t("Output mode...","Modo de salida...");
_t("In place (default)","En la capa actual (predet.)");
_t("New layer(s)","Capa/as nueva/as");
_t("New active layer(s)","Capa/as nueva/as activa");
_t("New image","Imagen nueva");
_t("Preview mode...","Previsualizaci\303\263n de la salida...");
_t("1st output (default)","1ra imagen (predet.)");
_t("2nd output","2da imagen");
_t("3rd output","3ra imagen");
_t("4th output","4ta imagen");
_t("1st -> 2nd","1ra -> 2da");
_t("1st -> 3rd","1ra -> 3ra");
_t("1st -> 4th","1ra -> 4ta");
_t("All outputs","Todas las imagenes (salida)");
_t("Output messages...","Mensajes de salida...");
_t("Quiet (default)","Sin mensajes (predet.)");
_t("Verbose (layer name)","Detallado (nombre de la capa)");
_t("Verbose (console)","Detallado (consola)");
_t("Verbose (logfile)","Detallado (archivo_registro)");
_t("Very verbose (console)","Muy detallado (consola)");
_t("Very verbose (logfile)","Muy detallado (archivo_registro)");
_t("Debug mode (console)","Depuraci\303\263n (consola)");
_t("Debug mode (logfile)","Depuraci\303\263n (archivo_registro)");
_t("Preview size...","Tamano de previsualizaci\303\263n...");
_t("Tiny","Muy pequena");
_t("Small","Pequena");
_t("Normal","Normal");
_t("Large","Grande");
_t(" Available filters (%u)"," Filtros disponibles (%u)");
_t("Update","Actualitzaci\303\263n");
_t("Rename","Renombrar");
_t("Add filter to faves","A\303\261ade filtro a favoritos");
_t("Remove filter from faves","Remueve filtro de favoritos");
_t("Update filters","Actualiza filtros");
_t("Enable Internet updates","Permite actualizaciones de Internet");
_t("Expand/collapse","Expande/Colapsa");
_t("Reset zoom","Restablecer zoom");
}
// English translation (default).
if (!s) {
const char *const ns = "Filters update from Internet (partially) failed !\n\n"
"Unable to reach these filters sources:\n";
return ns;
}
return s;
}
// Functions to sort filter tree view.
//------------------------------------
gint tree_view_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer) {
char *name1, *name2;
gtk_tree_model_get(model,a,2,&name1,-1);
gtk_tree_model_get(model,b,2,&name2,-1);
return cimg::strcasecmp(name1,name2);
}
const char *tree_view_sort_str(const char *str) {
if (!str || !*str) return str;
while (str[0]=='<' && str[1] && str[2]=='>') str+=3;
return str;
}
// Flush filter tree view
//------------------------
void flush_tree_view(GtkWidget *const tree_view) {
const unsigned int filter = get_current_filter();
bool tree_mode = get_tree_mode();
unsigned int current_dir = 0;
CImg<char> current_path(64); *current_path = 0;
gimp_get_data("gmic_current_treepath",current_path);
if (tree_mode) { // Expand
cimglist_for(gmic_1stlevel_entries,l) {
GtkTreePath *path = gtk_tree_path_new_from_string(gmic_1stlevel_entries[l].data());
gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view),path,false);
gtk_tree_path_free(path);
}
} else { // Collapse
if (filter && *current_path && cimg_sscanf(current_path,"%u",&current_dir)==1) {
cimglist_for(gmic_1stlevel_entries,l) {
const char *const s_path = gmic_1stlevel_entries[l].data();
unsigned int dir = 0;
if (cimg_sscanf(s_path,"%u",&dir)!=1 || dir!=current_dir) {
GtkTreePath *path = gtk_tree_path_new_from_string(gmic_1stlevel_entries[l].data());
gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree_view),path);
gtk_tree_path_free(path);
}
}
} else gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
}
if (filter && *current_path) {
GtkTreePath *path = gtk_tree_path_new_from_string(current_path);
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(tree_view),path);
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree_view),path,NULL,FALSE,0,0);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_select_path(selection,path);
gtk_tree_path_free(path);
}
if (indice_faves<gmic_entries.size()) { // Always shows 'Faves' folder when available.
GtkTreePath *path = gtk_tree_path_new_from_string(gmic_1stlevel_entries[0].data());
gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view),path,false);
gtk_tree_path_free(path);
}
if (tree_mode_stock) gtk_widget_destroy(tree_mode_stock);
tree_mode_stock = gtk_button_new_from_stock(tree_mode?GTK_STOCK_ZOOM_OUT:GTK_STOCK_ZOOM_IN);
GtkWidget *tree_image = gtk_button_get_image(GTK_BUTTON(tree_mode_stock));
gtk_button_set_image(GTK_BUTTON(tree_mode_button),tree_image);
gtk_widget_show(tree_mode_button);
gtk_tree_view_remove_column(GTK_TREE_VIEW(tree_view),gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),0));
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
CImg<char> tree_view_title(64);
cimg_snprintf(tree_view_title,tree_view_title.width(),t(" Available filters (%u)"),nb_available_filters);
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(tree_view_title,renderer,"markup",1,NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view),column);
}
// Retrieve files and update filter tree structure.
//-------------------------------------------------
CImgList<char> update_filters(const bool try_net_update, const bool is_silent=false) {
// Build list of filter sources.
CImgList<gmic_pixel_type> _sources;
CImgList<char> _names;
CImg<char> command(1024);
cimg_snprintf(command,command.width(),
"%s-gimp_filter_sources %d",
get_verbosity_mode()>5?"-debug ":get_verbosity_mode()>3?"":"-v -99 ",
try_net_update?1:0);
try { gmic(command,_sources,_names,gmic_additional_commands,true); } catch (...) { }
CImgList<char> sources;
_sources.move_to(sources);
cimglist_for(sources,l) {
char &c = sources[l].unroll('x').back();
if (c) {
if (c==1) { c = 0; sources[l].columns(0,sources[l].width()); sources[l].back() = 1; }
else sources[l].columns(0,sources[l].width());
}
}
// Free existing definitions.
const unsigned int old_nb_filters = get_nbfilters();
if (tree_view_store) { g_object_unref(tree_view_store); tree_view_store = 0; }
gmic_additional_commands.assign();
gmic_1stlevel_entries.assign();
gmic_faves.assign();
gmic_entries.assign(1);
gmic_commands.assign(1);
gmic_preview_commands.assign(1);
gmic_preview_factors.assign(1);
gmic_arguments.assign(1);
if (try_net_update && !is_silent) gimp_progress_init(" G'MIC: Update filters...");
// Get filter definition files from external web servers.
CImg<char> filename(1024);
if (try_net_update && !is_silent) gimp_progress_pulse();
CImg<char> filename_tmp(1024);
CImgList<char> invalid_servers;
char sep = 0;
cimglist_for(sources,l) if (try_net_update && (!cimg::strncasecmp(sources[l],"http://",7) ||
!cimg::strncasecmp(sources[l],"https://",8))) {
const char *const s_basename = gmic::basename(sources[l]);
CImg<char> _s_basename = CImg<char>::string(s_basename);
cimg::strwindows_reserved(_s_basename);
if (!is_silent) gimp_progress_set_text_printf(" G'MIC: Update filters '%s'...",s_basename);
cimg_snprintf(filename,filename.width(),"%s%s",
gmic::path_rc(),_s_basename.data());
// Download filter file.
if (get_verbosity_mode()>1) { // Verbose mode.
std::fprintf(cimg::output(),"\n[gmic_gimp]./update/ Download new filter data from '%s'.\n",
sources[l].data());
std::fflush(cimg::output());
}
const unsigned int omode = cimg::exception_mode();
cimg::exception_mode() = 0;
try {
cimg::load_network(sources[l],filename_tmp,is_silent?4:60);
std::FILE *file = std::fopen(filename_tmp,"rb");
// Eventually, uncompress .cimgz file.
if (file && (std::fscanf(file," #@gmi%c",&sep)!=1 || sep!='c')) {
std::rewind(file);
try {
CImg<unsigned char> buffer; buffer.load_cimg(file); std::fclose(file);
if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./update/ Uncompress file '%s' (was '%s').\n",
filename_tmp.data(),sources[l].data());
buffer.save_raw(filename_tmp); file = std::fopen(filename_tmp,"rb"); }
catch (...) { }
}
if (file) std::rewind(file);
// Copy file to its final location.
if (file && std::fscanf(file," #@gmi%c",&sep)==1 && sep=='c') {
std::fclose(file);
if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./update/ Copy temporary file '%s' at destination '%s'.\n",
filename_tmp.data(),filename.data());
CImg<unsigned char>::get_load_raw(filename_tmp).save_raw(filename);
} else invalid_servers.insert(sources[l]); // Failed in recognizing file header.
} catch (...) { // Failed in downloading file.
invalid_servers.insert(sources[l]);
}
cimg::exception_mode() = omode;
if (!is_silent) gimp_progress_pulse();
std::remove(filename_tmp);
}
if (!is_silent) gimp_progress_set_text(" G'MIC: Update filters...");
// Read local source files.
CImgList<char> _gmic_additional_commands;
bool is_default_update = false;
cimglist_for(sources,l) {
const char *s_basename = gmic::basename(sources[l]);
CImg<char> _s_basename = CImg<char>::string(s_basename);
cimg::strwindows_reserved(_s_basename);
if (!cimg::strncasecmp(sources[l],"http://",7) ||
!cimg::strncasecmp(sources[l],"https://",8)) // Network file should have been copied in resources folder.
cimg_snprintf(filename,filename.width(),"%s%s",
gmic::path_rc(),_s_basename.data());
else // Local file, try to locate it at its hard-coded path.
cimg_snprintf(filename,filename.width(),"%s",
sources[l].data());
const unsigned int omode = cimg::exception_mode();
try {
CImg<char> com;
bool add_code_separator = false;
cimg::exception_mode(0);
if (sources[l].back()==1) { // Overload default, add more checking.
com.load_raw(filename);
const char *_com = com.data(), *const ecom = com.end();
while (_com<ecom && *_com<=32) ++_com;
if (_com + 6<ecom && !std::strncmp(_com,"#@gmic",6)) {
is_default_update = true;
com.move_to(_gmic_additional_commands);
add_code_separator = true;
} else if (com.data() + 15<ecom && !std::strncmp(com,"1 unsigned_char",15)) {
CImgList<char>::get_unserialize(com)[0].move_to(_gmic_additional_commands);
is_default_update = true;
add_code_separator = true;
}
} else {
com.load_raw(filename);
const char *const ecom = com.end();
if (com.data() + 15<ecom && !std::strncmp(com,"1 unsigned_char",15))
CImgList<char>::get_unserialize(com)[0].move_to(_gmic_additional_commands);
else com.move_to(_gmic_additional_commands);
add_code_separator = true;
}
if (add_code_separator)
CImg<char>::string("\n#@gimp ________\n",false).unroll('y').move_to(_gmic_additional_commands);
} catch(...) {
if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./update/ Filter file '%s' not found.\n",
filename.data());
std::fflush(cimg::output());
}
cimg::exception_mode(omode);
if (try_net_update && !is_silent) gimp_progress_pulse();
}
if (!is_default_update) { // Add hardcoded default filters if no updates of the default commands.
_gmic_additional_commands.insert(gmic::uncompress_stdlib());
CImg<char>::string("\n#@gimp ________\n",false).unroll('y').move_to(_gmic_additional_commands);
}
cimglist_for(_gmic_additional_commands,l) { // Remove unusual characters.
char *_p = _gmic_additional_commands[l];
cimg_for(_gmic_additional_commands[l],p,char) if (*p!=13) *(_p++) = (unsigned char)*p<' ' && *p!=10?' ':*p;
if (_p<_gmic_additional_commands[l].end())
CImg<char>(_gmic_additional_commands[l].data(),1,_p - _gmic_additional_commands[l].data()).
move_to(_gmic_additional_commands[l]);
}
CImg<char>::vector(0).move_to(_gmic_additional_commands);
(_gmic_additional_commands>'y').move_to(gmic_additional_commands);
// Add fave folder if necessary (make it before actually adding faves to make tree paths valids).
CImgList<char> gmic_1stlevel_names;
GtkTreeIter iter, fave_iter, parent[8];
CImg<char> filename_gmic_faves(1024);
tree_view_store = gtk_tree_store_new(3,G_TYPE_UINT,G_TYPE_STRING,G_TYPE_STRING);
cimg_snprintf(filename_gmic_faves,filename_gmic_faves.width(),"%sgimp_faves",
gmic::path_rc());
std::FILE *file_gmic_faves = std::fopen(filename_gmic_faves,"rb");
if (file_gmic_faves) {
gtk_tree_store_append(tree_view_store,&fave_iter,0);
gtk_tree_store_set(tree_view_store,&fave_iter,0,0,1,"<b>Faves</b>",2,"",-1);
const char *treepath = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(tree_view_store),&fave_iter);
CImg<char>::vector(0).move_to(gmic_1stlevel_names);
CImg<char>::string(treepath).move_to(gmic_1stlevel_entries);
}
// Parse filters descriptions for GIMP, and create corresponding sorted treeview_store.
CImg<char> line(256*1024), preview_command(256), arguments(65536), entry(256), locale = get_locale();
*line = *preview_command = *arguments = *entry = 0;
int level = 0, err = 0;
bool is_testing = false;
nb_available_filters = 0;
cimg_snprintf(line,line.width(),"#@gimp_%s ",locale.data());
// Use English for default language if no translated filters found.
if (!std::strstr(gmic_additional_commands,line)) { locale[0] = 'e'; locale[1] = 'n'; locale[2] = 0; }
for (const char *data = gmic_additional_commands; *data; ) {
char *_line = line;
// Read new line.
while (*data!='\n' && *data && _line<line.data() + line.width()) *(_line++) = *(data++); *_line = 0;
while (*data=='\n') ++data; // Skip next '\n'.
for (_line = line; *_line; ++_line) if (*_line<' ') *_line = ' '; // Replace non-usual characters by spaces.
if (line[0]!='#' || line[1]!='@' || line[2]!='g' || // Check for a '#@gimp' line.
line[3]!='i' || line[4]!='m' || line[5]!='p') continue;
if (line[6]=='_') { // Check for a localized filter.
// Whether the entry match current locale or not.
if (line[7]==locale[0] && line[8]==locale[1] && line[9]==' ') _line = line.data() + 10;
else continue;
} else if (line[6]==' ') _line = line.data() + 7; else continue; // Check for a non-localized filter.
if (*_line!=':') { // Check for a description of a possible filter or menu folder.
*entry = *command = *preview_command = *arguments = 0;
err = cimg_sscanf(_line," %255[^:]: %1023[^,]%*c %255[^,]%*c %65533[^\n]",
entry.data(),command.data(),preview_command.data(),arguments.data());
if (err==1) { // If entry defines a menu folder.
cimg::strpare(entry,' ',false,true);
char *nentry = entry; while (*nentry=='_') { ++nentry; --level; }
if (level<0) level = 0; else if (level>7) level = 7;
cimg::strpare(nentry,' ',false,true);
cimg::strpare(nentry,'\"',true,false);
if (*nentry) {
if (level) {
gtk_tree_store_append(tree_view_store,&parent[level],level?&parent[level - 1]:0);
gtk_tree_store_set(tree_view_store,&parent[level],0,0,1,nentry,
2,tree_view_sort_str(nentry),-1);
} else { // 1st-level folder.
bool is_duplicate = false;
cimglist_for(gmic_1stlevel_names,l)
if (!std::strcmp(nentry,gmic_1stlevel_names[l].data())) { // Folder name is a duplicate.
if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_view_store),&parent[level],
gmic_1stlevel_entries[l].data())) {
is_duplicate = true;
break;
}
}
// Detect if filter is in 'Testing/' (won't be counted in number of filters).
gtk_label_set_markup(GTK_LABEL(markup2ascii),nentry);
const char *_nentry = gtk_label_get_text(GTK_LABEL(markup2ascii));
is_testing = !std::strcmp(_nentry,"Testing");
if (!is_duplicate) {
gtk_tree_store_append(tree_view_store,&parent[level],level?&parent[level - 1]:0);
gtk_tree_store_set(tree_view_store,&parent[level],0,0,1,nentry,
2,tree_view_sort_str(nentry),-1);
const char *treepath = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(tree_view_store),
&parent[level]);
CImg<char>::string(nentry).move_to(gmic_1stlevel_names);
CImg<char>::string(treepath).move_to(gmic_1stlevel_entries);
unsigned int order = 0;
for (unsigned int i = 0; i<4; ++i) {
order<<=8;
if (*_nentry) order|=(unsigned char)cimg::lowercase(*(_nentry++));
}
}
}
++level;
}
} else if (err>=2) { // If entry defines a regular filter.
cimg::strpare(entry,' ',false,true);
char *nentry = entry; while (*nentry=='_') { ++nentry; --level; }
if (level<0) level = 0; else if (level>7) level = 7;
cimg::strpare(nentry,' ',false,true);
cimg::strpare(nentry,'\"',true,false);
cimg::strpare(command,' ',false,true);
cimg::strpare(arguments,' ',false,true);
if (*nentry) {
CImg<char>::string(nentry).move_to(gmic_entries);
CImg<char>::string(command).move_to(gmic_commands);
CImg<char>::string(arguments).move_to(gmic_arguments);
if (err>=3) { // Filter has a specified preview command.
cimg::strpare(preview_command,' ',false,true);
char *const preview_mode = std::strchr(preview_command,'(');
bool is_accurate_when_zoomed = false;
double factor = 1;
char sep = 0;
if (preview_mode &&
((cimg_sscanf(preview_mode + 1,"%lf)%c",&factor,&sep)==2 && sep=='+') ||
(cimg_sscanf(preview_mode + 1,"%lf%c",&factor,&sep)==2 && sep==')')) &&
factor>=0) {
*preview_mode = 0;
is_accurate_when_zoomed = sep=='+';
} else factor = -1;
CImg<char>::string(preview_command).move_to(gmic_preview_commands);
CImg<double>::vector(factor,(double)is_accurate_when_zoomed).move_to(gmic_preview_factors);
} else {
CImg<char>::string("_none_").move_to(gmic_preview_commands);
CImg<double>::vector(-1,1).move_to(gmic_preview_factors);
}
gtk_tree_store_append(tree_view_store,&iter,level?&parent[level - 1]:0);
gtk_tree_store_set(tree_view_store,&iter,0,gmic_entries.size() - 1,1,nentry,
2,tree_view_sort_str(nentry),-1);
if (!level) {
gtk_label_set_markup(GTK_LABEL(markup2ascii),nentry);
const char *_nentry = gtk_label_get_text(GTK_LABEL(markup2ascii));
unsigned int order = 0;
for (unsigned int i = 0; i<3; ++i) { order<<=8; if (*_nentry) order|=cimg::lowercase(*(_nentry++)); }
}
if (!is_testing) ++nb_available_filters; // Count only non-testing filters.
}
}
} else { // Line is the continuation of an entry.
if (gmic_arguments) {
if (gmic_arguments.back()) gmic_arguments.back().back() = ' ';
cimg::strpare(++_line,' ',false,true);
gmic_arguments.back().append(CImg<char>(_line,std::strlen(_line) + 1,1,1,1,true),'x');
}
}
}
// Sort current GtkTreeModel in alphabetical order.
GtkTreeSortable *const sortable = GTK_TREE_SORTABLE(tree_view_store);
gtk_tree_sortable_set_sort_func(sortable,0,tree_view_sort_func,NULL,NULL);
gtk_tree_sortable_set_sort_column_id(sortable,0,GTK_SORT_ASCENDING);
if (try_net_update && !is_silent) gimp_progress_pulse();
// Load faves.
CImg<char> label(256);
indice_faves = gmic_entries.size();
if (file_gmic_faves) {
for (unsigned int line_nb = 1; std::fscanf(file_gmic_faves," %[^\n]",line.data())==1; ++line_nb) {
char sep = 0;
if (cimg_sscanf(line,"{%255[^}]}{%255[^}]}{%255[^}]}{%255[^}]%c",
label.data(),entry.data(),command.data(),preview_command.data(),&sep)==5 && sep=='}') {
const char *_line = line.data() + 8 + std::strlen(label) + std::strlen(entry) + std::strlen(command) +
std::strlen(preview_command);
int entry_found = -1, command_found = -1, preview_found = -1;
unsigned int filter = 0;
for (filter = 1; filter<indice_faves; ++filter) {
const bool
is_entry_match = !std::strcmp(gmic_entries[filter].data(),entry),
is_command_match = !std::strcmp(gmic_commands[filter].data(),command),
is_preview_match = !std::strcmp(gmic_preview_commands[filter].data(),preview_command);
if (is_entry_match) entry_found = filter;
if (is_command_match) command_found = filter;
if (is_preview_match) preview_found = filter;
if (is_command_match && is_preview_match) break;
}
CImg<char>::string(line).move_to(gmic_faves);
// Get back '}' if necessary.
for (char *p = std::strchr(label,gmic_rbrace); p; p = std::strchr(p,gmic_rbrace)) *p = '}';
for (char *p = std::strchr(entry,gmic_rbrace); p; p = std::strchr(p,gmic_rbrace)) *p = '}';
if (filter>=indice_faves) { // Entry not found.
CImg<char>::string(label).move_to(gmic_entries);
CImg<char>::string("_none_").move_to(gmic_commands);
CImg<char>::string("_none_").move_to(gmic_preview_commands);
cimg_sprintf(line,"note = note{\"<span foreground=\"red\"><b>Warning: </b></span>This fave links to an "
"unreferenced entry/set of G'MIC commands:\n\n"
" - '<span foreground=\"purple\">%s</span>' as the entry name (%s%s%s%s%s).\n\n"
" - '<span foreground=\"purple\">%s</span>' as the command to compute the filter "
"(%s%s%s%s%s).\n\n"
" - '<span foreground=\"purple\">%s</span>' as the command to preview the filter "
"(%s%s%s%s%s)."
"\"}",
entry.data(),
entry_found>=0?"recognized, associated to <i>":"<b>not recognized</b>",
entry_found>=0?gmic_commands[entry_found].data():"",
entry_found>=0?", ":"",
entry_found>=0?gmic_preview_commands[entry_found].data():"",
entry_found>=0?"</i>":"",
command.data(),
command_found>=0?"recognized, associated to <i>":"<b>not recognized</b>",
command_found>=0?gmic_entries[command_found].data():"",
command_found>=0?", ":"",
command_found>=0?gmic_preview_commands[command_found].data():"",
command_found>=0?"</i>":"",
preview_command.data(),
preview_found>=0?"recognized, associated to <i>":"<b>not recognized</b>",
preview_found>=0?gmic_entries[preview_found].data():"",
preview_found>=0?", ":"",
preview_found>=0?gmic_commands[preview_found].data():"",
preview_found>=0?"</i>":"");
CImg<char>::string(line).move_to(gmic_arguments);
CImg<double>::vector(0,0).move_to(gmic_preview_factors);
set_filter_nbparams(gmic_entries.size() - 1,0);
} else { // Entry found.
CImg<char>::string(label).move_to(gmic_entries);
gmic_commands.insert(gmic_commands[filter]);
gmic_preview_commands.insert(gmic_preview_commands[filter]);
gmic_arguments.insert(gmic_arguments[filter]);
gmic_preview_factors.insert(gmic_preview_factors[filter]);
unsigned int nbp = 0;
for (nbp = 0; cimg_sscanf(_line,"{%65533[^}]%c",arguments.data(),&sep)==2 && sep=='}'; ++nbp) {
// Get back '}' if necessary.
for (char *p = std::strchr(arguments,gmic_rbrace); p; p = std::strchr(p,gmic_rbrace)) *p = '}';
// Get back '\n' if necessary.
for (char *p = std::strchr(arguments,gmic_newline); p; p = std::strchr(p,gmic_newline)) *p = '\n';
set_fave_parameter(gmic_entries.size() - 1,nbp,arguments);
_line+=2 + std::strlen(arguments);
}
set_filter_nbparams(gmic_entries.size() - 1,nbp);
}
gtk_tree_store_append(tree_view_store,&iter,&fave_iter);
gtk_tree_store_set(tree_view_store,&iter,0,gmic_entries.size() - 1,1,label.data(),
2,tree_view_sort_str(label),-1);
} else if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./update/ Malformed line %u in fave file '%s': '%s'.\n",
line_nb,filename_gmic_faves.data(),line.data());
}
std::fclose(file_gmic_faves);
}
set_nbfilters(gmic_entries.size());
if (try_net_update && !is_silent) {
gimp_progress_update(1);
gimp_progress_end();
}
if (gmic_entries.size()!=old_nb_filters) { reset_filters_parameters(); set_current_filter(0); }
return invalid_servers;
}
// 'Convert' a CImg<T> image to a RGB[A] CImg<unsigned char> image, withing the same buffer.
//------------------------------------------------------------------------------------------
template<typename T>
void convert_image2uchar(CImg<T>& img) {
const unsigned int siz = img.width()*img.height();
unsigned char *ptrd = (unsigned char*)img.data();
switch (img.spectrum()) {
case 1 : {
const T *ptr0 = img.data(0,0,0,0);
for (unsigned int i = 0; i<siz; ++i) *(ptrd++) = (unsigned char)*(ptr0++);
} break;
case 2 : {
const T *ptr0 = img.data(0,0,0,0), *ptr1 = img.data(0,0,0,1);
for (unsigned int i = 0; i<siz; ++i) {
*(ptrd++) = (unsigned char)*(ptr0++);
*(ptrd++) = (unsigned char)*(ptr1++);
}
} break;
case 3 : {
const T *ptr0 = img.data(0,0,0,0), *ptr1 = img.data(0,0,0,1), *ptr2 = img.data(0,0,0,2);
for (unsigned int i = 0; i<siz; ++i) {
*(ptrd++) = (unsigned char)*(ptr0++);
*(ptrd++) = (unsigned char)*(ptr1++);
*(ptrd++) = (unsigned char)*(ptr2++);
}
} break;
case 4 : {
const T *ptr0 = img.data(0,0,0,0), *ptr1 = img.data(0,0,0,1),
*ptr2 = img.data(0,0,0,2), *ptr3 = img.data(0,0,0,3);
for (unsigned int i = 0; i<siz; ++i) {
*(ptrd++) = (unsigned char)*(ptr0++);
*(ptrd++) = (unsigned char)*(ptr1++);
*(ptrd++) = (unsigned char)*(ptr2++);
*(ptrd++) = (unsigned char)*(ptr3++);
}
} break;
default: return;
}
}
// Calibrate any image to fit the required number of channels (GRAY,GRAYA, RGB or RGBA).
//---------------------------------------------------------------------------------------
#define CLAMPF(a, mn, mx) ((a) < (mn) ? (mn) : ((a) > (mx) ? (mx) : (a)))
template<typename T>
void convert_to_display(CImg<T>& img) {
if (!img ) return;
GimpColorConfig* color_config = gimp_get_color_configuration();
if(!color_config) return;
printf("convert_to_display(): img_profile=%p\n", (void*)img_profile);
if( img_profile == NULL ) return;
//printf("convert_to_display(): img.spectrum()=%d WxH=%dx%d\n",
// (int)img.spectrum(),(int)img.width(),(int)img.height());
const unsigned int linsiz = img.width()*img.spectrum();
float* linbuf = (float*)malloc(linsiz*sizeof(float));
//printf("convert_to_display(): linsiz=%d, linbuf=%p\n",
// (int)linsiz, (void*)linbuf);
const unsigned int siz = img.width()*img.height();
GimpColorRenderingIntent intent =
GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC;
GimpColorTransformFlags flags = 0;
flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
switch (img.spectrum()) {
case 1 : {
} break;
case 2 : {
} break;
case 3 : {
Babl* fmt = babl_format ("R'G'B' float");
GimpColorTransform* transform =
gimp_widget_get_color_transform(gui_preview, color_config, img_profile, fmt, fmt);
// gimp_color_transform_new(img_profile, fmt,
// dpy_profile, fmt,
// intent,
// flags);
printf("convert_to_display(): transform=%p\n", (void*)transform);
if( transform ) {
for(unsigned int r = 0; r < img.height(); r++ ){
T *ptr0 = img.data(0,r,0,0);
T *ptr1 = img.data(0,r,0,1);
T *ptr2 = img.data(0,r,0,2);
//printf(" processing row %d (%p %p %p %p)\n",
// r, (void*)ptr0, (void*)ptr1, (void*)ptr2, (void*)linbuf);
//printf(" processing row %d\n", r);
for (unsigned int i = 0, j = 0; i<linsiz; i += img.spectrum(), j++) {
//printf(" copying col %d (i=%d), linbuf=%p\n", j, i, (void*)linbuf);
linbuf[i] = ptr0[j]/255;
linbuf[i+1] = ptr1[j]/255;
linbuf[i+2] = ptr2[j]/255;
if( false && r==0 && j<4) {
printf(" in -> ptr: %f %f %f, linbuf[i]: %f %f %f\n",
(float)ptr0[j],(float)ptr1[j],(float)ptr2[j],
linbuf[i],linbuf[i+1],linbuf[i+2]);
}
}
//printf(" copying finished\n");
//printf(" applying ICC transform...\n");
gimp_color_transform_process_pixels(transform,
fmt, linbuf,
fmt, linbuf,
img.width());
//printf(" ... done.\n");
for (unsigned int i = 0, j = 0; i<linsiz; i += img.spectrum(), j++) {
//printf(" copying back col %d\n", j);
ptr0[j] = CLAMPF(linbuf[i]*255,0,255);
ptr1[j] = CLAMPF(linbuf[i+1]*255,0,255);
ptr2[j] = CLAMPF(linbuf[i+2]*255,0,255);
if( false && r==0 && j<4) {
printf(" out -> ptr: %f %f %f, linbuf[i]: %f %f %f\n",
(float)ptr0[j],(float)ptr1[j],(float)ptr2[j],
linbuf[i],linbuf[i+1],linbuf[i+2]);
}
}
}
g_object_unref(transform);
}
} break;
case 4 : {
Babl* fmt = babl_format ("R'G'B'A float");
GimpColorTransform* transform =
gimp_widget_get_color_transform(gui_preview, color_config, img_profile, fmt, fmt);
// gimp_color_transform_new(img_profile, fmt,
// dpy_profile, fmt,
// intent,
// flags);
printf("convert_to_display(): transform=%p\n", (void*)transform);
if( transform ) {
for(unsigned int r = 0; r < img.height(); r++ ){
T *ptr0 = img.data(0,r,0,0);
T *ptr1 = img.data(0,r,0,1);
T *ptr2 = img.data(0,r,0,2);
T *ptr3 = img.data(0,r,0,3);
//printf(" processing row %d (%p %p %p %p)\n",
// r, (void*)ptr0, (void*)ptr1, (void*)ptr2, (void*)linbuf);
//printf(" processing row %d\n", r);
for (unsigned int i = 0, j = 0; i<linsiz; i += img.spectrum(), j++) {
//printf(" copying col %d\n", j);
linbuf[i] = ptr0[j]/255;
linbuf[i+1] = ptr1[j]/255;
linbuf[i+2] = ptr2[j]/255;
linbuf[i+3] = ptr3[j]/255;
if( false && r==0 && j<4) {
printf(" in -> ptr: %f %f %f, linbuf[i]: %f %f %f\n",
(float)ptr0[j],(float)ptr1[j],(float)ptr2[j],
linbuf[i],linbuf[i+1],linbuf[i+2]);
}
}
//printf(" applying ICC transform...\n");
gimp_color_transform_process_pixels(transform,
fmt, linbuf,
fmt, linbuf,
img.width());
//printf(" ... done.\n");
for (unsigned int i = 0, j = 0; i<linsiz; i += img.spectrum(), j++) {
//printf(" copying back col %d\n", j);
ptr0[j] = CLAMPF(linbuf[i]*255,0,255);
ptr1[j] = CLAMPF(linbuf[i+1]*255,0,255);
ptr2[j] = CLAMPF(linbuf[i+2]*255,0,255);
if( false && r==0 && j<4) {
printf(" out -> ptr: %f %f %f, linbuf[i]: %f %f %f\n",
(float)ptr0[j],(float)ptr1[j],(float)ptr2[j],
linbuf[i],linbuf[i+1],linbuf[i+2]);
}
}
}
g_object_unref(transform);
}
} break;
}
//printf("convert_to_display(): freeing linbuf\n");
if(linbuf) free(linbuf);
}
// Calibrate any image to fit the required number of channels (GRAY,GRAYA, RGB or RGBA).
//---------------------------------------------------------------------------------------
template<typename T>
void calibrate_image(CImg<T>& img, const unsigned int spectrum, const bool is_preview) {
if (!img || !spectrum) return;
switch (spectrum) {
case 1 : // To GRAY
switch (img.spectrum()) {
case 1 : // from GRAY
break;
case 2 : // from GRAYA
if (is_preview) {
T *ptr_r = img.data(0,0,0,0), *ptr_a = img.data(0,0,0,1);
cimg_forXY(img,x,y) {
const unsigned int a = (unsigned int)*(ptr_a++), i = 96 + (((x^y)&8)<<3);
*ptr_r = (T)((a*(unsigned int)*ptr_r + (255 - a)*i)>>8);
++ptr_r;
}
}
img.channel(0);
break;
case 3 : // from RGB
(img.get_shared_channel(0)+=img.get_shared_channel(1)+=img.get_shared_channel(2))/=3;
img.channel(0);
break;
case 4 : // from RGBA
(img.get_shared_channel(0)+=img.get_shared_channel(1)+=img.get_shared_channel(2))/=3;
if (is_preview) {
T *ptr_r = img.data(0,0,0,0), *ptr_a = img.data(0,0,0,3);
cimg_forXY(img,x,y) {
const unsigned int a = (unsigned int)*(ptr_a++), i = 96 + (((x^y)&8)<<3);
*ptr_r = (T)((a*(unsigned int)*ptr_r + (255 - a)*i)>>8);
++ptr_r;
}
}
img.channel(0);
break;
default : // from multi-channel (>4)
img.channel(0);
} break;
case 2: // To GRAYA
switch (img.spectrum()) {
case 1: // from GRAY
img.resize(-100,-100,1,2,0).get_shared_channel(1).fill(255);
break;
case 2: // from GRAYA
break;
case 3: // from RGB
(img.get_shared_channel(0)+=img.get_shared_channel(1)+=img.get_shared_channel(2))/=3;
img.channels(0,1).get_shared_channel(1).fill(255);
break;
case 4: // from RGBA
(img.get_shared_channel(0)+=img.get_shared_channel(1)+=img.get_shared_channel(2))/=3;
img.get_shared_channel(1) = img.get_shared_channel(3);
img.channels(0,1);
break;
default: // from multi-channel (>4)
img.channels(0,1);
} break;
case 3: // to RGB
switch (img.spectrum()) {
case 1: // from GRAY
img.resize(-100,-100,1,3);
break;
case 2: // from GRAYA
if (is_preview) {
T *ptr_r = img.data(0,0,0,0), *ptr_a = img.data(0,0,0,1);
cimg_forXY(img,x,y) {
const unsigned int a = (unsigned int)*(ptr_a++), i = 96 + (((x^y)&8)<<3);
*ptr_r = (T)((a*(unsigned int)*ptr_r + (255 - a)*i)>>8);
++ptr_r;
}
}
img.channel(0).resize(-100,-100,1,3);
break;
case 3: // from RGB
break;
case 4: // from RGBA
if (is_preview) {
T *ptr_r = img.data(0,0,0,0), *ptr_g = img.data(0,0,0,1),
*ptr_b = img.data(0,0,0,2), *ptr_a = img.data(0,0,0,3);
cimg_forXY(img,x,y) {
const unsigned int a = (unsigned int)*(ptr_a++), i = 96 + (((x^y)&8)<<3);
*ptr_r = (T)((a*(unsigned int)*ptr_r + (255 - a)*i)>>8);
*ptr_g = (T)((a*(unsigned int)*ptr_g + (255 - a)*i)>>8);
*ptr_b = (T)((a*(unsigned int)*ptr_b + (255 - a)*i)>>8);
++ptr_r; ++ptr_g; ++ptr_b;
}
}
img.channels(0,2);
break;
default: // from multi-channel (>4)
img.channels(0,2);
} break;
case 4: // to RGBA
switch (img.spectrum()) {
case 1: // from GRAY
img.resize(-100,-100,1,4).get_shared_channel(3).fill(255);
break;
case 2: // from GRAYA
img.resize(-100,-100,1,4,0);
img.get_shared_channel(3) = img.get_shared_channel(1);
img.get_shared_channel(1) = img.get_shared_channel(0);
img.get_shared_channel(2) = img.get_shared_channel(0);
break;
case 3: // from RGB
img.resize(-100,-100,1,4,0).get_shared_channel(3).fill(255);
break;
case 4: // from RGBA
break;
default: // from multi-channel (>4)
img.channels(0,3);
} break;
}
}
// Get the input layers of a GIMP image as a list of CImg<T>.
//-----------------------------------------------------------
template<typename T>
CImg<int> get_input_layers(CImgList<T>& images) {
// Retrieve the list of desired layers.
int
nb_layers = 0,
*layers = gimp_image_get_layers(image_id,&nb_layers),
active_layer_id = gimp_image_get_active_layer(image_id);
CImg<int> input_layers;
if (gimp_item_is_group(active_layer_id)) { images.assign(); return input_layers; }
const unsigned int input_mode = get_input_mode();
switch (input_mode) {
case 0 : // Input none
break;
case 1 : // Input active layer
if (active_layer_id>=0) input_layers = CImg<int>::vector(active_layer_id);
break;
case 2 : case 9 : // Input all image layers
input_layers = CImg<int>(layers,1,nb_layers);
if (input_mode==9) input_layers.mirror('y');
break;
case 3 : // Input active & below layers
if (active_layer_id>=0) {
int i = 0; for (i = 0; i<nb_layers; ++i) if (layers[i]==active_layer_id) break;
if (i<nb_layers - 1) input_layers = CImg<int>::vector(active_layer_id,layers[i + 1]);
else input_layers = CImg<int>::vector(active_layer_id);
} break;
case 4 : // Input active & above layers
if (active_layer_id>=0) {
int i = 0; for (i = 0; i<nb_layers; ++i) if (layers[i]==active_layer_id) break;
if (i>0) input_layers = CImg<int>::vector(active_layer_id,layers[i - 1]);
else input_layers = CImg<int>::vector(active_layer_id);
} break;
case 5 : case 7 : { // Input all visible image layers
CImgList<int> visible_layers;
for (int i = 0; i<nb_layers; ++i)
if (_gimp_item_get_visible(layers[i])) CImg<int>::vector(layers[i]).move_to(visible_layers);
input_layers = visible_layers>'y';
if (input_mode==7) input_layers.mirror('y');
} break;
default : { // Input all invisible image layers
CImgList<int> invisible_layers;
for (int i = 0; i<nb_layers; ++i)
if (!_gimp_item_get_visible(layers[i])) CImg<int>::vector(layers[i]).move_to(invisible_layers);
input_layers = invisible_layers>'y';
if (input_mode==8) input_layers.mirror('y');
} break;
}
// Read input image data into a CImgList<T>.
images.assign(input_layers.height());
gint rgn_x, rgn_y, rgn_width, rgn_height;
cimglist_for(images,l) {
if (!_gimp_item_is_valid(input_layers[l])) continue;
if (!gimp_drawable_mask_intersect(input_layers[l],&rgn_x,&rgn_y,&rgn_width,&rgn_height)) continue;
const int spectrum = (gimp_drawable_is_rgb(input_layers[l])?3:1) +
(gimp_drawable_has_alpha(input_layers[l])?1:0);
#if GIMP_MINOR_VERSION<=8
GimpDrawable *drawable = gimp_drawable_get(input_layers[l]);
GimpPixelRgn region;
gimp_pixel_rgn_init(&region,drawable,rgn_x,rgn_y,rgn_width,rgn_height,false,false);
guchar *const row = g_new(guchar,rgn_width*spectrum), *ptrs = 0;
CImg<T> img(rgn_width,rgn_height,1,spectrum);
switch (spectrum) {
case 1 : {
T *ptr_r = img.data(0,0,0,0);
cimg_forY(img,y) {
gimp_pixel_rgn_get_row(&region,ptrs=row,rgn_x,rgn_y + y,rgn_width);
cimg_forX(img,x) *(ptr_r++) = (T)*(ptrs++);
}
} break;
case 2 : {
T *ptr_r = img.data(0,0,0,0), *ptr_g = img.data(0,0,0,1);
cimg_forY(img,y) {
gimp_pixel_rgn_get_row(&region,ptrs=row,rgn_x,rgn_y + y,rgn_width);
cimg_forX(img,x) {
*(ptr_r++) = (T)*(ptrs++);
*(ptr_g++) = (T)*(ptrs++);
}
}
} break;
case 3 : {
T *ptr_r = img.data(0,0,0,0), *ptr_g = img.data(0,0,0,1), *ptr_b = img.data(0,0,0,2);
cimg_forY(img,y) {
gimp_pixel_rgn_get_row(&region,ptrs=row,rgn_x,rgn_y + y,rgn_width);
cimg_forX(img,x) {
*(ptr_r++) = (T)*(ptrs++);
*(ptr_g++) = (T)*(ptrs++);
*(ptr_b++) = (T)*(ptrs++);
}
}
} break;
case 4 : {
T *ptr_r = img.data(0,0,0,0), *ptr_g = img.data(0,0,0,1),
*ptr_b = img.data(0,0,0,2), *ptr_a = img.data(0,0,0,3);
cimg_forY(img,y) {
gimp_pixel_rgn_get_row(&region,ptrs=row,rgn_x,rgn_y + y,rgn_width);
cimg_forX(img,x) {
*(ptr_r++) = (T)*(ptrs++);
*(ptr_g++) = (T)*(ptrs++);
*(ptr_b++) = (T)*(ptrs++);
*(ptr_a++) = (T)*(ptrs++);
}
}
} break;
}
g_free(row);
gimp_drawable_detach(drawable);
#else
GeglRectangle rect;
gegl_rectangle_set(&rect,rgn_x,rgn_y,rgn_width,rgn_height);
GeglBuffer *buffer = gimp_drawable_get_buffer(input_layers[l]);
const char *const format = spectrum==1?"Y' " s_gmic_pixel_type:spectrum==2?"Y'A " s_gmic_pixel_type:
spectrum==3?"R'G'B' " s_gmic_pixel_type:"R'G'B'A " s_gmic_pixel_type;
CImg<float> img(spectrum,rgn_width,rgn_height);
gegl_buffer_get(buffer,&rect,1,babl_format(format),img.data(),0,GEGL_ABYSS_NONE);
// GimpColorTransform transform = gimp_color_transform_new(gimp_image_get_color_profile(image),
// gimp_drawable_get_format(drawable),gimp_color_profile_new_srgb(),babl_format("R'G'B'A float"),intent,flags);
// if (transform==NULL) gegl_buffer_copy()...
// else gimp_color_transform_process_buffer(transform, gimp_drawable_get_buffer (drawable), rect1, tmp_buffer, rect2);
(img*=255).permute_axes("yzcx");
g_object_unref(buffer);
#endif
img.move_to(images[l]);
}
return input_layers;
}
// Return the G'MIC command line needed to run the selected filter.
//-----------------------------------------------------------------
CImg<char> get_command_line(const bool is_preview) {
CImg<char> res;
const unsigned int
filter = get_current_filter(),
nbparams = get_filter_nbparams(filter);
if (!filter) return res;
CImgList<char> lres;
switch (get_verbosity_mode()) {
case 0: case 1: case 2: case 3: CImg<char>("-v -99 -",8).move_to(lres); break; // Quiet or Verbose.
case 4: case 5 : CImg<char>("-v 0 -",6).move_to(lres); break; // Very verbose.
default: CImg<char>("-debug -",8).move_to(lres); // Debug.
}
const CImg<char> &command_item = (is_preview?gmic_preview_commands[filter]:gmic_commands[filter]);
if (command_item) {
lres.insert(command_item);
if (nbparams) {
lres[1].back() = ' ';
for (unsigned int p = 0; p<nbparams; ++p) {
const CImg<char> _ss = get_filter_parameter(filter,p);
const char *ss = _ss;
const unsigned int l = (unsigned int)std::strlen(ss);
CImg<char> nparam(l + 1);
*nparam = 0;
char *sd = nparam.data();
if (l>=2 && *ss=='\"' && ss[l - 1]=='\"') { // Replace special characters in a string or a filename.
++ss; *(sd++) = '\"';
for (unsigned int i = 1; i<l - 1; ++i, ++ss) { const char c = *ss; *(sd++) = c=='\"'?gmic_dquote:c; }
*(sd++) = '\"'; *(sd++) = 0;
nparam.move_to(lres);
} else CImg<char>(ss,l + 1).move_to(lres);
lres.back().back() = ',';
}
}
(res=lres>'x').back() = 0;
}
return res;
}
// Set defaut zoom factor for preview of the current filter.
//----------------------------------------------------------
void set_preview_factor() {
const unsigned int filter = get_current_filter();
if (filter && gmic_preview_factors[filter] && GIMP_IS_PREVIEW(gui_preview)) {
double factor = gmic_preview_factors(filter,0);
if (factor>=0) {
if (!factor) { // Compute factor so that 1:1 preview of the image is displayed.
int _pw = 0, _ph = 0;
gimp_preview_get_size(GIMP_PREVIEW(gui_preview),&_pw,&_ph);
#if GIMP_MINOR_VERSION<=8
const float
pw = (float)_pw,
ph = (float)_ph,
dw = (float)gimp_zoom_preview_get_drawable(GIMP_ZOOM_PREVIEW(gui_preview))->width,
dh = (float)gimp_zoom_preview_get_drawable(GIMP_ZOOM_PREVIEW(gui_preview))->height;
#else
const int preview_drawable_id = gimp_zoom_preview_get_drawable_id(GIMP_ZOOM_PREVIEW(gui_preview));
const float
pw = (float)_pw,
ph = (float)_ph,
dw = (float)gimp_drawable_width(preview_drawable_id),
dh = (float)gimp_drawable_height(preview_drawable_id);
#endif
factor = std::sqrt((dw*dw + dh*dh)/(pw*pw + ph*ph));
}
gimp_zoom_model_zoom(gimp_zoom_preview_get_model(GIMP_ZOOM_PREVIEW(gui_preview)),GIMP_ZOOM_TO,factor);
}
}
}
// Process image data with the G'MIC interpreter.
//-----------------------------------------------
// This structure stores the arguments required by the processing thread.
struct st_process_thread {
CImgList<gmic_pixel_type> images;
CImgList<char> images_names;
CImg<char> env, error_message, status;
bool is_thread, is_preview;
unsigned int verbosity_mode;
const char *command_line;
float progress;
bool is_abort;
pthread_mutex_t is_running, wait_lock;
pthread_cond_t wait_cond;
pthread_t thread;
void set_env() { // Must be called from main thread to avoid crash when doing 'gimp_get_data()'.
env.assign(256);
cimg_snprintf(env,env.width(),
"-v - _input_layers=%u _output_mode=%u _output_messages=%u _preview_mode=%u _preview_size=%u",
get_input_mode(),get_output_mode(),get_verbosity_mode(),get_preview_mode(),get_preview_size());
}
};
// Thread that runs the G'MIC interpreter.
void *process_thread(void *arg) {
st_process_thread &spt = *(st_process_thread*)arg;
if (spt.is_thread) {
pthread_mutex_lock(&spt.is_running);
pthread_mutex_lock(&spt.wait_lock);
pthread_cond_signal(&spt.wait_cond);
pthread_mutex_unlock(&spt.wait_lock);
}
try {
if (spt.verbosity_mode>1) {
CImg<char> cl = CImg<char>::string(spt.command_line);
std::fprintf(cimg::output(),
"\n[gmic_gimp]./%s/ %s\n",
spt.is_preview?"preview":"apply",
cl.data());
std::fflush(cimg::output());
}
gmic gmic_instance(spt.env,gmic_additional_commands,true);
gmic_instance.run(spt.command_line,spt.images,spt.images_names,&spt.progress,&spt.is_abort);
gmic_instance.status.move_to(spt.status);
} catch (gmic_exception &e) {
spt.images.assign();
spt.images_names.assign();
CImg<char>::string(e.what()).move_to(spt.error_message);
if (spt.verbosity_mode>1) {
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ %s\n",
spt.error_message.data());
std::fflush(cimg::output());
}
}
if (spt.is_thread) {
pthread_mutex_unlock(&spt.is_running);
pthread_exit(0);
}
return 0;
}
// Handle GUI event functions.
//----------------------------
void create_parameters_gui(const bool);
void process_image(const char *const, const bool is_apply);
void process_preview();
void on_preview_button_changed(GtkToggleButton *const toggle_button) {
cimg::mutex(25);
if (p_spt) { st_process_thread &spt = *(st_process_thread*)p_spt; spt.is_abort = true; }
cimg::mutex(25,0);
if (!gtk_toggle_button_get_active(toggle_button)) gtk_widget_hide(gui_preview_warning);
}
void on_dialog_reset_zoom_button_clicked(GtkCheckButton *const) {
set_preview_factor();
}
// Secure function for invalidate preview.
void _gimp_preview_invalidate() {
cimg::mutex(25);
if (p_spt) { st_process_thread &spt = *(st_process_thread*)p_spt; spt.is_abort = true; }
cimg::mutex(25,0);
const int active_layer_id = gimp_image_get_active_layer(image_id);
if (active_layer_id>=0 && gimp_layer_get_edit_mask(active_layer_id))
gimp_layer_set_edit_mask(active_layer_id,(gboolean)0);
computed_preview.assign();
#if GIMP_MINOR_VERSION<=8
const bool is_valid_preview_drawable = gui_preview && GIMP_IS_PREVIEW(gui_preview) &&
_gimp_item_is_valid(gimp_zoom_preview_get_drawable(GIMP_ZOOM_PREVIEW(gui_preview))->drawable_id);
#else
const bool is_valid_preview_drawable = gui_preview && GIMP_IS_PREVIEW(gui_preview) &&
gimp_item_is_valid(gimp_zoom_preview_get_drawable_id(GIMP_ZOOM_PREVIEW(gui_preview)));
#endif
if (is_valid_preview_drawable) gimp_preview_invalidate(GIMP_PREVIEW(gui_preview));
else {
if (GTK_IS_WIDGET(gui_preview)) gtk_widget_destroy(gui_preview);
const int w = gimp_image_width(image_id), h = gimp_image_height(image_id);
if (preview_image_id) gimp_image_delete(preview_image_id);
preview_image_id = 0; preview_image_factor = 1;
// Pre-compute image thumbnail for preview if image is too small.
const int min_preview_size = (200 + 120*get_preview_size(true))*2/3;
if (cimg::max(w,h)<min_preview_size) {
int pw = 0, ph = 0;
if (w>=h) ph = cimg::max(1,h*(pw=min_preview_size)/w);
else pw = cimg::max(1,w*(ph=min_preview_size)/h);
preview_image_id = gimp_image_duplicate(image_id);
preview_image_factor = (double)cimg::max(pw,ph)/cimg::max(w,h);
const GimpInterpolationType mode = gimp_context_get_interpolation();
gimp_context_set_interpolation(GIMP_INTERPOLATION_NONE);
gimp_image_scale(preview_image_id,pw,ph);
gimp_context_set_interpolation(mode);
}
#if GIMP_MINOR_VERSION<=8
GimpDrawable *const preview_drawable =
gimp_drawable_get(gimp_image_get_active_drawable(preview_image_id?preview_image_id:image_id));
gui_preview = gimp_zoom_preview_new(preview_drawable);
#else
const int preview_drawable_id = gimp_image_get_active_drawable(preview_image_id?preview_image_id:image_id);
gui_preview = gimp_zoom_preview_new_from_drawable_id(preview_drawable_id);
#endif
GtkWidget *const controls = gimp_preview_get_controls(GIMP_PREVIEW(gui_preview));
GList *const children1 = ((GtkBox*)controls)->children;
GtkBoxChild *const child1 = (GtkBoxChild*)children1->data;
GtkWidget *const preview_button = child1->widget;
g_signal_connect(preview_button,"toggled",G_CALLBACK(on_preview_button_changed),0);
// Add 'reset zoom' button.
GList *const children2 = children1->next;
GtkBoxChild *const child2 = (GtkBoxChild*)children2->data;
GtkWidget
*const zoom_buttons = child2->widget,
*const reset_zoom_button = gtk_button_new();
reset_zoom_stock = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
GtkWidget *const reset_zoom_image = gtk_button_get_image(GTK_BUTTON(reset_zoom_stock));
gtk_button_set_image(GTK_BUTTON(reset_zoom_button),reset_zoom_image);
gtk_widget_show(reset_zoom_button);
gtk_box_pack_start(GTK_BOX(zoom_buttons),reset_zoom_button,false,false,0);
gtk_widget_set_tooltip_text(reset_zoom_button,t("Reset zoom"));
g_signal_connect(reset_zoom_button,"clicked",G_CALLBACK(on_dialog_reset_zoom_button_clicked),0);
gtk_widget_show(gui_preview);
gtk_container_add(GTK_CONTAINER(gui_preview_align),gui_preview);
GtkRequisition requisition;
gtk_widget_size_request(gui_preview_align,&requisition);
gtk_widget_set_size_request(gui_preview_align,
-1,requisition.height<min_preview_size?min_preview_size:-1);
g_signal_connect(gui_preview,"invalidated",G_CALLBACK(process_preview),0);
}
}
// Resize preview widget.
void resize_preview(const unsigned int size) {
CImg<char> tmp(256);
cimg_sprintf(tmp,
"style \"gimp-large-preview\"\n"
"{\n"
" GimpPreview::size = %u\n"
"}\n"
"class \"GimpPreview\" style \"gimp-large-preview\"",
200 + size*120);
gtk_rc_parse_string(tmp);
if (GIMP_IS_PREVIEW(gui_preview)) {
gtk_widget_destroy(gui_preview);
gui_preview = 0;
_gimp_preview_invalidate();
}
}
// Reset value of all button parameters for current filter.
void reset_button_parameters() {
const unsigned int filter = get_current_filter();
cimglist_for(gmic_button_parameters,l) set_filter_parameter(filter,gmic_button_parameters(l,0),"0");
}
// Handle preview resize event.
void on_dialog_resized() {
reset_button_parameters();
static int opw = 0, oph = 0;
int pw = 0, ph = 0;
if (GIMP_IS_PREVIEW(gui_preview)) {
gimp_preview_get_size(GIMP_PREVIEW(gui_preview),&pw,&ph);
if (!opw || !oph) { opw = pw; oph = ph; computed_preview.assign(); } else {
if (pw!=opw || ph!=oph) { set_preview_factor(); opw = pw; oph = ph; computed_preview.assign(); }
}
}
}
// Handle widgets events related to parameter changes.
void on_float_parameter_changed(GtkAdjustment *const adjustment, const void *const event_infos) {
reset_button_parameters();
double value = 0;
gimp_double_adjustment_update(adjustment,&value);
CImg<char> s_value(32);
cimg_snprintf(s_value,s_value.width(),"%g",value);
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_value);
_create_dialog_gui = true;
}
void on_int_parameter_changed(GtkAdjustment *const adjustment, const void *const event_infos) {
reset_button_parameters();
int value = 0;
gimp_int_adjustment_update(adjustment,&value);
CImg<char> s_value(32);
cimg_snprintf(s_value,s_value.width(),"%d",value);
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_value);
_create_dialog_gui = true;
}
void on_bool_parameter_changed(GtkCheckButton *const checkbutton, const void *const event_infos) {
reset_button_parameters();
int value = 0;
g_object_get(checkbutton,"active",&value,NULL);
CImg<char> s_value(4);
cimg_snprintf(s_value,s_value.width(),"%d",value?1:0);
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_value);
_create_dialog_gui = true;
}
void on_button_parameter_clicked(GtkCheckButton *const button, const void *const event_infos) {
reset_button_parameters();
cimg::unused(button);
const unsigned int filter = get_current_filter();
set_filter_parameter(filter,*(int*)event_infos,"1");
_create_dialog_gui = true;
}
void on_list_parameter_changed(GtkComboBox *const combobox, const void *const event_infos) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
CImg<char> s_value(32);
cimg_snprintf(s_value,s_value.width(),"%d",value);
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_value);
_create_dialog_gui = true;
}
void on_text_parameter_changed(const void *const event_infos) {
reset_button_parameters();
GtkWidget *entry = *((GtkWidget**)event_infos + 1);
const char *const s_value = gtk_entry_get_text(GTK_ENTRY(entry));
CImg<char> s_param;
if (s_value && *s_value) {
CImg<char> _s_value = CImg<char>::string(s_value);
cimg_for(_s_value,ptr,char) if (*ptr=='\"') *ptr = gmic_dquote;
s_param.assign(_s_value.width() + 2);
cimg_snprintf(s_param,s_param.width(),"\"%s\"",_s_value.data());
} else std::strcpy(s_param.assign(3),"\"\"");
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_param);
_create_dialog_gui = true;
}
void on_multitext_parameter_changed(const void *const event_infos) {
reset_button_parameters();
GtkWidget *text_view = *((GtkWidget**)event_infos + 1);
GtkTextBuffer *const text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
GtkTextIter it_start, it_end;
gtk_text_buffer_get_bounds(text_buffer,&it_start,&it_end);
const char *const s_value = gtk_text_buffer_get_text(text_buffer,&it_start,&it_end,false);
CImg<char> s_param;
if (s_value && *s_value) {
CImg<char> _s_value = CImg<char>::string(s_value);
cimg_for(_s_value,ptr,char) if (*ptr=='\"') *ptr = gmic_dquote;
s_param.assign(_s_value.width() + 2);
cimg_snprintf(s_param,s_param.width(),"\"%s\"",_s_value.data());
} else std::strcpy(s_param.assign(3),"\"\"");
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_param);
_create_dialog_gui = true;
}
void on_file_parameter_changed(GtkFileChooser *const file_chooser, const void *const event_infos) {
reset_button_parameters();
const char
*const _s_value = gtk_file_chooser_get_filename(file_chooser),
*const s_value = _s_value?_s_value:"";
CImg<char> o_value = get_filter_parameter(get_current_filter(),*(int*)event_infos);
cimg::strpare(o_value,'\"',true,false);
const bool
is_same_file = !std::strcmp(gmic::basename(s_value),gmic::basename(o_value)),
is_silent_argument = (bool)*((void**)event_infos + 1);
CImg<char> s_param;
if (s_value && *s_value) {
s_param.assign(std::strlen(s_value) + 3);
cimg_snprintf(s_param,s_param.width(),"\"%s\"",s_value);
} else std::strcpy(s_param.assign(3),"\"\"");
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_param);
if (!is_same_file && !is_silent_argument)
_gimp_preview_invalidate(); // Invalidate preview only if filename has changed.
_create_dialog_gui = true;
}
void on_color_parameter_changed(GtkColorButton *const color_button, const void *const event_infos) {
reset_button_parameters();
GdkColor color;
gtk_color_button_get_color(color_button,&color);
CImg<char> s_value(256);
if (gtk_color_button_get_use_alpha(color_button))
cimg_snprintf(s_value,s_value.width(),"%d,%d,%d,%d",
color.red/257,color.green/257,color.blue/257,gtk_color_button_get_alpha(color_button)/257);
else
cimg_snprintf(s_value,s_value.width(),"%d,%d,%d",
color.red/257,color.green/257,color.blue/257);
set_filter_parameter(get_current_filter(),*(int*)event_infos,s_value);
_create_dialog_gui = true;
}
// Handle responses to the dialog window buttons.
void on_dialog_input_mode_changed(GtkComboBox *const combobox) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
if (value<2) gtk_combo_box_set_active(combobox,value=3);
set_input_mode((unsigned int)value);
_gimp_preview_invalidate();
}
void on_dialog_output_mode_changed(GtkComboBox *const combobox) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
if (value<2) gtk_combo_box_set_active(combobox,value=2);
set_output_mode((unsigned int)value);
}
void on_dialog_verbosity_mode_changed(GtkComboBox *const combobox) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
if (value<2) gtk_combo_box_set_active(combobox,value=2);
set_verbosity_mode((unsigned int)value);
set_logfile();
if (value>3) _gimp_preview_invalidate();
}
void on_dialog_preview_mode_changed(GtkComboBox *const combobox) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
if (value<2) gtk_combo_box_set_active(combobox,value=2);
set_preview_mode((unsigned int)value);
_gimp_preview_invalidate();
}
void on_dialog_preview_size_changed(GtkComboBox *const combobox) {
reset_button_parameters();
int value = 0;
g_object_get(combobox,"active",&value,NULL);
if (value<2) gtk_combo_box_set_active(combobox,value=2);
set_preview_size((unsigned int)value);
resize_preview(value - 2);
}
void on_dialog_maximize_button_clicked(GtkButton *const button) {
reset_button_parameters();
static int ow = 0, oh = 0;
GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(dialog_window));
int
width = gdk_screen_get_width(screen),
height = gdk_screen_get_height(screen);
if (width>0 && height>0 && !ow && !oh) {
gtk_window_get_size(GTK_WINDOW(dialog_window),&ow,&oh);
#if cimg_OS==1
height-=64;
#elif cimg_OS==2
// Subtract the height of the taskbar on windows.
RECT rect;
HWND taskBar = FindWindow("Shell_traywnd",NULL);
if (taskBar && GetWindowRect(taskBar,&rect)) height-=rect.bottom - rect.top;
#endif
if (height>0) gtk_window_resize(GTK_WINDOW(dialog_window),width,height);
gtk_window_move(GTK_WINDOW(dialog_window),0,0);
gtk_button_set_label(button,GTK_STOCK_LEAVE_FULLSCREEN);
} else if (ow>0 && oh>0) {
gtk_window_resize(GTK_WINDOW(dialog_window),ow,oh);
ow = oh = 0;
gtk_button_set_label(button,GTK_STOCK_FULLSCREEN);
}
_gimp_preview_invalidate();
}
void on_dialog_reset_clicked() {
create_parameters_gui(true);
_create_dialog_gui = true;
_gimp_preview_invalidate();
}
void on_dialog_cancel_clicked() {
cimg::mutex(25);
if (p_spt) { st_process_thread &spt = *(st_process_thread*)p_spt; spt.is_abort = true; }
cimg::mutex(25,0);
reset_button_parameters();
_create_dialog_gui = false;
gtk_main_quit();
}
void on_dialog_apply_clicked() {
gtk_widget_hide(dialog_window);
while (gtk_events_pending()) gtk_main_iteration();
process_image(0,true);
gtk_widget_show(dialog_window);
_create_dialog_gui = false;
_gimp_preview_invalidate();
const CImg<char> command_line = get_command_line(false);
if (command_line) { // Remember command line for the next use of the filter.
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_command_line%u",get_current_filter());
gimp_set_data(s_tmp,command_line,std::strlen(command_line) + 1);
}
}
void on_dialog_net_update_toggled(GtkToggleButton *const toggle_button) {
reset_button_parameters();
set_net_update(gtk_toggle_button_get_active(toggle_button));
}
void on_dialog_tree_mode_clicked(GtkWidget *const tree_view) {
reset_button_parameters();
set_tree_mode(!get_tree_mode());
flush_tree_view(tree_view);
}
void on_dialog_add_fave_clicked(GtkWidget *const tree_view) {
reset_button_parameters();
const unsigned int filter = get_current_filter();
gtk_widget_hide(relabel_hbox);
gtk_widget_hide(fave_delete_button);
if (filter) {
CImg<char> filename(1024);
cimg_snprintf(filename,filename.width(),"%sgimp_faves",
gmic::path_rc());
std::FILE *file = std::fopen(filename,"wb");
if (file) {
CImg<char> basename(256), label(256);
*basename = *label = 0;
unsigned int ind = 0;
std::strcpy(basename,gmic_entries[filter].data());
char *last_space = 0;
for (char *p = basename; p; p = std::strchr(p + 1,' ')) last_space = p;
if (last_space>basename) {
char sep = 0, end = 0;
if (cimg_sscanf(last_space + 1,"(%u%c%c",&ind,&sep,&end)==2 && sep==')') *last_space = 0;
}
std::strcpy(label,basename);
cimglist_for(gmic_faves,l) {
std::fprintf(file,"%s\n",gmic_faves[l].data());
if (!std::strcmp(label,gmic_entries[indice_faves + l].data()))
cimg_sprintf(label,"%s (%u)",basename.data(),++ind);
}
CImg<char> entry = gmic_entries[filter];
for (char *p = std::strchr(label,'}'); p; p = std::strchr(p,'}')) *p = gmic_rbrace; // Convert '}' if necessary.
for (char *p = std::strchr(entry,'}'); p; p = std::strchr(p,'}')) *p = gmic_rbrace;
std::fprintf(file,"{%s}{%s}{%s}{%s}",
label.data(),
entry.data(),
gmic_commands[filter].data(),
gmic_preview_commands[filter].data());
const unsigned int nbp = get_filter_nbparams(filter);
for (unsigned int n = 0; n<nbp; ++n) {
CImg<char> param = get_filter_parameter(filter,n);
set_filter_parameter(gmic_entries.size(),n,param);
for (char *p = std::strchr(param,'}'); p; p = std::strchr(p,'}')) *p = gmic_rbrace; // Convert '}' if necessary.
for (char *p = std::strchr(param,'\n'); p; p = std::strchr(p,'\n')) // Convert '\n' if necessary.
*p = gmic_newline;
std::fprintf(file,"{%s}",param.data());
}
std::fputc('\n',file);
std::fclose(file);
update_filters(false);
} else if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ Unable to write fave file '%s'.\n",
filename.data());
}
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view),GTK_TREE_MODEL(tree_view_store));
gimp_set_data("gmic_current_treepath","0",2);
set_current_filter(0);
flush_tree_view(tree_view);
GtkTreePath *path = gtk_tree_path_new_from_string("0");
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree_view),path,NULL,FALSE,0,0);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_select_path(selection,path);
gtk_tree_path_free(path);
create_parameters_gui(false);
_gimp_preview_invalidate();
}
void on_dialog_remove_fave_clicked(GtkWidget *const tree_view) {
reset_button_parameters();
const unsigned int filter = get_current_filter();
gtk_widget_hide(relabel_hbox);
gtk_widget_hide(fave_delete_button);
if (filter) {
CImg<char> filename(1024);
cimg_snprintf(filename,filename.width(),"%sgimp_faves",
gmic::path_rc());
std::FILE *file = std::fopen(filename,"wb");
if (file) {
const unsigned int _filter = filter - indice_faves;
if (gmic_faves.size()==1) { std::fclose(file); std::remove(filename); }
else {
cimglist_for(gmic_faves,l) if (l!=(int)_filter) std::fprintf(file,"%s\n",gmic_faves[l].data());
std::fclose(file);
for (unsigned int i = filter; i<gmic_entries.size() - 1; ++i) { // Shift current parameters.
const unsigned int nbp = get_filter_nbparams(i + 1);
for (unsigned int n = 0; n<nbp; ++n)
set_filter_parameter(i,n,get_filter_parameter(i + 1,n));
}
}
update_filters(false);
} else if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ Unable to write fave file '%s'.\n",
filename.data());
}
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view),GTK_TREE_MODEL(tree_view_store));
gimp_set_data("gmic_current_treepath","0",2);
set_current_filter(0);
flush_tree_view(tree_view);
GtkTreePath *path = gtk_tree_path_new_from_string("0");
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree_view),path,NULL,FALSE,0,0);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_select_path(selection,path);
gtk_tree_path_free(path);
create_parameters_gui(false);
_gimp_preview_invalidate();
}
void on_dialog_refresh_clicked(GtkWidget *const tree_view) {
reset_button_parameters();
gtk_widget_hide(relabel_hbox);
gtk_widget_hide(fave_delete_button);
const unsigned int old_nb_filters = get_nbfilters();
CImgList<char> invalid_servers = update_filters(get_net_update());
if (invalid_servers) {
if (get_verbosity_mode()>1) cimglist_for(invalid_servers,l) {
std::fprintf(cimg::output(),
"\n[gmic_gimp]./update/ Unable to reach filters source '%s'.\n",
invalid_servers[l].data());
std::fflush(cimg::output());
}
CImg<char>::string(t(0)).move_to(invalid_servers,0);
cimglist_for(invalid_servers,l) {
CImg<char>& server = invalid_servers[l];
if (l) {
server.resize(2 + server.width(),1,1,1,0,0,1);
server[0] = '*';
server[1] = ' ';
}
if (l!=invalid_servers.width() - 1) server.back() = '\n';
}
const CImg<char> error_message = invalid_servers>'x';
GtkWidget *const
message = gtk_message_dialog_new_with_markup(0,GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,
"%s",error_message.data());
gtk_widget_show(message);
gtk_dialog_run(GTK_DIALOG(message));
gtk_widget_destroy(message);
}
const bool reset_params = gmic_entries.size()!=old_nb_filters;
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view),GTK_TREE_MODEL(tree_view_store));
flush_tree_view(tree_view);
create_parameters_gui(reset_params);
_gimp_preview_invalidate();
}
void on_filter_selected(GtkWidget *const tree_view) {
reset_button_parameters();
GtkTreeSelection *const selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
GtkTreeIter iter;
GtkTreeModel *model;
unsigned int filter = 0;
if (gtk_tree_selection_get_selected(selection,&model,&iter)) {
gtk_tree_model_get(model,&iter,0,&filter,-1);
const char *const treepath = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(tree_view_store),&iter);
gimp_set_data("gmic_current_treepath",treepath,std::strlen(treepath) + 1);
}
if (filter!=get_current_filter()) {
gtk_widget_hide(relabel_hbox);
gtk_widget_hide(fave_delete_button);
set_current_filter(filter);
create_parameters_gui(false);
_create_dialog_gui = true;
latest_preview_buffer.assign();
_gimp_preview_invalidate();
}
}
void _on_filter_doubleclicked(GtkWidget *const entry) {
reset_button_parameters();
const unsigned int filter = get_current_filter();
cimg::unused(entry);
gtk_widget_hide(relabel_hbox);
gtk_widget_hide(fave_delete_button);
if (filter>=indice_faves) {
const char *const __label = gtk_entry_get_text(GTK_ENTRY(relabel_entry));
char *const label = g_markup_escape_text(__label,std::strlen(__label));
if (*label) {
CImg<char> filename(1024);
cimg_snprintf(filename,filename.width(),"%sgimp_faves",
gmic::path_rc());
std::FILE *file = std::fopen(filename,"wb");
if (file) {
const unsigned int _filter = filter - indice_faves;
cimglist_for(gmic_faves,l) if (l!=(int)_filter) std::fprintf(file,"%s\n",gmic_faves[l].data());
else {
CImg<char> _label = CImg<char>::string(label);
for (char *p = std::strchr(_label,'}'); p; p = std::strchr(p,'}')) // Convert '}' if necessary.
*p = gmic_rbrace;
std::fprintf(file,"{%s%s\n",_label.data(),std::strchr(gmic_faves[l].data(),'}'));
}
std::fclose(file);
update_filters(false);
} else if (get_verbosity_mode()>1)
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ Unable to write fave file '%s'.\n",
filename.data());
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view),GTK_TREE_MODEL(tree_view_store));
gimp_set_data("gmic_current_treepath","0",2);
set_current_filter(0);
flush_tree_view(tree_view);
GtkTreePath *path = gtk_tree_path_new_from_string("0");
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree_view),path,NULL,FALSE,0,0);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_select_path(selection,path);
gtk_tree_path_free(path);
create_parameters_gui(false);
}
g_free(label);
}
}
void on_filter_doubleclicked(GtkWidget *const tree_view) {
reset_button_parameters();
const unsigned int filter = get_current_filter();
if (!filter) { // Expand/collapse folder.
CImg<char> current_path(64); *current_path = 0;
gimp_get_data("gmic_current_treepath",current_path);
if (*current_path) {
GtkTreePath *path = gtk_tree_path_new_from_string(current_path);
const bool is_expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree_view),path);
if (is_expanded) gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree_view),path);
else gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view),path,false);
gtk_tree_path_free(path);
}
} else if (filter>=indice_faves) { // Rename fave filter.
is_block_preview = true;
gtk_label_set_markup(GTK_LABEL(markup2ascii),gmic_entries[filter].data());
gtk_entry_set_text(GTK_ENTRY(relabel_entry),gtk_label_get_text(GTK_LABEL(markup2ascii)));
gtk_widget_show(relabel_hbox);
gtk_widget_grab_focus(relabel_entry);
}
}
// Process the selected image/layers.
//------------------------------------
void process_image(const char *const command_line, const bool is_apply) {
if (!gimp_image_is_valid(image_id)) return;
const unsigned int
filter = get_current_filter(),
_output_mode = get_output_mode(),
output_mode = get_input_mode()==0?cimg::max(1U,_output_mode):_output_mode,
verbosity_mode = get_verbosity_mode();
if (!command_line && !filter) return;
bool update_parameters = false;
const CImg<char> _command_line = command_line?CImg<char>::string(command_line):get_command_line(false);
if (!_command_line || std::strstr(_command_line," -_none_")) return;
CImg<char> new_label(256), progress_label;
*new_label = 0;
if (run_mode!=GIMP_RUN_NONINTERACTIVE) {
gtk_label_set_markup(GTK_LABEL(markup2ascii),gmic_entries[filter].data());
CImg<char>::string(gtk_label_get_text(GTK_LABEL(markup2ascii))).move_to(progress_label);
gimp_progress_init_printf(" G'MIC: %s...",progress_label.data());
const char *const cl = _command_line.data() +
(!std::strncmp(_command_line,"-v -99 ",7)?7:
!std::strncmp(_command_line,"-v 0 ",5)?5:
!std::strncmp(_command_line,"-debug ",7)?7:0);
cimg_snprintf(new_label,new_label.width(),"[G'MIC] %s: %s",gtk_label_get_text(GTK_LABEL(markup2ascii)),cl);
cimg::strellipsize(new_label,240,false);
} else {
cimg_snprintf(new_label,new_label.width(),"G'MIC: %s...",_command_line.data());
cimg::strellipsize(new_label,240,false);
gimp_progress_init_printf("%s",new_label.data());
cimg_snprintf(new_label,new_label.width(),"[G'MIC]: %s",_command_line.data());
cimg::strellipsize(new_label,240,false);
}
// Get input layers for the chosen filter.
st_process_thread spt;
spt.is_preview = false;
spt.command_line = _command_line;
spt.verbosity_mode = get_verbosity_mode();
spt.images_names.assign();
spt.progress = -1;
spt.set_env();
const CImg<int> layers = get_input_layers(spt.images);
CImg<int> layer_dimensions(spt.images.size(),4);
CImg<char> layer_name(256);
int is_selection = 0, sel_x0 = 0, sel_y0 = 0, sel_x1 = 0, sel_y1 = 0;
if (!gimp_selection_bounds(image_id,&is_selection,&sel_x0,&sel_y0,&sel_x1,&sel_y1)) is_selection = 0;
else if (output_mode<1 || output_mode>2) sel_x0 = sel_y0 = 0;
cimglist_for(spt.images,p) {
const CImg<gmic_pixel_type>& img = spt.images[p];
layer_dimensions(p,0) = img.width(); layer_dimensions(p,1) = img.height();
layer_dimensions(p,2) = img.depth(); layer_dimensions(p,3) = img.spectrum();
const GimpLayerModeEffects blendmode = gimp_layer_get_mode(layers[p]);
const float opacity = gimp_layer_get_opacity(layers[p]);
int posx = 0, posy = 0;
if (is_selection && output_mode) {
if (output_mode>=1 && output_mode<=2) { posx = sel_x0; posy = sel_y0; }
} else gimp_drawable_offsets(layers[p],&posx,&posy);
CImg<char> _layer_name = CImg<char>::string(gimp_item_get_name(layers[p]));
cimg_for(_layer_name,pn,char) if (*pn=='(') *pn = 21; else if (*pn==')') *pn = 22;
cimg_snprintf(layer_name,layer_name.width(),"mode(%s),opacity(%g),pos(%d,%d),name(%s)",
s_blendmode[blendmode],opacity,posx,posy,
_layer_name.data());
CImg<char>::string(layer_name).move_to(spt.images_names);
}
// Get original image dimensions and number of layers.
int image_nb_layers = 0;
gimp_image_get_layers(image_id,&image_nb_layers);
unsigned int image_width = 0, image_height = 0;
if (layers.height()) {
image_width = gimp_image_width(image_id);
image_height = gimp_image_height(image_id);
}
// Reset values for button parameters.
cimglist_for(gmic_button_parameters,l) set_filter_parameter(filter,gmic_button_parameters(l,0),"0");
// Create processing thread and wait for its completion.
bool is_abort = spt.is_abort = false;
if (run_mode!=GIMP_RUN_NONINTERACTIVE) {
const cimg_ulong time0 = cimg::time();
cimg::mutex(25); p_spt = (void*)&spt; cimg::mutex(25,0);
spt.is_thread = true;
pthread_mutex_init(&spt.is_running,0);
pthread_mutex_init(&spt.wait_lock,0);
pthread_cond_init(&spt.wait_cond,0);
pthread_mutex_lock(&spt.wait_lock);
#if defined(__MACOSX__) || defined(__APPLE__)
cimg::unused(time0);
const cimg_ulong stacksize = (cimg_ulong)8*1024*1024;
pthread_attr_t thread_attr;
if (!pthread_attr_init(&thread_attr) && !pthread_attr_setstacksize(&thread_attr,stacksize))
pthread_create(&spt.thread,&thread_attr,process_thread,(void*)&spt);
else
#endif // #if defined(__MACOSX__) || defined(__APPLE__)
pthread_create(&spt.thread,0,process_thread,(void*)&spt);
pthread_cond_wait(&spt.wait_cond,&spt.wait_lock); // Wait for the thread to lock the mutex.
pthread_mutex_unlock(&spt.wait_lock);
pthread_mutex_destroy(&spt.wait_lock);
unsigned int i = 0, used_memory = 0;
cimg::unused(i,used_memory);
while (pthread_mutex_trylock(&spt.is_running)) {
if (spt.progress>=0) gimp_progress_update(cimg::min(1.0,spt.progress/100.0));
else gimp_progress_pulse();
cimg::wait(333);
#if !defined(__MACOSX__) && !defined(__APPLE__)
if (!(i%10)) { // Update memory usage.
used_memory = 0;
#if cimg_OS==2
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(),&pmc,sizeof(pmc)))
used_memory = (cimg_ulong)(pmc.WorkingSetSize/1024/1024);
#elif cimg_OS==1 // #if cimg_OS==2
CImg<char> st; st.load_raw("/proc/self/status",512); st.back() = 0;
const char *const s = std::strstr(st,"VmRSS:");
if (s && cimg_sscanf(s + 7,"%u",&used_memory)==1) used_memory/=1024;
#endif // #if cimg_OS==2
}
if (!(i%3)) {
const cimg_ulong t = (cimg::time() - time0)/1000;
if (used_memory)
gimp_progress_set_text_printf(spt.progress<0?
" G'MIC: %s... [%lu second%s, %u Mb]":
" G'MIC: %s... [%lu second%s, %u Mb, %d%%]",
progress_label.data(),
t,t!=1?"s":"",
used_memory,(int)spt.progress);
else
gimp_progress_set_text_printf(spt.progress<0?
" G'MIC: %s... [%lu second%s]":
" G'MIC: %s... [%lu second%s, %d%%]",
progress_label.data(),
t,t!=1?"s":"",(int)spt.progress);
}
++i;
#endif // #if !defined(__MACOSX__) && !defined(__APPLE__)
}
gimp_progress_update(1.0);
pthread_join(spt.thread,0);
pthread_mutex_unlock(&spt.is_running);
pthread_mutex_destroy(&spt.is_running);
is_abort = spt.is_abort;
cimg::mutex(25); p_spt = (void*)0; cimg::mutex(25,0);
} else {
spt.is_thread = false;
process_thread(&spt);
}
// Check that everything went fine, else display an error dialog.
if (spt.error_message) {
if (run_mode!=GIMP_RUN_NONINTERACTIVE) {
GtkWidget *const
message = gtk_message_dialog_new(0,GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,"%s",
spt.error_message.data());
gtk_widget_show(message);
gtk_dialog_run(GTK_DIALOG(message));
gtk_widget_destroy(message);
} else {
std::fprintf(cimg::output(),"\n[gmic_gimp]./error/ When running command '%s', this error occured:\n%s\n",
_command_line.data(),spt.error_message.data());
std::fflush(cimg::output());
}
status = GIMP_PDB_CALLING_ERROR;
} else if (!is_abort) {
// Get output layers dimensions and check if input/output layers have compatible dimensions.
unsigned int max_width = 0, max_height = 0, max_spectrum = 0;
cimglist_for(spt.images,l) {
if (spt.images[l].is_empty()) { spt.images.remove(l--); continue; } // Discard possible empty images.
if (spt.images[l]._width>max_width) max_width = spt.images[l]._width;
if (spt.images[l]._height>max_height) max_height = spt.images[l]._height;
if (spt.images[l]._spectrum>max_spectrum) max_spectrum = spt.images[l]._spectrum;
}
bool is_compatible_dimensions = (spt.images.size()==layers._height);
for (unsigned int p = 0; p<spt.images.size() && is_compatible_dimensions; ++p) {
const CImg<gmic_pixel_type>& img = spt.images[p];
const bool
source_is_alpha = (layer_dimensions(p,3)==2 || layer_dimensions(p,3)>=4),
dest_is_alpha = (img.spectrum()==2 || img.spectrum()>=4);
if (dest_is_alpha && !source_is_alpha) { gimp_layer_add_alpha(layers[p]); ++layer_dimensions(p,3); }
if (img.width()!=layer_dimensions(p,0) ||
img.height()!=layer_dimensions(p,1)) is_compatible_dimensions = false;
}
// Transfer the output layers back into GIMP.
GimpLayerModeEffects layer_blendmode = GIMP_NORMAL_MODE;
gint layer_posx = 0, layer_posy = 0;
double layer_opacity = 100;
CImg<char> layer_name;
switch (output_mode) {
case 0 : { // Output in 'Replace' mode.
gint rgn_x, rgn_y, rgn_width, rgn_height;
gimp_image_undo_group_start(image_id);
if (is_compatible_dimensions) cimglist_for(spt.images,p) { // Direct replacement of the layer data.
layer_blendmode = gimp_layer_get_mode(layers[p]);
layer_opacity = gimp_layer_get_opacity(layers[p]);
gimp_drawable_offsets(layers[p],&layer_posx,&layer_posy);
CImg<char>::string(gimp_item_get_name(layers[p])).move_to(layer_name);
get_output_layer_props(spt.images_names[p],layer_blendmode,layer_opacity,layer_posx,layer_posy,layer_name);
CImg<gmic_pixel_type> &img = spt.images[p];
calibrate_image(img,layer_dimensions(p,3),false);
if (gimp_drawable_mask_intersect(layers[p],&rgn_x,&rgn_y,&rgn_width,&rgn_height)) {
#if GIMP_MINOR_VERSION<=8
GimpDrawable *drawable = gimp_drawable_get(layers[p]);
GimpPixelRgn region;
gimp_pixel_rgn_init(&region,drawable,rgn_x,rgn_y,rgn_width,rgn_height,true,true);
convert_image2uchar(img);
gimp_pixel_rgn_set_rect(&region,(guchar*)img.data(),rgn_x,rgn_y,rgn_width,rgn_height);
gimp_drawable_flush(drawable);
gimp_drawable_merge_shadow(layers[p],true);
gimp_drawable_update(layers[p],rgn_x,rgn_y,rgn_width,rgn_height);
gimp_drawable_detach(drawable);
#else
GeglRectangle rect;
gegl_rectangle_set(&rect,rgn_x,rgn_y,rgn_width,rgn_height);
GeglBuffer *buffer = gimp_drawable_get_shadow_buffer(layers[p]);
const char *const format = img.spectrum()==1?"Y' float":img.spectrum()==2?"Y'A float":
img.spectrum()==3?"R'G'B' float":"R'G'B'A float";
(img/=255).permute_axes("cxyz");
gegl_buffer_set(buffer,&rect,0,babl_format(format),img.data(),0);
g_object_unref(buffer);
gimp_drawable_merge_shadow(layers[p],true);
gimp_drawable_update(layers[p],0,0,img.width(),img.height());
#endif
gimp_layer_set_mode(layers[p],layer_blendmode);
gimp_layer_set_opacity(layers[p],layer_opacity);
gimp_layer_set_offsets(layers[p],layer_posx,layer_posy);
if (verbosity_mode==1) gimp_item_set_name(layers[p],new_label);
else if (layer_name) gimp_item_set_name(layers[p],layer_name);
}
img.assign();
} else { // Indirect replacement: create new layers.
gimp_selection_none(image_id);
const int layer_pos = _gimp_image_get_item_position(image_id,layers[0]);
max_width = max_height = 0;
cimglist_for(spt.images,p) if (spt.images[p]) {
layer_posx = layer_posy = 0;
if (p<layers.height()) {
layer_blendmode = gimp_layer_get_mode(layers[p]);
layer_opacity = gimp_layer_get_opacity(layers[p]);
if (!is_selection) gimp_drawable_offsets(layers[p],&layer_posx,&layer_posy);
CImg<char>::string(gimp_item_get_name(layers[p])).move_to(layer_name);
gimp_image_remove_layer(image_id,layers[p]);
} else {
layer_blendmode = GIMP_NORMAL_MODE;
layer_opacity = 100;
layer_name.assign();
}
get_output_layer_props(spt.images_names[p],layer_blendmode,layer_opacity,layer_posx,layer_posy,layer_name);
if (layer_posx + spt.images[p]._width>max_width) max_width = layer_posx + spt.images[p]._width;
if (layer_posy + spt.images[p]._height>max_height) max_height = layer_posy + spt.images[p]._height;
CImg<gmic_pixel_type> &img = spt.images[p];
if (gimp_image_base_type(image_id)==GIMP_GRAY)
calibrate_image(img,(img.spectrum()==1 || img.spectrum()==3)?1:2,false);
else calibrate_image(img,(img.spectrum()==1 || img.spectrum()==3)?3:4,false);
gint layer_id = gimp_layer_new(image_id,new_label,img.width(),img.height(),
img.spectrum()==1?GIMP_GRAY_IMAGE:
img.spectrum()==2?GIMP_GRAYA_IMAGE:
img.spectrum()==3?GIMP_RGB_IMAGE:GIMP_RGBA_IMAGE,
layer_opacity,layer_blendmode);
gimp_layer_set_offsets(layer_id,layer_posx,layer_posy);
if (verbosity_mode==1) gimp_item_set_name(layer_id,new_label);
else if (layer_name) gimp_item_set_name(layer_id,layer_name);
gimp_image_insert_layer(image_id,layer_id,-1,layer_pos + p);
#if GIMP_MINOR_VERSION<=8
GimpDrawable *drawable = gimp_drawable_get(layer_id);
GimpPixelRgn region;
gimp_pixel_rgn_init(&region,drawable,0,0,drawable->width,drawable->height,true,true);
convert_image2uchar(img);
gimp_pixel_rgn_set_rect(&region,(guchar*)img.data(),0,0,img.width(),img.height());
gimp_drawable_flush(drawable);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,drawable->width,drawable->height);
gimp_drawable_detach(drawable);
#else
GeglBuffer *buffer = gimp_drawable_get_shadow_buffer(layer_id);
const char *const format = img.spectrum()==1?"Y' float":img.spectrum()==2?"Y'A float":
img.spectrum()==3?"R'G'B' float":"R'G'B'A float";
(img/=255).permute_axes("cxyz");
gegl_buffer_set(buffer,NULL,0,babl_format(format),img.data(),0);
g_object_unref(buffer);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,img.width(),img.height());
#endif
img.assign();
}
for (unsigned int p = spt.images._width; p<layers._height; ++p) gimp_image_remove_layer(image_id,layers[p]);
if (image_nb_layers==layers.height()) gimp_image_resize(image_id,max_width,max_height,0,0);
else gimp_image_resize(image_id,cimg::max(image_width,max_width),cimg::max(image_height,max_height),0,0);
}
gimp_image_undo_group_end(image_id);
} break;
case 1 : case 2 : { // Output in 'New [active] layer(s)' mode.
const gint active_layer_id = gimp_image_get_active_layer(image_id);
if (active_layer_id>=0) {
gimp_image_undo_group_start(image_id);
gint top_layer_id = 0, layer_id = 0;
max_width = max_height = 0;
cimglist_for(spt.images,p) if (spt.images[p]) {
layer_blendmode = GIMP_NORMAL_MODE;
layer_opacity = 100;
layer_posx = layer_posy = 0;
if (layers.height()==1) {
if (!is_selection) gimp_drawable_offsets(active_layer_id,&layer_posx,&layer_posy);
CImg<char>::string(gimp_item_get_name(active_layer_id)).move_to(layer_name);
} else layer_name.assign();
get_output_layer_props(spt.images_names[p],layer_blendmode,layer_opacity,layer_posx,layer_posy,layer_name);
if (layer_posx + spt.images[p]._width>max_width) max_width = layer_posx + spt.images[p]._width;
if (layer_posy + spt.images[p]._height>max_height) max_height = layer_posy + spt.images[p]._height;
CImg<gmic_pixel_type> &img = spt.images[p];
if (gimp_image_base_type(image_id)==GIMP_GRAY)
calibrate_image(img,!is_selection && (img.spectrum()==1 || img.spectrum()==3)?1:2,false);
else
calibrate_image(img,!is_selection && (img.spectrum()==1 || img.spectrum()==3)?3:4,false);
layer_id = gimp_layer_new(image_id,new_label,img.width(),img.height(),
img.spectrum()==1?GIMP_GRAY_IMAGE:
img.spectrum()==2?GIMP_GRAYA_IMAGE:
img.spectrum()==3?GIMP_RGB_IMAGE:GIMP_RGBA_IMAGE,
layer_opacity,layer_blendmode);
if (!p) top_layer_id = layer_id;
gimp_layer_set_offsets(layer_id,layer_posx,layer_posy);
if (verbosity_mode==1) gimp_item_set_name(layer_id,new_label);
else if (layer_name) gimp_item_set_name(layer_id,layer_name);
gimp_image_insert_layer(image_id,layer_id,-1,p);
#if GIMP_MINOR_VERSION<=8
GimpDrawable *drawable = gimp_drawable_get(layer_id);
GimpPixelRgn region;
gimp_pixel_rgn_init(&region,drawable,0,0,drawable->width,drawable->height,true,true);
convert_image2uchar(img);
gimp_pixel_rgn_set_rect(&region,(guchar*)img.data(),0,0,img.width(),img.height());
gimp_drawable_flush(drawable);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,drawable->width,drawable->height);
gimp_drawable_detach(drawable);
#else
GeglBuffer *buffer = gimp_drawable_get_shadow_buffer(layer_id);
const char *const format = img.spectrum()==1?"Y' float":img.spectrum()==2?"Y'A float":
img.spectrum()==3?"R'G'B' float":"R'G'B'A float";
(img/=255).permute_axes("cxyz");
gegl_buffer_set(buffer,NULL,0,babl_format(format),img.data(),0);
g_object_unref(buffer);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,img.width(),img.height());
#endif
img.assign();
}
const unsigned int Mw = cimg::max(image_width,max_width), Mh = cimg::max(image_height,max_height);
if (Mw && Mh) gimp_image_resize(image_id,Mw,Mh,0,0);
if (output_mode==1) gimp_image_set_active_layer(image_id,active_layer_id);
else { // Destroy preview widget will force the preview to get the new active layer as base image.
gimp_image_set_active_layer(image_id,top_layer_id);
if (is_apply) {
if (gui_preview && GTK_IS_WIDGET(gui_preview)) gtk_widget_destroy(gui_preview);
gui_preview = 0;
}
}
gimp_image_undo_group_end(image_id);
}
} break;
default : { // Output in 'New image' mode.
if (spt.images.size()) {
const gint active_layer_id = gimp_image_get_active_layer(image_id);
if (active_layer_id>=0) {
#if GIMP_MINOR_VERSION<=8
const int nimage_id = gimp_image_new(max_width,max_height,max_spectrum<=2?GIMP_GRAY:GIMP_RGB);
#else
const int nimage_id = gimp_image_new_with_precision(max_width,max_height,max_spectrum<=2?GIMP_GRAY:GIMP_RGB,
gimp_image_get_precision(image_id));
#endif
gimp_image_undo_group_start(nimage_id);
cimglist_for(spt.images,p) if (spt.images[p]) {
layer_blendmode = GIMP_NORMAL_MODE;
layer_opacity = 100;
layer_posx = layer_posy = 0;
if (layers.height()==1) {
if (!is_selection) gimp_drawable_offsets(active_layer_id,&layer_posx,&layer_posy);
CImg<char>::string(gimp_item_get_name(active_layer_id)).move_to(layer_name);
} else layer_name.assign();
get_output_layer_props(spt.images_names[p],layer_blendmode,layer_opacity,layer_posx,layer_posy,layer_name);
CImg<gmic_pixel_type> &img = spt.images[p];
if (gimp_image_base_type(nimage_id)!=GIMP_GRAY)
calibrate_image(img,(img.spectrum()==1 || img.spectrum()==3)?3:4,false);
gint layer_id = gimp_layer_new(nimage_id,new_label,img.width(),img.height(),
img.spectrum()==1?GIMP_GRAY_IMAGE:
img.spectrum()==2?GIMP_GRAYA_IMAGE:
img.spectrum()==3?GIMP_RGB_IMAGE:GIMP_RGBA_IMAGE,
layer_opacity,layer_blendmode);
gimp_layer_set_offsets(layer_id,layer_posx,layer_posy);
if (verbosity_mode==1) gimp_item_set_name(layer_id,new_label);
else if (layer_name) gimp_item_set_name(layer_id,layer_name);
gimp_image_insert_layer(nimage_id,layer_id,-1,p);
#if GIMP_MINOR_VERSION<=8
GimpDrawable *drawable = gimp_drawable_get(layer_id);
GimpPixelRgn region;
gimp_pixel_rgn_init(&region,drawable,0,0,drawable->width,drawable->height,true,true);
convert_image2uchar(img);
gimp_pixel_rgn_set_rect(&region,(guchar*)img.data(),0,0,img.width(),img.height());
gimp_drawable_flush(drawable);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,drawable->width,drawable->height);
gimp_drawable_detach(drawable);
#else
GeglBuffer *buffer = gimp_drawable_get_shadow_buffer(layer_id);
const char *const format = img.spectrum()==1?"Y' float":img.spectrum()==2?"Y'A float":
img.spectrum()==3?"R'G'B' float":"R'G'B'A float";
(img/=255).permute_axes("cxyz");
gegl_buffer_set(buffer,NULL,0,babl_format(format),img.data(),0);
g_object_unref(buffer);
gimp_drawable_merge_shadow(layer_id,true);
gimp_drawable_update(layer_id,0,0,img.width(),img.height());
#endif
img.assign();
}
gimp_display_new(nimage_id);
gimp_image_undo_group_end(nimage_id);
}
}
}
}
// Update values of parameters if invoked command requests it (using status value).
const unsigned int l_status = spt.status._width;
if (l_status>3 && spt.status[0]==gmic_lbrace && spt.status[l_status - 2]==gmic_rbrace) {
spt.status.crop(1,l_status - 3);
CImgList<char> return_values = spt.status.get_split(CImg<char>::vector(gmic_rbrace,gmic_lbrace),false,false);
if (return_values._width==get_filter_nbparams(filter))
cimglist_for(return_values,l) {
gmic::strreplace_fw(return_values[l].resize(1,return_values[l].height() + 1,1,1,0));
if (std::strcmp(return_values[l].data(),get_filter_parameter(filter,l))) {
set_filter_parameter(filter,l,return_values[l]);
update_parameters = true;
}
}
}
}
if (run_mode!=GIMP_RUN_NONINTERACTIVE) {
gimp_progress_end();
gimp_displays_flush();
if (update_parameters && is_apply) {
const bool pstate = gimp_preview_get_update(GIMP_PREVIEW(gui_preview));
gimp_preview_set_update(GIMP_PREVIEW(gui_preview),false);
create_parameters_gui(false);
gimp_preview_set_update(GIMP_PREVIEW(gui_preview),pstate);
}
}
}
// Process the preview image.
//---------------------------
void process_preview() {
if (is_block_preview) { is_block_preview = false; return; }
if (!gimp_image_is_valid(image_id)) return;
#if GIMP_MINOR_VERSION>8
//img_profile = gimp_image_get_color_profile(image_id);
img_profile = gimp_image_get_effective_color_profile(image_id);
printf("process_preview(): img_profile=%p\n", (void*)img_profile);
#endif
const unsigned int filter = get_current_filter();
//printf("process_preview(): filter=%d\n", (int)filter);
if (!filter) return;
const CImg<char> command_line = get_command_line(true);
if (!command_line || std::strstr(command_line," -_none_")) {
//printf("process_preview(): empty command line\n");
return;
}
bool update_parameters = false;
int wp, hp, sp, xp, yp;
static int _xp = -1, _yp = -1;
static double _factor = -1;
guchar *const ptr0 = gimp_zoom_preview_get_source(GIMP_ZOOM_PREVIEW(gui_preview),&wp,&hp,&sp);
const double factor = gimp_zoom_preview_get_factor(GIMP_ZOOM_PREVIEW(gui_preview));
gimp_preview_get_position(GIMP_PREVIEW(gui_preview),&xp,&yp);
if (xp!=_xp || _yp!=yp || _factor!=factor) {
_xp = xp; _yp = yp; _factor = factor;
computed_preview.assign();
latest_preview_buffer.assign();
}
if (!computed_preview) {
// Get input layers for the chosen filter and convert then to the preview size if necessary.
st_process_thread spt;
spt.is_preview = true;
spt.is_thread = false;
spt.command_line = command_line;
spt.verbosity_mode = get_verbosity_mode();
spt.progress = -1;
spt.set_env();
CImg<char> layer_name(256);
const unsigned int input_mode = get_input_mode();
int nb_layers = 0, *const layers = gimp_image_get_layers(image_id,&nb_layers);
if (nb_layers && input_mode) {
if (!preview_image_id &&
(input_mode==1 ||
(input_mode==2 && nb_layers==1) ||
(input_mode==3 && nb_layers==1 && _gimp_item_get_visible(*layers)) ||
(input_mode==4 && nb_layers==1 && !_gimp_item_get_visible(*layers)) ||
(input_mode==5 && nb_layers==1))) {
// Single input layer: get the default thumbnail provided by GIMP.
spt.images.assign(1);
spt.images_names.assign(1);
const guchar *ptrs = ptr0;
spt.images.assign(1,wp,hp,1,sp);
const int whp = wp*hp;
switch (sp) {
case 1 : {
float *ptr_r = spt.images[0].data(0,0,0,0);
for (int xy = 0; xy<whp; ++xy) *(ptr_r++) = (float)*(ptrs++);
} break;
case 2 : {
float
*ptr_r = spt.images[0].data(0,0,0,0),
*ptr_g = spt.images[0].data(0,0,0,1);
for (int xy = 0; xy<whp; ++xy) {
*(ptr_r++) = (float)*(ptrs++);
*(ptr_g++) = (float)*(ptrs++);
}
} break;
case 3 : {
float
*ptr_r = spt.images[0].data(0,0,0,0),
*ptr_g = spt.images[0].data(0,0,0,1),
*ptr_b = spt.images[0].data(0,0,0,2);
for (int xy = 0; xy<whp; ++xy) {
*(ptr_r++) = (float)*(ptrs++);
*(ptr_g++) = (float)*(ptrs++);
*(ptr_b++) = (float)*(ptrs++);
}
} break;
case 4 : {
float
*ptr_r = spt.images[0].data(0,0,0,0),
*ptr_g = spt.images[0].data(0,0,0,1),
*ptr_b = spt.images[0].data(0,0,0,2),
*ptr_a = spt.images[0].data(0,0,0,3);
for (int xy = 0; xy<whp; ++xy) {
*(ptr_r++) = (float)*(ptrs++);
*(ptr_g++) = (float)*(ptrs++);
*(ptr_b++) = (float)*(ptrs++);
*(ptr_a++) = (float)*(ptrs++);
}
} break;
}
const float opacity = gimp_layer_get_opacity(*layers);
const GimpLayerModeEffects blendmode = gimp_layer_get_mode(*layers);
int posx = 0, posy = 0;
gimp_drawable_offsets(*layers,&posx,&posy);
const int
w = gimp_drawable_width(*layers),
h = gimp_drawable_height(*layers),
ox = (int)(posx*wp/w),
oy = (int)(posy*hp/h);
CImg<char> _layer_name = CImg<char>::string(gimp_item_get_name(*layers));
cimg_for(_layer_name,p,char) if (*p=='(') *p = '['; else if (*p==')') *p = ']';
cimg_snprintf(layer_name,layer_name.width(),"mode(%s),opacity(%g),pos(%d,%d),name(%s)",
s_blendmode[blendmode],opacity,ox,oy,
_layer_name.data());
CImg<char>::string(layer_name).move_to(spt.images_names[0]);
} else {
// Multiple input layers: compute a 'hand-made' set of thumbnails.
if (preview_image_id) {
wp = gimp_image_width(preview_image_id);
hp = gimp_image_height(preview_image_id);
}
CImgList<unsigned char> images_uchar;
const CImg<int> layers = get_input_layers(images_uchar);
if (images_uchar) {
spt.images.assign(images_uchar.size());
spt.images_names.assign(images_uchar.size());
// Retrieve global preview ratio.
const int
active_layer = gimp_image_get_active_layer(image_id),
w0 = gimp_drawable_width(active_layer),
h0 = gimp_drawable_height(active_layer),
_wp = (int)cimg::round(wp/preview_image_factor),
_hp = (int)cimg::round(hp/preview_image_factor);
const double ratio = cimg::max((double)_wp/w0,(double)_hp/h0);
// Retrieve resized and cropped preview layers.
cimg_forY(layers,p) {
const float opacity = gimp_layer_get_opacity(layers[p]);
const GimpLayerModeEffects blendmode = gimp_layer_get_mode(layers[p]);
int posx = 0, posy = 0;
gimp_drawable_offsets(layers[p],&posx,&posy);
CImg<unsigned char>& img = images_uchar[p];
const int
w = img.width(),
h = img.height(),
x0 = (int)(xp*w/wp/factor),
y0 = (int)(yp*h/hp/factor),
x1 = x0 + (int)(w/factor) - 1,
y1 = y0 + (int)(h/factor) - 1,
ox = (int)(posx*_wp/w0),
oy = (int)(posy*_hp/h0);
img.get_crop(x0,y0,x1,y1).resize(cimg::round(img.width()*ratio),
cimg::round(img.height()*ratio)).
move_to(spt.images[p]);
cimg_snprintf(layer_name,layer_name.width(),"mode(%s),opacity(%g),pos(%d,%d)",
s_blendmode[blendmode],opacity,ox,oy);
CImg<char>::string(layer_name).move_to(spt.images_names[p]);
}
}
}
}
// Run G'MIC.
CImg<unsigned char> original_preview;
CImg<char> progress_label;
if (spt.images) original_preview = spt.images[0];
else original_preview.assign(wp,hp,1,4,0);
bool is_abort = spt.is_abort = false;
cimg::mutex(25); p_spt = (void*)&spt; cimg::mutex(25,0);
spt.is_thread = true;
pthread_mutex_init(&spt.is_running,0);
pthread_mutex_init(&spt.wait_lock,0);
pthread_cond_init(&spt.wait_cond,0);
pthread_mutex_lock(&spt.wait_lock);
#if defined(__MACOSX__) || defined(__APPLE__)
const cimg_ulong stacksize = (cimg_ulong)8*1024*1024;
pthread_attr_t thread_attr;
if (!pthread_attr_init(&thread_attr) && !pthread_attr_setstacksize(&thread_attr,stacksize))
// Reserve enough stack size for the new thread.
pthread_create(&spt.thread,&thread_attr,process_thread,(void*)&spt);
else
#endif // #if defined(__MACOSX__) || defined(__APPLE__)
pthread_create(&spt.thread,0,process_thread,(void*)&spt);
pthread_cond_wait(&spt.wait_cond,&spt.wait_lock); // Wait for the thread to lock the mutex.
pthread_mutex_unlock(&spt.wait_lock);
pthread_mutex_destroy(&spt.wait_lock);
if (latest_preview_buffer.width()==wp && latest_preview_buffer.height()==hp && // Avoid preview flickering effect.
latest_preview_buffer.spectrum()==sp)
gimp_preview_draw_buffer(GIMP_PREVIEW(gui_preview),latest_preview_buffer.data(),wp*sp);
while (pthread_mutex_trylock(&spt.is_running)) { // Loop that allows to get a responsive interface.
while (gtk_events_pending()) { gtk_main_iteration(); }
cimg::wait(333);
}
pthread_join(spt.thread,0);
pthread_mutex_unlock(&spt.is_running);
pthread_mutex_destroy(&spt.is_running);
is_abort = spt.is_abort;
cimg::mutex(25); p_spt = (void*)0; cimg::mutex(25,0);
if (is_abort) return;
// Manage possible errors.
if (spt.error_message) {
spt.images.assign();
spt.images_names.assign();
original_preview.move_to(spt.images);
CImg<char> command(1024);
cimg_snprintf(command,command.width(),"%s-gimp_error_preview \"%s\"",
get_verbosity_mode()>5?"-debug ":get_verbosity_mode()>3?"":"-v -99 ",
spt.error_message.data());
try {
gmic(command,spt.images,spt.images_names,gmic_additional_commands,true);
} catch (...) { // Fallback for '-gimp_error_preview'.
const unsigned char white[] = { 155,155,155 };
spt.images.assign(1).back().draw_text(0,0," Preview\n error ",white,0,1,57).
resize(-100,-100,1,4).get_shared_channel(3).dilate(5);
spt.images[0].resize(wp,hp,1,4,0,0,0.5,0.5)+=100;
}
spt.status.assign();
}
// Transfer the output layers back into GIMP preview.
CImgList<gmic_pixel_type> preview_images;
switch (get_preview_mode()) {
case 0 : // Preview 1st layer
if (spt.images && spt.images.size()>0) spt.images[0].move_to(preview_images);
break;
case 1 : // Preview 2nd layer
if (spt.images && spt.images.size()>1) spt.images[1].move_to(preview_images);
break;
case 2 : // Preview 3rd layer
if (spt.images && spt.images.size()>2) spt.images[2].move_to(preview_images);
break;
case 3 : // Preview 4th layer
if (spt.images && spt.images.size()>3) spt.images[3].move_to(preview_images);
break;
case 4 : { // Preview 1st->2nd layers
cimglist_for_in(spt.images,0,1,l) spt.images[l].move_to(preview_images);
} break;
case 5 : { // Preview 1st->3nd layers
cimglist_for_in(spt.images,0,2,l) spt.images[l].move_to(preview_images);
} break;
case 6 : { // Preview 1st->4nd layers
cimglist_for_in(spt.images,0,3,l) spt.images[l].move_to(preview_images);
} break;
default : // Preview all layers
spt.images.move_to(preview_images);
}
spt.images.assign();
spt.images_names.assign();
// Generate a single image representation from all output preview layers.
unsigned int _sp = 0;
cimglist_for(preview_images,l) if (preview_images[l]._spectrum>_sp) _sp = preview_images[l]._spectrum;
if (_sp==1 || _sp==3) ++_sp;
cimglist_for(preview_images,l) calibrate_image(preview_images[l],_sp,true);
computed_preview.assign();
if (preview_images.size()==1) preview_images[0].move_to(computed_preview);
else if (preview_images.size()>1) try {
CImgList<char> preview_images_names;
gmic("-gimp_preview",preview_images,preview_images_names,gmic_additional_commands,true);
if (preview_images.size()) preview_images[0].move_to(computed_preview);
preview_images.assign();
preview_images_names.assign();
} catch (...) { // Fallback for '-gimp_preview'.
const unsigned char white[] = { 155,155,155 };
preview_images.assign();
computed_preview.draw_text(0,0," Preview\n error ",white,0,1,57).
resize(-100,-100,1,4).get_shared_channel(3).dilate(5);
computed_preview.resize(wp,hp,1,4,0,0,0.5,0.5)+=100;
}
if (!computed_preview) computed_preview.assign(wp,hp,1,4,0);
if (computed_preview.width()!=wp || computed_preview.height()!=hp) {
const double ratio = cimg::min((double)wp/computed_preview.width(),
(double)hp/computed_preview.height());
computed_preview.resize((int)cimg::round(computed_preview.width()*ratio),
(int)cimg::round(computed_preview.height()*ratio),
1,-100,2);
}
if (computed_preview.width()!=wp || computed_preview.height()!=hp)
computed_preview.resize(wp,hp,1,-100,0,0,0.5,0.5);
convert_to_display(computed_preview);
calibrate_image(computed_preview,sp,true);
convert_image2uchar(computed_preview);
computed_preview.channel(0);
// Update values of parameters if invoked command requests it (using status value).
const unsigned int l_status = spt.status._width;
if (l_status>3 && spt.status[0]==gmic_lbrace && spt.status[l_status - 2]==gmic_rbrace) {
spt.status.crop(1,l_status - 3);
CImgList<char> return_values = spt.status.get_split(CImg<char>::vector(gmic_rbrace,gmic_lbrace),false,false);
if (return_values._width==get_filter_nbparams(filter))
cimglist_for(return_values,l) {
gmic::strreplace_fw(return_values[l].resize(1,return_values[l].height() + 1,1,1,0));
if (std::strcmp(return_values[l].data(),get_filter_parameter(filter,l))) {
set_filter_parameter(filter,l,return_values[l]);
update_parameters = true;
}
}
}
}
// Display warning message about preview inaccuracy, if needed.
double default_factor = gmic_preview_factors(filter,0);
if (!default_factor) {
int _pw = 0, _ph = 0;
gimp_preview_get_size(GIMP_PREVIEW(gui_preview),&_pw,&_ph);
#if GIMP_MINOR_VERSION<=8
const float
pw = (float)_pw,
ph = (float)_ph,
dw = (float)gimp_zoom_preview_get_drawable(GIMP_ZOOM_PREVIEW(gui_preview))->width,
dh = (float)gimp_zoom_preview_get_drawable(GIMP_ZOOM_PREVIEW(gui_preview))->height;
#else
const int preview_drawable_id = gimp_zoom_preview_get_drawable_id(GIMP_ZOOM_PREVIEW(gui_preview));
const float
pw = (float)_pw,
ph = (float)_ph,
dw = (float)gimp_drawable_width(preview_drawable_id),
dh = (float)gimp_drawable_height(preview_drawable_id);
#endif
default_factor = std::sqrt((dw*dw + dh*dh)/(pw*pw + ph*ph));
}
const bool is_accurate_when_zoomed = gmic_preview_factors(filter,1);
if (default_factor<0 || is_accurate_when_zoomed || cimg::abs(factor-default_factor)<0.1)
gtk_widget_hide(gui_preview_warning);
else gtk_widget_show(gui_preview_warning);
// Update rendered image in preview widget.
std::memcpy(ptr0,computed_preview.data(),wp*hp*sp*sizeof(unsigned char));
gimp_preview_draw_buffer(GIMP_PREVIEW(gui_preview),ptr0,wp*sp);
latest_preview_buffer.assign(ptr0,wp,hp,1,sp);
g_free(ptr0);
if (update_parameters) {
const bool pstate = gimp_preview_get_update(GIMP_PREVIEW(gui_preview));
gimp_preview_set_update(GIMP_PREVIEW(gui_preview),false);
create_parameters_gui(false);
gimp_preview_set_update(GIMP_PREVIEW(gui_preview),pstate);
}
}
// Create the parameters GUI for the chosen filter.
//--------------------------------------------------
void create_parameters_gui(const bool reset_params) {
const unsigned int filter = get_current_filter();
gmic_button_parameters.assign();
// Remove existing table in the parameters frame if necessary.
GtkWidget *const child = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(right_frame)));
if (child) gtk_container_remove(GTK_CONTAINER(right_frame),child);
// Create new table for the parameters frame.
GtkWidget *table = 0;
if (!filter) { // No filter selected -> 1x1 table with default message.
table = gtk_table_new(1,1,false);
gtk_widget_show(table);
GtkWidget *const label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label),t("<i>Select a filter...</i>"));
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
(GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0);
gtk_misc_set_alignment (GTK_MISC(label),0,0.5);
gtk_frame_set_label(GTK_FRAME(right_frame),NULL);
} else { // Filter selected -> Build the table for setting the parameters.
CImg<char> s_label(256);
cimg_snprintf(s_label,s_label.width(),"<b> %s </b>",gmic_entries[filter].data());
GtkWidget *const frame_title = gtk_label_new(NULL);
gtk_widget_show(frame_title);
gtk_label_set_markup(GTK_LABEL(frame_title),s_label);
gtk_frame_set_label_widget(GTK_FRAME(right_frame),frame_title);
// Count number of filter arguments.
CImg<char> argument_name(256), _argument_type(32), argument_arg(65536);
*argument_name = *_argument_type = *argument_arg = 0;
unsigned int nb_arguments = 0;
for (const char *argument = gmic_arguments[filter].data(); *argument; ) {
int err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-z](%65535[^)]",
argument_name.data(),_argument_type.data(),&(argument_arg[0]=0));
if (err!=3) err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-z]{%65535[^}]",
argument_name.data(),_argument_type.data(),argument_arg.data());
if (err!=3) err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-z][%65535[^]]",
argument_name.data(),_argument_type.data(),argument_arg.data());
if (err>=2) {
argument += std::strlen(argument_name) + std::strlen(_argument_type) + std::strlen(argument_arg) + 3;
if (*argument) ++argument;
++nb_arguments;
} else break;
}
unsigned int current_table_line = 0;
if (!nb_arguments) { // Filter requires no parameters -> 1x1 table with default message.
table = gtk_table_new(1,1,false);
gtk_widget_show(table);
GtkWidget *label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label),t("<i>No parameters to set...</i>"));
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
(GtkAttachOptions)(GTK_EXPAND),(GtkAttachOptions)(GTK_EXPAND),0,0);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
if (event_infos) delete[] event_infos; event_infos = 0;
set_filter_nbparams(filter,0);
} else { // Filter requires parameters -> Create parameters table.
// Create new table for putting parameters inside.
table = gtk_table_new(nb_arguments,3,false);
gtk_widget_show(table);
gtk_table_set_row_spacings(GTK_TABLE(table),6);
gtk_table_set_col_spacings(GTK_TABLE(table),6);
gtk_container_set_border_width(GTK_CONTAINER(table),8);
// Parse arguments list and add recognized one to the table.
if (event_infos) delete[] event_infos; event_infos = new void*[2*nb_arguments];
unsigned int current_argument = 0;
const bool is_fave = filter>=indice_faves;
for (const char *argument = gmic_arguments[filter].data(); *argument; ) {
int err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-Z_](%65535[^)]",
argument_name.data(),_argument_type.data(),&(argument_arg[0]=0));
if (err!=3) err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-Z_][%65535[^]]",
argument_name.data(),_argument_type.data(),argument_arg.data());
if (err!=3) err = cimg_sscanf(argument,"%255[^=]=%31[ a-zA-Z_]{%65535[^}]",
argument_name.data(),_argument_type.data(),argument_arg.data());
if (err>=2) {
argument += std::strlen(argument_name) + std::strlen(_argument_type) + std::strlen(argument_arg) + 3;
if (*argument) ++argument;
cimg::strpare(argument_name,' ',false,true);
cimg::strpare(argument_name,'\"',true,false);
cimg::strunescape(argument_name);
gtk_label_set_markup(GTK_LABEL(markup2ascii),argument_name);
cimg_snprintf(argument_name,argument_name.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
cimg::strpare(_argument_type,' ',false,true);
cimg::strpare(argument_arg,' ',false,true);
const bool is_silent_argument = (*_argument_type=='_');
char
*const argument_type = _argument_type.data() + (is_silent_argument?1:0);
CImg<char>
argument_fave = get_fave_parameter(filter,current_argument),
argument_value = get_filter_parameter(filter,current_argument);
// Check for a float-valued argument.
bool found_valid_argument = false;
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"float")) {
float value = 0, min_value = 0, max_value = 100;
setlocale(LC_NUMERIC,"C");
cimg_sscanf(argument_arg,"%f%*c%f%*c%f",&value,&min_value,&max_value);
if (is_fave) cimg_sscanf(argument_fave,"%f",&value);
if (!reset_params) cimg_sscanf(argument_value,"%f",&value);
GtkObject *const
scale = gimp_scale_entry_new(GTK_TABLE(table),0,(int)current_table_line,argument_name,50,6,
(double)value,(double)min_value,(double)max_value,
(double)(max_value - min_value)/100,
(double)(max_value - min_value)/20,
2,true,0,0,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
on_float_parameter_changed(GTK_ADJUSTMENT(scale),event_infos + 2*current_argument);
g_signal_connect(scale,"value_changed",G_CALLBACK(on_float_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(scale,"value_changed",G_CALLBACK(_gimp_preview_invalidate),0);
found_valid_argument = true; ++current_argument;
}
// Check for an int-valued argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"int")) {
float value = 0, min_value = 0, max_value = 100;
setlocale(LC_NUMERIC,"C");
cimg_sscanf(argument_arg,"%f%*c%f%*c%f",&value,&min_value,&max_value);
if (is_fave) cimg_sscanf(argument_fave,"%f",&value);
if (!reset_params) cimg_sscanf(argument_value,"%f",&value);
GtkObject *const
scale = gimp_scale_entry_new(GTK_TABLE(table),0,(int)current_table_line,argument_name,50,6,
(double)(int)cimg::round(value,1.0f),
(double)(int)cimg::round(min_value,1.0f),
(double)(int)cimg::round(max_value,1.0f),
(double)1,
(double)cimg::max(1.0,cimg::round((max_value - min_value)/20,1,1)),
0,true,0,0,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
on_int_parameter_changed(GTK_ADJUSTMENT(scale),event_infos + 2*current_argument);
g_signal_connect(scale,"value_changed",G_CALLBACK(on_int_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(scale,"value_changed",G_CALLBACK(_gimp_preview_invalidate),0);
found_valid_argument = true; ++current_argument;
}
// Check for a bool-valued argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"bool")) {
cimg::strpare(argument_arg,' ',false,true);
cimg::strpare(argument_arg,'\"',true,false);
bool
value = !(!*argument_arg || !cimg::strcasecmp(argument_arg,"false") ||
(argument_arg[0]=='0' && argument_arg[1]==0));
if (is_fave && *argument_fave)
value = !(!cimg::strcasecmp(argument_fave,"false") ||
(argument_fave[0]=='0' && argument_fave[1]==0));
if (!reset_params && *argument_value)
value = !(!cimg::strcasecmp(argument_value,"false") ||
(argument_value[0]=='0' && argument_value[1]==0));
GtkWidget *const checkbutton = gtk_check_button_new_with_label(argument_name);
gtk_widget_show(checkbutton);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton),value);
gtk_table_attach(GTK_TABLE(table),checkbutton,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
on_bool_parameter_changed(GTK_CHECK_BUTTON(checkbutton),event_infos + 2*current_argument);
g_signal_connect(checkbutton,"toggled",G_CALLBACK(on_bool_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(checkbutton,"toggled",G_CALLBACK(_gimp_preview_invalidate),0);
found_valid_argument = true; ++current_argument;
}
// Check for a button argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"button")) {
float alignment = 0;
setlocale(LC_NUMERIC,"C");
if (cimg_sscanf(argument_arg,"%f",&alignment)!=1) alignment = 0;
GtkWidget
*const button = gtk_button_new_with_label(argument_name),
*const align = gtk_alignment_new(alignment,0.5f,0,0);
gtk_widget_show(button);
gtk_container_add(GTK_CONTAINER(align),button);
gtk_widget_show(align);
gtk_table_attach(GTK_TABLE(table),align,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
g_signal_connect(button,"clicked",G_CALLBACK(on_button_parameter_clicked),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(button,"clicked",G_CALLBACK(_gimp_preview_invalidate),0);
set_filter_parameter(filter,current_argument,"0");
CImg<unsigned int>::vector(current_argument).move_to(gmic_button_parameters);
found_valid_argument = true; ++current_argument;
}
// Check for a choice-valued argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"choice")) {
GtkWidget *const label = gtk_label_new(argument_name);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,1,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
GtkWidget *const combobox = gtk_combo_box_new_text();
gtk_widget_show(combobox);
CImg<char> s_entry(1024); *s_entry = 0;
char end = 0; int err = 0;
unsigned int value = 0;
const char *entries = argument_arg;
if (cimg_sscanf(entries,"%u",&value)==1)
entries+=cimg_snprintf(s_entry,s_entry.width(),"%u",value) + 1;
while (*entries) {
if ((err = cimg_sscanf(entries,"%1023[^,]%c",s_entry.data(),&end))>0) {
entries += std::strlen(s_entry) + (err==2?1:0);
cimg::strpare(s_entry,' ',false,true);
cimg::strpare(s_entry,'\"',true,false);
cimg::strunescape(s_entry);
gtk_label_set_markup(GTK_LABEL(markup2ascii),s_entry);
cimg_snprintf(s_entry,s_entry.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
gtk_combo_box_append_text(GTK_COMBO_BOX(combobox),s_entry);
} else break;
}
if (is_fave) cimg_sscanf(argument_fave,"%u",&value);
if (!reset_params) cimg_sscanf(argument_value,"%u",&value);
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox),value);
gtk_table_attach(GTK_TABLE(table),combobox,1,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_FILL,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
on_list_parameter_changed(GTK_COMBO_BOX(combobox),event_infos + 2*current_argument);
g_signal_connect(combobox,"changed",G_CALLBACK(on_list_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(combobox,"changed",G_CALLBACK(_gimp_preview_invalidate),0);
found_valid_argument = true; ++current_argument;
}
// Check for a single or multi-line text-valued argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"text")) {
int line_number = 0;
char sep = 0;
if (cimg_sscanf(argument_arg,"%d%c",&line_number,&sep)==2 && sep==',' && line_number==1) {
// Multi-line entry
GtkWidget *const frame = gtk_frame_new(NULL);
gtk_widget_show(frame);
gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_IN);
gtk_container_set_border_width(GTK_CONTAINER(frame),4);
GtkWidget *const hbox = gtk_hbox_new(false,0);
gtk_widget_show(hbox);
CImg<char> s_label(256); *s_label = 0;
cimg_snprintf(s_label,s_label.width()," %s ",argument_name.data());
GtkWidget *const label = gtk_label_new(s_label);
gtk_widget_show(label);
gtk_box_pack_start(GTK_BOX(hbox),label,false,false,0);
GtkWidget *const button = gtk_button_new_with_label(t("Update"));
gtk_widget_show(button);
gtk_box_pack_start(GTK_BOX(hbox),button,false,false,0);
gtk_frame_set_label_widget(GTK_FRAME(frame),hbox);
GtkWidget *const alignment2 = gtk_alignment_new(0,0,1,1);
gtk_widget_show(alignment2);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment2),3,3,3,3);
gtk_container_add(GTK_CONTAINER(frame),alignment2);
GtkWidget *const view = gtk_text_view_new();
gtk_widget_show(view);
gtk_text_view_set_editable(GTK_TEXT_VIEW(view),true);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view),8);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view),8);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view),GTK_WRAP_CHAR);
gtk_container_add(GTK_CONTAINER(alignment2),view);
GtkTextBuffer *const buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
char *value = std::strchr(argument_arg,',') + 1;
if (is_fave) value = argument_fave;
if (!reset_params && *argument_value) value = argument_value;
else if (!is_fave) cimg::strunescape(value);
cimg::strpare(value,' ',false,true);
cimg::strpare(value,'\"',true,false);
gtk_label_set_markup(GTK_LABEL(markup2ascii),value);
cimg_snprintf(argument_arg,argument_arg.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
for (char *p = argument_arg; *p; ++p) if (*p==gmic_dquote) *p='\"';
gtk_text_buffer_set_text(buffer,argument_arg,-1);
gtk_table_attach(GTK_TABLE(table),frame,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)view;
on_multitext_parameter_changed(event_infos + 2*current_argument);
g_signal_connect_swapped(button,"clicked",G_CALLBACK(on_multitext_parameter_changed),
event_infos + 2*current_argument);
g_signal_connect_swapped(view,"key-release-event",G_CALLBACK(on_multitext_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(button,"clicked",G_CALLBACK(_gimp_preview_invalidate),0);
} else { // Single-line entry
GtkWidget *const label = gtk_label_new(argument_name);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,1,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
GtkWidget *const entry = gtk_entry_new_with_max_length(1023);
gtk_widget_show(entry);
char *value = (line_number!=0 || sep!=',')?argument_arg:(std::strchr(argument_arg,',') + 1);
if (is_fave) value = argument_fave;
if (!reset_params && *argument_value) value = argument_value;
else if (!is_fave) cimg::strunescape(value);
cimg::strpare(value,' ',false,true);
cimg::strpare(value,'\"',true,false);
gtk_label_set_markup(GTK_LABEL(markup2ascii),value);
cimg_snprintf(argument_arg,argument_arg.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
for (char *p = value; *p; ++p) if (*p==gmic_dquote) *p='\"';
gtk_entry_set_text(GTK_ENTRY(entry),argument_arg);
gtk_table_attach(GTK_TABLE(table),entry,1,2,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0);
GtkWidget *const button = gtk_button_new_with_label(t("Update"));
gtk_widget_show(button);
gtk_table_attach(GTK_TABLE(table),button,2,3,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)entry;
on_text_parameter_changed(event_infos + 2*current_argument);
g_signal_connect_swapped(button,"clicked",G_CALLBACK(on_text_parameter_changed),
event_infos + 2*current_argument);
g_signal_connect_swapped(entry,"changed",G_CALLBACK(on_text_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument) {
g_signal_connect(button,"clicked",G_CALLBACK(_gimp_preview_invalidate),0);
g_signal_connect(entry,"activate",G_CALLBACK(_gimp_preview_invalidate),0);
}
}
found_valid_argument = true; ++current_argument;
}
// Check for a filename or folder name argument.
if (!found_valid_argument && (!cimg::strcasecmp(argument_type,"file") ||
!cimg::strcasecmp(argument_type,"folder"))) {
GtkWidget *const label = gtk_label_new(argument_name);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,1,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
GtkWidget *const
file_chooser = gtk_file_chooser_button_new(argument_name,
cimg::lowercase(argument_type[1])=='i'?
GTK_FILE_CHOOSER_ACTION_OPEN:
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
gtk_widget_show(file_chooser);
char *value = argument_arg;
if (is_fave) value = argument_fave;
if (!reset_params && *argument_value) value = argument_value;
cimg::strpare(value,' ',false,true);
cimg::strpare(value,'\"',true,false);
gtk_label_set_markup(GTK_LABEL(markup2ascii),value);
cimg_snprintf(argument_arg,argument_arg.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_chooser),value);
gtk_table_attach(GTK_TABLE(table),file_chooser,1,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),(GtkAttachOptions)0,0,0);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)is_silent_argument; // Silent property handled by event.
on_file_parameter_changed(GTK_FILE_CHOOSER(file_chooser),event_infos + 2*current_argument);
g_signal_connect(file_chooser,"selection-changed",G_CALLBACK(on_file_parameter_changed),
event_infos + 2*current_argument);
found_valid_argument = true; ++current_argument;
}
// Check for a color argument.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"color")) {
GtkWidget *const label = gtk_label_new(argument_name);
gtk_widget_show(label);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
gtk_table_attach(GTK_TABLE(table),label,0,1,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
GtkWidget *const hbox = gtk_hbox_new(false,6);
gtk_widget_show(hbox);
gtk_table_attach(GTK_TABLE(table),hbox,1,2,(int)current_table_line,(int)current_table_line + 1,
GTK_FILL,GTK_SHRINK,0,0);
GtkWidget *const color_chooser = gtk_color_button_new();
gtk_widget_show(color_chooser);
gtk_color_button_set_title(GTK_COLOR_BUTTON(color_chooser),argument_name);
gtk_button_set_alignment(GTK_BUTTON(color_chooser),0,0.5);
gtk_box_pack_start(GTK_BOX(hbox),color_chooser,false,false,0);
float red = 0, green = 0, blue = 0, alpha = 255;
setlocale(LC_NUMERIC,"C");
const int err = cimg_sscanf(argument_arg,"%f%*c%f%*c%f%*c%f",&red,&green,&blue,&alpha);
if (is_fave) cimg_sscanf(argument_fave,"%f%*c%f%*c%f%*c%f",&red,&green,&blue,&alpha);
if (!reset_params) cimg_sscanf(argument_value,"%f%*c%f%*c%f%*c%f",&red,&green,&blue,&alpha);
red = red<0?0:red>255?255:red;
green = green<0?0:green>255?255:green;
blue = blue<0?0:blue>255?255:blue;
GdkColor color;
color.pixel = 0;
color.red = (unsigned int)(red*257);
color.green = (unsigned int)(green*257);
color.blue = (unsigned int)(blue*257);
gtk_color_button_set_color(GTK_COLOR_BUTTON(color_chooser),&color);
if (err==4) {
gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(color_chooser),true);
gtk_color_button_set_alpha(GTK_COLOR_BUTTON(color_chooser),(unsigned int)(alpha*257));
} else gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(color_chooser),false);
event_infos[2*current_argument] = (void*)(cimg_ulong)current_argument;
event_infos[2*current_argument + 1] = (void*)0;
on_color_parameter_changed(GTK_COLOR_BUTTON(color_chooser),event_infos + 2*current_argument);
g_signal_connect(color_chooser,"color-set",G_CALLBACK(on_color_parameter_changed),
event_infos + 2*current_argument);
if (!is_silent_argument)
g_signal_connect(color_chooser,"color-set",G_CALLBACK(_gimp_preview_invalidate),0);
found_valid_argument = true; ++current_argument;
}
// Check for a constant value.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"const")) {
const char *value = argument_arg;
if (is_fave) value = argument_fave;
if (!reset_params && *argument_value) value = argument_value;
set_filter_parameter(filter,current_argument,value);
found_valid_argument = true; ++current_argument;
}
// Check for a note.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"note")) {
cimg::strpare(argument_arg,' ',false,true);
cimg::strpare(argument_arg,'\"',true,false);
cimg::strunescape(argument_arg);
GtkWidget *const label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label),argument_arg);
gtk_label_set_line_wrap(GTK_LABEL(label),true);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table),label,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
found_valid_argument = true;
}
// Check for a link.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"link")) {
CImg<char> label(1024), url(1024); *label = *url = 0;
float alignment = 0.5f;
switch (cimg_sscanf(argument_arg,"%f,%1023[^,],%1023s",&alignment,label.data(),url.data())) {
case 2 : std::strcpy(url,label); break;
case 1 : cimg_snprintf(url,url.width(),"%g",alignment); break;
case 0 : if (cimg_sscanf(argument_arg,"%1023[^,],%1023s",label.data(),url.data())==1)
std::strcpy(url,label); break;
}
cimg::strpare(label,' ',false,true);
cimg::strpare(label,'\"',true,false);
cimg::strunescape(label);
gtk_label_set_markup(GTK_LABEL(markup2ascii),label);
cimg_snprintf(label,label.width(),"%s",gtk_label_get_text(GTK_LABEL(markup2ascii)));
cimg::strpare(url,' ',false,true);
cimg::strpare(url,'\"',true,false);
GtkWidget *const link = gtk_link_button_new_with_label(url,label);
gtk_widget_show(link);
gtk_button_set_alignment(GTK_BUTTON(link),alignment,0.5);
gtk_table_attach(GTK_TABLE(table),link,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
found_valid_argument = true;
}
// Check for an horizontal separator.
if (!found_valid_argument && !cimg::strcasecmp(argument_type,"separator")) {
GtkWidget *const separator = gtk_hseparator_new();
gtk_widget_show(separator);
gtk_table_attach(GTK_TABLE(table),separator,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
found_valid_argument = true;
}
if (!found_valid_argument) {
if (get_verbosity_mode()>1) {
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ Found invalid parameter type '%s' for argument '%s'.\n",
argument_type,argument_name.data());
std::fflush(cimg::output());
}
} else ++current_table_line;
} else break;
}
set_filter_nbparams(filter,current_argument);
}
// Add preview warning message (initially hidden).
gui_preview_warning = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(gui_preview_warning),
t("\n<span color=\"#AA0000\"><b>Warning:</b> Preview may be inaccurate\n"
"(zoom factor has been modified)</span>"));
gtk_table_attach(GTK_TABLE(table),gui_preview_warning,0,3,(int)current_table_line,(int)current_table_line + 1,
(GtkAttachOptions)(GTK_EXPAND | GTK_FILL),GTK_SHRINK,0,0);
}
gtk_container_add(GTK_CONTAINER(right_frame),table);
// Take care of the size of the parameter table.
GtkRequisition requisition;
gtk_widget_size_request(table,&requisition);
gtk_widget_set_size_request(right_pane,cimg::max(450,requisition.width),-1);
set_preview_factor();
// Set correct icon for fave button.
if (fave_stock) gtk_widget_destroy(fave_stock);
fave_stock = gtk_button_new_from_stock(!filter?GTK_STOCK_ABOUT:GTK_STOCK_ADD);
GtkWidget *const fave_image = gtk_button_get_image(GTK_BUTTON(fave_stock));
gtk_button_set_image(GTK_BUTTON(fave_add_button),fave_image);
gtk_widget_show(fave_add_button);
if (filter && filter>=indice_faves) gtk_widget_show(fave_delete_button);
else gtk_widget_hide(fave_delete_button);
}
// Create main plug-in dialog window and wait for events.
//-------------------------------------------------------
bool create_dialog_gui() {
// Init GUI_specific variables
_create_dialog_gui = true;
gimp_ui_init("gmic",true);
event_infos = 0;
// Create main dialog window with buttons.
CImg<char> dialog_title(64);
#ifdef gmic_prerelease
cimg_snprintf(dialog_title,dialog_title.width(),"%s %d.%d - %s %u bits - %d.%d.%dpre#%s",
t("G'MIC for GIMP"),
GIMP_MAJOR_VERSION,GIMP_MINOR_VERSION,
cimg::stros(),sizeof(void*)==8?64:32,
gmic_version/100,(gmic_version/10)%10,gmic_version%10,gmic_prerelease);
#else
cimg_snprintf(dialog_title,dialog_title.width(),"%s %d.%d - %s %u bits - %d.%d.%d",
t("G'MIC for GIMP"),
GIMP_MAJOR_VERSION,GIMP_MINOR_VERSION,
cimg::stros(),sizeof(void*)==8?64:32,
gmic_version/100,(gmic_version/10)%10,gmic_version%10);
#endif
dialog_window = gimp_dialog_new(dialog_title,"gmic",0,(GtkDialogFlags)0,0,0,NULL);
gimp_window_set_transient(GTK_WINDOW(dialog_window));
g_signal_connect(dialog_window,"close",G_CALLBACK(on_dialog_cancel_clicked),0);
g_signal_connect(dialog_window,"delete-event",G_CALLBACK(on_dialog_cancel_clicked),0);
GtkWidget *const cancel_button = gtk_dialog_add_button(GTK_DIALOG(dialog_window),
GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL);
g_signal_connect(cancel_button,"clicked",G_CALLBACK(on_dialog_cancel_clicked),0);
GtkWidget *const maximize_button = gtk_dialog_add_button(GTK_DIALOG(dialog_window),
GTK_STOCK_FULLSCREEN,GTK_RESPONSE_NONE);
g_signal_connect(maximize_button,"clicked",G_CALLBACK(on_dialog_maximize_button_clicked),0);
GtkWidget *const reset_button = gtk_dialog_add_button(GTK_DIALOG(dialog_window),
GIMP_STOCK_RESET,GTK_RESPONSE_NONE);
g_signal_connect(reset_button,"clicked",G_CALLBACK(on_dialog_reset_clicked),0);
GtkWidget *apply_button = gtk_dialog_add_button(GTK_DIALOG(dialog_window),
GTK_STOCK_APPLY,GTK_RESPONSE_APPLY);
g_signal_connect(apply_button,"clicked",G_CALLBACK(on_dialog_apply_clicked),0);
GtkWidget *ok_button = gtk_dialog_add_button(GTK_DIALOG(dialog_window),
GTK_STOCK_OK,GTK_RESPONSE_OK);
g_signal_connect(ok_button,"clicked",G_CALLBACK(gtk_main_quit),0);
GtkWidget *const dialog_hpaned = gtk_hpaned_new();
gtk_widget_show(dialog_hpaned);
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_window)->vbox),dialog_hpaned);
// Create the left pane.
GtkWidget *const left_pane = gtk_vbox_new(false,4);
gtk_widget_show(left_pane);
gtk_paned_pack1(GTK_PANED(dialog_hpaned),left_pane,true,false);
GtkWidget *const left_hbox = gtk_hbox_new(false,4);
gtk_widget_show(left_hbox);
gtk_box_pack_end(GTK_BOX(left_pane),left_hbox,false,false,0);
GtkWidget *const image_align = gtk_alignment_new(0.5,0.5,0,0);
gtk_widget_show(image_align);
gtk_box_pack_start(GTK_BOX(left_hbox),image_align,true,true,0);
CImg<unsigned char> gmic_logo =
CImgList<unsigned char>::get_unserialize(CImg<unsigned char>(data_gmic_logo,1,size_data_gmic_logo,1,1,true))[0];
const unsigned int logo_width = gmic_logo._width, logo_height = gmic_logo._height;
gmic_logo.permute_axes("cxyz");
GdkPixbuf *const pixbuf = gdk_pixbuf_new_from_data(gmic_logo,GDK_COLORSPACE_RGB,
true,8,logo_width,logo_height,4*logo_width,0,0);
GtkWidget *const image = gtk_image_new_from_pixbuf(pixbuf);
gtk_widget_set_tooltip_text(image,
"G'MIC (http://gmic.eu)\n"
"GREYC (http://www.greyc.fr)\n"
"CNRS (http://www.cnrs.fr)\n"
"Normandy University (http://www.normandie-univ.fr)\n"
"ENSICAEN (http://www.ensicaen.fr)");
// GtkWidget *const image_but = gtk_link_button_new_with_label("http://gmic.eu","");
// gtk_widget_show(image_but);
// gtk_button_set_image(GTK_BUTTON(image_but),image); // Doesn't display image on Windows...
gtk_widget_show(image);
gtk_container_add(GTK_CONTAINER(image_align),image);
GtkWidget *const right_align = gtk_alignment_new(1,0,0,0);
gtk_widget_show(right_align);
gtk_box_pack_start(GTK_BOX(left_hbox),right_align,true,true,0);
GtkWidget *const left_frame = gtk_frame_new(NULL);
gtk_widget_show(left_frame);
gtk_container_set_border_width(GTK_CONTAINER(left_frame),4);
gtk_container_add(GTK_CONTAINER(right_align),left_frame);
GtkWidget *const frame_title = gtk_label_new(NULL);
gtk_widget_show(frame_title);
gtk_label_set_markup(GTK_LABEL(frame_title),t("<b> Input / Output </b>"));
gtk_frame_set_label_widget(GTK_FRAME(left_frame),frame_title);
GtkWidget *const left_table = gtk_table_new(5,1,false);
gtk_widget_show(left_table);
gtk_table_set_row_spacings(GTK_TABLE(left_table),6);
gtk_table_set_col_spacings(GTK_TABLE(left_table),6);
gtk_container_set_border_width(GTK_CONTAINER(left_table),8);
gtk_container_add(GTK_CONTAINER(left_frame),left_table);
GtkWidget *const input_combobox = gtk_combo_box_new_text();
gtk_widget_show(input_combobox);
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("Input layers..."));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),"-");
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("None"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("Active (default)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("Active & below"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("Active & above"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All visibles"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All invisibles"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All visibles (decr.)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All invisibles (decr.)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(input_combobox),t("All (decr.)"));
gtk_combo_box_set_active(GTK_COMBO_BOX(input_combobox),get_input_mode(false));
gtk_table_attach_defaults(GTK_TABLE(left_table),input_combobox,0,1,0,1);
g_signal_connect(input_combobox,"changed",G_CALLBACK(on_dialog_input_mode_changed),0);
GtkWidget *const output_combobox = gtk_combo_box_new_text();
gtk_widget_show(output_combobox);
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),t("Output mode..."));
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),"-");
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),t("In place (default)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),t("New layer(s)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),t("New active layer(s)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(output_combobox),t("New image"));
gtk_combo_box_set_active(GTK_COMBO_BOX(output_combobox),get_output_mode(false));
gtk_table_attach_defaults(GTK_TABLE(left_table),output_combobox,0,1,1,2);
g_signal_connect(output_combobox,"changed",G_CALLBACK(on_dialog_output_mode_changed),0);
GtkWidget *const verbosity_combobox = gtk_combo_box_new_text();
gtk_widget_show(verbosity_combobox);
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Output messages..."));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),"-");
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Quiet (default)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Verbose (layer name)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Verbose (console)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Verbose (logfile)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Very verbose (console)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Very verbose (logfile)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Debug mode (console)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(verbosity_combobox),t("Debug mode (logfile)"));
gtk_combo_box_set_active(GTK_COMBO_BOX(verbosity_combobox),get_verbosity_mode(false));
gtk_table_attach_defaults(GTK_TABLE(left_table),verbosity_combobox,0,1,2,3);
g_signal_connect(verbosity_combobox,"changed",G_CALLBACK(on_dialog_verbosity_mode_changed),0);
GtkWidget *const preview_mode_combobox = gtk_combo_box_new_text();
gtk_widget_show(preview_mode_combobox);
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("Preview mode..."));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),"-");
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("1st output (default)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("2nd output"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("3rd output"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("4th output"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("1st -> 2nd"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("1st -> 3rd"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("1st -> 4th"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_mode_combobox),t("All outputs"));
gtk_combo_box_set_active(GTK_COMBO_BOX(preview_mode_combobox),get_preview_mode(false));
gtk_table_attach_defaults(GTK_TABLE(left_table),preview_mode_combobox,0,1,3,4);
g_signal_connect(preview_mode_combobox,"changed",G_CALLBACK(on_dialog_preview_mode_changed),0);
GtkWidget *preview_size_combobox = gtk_combo_box_new_text();
gtk_widget_show(preview_size_combobox);
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),t("Preview size..."));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),"-");
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),t("Tiny"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),t("Small"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),t("Normal"));
gtk_combo_box_append_text(GTK_COMBO_BOX(preview_size_combobox),t("Large"));
gtk_combo_box_set_active(GTK_COMBO_BOX(preview_size_combobox),get_preview_size(false));
gtk_table_attach_defaults(GTK_TABLE(left_table),preview_size_combobox,0,1,4,5);
g_signal_connect(preview_size_combobox,"changed",G_CALLBACK(on_dialog_preview_size_changed),0);
gui_preview_align = gtk_alignment_new(0.5,0.5,0,0);
gtk_widget_show(gui_preview_align);
gtk_box_pack_end(GTK_BOX(left_pane),gui_preview_align,true,true,0);
gui_preview = 0;
_gimp_preview_invalidate();
g_signal_connect(dialog_window,"size-request",G_CALLBACK(on_dialog_resized),0);
// Create the middle pane.
GtkWidget *const mr_hpaned = gtk_hpaned_new();
gtk_widget_show(mr_hpaned);
gtk_paned_pack2(GTK_PANED(dialog_hpaned),mr_hpaned,true,true);
GtkWidget *const middle_frame = gtk_frame_new(NULL);
gtk_widget_show(middle_frame);
gtk_container_set_border_width(GTK_CONTAINER(middle_frame),4);
gtk_paned_add1(GTK_PANED(mr_hpaned),middle_frame);
GtkWidget *const middle_pane = gtk_vbox_new(false,4);
gtk_widget_show(middle_pane);
gtk_container_add(GTK_CONTAINER(middle_frame),middle_pane);
relabel_hbox = gtk_hbox_new(false,3);
gtk_box_pack_start(GTK_BOX(middle_pane),relabel_hbox,false,false,0);
relabel_entry = gtk_entry_new_with_max_length(255);
gtk_widget_show(relabel_entry);
gtk_box_pack_start(GTK_BOX(relabel_hbox),relabel_entry,false,true,0);
GtkWidget *const relabel_button = gtk_button_new_with_label(t("Rename"));
gtk_widget_show(relabel_button);
gtk_box_pack_start(GTK_BOX(relabel_hbox),relabel_button,false,true,0);
g_signal_connect(relabel_button,"clicked",G_CALLBACK(_on_filter_doubleclicked),0);
g_signal_connect(relabel_entry,"activate",G_CALLBACK(_on_filter_doubleclicked),0);
GtkWidget *const scrolled_window = gtk_scrolled_window_new(NULL,NULL);
gtk_widget_show(scrolled_window);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(middle_pane),scrolled_window,true,true,0);
tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_view_store));
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tree_view),true);
gtk_widget_show(tree_view);
gtk_container_add(GTK_CONTAINER(scrolled_window),tree_view);
GtkWidget *const tree_hbox = gtk_hbox_new(false,6);
gtk_widget_show(tree_hbox);
gtk_box_pack_start(GTK_BOX(middle_pane),tree_hbox,false,false,0);
fave_add_button = gtk_button_new();
gtk_box_pack_start(GTK_BOX(tree_hbox),fave_add_button,false,false,0);
g_signal_connect_swapped(fave_add_button,"clicked",G_CALLBACK(on_dialog_add_fave_clicked),tree_view);
fave_delete_button = gtk_button_new();
gtk_box_pack_start(GTK_BOX(tree_hbox),fave_delete_button,false,false,0);
g_signal_connect_swapped(fave_delete_button,"clicked",G_CALLBACK(on_dialog_remove_fave_clicked),tree_view);
delete_stock = gtk_button_new_from_stock(GTK_STOCK_DELETE);
GtkWidget *const delete_image = gtk_button_get_image(GTK_BUTTON(delete_stock));
gtk_button_set_image(GTK_BUTTON(fave_delete_button),delete_image);
GtkWidget *const refresh_button = gtk_button_new();
refresh_stock = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
GtkWidget *const refresh_image = gtk_button_get_image(GTK_BUTTON(refresh_stock));
gtk_button_set_image(GTK_BUTTON(refresh_button),refresh_image);
gtk_widget_show(refresh_button);
gtk_box_pack_start(GTK_BOX(tree_hbox),refresh_button,false,false,0);
g_signal_connect_swapped(refresh_button,"clicked",G_CALLBACK(on_dialog_refresh_clicked),tree_view);
GtkWidget *const internet_checkbutton = gtk_check_button_new_with_mnemonic(t("Internet"));
gtk_widget_show(internet_checkbutton);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(internet_checkbutton),get_net_update());
gtk_box_pack_start(GTK_BOX(tree_hbox),internet_checkbutton,false,false,0);
g_signal_connect(internet_checkbutton,"toggled",G_CALLBACK(on_dialog_net_update_toggled),0);
tree_mode_button = gtk_button_new();
gtk_box_pack_start(GTK_BOX(tree_hbox),tree_mode_button,false,false,0);
g_signal_connect_swapped(tree_mode_button,"clicked",G_CALLBACK(on_dialog_tree_mode_clicked),tree_view);
GtkTreeViewColumn *const column = gtk_tree_view_column_new();
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view),column);
flush_tree_view(tree_view);
GtkRequisition requisition;
gtk_widget_size_request((GtkWidget*)tree_view,&requisition);
gtk_widget_set_size_request((GtkWidget*)tree_view,cimg::max(210,requisition.width),-1);
g_signal_connect(tree_view,"cursor-changed",G_CALLBACK(on_filter_selected),0);
g_signal_connect(tree_view,"row-activated",G_CALLBACK(on_filter_doubleclicked),0);
// Create the right pane.
right_pane = gtk_scrolled_window_new(NULL,NULL);
gtk_widget_show(right_pane);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(right_pane),
GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
gtk_paned_add2(GTK_PANED(mr_hpaned),right_pane);
right_frame = gtk_frame_new(NULL);
gtk_widget_show(right_frame);
gtk_container_set_border_width(GTK_CONTAINER(right_frame),4);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(right_pane),right_frame);
// Define tooltips.
gtk_widget_set_tooltip_text(fave_add_button,t("Add filter to faves"));
gtk_widget_set_tooltip_text(fave_delete_button,t("Remove filter from faves"));
gtk_widget_set_tooltip_text(refresh_button,t("Update filters"));
gtk_widget_set_tooltip_text(internet_checkbutton,t("Enable Internet updates"));
gtk_widget_set_tooltip_text(tree_mode_button,t("Expand/collapse"));
// Finalize dialog window.
create_parameters_gui(false);
gtk_widget_grab_focus(tree_view);
// Make sure the dialog window is entirely visible on screen.
unsigned int preview_size = get_preview_size(false);
for (bool is_entirely_visible = false; !is_entirely_visible && preview_size>2; ) {
GdkScreen *const screen = gtk_window_get_screen(GTK_WINDOW(dialog_window));
int
window_width, window_height,
screen_width = gdk_screen_get_width(screen),
screen_height = gdk_screen_get_height(screen);
gtk_window_get_size(GTK_WINDOW(dialog_window),&window_width,&window_height);
if (window_width>screen_width || window_height>screen_height) resize_preview(--preview_size - 2);
else { resize_preview(preview_size - 2); is_entirely_visible = true; }
}
gtk_widget_show(dialog_window);
#if GIMP_MINOR_VERSION>8
//if(!dpy_profile)
// dpy_profile = gimp_color_profile_new_rgb_srgb ();
// Get color profiles
//GimpColorConfig* color_config = gimp_get_color_configuration();
//if(color_config)
//dpy_profile = get_display_profile(dialog_window,color_config);
//else
//dpy_profile = NULL;
#endif
gtk_main();
// Destroy dialog box widget and free resources.
gtk_widget_destroy(dialog_window);
if (tree_mode_stock) gtk_widget_destroy(tree_mode_stock);
if (fave_stock) gtk_widget_destroy(fave_stock);
if (delete_stock) gtk_widget_destroy(delete_stock);
if (refresh_stock) gtk_widget_destroy(refresh_stock);
if (event_infos) delete[] event_infos;
return _create_dialog_gui;
}
// 'Run' function, required by the GIMP plug-in API.
//--------------------------------------------------
void gmic_run(const gchar *name, gint nparams, const GimpParam *param,
gint *nreturn_vals, GimpParam **return_vals) {
gimp_color_config_get_type();
// Init plug-in variables.
#if GIMP_MINOR_VERSION>8
gegl_init(NULL,NULL);
gimp_plugin_enable_precision();
#endif
markup2ascii = gtk_label_new(0);
static GimpParam return_values[1];
*return_vals = return_values;
*nreturn_vals = 1;
return_values[0].type = GIMP_PDB_STATUS;
cimg::unused(name,nparams);
run_mode = (GimpRunMode)param[0].data.d_int32;
set_logfile();
set_locale();
status = GIMP_PDB_SUCCESS;
#if cimg_OS==2
cimg::curl_path("_gmic\\curl",true);
#endif
unsigned int preview_size = get_preview_size(false);
if (!preview_size) { // Try to guess best preview size.
unsigned int h = 0;
try {
const unsigned int cimg_exception_mode = cimg::exception_mode();
cimg::exception_mode(0);
h = CImgDisplay::screen_height();
cimg::exception_mode(cimg_exception_mode);
} catch (...) { h = 800; }
preview_size = h>=1024?4:h>=800?3:2;
set_preview_size(preview_size);
}
// Create resources directory.
if (!gmic::init_rc()) {
std::fprintf(cimg::output(),
"\n[gmic_gimp]./error/ Unable to create resources directory 'gmicrc'.\n");
std::fflush(cimg::output());
}
// Update filters.
update_filters(false,true);
try {
image_id = param[1].data.d_drawable;
#if GIMP_MINOR_VERSION<=8
gimp_tile_cache_ntiles(2*(gimp_image_width(image_id)/gimp_tile_width() + 1));
#endif
// Check for run mode.
switch (run_mode) {
case GIMP_RUN_INTERACTIVE : {
// Try updating filters automatically if necessary (every week).
CImg<char> str(std::strlen(gmic::path_rc()) + 32);
cimg_snprintf(str,str.width(),"%sgimp_update.lock",
gmic::path_rc());
bool try_network_update = true;
gimp_get_data("gmic_lock_update",&try_network_update);
if (try_network_update) {
const int date = cimg::date(2), fdate = cimg::fdate(str,2);
if (fdate>=0 && cimg::mod(date - fdate,31)<7) try_network_update = false;
}
if (try_network_update) {
update_filters(true,true);
std::FILE *lockfile = std::fopen(str,"wb");
if (lockfile) std::fclose(lockfile);
try_network_update = false;
gimp_set_data("gmic_lock_update",&try_network_update,sizeof(bool));
}
// Display dialog window.
if (create_dialog_gui()) {
cimg::mutex(25);
if (p_spt) { st_process_thread &spt = *(st_process_thread*)p_spt; spt.is_abort = true; }
cimg::mutex(25,0);
process_image(0,false);
const CImg<char> command_line = get_command_line(false);
if (command_line) { // Remember command line for the next use of the filter.
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_command_line%u",get_current_filter());
gimp_set_data(s_tmp,command_line,std::strlen(command_line) + 1);
}
}
} break;
case GIMP_RUN_WITH_LAST_VALS : {
const unsigned int filter = get_current_filter();
if (filter) {
CImg<char> s_tmp(48);
cimg_snprintf(s_tmp,s_tmp.width(),"gmic_command_line%u",filter);
const unsigned int siz = 1U + gimp_get_data_size(s_tmp);
CImg<char> command_line(2*siz); *command_line = 0;
gimp_get_data(s_tmp,command_line);
process_image(command_line,false);
const CImg<char> next_command_line = get_command_line(false);
// Remember command line for the next use of the filter.
if (next_command_line) gimp_set_data(s_tmp,next_command_line,std::strlen(next_command_line) + 1);
}
} break;
case GIMP_RUN_NONINTERACTIVE : {
const unsigned int _input_mode = get_input_mode(), _output_mode = get_output_mode();
set_input_mode(param[3].data.d_int32 + 2);
set_output_mode(0);
process_image(param[4].data.d_string,false);
set_input_mode(_input_mode + 2);
set_output_mode(_output_mode + 2);
} break;
}
} catch (CImgException &e) {
GtkWidget *const
message = gtk_message_dialog_new(0,GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,"%s",e.what());
gtk_widget_show(message);
gtk_dialog_run(GTK_DIALOG(message));
gtk_widget_destroy(message);
status = GIMP_PDB_CALLING_ERROR;
}
gtk_widget_destroy(markup2ascii);
if (preview_image_id) gimp_image_delete(preview_image_id);
if (logfile) std::fclose(logfile);
return_values[0].data.d_status = status;
}
// 'Query' function, required by the GIMP plug-in API.
//----------------------------------------------------
void gmic_query() {
static const GimpParamDef args[] = {
{GIMP_PDB_INT32, (gchar*)"run_mode", (gchar*)"Interactive, non-interactive"},
{GIMP_PDB_IMAGE, (gchar*)"image", (gchar*)"Input image"},
{GIMP_PDB_DRAWABLE, (gchar*)"drawable", (gchar*)"Input drawable (unused)"},
{GIMP_PDB_INT32, (gchar*)"input", (gchar*)"Input layers mode, when non-interactive"
"(0=none, 1=active, 2=all, 3=active & below, 4=active & above, 5=all visibles, 6=all invisibles, "
"7=all visibles (decr.), 8=all invisibles (decr.), 9=all (decr.))"},
{GIMP_PDB_STRING, (gchar*)"command", (gchar*)"G'MIC command string, when non-interactive"},
};
set_locale();
gimp_install_procedure("plug-in-gmic", // name
"G'MIC", // blurb
"G'MIC", // help
"David Tschumperl\303\251", // author
"David Tschumperl\303\251", // copyright
"2015", // date
"_G'MIC...", // menu_path
"RGB*, GRAY*", // image_types
GIMP_PLUGIN, // type
G_N_ELEMENTS(args), // nparams
0, // nreturn_vals
args, // params
0); // return_vals
gimp_plugin_menu_register("plug-in-gmic", "<Image>/Filters");
}
GimpPlugInInfo PLUG_IN_INFO = { 0, 0, gmic_query, gmic_run };
MAIN()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment