From e16ab05ff4126f0f5eed105d6028a208a349765e Mon Sep 17 00:00:00 2001 From: Morxemplum Date: Fri, 17 Jun 2022 17:34:17 -0700 Subject: [PATCH] Add AVIF support --- src/bimp-gui.c | 2360 +++++++++--------- src/bimp-manipulations.h | 575 ++--- src/bimp-operate.c | 2955 ++++++++++++----------- src/bimp-serialize.c | 2047 ++++++++-------- src/manipulation-gui/gui-changeformat.c | 1036 ++++---- 5 files changed, 4531 insertions(+), 4442 deletions(-) diff --git a/src/bimp-gui.c b/src/bimp-gui.c index 0887070..2a6e4d5 100644 --- a/src/bimp-gui.c +++ b/src/bimp-gui.c @@ -1,1177 +1,1183 @@ -/* - * Functions used to build and return the main BIMP user interface - */ - -#include -#include -#include -#include -#include -#include -#include -#include "bimp.h" -#include "bimp-manipulations.h" -#include "bimp-gui.h" -#include "bimp-manipulations-gui.h" -#include "bimp-operate.h" -#include "bimp-serialize.h" -#include "bimp-utils.h" -#include "plugin-intl.h" - -static GtkWidget* sequence_panel_new(void); -static GtkWidget* option_panel_new(void); -static void init_fileview(void); -static void add_to_fileview(char*); -static GSList* get_treeview_selection(); -static void open_file_chooser(GtkWidget*, gpointer); -static void open_folder_chooser(GtkWidget*, gpointer); -static void add_input_file(char*); -static void add_input_folder(char*, gpointer); -static void add_opened_files(void); -static void remove_input_file(GtkWidget*, gpointer); -static void remove_all_input_files(GtkWidget*, gpointer); -static void select_filename (GtkTreeView*, gpointer); -static void update_selection(char*); -static void show_preview(GtkTreeView*, gpointer); -static char* get_outputfolder_name(); -static void open_outputfolder_chooser(GtkWidget*, gpointer); -static void set_source_output_folder(GtkWidget*, gpointer); -static void popmenus_init(void); -static void open_manipulation_popupmenu(GtkWidget*, gpointer); -static void open_addfiles_popupmenu(GtkWidget*, gpointer); -static void open_removefiles_popupmenu(GtkWidget*, gpointer); -static void add_manipulation_from_id(GtkMenuItem*, gpointer); -static void edit_clicked_manipulation(GtkMenuItem*, gpointer); -static void remove_clicked_manipulation(GtkMenuItem*, gpointer); -static void add_manipulation_button(manipulation); - -static void save_set(GtkMenuItem*, gpointer); -static void load_set(GtkMenuItem*, gpointer); - -static void open_about(); -static const gchar* progressbar_init_hidden (void); -static void progressbar_start_hidden(const gchar*, gboolean, gpointer); -static void progressbar_end_hidden (gpointer); -static void progressbar_settext_hidden (const gchar*, gpointer); -static void progressbar_setvalue_hidden (double, gpointer); - -GtkWidget* bimp_window_main; - -static GtkWidget *panel_sequence, *panel_options; -static GtkWidget *hbox_sequence; -static GtkWidget *scroll_sequence; -static GtkWidget *popmenu_add, *popmenu_edit, *popmenu_addfiles, *popmenu_removefiles; -static GtkWidget *check_keepfolderhierarchy, *check_deleteondone, *check_keepdates; -static GtkWidget *treeview_files; -static GtkWidget *button_preview, *button_outfolder, *button_samefolder; -static GtkWidget* progressbar_visible; - -static char* selected_source_folder; -static char* last_input_location; -static const gchar* progressbar_data; - -enum /* TreeView stuff... */ -{ - LIST_ITEM = 0, - N_COLUMNS -}; - -static manipulation clicked_man; /* temporary manipulation, selected by clicking on panel_seq buttons */ - -void bimp_show_gui() -{ - GtkWidget* vbox_main; - - gimp_ui_init (PLUG_IN_BINARY, FALSE); - - bimp_window_main = gimp_dialog_new( - PLUG_IN_FULLNAME, - PLUG_IN_BINARY, - NULL, - 0, - NULL, - NULL, - GTK_STOCK_ABOUT, GTK_RESPONSE_HELP, - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, - GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, - GTK_STOCK_STOP, GTK_RESPONSE_CANCEL, NULL - ); - - gimp_window_set_transient (GTK_WINDOW(bimp_window_main)); - gtk_window_set_position(GTK_WINDOW(bimp_window_main), GTK_WIN_POS_CENTER); - gtk_window_set_default_size(GTK_WINDOW(bimp_window_main), (int)(PREVIEW_IMG_W * 2.5), SEQ_BUTTON_H + PREVIEW_IMG_H + 160); - gtk_container_set_border_width(GTK_CONTAINER(bimp_window_main), 5); - - // Forces the visualization of label AND images on buttons (especially for Windows) - GtkSettings *default_settings = gtk_settings_get_default(); - g_object_set(default_settings, "gtk-button-images", TRUE, NULL); - - vbox_main = gtk_vbox_new(FALSE, 10); - panel_sequence = sequence_panel_new(); - panel_options = option_panel_new(); - - progressbar_visible = gtk_progress_bar_new(); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), " "); - progressbar_data = progressbar_init_hidden(); - - gtk_box_pack_start(GTK_BOX(vbox_main), panel_sequence, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_main), panel_options, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(vbox_main), progressbar_visible, FALSE, FALSE, 0); - - gtk_container_add (GTK_CONTAINER (GTK_DIALOG(bimp_window_main)->vbox), vbox_main); - gtk_widget_show_all(bimp_window_main); - gtk_widget_hide(button_preview); - - bimp_set_busy(FALSE); - - while(TRUE) { - gint run = gimp_dialog_run (GIMP_DIALOG(bimp_window_main)); - if (run == GTK_RESPONSE_APPLY) { - if (g_slist_length(bimp_selected_manipulations) == 0) { - bimp_show_error_dialog(_("The manipulations set is empty!"), bimp_window_main); - } else { - if (g_slist_length(bimp_input_filenames) == 0) { - bimp_show_error_dialog(_("The file list is empty!"), bimp_window_main); - } - else { - bimp_opt_alertoverwrite = BIMP_ASK_OVERWRITE; - bimp_opt_keepfolderhierarchy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_keepfolderhierarchy)); - bimp_opt_deleteondone = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_deleteondone)); - bimp_opt_keepdates = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_keepdates)); - bimp_start_batch(bimp_window_main); - } - } - } - else if (run == GTK_RESPONSE_HELP) { - open_about(); - } - else if (run == GTK_RESPONSE_CANCEL) { - bimp_set_busy(FALSE); - } - else { - gimp_progress_uninstall(progressbar_data); - gtk_widget_destroy (bimp_window_main); - return; - } - } -} - -/* builds and returns the upper panel with the manipulation buttons */ -static GtkWidget* sequence_panel_new() -{ - GtkWidget *panel; - - panel = gtk_frame_new(_("Manipulation set")); - - scroll_sequence = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_sequence), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - - hbox_sequence = gtk_hbox_new(FALSE, 10); - - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_sequence), hbox_sequence); - gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scroll_sequence))), GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(panel), scroll_sequence); - - bimp_refresh_sequence_panel(); - popmenus_init(); - - return panel; -} - -/* builds and returns the panel with file list and options */ -static GtkWidget* option_panel_new() -{ - GtkWidget *panel, *table; - GtkWidget *scroll_input; - GtkWidget *button_add, *button_remove; - - GtkWidget *vbox_useroptions, *hbox_outfolder; - GtkWidget *label_chooser; - - panel = gtk_frame_new(_("Input files and options")); - table = gtk_table_new(2, 3, FALSE); - gtk_table_set_row_spacings(GTK_TABLE(table), 5); - gtk_table_set_col_spacings(GTK_TABLE(table), 5); - - /* Sub-sub-panel for input file listing */ - scroll_input = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_input), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - treeview_files = gtk_tree_view_new(); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_files), FALSE); - gtk_tree_selection_set_mode (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files)), GTK_SELECTION_MULTIPLE); - - button_add = gtk_button_new_with_label(_("Add images")); - button_remove = gtk_button_new_with_label(_("Remove images")); - - /* Sub-panel for options */ - vbox_useroptions = gtk_vbox_new(FALSE, 3); - - hbox_outfolder = gtk_hbox_new(FALSE, 3); - label_chooser = gtk_label_new(g_strconcat(_("Output folder"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_chooser), 0, .5); - - bimp_output_folder = get_user_dir(); - - - button_outfolder = gtk_button_new_with_label(get_outputfolder_name()); - - gtk_widget_set_tooltip_text (button_outfolder, bimp_output_folder); - - button_samefolder = gtk_button_new(); - GtkWidget* samefolder_icon = gtk_image_new_from_stock(GTK_STOCK_UNDO, GTK_ICON_SIZE_BUTTON); - gtk_button_set_image(GTK_BUTTON(button_samefolder), samefolder_icon); - gtk_widget_set_tooltip_text (button_samefolder, _("Use the selected file's location as the output")); - - bimp_opt_alertoverwrite = BIMP_ASK_OVERWRITE; - //check_alertoverwrite = gtk_check_button_new_with_label(_("Alert when overwriting existing files")); - //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_alertoverwrite), TRUE); - - bimp_opt_keepfolderhierarchy = FALSE; - check_keepfolderhierarchy = gtk_check_button_new_with_label(_("Keep folder hierarchy")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_keepfolderhierarchy), bimp_opt_keepfolderhierarchy); - - /* TODO? */ - bimp_opt_deleteondone = FALSE; - check_deleteondone = gtk_check_button_new_with_label(_("Delete original file when done")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_deleteondone), bimp_opt_deleteondone); - - bimp_opt_keepdates = FALSE; - check_keepdates = gtk_check_button_new_with_label(_("Keep the modification dates")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_keepdates), bimp_opt_keepdates); - - button_preview = gtk_button_new(); - gtk_button_set_image_position (GTK_BUTTON(button_preview), GTK_POS_TOP); - gtk_button_set_label(GTK_BUTTON(button_preview), _("Click for preview")); - - /* All together */ - - gtk_container_add (GTK_CONTAINER(scroll_input), treeview_files); - - gtk_box_pack_start(GTK_BOX(vbox_useroptions), label_chooser, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_outfolder), button_outfolder, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_outfolder), button_samefolder, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_useroptions), hbox_outfolder, FALSE, FALSE, 0); - - //gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_alertoverwrite, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_keepfolderhierarchy, FALSE, FALSE, 0); - // TODO: delete on done? gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_deleteondone, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_keepdates, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_useroptions), button_preview, FALSE, FALSE, 2); - - gtk_table_attach_defaults(GTK_TABLE(table), scroll_input, 0, 2, 0, 1); - gtk_table_attach(GTK_TABLE(table), button_add, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); - gtk_table_attach(GTK_TABLE(table), button_remove, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); - - gtk_table_attach(GTK_TABLE(table), vbox_useroptions, 2, 3, 0, 2, GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0); - - gtk_container_add(GTK_CONTAINER(panel), table); - - g_signal_connect(G_OBJECT(button_add), "clicked", G_CALLBACK(open_addfiles_popupmenu), NULL); - g_signal_connect(G_OBJECT(button_remove), "clicked", G_CALLBACK(open_removefiles_popupmenu), NULL); - g_signal_connect(G_OBJECT(button_outfolder), "clicked", G_CALLBACK(open_outputfolder_chooser), NULL); - g_signal_connect(G_OBJECT(button_samefolder), "clicked", G_CALLBACK(set_source_output_folder), NULL); - g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files))), "changed", G_CALLBACK(select_filename), NULL); - g_signal_connect(G_OBJECT(button_preview), "clicked", G_CALLBACK(show_preview), NULL); - - init_fileview(); - bimp_refresh_fileview(); - - return panel; -} - -/* following: functions that modify the file list widget (addfile/addfolder/remove/removeall) */ - -static void add_input_file(char* filename) -{ - if (g_slist_find_custom(bimp_input_filenames, filename, (GCompareFunc)strcmp) == NULL) { - bimp_input_filenames = g_slist_append(bimp_input_filenames, filename); - bimp_refresh_fileview(); - } -} - -/* Recursive function to add all files from the hierarchy if desired */ -static void add_input_folder_r(char* folder, gboolean with_subdirs) -{ - GDir *dp; - const gchar* entry; - dp = g_dir_open (folder, 0, NULL); - - if (dp != NULL) { - while ((entry = g_dir_read_name (dp))) { - - char* filename = g_strconcat(folder, FILE_SEPARATOR_STR, entry, NULL); - char* file_extension = g_strdup(strrchr(filename, '.')); - GError *error; - GFileInfo *file_info = g_file_query_info (g_file_new_for_path(filename), "standard::*", 0, NULL, &error); - - /* Folder processing */ - if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_DIRECTORY){ - if (g_strcmp0(entry, ".") == 0 || g_strcmp0(entry, "..") == 0) - continue; - if (with_subdirs) - add_input_folder_r(filename, with_subdirs); - continue; - } - - if (( - g_ascii_strcasecmp(file_extension, ".bmp") == 0 || - g_ascii_strcasecmp(file_extension, ".jpeg") == 0 || - g_ascii_strcasecmp(file_extension, ".jpg") == 0 || - g_ascii_strcasecmp(file_extension, ".jpe") == 0 || - g_ascii_strcasecmp(file_extension, ".jp2") == 0 || - g_ascii_strcasecmp(file_extension, ".gif") == 0 || - g_ascii_strcasecmp(file_extension, ".heif") == 0 || - g_ascii_strcasecmp(file_extension, ".heic") == 0 || - g_ascii_strcasecmp(file_extension, ".png") == 0 || - g_ascii_strcasecmp(file_extension, ".tif") == 0 || - g_ascii_strcasecmp(file_extension, ".tiff") == 0 || - g_ascii_strcasecmp(file_extension, ".tga") == 0 || - g_ascii_strcasecmp(file_extension, ".svg") == 0 || - g_ascii_strcasecmp(file_extension, ".webp") == 0 || - g_ascii_strcasecmp(file_extension, ".xpm") == 0 || - g_ascii_strcasecmp(file_extension, ".exr") == 0 || - g_ascii_strcasecmp(file_extension, ".dds") == 0 || - g_ascii_strcasecmp(file_extension, ".xcf") == 0) && - g_slist_find_custom(bimp_input_filenames, filename, (GCompareFunc)strcmp) == NULL) - { - bimp_input_filenames = g_slist_append(bimp_input_filenames, filename); - } - } - g_dir_close (dp); - } - else { - bimp_show_error_dialog(g_strdup_printf(_("Couldn't read into \"%s\" directory."), folder), bimp_window_main); - } -} - -static void add_opened_files() -{ - gint num_images = 0; - int* image_ids = gimp_image_list (&num_images); - int i; - gboolean missing = FALSE; - for (i = 0; i < num_images; i++) { - gchar* uri = gimp_image_get_uri(image_ids[i]); - if (uri != NULL) { - gchar* path = g_filename_from_uri(uri, NULL, NULL); - if (path != NULL) add_input_file (path); - } - else missing = TRUE; - } - - if (missing) { - bimp_show_error_dialog(g_strdup_printf(_("Some images were not imported because they have not been saved on filesystem yet.")), bimp_window_main); - } - - g_free(image_ids); -} - -static void add_input_folder(char* folder, gpointer with_subdirs) -{ - add_input_folder_r(folder, (gboolean)GPOINTER_TO_INT(with_subdirs)); - bimp_refresh_fileview(); -} - -/* returns the list of currently selected filenames (NULL of none) */ -static GSList* get_treeview_selection() -{ - GtkTreeModel *model; - GList *selected_rows = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files)), &model); - GList *i = NULL; - GSList *out = NULL; - - if (selected_rows != NULL) { - for (i = selected_rows; i != NULL; i = g_list_next(i) ) { - GtkTreeIter iter; - char* selected_i; - if (gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)i->data) == TRUE) { - gtk_tree_model_get(model, &iter, LIST_ITEM, &selected_i, -1); - out = g_slist_append(out, selected_i); - } - } - - g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); - g_list_free (selected_rows); - } - - return out; -} - -static void remove_input_file(GtkWidget *widget, gpointer data) -{ - GSList *selection = get_treeview_selection(); - GSList *i; - - if (selection != NULL) { - for (i = selection; i != NULL; i = g_slist_next(i) ) { - bimp_input_filenames = g_slist_delete_link(bimp_input_filenames, g_slist_find_custom(bimp_input_filenames, (char*)(i->data), (GCompareFunc)strcmp)); - } - - bimp_refresh_fileview(); - update_selection(NULL); /* clear the preview widget */ - } -} - -static void remove_all_input_files(GtkWidget *widget, gpointer data) -{ - g_slist_free(bimp_input_filenames); - bimp_input_filenames = NULL; - bimp_refresh_fileview(); - update_selection(NULL); -} - -/* called when the user clicks on a filename row to update the preview widget */ -static void select_filename (GtkTreeView *tree_view, gpointer data) -{ - GSList *selection = get_treeview_selection(); - - if (selection != NULL && g_slist_length(selection) == 1) { - update_selection((gchar*)(selection->data)); - } - else { - update_selection(NULL); - } -} - -/* updates the GUI according to the current selected filename */ -static void update_selection (gchar* filename) -{ - g_free(selected_source_folder); - if (filename != NULL) { - // update preview - GdkPixbuf *pixbuf_prev = gdk_pixbuf_new_from_file_at_scale(filename, FILE_PREVIEW_W - 20, FILE_PREVIEW_H - 30, TRUE, NULL); - gtk_button_set_image(GTK_BUTTON(button_preview), gtk_image_new_from_pixbuf (pixbuf_prev)); - gtk_widget_show(button_preview); - - // update current selection - selected_source_folder = g_path_get_dirname(filename); - } else { - // invalidate - gtk_button_set_image(GTK_BUTTON(button_preview), NULL); - gtk_widget_hide(button_preview); - selected_source_folder = NULL; - } - - gtk_widget_set_sensitive(button_samefolder, (selected_source_folder != NULL)); -} - -/* opens a dialog containing two panels: the original selected image on the left and the result of the selected manipulations on the right */ -static void show_preview (GtkTreeView *tree_view, gpointer data) -{ - if (g_slist_length(bimp_selected_manipulations) == 0) { - bimp_show_error_dialog(_("Can't show a preview because the manipulations set is empty"), bimp_window_main); - } else { - GtkWidget *dialog_preview; - GtkWidget *vbox, *hbox, *align; - GtkWidget *label_descr; - GdkPixbuf *pixbuf_orig, *pixbuf_final; - GtkWidget *image_orig, *image_final; - GtkWidget *image_forward; - - GSList* selection = get_treeview_selection(); - if (selection == NULL && g_slist_length(selection) != 1) return; - - char* selected_str = g_slist_nth_data(selection, 0); - - image_output imageout_orig = (image_output)g_malloc(sizeof(struct imageout_str)); - image_output imageout_final = (image_output)g_malloc(sizeof(struct imageout_str)); - - imageout_orig->image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE, (gchar*)selected_str, (gchar*)selected_str); - int imageout_orig_drawable = gimp_image_merge_visible_layers(imageout_orig->image_id, GIMP_CLIP_TO_IMAGE); - - bimp_init_batch(); - bimp_apply_drawable_manipulations(imageout_final, (gchar*)selected_str, (gchar*)selected_str); - int imageout_final_drawable = gimp_image_merge_visible_layers(imageout_final->image_id, GIMP_CLIP_TO_IMAGE); - - pixbuf_orig = gimp_drawable_get_thumbnail(imageout_orig_drawable, PREVIEW_IMG_W, PREVIEW_IMG_H, GIMP_PIXBUF_KEEP_ALPHA); - pixbuf_final = gimp_drawable_get_thumbnail(imageout_final_drawable, PREVIEW_IMG_W, PREVIEW_IMG_H, GIMP_PIXBUF_KEEP_ALPHA); - - dialog_preview = gtk_dialog_new_with_buttons ( - _("Preview"), - GTK_WINDOW(bimp_window_main), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, NULL - ); - //gtk_widget_set_size_request (dialog_preview, PREVIEW_WINDOW_W, PREVIEW_WINDOW_H); - gtk_window_set_resizable (GTK_WINDOW(dialog_preview), FALSE); - gtk_window_set_position(GTK_WINDOW(dialog_preview), GTK_WIN_POS_CENTER); - gtk_container_set_border_width(GTK_CONTAINER(dialog_preview), 5); - - vbox = gtk_vbox_new(FALSE, 10); - label_descr = gtk_label_new(_("This is how the selected image will look like after the batch process")); - gtk_label_set_line_wrap (GTK_LABEL(label_descr), TRUE); - gtk_label_set_justify(GTK_LABEL(label_descr), GTK_JUSTIFY_CENTER); - - align = gtk_alignment_new(0.5, 0.5, 0, 0); - - hbox = gtk_hbox_new(FALSE, 10); - image_orig = gtk_image_new_from_pixbuf(pixbuf_orig); - image_forward = gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON); - image_final = gtk_image_new_from_pixbuf(pixbuf_final); - - gtk_box_pack_start(GTK_BOX(hbox), image_orig, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox), image_forward, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox), image_final, FALSE, FALSE, 0); - gtk_misc_set_alignment(GTK_MISC(hbox), 0, .5); - - gtk_box_pack_start(GTK_BOX(vbox), label_descr, FALSE, FALSE, 7); - gtk_container_add(GTK_CONTAINER(align), hbox); - gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, FALSE, 0); - - gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog_preview)->vbox), vbox); - gtk_widget_show_all(dialog_preview); - - if (gtk_dialog_run (GTK_DIALOG(dialog_preview)) == GTK_RESPONSE_CLOSE) { - gtk_widget_destroy (dialog_preview); - gimp_image_delete(imageout_orig->image_id); - gimp_image_delete(imageout_final->image_id); - g_free(imageout_orig); - g_free(imageout_final); - } - } -} - -static void init_fileview() -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkListStore *store; - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - "List Items", - renderer, - "text", - LIST_ITEM, - NULL - ); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_files), column); - - store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); - gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_files), GTK_TREE_MODEL(store)); - g_object_unref(store); -} - -static void add_to_fileview(char *str) -{ - GtkListStore *store; - GtkTreeIter iter; - - store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview_files))); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, LIST_ITEM, str, -1); -} - -/* update the visual of filename list */ -void bimp_refresh_fileview() -{ - GtkListStore *store; - GtkTreeModel *model; - GtkTreeIter treeiter; - - store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (treeview_files))); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview_files)); - - /* clear all rows in list */ - if (gtk_tree_model_get_iter_first(model, &treeiter) == TRUE) { - gtk_list_store_clear(store); - } - - GSList *iter; - if (g_slist_length(bimp_input_filenames) > 0) { - iter = bimp_input_filenames; - for (; iter; iter = iter->next) { - add_to_fileview(iter->data); - } - } -} - -static void open_file_chooser(GtkWidget *widget, gpointer data) -{ - GSList *selection; - - GtkFileFilter *filter_all, *supported[15]; - - GtkWidget* file_chooser = gtk_file_chooser_dialog_new( - _("Select images"), - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, - GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL - ); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_chooser), TRUE); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser), last_input_location); - - filter_all = gtk_file_filter_new(); - gtk_file_filter_set_name(filter_all,_("All supported types")); - - supported[0] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[0], "Bitmap (*.bmp)"); - gtk_file_filter_add_pattern (supported[0], "*.[bB][mM][pP]"); - gtk_file_filter_add_pattern (filter_all, "*.[bB][mM][pP]"); - - supported[1] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[1], "DDS (*.dds)"); - gtk_file_filter_add_pattern (supported[1], "*.[dD][dD][sS]"); - gtk_file_filter_add_pattern (filter_all, "*.[dD][dD][sS]"); - - supported[2] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[2], "JPEG (*.jpg, *.jpeg, *jpe)"); - gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][gG]"); - gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][eE][gG]"); - gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][eE]"); - gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][gG]"); - gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][eE][gG]"); - gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][eE]"); - - supported[3] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[3], "JPEG2000 (*.jp2)"); - gtk_file_filter_add_pattern (supported[3], "*.[jJ][pP][2]"); - - supported[4] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[4], "GIF (*.gif)"); - gtk_file_filter_add_pattern (supported[4], "*.[gG][iI][fF]"); - gtk_file_filter_add_pattern (filter_all, "*.[gG][iI][fF]"); - - supported[5] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[5], "PNG (*.png)"); - gtk_file_filter_add_pattern (supported[5], "*.[pP][nN][gG]"); - gtk_file_filter_add_pattern (filter_all, "*.[pP][nN][gG]"); - - supported[6] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[6], "HEIF/HEIC (*.heif, *.heic)"); - gtk_file_filter_add_pattern (supported[6], "*.[hH][eE][iI][fF]"); - gtk_file_filter_add_pattern (supported[6], "*.[hH][eE][iI][cC]"); - gtk_file_filter_add_pattern (filter_all, "*.[hH][eE][iI][fF]"); - gtk_file_filter_add_pattern (filter_all, "*.[hH][eE][iI][cC]"); - - supported[7] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[7], "Icon (*.ico)"); - gtk_file_filter_add_pattern (supported[7], "*.[iI][cC][oO]"); - gtk_file_filter_add_pattern (filter_all, "*.[iI][cC][oO]"); - - supported[8] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[8], "Scalable Vector Graphics (*.svg)"); - gtk_file_filter_add_pattern (supported[8], "*.[sS][vV][gG]"); - gtk_file_filter_add_pattern (filter_all, "*.[sS][vV][gG]"); - - supported[9] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[9], "TIFF (*tif, *.tiff)"); - gtk_file_filter_add_pattern (supported[9], "*.[tT][iI][fF][fF]"); - gtk_file_filter_add_pattern (supported[9], "*.[tT][iI][fF]"); - gtk_file_filter_add_pattern (filter_all, "*.[tT][iI][fF][fF]"); - gtk_file_filter_add_pattern (filter_all, "*.[tT][iI][fF]"); - - supported[10] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[10], "Targa (*.tga)"); - gtk_file_filter_add_pattern (supported[10], "*.[tT][gG][aA]"); - gtk_file_filter_add_pattern (filter_all, "*.[tT][gG][aA]"); - - supported[11] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[11], "WebP (*.webp)"); - gtk_file_filter_add_pattern (supported[11], "*.[wW][eE][bB][pP]"); - gtk_file_filter_add_pattern (filter_all, "*.[wW][eE][bB][pP]"); - - supported[12] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[12], "XPM (*.xpm)"); - gtk_file_filter_add_pattern (supported[12], "*.[xX][pP][mM]"); - gtk_file_filter_add_pattern (filter_all, "*.[xX][pP][mM]"); - - supported[13] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[13], "OpenEXR (*.exr)"); - gtk_file_filter_add_pattern (supported[13], "*.[eE][xX][rR]"); - gtk_file_filter_add_pattern (filter_all, "*.[eE][xX][rR]"); - - supported[14] = gtk_file_filter_new(); - gtk_file_filter_set_name(supported[14], "GIMP XCF (*.xcf)"); - gtk_file_filter_add_pattern (supported[14], "*.[xX][cC][fF]"); - gtk_file_filter_add_pattern (filter_all, "*.[xX][cC][fF]"); - - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter_all); - size_t i; - for(i = 0; i < 15; i++) { - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), supported[i]); - } - - /* show dialog */ - if (gtk_dialog_run (GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT) { - selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_chooser)); - - g_free(last_input_location); - last_input_location = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(file_chooser)); - - g_slist_foreach(selection, (GFunc)add_input_file, NULL); - } - - gtk_widget_destroy (file_chooser); -} - -static void open_folder_chooser(GtkWidget *widget, gpointer data) -{ - GSList *selection; - gboolean include_subdirs; - - GtkWidget* folder_chooser = gtk_file_chooser_dialog_new( - _("Select folders containing images"), - NULL, - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, - GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL - ); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(folder_chooser), TRUE); - - /* Add checkbox to select the depth of file search */ - GtkWidget* with_subdirs = gtk_check_button_new_with_label(_("Add files from the whole hierarchy")); - gtk_widget_show (with_subdirs); - gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(folder_chooser), GTK_WIDGET(with_subdirs)); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(folder_chooser), last_input_location); - - /* show dialog */ - if (gtk_dialog_run (GTK_DIALOG(folder_chooser)) == GTK_RESPONSE_ACCEPT) { - selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(folder_chooser)); - - g_free(last_input_location); - last_input_location = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(folder_chooser)); - - include_subdirs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(with_subdirs)); - g_slist_foreach(selection, (GFunc)add_input_folder, GINT_TO_POINTER(include_subdirs)); - } - - gtk_widget_destroy (folder_chooser); -} - -static char* get_outputfolder_name() -{ - char* last_folder = g_strrstr(bimp_output_folder, FILE_SEPARATOR_STR) + 1; - if (last_folder == NULL || strlen(last_folder) == 0) last_folder = bimp_output_folder; - char *folder_name = malloc(25); - if (strlen(last_folder) > 24) { - char *folder_name = malloc(25); - memcpy(folder_name, last_folder, 21); - folder_name[21] = folder_name[22] = folder_name[23] = '.'; - folder_name[24] = '\0'; - return folder_name; - } - else { - return last_folder; - } -} - -static void open_outputfolder_chooser(GtkWidget *widget, gpointer data) -{ - GtkWidget* chooser = gtk_file_chooser_dialog_new( - _("Select output folder"), - NULL, - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL - ); - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE); - if (selected_source_folder != NULL) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), selected_source_folder); - - if (gtk_dialog_run (GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) { - bimp_output_folder = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser))->data; - - gtk_button_set_label(GTK_BUTTON(button_outfolder), get_outputfolder_name()); - - gtk_widget_set_tooltip_text(button_outfolder, bimp_output_folder); - } - - gtk_widget_destroy (chooser); -} - -static void set_source_output_folder(GtkWidget *widget, gpointer data) -{ - if (selected_source_folder != NULL) { - gtk_button_set_label(GTK_BUTTON(button_outfolder), get_outputfolder_name()); - gtk_widget_set_tooltip_text(button_outfolder, selected_source_folder); - bimp_output_folder = g_strdup(selected_source_folder); - } -} - -/* initializes the two menus that appears when the user clicks on the "add new" button - * or on one of the already added manipulations */ -static void popmenus_init() -{ - GtkWidget *menuitem; - - /* Menu to add a manipulation */ - popmenu_add = gtk_menu_new(); - - int man_id; - for (man_id = 0; man_id < MANIP_END; man_id++) - { - menuitem = gtk_menu_item_new_with_label(bimp_manip_get_string(man_id)); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_manipulation_from_id), GINT_TO_POINTER(man_id)); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); - } - - - /* last items to save and load a manipulations set */ - - menuitem = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); - - menuitem = gtk_menu_item_new_with_label(_("Save this set...")); - g_signal_connect(menuitem, "activate", G_CALLBACK(save_set), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); - - menuitem = gtk_menu_item_new_with_label(_("Load set...")); - g_signal_connect(menuitem, "activate", G_CALLBACK(load_set), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); - - /* menu to edit a manipulation */ - popmenu_edit = gtk_menu_new(); - - menuitem = gtk_menu_item_new_with_label(""); /* first element shows only the step name */ - gtk_widget_set_sensitive(menuitem, FALSE); /* and it's non-selectable */ - - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); - menuitem = gtk_menu_item_new_with_label(_("Edit properties...")); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_clicked_manipulation), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); - menuitem = gtk_menu_item_new_with_label(_("Remove this manipulation")); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_clicked_manipulation), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); - - /* menu to add files to the list in various ways */ - popmenu_addfiles = gtk_menu_new(); - - menuitem = gtk_menu_item_new_with_label(_("Add single images...")); - g_signal_connect(menuitem, "activate", G_CALLBACK(open_file_chooser), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); - menuitem = gtk_menu_item_new_with_label(_("Add folders...")); - g_signal_connect(menuitem, "activate", G_CALLBACK(open_folder_chooser), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); - menuitem = gtk_menu_item_new_with_label(_("Add all opened images")); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_opened_files), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); - - /* menu to remove files to the list */ - popmenu_removefiles = gtk_menu_new(); - - menuitem = gtk_menu_item_new_with_label(_("Remove selected")); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_input_file), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_removefiles), menuitem); - menuitem = gtk_menu_item_new_with_label(_("Remove all")); - g_signal_connect(menuitem, "activate", G_CALLBACK(remove_all_input_files), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_removefiles), menuitem); - - gtk_widget_show_all(popmenu_add); - gtk_widget_show_all(popmenu_edit); - gtk_widget_show_all(popmenu_addfiles); - gtk_widget_show_all(popmenu_removefiles); -} - -static void open_manipulation_popupmenu(GtkWidget *widget, gpointer data) -{ - if (data == NULL) { - gtk_menu_popup(GTK_MENU(popmenu_add), NULL, NULL, NULL, NULL, 0, 0); - } - else { - const gchar* item_label; - - clicked_man = (manipulation)data; - if (clicked_man->type == MANIP_USERDEF) { - item_label = ((userdef_settings)(clicked_man->settings))->procedure; - } else { - item_label = bimp_manip_get_string(clicked_man->type); - } - gtk_menu_item_set_label(g_list_first(gtk_container_get_children(GTK_CONTAINER(popmenu_edit)))->data, item_label); - gtk_menu_popup(GTK_MENU(popmenu_edit), NULL, NULL, NULL, NULL, 0, 0); - } -} - -static void open_addfiles_popupmenu(GtkWidget *widget, gpointer data) -{ - gtk_menu_popup(GTK_MENU(popmenu_addfiles), NULL, NULL, NULL, NULL, 0, 0); -} - -static void open_removefiles_popupmenu(GtkWidget *widget, gpointer data) -{ - gtk_menu_popup(GTK_MENU(popmenu_removefiles), NULL, NULL, NULL, NULL, 0, 0); -} - -static void add_manipulation_from_id(GtkMenuItem *menuitem, gpointer id) -{ - int man_id = GPOINTER_TO_INT(id); - manipulation newman = bimp_append_manipulation((manipulation_type)man_id); - if (newman == NULL) { - bimp_show_error_dialog(_("Can't add another manipulation of this kind. Only one is permitted!"), bimp_window_main); - } - else { - bimp_refresh_sequence_panel(); - - GtkAdjustment* hadj_sequence = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll_sequence)); - gtk_adjustment_set_value(hadj_sequence, gtk_adjustment_get_upper(hadj_sequence)); - - bimp_open_editwindow(newman, TRUE); - } -} - -static void edit_clicked_manipulation(GtkMenuItem *menuitem, gpointer user_data) -{ - if (clicked_man != NULL) { - bimp_open_editwindow(clicked_man, FALSE); - } -} - -static void remove_clicked_manipulation(GtkMenuItem *menuitem, gpointer user_data) -{ - if (clicked_man != NULL) { - bimp_remove_manipulation(clicked_man); - g_free(clicked_man); - bimp_refresh_sequence_panel(); - } -} - -/* update the visual of the sequence panel */ -void bimp_refresh_sequence_panel() -{ - GtkWidget* button; - - /* Remove all buttons */ - g_list_foreach(gtk_container_get_children(GTK_CONTAINER(hbox_sequence)), (GFunc)gtk_widget_destroy, NULL); - - /* Rebuild panel */ - g_slist_foreach(bimp_selected_manipulations, (GFunc)add_manipulation_button, NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_ADD); - gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_TOP); - gtk_widget_set_size_request (button, SEQ_BUTTON_W - 20, SEQ_BUTTON_H); - gtk_box_pack_start(GTK_BOX(hbox_sequence), button, FALSE, FALSE, 3); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_manipulation_popupmenu), NULL); - - gtk_widget_show_all(hbox_sequence); -} - -/* creates a manipulation button and appends it to the sequence box */ -static void add_manipulation_button(manipulation man) -{ - GtkWidget* button; - - button = gtk_button_new(); - gtk_button_set_image(GTK_BUTTON(button), image_new_from_resource(man->icon)); - gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_TOP); - gtk_widget_set_size_request(button, SEQ_BUTTON_W, SEQ_BUTTON_H); - gtk_box_pack_start(GTK_BOX(hbox_sequence), button, FALSE, FALSE, 3); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_manipulation_popupmenu), man); -} - -static void save_set(GtkMenuItem *menuitem, gpointer user_data) -{ - if (g_slist_length(bimp_selected_manipulations) == 0) { - bimp_show_error_dialog(_("The manipulations set is empty!"), bimp_window_main); - } else { - gchar* output_file; - char* extension = ".bimp"; - - GtkWidget* file_saver = gtk_file_chooser_dialog_new( - _("Save this set..."), - NULL, - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL - ); - - GtkFileFilter *filter_bimp = gtk_file_filter_new(); - gtk_file_filter_set_name(filter_bimp,"BIMP manipulations set (*.bimp)"); - gtk_file_filter_add_pattern (filter_bimp, "*.bimp"); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_saver), filter_bimp); - - if (gtk_dialog_run (GTK_DIALOG(file_saver)) == GTK_RESPONSE_ACCEPT) { - - output_file = g_strdup(g_slist_nth (gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_saver)), 0)->data); - if (!g_str_has_suffix(output_file, extension)) { - output_file = g_strconcat(output_file, extension, NULL); - } - gtk_widget_destroy (file_saver); - - if (!bimp_serialize_to_file(output_file)) { - bimp_show_error_dialog(_("An error occured when importing a saved batch file :("), bimp_window_main); - } - - return; - } - - gtk_widget_destroy (file_saver); - return; - } -} - -static void load_set(GtkMenuItem *menuitem, gpointer user_data) -{ - gboolean can_continue = TRUE; - - if (g_slist_length(bimp_selected_manipulations) > 0) { - GtkWidget *dialog; - dialog = gtk_message_dialog_new( - GTK_WINDOW(bimp_window_main), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - _("This will overwrite current manipulations set. Continue?") - ); - gtk_window_set_title(GTK_WINDOW(dialog), "Continue?"); - gint result = gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - - can_continue = (result == GTK_RESPONSE_YES); - } - - if (can_continue) { - gchar* input_file; - GtkWidget* file_loader = gtk_file_chooser_dialog_new( - _("Load set..."), - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL - ); - - GtkFileFilter *filter_bimp = gtk_file_filter_new(); - gtk_file_filter_set_name(filter_bimp, "BIMP manipulations set (*.bimp)"); - gtk_file_filter_add_pattern (filter_bimp, "*.bimp"); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_loader), filter_bimp); - - if (gtk_dialog_run (GTK_DIALOG(file_loader)) == GTK_RESPONSE_ACCEPT) { - input_file = g_strdup(g_slist_nth (gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_loader)), 0)->data); - gtk_widget_destroy (file_loader); - - if (!bimp_deserialize_from_file(input_file)) { - bimp_show_error_dialog(_("An error occured when importing a saved batch file :("), bimp_window_main); - } - else { - bimp_refresh_sequence_panel(); - } - return; - } - - gtk_widget_destroy (file_loader); - return; - } -} - -static void open_about() -{ - const gchar *auth[] = { - "Alessandro Francesconi ", - "GitHub contributors ", - NULL }; - const gchar *license = - "This program is free software; you can redistribute it and/or modify " - "it under the terms of the GNU General Public License as published by " - "the Free Software Foundation; either version 2 of the License, or " - "(at your option) any later version. \n\n" - "This program is distributed in the hope that it will be useful, " - "but WITHOUT ANY WARRANTY; without even the implied warranty of " - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " - "GNU General Public License for more details.\n\n" - "You should have received a copy of the GNU General Public License " - "along with this program; if not, write to the Free Software " - "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, " - "MA 02110-1301, USA. "; - - gtk_show_about_dialog( - GTK_WINDOW(bimp_window_main), - "program-name", PLUG_IN_FULLNAME, - "version", PLUG_IN_VERSION, - "comments", _("Applies GIMP manipulations on groups of images"), - "logo", pixbuf_new_from_resource("/gimp/plugin/bimp/icons/bimp-icon.png"), - "copyright", PLUG_IN_COPYRIGHT, - "license", license, - "wrap-license", TRUE, - "website", PLUG_IN_WEBSITE, - "authors", auth, - "translator-credits", _("translator-name "), - NULL - ); -} - -/* shows an error dialog with a custom message */ -void bimp_show_error_dialog(char* message, GtkWidget* parent) -{ - GtkWidget* dialog = gtk_message_dialog_new ( - GTK_WINDOW(parent), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", - message - ); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); -} - -/* suppress progress popup by installing progress handlers that do nothing */ -static const gchar* progressbar_init_hidden () -{ - GimpProgressVtable vtable = { 0, }; - - vtable.start = progressbar_start_hidden; - vtable.end = progressbar_end_hidden; - vtable.set_text = progressbar_settext_hidden; - vtable.set_value = progressbar_setvalue_hidden; - - return gimp_progress_install_vtable (&vtable, NULL); -} -static void progressbar_start_hidden (const gchar *message, gboolean cancelable, gpointer user_data) { } -static void progressbar_end_hidden (gpointer user_data) { } -static void progressbar_settext_hidden (const gchar *message, gpointer user_data) { } -static void progressbar_setvalue_hidden (double percent, gpointer user_data) { } - -void bimp_progress_bar_set(double fraction, char* text) { - if (fraction > 1.0) fraction = 1.0; - - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar_visible), fraction); - if (text != NULL) { - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), text); - } - else { - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), " "); - } -} - -void bimp_set_busy(gboolean busy) { - GList *actions_children, *tmp_child; - struct _ResponseData { gint response_id; }; - - bimp_is_busy = busy; - - gtk_dialog_set_response_sensitive (GTK_DIALOG(bimp_window_main), GTK_RESPONSE_CLOSE, !busy); - gtk_dialog_set_response_sensitive (GTK_DIALOG(bimp_window_main), GTK_RESPONSE_HELP, !busy); - - /* procedure that hides and shows some widgets in the dialog's action area. Compatible with GTK+ 2.16 */ - GtkWidget* actions = gtk_dialog_get_action_area (GTK_DIALOG(bimp_window_main)); - actions_children = gtk_container_get_children (GTK_CONTAINER (actions)); - tmp_child = actions_children; - while (tmp_child != NULL) - { - GtkWidget *widget = tmp_child->data; - struct _ResponseData *rd = g_object_get_data (G_OBJECT (widget), "gtk-dialog-response-data"); - - if (rd && rd->response_id == GTK_RESPONSE_APPLY) { - if (busy) { - gtk_widget_hide (widget); - } else { - gtk_widget_show (widget); - } - } - else if (rd && rd->response_id == GTK_RESPONSE_CANCEL) { - if (!busy) { - gtk_widget_hide (widget); - } else { - gtk_widget_show (widget); - } - } - - tmp_child = g_list_next (tmp_child); - } - g_list_free (actions_children); - - gtk_widget_set_sensitive(panel_sequence, !busy); - gtk_widget_set_sensitive(panel_options, !busy); -} +/* + * Functions used to build and return the main BIMP user interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bimp.h" +#include "bimp-manipulations.h" +#include "bimp-gui.h" +#include "bimp-manipulations-gui.h" +#include "bimp-operate.h" +#include "bimp-serialize.h" +#include "bimp-utils.h" +#include "plugin-intl.h" + +static GtkWidget* sequence_panel_new(void); +static GtkWidget* option_panel_new(void); +static void init_fileview(void); +static void add_to_fileview(char*); +static GSList* get_treeview_selection(); +static void open_file_chooser(GtkWidget*, gpointer); +static void open_folder_chooser(GtkWidget*, gpointer); +static void add_input_file(char*); +static void add_input_folder(char*, gpointer); +static void add_opened_files(void); +static void remove_input_file(GtkWidget*, gpointer); +static void remove_all_input_files(GtkWidget*, gpointer); +static void select_filename (GtkTreeView*, gpointer); +static void update_selection(char*); +static void show_preview(GtkTreeView*, gpointer); +static char* get_outputfolder_name(); +static void open_outputfolder_chooser(GtkWidget*, gpointer); +static void set_source_output_folder(GtkWidget*, gpointer); +static void popmenus_init(void); +static void open_manipulation_popupmenu(GtkWidget*, gpointer); +static void open_addfiles_popupmenu(GtkWidget*, gpointer); +static void open_removefiles_popupmenu(GtkWidget*, gpointer); +static void add_manipulation_from_id(GtkMenuItem*, gpointer); +static void edit_clicked_manipulation(GtkMenuItem*, gpointer); +static void remove_clicked_manipulation(GtkMenuItem*, gpointer); +static void add_manipulation_button(manipulation); + +static void save_set(GtkMenuItem*, gpointer); +static void load_set(GtkMenuItem*, gpointer); + +static void open_about(); +static const gchar* progressbar_init_hidden (void); +static void progressbar_start_hidden(const gchar*, gboolean, gpointer); +static void progressbar_end_hidden (gpointer); +static void progressbar_settext_hidden (const gchar*, gpointer); +static void progressbar_setvalue_hidden (double, gpointer); + +GtkWidget* bimp_window_main; + +static GtkWidget *panel_sequence, *panel_options; +static GtkWidget *hbox_sequence; +static GtkWidget *scroll_sequence; +static GtkWidget *popmenu_add, *popmenu_edit, *popmenu_addfiles, *popmenu_removefiles; +static GtkWidget *check_keepfolderhierarchy, *check_deleteondone, *check_keepdates; +static GtkWidget *treeview_files; +static GtkWidget *button_preview, *button_outfolder, *button_samefolder; +static GtkWidget* progressbar_visible; + +static char* selected_source_folder; +static char* last_input_location; +static const gchar* progressbar_data; + +enum /* TreeView stuff... */ +{ + LIST_ITEM = 0, + N_COLUMNS +}; + +static manipulation clicked_man; /* temporary manipulation, selected by clicking on panel_seq buttons */ + +void bimp_show_gui() +{ + GtkWidget* vbox_main; + + gimp_ui_init (PLUG_IN_BINARY, FALSE); + + bimp_window_main = gimp_dialog_new( + PLUG_IN_FULLNAME, + PLUG_IN_BINARY, + NULL, + 0, + NULL, + NULL, + GTK_STOCK_ABOUT, GTK_RESPONSE_HELP, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, + GTK_STOCK_STOP, GTK_RESPONSE_CANCEL, NULL + ); + + gimp_window_set_transient (GTK_WINDOW(bimp_window_main)); + gtk_window_set_position(GTK_WINDOW(bimp_window_main), GTK_WIN_POS_CENTER); + gtk_window_set_default_size(GTK_WINDOW(bimp_window_main), (int)(PREVIEW_IMG_W * 2.5), SEQ_BUTTON_H + PREVIEW_IMG_H + 160); + gtk_container_set_border_width(GTK_CONTAINER(bimp_window_main), 5); + + // Forces the visualization of label AND images on buttons (especially for Windows) + GtkSettings *default_settings = gtk_settings_get_default(); + g_object_set(default_settings, "gtk-button-images", TRUE, NULL); + + vbox_main = gtk_vbox_new(FALSE, 10); + panel_sequence = sequence_panel_new(); + panel_options = option_panel_new(); + + progressbar_visible = gtk_progress_bar_new(); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), " "); + progressbar_data = progressbar_init_hidden(); + + gtk_box_pack_start(GTK_BOX(vbox_main), panel_sequence, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_main), panel_options, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_main), progressbar_visible, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(bimp_window_main)->vbox), vbox_main); + gtk_widget_show_all(bimp_window_main); + gtk_widget_hide(button_preview); + + bimp_set_busy(FALSE); + + while(TRUE) { + gint run = gimp_dialog_run (GIMP_DIALOG(bimp_window_main)); + if (run == GTK_RESPONSE_APPLY) { + if (g_slist_length(bimp_selected_manipulations) == 0) { + bimp_show_error_dialog(_("The manipulations set is empty!"), bimp_window_main); + } else { + if (g_slist_length(bimp_input_filenames) == 0) { + bimp_show_error_dialog(_("The file list is empty!"), bimp_window_main); + } + else { + bimp_opt_alertoverwrite = BIMP_ASK_OVERWRITE; + bimp_opt_keepfolderhierarchy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_keepfolderhierarchy)); + bimp_opt_deleteondone = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_deleteondone)); + bimp_opt_keepdates = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_keepdates)); + bimp_start_batch(bimp_window_main); + } + } + } + else if (run == GTK_RESPONSE_HELP) { + open_about(); + } + else if (run == GTK_RESPONSE_CANCEL) { + bimp_set_busy(FALSE); + } + else { + gimp_progress_uninstall(progressbar_data); + gtk_widget_destroy (bimp_window_main); + return; + } + } +} + +/* builds and returns the upper panel with the manipulation buttons */ +static GtkWidget* sequence_panel_new() +{ + GtkWidget *panel; + + panel = gtk_frame_new(_("Manipulation set")); + + scroll_sequence = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_sequence), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); + + hbox_sequence = gtk_hbox_new(FALSE, 10); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_sequence), hbox_sequence); + gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scroll_sequence))), GTK_SHADOW_NONE); + gtk_container_add(GTK_CONTAINER(panel), scroll_sequence); + + bimp_refresh_sequence_panel(); + popmenus_init(); + + return panel; +} + +/* builds and returns the panel with file list and options */ +static GtkWidget* option_panel_new() +{ + GtkWidget *panel, *table; + GtkWidget *scroll_input; + GtkWidget *button_add, *button_remove; + + GtkWidget *vbox_useroptions, *hbox_outfolder; + GtkWidget *label_chooser; + + panel = gtk_frame_new(_("Input files and options")); + table = gtk_table_new(2, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + + /* Sub-sub-panel for input file listing */ + scroll_input = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_input), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + treeview_files = gtk_tree_view_new(); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview_files), FALSE); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files)), GTK_SELECTION_MULTIPLE); + + button_add = gtk_button_new_with_label(_("Add images")); + button_remove = gtk_button_new_with_label(_("Remove images")); + + /* Sub-panel for options */ + vbox_useroptions = gtk_vbox_new(FALSE, 3); + + hbox_outfolder = gtk_hbox_new(FALSE, 3); + label_chooser = gtk_label_new(g_strconcat(_("Output folder"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_chooser), 0, .5); + + bimp_output_folder = get_user_dir(); + + + button_outfolder = gtk_button_new_with_label(get_outputfolder_name()); + + gtk_widget_set_tooltip_text (button_outfolder, bimp_output_folder); + + button_samefolder = gtk_button_new(); + GtkWidget* samefolder_icon = gtk_image_new_from_stock(GTK_STOCK_UNDO, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image(GTK_BUTTON(button_samefolder), samefolder_icon); + gtk_widget_set_tooltip_text (button_samefolder, _("Use the selected file's location as the output")); + + bimp_opt_alertoverwrite = BIMP_ASK_OVERWRITE; + //check_alertoverwrite = gtk_check_button_new_with_label(_("Alert when overwriting existing files")); + //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_alertoverwrite), TRUE); + + bimp_opt_keepfolderhierarchy = FALSE; + check_keepfolderhierarchy = gtk_check_button_new_with_label(_("Keep folder hierarchy")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_keepfolderhierarchy), bimp_opt_keepfolderhierarchy); + + /* TODO? */ + bimp_opt_deleteondone = FALSE; + check_deleteondone = gtk_check_button_new_with_label(_("Delete original file when done")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_deleteondone), bimp_opt_deleteondone); + + bimp_opt_keepdates = FALSE; + check_keepdates = gtk_check_button_new_with_label(_("Keep the modification dates")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_keepdates), bimp_opt_keepdates); + + button_preview = gtk_button_new(); + gtk_button_set_image_position (GTK_BUTTON(button_preview), GTK_POS_TOP); + gtk_button_set_label(GTK_BUTTON(button_preview), _("Click for preview")); + + /* All together */ + + gtk_container_add (GTK_CONTAINER(scroll_input), treeview_files); + + gtk_box_pack_start(GTK_BOX(vbox_useroptions), label_chooser, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_outfolder), button_outfolder, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_outfolder), button_samefolder, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_useroptions), hbox_outfolder, FALSE, FALSE, 0); + + //gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_alertoverwrite, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_keepfolderhierarchy, FALSE, FALSE, 0); + // TODO: delete on done? gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_deleteondone, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_useroptions), check_keepdates, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_useroptions), button_preview, FALSE, FALSE, 2); + + gtk_table_attach_defaults(GTK_TABLE(table), scroll_input, 0, 2, 0, 1); + gtk_table_attach(GTK_TABLE(table), button_add, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_table_attach(GTK_TABLE(table), button_remove, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + + gtk_table_attach(GTK_TABLE(table), vbox_useroptions, 2, 3, 0, 2, GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0); + + gtk_container_add(GTK_CONTAINER(panel), table); + + g_signal_connect(G_OBJECT(button_add), "clicked", G_CALLBACK(open_addfiles_popupmenu), NULL); + g_signal_connect(G_OBJECT(button_remove), "clicked", G_CALLBACK(open_removefiles_popupmenu), NULL); + g_signal_connect(G_OBJECT(button_outfolder), "clicked", G_CALLBACK(open_outputfolder_chooser), NULL); + g_signal_connect(G_OBJECT(button_samefolder), "clicked", G_CALLBACK(set_source_output_folder), NULL); + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files))), "changed", G_CALLBACK(select_filename), NULL); + g_signal_connect(G_OBJECT(button_preview), "clicked", G_CALLBACK(show_preview), NULL); + + init_fileview(); + bimp_refresh_fileview(); + + return panel; +} + +/* following: functions that modify the file list widget (addfile/addfolder/remove/removeall) */ + +static void add_input_file(char* filename) +{ + if (g_slist_find_custom(bimp_input_filenames, filename, (GCompareFunc)strcmp) == NULL) { + bimp_input_filenames = g_slist_append(bimp_input_filenames, filename); + bimp_refresh_fileview(); + } +} + +/* Recursive function to add all files from the hierarchy if desired */ +static void add_input_folder_r(char* folder, gboolean with_subdirs) +{ + GDir *dp; + const gchar* entry; + dp = g_dir_open (folder, 0, NULL); + + if (dp != NULL) { + while ((entry = g_dir_read_name (dp))) { + + char* filename = g_strconcat(folder, FILE_SEPARATOR_STR, entry, NULL); + char* file_extension = g_strdup(strrchr(filename, '.')); + GError *error; + GFileInfo *file_info = g_file_query_info (g_file_new_for_path(filename), "standard::*", 0, NULL, &error); + + /* Folder processing */ + if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_DIRECTORY){ + if (g_strcmp0(entry, ".") == 0 || g_strcmp0(entry, "..") == 0) + continue; + if (with_subdirs) + add_input_folder_r(filename, with_subdirs); + continue; + } + + if (( + g_ascii_strcasecmp(file_extension, ".bmp") == 0 || + g_ascii_strcasecmp(file_extension, ".jpeg") == 0 || + g_ascii_strcasecmp(file_extension, ".jpg") == 0 || + g_ascii_strcasecmp(file_extension, ".jpe") == 0 || + g_ascii_strcasecmp(file_extension, ".jp2") == 0 || + g_ascii_strcasecmp(file_extension, ".gif") == 0 || + g_ascii_strcasecmp(file_extension, ".heif") == 0 || + g_ascii_strcasecmp(file_extension, ".heic") == 0 || + g_ascii_strcasecmp(file_extension, ".png") == 0 || + g_ascii_strcasecmp(file_extension, ".tif") == 0 || + g_ascii_strcasecmp(file_extension, ".tiff") == 0 || + g_ascii_strcasecmp(file_extension, ".tga") == 0 || + g_ascii_strcasecmp(file_extension, ".svg") == 0 || + g_ascii_strcasecmp(file_extension, ".webp") == 0 || + g_ascii_strcasecmp(file_extension, ".avif") == 0 || + g_ascii_strcasecmp(file_extension, ".xpm") == 0 || + g_ascii_strcasecmp(file_extension, ".exr") == 0 || + g_ascii_strcasecmp(file_extension, ".dds") == 0 || + g_ascii_strcasecmp(file_extension, ".xcf") == 0) && + g_slist_find_custom(bimp_input_filenames, filename, (GCompareFunc)strcmp) == NULL) + { + bimp_input_filenames = g_slist_append(bimp_input_filenames, filename); + } + } + g_dir_close (dp); + } + else { + bimp_show_error_dialog(g_strdup_printf(_("Couldn't read into \"%s\" directory."), folder), bimp_window_main); + } +} + +static void add_opened_files() +{ + gint num_images = 0; + int* image_ids = gimp_image_list (&num_images); + int i; + gboolean missing = FALSE; + for (i = 0; i < num_images; i++) { + gchar* uri = gimp_image_get_uri(image_ids[i]); + if (uri != NULL) { + gchar* path = g_filename_from_uri(uri, NULL, NULL); + if (path != NULL) add_input_file (path); + } + else missing = TRUE; + } + + if (missing) { + bimp_show_error_dialog(g_strdup_printf(_("Some images were not imported because they have not been saved on filesystem yet.")), bimp_window_main); + } + + g_free(image_ids); +} + +static void add_input_folder(char* folder, gpointer with_subdirs) +{ + add_input_folder_r(folder, (gboolean)GPOINTER_TO_INT(with_subdirs)); + bimp_refresh_fileview(); +} + +/* returns the list of currently selected filenames (NULL of none) */ +static GSList* get_treeview_selection() +{ + GtkTreeModel *model; + GList *selected_rows = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_files)), &model); + GList *i = NULL; + GSList *out = NULL; + + if (selected_rows != NULL) { + for (i = selected_rows; i != NULL; i = g_list_next(i) ) { + GtkTreeIter iter; + char* selected_i; + if (gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)i->data) == TRUE) { + gtk_tree_model_get(model, &iter, LIST_ITEM, &selected_i, -1); + out = g_slist_append(out, selected_i); + } + } + + g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected_rows); + } + + return out; +} + +static void remove_input_file(GtkWidget *widget, gpointer data) +{ + GSList *selection = get_treeview_selection(); + GSList *i; + + if (selection != NULL) { + for (i = selection; i != NULL; i = g_slist_next(i) ) { + bimp_input_filenames = g_slist_delete_link(bimp_input_filenames, g_slist_find_custom(bimp_input_filenames, (char*)(i->data), (GCompareFunc)strcmp)); + } + + bimp_refresh_fileview(); + update_selection(NULL); /* clear the preview widget */ + } +} + +static void remove_all_input_files(GtkWidget *widget, gpointer data) +{ + g_slist_free(bimp_input_filenames); + bimp_input_filenames = NULL; + bimp_refresh_fileview(); + update_selection(NULL); +} + +/* called when the user clicks on a filename row to update the preview widget */ +static void select_filename (GtkTreeView *tree_view, gpointer data) +{ + GSList *selection = get_treeview_selection(); + + if (selection != NULL && g_slist_length(selection) == 1) { + update_selection((gchar*)(selection->data)); + } + else { + update_selection(NULL); + } +} + +/* updates the GUI according to the current selected filename */ +static void update_selection (gchar* filename) +{ + g_free(selected_source_folder); + if (filename != NULL) { + // update preview + GdkPixbuf *pixbuf_prev = gdk_pixbuf_new_from_file_at_scale(filename, FILE_PREVIEW_W - 20, FILE_PREVIEW_H - 30, TRUE, NULL); + gtk_button_set_image(GTK_BUTTON(button_preview), gtk_image_new_from_pixbuf (pixbuf_prev)); + gtk_widget_show(button_preview); + + // update current selection + selected_source_folder = g_path_get_dirname(filename); + } else { + // invalidate + gtk_button_set_image(GTK_BUTTON(button_preview), NULL); + gtk_widget_hide(button_preview); + selected_source_folder = NULL; + } + + gtk_widget_set_sensitive(button_samefolder, (selected_source_folder != NULL)); +} + +/* opens a dialog containing two panels: the original selected image on the left and the result of the selected manipulations on the right */ +static void show_preview (GtkTreeView *tree_view, gpointer data) +{ + if (g_slist_length(bimp_selected_manipulations) == 0) { + bimp_show_error_dialog(_("Can't show a preview because the manipulations set is empty"), bimp_window_main); + } else { + GtkWidget *dialog_preview; + GtkWidget *vbox, *hbox, *align; + GtkWidget *label_descr; + GdkPixbuf *pixbuf_orig, *pixbuf_final; + GtkWidget *image_orig, *image_final; + GtkWidget *image_forward; + + GSList* selection = get_treeview_selection(); + if (selection == NULL && g_slist_length(selection) != 1) return; + + char* selected_str = g_slist_nth_data(selection, 0); + + image_output imageout_orig = (image_output)g_malloc(sizeof(struct imageout_str)); + image_output imageout_final = (image_output)g_malloc(sizeof(struct imageout_str)); + + imageout_orig->image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE, (gchar*)selected_str, (gchar*)selected_str); + int imageout_orig_drawable = gimp_image_merge_visible_layers(imageout_orig->image_id, GIMP_CLIP_TO_IMAGE); + + bimp_init_batch(); + bimp_apply_drawable_manipulations(imageout_final, (gchar*)selected_str, (gchar*)selected_str); + int imageout_final_drawable = gimp_image_merge_visible_layers(imageout_final->image_id, GIMP_CLIP_TO_IMAGE); + + pixbuf_orig = gimp_drawable_get_thumbnail(imageout_orig_drawable, PREVIEW_IMG_W, PREVIEW_IMG_H, GIMP_PIXBUF_KEEP_ALPHA); + pixbuf_final = gimp_drawable_get_thumbnail(imageout_final_drawable, PREVIEW_IMG_W, PREVIEW_IMG_H, GIMP_PIXBUF_KEEP_ALPHA); + + dialog_preview = gtk_dialog_new_with_buttons ( + _("Preview"), + GTK_WINDOW(bimp_window_main), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE, NULL + ); + //gtk_widget_set_size_request (dialog_preview, PREVIEW_WINDOW_W, PREVIEW_WINDOW_H); + gtk_window_set_resizable (GTK_WINDOW(dialog_preview), FALSE); + gtk_window_set_position(GTK_WINDOW(dialog_preview), GTK_WIN_POS_CENTER); + gtk_container_set_border_width(GTK_CONTAINER(dialog_preview), 5); + + vbox = gtk_vbox_new(FALSE, 10); + label_descr = gtk_label_new(_("This is how the selected image will look like after the batch process")); + gtk_label_set_line_wrap (GTK_LABEL(label_descr), TRUE); + gtk_label_set_justify(GTK_LABEL(label_descr), GTK_JUSTIFY_CENTER); + + align = gtk_alignment_new(0.5, 0.5, 0, 0); + + hbox = gtk_hbox_new(FALSE, 10); + image_orig = gtk_image_new_from_pixbuf(pixbuf_orig); + image_forward = gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON); + image_final = gtk_image_new_from_pixbuf(pixbuf_final); + + gtk_box_pack_start(GTK_BOX(hbox), image_orig, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), image_forward, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), image_final, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(hbox), 0, .5); + + gtk_box_pack_start(GTK_BOX(vbox), label_descr, FALSE, FALSE, 7); + gtk_container_add(GTK_CONTAINER(align), hbox); + gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog_preview)->vbox), vbox); + gtk_widget_show_all(dialog_preview); + + if (gtk_dialog_run (GTK_DIALOG(dialog_preview)) == GTK_RESPONSE_CLOSE) { + gtk_widget_destroy (dialog_preview); + gimp_image_delete(imageout_orig->image_id); + gimp_image_delete(imageout_final->image_id); + g_free(imageout_orig); + g_free(imageout_final); + } + } +} + +static void init_fileview() +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkListStore *store; + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + "List Items", + renderer, + "text", + LIST_ITEM, + NULL + ); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview_files), column); + + store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); + gtk_tree_view_set_model(GTK_TREE_VIEW(treeview_files), GTK_TREE_MODEL(store)); + g_object_unref(store); +} + +static void add_to_fileview(char *str) +{ + GtkListStore *store; + GtkTreeIter iter; + + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview_files))); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, LIST_ITEM, str, -1); +} + +/* update the visual of filename list */ +void bimp_refresh_fileview() +{ + GtkListStore *store; + GtkTreeModel *model; + GtkTreeIter treeiter; + + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (treeview_files))); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview_files)); + + /* clear all rows in list */ + if (gtk_tree_model_get_iter_first(model, &treeiter) == TRUE) { + gtk_list_store_clear(store); + } + + GSList *iter; + if (g_slist_length(bimp_input_filenames) > 0) { + iter = bimp_input_filenames; + for (; iter; iter = iter->next) { + add_to_fileview(iter->data); + } + } +} + +static void open_file_chooser(GtkWidget *widget, gpointer data) +{ + GSList *selection; + + GtkFileFilter *filter_all, *supported[16]; + + GtkWidget* file_chooser = gtk_file_chooser_dialog_new( + _("Select images"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, + GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL + ); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_chooser), TRUE); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser), last_input_location); + + filter_all = gtk_file_filter_new(); + gtk_file_filter_set_name(filter_all,_("All supported types")); + + supported[0] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[0], "Bitmap (*.bmp)"); + gtk_file_filter_add_pattern (supported[0], "*.[bB][mM][pP]"); + gtk_file_filter_add_pattern (filter_all, "*.[bB][mM][pP]"); + + supported[1] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[1], "DDS (*.dds)"); + gtk_file_filter_add_pattern (supported[1], "*.[dD][dD][sS]"); + gtk_file_filter_add_pattern (filter_all, "*.[dD][dD][sS]"); + + supported[2] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[2], "JPEG (*.jpg, *.jpeg, *jpe)"); + gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][gG]"); + gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][eE][gG]"); + gtk_file_filter_add_pattern (supported[2], "*.[jJ][pP][eE]"); + gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][gG]"); + gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][eE][gG]"); + gtk_file_filter_add_pattern (filter_all, "*.[jJ][pP][eE]"); + + supported[3] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[3], "JPEG2000 (*.jp2)"); + gtk_file_filter_add_pattern (supported[3], "*.[jJ][pP][2]"); + + supported[4] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[4], "GIF (*.gif)"); + gtk_file_filter_add_pattern (supported[4], "*.[gG][iI][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[gG][iI][fF]"); + + supported[5] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[5], "PNG (*.png)"); + gtk_file_filter_add_pattern (supported[5], "*.[pP][nN][gG]"); + gtk_file_filter_add_pattern (filter_all, "*.[pP][nN][gG]"); + + supported[6] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[6], "HEIF/HEIC (*.heif, *.heic)"); + gtk_file_filter_add_pattern (supported[6], "*.[hH][eE][iI][fF]"); + gtk_file_filter_add_pattern (supported[6], "*.[hH][eE][iI][cC]"); + gtk_file_filter_add_pattern (filter_all, "*.[hH][eE][iI][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[hH][eE][iI][cC]"); + + supported[7] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[7], "Icon (*.ico)"); + gtk_file_filter_add_pattern (supported[7], "*.[iI][cC][oO]"); + gtk_file_filter_add_pattern (filter_all, "*.[iI][cC][oO]"); + + supported[8] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[8], "Scalable Vector Graphics (*.svg)"); + gtk_file_filter_add_pattern (supported[8], "*.[sS][vV][gG]"); + gtk_file_filter_add_pattern (filter_all, "*.[sS][vV][gG]"); + + supported[9] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[9], "TIFF (*tif, *.tiff)"); + gtk_file_filter_add_pattern (supported[9], "*.[tT][iI][fF][fF]"); + gtk_file_filter_add_pattern (supported[9], "*.[tT][iI][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[tT][iI][fF][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[tT][iI][fF]"); + + supported[10] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[10], "Targa (*.tga)"); + gtk_file_filter_add_pattern (supported[10], "*.[tT][gG][aA]"); + gtk_file_filter_add_pattern (filter_all, "*.[tT][gG][aA]"); + + supported[11] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[11], "WebP (*.webp)"); + gtk_file_filter_add_pattern (supported[11], "*.[wW][eE][bB][pP]"); + gtk_file_filter_add_pattern (filter_all, "*.[wW][eE][bB][pP]"); + + supported[12] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[12], "AVIF (*.avif)"); + gtk_file_filter_add_pattern (supported[12], "*.[aA][vV][iI][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[aA][vV][iI][fF]"); + + supported[13] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[13], "XPM (*.xpm)"); + gtk_file_filter_add_pattern (supported[13], "*.[xX][pP][mM]"); + gtk_file_filter_add_pattern (filter_all, "*.[xX][pP][mM]"); + + supported[14] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[14], "OpenEXR (*.exr)"); + gtk_file_filter_add_pattern (supported[14], "*.[eE][xX][rR]"); + gtk_file_filter_add_pattern (filter_all, "*.[eE][xX][rR]"); + + supported[15] = gtk_file_filter_new(); + gtk_file_filter_set_name(supported[15], "GIMP XCF (*.xcf)"); + gtk_file_filter_add_pattern (supported[15], "*.[xX][cC][fF]"); + gtk_file_filter_add_pattern (filter_all, "*.[xX][cC][fF]"); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter_all); + size_t i; + for(i = 0; i < 16; i++) { + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), supported[i]); + } + + /* show dialog */ + if (gtk_dialog_run (GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT) { + selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_chooser)); + + g_free(last_input_location); + last_input_location = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(file_chooser)); + + g_slist_foreach(selection, (GFunc)add_input_file, NULL); + } + + gtk_widget_destroy (file_chooser); +} + +static void open_folder_chooser(GtkWidget *widget, gpointer data) +{ + GSList *selection; + gboolean include_subdirs; + + GtkWidget* folder_chooser = gtk_file_chooser_dialog_new( + _("Select folders containing images"), + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, + GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL + ); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(folder_chooser), TRUE); + + /* Add checkbox to select the depth of file search */ + GtkWidget* with_subdirs = gtk_check_button_new_with_label(_("Add files from the whole hierarchy")); + gtk_widget_show (with_subdirs); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(folder_chooser), GTK_WIDGET(with_subdirs)); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(folder_chooser), last_input_location); + + /* show dialog */ + if (gtk_dialog_run (GTK_DIALOG(folder_chooser)) == GTK_RESPONSE_ACCEPT) { + selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(folder_chooser)); + + g_free(last_input_location); + last_input_location = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(folder_chooser)); + + include_subdirs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(with_subdirs)); + g_slist_foreach(selection, (GFunc)add_input_folder, GINT_TO_POINTER(include_subdirs)); + } + + gtk_widget_destroy (folder_chooser); +} + +static char* get_outputfolder_name() +{ + char* last_folder = g_strrstr(bimp_output_folder, FILE_SEPARATOR_STR) + 1; + if (last_folder == NULL || strlen(last_folder) == 0) last_folder = bimp_output_folder; + char *folder_name = malloc(25); + if (strlen(last_folder) > 24) { + char *folder_name = malloc(25); + memcpy(folder_name, last_folder, 21); + folder_name[21] = folder_name[22] = folder_name[23] = '.'; + folder_name[24] = '\0'; + return folder_name; + } + else { + return last_folder; + } +} + +static void open_outputfolder_chooser(GtkWidget *widget, gpointer data) +{ + GtkWidget* chooser = gtk_file_chooser_dialog_new( + _("Select output folder"), + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL + ); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE); + if (selected_source_folder != NULL) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), selected_source_folder); + + if (gtk_dialog_run (GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT) { + bimp_output_folder = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser))->data; + + gtk_button_set_label(GTK_BUTTON(button_outfolder), get_outputfolder_name()); + + gtk_widget_set_tooltip_text(button_outfolder, bimp_output_folder); + } + + gtk_widget_destroy (chooser); +} + +static void set_source_output_folder(GtkWidget *widget, gpointer data) +{ + if (selected_source_folder != NULL) { + gtk_button_set_label(GTK_BUTTON(button_outfolder), get_outputfolder_name()); + gtk_widget_set_tooltip_text(button_outfolder, selected_source_folder); + bimp_output_folder = g_strdup(selected_source_folder); + } +} + +/* initializes the two menus that appears when the user clicks on the "add new" button + * or on one of the already added manipulations */ +static void popmenus_init() +{ + GtkWidget *menuitem; + + /* Menu to add a manipulation */ + popmenu_add = gtk_menu_new(); + + int man_id; + for (man_id = 0; man_id < MANIP_END; man_id++) + { + menuitem = gtk_menu_item_new_with_label(bimp_manip_get_string(man_id)); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_manipulation_from_id), GINT_TO_POINTER(man_id)); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); + } + + + /* last items to save and load a manipulations set */ + + menuitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); + + menuitem = gtk_menu_item_new_with_label(_("Save this set...")); + g_signal_connect(menuitem, "activate", G_CALLBACK(save_set), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); + + menuitem = gtk_menu_item_new_with_label(_("Load set...")); + g_signal_connect(menuitem, "activate", G_CALLBACK(load_set), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_add), menuitem); + + /* menu to edit a manipulation */ + popmenu_edit = gtk_menu_new(); + + menuitem = gtk_menu_item_new_with_label(""); /* first element shows only the step name */ + gtk_widget_set_sensitive(menuitem, FALSE); /* and it's non-selectable */ + + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); + menuitem = gtk_menu_item_new_with_label(_("Edit properties...")); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_clicked_manipulation), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); + menuitem = gtk_menu_item_new_with_label(_("Remove this manipulation")); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_clicked_manipulation), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_edit), menuitem); + + /* menu to add files to the list in various ways */ + popmenu_addfiles = gtk_menu_new(); + + menuitem = gtk_menu_item_new_with_label(_("Add single images...")); + g_signal_connect(menuitem, "activate", G_CALLBACK(open_file_chooser), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); + menuitem = gtk_menu_item_new_with_label(_("Add folders...")); + g_signal_connect(menuitem, "activate", G_CALLBACK(open_folder_chooser), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); + menuitem = gtk_menu_item_new_with_label(_("Add all opened images")); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_opened_files), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_addfiles), menuitem); + + /* menu to remove files to the list */ + popmenu_removefiles = gtk_menu_new(); + + menuitem = gtk_menu_item_new_with_label(_("Remove selected")); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_input_file), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_removefiles), menuitem); + menuitem = gtk_menu_item_new_with_label(_("Remove all")); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_all_input_files), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(popmenu_removefiles), menuitem); + + gtk_widget_show_all(popmenu_add); + gtk_widget_show_all(popmenu_edit); + gtk_widget_show_all(popmenu_addfiles); + gtk_widget_show_all(popmenu_removefiles); +} + +static void open_manipulation_popupmenu(GtkWidget *widget, gpointer data) +{ + if (data == NULL) { + gtk_menu_popup(GTK_MENU(popmenu_add), NULL, NULL, NULL, NULL, 0, 0); + } + else { + const gchar* item_label; + + clicked_man = (manipulation)data; + if (clicked_man->type == MANIP_USERDEF) { + item_label = ((userdef_settings)(clicked_man->settings))->procedure; + } else { + item_label = bimp_manip_get_string(clicked_man->type); + } + gtk_menu_item_set_label(g_list_first(gtk_container_get_children(GTK_CONTAINER(popmenu_edit)))->data, item_label); + gtk_menu_popup(GTK_MENU(popmenu_edit), NULL, NULL, NULL, NULL, 0, 0); + } +} + +static void open_addfiles_popupmenu(GtkWidget *widget, gpointer data) +{ + gtk_menu_popup(GTK_MENU(popmenu_addfiles), NULL, NULL, NULL, NULL, 0, 0); +} + +static void open_removefiles_popupmenu(GtkWidget *widget, gpointer data) +{ + gtk_menu_popup(GTK_MENU(popmenu_removefiles), NULL, NULL, NULL, NULL, 0, 0); +} + +static void add_manipulation_from_id(GtkMenuItem *menuitem, gpointer id) +{ + int man_id = GPOINTER_TO_INT(id); + manipulation newman = bimp_append_manipulation((manipulation_type)man_id); + if (newman == NULL) { + bimp_show_error_dialog(_("Can't add another manipulation of this kind. Only one is permitted!"), bimp_window_main); + } + else { + bimp_refresh_sequence_panel(); + + GtkAdjustment* hadj_sequence = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll_sequence)); + gtk_adjustment_set_value(hadj_sequence, gtk_adjustment_get_upper(hadj_sequence)); + + bimp_open_editwindow(newman, TRUE); + } +} + +static void edit_clicked_manipulation(GtkMenuItem *menuitem, gpointer user_data) +{ + if (clicked_man != NULL) { + bimp_open_editwindow(clicked_man, FALSE); + } +} + +static void remove_clicked_manipulation(GtkMenuItem *menuitem, gpointer user_data) +{ + if (clicked_man != NULL) { + bimp_remove_manipulation(clicked_man); + g_free(clicked_man); + bimp_refresh_sequence_panel(); + } +} + +/* update the visual of the sequence panel */ +void bimp_refresh_sequence_panel() +{ + GtkWidget* button; + + /* Remove all buttons */ + g_list_foreach(gtk_container_get_children(GTK_CONTAINER(hbox_sequence)), (GFunc)gtk_widget_destroy, NULL); + + /* Rebuild panel */ + g_slist_foreach(bimp_selected_manipulations, (GFunc)add_manipulation_button, NULL); + + button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_TOP); + gtk_widget_set_size_request (button, SEQ_BUTTON_W - 20, SEQ_BUTTON_H); + gtk_box_pack_start(GTK_BOX(hbox_sequence), button, FALSE, FALSE, 3); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_manipulation_popupmenu), NULL); + + gtk_widget_show_all(hbox_sequence); +} + +/* creates a manipulation button and appends it to the sequence box */ +static void add_manipulation_button(manipulation man) +{ + GtkWidget* button; + + button = gtk_button_new(); + gtk_button_set_image(GTK_BUTTON(button), image_new_from_resource(man->icon)); + gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_TOP); + gtk_widget_set_size_request(button, SEQ_BUTTON_W, SEQ_BUTTON_H); + gtk_box_pack_start(GTK_BOX(hbox_sequence), button, FALSE, FALSE, 3); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_manipulation_popupmenu), man); +} + +static void save_set(GtkMenuItem *menuitem, gpointer user_data) +{ + if (g_slist_length(bimp_selected_manipulations) == 0) { + bimp_show_error_dialog(_("The manipulations set is empty!"), bimp_window_main); + } else { + gchar* output_file; + char* extension = ".bimp"; + + GtkWidget* file_saver = gtk_file_chooser_dialog_new( + _("Save this set..."), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL + ); + + GtkFileFilter *filter_bimp = gtk_file_filter_new(); + gtk_file_filter_set_name(filter_bimp,"BIMP manipulations set (*.bimp)"); + gtk_file_filter_add_pattern (filter_bimp, "*.bimp"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_saver), filter_bimp); + + if (gtk_dialog_run (GTK_DIALOG(file_saver)) == GTK_RESPONSE_ACCEPT) { + + output_file = g_strdup(g_slist_nth (gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_saver)), 0)->data); + if (!g_str_has_suffix(output_file, extension)) { + output_file = g_strconcat(output_file, extension, NULL); + } + gtk_widget_destroy (file_saver); + + if (!bimp_serialize_to_file(output_file)) { + bimp_show_error_dialog(_("An error occured when importing a saved batch file :("), bimp_window_main); + } + + return; + } + + gtk_widget_destroy (file_saver); + return; + } +} + +static void load_set(GtkMenuItem *menuitem, gpointer user_data) +{ + gboolean can_continue = TRUE; + + if (g_slist_length(bimp_selected_manipulations) > 0) { + GtkWidget *dialog; + dialog = gtk_message_dialog_new( + GTK_WINDOW(bimp_window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + _("This will overwrite current manipulations set. Continue?") + ); + gtk_window_set_title(GTK_WINDOW(dialog), "Continue?"); + gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + can_continue = (result == GTK_RESPONSE_YES); + } + + if (can_continue) { + gchar* input_file; + GtkWidget* file_loader = gtk_file_chooser_dialog_new( + _("Load set..."), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL + ); + + GtkFileFilter *filter_bimp = gtk_file_filter_new(); + gtk_file_filter_set_name(filter_bimp, "BIMP manipulations set (*.bimp)"); + gtk_file_filter_add_pattern (filter_bimp, "*.bimp"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_loader), filter_bimp); + + if (gtk_dialog_run (GTK_DIALOG(file_loader)) == GTK_RESPONSE_ACCEPT) { + input_file = g_strdup(g_slist_nth (gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_loader)), 0)->data); + gtk_widget_destroy (file_loader); + + if (!bimp_deserialize_from_file(input_file)) { + bimp_show_error_dialog(_("An error occured when importing a saved batch file :("), bimp_window_main); + } + else { + bimp_refresh_sequence_panel(); + } + return; + } + + gtk_widget_destroy (file_loader); + return; + } +} + +static void open_about() +{ + const gchar *auth[] = { + "Alessandro Francesconi ", + "GitHub contributors ", + NULL }; + const gchar *license = + "This program is free software; you can redistribute it and/or modify " + "it under the terms of the GNU General Public License as published by " + "the Free Software Foundation; either version 2 of the License, or " + "(at your option) any later version. \n\n" + "This program is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License " + "along with this program; if not, write to the Free Software " + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, " + "MA 02110-1301, USA. "; + + gtk_show_about_dialog( + GTK_WINDOW(bimp_window_main), + "program-name", PLUG_IN_FULLNAME, + "version", PLUG_IN_VERSION, + "comments", _("Applies GIMP manipulations on groups of images"), + "logo", pixbuf_new_from_resource("/gimp/plugin/bimp/icons/bimp-icon.png"), + "copyright", PLUG_IN_COPYRIGHT, + "license", license, + "wrap-license", TRUE, + "website", PLUG_IN_WEBSITE, + "authors", auth, + "translator-credits", _("translator-name "), + NULL + ); +} + +/* shows an error dialog with a custom message */ +void bimp_show_error_dialog(char* message, GtkWidget* parent) +{ + GtkWidget* dialog = gtk_message_dialog_new ( + GTK_WINDOW(parent), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + message + ); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +/* suppress progress popup by installing progress handlers that do nothing */ +static const gchar* progressbar_init_hidden () +{ + GimpProgressVtable vtable = { 0, }; + + vtable.start = progressbar_start_hidden; + vtable.end = progressbar_end_hidden; + vtable.set_text = progressbar_settext_hidden; + vtable.set_value = progressbar_setvalue_hidden; + + return gimp_progress_install_vtable (&vtable, NULL); +} +static void progressbar_start_hidden (const gchar *message, gboolean cancelable, gpointer user_data) { } +static void progressbar_end_hidden (gpointer user_data) { } +static void progressbar_settext_hidden (const gchar *message, gpointer user_data) { } +static void progressbar_setvalue_hidden (double percent, gpointer user_data) { } + +void bimp_progress_bar_set(double fraction, char* text) { + if (fraction > 1.0) fraction = 1.0; + + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar_visible), fraction); + if (text != NULL) { + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), text); + } + else { + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar_visible), " "); + } +} + +void bimp_set_busy(gboolean busy) { + GList *actions_children, *tmp_child; + struct _ResponseData { gint response_id; }; + + bimp_is_busy = busy; + + gtk_dialog_set_response_sensitive (GTK_DIALOG(bimp_window_main), GTK_RESPONSE_CLOSE, !busy); + gtk_dialog_set_response_sensitive (GTK_DIALOG(bimp_window_main), GTK_RESPONSE_HELP, !busy); + + /* procedure that hides and shows some widgets in the dialog's action area. Compatible with GTK+ 2.16 */ + GtkWidget* actions = gtk_dialog_get_action_area (GTK_DIALOG(bimp_window_main)); + actions_children = gtk_container_get_children (GTK_CONTAINER (actions)); + tmp_child = actions_children; + while (tmp_child != NULL) + { + GtkWidget *widget = tmp_child->data; + struct _ResponseData *rd = g_object_get_data (G_OBJECT (widget), "gtk-dialog-response-data"); + + if (rd && rd->response_id == GTK_RESPONSE_APPLY) { + if (busy) { + gtk_widget_hide (widget); + } else { + gtk_widget_show (widget); + } + } + else if (rd && rd->response_id == GTK_RESPONSE_CANCEL) { + if (!busy) { + gtk_widget_hide (widget); + } else { + gtk_widget_show (widget); + } + } + + tmp_child = g_list_next (tmp_child); + } + g_list_free (actions_children); + + gtk_widget_set_sensitive(panel_sequence, !busy); + gtk_widget_set_sensitive(panel_options, !busy); +} diff --git a/src/bimp-manipulations.h b/src/bimp-manipulations.h index e9bdd9f..1443a1b 100644 --- a/src/bimp-manipulations.h +++ b/src/bimp-manipulations.h @@ -1,284 +1,291 @@ -#ifndef __BIMP_MANIPULATIONS_H__ -#define __BIMP_MANIPULATIONS_H__ - -#include -#include -#include - -#define RENAME_KEY_ORIG "$$" -#define RENAME_KEY_COUNT "##" -#define RENAME_KEY_DATETIME "@@" - -typedef enum manipulation_type { - MANIP_RESIZE = 0, - MANIP_CROP, - MANIP_FLIPROTATE, - MANIP_COLOR, - MANIP_SHARPBLUR, - MANIP_WATERMARK, - MANIP_CHANGEFORMAT, - MANIP_RENAME, - MANIP_USERDEF, - MANIP_END -} manipulation_type; - -typedef enum resize_mode { - RESIZE_PERCENT = 0, - RESIZE_PIXEL, - RESIZE_DISABLE, - RESIZE_END -} resize_mode; - -typedef enum stretch_mode { - STRETCH_ALLOW = 0, - STRETCH_ASPECT, - STRETCH_PADDED, - STRETCH_END -} stretch_mode; - -typedef enum crop_preset { - CROP_PRESET_11 = 0, - CROP_PRESET_32, - CROP_PRESET_43, - CROP_PRESET_169, - CROP_PRESET_1610, - CROP_PRESET_EUPORT, - CROP_PRESET_PHONE, - CROP_PRESET_TALLPHONE, - CROP_PRESET_TABLET, - CROP_PRESET_CUSTOM, - CROP_PRESET_END -} crop_preset; -static const short int crop_preset_ratio[][2] = { - {1,1}, /* CROP_PRESET_11 */ - {3,2}, /* CROP_PRESET_32 */ - {4,3}, /* CROP_PRESET_43 */ - {16,9}, /* CROP_PRESET_169 */ - {16,10}, /* CROP_PRESET_1610 */ - {7,9}, /* CROP_PRESET_EUPORT */ - {2,3}, /* CROP_PRESET_PHONE */ - {40,71}, /* CROP_PRESET_TALLPHONE */ - {3,4} /* CROP_PRESET_TABLET */ -}; - -typedef enum crop_start_position { - CROP_START_CC = 0, - CROP_START_TL, - CROP_START_TR, - CROP_START_BL, - CROP_START_BR, - CROP_START_XY, - CROP_START_END -} crop_start_position; - -typedef enum format_type { - FORMAT_BMP = 0, - FORMAT_GIF, - FORMAT_ICON, - FORMAT_JPEG, - FORMAT_PNG, - FORMAT_TGA, - FORMAT_TIFF, - FORMAT_HEIF, - FORMAT_WEBP, - FORMAT_EXR, - FORMAT_END -} format_type; -static const char* format_type_string[][2] = { - {"bmp", "Bitmap (.bmp)"}, /* FORMAT_BMP */ - {"gif", "Gif (.gif)"}, /* FORMAT_GIF */ - {"ico", "Icon (.ico)"}, /* FORMAT_ICON */ - {"jpg", "Jpeg (.jpeg)"}, /* FORMAT_JPEG */ - {"png", "Portable Network Graphics (.png)"}, /* FORMAT_PNG */ - {"tga", "Targa (.tga)"}, /* FORMAT_TGA */ - {"tiff", "Tagged Image File Format (.tiff)"}, /* FORMAT_TIFF */ - {"heif", "Heif (.heif)"}, /* FORMAT_HEIF */ - {"webp", "WebP (.webp)"}, /* FORMAT_WEBP */ - {"exr", "OpenEXR (.exr)"} /* FORMAT_EXR */ -}; - -// First two bits = column, second two bits = row -typedef enum watermark_position { - WM_POS_TL = 0, - WM_POS_TC, - WM_POS_TR, - WM_POS_CL, - WM_POS_CC, - WM_POS_CR, - WM_POS_BL, - WM_POS_BC, - WM_POS_BR, - WM_POS_END -} watermark_position; - -typedef enum watermark_image_sizemode { - WM_IMG_NOSIZE = 0, - WM_IMG_SIZEW, - WM_IMG_SIZEH, - WM_IMG_END -} watermark_image_sizemode; - -typedef void *manipulation_settings; -typedef void *format_params; - -/* Single manipulation struct */ -typedef struct manip_str { - manipulation_type type; - char* icon; - manipulation_settings settings; /* Pointer to one of the following settings structs */ -} *manipulation; - -/* Settings structs */ - -typedef struct manip_resize_set { - gdouble new_w_pc; - gdouble new_h_pc; - gint new_w_px; - gint new_h_px; - resize_mode resize_mode_width; - resize_mode resize_mode_height; - stretch_mode stretch_mode; - GdkColor padding_color; - guint16 padding_color_alpha; - GimpInterpolationType interpolation; - gboolean change_res; - gdouble new_res_x; - gdouble new_res_y; -} *resize_settings; - -typedef struct manip_crop_set { - gint new_w; - gint new_h; - gboolean manual; - crop_preset ratio; - float custom_ratio1; - float custom_ratio2; - crop_start_position start_pos; -} *crop_settings; - -typedef struct manip_fliprotate_set { - gboolean flip_h; - gboolean flip_v; - gboolean rotate; - GimpRotationType rotation_type; -} *fliprotate_settings; - -typedef struct manip_color_set { - gdouble brightness; - gdouble contrast; - gboolean levels_auto; - gboolean grayscale; - char* curve_file; -} *color_settings; - -typedef struct manip_sharpblur_set { - int amount; -} *sharpblur_settings; - -typedef struct manip_watermark_set { - gboolean mode; /* TRUE = text mode; FALSE = image mode */ - gchar* text; - PangoFontDescription* font; - GdkColor color; - char* image_file; - watermark_image_sizemode image_sizemode; - float image_size_percent; - float opacity; - int edge_distance; - watermark_position position; -} *watermark_settings; - -typedef struct manip_changeformat_set { - format_type format; - format_params params; /* Pointer to one of the format params structs */ -} *changeformat_settings; - -typedef struct changeformat_params_gif { - gboolean interlace; -} *format_params_gif; - -typedef struct changeformat_params_jpeg { - float quality; - float smoothing; - gboolean entropy; - gboolean progressive; - gchar* comment; - int subsampling; - gboolean baseline; - int markers; - int dct; -} *format_params_jpeg; - -typedef struct changeformat_params_png { - gboolean interlace; - int compression; - gboolean savebgc; - gboolean savegamma; - gboolean saveoff; - gboolean savephys; - gboolean savetime; - gboolean savecomm; - gboolean savetrans; -} *format_params_png; - -typedef struct changeformat_params_tga { - gboolean rle; - int origin; -} *format_params_tga; - -typedef struct changeformat_params_tiff { - int compression; -} *format_params_tiff; - -typedef struct changeformat_params_heif { - gboolean lossless; - int quality; -} *format_params_heif; - -typedef struct changeformat_params_webp { - int preset; - gboolean lossless; - float quality; - float alpha_quality; - gboolean animation; - gboolean anim_loop; - gboolean minimize_size; - int kf_distance; - gboolean exif; - gboolean iptc; - gboolean xmp; - int delay; - int force_delay; -} *format_params_webp; - -typedef struct manip_rename_set { - gchar* pattern; -} *rename_settings; - -typedef struct manip_userdef_set { - gchar* procedure; - gint num_params; - GimpParam* params; /* array of procedure params (GimpParamDef structs) */ -} *userdef_settings; - -manipulation bimp_append_manipulation(manipulation_type); -void bimp_remove_manipulation(manipulation); -gboolean bimp_list_contains_manip(manipulation_type); -gboolean bimp_list_contains_savingplugin(void); -manipulation bimp_list_get_manip(manipulation_type); -GSList* bimp_list_get_manip_all(manipulation_type); -char* bimp_manip_get_string(manipulation_type); - -manipulation manipulation_sharpblur_new(void); -manipulation manipulation_resize_new(void); -manipulation manipulation_crop_new(void); -manipulation manipulation_fliprotate_new(void); -manipulation manipulation_color_new(void); -manipulation manipulation_watermark_new(void); -manipulation manipulation_changeformat_new(void); -manipulation manipulation_rename_new(void); -manipulation manipulation_userdef_new(void); - -extern GSList* bimp_selected_manipulations; /* Manipulations selected by user */ - -#endif +#ifndef __BIMP_MANIPULATIONS_H__ +#define __BIMP_MANIPULATIONS_H__ + +#include +#include +#include + +#define RENAME_KEY_ORIG "$$" +#define RENAME_KEY_COUNT "##" +#define RENAME_KEY_DATETIME "@@" + +typedef enum manipulation_type { + MANIP_RESIZE = 0, + MANIP_CROP, + MANIP_FLIPROTATE, + MANIP_COLOR, + MANIP_SHARPBLUR, + MANIP_WATERMARK, + MANIP_CHANGEFORMAT, + MANIP_RENAME, + MANIP_USERDEF, + MANIP_END +} manipulation_type; + +typedef enum resize_mode { + RESIZE_PERCENT = 0, + RESIZE_PIXEL, + RESIZE_DISABLE, + RESIZE_END +} resize_mode; + +typedef enum stretch_mode { + STRETCH_ALLOW = 0, + STRETCH_ASPECT, + STRETCH_PADDED, + STRETCH_END +} stretch_mode; + +typedef enum crop_preset { + CROP_PRESET_11 = 0, + CROP_PRESET_32, + CROP_PRESET_43, + CROP_PRESET_169, + CROP_PRESET_1610, + CROP_PRESET_EUPORT, + CROP_PRESET_PHONE, + CROP_PRESET_TALLPHONE, + CROP_PRESET_TABLET, + CROP_PRESET_CUSTOM, + CROP_PRESET_END +} crop_preset; +static const short int crop_preset_ratio[][2] = { + {1,1}, /* CROP_PRESET_11 */ + {3,2}, /* CROP_PRESET_32 */ + {4,3}, /* CROP_PRESET_43 */ + {16,9}, /* CROP_PRESET_169 */ + {16,10}, /* CROP_PRESET_1610 */ + {7,9}, /* CROP_PRESET_EUPORT */ + {2,3}, /* CROP_PRESET_PHONE */ + {40,71}, /* CROP_PRESET_TALLPHONE */ + {3,4} /* CROP_PRESET_TABLET */ +}; + +typedef enum crop_start_position { + CROP_START_CC = 0, + CROP_START_TL, + CROP_START_TR, + CROP_START_BL, + CROP_START_BR, + CROP_START_XY, + CROP_START_END +} crop_start_position; + +typedef enum format_type { + FORMAT_BMP = 0, + FORMAT_GIF, + FORMAT_ICON, + FORMAT_JPEG, + FORMAT_PNG, + FORMAT_TGA, + FORMAT_TIFF, + FORMAT_HEIF, + FORMAT_WEBP, + FORMAT_AVIF, + FORMAT_EXR, + FORMAT_END +} format_type; +static const char* format_type_string[][2] = { + {"bmp", "Bitmap (.bmp)"}, /* FORMAT_BMP */ + {"gif", "Gif (.gif)"}, /* FORMAT_GIF */ + {"ico", "Icon (.ico)"}, /* FORMAT_ICON */ + {"jpg", "Jpeg (.jpeg)"}, /* FORMAT_JPEG */ + {"png", "Portable Network Graphics (.png)"}, /* FORMAT_PNG */ + {"tga", "Targa (.tga)"}, /* FORMAT_TGA */ + {"tiff", "Tagged Image File Format (.tiff)"}, /* FORMAT_TIFF */ + {"heif", "Heif (.heif)"}, /* FORMAT_HEIF */ + {"webp", "WebP (.webp)"}, /* FORMAT_WEBP */ + {"avif", "AV1 Image Format (.avif)"}, /* FORMAT_AVIF */ + {"exr", "OpenEXR (.exr)"} /* FORMAT_EXR */ +}; + +// First two bits = column, second two bits = row +typedef enum watermark_position { + WM_POS_TL = 0, + WM_POS_TC, + WM_POS_TR, + WM_POS_CL, + WM_POS_CC, + WM_POS_CR, + WM_POS_BL, + WM_POS_BC, + WM_POS_BR, + WM_POS_END +} watermark_position; + +typedef enum watermark_image_sizemode { + WM_IMG_NOSIZE = 0, + WM_IMG_SIZEW, + WM_IMG_SIZEH, + WM_IMG_END +} watermark_image_sizemode; + +typedef void *manipulation_settings; +typedef void *format_params; + +/* Single manipulation struct */ +typedef struct manip_str { + manipulation_type type; + char* icon; + manipulation_settings settings; /* Pointer to one of the following settings structs */ +} *manipulation; + +/* Settings structs */ + +typedef struct manip_resize_set { + gdouble new_w_pc; + gdouble new_h_pc; + gint new_w_px; + gint new_h_px; + resize_mode resize_mode_width; + resize_mode resize_mode_height; + stretch_mode stretch_mode; + GdkColor padding_color; + guint16 padding_color_alpha; + GimpInterpolationType interpolation; + gboolean change_res; + gdouble new_res_x; + gdouble new_res_y; +} *resize_settings; + +typedef struct manip_crop_set { + gint new_w; + gint new_h; + gboolean manual; + crop_preset ratio; + float custom_ratio1; + float custom_ratio2; + crop_start_position start_pos; +} *crop_settings; + +typedef struct manip_fliprotate_set { + gboolean flip_h; + gboolean flip_v; + gboolean rotate; + GimpRotationType rotation_type; +} *fliprotate_settings; + +typedef struct manip_color_set { + gdouble brightness; + gdouble contrast; + gboolean levels_auto; + gboolean grayscale; + char* curve_file; +} *color_settings; + +typedef struct manip_sharpblur_set { + int amount; +} *sharpblur_settings; + +typedef struct manip_watermark_set { + gboolean mode; /* TRUE = text mode; FALSE = image mode */ + gchar* text; + PangoFontDescription* font; + GdkColor color; + char* image_file; + watermark_image_sizemode image_sizemode; + float image_size_percent; + float opacity; + int edge_distance; + watermark_position position; +} *watermark_settings; + +typedef struct manip_changeformat_set { + format_type format; + format_params params; /* Pointer to one of the format params structs */ +} *changeformat_settings; + +typedef struct changeformat_params_gif { + gboolean interlace; +} *format_params_gif; + +typedef struct changeformat_params_jpeg { + float quality; + float smoothing; + gboolean entropy; + gboolean progressive; + gchar* comment; + int subsampling; + gboolean baseline; + int markers; + int dct; +} *format_params_jpeg; + +typedef struct changeformat_params_png { + gboolean interlace; + int compression; + gboolean savebgc; + gboolean savegamma; + gboolean saveoff; + gboolean savephys; + gboolean savetime; + gboolean savecomm; + gboolean savetrans; +} *format_params_png; + +typedef struct changeformat_params_tga { + gboolean rle; + int origin; +} *format_params_tga; + +typedef struct changeformat_params_tiff { + int compression; +} *format_params_tiff; + +typedef struct changeformat_params_heif { + gboolean lossless; + int quality; +} *format_params_heif; + +typedef struct changeformat_params_webp { + int preset; + gboolean lossless; + float quality; + float alpha_quality; + gboolean animation; + gboolean anim_loop; + gboolean minimize_size; + int kf_distance; + gboolean exif; + gboolean iptc; + gboolean xmp; + int delay; + int force_delay; +} *format_params_webp; + +typedef struct changeformat_params_avif { + gboolean lossless; + int quality; +} *format_params_avif; + +typedef struct manip_rename_set { + gchar* pattern; +} *rename_settings; + +typedef struct manip_userdef_set { + gchar* procedure; + gint num_params; + GimpParam* params; /* array of procedure params (GimpParamDef structs) */ +} *userdef_settings; + +manipulation bimp_append_manipulation(manipulation_type); +void bimp_remove_manipulation(manipulation); +gboolean bimp_list_contains_manip(manipulation_type); +gboolean bimp_list_contains_savingplugin(void); +manipulation bimp_list_get_manip(manipulation_type); +GSList* bimp_list_get_manip_all(manipulation_type); +char* bimp_manip_get_string(manipulation_type); + +manipulation manipulation_sharpblur_new(void); +manipulation manipulation_resize_new(void); +manipulation manipulation_crop_new(void); +manipulation manipulation_fliprotate_new(void); +manipulation manipulation_color_new(void); +manipulation manipulation_watermark_new(void); +manipulation manipulation_changeformat_new(void); +manipulation manipulation_rename_new(void); +manipulation manipulation_userdef_new(void); + +extern GSList* bimp_selected_manipulations; /* Manipulations selected by user */ + +#endif diff --git a/src/bimp-operate.c b/src/bimp-operate.c index c72dc2b..46c033a 100644 --- a/src/bimp-operate.c +++ b/src/bimp-operate.c @@ -1,1464 +1,1491 @@ -// Functions called when the user clicks on 'APPLY' - - -#include -#include -#include -#include -#include -#include -#include -#include "bimp-operate.h" -#include "bimp.h" -#include "bimp-manipulations.h" -#include "bimp-gui.h" -#include "bimp-utils.h" -#include "bimp-serialize.h" -#include "plugin-intl.h" - -static gboolean process_image(gpointer); - -static gboolean apply_manipulation(manipulation, image_output); -static gboolean apply_resize(resize_settings, image_output); -static gboolean apply_crop(crop_settings, image_output); -static gboolean apply_fliprotate(fliprotate_settings, image_output); -static gboolean apply_color(color_settings, image_output); -static gboolean apply_sharpblur(sharpblur_settings, image_output); -static gboolean apply_watermark(watermark_settings, image_output); -static void calc_watermark_xy (int, int, int, int, watermark_position, int, gdouble*, gdouble*); -static gboolean apply_userdef(userdef_settings, image_output); -static gboolean apply_rename(rename_settings, image_output, char*); - -static gboolean image_save(format_type, image_output, format_params); -static gboolean image_save_bmp(image_output); -static gboolean image_save_gif(image_output, gboolean); -static gboolean image_save_icon(image_output); -static gboolean image_save_jpeg(image_output, float, float, gboolean, gboolean, gchar*, int, gboolean, int, int); -static gboolean image_save_heif(image_output, int, gboolean); -static gboolean image_save_png(image_output, gboolean, int, gboolean, gboolean, gboolean, gboolean, gboolean, gboolean, gboolean); -static gboolean image_save_tga(image_output, gboolean, int); -static gboolean image_save_tiff(image_output, int); -static gboolean image_save_webp(image_output, int, gboolean, float, float, gboolean, gboolean, gboolean, int, gboolean, gboolean, gboolean, int, int); -static gboolean image_save_exr(image_output); - -static int overwrite_result(char*, GtkWidget*); - -static char* current_datetime; -static int processed_count; -static int success_count; -static int total_images; - -static char* common_folder_path; - -static gboolean list_contains_changeformat; -static gboolean list_contains_rename; -static gboolean list_contains_watermark; -static gboolean list_contains_savingplugin; - -// set of variables to be used when doing Curve color correction -// they are global so the batch process will read the source curve file once -static gboolean colorcurve_init; -static int colorcurve_num_points_v; -static gdouble* colorcurve_ctr_points_v; -static int colorcurve_num_points_r; -static gdouble* colorcurve_ctr_points_r; -static int colorcurve_num_points_g; -static gdouble* colorcurve_ctr_points_g; -static int colorcurve_num_points_b; -static gdouble* colorcurve_ctr_points_b; -static int colorcurve_num_points_a; -static gdouble* colorcurve_ctr_points_a; - -void bimp_start_batch(gpointer parent_dialog) -{ - bimp_set_busy(TRUE); - - // initialization - g_print("\nBIMP - Batch Manipulation Plugin\nStart batch processing...\n"); - processed_count = 0; - success_count = 0; - total_images = g_slist_length(bimp_input_filenames); - bimp_progress_bar_set(0.0, ""); - - bimp_init_batch(); - - current_datetime = get_datetime(); - common_folder_path = NULL; - - if (bimp_opt_keepfolderhierarchy){ - int i, j; - gboolean need_hierarchy = FALSE; - char * path = NULL; - char ** common_folder; - char ** current_folder; - size_t common_folder_size, current_folder_size; - - path = comp_get_filefolder(g_slist_nth(bimp_input_filenames,0)->data); - - common_folder = get_path_folders(path); - common_folder_size = 0; - for (common_folder_size = 0; common_folder[common_folder_size] != NULL; ++common_folder_size); - - for (i=1; i < total_images ; i++) - { - path = comp_get_filefolder(g_slist_nth(bimp_input_filenames,i)->data); - current_folder = get_path_folders (path); - for (current_folder_size = 0; current_folder[current_folder_size] != NULL; ++current_folder_size); - - // The common path is at most as long as the shortest path - while (common_folder_size > current_folder_size) - { - need_hierarchy = TRUE; - g_free(common_folder[common_folder_size-1]); - common_folder[common_folder_size-1] = NULL; - common_folder_size--; - } - - for (j=0; j < common_folder_size; ++j) - { - if (strcmp(common_folder[j], current_folder[j]) != 0) { - need_hierarchy = TRUE; - while (common_folder_size > j) - { - g_free(common_folder[common_folder_size-1]); - common_folder[common_folder_size-1] = NULL; - common_folder_size--; - } - break; - } - } - - g_strfreev(current_folder); - } - - if (need_hierarchy) - common_folder_path = g_strjoinv(FILE_SEPARATOR_STR, common_folder); - - g_strfreev(common_folder); - } - - // start on a new thread - guint batch_idle_tag = g_idle_add((GSourceFunc)process_image, parent_dialog); -} - -void bimp_init_batch() -{ - list_contains_changeformat = bimp_list_contains_manip(MANIP_CHANGEFORMAT); - list_contains_rename = bimp_list_contains_manip(MANIP_RENAME); - list_contains_watermark = bimp_list_contains_manip(MANIP_WATERMARK); - list_contains_savingplugin = bimp_list_contains_savingplugin(); - - colorcurve_init = FALSE; -} - -static gboolean process_image(gpointer parent) -{ - gboolean success = TRUE; - - image_output imageout = (image_output)g_malloc(sizeof(struct imageout_str)); - char* orig_filename = NULL; - char* orig_basename = NULL; - char* orig_file_ext = NULL; - char* output_file_comp = NULL; - - // store original file path and name - orig_filename = g_slist_nth (bimp_input_filenames, processed_count)->data; - orig_basename = g_strdup(comp_get_filename(orig_filename)); - - // store original extension and check error cases - orig_file_ext = g_strdup(strrchr(orig_basename, '.')); - if (orig_file_ext == NULL) { - /* under Linux, GtkFileChooser lets to pick an image file without extension, but GIMP cannot - * save it back if its format remains unchanged. Operation can continue only if a MANIP_CHANGEFORMAT - * is present */ - if (list_contains_changeformat) { - orig_file_ext = g_malloc0(sizeof(char)); - } - else { - bimp_show_error_dialog(g_strdup_printf(_("Can't save image \"%s\": input file has no extension.\nYou can solve this error by adding a \"Change format or compression\" step"), orig_basename), bimp_window_main); - success = FALSE; - goto process_end; - } - } - else if (g_ascii_strcasecmp(orig_file_ext, ".svg") == 0 && !list_contains_changeformat) { - bimp_show_error_dialog(g_strdup_printf(_("GIMP can't save %s back to its original SVG format.\nYou can solve this error by adding a \"Change format or compression\" step"), orig_basename), bimp_window_main); - success = FALSE; - goto process_end; - } - - g_print("\nWorking on file %d of %d (%s)\n", processed_count + 1, total_images, orig_filename); - bimp_progress_bar_set(((double)processed_count)/total_images, g_strdup_printf(_("Working on file \"%s\"..."), orig_basename)); - - // rename and save process... - orig_basename[strlen(orig_basename) - strlen(orig_file_ext)] = '\0'; // remove extension from basename - - // check if a rename pattern is defined - if(list_contains_rename) { - g_print("Applying RENAME...\n"); - apply_rename((rename_settings)(bimp_list_get_manip(MANIP_RENAME))->settings, imageout, orig_basename); - } - else { - imageout->filename = orig_basename; - } - - // To keep the folder hierarchy - if (common_folder_path == NULL) { - // Not selected or required, everything goes into the same destination folder - output_file_comp = g_malloc0(sizeof(char)); - } - else { - // keep folders to add to output path - output_file_comp = - g_strndup(&orig_filename[strlen(common_folder_path)+1], - strlen(orig_filename)-(strlen(common_folder_path)+1) - -strlen(orig_basename)-strlen(orig_file_ext)); - } - - if (strlen(output_file_comp) > 0) { -#ifdef _WIN32 - // Clean output_file_comp - // Should only be concerned for ':' in Drive letter - int i; - for (i = 0; i < strlen(output_file_comp); ++i) - if ( output_file_comp[i] == ':' ) - output_file_comp[i] = '_'; -#endif - // Create path if needed - g_mkdir_with_parents( - g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, NULL), - 0777 - ); - } - - // save the final image in output dir with proper format and params - format_type final_format = -1; - format_params params = NULL; - - if(list_contains_changeformat) { - changeformat_settings settings = (changeformat_settings)(bimp_list_get_manip(MANIP_CHANGEFORMAT))->settings; - final_format = settings->format; - params = settings->params; - - g_print("Changing FORMAT to %s\n", format_type_string[final_format][0]); - imageout->filename = g_strconcat(imageout->filename, ".", format_type_string[final_format][0], NULL); // append new file extension - imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path - } - // TO CHECK what apply_userdef does once coded - - else if (list_contains_savingplugin) { - // leave filename without extension and proceed calling each saving plugin - imageout->filename = g_strconcat(imageout->filename, ".dds", NULL); - imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path - - GSList *iterator = NULL; - manipulation man; - for (iterator = bimp_selected_manipulations; iterator; iterator = iterator->next) { - man = (manipulation)(iterator->data); - if (man->type == MANIP_USERDEF && strstr(((userdef_settings)(man->settings))->procedure, "-save") != NULL) { - /* found a saving plugin, execute it - // TODO!!!! This won't work yet, we need a way to extract the file extension managed by the selected saving plugin - * e.g. "file-dds-save" -> "dds" (don't do it with regexp on plugin's name... too easy...) */ - apply_userdef((userdef_settings)(man->settings), imageout); - } - } - } - else { - // if not specified, save in original format - imageout->filename = g_strconcat(imageout->filename, orig_file_ext, NULL); // append old file extension - imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path - final_format = -1; - } - - // check if writing possible - gboolean will_overwrite = FALSE; - if (bimp_opt_alertoverwrite != BIMP_OVERWRITE_SKIP_ASK) { - // file already exists ? - will_overwrite = g_file_test(imageout->filepath, G_FILE_TEST_IS_REGULAR); - if (will_overwrite) { - // "Don't overwrite" without confirmation - if (bimp_opt_alertoverwrite == BIMP_DONT_OVERWRITE_SKIP_ASK) { - g_print("Destination file already exists and won't be overwritten\n"); - goto process_end; - } - else { - // Ask what to do - int ow_res = overwrite_result(imageout->filepath, parent); - if (ow_res == 0) { - g_print("Destination file already exists and user select to don't overwrite\n"); - goto process_end; - } - } - } - } - - // apply all the main manipulations - bimp_apply_drawable_manipulations(imageout, (gchar*)orig_filename, (gchar*)orig_basename); - - time_t mod_time = -1; - if (will_overwrite && bimp_opt_keepdates) { - // I must keep the dates even if the file has been overwritten - mod_time = get_modification_time(imageout->filepath); - if (mod_time == -1) g_print("An error occurred when retrieving the modification date of file.\n"); - } - - // Save - g_print("Saving file %s in %s\n", imageout->filename, imageout->filepath); - image_save(final_format, imageout, params); - - if (will_overwrite && bimp_opt_keepdates && mod_time > -1) { - // replace with the old dates - int res = set_modification_time(imageout->filepath, mod_time); - if (res == -1) g_print("An error occurred when replacing the modification date of file.\n"); - } - - gimp_image_delete(imageout->image_id); // is it useful? - -process_end: - - g_free(orig_basename); - g_free(orig_file_ext); - g_free(output_file_comp); - g_free(imageout->filename); - g_free(imageout->filepath); - g_free(imageout); - - processed_count++; - if (success) success_count++; - - // TODO: errors check here - if (!bimp_is_busy) { - bimp_progress_bar_set(0.0, _("Operations stopped")); - g_print("\nStopped, %d files processed.\n", processed_count); - return FALSE; - } - else { - if (processed_count == total_images) { - int errors_count = processed_count - success_count; - bimp_progress_bar_set(1.0, g_strdup_printf(_("End, all files have been processed with %d errors"), errors_count)); - g_print("\nEnd, %d files have been processed with %d errors.\n", processed_count, errors_count); - - bimp_set_busy(FALSE); - - return FALSE; - } - else { - return TRUE; - } - } -} - -void bimp_apply_drawable_manipulations(image_output imageout, gchar* orig_filename, gchar* orig_basename) -{ - imageout->image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE, orig_filename, orig_basename); // load file and get image id - // LOAD ERROR CHECK HERE - g_print("Image ID is %d\n", imageout->image_id); - - // stop saving the undo steps for this session - gimp_image_undo_freeze(imageout->image_id); - - imageout->drawable_ids = gimp_image_get_layers(imageout->image_id, &imageout->drawable_count); // get all drawables - g_print("Total drawables count: %d\n", imageout->drawable_count); - - // apply all the intermediate manipulations - g_slist_foreach(bimp_selected_manipulations, (GFunc)apply_manipulation, imageout); - - // watermark at last - if(list_contains_watermark) { - GSList* watermarks = bimp_list_get_manip_all(MANIP_WATERMARK); - GSList *iterator = NULL; - for (iterator = watermarks; iterator; iterator = iterator->next) { - g_print("Applying WATERMARK...\n"); - apply_watermark((watermark_settings)(((manipulation)(iterator->data))->settings), imageout); - } - - - } - - // re-enable undo - gimp_image_undo_thaw(imageout->image_id); -} - -static gboolean apply_manipulation(manipulation man, image_output out) -{ - gboolean success = TRUE; - - if (man->type == MANIP_RESIZE) { - g_print("Applying RESIZE...\n"); - apply_resize((resize_settings)(bimp_list_get_manip(MANIP_RESIZE))->settings, out); - } - else if (man->type == MANIP_CROP) { - g_print("Applying CROP...\n"); - apply_crop((crop_settings)(bimp_list_get_manip(MANIP_CROP))->settings, out); - } - else if (man->type == MANIP_FLIPROTATE) { - g_print("Applying FLIP OR ROTATE...\n"); - success = apply_fliprotate((fliprotate_settings)(man->settings), out); - } - else if (man->type == MANIP_COLOR) { - g_print("Applying COLOR CORRECTION...\n"); - success = apply_color((color_settings)(man->settings), out); - } - else if (man->type == MANIP_SHARPBLUR) { - g_print("Applying SHARPBLUR...\n"); - success = apply_sharpblur((sharpblur_settings)(man->settings), out); - } - else if (man->type == MANIP_USERDEF && strstr(((userdef_settings)(man->settings))->procedure, "-save") == NULL) { - g_print("Applying %s...\n", ((userdef_settings)(man->settings))->procedure); - success = apply_userdef((userdef_settings)(man->settings), out); - } - - return success; -} - -static gboolean apply_resize(resize_settings settings, image_output out) -{ - gboolean success = FALSE; - gint orig_w, orig_h, final_w, final_h, view_w, view_h; - gdouble orig_res_x, orig_res_y; - - if (settings->change_res) { - success = gimp_image_get_resolution( - out->image_id, - &orig_res_x, - &orig_res_y - ); - - if ((settings->new_res_x != orig_res_x) || (settings->new_res_y != orig_res_y)) { - // change resolution - success = gimp_image_set_resolution( - out->image_id, - settings->new_res_x, - settings->new_res_y - ); - } - } - - orig_w = gimp_image_width(out->image_id); - orig_h = gimp_image_height(out->image_id); - - if (settings->resize_mode_width == RESIZE_DISABLE && settings->resize_mode_height == RESIZE_DISABLE) { - return !settings->change_res || success; - } - - gdouble newwpct, newwpctmax; - if (settings->resize_mode_width == RESIZE_PERCENT) { - newwpct = newwpctmax = settings->new_w_pc / 100.0; - } - else if(settings->resize_mode_width == RESIZE_PIXEL) { - newwpct = newwpctmax = (double)settings->new_w_px / (double)orig_w; - } - else { - newwpct = 1; - newwpctmax = DBL_MAX; - } - - gdouble newhpct, newhpctmax; - if (settings->resize_mode_height == RESIZE_PERCENT) { - newhpct = newhpctmax = settings->new_h_pc / 100.0; - } - else if(settings->resize_mode_height == RESIZE_PIXEL) { - newhpct = newhpctmax = (double)settings->new_h_px / (double)orig_h; - } - else { - newhpct = 1; - newhpctmax = DBL_MAX; - } - - if(settings->stretch_mode == STRETCH_ASPECT) { - gdouble newpct = min(newwpctmax, newhpctmax); - - final_w = view_w = round(orig_w * newpct); - final_h = view_h = round(orig_h * newpct); - } - else if (settings->stretch_mode == STRETCH_PADDED) { - gdouble newpct = min(newwpctmax, newhpctmax); - - final_w = round(orig_w * newpct); - final_h = round(orig_h * newpct); - view_w = round(orig_w * newwpct); - view_h = round(orig_h * newhpct); - } - else { - final_w = view_w = round(orig_w * newwpct); - final_h = view_h = round(orig_h * newhpct); - } - - // use gimp_image_scale instead - GimpInterpolationType old_interpolation; - old_interpolation = gimp_context_get_interpolation(); - - success = gimp_context_set_interpolation (settings->interpolation); - success = gimp_image_scale ( - out->image_id, - final_w, - final_h - ); - success = gimp_context_set_interpolation (old_interpolation); - - // add a padding if requested - if (settings->stretch_mode == STRETCH_PADDED) { - - // the padding will be drawn using a coloured layer at the bottom of the image - int imageType = gimp_image_base_type(out->image_id); - int layerType; - if (imageType == 2) layerType = 4; // see http://oldhome.schmorp.de/marc/pdb/gimp_layer_new.html - else if (imageType == 1) layerType = 2; - else layerType = 0; - - if (gimp_drawable_has_alpha(out->drawable_ids[0])) layerType ++; - - gint32 layerId = gimp_layer_new( - out->image_id, - "padding_layer", - view_w, view_h, - layerType, - (settings->padding_color_alpha / (float)G_MAXUINT16) * 100, - GIMP_LAYER_MODE_NORMAL_LEGACY - ); - - gimp_image_insert_layer( - out->image_id, - layerId, - 0, - 0 - ); - - gimp_image_lower_item_to_bottom(out->image_id, layerId); - - // fill it with the selected color - GimpRGB old_background, new_background; - - gimp_context_get_background(&old_background); - gimp_rgb_parse_hex (&new_background, gdk_color_to_string(&(settings->padding_color)), strlen(gdk_color_to_string(&(settings->padding_color)))); - gimp_context_set_background(&new_background); - gimp_drawable_fill(layerId, GIMP_FILL_BACKGROUND); - gimp_context_set_background(&old_background); - - // move it to the center - gimp_item_transform_translate(layerId, -abs(view_w - final_w) / 2, -abs(view_h - final_h) / 2); - - // finish changing the canvas size accordingly - success = gimp_image_resize_to_layers(out->image_id); - } - - return success; -} - -static gboolean apply_crop(crop_settings settings, image_output out) -{ - gboolean success = TRUE; - gint newWidth, newHeight, oldWidth, oldHeight, posX = 0, posY = 0; - gboolean keepX = FALSE, keepY = FALSE; - - oldWidth = gimp_image_width(out->image_id); - oldHeight = gimp_image_height(out->image_id); - - if (settings->manual) { - newWidth = min(oldWidth, settings->new_w); - newHeight = min(oldHeight, settings->new_h); - } - else { - float ratio1, ratio2; - if (settings->ratio == CROP_PRESET_CUSTOM) { - ratio1 = settings->custom_ratio1; - ratio2 = settings->custom_ratio2; - } - else { - ratio1 = (float)crop_preset_ratio[settings->ratio][0]; - ratio2 = (float)crop_preset_ratio[settings->ratio][1]; - } - - if (( (float)oldWidth / oldHeight ) > ( ratio1 / ratio2) ) { - // crop along the width - newHeight = oldHeight; - newWidth = round(( ratio1 * (float)newHeight ) / ratio2); - keepY = TRUE; - } else { - // crop along the height - newWidth = oldWidth; - newHeight = round(( ratio2 * (float)newWidth) / ratio1); - keepX = TRUE; - } - } - - switch (settings->start_pos) { - case CROP_START_TL: - posX = 0; - posY = 0; - break; - - case CROP_START_TR: - posX = (oldWidth - newWidth); - posY = 0; - break; - - case CROP_START_BL: - posX = 0; - posY = (oldHeight - newHeight); - break; - - case CROP_START_BR: - posX = (oldWidth - newWidth); - posY = (oldHeight - newHeight); - break; - - default: - if (!keepX) posX = (oldWidth - newWidth) / 2; - if (!keepY) posY = (oldHeight - newHeight) / 2; - break; - } - - success = gimp_image_crop ( - out->image_id, - newWidth, - newHeight, - posX, - posY - ); - - return success; -} - -static gboolean apply_fliprotate(fliprotate_settings settings, image_output out) -{ - gboolean success = TRUE; - - if (settings->flip_h) { - // do horizontal flip - success = gimp_image_flip ( - out->image_id, - GIMP_ORIENTATION_HORIZONTAL - ); - } - - if (settings->flip_v) { - // do vertical flip - success = gimp_image_flip ( - out->image_id, - GIMP_ORIENTATION_VERTICAL - ); - } - - if (settings->rotate) { - // do rotation - success = gimp_image_rotate ( - out->image_id, - settings->rotation_type - ); - } - - return success; -} - -static gboolean apply_color(color_settings settings, image_output out) -{ - gboolean success = TRUE; - - int default_drawable = out->drawable_ids[0]; - if (settings->brightness != 0 || settings->contrast != 0) { - // brightness or contrast have been modified, apply the manipulation - - if (!gimp_drawable_is_rgb(default_drawable)) { - gimp_image_convert_rgb(out->image_id); - } - - int i; - for (i = 0; i < out->drawable_count; i++) { - success = gimp_drawable_brightness_contrast( - out->drawable_ids[i], - settings->brightness, - settings->contrast - ); - } - } - - if (settings->grayscale && !gimp_drawable_is_gray(default_drawable)) { - // do grayscale conversion - success = gimp_image_convert_grayscale(out->image_id); - } - - if (settings->levels_auto) { - // do levels correction - int i; - for (i = 0; i < out->drawable_count; i++) { - success = gimp_drawable_levels_stretch(out->drawable_ids[i]); - } - } - - if (settings->curve_file != NULL && !gimp_drawable_is_indexed(default_drawable)) { - // apply curve - - if (!colorcurve_init) { // read from the curve file only the first time - success = parse_curve_file( - settings->curve_file, - &colorcurve_num_points_v, &colorcurve_ctr_points_v, - &colorcurve_num_points_r, &colorcurve_ctr_points_r, - &colorcurve_num_points_g, &colorcurve_ctr_points_g, - &colorcurve_num_points_b, &colorcurve_ctr_points_b, - &colorcurve_num_points_a, &colorcurve_ctr_points_a - ); - - colorcurve_init = TRUE; - } - else success = TRUE; - - if (success) { - - int i; - for (i = 0; i < out->drawable_count; i++) { - if (colorcurve_num_points_v >= 4 && colorcurve_num_points_v <= 34) { - success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_VALUE, colorcurve_num_points_v, colorcurve_ctr_points_v); - } - - if (colorcurve_num_points_r >= 4 && colorcurve_num_points_r <= 34) { - success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_RED, colorcurve_num_points_r, colorcurve_ctr_points_r); - } - - if (colorcurve_num_points_g >= 4 && colorcurve_num_points_g <= 34) { - success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_GREEN, colorcurve_num_points_g, colorcurve_ctr_points_g); - } - - if (colorcurve_num_points_b >= 4 && colorcurve_num_points_b <= 34) { - success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_BLUE, colorcurve_num_points_b, colorcurve_ctr_points_b); - } - - if (colorcurve_num_points_a >= 4 && colorcurve_num_points_a <= 34) { - success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_ALPHA, colorcurve_num_points_a, colorcurve_ctr_points_a); - } - } - } - } - - return success; -} - -static gboolean apply_sharpblur(sharpblur_settings settings, image_output out) -{ - gboolean success = TRUE; - gint nreturn_vals; - - if (settings->amount < 0) { - // do sharp - int i; - for (i = 0; i < out->drawable_count; i++) { - GimpParam *return_vals = gimp_run_procedure( - "plug_in_sharpen", // could use plug_in_unsharp_mask, but there's a datatype bug in 2.6.x version - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, out->drawable_ids[i], - GIMP_PDB_INT32, -(settings->amount), - GIMP_PDB_END - ); - } - } else if (settings->amount > 0){ - // do blur - float minsize = min(gimp_image_width(out->image_id)/4, gimp_image_height(out->image_id)/4); - float radius = (minsize / 100) * settings->amount; - - int i; - for (i = 0; i < out->drawable_count; i++) { - GimpParam *return_vals = gimp_run_procedure( - "plug_in_gauss", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, out->drawable_ids[i], - GIMP_PDB_FLOAT, radius, - GIMP_PDB_FLOAT, radius, - GIMP_PDB_INT32, 0, - GIMP_PDB_END - ); - } - } - - return success; -} - -static gboolean apply_watermark(watermark_settings settings, image_output out) -{ - gboolean success = TRUE; - gint32 layerId; - gdouble posX, posY; - gint wmwidth, wmheight, wmasc, wmdesc; - - gint imgwidth = gimp_image_width(out->image_id); - gint imgheight = gimp_image_height(out->image_id); - - if (settings->mode) { - if (strlen(settings->text) == 0) { - return TRUE; - } - - GimpRGB old_foreground, new_foreground; - - gimp_context_get_foreground(&old_foreground); - gimp_rgb_parse_hex (&new_foreground, gdk_color_to_string(&(settings->color)), strlen(gdk_color_to_string(&(settings->color)))); - gimp_context_set_foreground(&new_foreground); - - gimp_text_get_extents_fontname( - settings->text, - pango_font_description_get_size(settings->font) / PANGO_SCALE, - GIMP_PIXELS, - pango_font_description_get_family(settings->font), - &wmwidth, - &wmheight, - &wmasc, - &wmdesc - ); - - calc_watermark_xy ( - imgwidth, imgheight, - wmwidth, wmheight, - settings->position, - settings->edge_distance, - &posX, &posY); - - layerId = gimp_text_fontname( - out->image_id, - -1, - posX, - posY, - settings->text, - -1, - TRUE, - pango_font_description_get_size(settings->font) / PANGO_SCALE, - GIMP_PIXELS, - pango_font_description_get_family(settings->font) - ); - gimp_context_set_foreground(&old_foreground); - gimp_layer_set_opacity(layerId, settings->opacity); - } - else { - if (!g_file_test(settings->image_file, G_FILE_TEST_IS_REGULAR)) { - // error, can't access image file - return TRUE; - } - - layerId = gimp_file_load_layer( - GIMP_RUN_NONINTERACTIVE, - out->image_id, - settings->image_file - ); - - gimp_image_insert_layer( - out->image_id, - layerId, - 0, - 0 - ); - - wmwidth = gimp_drawable_width(layerId); - wmheight = gimp_drawable_height(layerId); - if (settings->image_sizemode != WM_IMG_NOSIZE) { - if (settings->image_sizemode == WM_IMG_SIZEW) { - float wmwidth_ = (imgwidth * settings->image_size_percent) / 100.0; - float diff = (wmwidth_ / wmwidth) * 100; - wmheight = round((wmheight * diff) / 100.0); - wmwidth = round(wmwidth_); - } - else if (settings->image_sizemode == WM_IMG_SIZEH) { - float wmheight_ = (imgheight * settings->image_size_percent) / 100.0; - float diff = (wmheight_ / wmheight) * 100; - wmwidth = round((wmwidth * diff) / 100.0); - wmheight = round(wmheight_); - } - - GimpInterpolationType old_interpolation; - old_interpolation = gimp_context_get_interpolation(); - - success = gimp_context_set_interpolation (GIMP_INTERPOLATION_CUBIC); - success = gimp_layer_scale ( - layerId, - wmwidth, - wmheight, - TRUE - ); - success = gimp_context_set_interpolation (old_interpolation); - } - - gimp_layer_set_opacity(layerId, settings->opacity); - - calc_watermark_xy ( - imgwidth, imgheight, - wmwidth, wmheight, - settings->position, - settings->edge_distance, - &posX, &posY); - - gimp_layer_set_offsets( - layerId, - posX, - posY - ); - } - - // refresh all drawables - g_free(out->drawable_ids); - out->drawable_ids = gimp_image_get_layers(out->image_id, &out->drawable_count); - - return success; -} - -static void calc_watermark_xy (int imgwidth, int imgheight, int wmwidth, int wmheight, watermark_position position, int edge, gdouble* posX, gdouble* posY) { - if (position == WM_POS_TL) { - *posX = edge; - *posY = edge; - } - else if (position == WM_POS_TC) { - *posX = (imgwidth / 2) - (wmwidth / 2); - *posY = edge; - } - else if (position == WM_POS_TR) { - *posX = imgwidth - wmwidth - edge; - *posY = edge; - } - else if (position == WM_POS_BL) { - *posX = edge; - *posY = imgheight - wmheight - edge; - } - else if (position == WM_POS_BC) { - *posX = (imgwidth / 2) - (wmwidth / 2); - *posY = imgheight - wmheight - edge; - } - else if (position == WM_POS_BR) { - *posX = imgwidth - wmwidth - edge; - *posY = imgheight - wmheight - edge; - } - else if (position == WM_POS_CL) { - *posX = edge; - *posY = (imgheight / 2) - (wmheight / 2); - } - else if (position == WM_POS_CR) { - *posX = imgwidth - wmwidth - edge; - *posY = (imgheight / 2) - (wmheight / 2); - } - else { - *posX = (imgwidth / 2) - (wmwidth / 2); - *posY = (imgheight / 2) - (wmheight / 2); - } -} - -static gboolean apply_userdef(userdef_settings settings, image_output out) -{ - gboolean success = TRUE; - - int param_i; - GimpParamDef param_info; - gboolean saving_function = (strstr(settings->procedure, "-save") != NULL); - - int single_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - gimp_selection_none(out->image_id); - - for (param_i = 0; param_i < settings->num_params; param_i++) { - switch((settings->params[param_i]).type) { - case GIMP_PDB_IMAGE: - (settings->params[param_i]).data.d_image = out->image_id; - break; - - case GIMP_PDB_DRAWABLE: - case GIMP_PDB_ITEM: - (settings->params[param_i]).data.d_drawable = single_drawable; - break; - - case GIMP_PDB_STRING: - if (saving_function) { - param_info = pdb_proc_get_param_info(settings->procedure, param_i); - if (strcmp(param_info.name, "filename") == 0) { - (settings->params[param_i]).data.d_string = g_strdup(out->filepath); - } - else if (strcmp(param_info.name, "raw-filename") == 0) { - (settings->params[param_i]).data.d_string = g_strdup(out->filename); - } - } - break; - - default: break; - } - } - - gint nreturn_vals; - GimpParam *return_vals = gimp_run_procedure2( - settings->procedure, - &nreturn_vals, - settings->num_params, - settings->params - ); - - gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - g_free(out->drawable_ids); - out->drawable_ids = gimp_image_get_layers(out->image_id, &out->drawable_count); - - return success; -} - -static gboolean apply_rename(rename_settings settings, image_output out, char* orig_basename) -{ - char *orig_name = g_strdup(orig_basename); - - out->filename = g_strdup(settings->pattern); - - // search for 'RENAME_KEY_ORIG' occurrences and replace the final filename - if(strstr(out->filename, RENAME_KEY_ORIG) != NULL) { - out->filename = str_replace(out->filename, RENAME_KEY_ORIG, orig_name); - } - - // same thing for count and datetime - - if(strstr(out->filename, RENAME_KEY_COUNT) != NULL) { - char strcount[5]; - sprintf(strcount, "%i", processed_count + 1); - out->filename = str_replace(out->filename, RENAME_KEY_COUNT, strcount); - } - - if(strstr(out->filename, RENAME_KEY_DATETIME) != NULL) { - out->filename = str_replace(out->filename, RENAME_KEY_DATETIME, current_datetime); - } - - g_free(orig_name); - - return TRUE; -} - -// following: set of functions that saves the image file in various formats - -static gboolean image_save(format_type type, image_output imageout, format_params params) -{ - gboolean result; - - if (type == FORMAT_BMP) { - result = image_save_bmp(imageout); - } - else if(type == FORMAT_GIF) { - result = image_save_gif(imageout, ((format_params_gif)params)->interlace); - } - else if(type == FORMAT_ICON) { - result = image_save_icon(imageout); - } - else if(type == FORMAT_JPEG) { - result = image_save_jpeg( - imageout, - ((format_params_jpeg)params)->quality, - ((format_params_jpeg)params)->smoothing, - ((format_params_jpeg)params)->entropy, - ((format_params_jpeg)params)->progressive, - ((format_params_jpeg)params)->comment, - ((format_params_jpeg)params)->subsampling, - ((format_params_jpeg)params)->baseline, - ((format_params_jpeg)params)->markers, - ((format_params_jpeg)params)->dct - ); - } - else if(type == FORMAT_PNG) { - result = image_save_png(imageout, - ((format_params_png)params)->interlace, - ((format_params_png)params)->compression, - ((format_params_png)params)->savebgc, - ((format_params_png)params)->savegamma, - ((format_params_png)params)->saveoff, - ((format_params_png)params)->savephys, - ((format_params_png)params)->savetime, - ((format_params_png)params)->savecomm, - ((format_params_png)params)->savetrans - ); - } - else if(type == FORMAT_TGA) { - result = image_save_tga(imageout, ((format_params_tga)params)->rle, ((format_params_tga)params)->origin); - } - else if(type == FORMAT_TIFF) { - result = image_save_tiff(imageout, ((format_params_tiff)params)->compression); - } - else if(type == FORMAT_HEIF) { - result = image_save_heif( - imageout, - ((format_params_heif)params)->quality, - ((format_params_heif)params)->lossless - ); - } - else if(type == FORMAT_WEBP) { - result = image_save_webp( - imageout, - ((format_params_webp)params)->preset, - ((format_params_webp)params)->lossless, - ((format_params_webp)params)->quality, - ((format_params_webp)params)->alpha_quality, - ((format_params_webp)params)->animation, - ((format_params_webp)params)->anim_loop, - ((format_params_webp)params)->minimize_size, - ((format_params_webp)params)->kf_distance, - ((format_params_webp)params)->exif, - ((format_params_webp)params)->iptc, - ((format_params_webp)params)->xmp, - ((format_params_webp)params)->delay, - ((format_params_webp)params)->force_delay - ); - } - else if(type == FORMAT_EXR) { - result = image_save_exr(imageout); - } - else { - // save in the original format - int final_drawable = gimp_image_merge_visible_layers(imageout->image_id, GIMP_CLIP_TO_IMAGE); - // but first check if the images was a GIF and it's palette has changed during the process - if (file_has_extension(imageout->filename, ".gif") && gimp_drawable_is_rgb(final_drawable)) { - gimp_image_convert_indexed( - imageout->image_id, - GIMP_CONVERT_DITHER_FS, - GIMP_CONVERT_PALETTE_GENERATE, - gimp_drawable_has_alpha (final_drawable) ? 255 : 256, - TRUE, - FALSE, - "" - ); - } - - // for HEIF, the default values are "0 quality"... save lossless instead - if ((file_has_extension(imageout->filename, ".heif") || file_has_extension(imageout->filename, ".heic"))) { - result = image_save_heif( - imageout, - 100, - TRUE - ); - } - // same thing for WEBP - else if (file_has_extension(imageout->filename, ".webp")) { - result = image_save_webp( - imageout, - 0, - FALSE, - 90, - 100, - FALSE, - TRUE, - TRUE, - 50, - TRUE, - TRUE, - TRUE, - 200, - FALSE - ); - } - else - { - result = gimp_file_save( - GIMP_RUN_NONINTERACTIVE, - imageout->image_id, - final_drawable, - imageout->filepath, - imageout->filename - ); - } - } - - return result; -} - -static gboolean image_save_bmp(image_output out) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_bmp_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_gif(image_output out, gboolean interlace) -{ - gint nreturn_vals; - - // first, convert to indexed-256 color mode - gimp_image_convert_indexed( - out->image_id, - GIMP_CONVERT_DITHER_FS, - GIMP_CONVERT_PALETTE_GENERATE, - gimp_drawable_has_alpha (out->drawable_ids[0]) ? 255 : 256, - TRUE, - FALSE, - "" - ); - - GimpParam *return_vals = gimp_run_procedure( - "file_gif_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, 0, // drawable is ignored - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, interlace ? 1 : 0, // Try to save as interlaced - GIMP_PDB_INT32, 1, // (animated gif) loop infinitely - GIMP_PDB_INT32, 0, // (animated gif) Default delay between framese in milliseconds - GIMP_PDB_INT32, 0, // (animated gif) Default disposal type (0=don't care, 1=combine, 2=replace) - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_icon(image_output out) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_ico_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_jpeg(image_output out, float quality, float smoothing, gboolean entropy, gboolean progressive, gchar* comment, int subsampling, gboolean baseline, int markers, int dct) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - // "file_jpeg_save" doesn't support indexed images - if (gimp_drawable_is_indexed(final_drawable)) { - gimp_image_convert_rgb(out->image_id); - } - - GimpParam *return_vals = gimp_run_procedure( - "file_jpeg_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_FLOAT, quality >= 3 ? quality/100 : 0.03, // Quality of saved image (0 <= quality <= 1) + small fix because final image doesn't change when quality < 3 - GIMP_PDB_FLOAT, smoothing, // Smoothing factor for saved image (0 <= smoothing <= 1) - GIMP_PDB_INT32, entropy ? 1 : 0, // Optimization of entropy encoding parameters (0/1) - GIMP_PDB_INT32, progressive ? 1 : 0, // Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG (0/1) - GIMP_PDB_STRING, comment, // Image comment - GIMP_PDB_INT32, subsampling, // The subsampling option number - GIMP_PDB_INT32, baseline ? 1 : 0, // Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1) - GIMP_PDB_INT32, markers, // Frequency of restart markers (in rows, 0 = no restart markers) - GIMP_PDB_INT32, dct, // DCT algorithm to use (speed/quality tradeoff) - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_heif(image_output out, int quality, gboolean lossless) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_heif_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, quality, // Quality factor (range: 0-100. 0 = worst, 100 = best) - GIMP_PDB_INT32, lossless ? 1 : 0, // Use lossless compression (0 = lossy, 1 = lossless) - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_png(image_output out, gboolean interlace, int compression, gboolean savebgc, gboolean savegamma, gboolean saveoff, gboolean savephys, gboolean savetime, gboolean savecomm, gboolean savetrans) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_png_save2", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, interlace? 1 : 0, // Use Adam7 interlacing? - GIMP_PDB_INT32, compression, // Deflate Compression factor (0-9) - GIMP_PDB_INT32, savebgc? 1 : 0, // Write bKGD chunk? - GIMP_PDB_INT32, savegamma? 1 : 0, // Write gAMA chunk? - GIMP_PDB_INT32, saveoff? 1 : 0, // Write oFFs chunk? - GIMP_PDB_INT32, savephys? 1 : 0, // Write phys chunk? - GIMP_PDB_INT32, savetime? 1 : 0, // Write tIME chunk? - GIMP_PDB_INT32, savecomm? 1 : 0, // Write comments chunk? - GIMP_PDB_INT32, savetrans? 1 : 0, // Write trans chunk? - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_tga(image_output out, gboolean rle, int origin) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_tga_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, rle? 1 : 0, // Use RLE compression - GIMP_PDB_INT32, origin, // Image origin - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_tiff(image_output out, int compression) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_tiff_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, compression, // Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3), JPEG (4) } - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_webp(image_output out, int preset, gboolean lossless, float quality, float alpha_quality, gboolean animation, gboolean anim_loop, gboolean minimize_size, int kf_distance, gboolean exif, gboolean iptc, gboolean xmp, int delay, int force_delay) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_webp_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_INT32, preset, // preset (Default=0, Picture=1, Photo=2, Drawing=3, Icon=4, Text=5) - GIMP_PDB_INT32, lossless, // Use lossless encoding (0/1) - GIMP_PDB_FLOAT, quality, // Quality of the image (0 <= quality <= 100) - GIMP_PDB_FLOAT, alpha_quality, // Quality of the image's alpha channel (0 <= alpha-quality <= 100) - GIMP_PDB_INT32, animation, // Use layers for animation (0/1) - GIMP_PDB_INT32, anim_loop, // Loop animation infinitely (0/1) - GIMP_PDB_INT32, minimize_size, // Minimize animation size (0/1) - GIMP_PDB_INT32, kf_distance, // Maximum distance between key-frames (>=0) - GIMP_PDB_INT32, exif, // Toggle saving exif data (0/1) - GIMP_PDB_INT32, iptc, // Toggle saving iptc data (0/1) - GIMP_PDB_INT32, xmp, // Toggle saving xmp data (0/1) - GIMP_PDB_INT32, delay, // Delay to use when timestamps are not available or forced - GIMP_PDB_INT32, force_delay, // Force delay on all frames - GIMP_PDB_END - ); - - return TRUE; -} - -static gboolean image_save_exr(image_output out) -{ - gint nreturn_vals; - int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); - - GimpParam *return_vals = gimp_run_procedure( - "file_exr_save", - &nreturn_vals, - GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, - GIMP_PDB_IMAGE, out->image_id, - GIMP_PDB_DRAWABLE, final_drawable, - GIMP_PDB_STRING, out->filepath, - GIMP_PDB_STRING, out->filename, - GIMP_PDB_END - ); - - return TRUE; -} - -/* returns a result code following this schema: - * 0 = user responses "don't overwrite" to a confirm dialog - * 1 = old file was the same as the new one and user responses "yes, overwrite" - * 2 = old file wasn't the same (implicit overwrite) */ -static int overwrite_result(char* path, GtkWidget* parent) { - gboolean oldfile_access = g_file_test(path, G_FILE_TEST_IS_REGULAR); - - if ( (bimp_opt_alertoverwrite == BIMP_ASK_OVERWRITE) && oldfile_access) { - GtkWidget *dialog; - GtkWidget *check_alertoverwrite; - GtkWidget *dialog_action; - - - dialog = gtk_message_dialog_new( - GTK_WINDOW(parent), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - _("File %s already exists, overwrite it?"), comp_get_filename(path) - ); - - // Add checkbox "Always apply decision" - dialog_action = gtk_dialog_get_action_area(GTK_DIALOG(dialog)); - check_alertoverwrite = gtk_check_button_new_with_label(_("Always apply this decision")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_alertoverwrite), FALSE); - gtk_box_pack_start (GTK_BOX(dialog_action), check_alertoverwrite, FALSE, FALSE, 0); - gtk_widget_show (check_alertoverwrite); - - gtk_dialog_add_buttons ( - GTK_DIALOG(dialog), - GTK_STOCK_YES, GTK_RESPONSE_YES, - - GTK_STOCK_NO, GTK_RESPONSE_NO, NULL - ); - - gtk_window_set_title(GTK_WINDOW(dialog), _("Overwrite?")); - gint result = gtk_dialog_run(GTK_DIALOG(dialog)); - gboolean dont_ask_anymore = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_alertoverwrite)); - gtk_widget_destroy(dialog); - - - if (result == GTK_RESPONSE_YES) { - if (dont_ask_anymore) - bimp_opt_alertoverwrite = BIMP_OVERWRITE_SKIP_ASK; - return 1; - } - else { - if (dont_ask_anymore) - bimp_opt_alertoverwrite = BIMP_DONT_OVERWRITE_SKIP_ASK; - - return 0; - } - } - else { - if (oldfile_access) { - return (bimp_opt_alertoverwrite == BIMP_OVERWRITE_SKIP_ASK) ? 1 : 0; - } - else { - return 2; - } - } -} +// Functions called when the user clicks on 'APPLY' + + +#include +#include +#include +#include +#include +#include +#include +#include "bimp-operate.h" +#include "bimp.h" +#include "bimp-manipulations.h" +#include "bimp-gui.h" +#include "bimp-utils.h" +#include "bimp-serialize.h" +#include "plugin-intl.h" + +static gboolean process_image(gpointer); + +static gboolean apply_manipulation(manipulation, image_output); +static gboolean apply_resize(resize_settings, image_output); +static gboolean apply_crop(crop_settings, image_output); +static gboolean apply_fliprotate(fliprotate_settings, image_output); +static gboolean apply_color(color_settings, image_output); +static gboolean apply_sharpblur(sharpblur_settings, image_output); +static gboolean apply_watermark(watermark_settings, image_output); +static void calc_watermark_xy (int, int, int, int, watermark_position, int, gdouble*, gdouble*); +static gboolean apply_userdef(userdef_settings, image_output); +static gboolean apply_rename(rename_settings, image_output, char*); + +static gboolean image_save(format_type, image_output, format_params); +static gboolean image_save_bmp(image_output); +static gboolean image_save_gif(image_output, gboolean); +static gboolean image_save_icon(image_output); +static gboolean image_save_jpeg(image_output, float, float, gboolean, gboolean, gchar*, int, gboolean, int, int); +static gboolean image_save_heif(image_output, int, gboolean); +static gboolean image_save_png(image_output, gboolean, int, gboolean, gboolean, gboolean, gboolean, gboolean, gboolean, gboolean); +static gboolean image_save_tga(image_output, gboolean, int); +static gboolean image_save_tiff(image_output, int); +static gboolean image_save_webp(image_output, int, gboolean, float, float, gboolean, gboolean, gboolean, int, gboolean, gboolean, gboolean, int, int); +static gboolean image_save_avif(image_output, gboolean, int); +static gboolean image_save_exr(image_output); + +static int overwrite_result(char*, GtkWidget*); + +static char* current_datetime; +static int processed_count; +static int success_count; +static int total_images; + +static char* common_folder_path; + +static gboolean list_contains_changeformat; +static gboolean list_contains_rename; +static gboolean list_contains_watermark; +static gboolean list_contains_savingplugin; + +// set of variables to be used when doing Curve color correction +// they are global so the batch process will read the source curve file once +static gboolean colorcurve_init; +static int colorcurve_num_points_v; +static gdouble* colorcurve_ctr_points_v; +static int colorcurve_num_points_r; +static gdouble* colorcurve_ctr_points_r; +static int colorcurve_num_points_g; +static gdouble* colorcurve_ctr_points_g; +static int colorcurve_num_points_b; +static gdouble* colorcurve_ctr_points_b; +static int colorcurve_num_points_a; +static gdouble* colorcurve_ctr_points_a; + +void bimp_start_batch(gpointer parent_dialog) +{ + bimp_set_busy(TRUE); + + // initialization + g_print("\nBIMP - Batch Manipulation Plugin\nStart batch processing...\n"); + processed_count = 0; + success_count = 0; + total_images = g_slist_length(bimp_input_filenames); + bimp_progress_bar_set(0.0, ""); + + bimp_init_batch(); + + current_datetime = get_datetime(); + common_folder_path = NULL; + + if (bimp_opt_keepfolderhierarchy){ + int i, j; + gboolean need_hierarchy = FALSE; + char * path = NULL; + char ** common_folder; + char ** current_folder; + size_t common_folder_size, current_folder_size; + + path = comp_get_filefolder(g_slist_nth(bimp_input_filenames,0)->data); + + common_folder = get_path_folders(path); + common_folder_size = 0; + for (common_folder_size = 0; common_folder[common_folder_size] != NULL; ++common_folder_size); + + for (i=1; i < total_images ; i++) + { + path = comp_get_filefolder(g_slist_nth(bimp_input_filenames,i)->data); + current_folder = get_path_folders (path); + for (current_folder_size = 0; current_folder[current_folder_size] != NULL; ++current_folder_size); + + // The common path is at most as long as the shortest path + while (common_folder_size > current_folder_size) + { + need_hierarchy = TRUE; + g_free(common_folder[common_folder_size-1]); + common_folder[common_folder_size-1] = NULL; + common_folder_size--; + } + + for (j=0; j < common_folder_size; ++j) + { + if (strcmp(common_folder[j], current_folder[j]) != 0) { + need_hierarchy = TRUE; + while (common_folder_size > j) + { + g_free(common_folder[common_folder_size-1]); + common_folder[common_folder_size-1] = NULL; + common_folder_size--; + } + break; + } + } + + g_strfreev(current_folder); + } + + if (need_hierarchy) + common_folder_path = g_strjoinv(FILE_SEPARATOR_STR, common_folder); + + g_strfreev(common_folder); + } + + // start on a new thread + guint batch_idle_tag = g_idle_add((GSourceFunc)process_image, parent_dialog); +} + +void bimp_init_batch() +{ + list_contains_changeformat = bimp_list_contains_manip(MANIP_CHANGEFORMAT); + list_contains_rename = bimp_list_contains_manip(MANIP_RENAME); + list_contains_watermark = bimp_list_contains_manip(MANIP_WATERMARK); + list_contains_savingplugin = bimp_list_contains_savingplugin(); + + colorcurve_init = FALSE; +} + +static gboolean process_image(gpointer parent) +{ + gboolean success = TRUE; + + image_output imageout = (image_output)g_malloc(sizeof(struct imageout_str)); + char* orig_filename = NULL; + char* orig_basename = NULL; + char* orig_file_ext = NULL; + char* output_file_comp = NULL; + + // store original file path and name + orig_filename = g_slist_nth (bimp_input_filenames, processed_count)->data; + orig_basename = g_strdup(comp_get_filename(orig_filename)); + + // store original extension and check error cases + orig_file_ext = g_strdup(strrchr(orig_basename, '.')); + if (orig_file_ext == NULL) { + /* under Linux, GtkFileChooser lets to pick an image file without extension, but GIMP cannot + * save it back if its format remains unchanged. Operation can continue only if a MANIP_CHANGEFORMAT + * is present */ + if (list_contains_changeformat) { + orig_file_ext = g_malloc0(sizeof(char)); + } + else { + bimp_show_error_dialog(g_strdup_printf(_("Can't save image \"%s\": input file has no extension.\nYou can solve this error by adding a \"Change format or compression\" step"), orig_basename), bimp_window_main); + success = FALSE; + goto process_end; + } + } + else if (g_ascii_strcasecmp(orig_file_ext, ".svg") == 0 && !list_contains_changeformat) { + bimp_show_error_dialog(g_strdup_printf(_("GIMP can't save %s back to its original SVG format.\nYou can solve this error by adding a \"Change format or compression\" step"), orig_basename), bimp_window_main); + success = FALSE; + goto process_end; + } + + g_print("\nWorking on file %d of %d (%s)\n", processed_count + 1, total_images, orig_filename); + bimp_progress_bar_set(((double)processed_count)/total_images, g_strdup_printf(_("Working on file \"%s\"..."), orig_basename)); + + // rename and save process... + orig_basename[strlen(orig_basename) - strlen(orig_file_ext)] = '\0'; // remove extension from basename + + // check if a rename pattern is defined + if(list_contains_rename) { + g_print("Applying RENAME...\n"); + apply_rename((rename_settings)(bimp_list_get_manip(MANIP_RENAME))->settings, imageout, orig_basename); + } + else { + imageout->filename = orig_basename; + } + + // To keep the folder hierarchy + if (common_folder_path == NULL) { + // Not selected or required, everything goes into the same destination folder + output_file_comp = g_malloc0(sizeof(char)); + } + else { + // keep folders to add to output path + output_file_comp = + g_strndup(&orig_filename[strlen(common_folder_path)+1], + strlen(orig_filename)-(strlen(common_folder_path)+1) + -strlen(orig_basename)-strlen(orig_file_ext)); + } + + if (strlen(output_file_comp) > 0) { +#ifdef _WIN32 + // Clean output_file_comp + // Should only be concerned for ':' in Drive letter + int i; + for (i = 0; i < strlen(output_file_comp); ++i) + if ( output_file_comp[i] == ':' ) + output_file_comp[i] = '_'; +#endif + // Create path if needed + g_mkdir_with_parents( + g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, NULL), + 0777 + ); + } + + // save the final image in output dir with proper format and params + format_type final_format = -1; + format_params params = NULL; + + if(list_contains_changeformat) { + changeformat_settings settings = (changeformat_settings)(bimp_list_get_manip(MANIP_CHANGEFORMAT))->settings; + final_format = settings->format; + params = settings->params; + + g_print("Changing FORMAT to %s\n", format_type_string[final_format][0]); + imageout->filename = g_strconcat(imageout->filename, ".", format_type_string[final_format][0], NULL); // append new file extension + imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path + } + // TO CHECK what apply_userdef does once coded + + else if (list_contains_savingplugin) { + // leave filename without extension and proceed calling each saving plugin + imageout->filename = g_strconcat(imageout->filename, ".dds", NULL); + imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path + + GSList *iterator = NULL; + manipulation man; + for (iterator = bimp_selected_manipulations; iterator; iterator = iterator->next) { + man = (manipulation)(iterator->data); + if (man->type == MANIP_USERDEF && strstr(((userdef_settings)(man->settings))->procedure, "-save") != NULL) { + /* found a saving plugin, execute it + // TODO!!!! This won't work yet, we need a way to extract the file extension managed by the selected saving plugin + * e.g. "file-dds-save" -> "dds" (don't do it with regexp on plugin's name... too easy...) */ + apply_userdef((userdef_settings)(man->settings), imageout); + } + } + } + else { + // if not specified, save in original format + imageout->filename = g_strconcat(imageout->filename, orig_file_ext, NULL); // append old file extension + imageout->filepath = g_strconcat(bimp_output_folder, FILE_SEPARATOR_STR, output_file_comp, imageout->filename, NULL); // build new path + final_format = -1; + } + + // check if writing possible + gboolean will_overwrite = FALSE; + if (bimp_opt_alertoverwrite != BIMP_OVERWRITE_SKIP_ASK) { + // file already exists ? + will_overwrite = g_file_test(imageout->filepath, G_FILE_TEST_IS_REGULAR); + if (will_overwrite) { + // "Don't overwrite" without confirmation + if (bimp_opt_alertoverwrite == BIMP_DONT_OVERWRITE_SKIP_ASK) { + g_print("Destination file already exists and won't be overwritten\n"); + goto process_end; + } + else { + // Ask what to do + int ow_res = overwrite_result(imageout->filepath, parent); + if (ow_res == 0) { + g_print("Destination file already exists and user select to don't overwrite\n"); + goto process_end; + } + } + } + } + + // apply all the main manipulations + bimp_apply_drawable_manipulations(imageout, (gchar*)orig_filename, (gchar*)orig_basename); + + time_t mod_time = -1; + if (will_overwrite && bimp_opt_keepdates) { + // I must keep the dates even if the file has been overwritten + mod_time = get_modification_time(imageout->filepath); + if (mod_time == -1) g_print("An error occurred when retrieving the modification date of file.\n"); + } + + // Save + g_print("Saving file %s in %s\n", imageout->filename, imageout->filepath); + image_save(final_format, imageout, params); + + if (will_overwrite && bimp_opt_keepdates && mod_time > -1) { + // replace with the old dates + int res = set_modification_time(imageout->filepath, mod_time); + if (res == -1) g_print("An error occurred when replacing the modification date of file.\n"); + } + + gimp_image_delete(imageout->image_id); // is it useful? + +process_end: + + g_free(orig_basename); + g_free(orig_file_ext); + g_free(output_file_comp); + g_free(imageout->filename); + g_free(imageout->filepath); + g_free(imageout); + + processed_count++; + if (success) success_count++; + + // TODO: errors check here + if (!bimp_is_busy) { + bimp_progress_bar_set(0.0, _("Operations stopped")); + g_print("\nStopped, %d files processed.\n", processed_count); + return FALSE; + } + else { + if (processed_count == total_images) { + int errors_count = processed_count - success_count; + bimp_progress_bar_set(1.0, g_strdup_printf(_("End, all files have been processed with %d errors"), errors_count)); + g_print("\nEnd, %d files have been processed with %d errors.\n", processed_count, errors_count); + + bimp_set_busy(FALSE); + + return FALSE; + } + else { + return TRUE; + } + } +} + +void bimp_apply_drawable_manipulations(image_output imageout, gchar* orig_filename, gchar* orig_basename) +{ + imageout->image_id = gimp_file_load(GIMP_RUN_NONINTERACTIVE, orig_filename, orig_basename); // load file and get image id + // LOAD ERROR CHECK HERE + g_print("Image ID is %d\n", imageout->image_id); + + // stop saving the undo steps for this session + gimp_image_undo_freeze(imageout->image_id); + + imageout->drawable_ids = gimp_image_get_layers(imageout->image_id, &imageout->drawable_count); // get all drawables + g_print("Total drawables count: %d\n", imageout->drawable_count); + + // apply all the intermediate manipulations + g_slist_foreach(bimp_selected_manipulations, (GFunc)apply_manipulation, imageout); + + // watermark at last + if(list_contains_watermark) { + GSList* watermarks = bimp_list_get_manip_all(MANIP_WATERMARK); + GSList *iterator = NULL; + for (iterator = watermarks; iterator; iterator = iterator->next) { + g_print("Applying WATERMARK...\n"); + apply_watermark((watermark_settings)(((manipulation)(iterator->data))->settings), imageout); + } + + + } + + // re-enable undo + gimp_image_undo_thaw(imageout->image_id); +} + +static gboolean apply_manipulation(manipulation man, image_output out) +{ + gboolean success = TRUE; + + if (man->type == MANIP_RESIZE) { + g_print("Applying RESIZE...\n"); + apply_resize((resize_settings)(bimp_list_get_manip(MANIP_RESIZE))->settings, out); + } + else if (man->type == MANIP_CROP) { + g_print("Applying CROP...\n"); + apply_crop((crop_settings)(bimp_list_get_manip(MANIP_CROP))->settings, out); + } + else if (man->type == MANIP_FLIPROTATE) { + g_print("Applying FLIP OR ROTATE...\n"); + success = apply_fliprotate((fliprotate_settings)(man->settings), out); + } + else if (man->type == MANIP_COLOR) { + g_print("Applying COLOR CORRECTION...\n"); + success = apply_color((color_settings)(man->settings), out); + } + else if (man->type == MANIP_SHARPBLUR) { + g_print("Applying SHARPBLUR...\n"); + success = apply_sharpblur((sharpblur_settings)(man->settings), out); + } + else if (man->type == MANIP_USERDEF && strstr(((userdef_settings)(man->settings))->procedure, "-save") == NULL) { + g_print("Applying %s...\n", ((userdef_settings)(man->settings))->procedure); + success = apply_userdef((userdef_settings)(man->settings), out); + } + + return success; +} + +static gboolean apply_resize(resize_settings settings, image_output out) +{ + gboolean success = FALSE; + gint orig_w, orig_h, final_w, final_h, view_w, view_h; + gdouble orig_res_x, orig_res_y; + + if (settings->change_res) { + success = gimp_image_get_resolution( + out->image_id, + &orig_res_x, + &orig_res_y + ); + + if ((settings->new_res_x != orig_res_x) || (settings->new_res_y != orig_res_y)) { + // change resolution + success = gimp_image_set_resolution( + out->image_id, + settings->new_res_x, + settings->new_res_y + ); + } + } + + orig_w = gimp_image_width(out->image_id); + orig_h = gimp_image_height(out->image_id); + + if (settings->resize_mode_width == RESIZE_DISABLE && settings->resize_mode_height == RESIZE_DISABLE) { + return !settings->change_res || success; + } + + gdouble newwpct, newwpctmax; + if (settings->resize_mode_width == RESIZE_PERCENT) { + newwpct = newwpctmax = settings->new_w_pc / 100.0; + } + else if(settings->resize_mode_width == RESIZE_PIXEL) { + newwpct = newwpctmax = (double)settings->new_w_px / (double)orig_w; + } + else { + newwpct = 1; + newwpctmax = DBL_MAX; + } + + gdouble newhpct, newhpctmax; + if (settings->resize_mode_height == RESIZE_PERCENT) { + newhpct = newhpctmax = settings->new_h_pc / 100.0; + } + else if(settings->resize_mode_height == RESIZE_PIXEL) { + newhpct = newhpctmax = (double)settings->new_h_px / (double)orig_h; + } + else { + newhpct = 1; + newhpctmax = DBL_MAX; + } + + if(settings->stretch_mode == STRETCH_ASPECT) { + gdouble newpct = min(newwpctmax, newhpctmax); + + final_w = view_w = round(orig_w * newpct); + final_h = view_h = round(orig_h * newpct); + } + else if (settings->stretch_mode == STRETCH_PADDED) { + gdouble newpct = min(newwpctmax, newhpctmax); + + final_w = round(orig_w * newpct); + final_h = round(orig_h * newpct); + view_w = round(orig_w * newwpct); + view_h = round(orig_h * newhpct); + } + else { + final_w = view_w = round(orig_w * newwpct); + final_h = view_h = round(orig_h * newhpct); + } + + // use gimp_image_scale instead + GimpInterpolationType old_interpolation; + old_interpolation = gimp_context_get_interpolation(); + + success = gimp_context_set_interpolation (settings->interpolation); + success = gimp_image_scale ( + out->image_id, + final_w, + final_h + ); + success = gimp_context_set_interpolation (old_interpolation); + + // add a padding if requested + if (settings->stretch_mode == STRETCH_PADDED) { + + // the padding will be drawn using a coloured layer at the bottom of the image + int imageType = gimp_image_base_type(out->image_id); + int layerType; + if (imageType == 2) layerType = 4; // see http://oldhome.schmorp.de/marc/pdb/gimp_layer_new.html + else if (imageType == 1) layerType = 2; + else layerType = 0; + + if (gimp_drawable_has_alpha(out->drawable_ids[0])) layerType ++; + + gint32 layerId = gimp_layer_new( + out->image_id, + "padding_layer", + view_w, view_h, + layerType, + (settings->padding_color_alpha / (float)G_MAXUINT16) * 100, + GIMP_LAYER_MODE_NORMAL_LEGACY + ); + + gimp_image_insert_layer( + out->image_id, + layerId, + 0, + 0 + ); + + gimp_image_lower_item_to_bottom(out->image_id, layerId); + + // fill it with the selected color + GimpRGB old_background, new_background; + + gimp_context_get_background(&old_background); + gimp_rgb_parse_hex (&new_background, gdk_color_to_string(&(settings->padding_color)), strlen(gdk_color_to_string(&(settings->padding_color)))); + gimp_context_set_background(&new_background); + gimp_drawable_fill(layerId, GIMP_FILL_BACKGROUND); + gimp_context_set_background(&old_background); + + // move it to the center + gimp_item_transform_translate(layerId, -abs(view_w - final_w) / 2, -abs(view_h - final_h) / 2); + + // finish changing the canvas size accordingly + success = gimp_image_resize_to_layers(out->image_id); + } + + return success; +} + +static gboolean apply_crop(crop_settings settings, image_output out) +{ + gboolean success = TRUE; + gint newWidth, newHeight, oldWidth, oldHeight, posX = 0, posY = 0; + gboolean keepX = FALSE, keepY = FALSE; + + oldWidth = gimp_image_width(out->image_id); + oldHeight = gimp_image_height(out->image_id); + + if (settings->manual) { + newWidth = min(oldWidth, settings->new_w); + newHeight = min(oldHeight, settings->new_h); + } + else { + float ratio1, ratio2; + if (settings->ratio == CROP_PRESET_CUSTOM) { + ratio1 = settings->custom_ratio1; + ratio2 = settings->custom_ratio2; + } + else { + ratio1 = (float)crop_preset_ratio[settings->ratio][0]; + ratio2 = (float)crop_preset_ratio[settings->ratio][1]; + } + + if (( (float)oldWidth / oldHeight ) > ( ratio1 / ratio2) ) { + // crop along the width + newHeight = oldHeight; + newWidth = round(( ratio1 * (float)newHeight ) / ratio2); + keepY = TRUE; + } else { + // crop along the height + newWidth = oldWidth; + newHeight = round(( ratio2 * (float)newWidth) / ratio1); + keepX = TRUE; + } + } + + switch (settings->start_pos) { + case CROP_START_TL: + posX = 0; + posY = 0; + break; + + case CROP_START_TR: + posX = (oldWidth - newWidth); + posY = 0; + break; + + case CROP_START_BL: + posX = 0; + posY = (oldHeight - newHeight); + break; + + case CROP_START_BR: + posX = (oldWidth - newWidth); + posY = (oldHeight - newHeight); + break; + + default: + if (!keepX) posX = (oldWidth - newWidth) / 2; + if (!keepY) posY = (oldHeight - newHeight) / 2; + break; + } + + success = gimp_image_crop ( + out->image_id, + newWidth, + newHeight, + posX, + posY + ); + + return success; +} + +static gboolean apply_fliprotate(fliprotate_settings settings, image_output out) +{ + gboolean success = TRUE; + + if (settings->flip_h) { + // do horizontal flip + success = gimp_image_flip ( + out->image_id, + GIMP_ORIENTATION_HORIZONTAL + ); + } + + if (settings->flip_v) { + // do vertical flip + success = gimp_image_flip ( + out->image_id, + GIMP_ORIENTATION_VERTICAL + ); + } + + if (settings->rotate) { + // do rotation + success = gimp_image_rotate ( + out->image_id, + settings->rotation_type + ); + } + + return success; +} + +static gboolean apply_color(color_settings settings, image_output out) +{ + gboolean success = TRUE; + + int default_drawable = out->drawable_ids[0]; + if (settings->brightness != 0 || settings->contrast != 0) { + // brightness or contrast have been modified, apply the manipulation + + if (!gimp_drawable_is_rgb(default_drawable)) { + gimp_image_convert_rgb(out->image_id); + } + + int i; + for (i = 0; i < out->drawable_count; i++) { + success = gimp_drawable_brightness_contrast( + out->drawable_ids[i], + settings->brightness, + settings->contrast + ); + } + } + + if (settings->grayscale && !gimp_drawable_is_gray(default_drawable)) { + // do grayscale conversion + success = gimp_image_convert_grayscale(out->image_id); + } + + if (settings->levels_auto) { + // do levels correction + int i; + for (i = 0; i < out->drawable_count; i++) { + success = gimp_drawable_levels_stretch(out->drawable_ids[i]); + } + } + + if (settings->curve_file != NULL && !gimp_drawable_is_indexed(default_drawable)) { + // apply curve + + if (!colorcurve_init) { // read from the curve file only the first time + success = parse_curve_file( + settings->curve_file, + &colorcurve_num_points_v, &colorcurve_ctr_points_v, + &colorcurve_num_points_r, &colorcurve_ctr_points_r, + &colorcurve_num_points_g, &colorcurve_ctr_points_g, + &colorcurve_num_points_b, &colorcurve_ctr_points_b, + &colorcurve_num_points_a, &colorcurve_ctr_points_a + ); + + colorcurve_init = TRUE; + } + else success = TRUE; + + if (success) { + + int i; + for (i = 0; i < out->drawable_count; i++) { + if (colorcurve_num_points_v >= 4 && colorcurve_num_points_v <= 34) { + success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_VALUE, colorcurve_num_points_v, colorcurve_ctr_points_v); + } + + if (colorcurve_num_points_r >= 4 && colorcurve_num_points_r <= 34) { + success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_RED, colorcurve_num_points_r, colorcurve_ctr_points_r); + } + + if (colorcurve_num_points_g >= 4 && colorcurve_num_points_g <= 34) { + success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_GREEN, colorcurve_num_points_g, colorcurve_ctr_points_g); + } + + if (colorcurve_num_points_b >= 4 && colorcurve_num_points_b <= 34) { + success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_BLUE, colorcurve_num_points_b, colorcurve_ctr_points_b); + } + + if (colorcurve_num_points_a >= 4 && colorcurve_num_points_a <= 34) { + success = gimp_drawable_curves_spline(out->drawable_ids[i], GIMP_HISTOGRAM_ALPHA, colorcurve_num_points_a, colorcurve_ctr_points_a); + } + } + } + } + + return success; +} + +static gboolean apply_sharpblur(sharpblur_settings settings, image_output out) +{ + gboolean success = TRUE; + gint nreturn_vals; + + if (settings->amount < 0) { + // do sharp + int i; + for (i = 0; i < out->drawable_count; i++) { + GimpParam *return_vals = gimp_run_procedure( + "plug_in_sharpen", // could use plug_in_unsharp_mask, but there's a datatype bug in 2.6.x version + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, out->drawable_ids[i], + GIMP_PDB_INT32, -(settings->amount), + GIMP_PDB_END + ); + } + } else if (settings->amount > 0){ + // do blur + float minsize = min(gimp_image_width(out->image_id)/4, gimp_image_height(out->image_id)/4); + float radius = (minsize / 100) * settings->amount; + + int i; + for (i = 0; i < out->drawable_count; i++) { + GimpParam *return_vals = gimp_run_procedure( + "plug_in_gauss", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, out->drawable_ids[i], + GIMP_PDB_FLOAT, radius, + GIMP_PDB_FLOAT, radius, + GIMP_PDB_INT32, 0, + GIMP_PDB_END + ); + } + } + + return success; +} + +static gboolean apply_watermark(watermark_settings settings, image_output out) +{ + gboolean success = TRUE; + gint32 layerId; + gdouble posX, posY; + gint wmwidth, wmheight, wmasc, wmdesc; + + gint imgwidth = gimp_image_width(out->image_id); + gint imgheight = gimp_image_height(out->image_id); + + if (settings->mode) { + if (strlen(settings->text) == 0) { + return TRUE; + } + + GimpRGB old_foreground, new_foreground; + + gimp_context_get_foreground(&old_foreground); + gimp_rgb_parse_hex (&new_foreground, gdk_color_to_string(&(settings->color)), strlen(gdk_color_to_string(&(settings->color)))); + gimp_context_set_foreground(&new_foreground); + + gimp_text_get_extents_fontname( + settings->text, + pango_font_description_get_size(settings->font) / PANGO_SCALE, + GIMP_PIXELS, + pango_font_description_get_family(settings->font), + &wmwidth, + &wmheight, + &wmasc, + &wmdesc + ); + + calc_watermark_xy ( + imgwidth, imgheight, + wmwidth, wmheight, + settings->position, + settings->edge_distance, + &posX, &posY); + + layerId = gimp_text_fontname( + out->image_id, + -1, + posX, + posY, + settings->text, + -1, + TRUE, + pango_font_description_get_size(settings->font) / PANGO_SCALE, + GIMP_PIXELS, + pango_font_description_get_family(settings->font) + ); + gimp_context_set_foreground(&old_foreground); + gimp_layer_set_opacity(layerId, settings->opacity); + } + else { + if (!g_file_test(settings->image_file, G_FILE_TEST_IS_REGULAR)) { + // error, can't access image file + return TRUE; + } + + layerId = gimp_file_load_layer( + GIMP_RUN_NONINTERACTIVE, + out->image_id, + settings->image_file + ); + + gimp_image_insert_layer( + out->image_id, + layerId, + 0, + 0 + ); + + wmwidth = gimp_drawable_width(layerId); + wmheight = gimp_drawable_height(layerId); + if (settings->image_sizemode != WM_IMG_NOSIZE) { + if (settings->image_sizemode == WM_IMG_SIZEW) { + float wmwidth_ = (imgwidth * settings->image_size_percent) / 100.0; + float diff = (wmwidth_ / wmwidth) * 100; + wmheight = round((wmheight * diff) / 100.0); + wmwidth = round(wmwidth_); + } + else if (settings->image_sizemode == WM_IMG_SIZEH) { + float wmheight_ = (imgheight * settings->image_size_percent) / 100.0; + float diff = (wmheight_ / wmheight) * 100; + wmwidth = round((wmwidth * diff) / 100.0); + wmheight = round(wmheight_); + } + + GimpInterpolationType old_interpolation; + old_interpolation = gimp_context_get_interpolation(); + + success = gimp_context_set_interpolation (GIMP_INTERPOLATION_CUBIC); + success = gimp_layer_scale ( + layerId, + wmwidth, + wmheight, + TRUE + ); + success = gimp_context_set_interpolation (old_interpolation); + } + + gimp_layer_set_opacity(layerId, settings->opacity); + + calc_watermark_xy ( + imgwidth, imgheight, + wmwidth, wmheight, + settings->position, + settings->edge_distance, + &posX, &posY); + + gimp_layer_set_offsets( + layerId, + posX, + posY + ); + } + + // refresh all drawables + g_free(out->drawable_ids); + out->drawable_ids = gimp_image_get_layers(out->image_id, &out->drawable_count); + + return success; +} + +static void calc_watermark_xy (int imgwidth, int imgheight, int wmwidth, int wmheight, watermark_position position, int edge, gdouble* posX, gdouble* posY) { + if (position == WM_POS_TL) { + *posX = edge; + *posY = edge; + } + else if (position == WM_POS_TC) { + *posX = (imgwidth / 2) - (wmwidth / 2); + *posY = edge; + } + else if (position == WM_POS_TR) { + *posX = imgwidth - wmwidth - edge; + *posY = edge; + } + else if (position == WM_POS_BL) { + *posX = edge; + *posY = imgheight - wmheight - edge; + } + else if (position == WM_POS_BC) { + *posX = (imgwidth / 2) - (wmwidth / 2); + *posY = imgheight - wmheight - edge; + } + else if (position == WM_POS_BR) { + *posX = imgwidth - wmwidth - edge; + *posY = imgheight - wmheight - edge; + } + else if (position == WM_POS_CL) { + *posX = edge; + *posY = (imgheight / 2) - (wmheight / 2); + } + else if (position == WM_POS_CR) { + *posX = imgwidth - wmwidth - edge; + *posY = (imgheight / 2) - (wmheight / 2); + } + else { + *posX = (imgwidth / 2) - (wmwidth / 2); + *posY = (imgheight / 2) - (wmheight / 2); + } +} + +static gboolean apply_userdef(userdef_settings settings, image_output out) +{ + gboolean success = TRUE; + + int param_i; + GimpParamDef param_info; + gboolean saving_function = (strstr(settings->procedure, "-save") != NULL); + + int single_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + gimp_selection_none(out->image_id); + + for (param_i = 0; param_i < settings->num_params; param_i++) { + switch((settings->params[param_i]).type) { + case GIMP_PDB_IMAGE: + (settings->params[param_i]).data.d_image = out->image_id; + break; + + case GIMP_PDB_DRAWABLE: + case GIMP_PDB_ITEM: + (settings->params[param_i]).data.d_drawable = single_drawable; + break; + + case GIMP_PDB_STRING: + if (saving_function) { + param_info = pdb_proc_get_param_info(settings->procedure, param_i); + if (strcmp(param_info.name, "filename") == 0) { + (settings->params[param_i]).data.d_string = g_strdup(out->filepath); + } + else if (strcmp(param_info.name, "raw-filename") == 0) { + (settings->params[param_i]).data.d_string = g_strdup(out->filename); + } + } + break; + + default: break; + } + } + + gint nreturn_vals; + GimpParam *return_vals = gimp_run_procedure2( + settings->procedure, + &nreturn_vals, + settings->num_params, + settings->params + ); + + gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + g_free(out->drawable_ids); + out->drawable_ids = gimp_image_get_layers(out->image_id, &out->drawable_count); + + return success; +} + +static gboolean apply_rename(rename_settings settings, image_output out, char* orig_basename) +{ + char *orig_name = g_strdup(orig_basename); + + out->filename = g_strdup(settings->pattern); + + // search for 'RENAME_KEY_ORIG' occurrences and replace the final filename + if(strstr(out->filename, RENAME_KEY_ORIG) != NULL) { + out->filename = str_replace(out->filename, RENAME_KEY_ORIG, orig_name); + } + + // same thing for count and datetime + + if(strstr(out->filename, RENAME_KEY_COUNT) != NULL) { + char strcount[5]; + sprintf(strcount, "%i", processed_count + 1); + out->filename = str_replace(out->filename, RENAME_KEY_COUNT, strcount); + } + + if(strstr(out->filename, RENAME_KEY_DATETIME) != NULL) { + out->filename = str_replace(out->filename, RENAME_KEY_DATETIME, current_datetime); + } + + g_free(orig_name); + + return TRUE; +} + +// following: set of functions that saves the image file in various formats + +static gboolean image_save(format_type type, image_output imageout, format_params params) +{ + gboolean result; + + if (type == FORMAT_BMP) { + result = image_save_bmp(imageout); + } + else if(type == FORMAT_GIF) { + result = image_save_gif(imageout, ((format_params_gif)params)->interlace); + } + else if(type == FORMAT_ICON) { + result = image_save_icon(imageout); + } + else if(type == FORMAT_JPEG) { + result = image_save_jpeg( + imageout, + ((format_params_jpeg)params)->quality, + ((format_params_jpeg)params)->smoothing, + ((format_params_jpeg)params)->entropy, + ((format_params_jpeg)params)->progressive, + ((format_params_jpeg)params)->comment, + ((format_params_jpeg)params)->subsampling, + ((format_params_jpeg)params)->baseline, + ((format_params_jpeg)params)->markers, + ((format_params_jpeg)params)->dct + ); + } + else if(type == FORMAT_PNG) { + result = image_save_png(imageout, + ((format_params_png)params)->interlace, + ((format_params_png)params)->compression, + ((format_params_png)params)->savebgc, + ((format_params_png)params)->savegamma, + ((format_params_png)params)->saveoff, + ((format_params_png)params)->savephys, + ((format_params_png)params)->savetime, + ((format_params_png)params)->savecomm, + ((format_params_png)params)->savetrans + ); + } + else if(type == FORMAT_TGA) { + result = image_save_tga(imageout, ((format_params_tga)params)->rle, ((format_params_tga)params)->origin); + } + else if(type == FORMAT_TIFF) { + result = image_save_tiff(imageout, ((format_params_tiff)params)->compression); + } + else if(type == FORMAT_HEIF) { + result = image_save_heif( + imageout, + ((format_params_heif)params)->quality, + ((format_params_heif)params)->lossless + ); + } + else if(type == FORMAT_WEBP) { + result = image_save_webp( + imageout, + ((format_params_webp)params)->preset, + ((format_params_webp)params)->lossless, + ((format_params_webp)params)->quality, + ((format_params_webp)params)->alpha_quality, + ((format_params_webp)params)->animation, + ((format_params_webp)params)->anim_loop, + ((format_params_webp)params)->minimize_size, + ((format_params_webp)params)->kf_distance, + ((format_params_webp)params)->exif, + ((format_params_webp)params)->iptc, + ((format_params_webp)params)->xmp, + ((format_params_webp)params)->delay, + ((format_params_webp)params)->force_delay + ); + } + else if(type == FORMAT_AVIF) { + result = image_save_avif( + imageout, + ((format_params_avif)params)->lossless, + ((format_params_avif)params)->quality + ); + } + else if(type == FORMAT_EXR) { + result = image_save_exr(imageout); + } + else { + // save in the original format + int final_drawable = gimp_image_merge_visible_layers(imageout->image_id, GIMP_CLIP_TO_IMAGE); + // but first check if the images was a GIF and it's palette has changed during the process + if (file_has_extension(imageout->filename, ".gif") && gimp_drawable_is_rgb(final_drawable)) { + gimp_image_convert_indexed( + imageout->image_id, + GIMP_CONVERT_DITHER_FS, + GIMP_CONVERT_PALETTE_GENERATE, + gimp_drawable_has_alpha (final_drawable) ? 255 : 256, + TRUE, + FALSE, + "" + ); + } + + // for HEIF, the default values are "0 quality"... save lossless instead + if ((file_has_extension(imageout->filename, ".heif") || file_has_extension(imageout->filename, ".heic"))) { + result = image_save_heif( + imageout, + 100, + TRUE + ); + } + // same thing for WEBP + else if (file_has_extension(imageout->filename, ".webp")) { + result = image_save_webp( + imageout, + 0, + FALSE, + 90, + 100, + FALSE, + TRUE, + TRUE, + 50, + TRUE, + TRUE, + TRUE, + 200, + FALSE + ); + } + else + { + result = gimp_file_save( + GIMP_RUN_NONINTERACTIVE, + imageout->image_id, + final_drawable, + imageout->filepath, + imageout->filename + ); + } + } + + return result; +} + +static gboolean image_save_bmp(image_output out) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_bmp_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_gif(image_output out, gboolean interlace) +{ + gint nreturn_vals; + + // first, convert to indexed-256 color mode + gimp_image_convert_indexed( + out->image_id, + GIMP_CONVERT_DITHER_FS, + GIMP_CONVERT_PALETTE_GENERATE, + gimp_drawable_has_alpha (out->drawable_ids[0]) ? 255 : 256, + TRUE, + FALSE, + "" + ); + + GimpParam *return_vals = gimp_run_procedure( + "file_gif_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, 0, // drawable is ignored + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, interlace ? 1 : 0, // Try to save as interlaced + GIMP_PDB_INT32, 1, // (animated gif) loop infinitely + GIMP_PDB_INT32, 0, // (animated gif) Default delay between framese in milliseconds + GIMP_PDB_INT32, 0, // (animated gif) Default disposal type (0=don't care, 1=combine, 2=replace) + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_icon(image_output out) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_ico_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_jpeg(image_output out, float quality, float smoothing, gboolean entropy, gboolean progressive, gchar* comment, int subsampling, gboolean baseline, int markers, int dct) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + // "file_jpeg_save" doesn't support indexed images + if (gimp_drawable_is_indexed(final_drawable)) { + gimp_image_convert_rgb(out->image_id); + } + + GimpParam *return_vals = gimp_run_procedure( + "file_jpeg_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_FLOAT, quality >= 3 ? quality/100 : 0.03, // Quality of saved image (0 <= quality <= 1) + small fix because final image doesn't change when quality < 3 + GIMP_PDB_FLOAT, smoothing, // Smoothing factor for saved image (0 <= smoothing <= 1) + GIMP_PDB_INT32, entropy ? 1 : 0, // Optimization of entropy encoding parameters (0/1) + GIMP_PDB_INT32, progressive ? 1 : 0, // Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG (0/1) + GIMP_PDB_STRING, comment, // Image comment + GIMP_PDB_INT32, subsampling, // The subsampling option number + GIMP_PDB_INT32, baseline ? 1 : 0, // Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1) + GIMP_PDB_INT32, markers, // Frequency of restart markers (in rows, 0 = no restart markers) + GIMP_PDB_INT32, dct, // DCT algorithm to use (speed/quality tradeoff) + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_heif(image_output out, int quality, gboolean lossless) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_heif_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, quality, // Quality factor (range: 0-100. 0 = worst, 100 = best) + GIMP_PDB_INT32, lossless ? 1 : 0, // Use lossless compression (0 = lossy, 1 = lossless) + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_png(image_output out, gboolean interlace, int compression, gboolean savebgc, gboolean savegamma, gboolean saveoff, gboolean savephys, gboolean savetime, gboolean savecomm, gboolean savetrans) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_png_save2", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, interlace? 1 : 0, // Use Adam7 interlacing? + GIMP_PDB_INT32, compression, // Deflate Compression factor (0-9) + GIMP_PDB_INT32, savebgc? 1 : 0, // Write bKGD chunk? + GIMP_PDB_INT32, savegamma? 1 : 0, // Write gAMA chunk? + GIMP_PDB_INT32, saveoff? 1 : 0, // Write oFFs chunk? + GIMP_PDB_INT32, savephys? 1 : 0, // Write phys chunk? + GIMP_PDB_INT32, savetime? 1 : 0, // Write tIME chunk? + GIMP_PDB_INT32, savecomm? 1 : 0, // Write comments chunk? + GIMP_PDB_INT32, savetrans? 1 : 0, // Write trans chunk? + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_tga(image_output out, gboolean rle, int origin) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_tga_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, rle? 1 : 0, // Use RLE compression + GIMP_PDB_INT32, origin, // Image origin + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_tiff(image_output out, int compression) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_tiff_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, compression, // Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3), JPEG (4) } + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_webp(image_output out, int preset, gboolean lossless, float quality, float alpha_quality, gboolean animation, gboolean anim_loop, gboolean minimize_size, int kf_distance, gboolean exif, gboolean iptc, gboolean xmp, int delay, int force_delay) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_webp_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, preset, // preset (Default=0, Picture=1, Photo=2, Drawing=3, Icon=4, Text=5) + GIMP_PDB_INT32, lossless, // Use lossless encoding (0/1) + GIMP_PDB_FLOAT, quality, // Quality of the image (0 <= quality <= 100) + GIMP_PDB_FLOAT, alpha_quality, // Quality of the image's alpha channel (0 <= alpha-quality <= 100) + GIMP_PDB_INT32, animation, // Use layers for animation (0/1) + GIMP_PDB_INT32, anim_loop, // Loop animation infinitely (0/1) + GIMP_PDB_INT32, minimize_size, // Minimize animation size (0/1) + GIMP_PDB_INT32, kf_distance, // Maximum distance between key-frames (>=0) + GIMP_PDB_INT32, exif, // Toggle saving exif data (0/1) + GIMP_PDB_INT32, iptc, // Toggle saving iptc data (0/1) + GIMP_PDB_INT32, xmp, // Toggle saving xmp data (0/1) + GIMP_PDB_INT32, delay, // Delay to use when timestamps are not available or forced + GIMP_PDB_INT32, force_delay, // Force delay on all frames + GIMP_PDB_END + ); + + return TRUE; +} + +static gboolean image_save_avif(image_output out, gboolean lossless, int quality) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_heif_av1_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_INT32, quality, // Quality of the image (0 <= quality <= 100) + GIMP_PDB_INT32, lossless, // Use lossless encoding (0/1) + GIMP_PDB_END + ); +} + +static gboolean image_save_exr(image_output out) +{ + gint nreturn_vals; + int final_drawable = gimp_image_merge_visible_layers(out->image_id, GIMP_CLIP_TO_IMAGE); + + GimpParam *return_vals = gimp_run_procedure( + "file_exr_save", + &nreturn_vals, + GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, + GIMP_PDB_IMAGE, out->image_id, + GIMP_PDB_DRAWABLE, final_drawable, + GIMP_PDB_STRING, out->filepath, + GIMP_PDB_STRING, out->filename, + GIMP_PDB_END + ); + + return TRUE; +} + +/* returns a result code following this schema: + * 0 = user responses "don't overwrite" to a confirm dialog + * 1 = old file was the same as the new one and user responses "yes, overwrite" + * 2 = old file wasn't the same (implicit overwrite) */ +static int overwrite_result(char* path, GtkWidget* parent) { + gboolean oldfile_access = g_file_test(path, G_FILE_TEST_IS_REGULAR); + + if ( (bimp_opt_alertoverwrite == BIMP_ASK_OVERWRITE) && oldfile_access) { + GtkWidget *dialog; + GtkWidget *check_alertoverwrite; + GtkWidget *dialog_action; + + + dialog = gtk_message_dialog_new( + GTK_WINDOW(parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("File %s already exists, overwrite it?"), comp_get_filename(path) + ); + + // Add checkbox "Always apply decision" + dialog_action = gtk_dialog_get_action_area(GTK_DIALOG(dialog)); + check_alertoverwrite = gtk_check_button_new_with_label(_("Always apply this decision")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_alertoverwrite), FALSE); + gtk_box_pack_start (GTK_BOX(dialog_action), check_alertoverwrite, FALSE, FALSE, 0); + gtk_widget_show (check_alertoverwrite); + + gtk_dialog_add_buttons ( + GTK_DIALOG(dialog), + GTK_STOCK_YES, GTK_RESPONSE_YES, + + GTK_STOCK_NO, GTK_RESPONSE_NO, NULL + ); + + gtk_window_set_title(GTK_WINDOW(dialog), _("Overwrite?")); + gint result = gtk_dialog_run(GTK_DIALOG(dialog)); + gboolean dont_ask_anymore = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_alertoverwrite)); + gtk_widget_destroy(dialog); + + + if (result == GTK_RESPONSE_YES) { + if (dont_ask_anymore) + bimp_opt_alertoverwrite = BIMP_OVERWRITE_SKIP_ASK; + return 1; + } + else { + if (dont_ask_anymore) + bimp_opt_alertoverwrite = BIMP_DONT_OVERWRITE_SKIP_ASK; + + return 0; + } + } + else { + if (oldfile_access) { + return (bimp_opt_alertoverwrite == BIMP_OVERWRITE_SKIP_ASK) ? 1 : 0; + } + else { + return 2; + } + } +} diff --git a/src/bimp-serialize.c b/src/bimp-serialize.c index eb1cd6f..efa11e3 100644 --- a/src/bimp-serialize.c +++ b/src/bimp-serialize.c @@ -1,1016 +1,1031 @@ -/* - * Functions to serialize and load manipulation sets - * Serialization uses GKeyFile method - */ - -#include -#include -#include -#include -#include -#include -#include -#include "bimp.h" -#include "bimp-manipulations.h" -#include "bimp-utils.h" -#include "bimp-serialize.h" - -static void append_manipulation_details(manipulation, GKeyFile*); -static GSList* parse_manipulations(GKeyFile*, int); -static void write_resize(resize_settings, GKeyFile*); -static manipulation read_resize(GKeyFile*); -static void write_crop(crop_settings, GKeyFile*); -static manipulation read_crop(GKeyFile*); -static void write_fliprotate(fliprotate_settings, GKeyFile*); -static manipulation read_fliprotate(GKeyFile*); -static void write_color(color_settings, GKeyFile*); -static manipulation read_color(GKeyFile*, int); -static void write_sharpblur(sharpblur_settings, GKeyFile*); -static manipulation read_sharpblur(GKeyFile*); -static void write_watermark(watermark_settings, GKeyFile*, int); -static manipulation read_watermark(GKeyFile*, int); -static void write_changeformat(changeformat_settings, GKeyFile*); -static manipulation read_changeformat(GKeyFile*); -static void write_rename(rename_settings, GKeyFile*); -static manipulation read_rename(GKeyFile*); -static void write_userdef(userdef_settings, GKeyFile*, int); -static manipulation read_userdef(GKeyFile*, int); - -static int watermark_count, userdef_count; -static int loaded_build; - -gboolean bimp_serialize_to_file(gchar* filename) -{ - gboolean result; - - GKeyFile *output_file = g_key_file_new(); - g_key_file_set_list_separator(output_file, ';'); - - g_key_file_set_comment (output_file, NULL, NULL, g_strdup_printf("BIMP %s\nMANIPULATION SET DEFINITION", PLUG_IN_VERSION), NULL); - - watermark_count = 0; - userdef_count = 0; - g_slist_foreach(bimp_selected_manipulations, (GFunc)append_manipulation_details, output_file); - - result = g_file_set_contents (filename, g_key_file_to_data(output_file, NULL, NULL), -1, NULL); - - g_key_file_free(output_file); - - return result; -} - -gboolean bimp_deserialize_from_file(gchar* filename) -{ - gboolean result; - - GKeyFile* input_file = g_key_file_new(); - g_key_file_set_list_separator(input_file, ';'); - - if ((result = g_key_file_load_from_file (input_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL))) { - - // read build code - int buildnumber = 0; - gchar* header = g_key_file_get_comment(input_file, NULL, NULL, NULL); - GRegex *regex = g_regex_new ("^BIMP\\s(\\d+)\\.(\\d+)", 0, 0, NULL); - - GMatchInfo *match_info; - g_regex_match (regex, header, 0, &match_info); - while (g_match_info_matches (match_info)) - { - gchar *major = g_match_info_fetch (match_info, 1); - int major_i = g_ascii_strtoll(major, NULL, 10); - gchar *minor = g_match_info_fetch (match_info, 2); - int minor_i = g_ascii_strtoll(major, NULL, 10); - buildnumber = (major_i * 1000) + minor_i; - if (buildnumber > 0) break; - - g_match_info_next (match_info, NULL); - } - g_match_info_free (match_info); - g_regex_unref (regex); - - GSList* new_list = parse_manipulations(input_file, buildnumber); - if (new_list != NULL) { - g_slist_free(bimp_selected_manipulations); - bimp_selected_manipulations = new_list; - - result = TRUE; - } - else { - result = FALSE; - } - } - - g_key_file_free(input_file); - - return result; -} - -static void append_manipulation_details(manipulation man, GKeyFile* output_file) -{ - if (man->type == MANIP_RESIZE) { - write_resize((resize_settings)man->settings, output_file); - } - else if (man->type == MANIP_CROP) { - write_crop((crop_settings)man->settings, output_file); - } - else if (man->type == MANIP_FLIPROTATE) { - write_fliprotate((fliprotate_settings)man->settings, output_file); - } - else if (man->type == MANIP_COLOR) { - write_color((color_settings)man->settings, output_file); - } - else if (man->type == MANIP_SHARPBLUR) { - write_sharpblur((sharpblur_settings)man->settings, output_file); - } - else if (man->type == MANIP_WATERMARK) { - write_watermark((watermark_settings)man->settings, output_file, watermark_count); - watermark_count++; - } - else if (man->type == MANIP_CHANGEFORMAT) { - write_changeformat((changeformat_settings)man->settings, output_file); - } - else if (man->type == MANIP_RENAME) { - write_rename((rename_settings)man->settings, output_file); - } - else if (man->type == MANIP_USERDEF) { - write_userdef((userdef_settings)man->settings, output_file, userdef_count); - userdef_count++; - } -} - -static GSList* parse_manipulations(GKeyFile* file, int version) -{ - GSList* manipulations = NULL; - manipulation newman = NULL; - gsize count = 0; - gchar** groups; - - groups = g_key_file_get_groups(file, &count); - int i = 0; - while (i < count) { - - if (strcmp(groups[i], "RESIZE") == 0) { - newman = read_resize(file); - } - else if (strcmp(groups[i], "CROP") == 0) { - newman = read_crop(file); - } - else if (strcmp(groups[i], "FLIPROTATE") == 0) { - newman = read_fliprotate(file); - } - else if (strcmp(groups[i], "COLOR") == 0) { - newman = read_color(file, version); - } - else if (strcmp(groups[i], "SHARPBLUR") == 0) { - newman = read_sharpblur(file); - } - else if (strncmp(groups[i], "WATERMARK", strlen("WATERMARK")) == 0) { - if (strcmp(groups[i], "WATERMARK") == 0) { - newman = read_watermark(file, -1); - } - else { - int watermark_id; - if (sscanf(groups[i], "WATERMARK%d", &watermark_id) == 1) { - newman = read_watermark(file, watermark_id); - } - } - } - else if (strcmp(groups[i], "CHANGEFORMAT") == 0) { - newman = read_changeformat(file); - } - else if (strcmp(groups[i], "RENAME") == 0) { - newman = read_rename(file); - } - else if (strncmp(groups[i], "USERDEF", strlen("USERDEF")) == 0) { - int userdef_id; - if (sscanf(groups[i], "USERDEF%d", &userdef_id) == 1) { - newman = read_userdef(file, userdef_id); - } - } - - if (newman != NULL) manipulations = g_slist_append(manipulations, newman); - - i++; - } - - return manipulations; -} - -static void write_resize(resize_settings settings, GKeyFile* file) -{ - gchar* group_name = "RESIZE"; - - g_key_file_set_double(file, group_name, "new_w_pc", settings->new_w_pc); - g_key_file_set_double(file, group_name, "new_h_pc", settings->new_h_pc); - g_key_file_set_integer(file, group_name, "new_w_px", settings->new_w_px); - g_key_file_set_integer(file, group_name, "new_h_px", settings->new_h_px); - g_key_file_set_integer(file, group_name, "resize_mode_width", settings->resize_mode_width); - g_key_file_set_integer(file, group_name, "resize_mode_height", settings->resize_mode_height); - g_key_file_set_integer(file, group_name, "stretch_mode", settings->stretch_mode); - g_key_file_set_string(file, group_name, "padding_color", gdk_color_to_string(&(settings->padding_color))); - g_key_file_set_integer(file, group_name, "padding_color_alpha", settings->padding_color_alpha); - g_key_file_set_integer(file, group_name, "interpolation", settings->interpolation); - g_key_file_set_boolean(file, group_name, "change_res", settings->change_res); - g_key_file_set_integer(file, group_name, "new_res_x", settings->new_res_x); - g_key_file_set_integer(file, group_name, "new_res_y", settings->new_res_y); -} - -/* deserializes a string and returns a resize manipulation */ -static manipulation read_resize(GKeyFile* file) -{ - gchar* group_name = "RESIZE"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_resize_new(); - resize_settings settings = ((resize_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "new_w_pc", NULL)) - settings->new_w_pc = g_key_file_get_double(file, group_name, "new_w_pc", NULL); - - if (g_key_file_has_key(file, group_name, "new_h_pc", NULL)) - settings->new_h_pc = g_key_file_get_double(file, group_name, "new_h_pc", NULL); - - if (g_key_file_has_key(file, group_name, "new_w_px", NULL)) - settings->new_w_px = g_key_file_get_integer(file, group_name, "new_w_px", NULL); - - if (g_key_file_has_key(file, group_name, "new_h_px", NULL)) - settings->new_h_px = g_key_file_get_integer(file, group_name, "new_h_px", NULL); - - // for backwards compatibility - if (g_key_file_has_key(file, group_name, "resize_mode", NULL)) { - switch(g_key_file_get_integer(file, group_name, "resize_mode", NULL)) { - case 0: // RESIZE_PERCENT - settings->resize_mode_width = settings->resize_mode_height = RESIZE_PERCENT; - break; - case 1: // RESIZE_PIZEL_BOTH - settings->resize_mode_width = settings->resize_mode_height = RESIZE_PIXEL; - break; - case 2: // RESIZE_PIXEL_WIDTH - settings->resize_mode_width = RESIZE_PIXEL; - settings->resize_mode_height = RESIZE_DISABLE; - break; - case 3: // RESIZE_PIXEL_HEIGHT - settings->resize_mode_width = RESIZE_DISABLE; - settings->resize_mode_height = RESIZE_PIXEL; - break; - case 4: // RESIZE_END - default: - settings->resize_mode_width = settings->resize_mode_height = RESIZE_END; - } - } - - if (g_key_file_has_key(file, group_name, "resize_mode_width", NULL)) - settings->resize_mode_width = g_key_file_get_integer(file, group_name, "resize_mode_width", NULL); - - if (g_key_file_has_key(file, group_name, "resize_mode_height", NULL)) - settings->resize_mode_height = g_key_file_get_integer(file, group_name, "resize_mode_height", NULL); - - if (g_key_file_has_key(file, group_name, "stretch_mode", NULL)) - settings->stretch_mode = g_key_file_get_integer(file, group_name, "stretch_mode", NULL); - - if (g_key_file_has_key(file, group_name, "padding_color", NULL)) - gdk_color_parse(g_key_file_get_string(file, group_name, "padding_color", NULL), &(settings->padding_color)); - - if (g_key_file_has_key(file, group_name, "padding_color_alpha", NULL)) - settings->padding_color_alpha = (guint16)g_key_file_get_integer(file, group_name, "padding_color_alpha", NULL); - - if (g_key_file_has_key(file, group_name, "interpolation", NULL)) - settings->interpolation = g_key_file_get_integer(file, group_name, "interpolation", NULL); - - if (g_key_file_has_key(file, group_name, "change_res", NULL)) - settings->change_res = g_key_file_get_boolean(file, group_name, "change_res", NULL); - - if (g_key_file_has_key(file, group_name, "new_res_x", NULL)) - settings->new_res_x = g_key_file_get_integer(file, group_name, "new_res_x", NULL); - - if (g_key_file_has_key(file, group_name, "new_res_y", NULL)) - settings->new_res_y = g_key_file_get_integer(file, group_name, "new_res_y", NULL); - } - - return man; -} - -static void write_crop(crop_settings settings, GKeyFile* file) -{ - gchar* group_name = "CROP"; - - g_key_file_set_integer(file, group_name, "new_w", settings->new_w); - g_key_file_set_integer(file, group_name, "new_h", settings->new_h); - g_key_file_set_boolean(file, group_name, "manual", settings->manual); - g_key_file_set_integer(file, group_name, "ratio", settings->ratio); - g_key_file_set_double(file, group_name, "custom_ratio1", settings->custom_ratio1); - g_key_file_set_double(file, group_name, "custom_ratio2", settings->custom_ratio2); - g_key_file_set_double(file, group_name, "start_pos", settings->start_pos); -} - -static manipulation read_crop(GKeyFile* file) -{ - gchar* group_name = "CROP"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_crop_new(); - crop_settings settings = ((crop_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "new_w", NULL)) - settings->new_w = g_key_file_get_integer(file, group_name, "new_w", NULL); - - if (g_key_file_has_key(file, group_name, "new_h", NULL)) - settings->new_h = g_key_file_get_integer(file, group_name, "new_h", NULL); - - if (g_key_file_has_key(file, group_name, "manual", NULL)) - settings->manual = g_key_file_get_boolean(file, group_name, "manual", NULL); - - if (g_key_file_has_key(file, group_name, "ratio", NULL)) - settings->ratio = g_key_file_get_integer(file, group_name, "ratio", NULL); - - if (g_key_file_has_key(file, group_name, "custom_ratio1", NULL)) - settings->custom_ratio1 = g_key_file_get_integer(file, group_name, "custom_ratio1", NULL); - - if (g_key_file_has_key(file, group_name, "custom_ratio2", NULL)) - settings->custom_ratio2 = g_key_file_get_integer(file, group_name, "custom_ratio2", NULL); - - if (g_key_file_has_key(file, group_name, "start_pos", NULL)) - settings->start_pos = g_key_file_get_integer(file, group_name, "start_pos", NULL); - } - - return man; -} - -static void write_fliprotate(fliprotate_settings settings, GKeyFile* file) -{ - gchar* group_name = "FLIPROTATE"; - - g_key_file_set_boolean(file, group_name, "flip_h", settings->flip_h); - g_key_file_set_boolean(file, group_name, "flip_v", settings->flip_v); - g_key_file_set_boolean(file, group_name, "rotate", settings->rotate); - g_key_file_set_integer(file, group_name, "rotation_type", settings->rotation_type); -} - -static manipulation read_fliprotate(GKeyFile* file) -{ - gchar* group_name = "FLIPROTATE"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_fliprotate_new(); - fliprotate_settings settings = ((fliprotate_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "flip_h", NULL)) - settings->flip_h = g_key_file_get_boolean(file, group_name, "flip_h", NULL); - - if (g_key_file_has_key(file, group_name, "flip_v", NULL)) - settings->flip_v = g_key_file_get_boolean(file, group_name, "flip_v", NULL); - - if (g_key_file_has_key(file, group_name, "rotate", NULL)) - settings->rotate = g_key_file_get_boolean(file, group_name, "rotate", NULL); - - if (g_key_file_has_key(file, group_name, "rotation_type", NULL)) - settings->rotation_type = g_key_file_get_integer(file, group_name, "rotation_type", NULL); - } - - return man; -} - -static void write_color(color_settings settings, GKeyFile* file) -{ - gchar* group_name = "COLOR"; - - g_key_file_set_double(file, group_name, "brightness", settings->brightness); - g_key_file_set_double(file, group_name, "contrast", settings->contrast); - g_key_file_set_boolean(file, group_name, "levels_auto", settings->levels_auto); - g_key_file_set_boolean(file, group_name, "grayscale", settings->grayscale); - if (settings->curve_file != NULL) g_key_file_set_string(file, group_name, "curve_file", settings->curve_file); -} - -static manipulation read_color(GKeyFile* file, int version) -{ - gchar* group_name = "COLOR"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_color_new(); - color_settings settings = ((color_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "brightness", NULL)) - { - double b = g_key_file_get_double(file, group_name, "brightness", NULL); - if (version < 2000) - { - // prior to BIMP 2.0, brightness range was [-127;127] - // now map to [-0.5;0.5] - settings->brightness = -0.5 + ((0.5 - (-0.5)) / (127 - (-127)) * (b - (-127))); - } - else - { - settings->brightness = b; - } - } - - if (g_key_file_has_key(file, group_name, "contrast", NULL)) - { - double c = g_key_file_get_double(file, group_name, "contrast", NULL); - if (version < 2000) - { - // prior to BIMP 2.0, contrast range was [-127;127] - // now map to [-0.5;0.5] - settings->contrast = -0.5 + ((0.5 - (-0.5)) / (127 - (-127)) * (c - (-127))); - } - else - { - settings->contrast = c; - } - } - - if (g_key_file_has_key(file, group_name, "levels_auto", NULL)) - settings->levels_auto = g_key_file_get_boolean(file, group_name, "levels_auto", NULL); - - if (g_key_file_has_key(file, group_name, "grayscale", NULL)) - settings->grayscale = g_key_file_get_boolean(file, group_name, "grayscale", NULL); - - if (g_key_file_has_key(file, group_name, "curve_file", NULL)) - settings->curve_file = g_key_file_get_string(file, group_name, "curve_file", NULL); - } - - return man; -} - -/* Reads the content of a GIMP's curve file (for Color manipulations). - * The result is saved to be used with the gimp_curves_spline() function */ -gboolean parse_curve_file( - char* file, - int* num_points_v, - gdouble** ctr_points_v, - int* num_points_r, - gdouble** ctr_points_r, - int* num_points_g, - gdouble** ctr_points_g, - int* num_points_b, - gdouble** ctr_points_b, - int* num_points_a, - gdouble** ctr_points_a -) { - FILE* pFile; - pFile = fopen (file, "r"); - - char* old_locale = setlocale(LC_NUMERIC, ""); - setlocale(LC_NUMERIC, "C"); - - char line[2400]; - char channel_name[6]; - int num_points_temp = 0; - gdouble* ctr_points_temp = NULL; - - if (pFile == NULL) goto err; - else { - // read header ("# GIMP curves tool settings") - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - if (!g_str_has_prefix(line, "# GIMP")) goto err; - - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - while(!g_str_has_prefix(line, "(channel ")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - - // reached the first "(channel " line - while (sscanf (line, "(channel %[a-z])", channel_name) == 1) { - - g_free(ctr_points_temp); - ctr_points_temp = NULL; - - // "(curve", ignored - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - if (g_str_has_prefix(line, "(curve")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - // " (curve-type", ignored - if (g_str_has_prefix(line, " (curve-type")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - - // n-points, ignored (also it is missing since GIMP 2.10) - // the actual number of points is parsed from "(points N..." - if (g_str_has_prefix(line, " (n-points")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - - // number of points and list - double pX, pY; - int p_count = 0; - char* token = strtok(line + strlen(g_strdup_printf(" (points ")), " "); - int num_points = atoi(token); // first token holds the number of points - - token = strtok(NULL, " "); - while (token) { - pX = atof(token); - token = strtok(NULL, " "); - if (!token) goto err; - pY = atof(token); - - if (pX >= 0 && pX <= 1 && - pY >= 0 && pY <= 1) - { - // save X and Y - ctr_points_temp = (gdouble*)g_realloc(ctr_points_temp, sizeof(gdouble) * (p_count + 2)); // add one element to the array - ctr_points_temp[p_count] = pX; - ctr_points_temp[p_count + 1] = pY; - - p_count += 2; - } - - token = strtok(NULL, " "); - } - - num_points_temp = p_count; - - // "(n-samples XX)", ignored - if (g_str_has_prefix(line, " (n-samples")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - // "(samples XX ...", ignored - if (g_str_has_prefix(line, " (samples")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - } - - // save in the proper variables - if (strcmp(channel_name, "value") == 0) { - *num_points_v = num_points_temp; - *ctr_points_v = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); - } - else if (strcmp(channel_name, "red") == 0) { - *num_points_r = num_points_temp; - *ctr_points_r = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); - } - else if (strcmp(channel_name, "green") == 0) { - *num_points_g = num_points_temp; - *ctr_points_g = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); - } - else if (strcmp(channel_name, "blue") == 0) { - *num_points_b = num_points_temp; - *ctr_points_b = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); - } - else if (strcmp(channel_name, "alpha") == 0) { - *num_points_a = num_points_temp; - *ctr_points_a = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); - } - else goto err; - - // reach the next channel, or the end if this was the last - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - while(!g_str_has_prefix(line, "(channel ")) { - if (fgets(line, sizeof(line), pFile) == NULL) goto err; - if (g_str_has_prefix(line, "# end")) goto finish; - } - } - - // "# end of curves tool settings" -finish: - setlocale(LC_NUMERIC, old_locale); - g_free(ctr_points_temp); - fclose (pFile); - return TRUE; - } - -err: - setlocale(LC_NUMERIC, old_locale); - if (pFile != NULL) fclose (pFile); - if (ctr_points_temp != NULL) g_free(ctr_points_temp); - return FALSE; -} - -static void write_sharpblur(sharpblur_settings settings, GKeyFile* file) -{ - gchar* group_name = "SHARPBLUR"; - - g_key_file_set_integer(file, group_name, "amount", settings->amount); -} - -static manipulation read_sharpblur(GKeyFile* file) -{ - gchar* group_name = "SHARPBLUR"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_sharpblur_new(); - sharpblur_settings settings = ((sharpblur_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "amount", NULL)) - settings->amount = g_key_file_get_integer(file, group_name, "amount", NULL); - } - - return man; -} - -static void write_watermark(watermark_settings settings, GKeyFile* file, int id) -{ - gchar* group_name = g_strdup_printf("WATERMARK%d", id); - - g_key_file_set_boolean(file, group_name, "mode", settings->mode); - g_key_file_set_string(file, group_name, "text", settings->text); - g_key_file_set_string(file, group_name, "font", pango_font_description_to_string(settings->font)); - g_key_file_set_string(file, group_name, "color", gdk_color_to_string(&(settings->color))); - if (settings->image_file != NULL) g_key_file_set_string(file, group_name, "image_file", settings->image_file); - g_key_file_set_integer(file, group_name, "image_sizemode", settings->image_sizemode); - g_key_file_set_double(file, group_name, "image_size_percent", settings->image_size_percent); - g_key_file_set_integer(file, group_name, "opacity", settings->opacity); - g_key_file_set_integer(file, group_name, "edge_distance", settings->edge_distance); - g_key_file_set_integer(file, group_name, "position", settings->position); -} - -static manipulation read_watermark(GKeyFile* file, int id) -{ - gchar* group_name; - if (id == -1) group_name = "WATERMARK"; - else group_name = g_strdup_printf("WATERMARK%d", id); - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_watermark_new(); - watermark_settings settings = ((watermark_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "mode", NULL)) - settings->mode = g_key_file_get_boolean(file, group_name, "mode", NULL); - - if (g_key_file_has_key(file, group_name, "text", NULL)) - settings->text = g_key_file_get_string(file, group_name, "text", NULL); - - if (g_key_file_has_key(file, group_name, "font", NULL)) - settings->font = pango_font_description_from_string(g_key_file_get_string(file, group_name, "font", NULL)); - - if (g_key_file_has_key(file, group_name, "color", NULL)) - gdk_color_parse(g_key_file_get_string(file, group_name, "color", NULL), &(settings->color)); - - if (g_key_file_has_key(file, group_name, "image_file", NULL)) - settings->image_file = g_key_file_get_string(file, group_name, "image_file", NULL); - - if (g_key_file_has_key(file, group_name, "image_sizemode", NULL)) - settings->image_sizemode = g_key_file_get_integer(file, group_name, "image_sizemode", NULL); - - if (g_key_file_has_key(file, group_name, "image_size_percent", NULL)) - settings->image_size_percent = g_key_file_get_double(file, group_name, "image_size_percent", NULL); - - if (g_key_file_has_key(file, group_name, "opacity", NULL)) - settings->opacity = g_key_file_get_integer(file, group_name, "opacity", NULL); - - if (g_key_file_has_key(file, group_name, "edge_distance", NULL)) - settings->edge_distance = g_key_file_get_integer(file, group_name, "edge_distance", NULL); - - if (g_key_file_has_key(file, group_name, "position", NULL)) - settings->position = g_key_file_get_integer(file, group_name, "position", NULL); - } - - return man; -} - -static void write_changeformat(changeformat_settings settings, GKeyFile* file) -{ - gchar* group_name = "CHANGEFORMAT"; - - g_key_file_set_integer(file, group_name, "format", settings->format); - - if(settings->format == FORMAT_GIF) { - format_params_gif params = settings->params; - g_key_file_set_boolean(file, group_name, "interlace", params->interlace); - } - else if(settings->format == FORMAT_JPEG) { - format_params_jpeg params = settings->params; - g_key_file_set_double(file, group_name, "quality", params->quality); - g_key_file_set_double(file, group_name, "smoothing", params->smoothing); - g_key_file_set_boolean(file, group_name, "entropy", params->entropy); - g_key_file_set_boolean(file, group_name, "progressive", params->progressive); - if (params->comment != NULL) g_key_file_set_string(file, group_name, "comment", params->comment); - g_key_file_set_integer(file, group_name, "subsampling", params->subsampling); - g_key_file_set_boolean(file, group_name, "baseline", params->baseline); - g_key_file_set_integer(file, group_name, "markers", params->markers); - g_key_file_set_integer(file, group_name, "dct", params->dct); - } - else if(settings->format == FORMAT_PNG) { - format_params_png params = settings->params; - g_key_file_set_boolean(file, group_name, "interlace", params->interlace); - g_key_file_set_integer(file, group_name, "compression", params->compression); - g_key_file_set_boolean(file, group_name, "savebgc", params->savebgc); - g_key_file_set_boolean(file, group_name, "savegamma", params->savegamma); - g_key_file_set_boolean(file, group_name, "saveoff", params->saveoff); - g_key_file_set_boolean(file, group_name, "savephys", params->savephys); - g_key_file_set_boolean(file, group_name, "savetime", params->savetime); - g_key_file_set_boolean(file, group_name, "savecomm", params->savecomm); - g_key_file_set_boolean(file, group_name, "savetrans", params->savetrans); - } - else if(settings->format == FORMAT_TGA) { - format_params_tga params = settings->params; - g_key_file_set_boolean(file, group_name, "rle", params->rle); - g_key_file_set_integer(file, group_name, "origin", params->origin); - } - else if(settings->format == FORMAT_TIFF) { - format_params_tiff params = settings->params; - g_key_file_set_integer(file, group_name, "compression", params->compression); - } - else if(settings->format == FORMAT_HEIF) { - format_params_heif params = settings->params; - g_key_file_set_boolean(file, group_name, "lossless", params->lossless); - g_key_file_set_integer(file, group_name, "quality", params->quality); - } - else if(settings->format == FORMAT_WEBP) { - format_params_webp params = settings->params; - g_key_file_set_integer(file, group_name, "preset", params->preset); - g_key_file_set_boolean(file, group_name, "lossless", params->lossless); - g_key_file_set_double(file, group_name, "quality", params->quality); - g_key_file_set_double(file, group_name, "alpha_quality", params->alpha_quality); - g_key_file_set_boolean(file, group_name, "animation", params->animation); - g_key_file_set_boolean(file, group_name, "anim_loop", params->anim_loop); - g_key_file_set_boolean(file, group_name, "minimize_size", params->minimize_size); - g_key_file_set_integer(file, group_name, "kf_distance", params->kf_distance); - g_key_file_set_boolean(file, group_name, "exif", params->exif); - g_key_file_set_boolean(file, group_name, "iptc", params->iptc); - g_key_file_set_boolean(file, group_name, "xmp", params->xmp); - g_key_file_set_integer(file, group_name, "delay", params->delay); - g_key_file_set_integer(file, group_name, "force_delay", params->force_delay); - } -} - -static manipulation read_changeformat(GKeyFile* file) -{ - gchar* group_name = "CHANGEFORMAT"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_changeformat_new(); - changeformat_settings settings = ((changeformat_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "format", NULL)) { - settings->format = g_key_file_get_integer(file, group_name, "format", NULL); - - if (settings->format == FORMAT_GIF) { - settings->params = (format_params_gif) g_malloc(sizeof(struct changeformat_params_gif)); - format_params_gif params = settings->params; - - if (g_key_file_has_key(file, group_name, "interlace", NULL)) - params->interlace = g_key_file_get_boolean(file, group_name, "interlace", NULL); - } - else if (settings->format == FORMAT_JPEG) { - settings->params = (format_params_jpeg) g_malloc(sizeof(struct changeformat_params_jpeg)); - format_params_jpeg params = settings->params; - - if (g_key_file_has_key(file, group_name, "quality", NULL)) - params->quality = g_key_file_get_double(file, group_name, "quality", NULL); - - if (g_key_file_has_key(file, group_name, "smoothing", NULL)) - params->smoothing = g_key_file_get_double(file, group_name, "smoothing", NULL); - - if (g_key_file_has_key(file, group_name, "entropy", NULL)) - params->entropy = g_key_file_get_boolean(file, group_name, "entropy", NULL); - - if (g_key_file_has_key(file, group_name, "progressive", NULL)) - params->progressive = g_key_file_get_boolean(file, group_name, "progressive", NULL); - - if (g_key_file_has_key(file, group_name, "comment", NULL)) - params->comment = g_key_file_get_string(file, group_name, "comment", NULL); - - if (g_key_file_has_key(file, group_name, "subsampling", NULL)) - params->subsampling = g_key_file_get_integer(file, group_name, "subsampling", NULL); - - if (g_key_file_has_key(file, group_name, "baseline", NULL)) - params->baseline = g_key_file_get_boolean(file, group_name, "baseline", NULL); - - if (g_key_file_has_key(file, group_name, "markers", NULL)) - params->markers = g_key_file_get_integer(file, group_name, "markers", NULL); - - if (g_key_file_has_key(file, group_name, "dct", NULL)) - params->dct = g_key_file_get_integer(file, group_name, "dct", NULL); - } - else if (settings->format == FORMAT_PNG) { - settings->params = (format_params_png) g_malloc(sizeof(struct changeformat_params_png)); - format_params_png params = settings->params; - - if (g_key_file_has_key(file, group_name, "interlace", NULL)) - params->interlace = g_key_file_get_boolean(file, group_name, "interlace", NULL); - - if (g_key_file_has_key(file, group_name, "compression", NULL)) - params->compression = g_key_file_get_integer(file, group_name, "compression", NULL); - - if (g_key_file_has_key(file, group_name, "savebgc", NULL)) - params->savebgc = g_key_file_get_boolean(file, group_name, "savebgc", NULL); - - if (g_key_file_has_key(file, group_name, "savegamma", NULL)) - params->savegamma = g_key_file_get_boolean(file, group_name, "savegamma", NULL); - - if (g_key_file_has_key(file, group_name, "saveoff", NULL)) - params->saveoff = g_key_file_get_boolean(file, group_name, "saveoff", NULL); - - if (g_key_file_has_key(file, group_name, "savephys", NULL)) - params->savephys = g_key_file_get_boolean(file, group_name, "savephys", NULL); - - if (g_key_file_has_key(file, group_name, "savetime", NULL)) - params->savetime = g_key_file_get_boolean(file, group_name, "savetime", NULL); - - if (g_key_file_has_key(file, group_name, "savecomm", NULL)) - params->savecomm = g_key_file_get_boolean(file, group_name, "savecomm", NULL); - - if (g_key_file_has_key(file, group_name, "savetrans", NULL)) - params->savetrans = g_key_file_get_boolean(file, group_name, "savetrans", NULL); - } - else if (settings->format == FORMAT_TGA) { - settings->params = (format_params_tga) g_malloc(sizeof(struct changeformat_params_tga)); - format_params_tga params = settings->params; - - if (g_key_file_has_key(file, group_name, "rle", NULL)) - params->rle = g_key_file_get_boolean(file, group_name, "rle", NULL); - - if (g_key_file_has_key(file, group_name, "origin", NULL)) - params->origin = g_key_file_get_integer(file, group_name, "origin", NULL); - } - else if (settings->format == FORMAT_TIFF) { - settings->params = (format_params_tiff) g_malloc(sizeof(struct changeformat_params_tiff)); - format_params_tiff params = settings->params; - - if (g_key_file_has_key(file, group_name, "compression", NULL)) - params->compression = g_key_file_get_integer(file, group_name, "compression", NULL); - } - else if (settings->format == FORMAT_HEIF) { - settings->params = (format_params_heif) g_malloc(sizeof(struct changeformat_params_heif)); - format_params_heif params = settings->params; - - if (g_key_file_has_key(file, group_name, "lossless", NULL)) - params->lossless = g_key_file_get_boolean(file, group_name, "lossless", NULL); - - if (g_key_file_has_key(file, group_name, "quality", NULL)) - params->quality = g_key_file_get_integer(file, group_name, "quality", NULL); - } - else if (settings->format == FORMAT_WEBP) { - settings->params = (format_params_webp) g_malloc(sizeof(struct changeformat_params_webp)); - format_params_webp params = settings->params; - - if (g_key_file_has_key(file, group_name, "preset", NULL)) - params->preset = g_key_file_get_integer(file, group_name, "preset", NULL); - - if (g_key_file_has_key(file, group_name, "lossless", NULL)) - params->lossless = g_key_file_get_boolean(file, group_name, "lossless", NULL); - - if (g_key_file_has_key(file, group_name, "quality", NULL)) - params->quality = g_key_file_get_double(file, group_name, "quality", NULL); - - if (g_key_file_has_key(file, group_name, "alpha_quality", NULL)) - params->alpha_quality = g_key_file_get_double(file, group_name, "alpha_quality", NULL); - - if (g_key_file_has_key(file, group_name, "animation", NULL)) - params->animation = g_key_file_get_boolean(file, group_name, "animation", NULL); - - if (g_key_file_has_key(file, group_name, "anim_loop", NULL)) - params->anim_loop = g_key_file_get_boolean(file, group_name, "anim_loop", NULL); - - if (g_key_file_has_key(file, group_name, "minimize_size", NULL)) - params->minimize_size = g_key_file_get_boolean(file, group_name, "minimize_size", NULL); - - if (g_key_file_has_key(file, group_name, "kf_distance", NULL)) - params->kf_distance = g_key_file_get_integer(file, group_name, "kf_distance", NULL); - - if (g_key_file_has_key(file, group_name, "exif", NULL)) - params->exif = g_key_file_get_boolean(file, group_name, "exif", NULL); - - if (g_key_file_has_key(file, group_name, "iptc", NULL)) - params->iptc = g_key_file_get_boolean(file, group_name, "iptc", NULL); - - if (g_key_file_has_key(file, group_name, "xmp", NULL)) - params->xmp = g_key_file_get_boolean(file, group_name, "xmp", NULL); - - if (g_key_file_has_key(file, group_name, "delay", NULL)) - params->delay = g_key_file_get_boolean(file, group_name, "delay", NULL); - - if (g_key_file_has_key(file, group_name, "force_delay", NULL)) - params->force_delay = g_key_file_get_integer(file, group_name, "force_delay", NULL); - } - } - } - - return man; -} - -static void write_rename(rename_settings settings, GKeyFile* file) -{ - gchar* group_name = "RENAME"; - - if (settings->pattern != NULL) g_key_file_set_string(file, group_name, "pattern", settings->pattern); -} - -static manipulation read_rename(GKeyFile* file) -{ - gchar* group_name = "RENAME"; - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_rename_new(); - rename_settings settings = ((rename_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "pattern", NULL)) - settings->pattern = g_key_file_get_string(file, group_name, "pattern", NULL); - } - - return man; -} - -static void write_userdef(userdef_settings settings, GKeyFile* file, int id) -{ - gchar* group_name = g_strdup_printf("USERDEF%d", id); - - g_key_file_set_string(file, group_name, "procedure", settings->procedure); - g_key_file_set_integer(file, group_name, "num_params", settings->num_params); - - if (settings->num_params > 0) { - int param_i; - GdkColor tempcolor; - for (param_i = 0; param_i < settings->num_params; param_i++) { - - gchar* param_i_str = g_strdup_printf("PARAM%d", param_i); - switch(settings->params[param_i].type) { - case GIMP_PDB_INT32: - g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int32); - break; - case GIMP_PDB_INT16: - g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int16); - break; - case GIMP_PDB_INT8: - g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int8); - break; - case GIMP_PDB_FLOAT: - g_key_file_set_double(file, group_name, param_i_str, settings->params[param_i].data.d_float); - break; - case GIMP_PDB_STRING: - g_key_file_set_string(file, group_name, param_i_str, settings->params[param_i].data.d_string); - break; - case GIMP_PDB_COLOR: - tempcolor.red = (guint16)(((settings->params[param_i]).data.d_color.r)*65535); - tempcolor.green = (guint16)(((settings->params[param_i]).data.d_color.g)*65535); - tempcolor.blue = (guint16)(((settings->params[param_i]).data.d_color.b)*65535); - - g_key_file_set_string(file, group_name, param_i_str, gdk_color_to_string(&(tempcolor))); - break; - - default: - g_key_file_set_string(file, group_name, param_i_str, "NOT_USED"); - break; - } - } - } -} - -static manipulation read_userdef(GKeyFile* file, int id) -{ - gchar* group_name = g_strdup_printf("USERDEF%d", id); - manipulation man = NULL; - - if (g_key_file_has_group(file, group_name)) { - man = manipulation_userdef_new(); - userdef_settings settings = ((userdef_settings)man->settings); - - if (g_key_file_has_key(file, group_name, "procedure", NULL) && g_key_file_has_key(file, group_name, "num_params", NULL)) { - settings->procedure = g_key_file_get_string(file, group_name, "procedure", NULL); - settings->num_params = g_key_file_get_integer(file, group_name, "num_params", NULL); - - settings->params = g_new(GimpParam, settings->num_params); - - int param_i; - GimpParamDef param_info; - GdkColor usercolor; - GimpRGB rgbdata; - for (param_i = 0; param_i < settings->num_params; param_i++) { - param_info = pdb_proc_get_param_info(settings->procedure, param_i); - - settings->params[param_i].type = param_info.type; - gchar* param_i_str = g_strdup_printf("PARAM%d", param_i); - switch(settings->params[param_i].type) { - case GIMP_PDB_INT32: - (settings->params[param_i]).data.d_int32 = (gint32)g_key_file_get_integer(file, group_name, param_i_str, NULL); - break; - - case GIMP_PDB_INT16: - (settings->params[param_i]).data.d_int16 = (gint16)g_key_file_get_integer(file, group_name, param_i_str, NULL); - break; - - case GIMP_PDB_INT8: - (settings->params[param_i]).data.d_int8 = (gint8)g_key_file_get_integer(file, group_name, param_i_str, NULL); - break; - - case GIMP_PDB_FLOAT: - (settings->params[param_i]).data.d_float = (gdouble)g_key_file_get_double(file, group_name, param_i_str, NULL); - break; - - case GIMP_PDB_STRING: - (settings->params[param_i]).data.d_string = g_key_file_get_string(file, group_name, param_i_str, NULL); - break; - - case GIMP_PDB_COLOR: - gdk_color_parse (g_key_file_get_string(file, group_name, param_i_str, NULL), &usercolor); - gimp_rgb_set(&rgbdata, (gdouble)usercolor.red/65535, (gdouble)usercolor.green/65535, (gdouble)usercolor.blue/65535); - (settings->params[param_i]).data.d_color = rgbdata; - break; - - default: break; - } - } - } - } - - return man; -} +/* + * Functions to serialize and load manipulation sets + * Serialization uses GKeyFile method + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bimp.h" +#include "bimp-manipulations.h" +#include "bimp-utils.h" +#include "bimp-serialize.h" + +static void append_manipulation_details(manipulation, GKeyFile*); +static GSList* parse_manipulations(GKeyFile*, int); +static void write_resize(resize_settings, GKeyFile*); +static manipulation read_resize(GKeyFile*); +static void write_crop(crop_settings, GKeyFile*); +static manipulation read_crop(GKeyFile*); +static void write_fliprotate(fliprotate_settings, GKeyFile*); +static manipulation read_fliprotate(GKeyFile*); +static void write_color(color_settings, GKeyFile*); +static manipulation read_color(GKeyFile*, int); +static void write_sharpblur(sharpblur_settings, GKeyFile*); +static manipulation read_sharpblur(GKeyFile*); +static void write_watermark(watermark_settings, GKeyFile*, int); +static manipulation read_watermark(GKeyFile*, int); +static void write_changeformat(changeformat_settings, GKeyFile*); +static manipulation read_changeformat(GKeyFile*); +static void write_rename(rename_settings, GKeyFile*); +static manipulation read_rename(GKeyFile*); +static void write_userdef(userdef_settings, GKeyFile*, int); +static manipulation read_userdef(GKeyFile*, int); + +static int watermark_count, userdef_count; +static int loaded_build; + +gboolean bimp_serialize_to_file(gchar* filename) +{ + gboolean result; + + GKeyFile *output_file = g_key_file_new(); + g_key_file_set_list_separator(output_file, ';'); + + g_key_file_set_comment (output_file, NULL, NULL, g_strdup_printf("BIMP %s\nMANIPULATION SET DEFINITION", PLUG_IN_VERSION), NULL); + + watermark_count = 0; + userdef_count = 0; + g_slist_foreach(bimp_selected_manipulations, (GFunc)append_manipulation_details, output_file); + + result = g_file_set_contents (filename, g_key_file_to_data(output_file, NULL, NULL), -1, NULL); + + g_key_file_free(output_file); + + return result; +} + +gboolean bimp_deserialize_from_file(gchar* filename) +{ + gboolean result; + + GKeyFile* input_file = g_key_file_new(); + g_key_file_set_list_separator(input_file, ';'); + + if ((result = g_key_file_load_from_file (input_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL))) { + + // read build code + int buildnumber = 0; + gchar* header = g_key_file_get_comment(input_file, NULL, NULL, NULL); + GRegex *regex = g_regex_new ("^BIMP\\s(\\d+)\\.(\\d+)", 0, 0, NULL); + + GMatchInfo *match_info; + g_regex_match (regex, header, 0, &match_info); + while (g_match_info_matches (match_info)) + { + gchar *major = g_match_info_fetch (match_info, 1); + int major_i = g_ascii_strtoll(major, NULL, 10); + gchar *minor = g_match_info_fetch (match_info, 2); + int minor_i = g_ascii_strtoll(major, NULL, 10); + buildnumber = (major_i * 1000) + minor_i; + if (buildnumber > 0) break; + + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + g_regex_unref (regex); + + GSList* new_list = parse_manipulations(input_file, buildnumber); + if (new_list != NULL) { + g_slist_free(bimp_selected_manipulations); + bimp_selected_manipulations = new_list; + + result = TRUE; + } + else { + result = FALSE; + } + } + + g_key_file_free(input_file); + + return result; +} + +static void append_manipulation_details(manipulation man, GKeyFile* output_file) +{ + if (man->type == MANIP_RESIZE) { + write_resize((resize_settings)man->settings, output_file); + } + else if (man->type == MANIP_CROP) { + write_crop((crop_settings)man->settings, output_file); + } + else if (man->type == MANIP_FLIPROTATE) { + write_fliprotate((fliprotate_settings)man->settings, output_file); + } + else if (man->type == MANIP_COLOR) { + write_color((color_settings)man->settings, output_file); + } + else if (man->type == MANIP_SHARPBLUR) { + write_sharpblur((sharpblur_settings)man->settings, output_file); + } + else if (man->type == MANIP_WATERMARK) { + write_watermark((watermark_settings)man->settings, output_file, watermark_count); + watermark_count++; + } + else if (man->type == MANIP_CHANGEFORMAT) { + write_changeformat((changeformat_settings)man->settings, output_file); + } + else if (man->type == MANIP_RENAME) { + write_rename((rename_settings)man->settings, output_file); + } + else if (man->type == MANIP_USERDEF) { + write_userdef((userdef_settings)man->settings, output_file, userdef_count); + userdef_count++; + } +} + +static GSList* parse_manipulations(GKeyFile* file, int version) +{ + GSList* manipulations = NULL; + manipulation newman = NULL; + gsize count = 0; + gchar** groups; + + groups = g_key_file_get_groups(file, &count); + int i = 0; + while (i < count) { + + if (strcmp(groups[i], "RESIZE") == 0) { + newman = read_resize(file); + } + else if (strcmp(groups[i], "CROP") == 0) { + newman = read_crop(file); + } + else if (strcmp(groups[i], "FLIPROTATE") == 0) { + newman = read_fliprotate(file); + } + else if (strcmp(groups[i], "COLOR") == 0) { + newman = read_color(file, version); + } + else if (strcmp(groups[i], "SHARPBLUR") == 0) { + newman = read_sharpblur(file); + } + else if (strncmp(groups[i], "WATERMARK", strlen("WATERMARK")) == 0) { + if (strcmp(groups[i], "WATERMARK") == 0) { + newman = read_watermark(file, -1); + } + else { + int watermark_id; + if (sscanf(groups[i], "WATERMARK%d", &watermark_id) == 1) { + newman = read_watermark(file, watermark_id); + } + } + } + else if (strcmp(groups[i], "CHANGEFORMAT") == 0) { + newman = read_changeformat(file); + } + else if (strcmp(groups[i], "RENAME") == 0) { + newman = read_rename(file); + } + else if (strncmp(groups[i], "USERDEF", strlen("USERDEF")) == 0) { + int userdef_id; + if (sscanf(groups[i], "USERDEF%d", &userdef_id) == 1) { + newman = read_userdef(file, userdef_id); + } + } + + if (newman != NULL) manipulations = g_slist_append(manipulations, newman); + + i++; + } + + return manipulations; +} + +static void write_resize(resize_settings settings, GKeyFile* file) +{ + gchar* group_name = "RESIZE"; + + g_key_file_set_double(file, group_name, "new_w_pc", settings->new_w_pc); + g_key_file_set_double(file, group_name, "new_h_pc", settings->new_h_pc); + g_key_file_set_integer(file, group_name, "new_w_px", settings->new_w_px); + g_key_file_set_integer(file, group_name, "new_h_px", settings->new_h_px); + g_key_file_set_integer(file, group_name, "resize_mode_width", settings->resize_mode_width); + g_key_file_set_integer(file, group_name, "resize_mode_height", settings->resize_mode_height); + g_key_file_set_integer(file, group_name, "stretch_mode", settings->stretch_mode); + g_key_file_set_string(file, group_name, "padding_color", gdk_color_to_string(&(settings->padding_color))); + g_key_file_set_integer(file, group_name, "padding_color_alpha", settings->padding_color_alpha); + g_key_file_set_integer(file, group_name, "interpolation", settings->interpolation); + g_key_file_set_boolean(file, group_name, "change_res", settings->change_res); + g_key_file_set_integer(file, group_name, "new_res_x", settings->new_res_x); + g_key_file_set_integer(file, group_name, "new_res_y", settings->new_res_y); +} + +/* deserializes a string and returns a resize manipulation */ +static manipulation read_resize(GKeyFile* file) +{ + gchar* group_name = "RESIZE"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_resize_new(); + resize_settings settings = ((resize_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "new_w_pc", NULL)) + settings->new_w_pc = g_key_file_get_double(file, group_name, "new_w_pc", NULL); + + if (g_key_file_has_key(file, group_name, "new_h_pc", NULL)) + settings->new_h_pc = g_key_file_get_double(file, group_name, "new_h_pc", NULL); + + if (g_key_file_has_key(file, group_name, "new_w_px", NULL)) + settings->new_w_px = g_key_file_get_integer(file, group_name, "new_w_px", NULL); + + if (g_key_file_has_key(file, group_name, "new_h_px", NULL)) + settings->new_h_px = g_key_file_get_integer(file, group_name, "new_h_px", NULL); + + // for backwards compatibility + if (g_key_file_has_key(file, group_name, "resize_mode", NULL)) { + switch(g_key_file_get_integer(file, group_name, "resize_mode", NULL)) { + case 0: // RESIZE_PERCENT + settings->resize_mode_width = settings->resize_mode_height = RESIZE_PERCENT; + break; + case 1: // RESIZE_PIZEL_BOTH + settings->resize_mode_width = settings->resize_mode_height = RESIZE_PIXEL; + break; + case 2: // RESIZE_PIXEL_WIDTH + settings->resize_mode_width = RESIZE_PIXEL; + settings->resize_mode_height = RESIZE_DISABLE; + break; + case 3: // RESIZE_PIXEL_HEIGHT + settings->resize_mode_width = RESIZE_DISABLE; + settings->resize_mode_height = RESIZE_PIXEL; + break; + case 4: // RESIZE_END + default: + settings->resize_mode_width = settings->resize_mode_height = RESIZE_END; + } + } + + if (g_key_file_has_key(file, group_name, "resize_mode_width", NULL)) + settings->resize_mode_width = g_key_file_get_integer(file, group_name, "resize_mode_width", NULL); + + if (g_key_file_has_key(file, group_name, "resize_mode_height", NULL)) + settings->resize_mode_height = g_key_file_get_integer(file, group_name, "resize_mode_height", NULL); + + if (g_key_file_has_key(file, group_name, "stretch_mode", NULL)) + settings->stretch_mode = g_key_file_get_integer(file, group_name, "stretch_mode", NULL); + + if (g_key_file_has_key(file, group_name, "padding_color", NULL)) + gdk_color_parse(g_key_file_get_string(file, group_name, "padding_color", NULL), &(settings->padding_color)); + + if (g_key_file_has_key(file, group_name, "padding_color_alpha", NULL)) + settings->padding_color_alpha = (guint16)g_key_file_get_integer(file, group_name, "padding_color_alpha", NULL); + + if (g_key_file_has_key(file, group_name, "interpolation", NULL)) + settings->interpolation = g_key_file_get_integer(file, group_name, "interpolation", NULL); + + if (g_key_file_has_key(file, group_name, "change_res", NULL)) + settings->change_res = g_key_file_get_boolean(file, group_name, "change_res", NULL); + + if (g_key_file_has_key(file, group_name, "new_res_x", NULL)) + settings->new_res_x = g_key_file_get_integer(file, group_name, "new_res_x", NULL); + + if (g_key_file_has_key(file, group_name, "new_res_y", NULL)) + settings->new_res_y = g_key_file_get_integer(file, group_name, "new_res_y", NULL); + } + + return man; +} + +static void write_crop(crop_settings settings, GKeyFile* file) +{ + gchar* group_name = "CROP"; + + g_key_file_set_integer(file, group_name, "new_w", settings->new_w); + g_key_file_set_integer(file, group_name, "new_h", settings->new_h); + g_key_file_set_boolean(file, group_name, "manual", settings->manual); + g_key_file_set_integer(file, group_name, "ratio", settings->ratio); + g_key_file_set_double(file, group_name, "custom_ratio1", settings->custom_ratio1); + g_key_file_set_double(file, group_name, "custom_ratio2", settings->custom_ratio2); + g_key_file_set_double(file, group_name, "start_pos", settings->start_pos); +} + +static manipulation read_crop(GKeyFile* file) +{ + gchar* group_name = "CROP"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_crop_new(); + crop_settings settings = ((crop_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "new_w", NULL)) + settings->new_w = g_key_file_get_integer(file, group_name, "new_w", NULL); + + if (g_key_file_has_key(file, group_name, "new_h", NULL)) + settings->new_h = g_key_file_get_integer(file, group_name, "new_h", NULL); + + if (g_key_file_has_key(file, group_name, "manual", NULL)) + settings->manual = g_key_file_get_boolean(file, group_name, "manual", NULL); + + if (g_key_file_has_key(file, group_name, "ratio", NULL)) + settings->ratio = g_key_file_get_integer(file, group_name, "ratio", NULL); + + if (g_key_file_has_key(file, group_name, "custom_ratio1", NULL)) + settings->custom_ratio1 = g_key_file_get_integer(file, group_name, "custom_ratio1", NULL); + + if (g_key_file_has_key(file, group_name, "custom_ratio2", NULL)) + settings->custom_ratio2 = g_key_file_get_integer(file, group_name, "custom_ratio2", NULL); + + if (g_key_file_has_key(file, group_name, "start_pos", NULL)) + settings->start_pos = g_key_file_get_integer(file, group_name, "start_pos", NULL); + } + + return man; +} + +static void write_fliprotate(fliprotate_settings settings, GKeyFile* file) +{ + gchar* group_name = "FLIPROTATE"; + + g_key_file_set_boolean(file, group_name, "flip_h", settings->flip_h); + g_key_file_set_boolean(file, group_name, "flip_v", settings->flip_v); + g_key_file_set_boolean(file, group_name, "rotate", settings->rotate); + g_key_file_set_integer(file, group_name, "rotation_type", settings->rotation_type); +} + +static manipulation read_fliprotate(GKeyFile* file) +{ + gchar* group_name = "FLIPROTATE"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_fliprotate_new(); + fliprotate_settings settings = ((fliprotate_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "flip_h", NULL)) + settings->flip_h = g_key_file_get_boolean(file, group_name, "flip_h", NULL); + + if (g_key_file_has_key(file, group_name, "flip_v", NULL)) + settings->flip_v = g_key_file_get_boolean(file, group_name, "flip_v", NULL); + + if (g_key_file_has_key(file, group_name, "rotate", NULL)) + settings->rotate = g_key_file_get_boolean(file, group_name, "rotate", NULL); + + if (g_key_file_has_key(file, group_name, "rotation_type", NULL)) + settings->rotation_type = g_key_file_get_integer(file, group_name, "rotation_type", NULL); + } + + return man; +} + +static void write_color(color_settings settings, GKeyFile* file) +{ + gchar* group_name = "COLOR"; + + g_key_file_set_double(file, group_name, "brightness", settings->brightness); + g_key_file_set_double(file, group_name, "contrast", settings->contrast); + g_key_file_set_boolean(file, group_name, "levels_auto", settings->levels_auto); + g_key_file_set_boolean(file, group_name, "grayscale", settings->grayscale); + if (settings->curve_file != NULL) g_key_file_set_string(file, group_name, "curve_file", settings->curve_file); +} + +static manipulation read_color(GKeyFile* file, int version) +{ + gchar* group_name = "COLOR"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_color_new(); + color_settings settings = ((color_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "brightness", NULL)) + { + double b = g_key_file_get_double(file, group_name, "brightness", NULL); + if (version < 2000) + { + // prior to BIMP 2.0, brightness range was [-127;127] + // now map to [-0.5;0.5] + settings->brightness = -0.5 + ((0.5 - (-0.5)) / (127 - (-127)) * (b - (-127))); + } + else + { + settings->brightness = b; + } + } + + if (g_key_file_has_key(file, group_name, "contrast", NULL)) + { + double c = g_key_file_get_double(file, group_name, "contrast", NULL); + if (version < 2000) + { + // prior to BIMP 2.0, contrast range was [-127;127] + // now map to [-0.5;0.5] + settings->contrast = -0.5 + ((0.5 - (-0.5)) / (127 - (-127)) * (c - (-127))); + } + else + { + settings->contrast = c; + } + } + + if (g_key_file_has_key(file, group_name, "levels_auto", NULL)) + settings->levels_auto = g_key_file_get_boolean(file, group_name, "levels_auto", NULL); + + if (g_key_file_has_key(file, group_name, "grayscale", NULL)) + settings->grayscale = g_key_file_get_boolean(file, group_name, "grayscale", NULL); + + if (g_key_file_has_key(file, group_name, "curve_file", NULL)) + settings->curve_file = g_key_file_get_string(file, group_name, "curve_file", NULL); + } + + return man; +} + +/* Reads the content of a GIMP's curve file (for Color manipulations). + * The result is saved to be used with the gimp_curves_spline() function */ +gboolean parse_curve_file( + char* file, + int* num_points_v, + gdouble** ctr_points_v, + int* num_points_r, + gdouble** ctr_points_r, + int* num_points_g, + gdouble** ctr_points_g, + int* num_points_b, + gdouble** ctr_points_b, + int* num_points_a, + gdouble** ctr_points_a +) { + FILE* pFile; + pFile = fopen (file, "r"); + + char* old_locale = setlocale(LC_NUMERIC, ""); + setlocale(LC_NUMERIC, "C"); + + char line[2400]; + char channel_name[6]; + int num_points_temp = 0; + gdouble* ctr_points_temp = NULL; + + if (pFile == NULL) goto err; + else { + // read header ("# GIMP curves tool settings") + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + if (!g_str_has_prefix(line, "# GIMP")) goto err; + + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + while(!g_str_has_prefix(line, "(channel ")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + + // reached the first "(channel " line + while (sscanf (line, "(channel %[a-z])", channel_name) == 1) { + + g_free(ctr_points_temp); + ctr_points_temp = NULL; + + // "(curve", ignored + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + if (g_str_has_prefix(line, "(curve")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + // " (curve-type", ignored + if (g_str_has_prefix(line, " (curve-type")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + + // n-points, ignored (also it is missing since GIMP 2.10) + // the actual number of points is parsed from "(points N..." + if (g_str_has_prefix(line, " (n-points")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + + // number of points and list + double pX, pY; + int p_count = 0; + char* token = strtok(line + strlen(g_strdup_printf(" (points ")), " "); + int num_points = atoi(token); // first token holds the number of points + + token = strtok(NULL, " "); + while (token) { + pX = atof(token); + token = strtok(NULL, " "); + if (!token) goto err; + pY = atof(token); + + if (pX >= 0 && pX <= 1 && + pY >= 0 && pY <= 1) + { + // save X and Y + ctr_points_temp = (gdouble*)g_realloc(ctr_points_temp, sizeof(gdouble) * (p_count + 2)); // add one element to the array + ctr_points_temp[p_count] = pX; + ctr_points_temp[p_count + 1] = pY; + + p_count += 2; + } + + token = strtok(NULL, " "); + } + + num_points_temp = p_count; + + // "(n-samples XX)", ignored + if (g_str_has_prefix(line, " (n-samples")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + // "(samples XX ...", ignored + if (g_str_has_prefix(line, " (samples")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + } + + // save in the proper variables + if (strcmp(channel_name, "value") == 0) { + *num_points_v = num_points_temp; + *ctr_points_v = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); + } + else if (strcmp(channel_name, "red") == 0) { + *num_points_r = num_points_temp; + *ctr_points_r = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); + } + else if (strcmp(channel_name, "green") == 0) { + *num_points_g = num_points_temp; + *ctr_points_g = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); + } + else if (strcmp(channel_name, "blue") == 0) { + *num_points_b = num_points_temp; + *ctr_points_b = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); + } + else if (strcmp(channel_name, "alpha") == 0) { + *num_points_a = num_points_temp; + *ctr_points_a = g_memdup(ctr_points_temp, num_points_temp * sizeof(gdouble)); + } + else goto err; + + // reach the next channel, or the end if this was the last + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + while(!g_str_has_prefix(line, "(channel ")) { + if (fgets(line, sizeof(line), pFile) == NULL) goto err; + if (g_str_has_prefix(line, "# end")) goto finish; + } + } + + // "# end of curves tool settings" +finish: + setlocale(LC_NUMERIC, old_locale); + g_free(ctr_points_temp); + fclose (pFile); + return TRUE; + } + +err: + setlocale(LC_NUMERIC, old_locale); + if (pFile != NULL) fclose (pFile); + if (ctr_points_temp != NULL) g_free(ctr_points_temp); + return FALSE; +} + +static void write_sharpblur(sharpblur_settings settings, GKeyFile* file) +{ + gchar* group_name = "SHARPBLUR"; + + g_key_file_set_integer(file, group_name, "amount", settings->amount); +} + +static manipulation read_sharpblur(GKeyFile* file) +{ + gchar* group_name = "SHARPBLUR"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_sharpblur_new(); + sharpblur_settings settings = ((sharpblur_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "amount", NULL)) + settings->amount = g_key_file_get_integer(file, group_name, "amount", NULL); + } + + return man; +} + +static void write_watermark(watermark_settings settings, GKeyFile* file, int id) +{ + gchar* group_name = g_strdup_printf("WATERMARK%d", id); + + g_key_file_set_boolean(file, group_name, "mode", settings->mode); + g_key_file_set_string(file, group_name, "text", settings->text); + g_key_file_set_string(file, group_name, "font", pango_font_description_to_string(settings->font)); + g_key_file_set_string(file, group_name, "color", gdk_color_to_string(&(settings->color))); + if (settings->image_file != NULL) g_key_file_set_string(file, group_name, "image_file", settings->image_file); + g_key_file_set_integer(file, group_name, "image_sizemode", settings->image_sizemode); + g_key_file_set_double(file, group_name, "image_size_percent", settings->image_size_percent); + g_key_file_set_integer(file, group_name, "opacity", settings->opacity); + g_key_file_set_integer(file, group_name, "edge_distance", settings->edge_distance); + g_key_file_set_integer(file, group_name, "position", settings->position); +} + +static manipulation read_watermark(GKeyFile* file, int id) +{ + gchar* group_name; + if (id == -1) group_name = "WATERMARK"; + else group_name = g_strdup_printf("WATERMARK%d", id); + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_watermark_new(); + watermark_settings settings = ((watermark_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "mode", NULL)) + settings->mode = g_key_file_get_boolean(file, group_name, "mode", NULL); + + if (g_key_file_has_key(file, group_name, "text", NULL)) + settings->text = g_key_file_get_string(file, group_name, "text", NULL); + + if (g_key_file_has_key(file, group_name, "font", NULL)) + settings->font = pango_font_description_from_string(g_key_file_get_string(file, group_name, "font", NULL)); + + if (g_key_file_has_key(file, group_name, "color", NULL)) + gdk_color_parse(g_key_file_get_string(file, group_name, "color", NULL), &(settings->color)); + + if (g_key_file_has_key(file, group_name, "image_file", NULL)) + settings->image_file = g_key_file_get_string(file, group_name, "image_file", NULL); + + if (g_key_file_has_key(file, group_name, "image_sizemode", NULL)) + settings->image_sizemode = g_key_file_get_integer(file, group_name, "image_sizemode", NULL); + + if (g_key_file_has_key(file, group_name, "image_size_percent", NULL)) + settings->image_size_percent = g_key_file_get_double(file, group_name, "image_size_percent", NULL); + + if (g_key_file_has_key(file, group_name, "opacity", NULL)) + settings->opacity = g_key_file_get_integer(file, group_name, "opacity", NULL); + + if (g_key_file_has_key(file, group_name, "edge_distance", NULL)) + settings->edge_distance = g_key_file_get_integer(file, group_name, "edge_distance", NULL); + + if (g_key_file_has_key(file, group_name, "position", NULL)) + settings->position = g_key_file_get_integer(file, group_name, "position", NULL); + } + + return man; +} + +static void write_changeformat(changeformat_settings settings, GKeyFile* file) +{ + gchar* group_name = "CHANGEFORMAT"; + + g_key_file_set_integer(file, group_name, "format", settings->format); + + if(settings->format == FORMAT_GIF) { + format_params_gif params = settings->params; + g_key_file_set_boolean(file, group_name, "interlace", params->interlace); + } + else if(settings->format == FORMAT_JPEG) { + format_params_jpeg params = settings->params; + g_key_file_set_double(file, group_name, "quality", params->quality); + g_key_file_set_double(file, group_name, "smoothing", params->smoothing); + g_key_file_set_boolean(file, group_name, "entropy", params->entropy); + g_key_file_set_boolean(file, group_name, "progressive", params->progressive); + if (params->comment != NULL) g_key_file_set_string(file, group_name, "comment", params->comment); + g_key_file_set_integer(file, group_name, "subsampling", params->subsampling); + g_key_file_set_boolean(file, group_name, "baseline", params->baseline); + g_key_file_set_integer(file, group_name, "markers", params->markers); + g_key_file_set_integer(file, group_name, "dct", params->dct); + } + else if(settings->format == FORMAT_PNG) { + format_params_png params = settings->params; + g_key_file_set_boolean(file, group_name, "interlace", params->interlace); + g_key_file_set_integer(file, group_name, "compression", params->compression); + g_key_file_set_boolean(file, group_name, "savebgc", params->savebgc); + g_key_file_set_boolean(file, group_name, "savegamma", params->savegamma); + g_key_file_set_boolean(file, group_name, "saveoff", params->saveoff); + g_key_file_set_boolean(file, group_name, "savephys", params->savephys); + g_key_file_set_boolean(file, group_name, "savetime", params->savetime); + g_key_file_set_boolean(file, group_name, "savecomm", params->savecomm); + g_key_file_set_boolean(file, group_name, "savetrans", params->savetrans); + } + else if(settings->format == FORMAT_TGA) { + format_params_tga params = settings->params; + g_key_file_set_boolean(file, group_name, "rle", params->rle); + g_key_file_set_integer(file, group_name, "origin", params->origin); + } + else if(settings->format == FORMAT_TIFF) { + format_params_tiff params = settings->params; + g_key_file_set_integer(file, group_name, "compression", params->compression); + } + else if(settings->format == FORMAT_HEIF) { + format_params_heif params = settings->params; + g_key_file_set_boolean(file, group_name, "lossless", params->lossless); + g_key_file_set_integer(file, group_name, "quality", params->quality); + } + else if(settings->format == FORMAT_WEBP) { + format_params_webp params = settings->params; + g_key_file_set_integer(file, group_name, "preset", params->preset); + g_key_file_set_boolean(file, group_name, "lossless", params->lossless); + g_key_file_set_double(file, group_name, "quality", params->quality); + g_key_file_set_double(file, group_name, "alpha_quality", params->alpha_quality); + g_key_file_set_boolean(file, group_name, "animation", params->animation); + g_key_file_set_boolean(file, group_name, "anim_loop", params->anim_loop); + g_key_file_set_boolean(file, group_name, "minimize_size", params->minimize_size); + g_key_file_set_integer(file, group_name, "kf_distance", params->kf_distance); + g_key_file_set_boolean(file, group_name, "exif", params->exif); + g_key_file_set_boolean(file, group_name, "iptc", params->iptc); + g_key_file_set_boolean(file, group_name, "xmp", params->xmp); + g_key_file_set_integer(file, group_name, "delay", params->delay); + g_key_file_set_integer(file, group_name, "force_delay", params->force_delay); + } + else if(settings->format == FORMAT_AVIF) { + format_params_avif params = settings->params; + g_key_file_set_boolean(file, group_name, "lossless", params->lossless); + g_key_file_set_integer(file, group_name, "quality", params->quality); + } +} + +static manipulation read_changeformat(GKeyFile* file) +{ + gchar* group_name = "CHANGEFORMAT"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_changeformat_new(); + changeformat_settings settings = ((changeformat_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "format", NULL)) { + settings->format = g_key_file_get_integer(file, group_name, "format", NULL); + + if (settings->format == FORMAT_GIF) { + settings->params = (format_params_gif) g_malloc(sizeof(struct changeformat_params_gif)); + format_params_gif params = settings->params; + + if (g_key_file_has_key(file, group_name, "interlace", NULL)) + params->interlace = g_key_file_get_boolean(file, group_name, "interlace", NULL); + } + else if (settings->format == FORMAT_JPEG) { + settings->params = (format_params_jpeg) g_malloc(sizeof(struct changeformat_params_jpeg)); + format_params_jpeg params = settings->params; + + if (g_key_file_has_key(file, group_name, "quality", NULL)) + params->quality = g_key_file_get_double(file, group_name, "quality", NULL); + + if (g_key_file_has_key(file, group_name, "smoothing", NULL)) + params->smoothing = g_key_file_get_double(file, group_name, "smoothing", NULL); + + if (g_key_file_has_key(file, group_name, "entropy", NULL)) + params->entropy = g_key_file_get_boolean(file, group_name, "entropy", NULL); + + if (g_key_file_has_key(file, group_name, "progressive", NULL)) + params->progressive = g_key_file_get_boolean(file, group_name, "progressive", NULL); + + if (g_key_file_has_key(file, group_name, "comment", NULL)) + params->comment = g_key_file_get_string(file, group_name, "comment", NULL); + + if (g_key_file_has_key(file, group_name, "subsampling", NULL)) + params->subsampling = g_key_file_get_integer(file, group_name, "subsampling", NULL); + + if (g_key_file_has_key(file, group_name, "baseline", NULL)) + params->baseline = g_key_file_get_boolean(file, group_name, "baseline", NULL); + + if (g_key_file_has_key(file, group_name, "markers", NULL)) + params->markers = g_key_file_get_integer(file, group_name, "markers", NULL); + + if (g_key_file_has_key(file, group_name, "dct", NULL)) + params->dct = g_key_file_get_integer(file, group_name, "dct", NULL); + } + else if (settings->format == FORMAT_PNG) { + settings->params = (format_params_png) g_malloc(sizeof(struct changeformat_params_png)); + format_params_png params = settings->params; + + if (g_key_file_has_key(file, group_name, "interlace", NULL)) + params->interlace = g_key_file_get_boolean(file, group_name, "interlace", NULL); + + if (g_key_file_has_key(file, group_name, "compression", NULL)) + params->compression = g_key_file_get_integer(file, group_name, "compression", NULL); + + if (g_key_file_has_key(file, group_name, "savebgc", NULL)) + params->savebgc = g_key_file_get_boolean(file, group_name, "savebgc", NULL); + + if (g_key_file_has_key(file, group_name, "savegamma", NULL)) + params->savegamma = g_key_file_get_boolean(file, group_name, "savegamma", NULL); + + if (g_key_file_has_key(file, group_name, "saveoff", NULL)) + params->saveoff = g_key_file_get_boolean(file, group_name, "saveoff", NULL); + + if (g_key_file_has_key(file, group_name, "savephys", NULL)) + params->savephys = g_key_file_get_boolean(file, group_name, "savephys", NULL); + + if (g_key_file_has_key(file, group_name, "savetime", NULL)) + params->savetime = g_key_file_get_boolean(file, group_name, "savetime", NULL); + + if (g_key_file_has_key(file, group_name, "savecomm", NULL)) + params->savecomm = g_key_file_get_boolean(file, group_name, "savecomm", NULL); + + if (g_key_file_has_key(file, group_name, "savetrans", NULL)) + params->savetrans = g_key_file_get_boolean(file, group_name, "savetrans", NULL); + } + else if (settings->format == FORMAT_TGA) { + settings->params = (format_params_tga) g_malloc(sizeof(struct changeformat_params_tga)); + format_params_tga params = settings->params; + + if (g_key_file_has_key(file, group_name, "rle", NULL)) + params->rle = g_key_file_get_boolean(file, group_name, "rle", NULL); + + if (g_key_file_has_key(file, group_name, "origin", NULL)) + params->origin = g_key_file_get_integer(file, group_name, "origin", NULL); + } + else if (settings->format == FORMAT_TIFF) { + settings->params = (format_params_tiff) g_malloc(sizeof(struct changeformat_params_tiff)); + format_params_tiff params = settings->params; + + if (g_key_file_has_key(file, group_name, "compression", NULL)) + params->compression = g_key_file_get_integer(file, group_name, "compression", NULL); + } + else if (settings->format == FORMAT_HEIF) { + settings->params = (format_params_heif) g_malloc(sizeof(struct changeformat_params_heif)); + format_params_heif params = settings->params; + + if (g_key_file_has_key(file, group_name, "lossless", NULL)) + params->lossless = g_key_file_get_boolean(file, group_name, "lossless", NULL); + + if (g_key_file_has_key(file, group_name, "quality", NULL)) + params->quality = g_key_file_get_integer(file, group_name, "quality", NULL); + } + else if (settings->format == FORMAT_WEBP) { + settings->params = (format_params_webp) g_malloc(sizeof(struct changeformat_params_webp)); + format_params_webp params = settings->params; + + if (g_key_file_has_key(file, group_name, "preset", NULL)) + params->preset = g_key_file_get_integer(file, group_name, "preset", NULL); + + if (g_key_file_has_key(file, group_name, "lossless", NULL)) + params->lossless = g_key_file_get_boolean(file, group_name, "lossless", NULL); + + if (g_key_file_has_key(file, group_name, "quality", NULL)) + params->quality = g_key_file_get_double(file, group_name, "quality", NULL); + + if (g_key_file_has_key(file, group_name, "alpha_quality", NULL)) + params->alpha_quality = g_key_file_get_double(file, group_name, "alpha_quality", NULL); + + if (g_key_file_has_key(file, group_name, "animation", NULL)) + params->animation = g_key_file_get_boolean(file, group_name, "animation", NULL); + + if (g_key_file_has_key(file, group_name, "anim_loop", NULL)) + params->anim_loop = g_key_file_get_boolean(file, group_name, "anim_loop", NULL); + + if (g_key_file_has_key(file, group_name, "minimize_size", NULL)) + params->minimize_size = g_key_file_get_boolean(file, group_name, "minimize_size", NULL); + + if (g_key_file_has_key(file, group_name, "kf_distance", NULL)) + params->kf_distance = g_key_file_get_integer(file, group_name, "kf_distance", NULL); + + if (g_key_file_has_key(file, group_name, "exif", NULL)) + params->exif = g_key_file_get_boolean(file, group_name, "exif", NULL); + + if (g_key_file_has_key(file, group_name, "iptc", NULL)) + params->iptc = g_key_file_get_boolean(file, group_name, "iptc", NULL); + + if (g_key_file_has_key(file, group_name, "xmp", NULL)) + params->xmp = g_key_file_get_boolean(file, group_name, "xmp", NULL); + + if (g_key_file_has_key(file, group_name, "delay", NULL)) + params->delay = g_key_file_get_boolean(file, group_name, "delay", NULL); + + if (g_key_file_has_key(file, group_name, "force_delay", NULL)) + params->force_delay = g_key_file_get_integer(file, group_name, "force_delay", NULL); + } + else if (settings->format == FORMAT_AVIF) { + settings->params = (format_params_avif) g_malloc(sizeof(struct changeformat_params_avif)); + format_params_avif params = settings->params; + + if (g_key_file_has_key(file, group_name, "lossless", NULL)) + params->lossless = g_key_file_get_boolean(file, group_name, "lossless", NULL); + + if (g_key_file_has_key(file, group_name, "quality", NULL)) + params->quality = g_key_file_get_integer(file, group_name, "quality", NULL); + } + } + } + + return man; +} + +static void write_rename(rename_settings settings, GKeyFile* file) +{ + gchar* group_name = "RENAME"; + + if (settings->pattern != NULL) g_key_file_set_string(file, group_name, "pattern", settings->pattern); +} + +static manipulation read_rename(GKeyFile* file) +{ + gchar* group_name = "RENAME"; + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_rename_new(); + rename_settings settings = ((rename_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "pattern", NULL)) + settings->pattern = g_key_file_get_string(file, group_name, "pattern", NULL); + } + + return man; +} + +static void write_userdef(userdef_settings settings, GKeyFile* file, int id) +{ + gchar* group_name = g_strdup_printf("USERDEF%d", id); + + g_key_file_set_string(file, group_name, "procedure", settings->procedure); + g_key_file_set_integer(file, group_name, "num_params", settings->num_params); + + if (settings->num_params > 0) { + int param_i; + GdkColor tempcolor; + for (param_i = 0; param_i < settings->num_params; param_i++) { + + gchar* param_i_str = g_strdup_printf("PARAM%d", param_i); + switch(settings->params[param_i].type) { + case GIMP_PDB_INT32: + g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int32); + break; + case GIMP_PDB_INT16: + g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int16); + break; + case GIMP_PDB_INT8: + g_key_file_set_integer(file, group_name, param_i_str, settings->params[param_i].data.d_int8); + break; + case GIMP_PDB_FLOAT: + g_key_file_set_double(file, group_name, param_i_str, settings->params[param_i].data.d_float); + break; + case GIMP_PDB_STRING: + g_key_file_set_string(file, group_name, param_i_str, settings->params[param_i].data.d_string); + break; + case GIMP_PDB_COLOR: + tempcolor.red = (guint16)(((settings->params[param_i]).data.d_color.r)*65535); + tempcolor.green = (guint16)(((settings->params[param_i]).data.d_color.g)*65535); + tempcolor.blue = (guint16)(((settings->params[param_i]).data.d_color.b)*65535); + + g_key_file_set_string(file, group_name, param_i_str, gdk_color_to_string(&(tempcolor))); + break; + + default: + g_key_file_set_string(file, group_name, param_i_str, "NOT_USED"); + break; + } + } + } +} + +static manipulation read_userdef(GKeyFile* file, int id) +{ + gchar* group_name = g_strdup_printf("USERDEF%d", id); + manipulation man = NULL; + + if (g_key_file_has_group(file, group_name)) { + man = manipulation_userdef_new(); + userdef_settings settings = ((userdef_settings)man->settings); + + if (g_key_file_has_key(file, group_name, "procedure", NULL) && g_key_file_has_key(file, group_name, "num_params", NULL)) { + settings->procedure = g_key_file_get_string(file, group_name, "procedure", NULL); + settings->num_params = g_key_file_get_integer(file, group_name, "num_params", NULL); + + settings->params = g_new(GimpParam, settings->num_params); + + int param_i; + GimpParamDef param_info; + GdkColor usercolor; + GimpRGB rgbdata; + for (param_i = 0; param_i < settings->num_params; param_i++) { + param_info = pdb_proc_get_param_info(settings->procedure, param_i); + + settings->params[param_i].type = param_info.type; + gchar* param_i_str = g_strdup_printf("PARAM%d", param_i); + switch(settings->params[param_i].type) { + case GIMP_PDB_INT32: + (settings->params[param_i]).data.d_int32 = (gint32)g_key_file_get_integer(file, group_name, param_i_str, NULL); + break; + + case GIMP_PDB_INT16: + (settings->params[param_i]).data.d_int16 = (gint16)g_key_file_get_integer(file, group_name, param_i_str, NULL); + break; + + case GIMP_PDB_INT8: + (settings->params[param_i]).data.d_int8 = (gint8)g_key_file_get_integer(file, group_name, param_i_str, NULL); + break; + + case GIMP_PDB_FLOAT: + (settings->params[param_i]).data.d_float = (gdouble)g_key_file_get_double(file, group_name, param_i_str, NULL); + break; + + case GIMP_PDB_STRING: + (settings->params[param_i]).data.d_string = g_key_file_get_string(file, group_name, param_i_str, NULL); + break; + + case GIMP_PDB_COLOR: + gdk_color_parse (g_key_file_get_string(file, group_name, param_i_str, NULL), &usercolor); + gimp_rgb_set(&rgbdata, (gdouble)usercolor.red/65535, (gdouble)usercolor.green/65535, (gdouble)usercolor.blue/65535); + (settings->params[param_i]).data.d_color = rgbdata; + break; + + default: break; + } + } + } + } + + return man; +} diff --git a/src/manipulation-gui/gui-changeformat.c b/src/manipulation-gui/gui-changeformat.c index 5539197..bb78300 100644 --- a/src/manipulation-gui/gui-changeformat.c +++ b/src/manipulation-gui/gui-changeformat.c @@ -1,501 +1,535 @@ -#include -#include -#include "gui-changeformat.h" -#include "../bimp-manipulations.h" -#include "../plugin-intl.h" - -static void update_frame_params(GtkComboBox*, changeformat_settings); -static void adv_expanded (GtkWidget*, GtkRequisition*, gpointer); -static void update_window_size(); - -static GtkWidget *frame_params, *inner_widget; -static GtkWidget *combo_format, *scale_quality, *scale_alpha_quality, *scale_smoothing, *check_interlace, *scale_compression, *check_baseline; -static GtkWidget *check_rle, *check_progressive, *check_entrophy, *combo_compression, *spin_markers, *combo_subsampling, *combo_dct, *combo_origin; -static GtkWidget *check_savebgc, *check_savegamma, *check_saveoff, *check_savephys, *check_savetime, *check_savecomm, *check_savetrans, *check_lossless; -static GtkWidget *combo_preset, *check_saveexif, *check_savexmp, *check_savecp; -static GtkWidget *expander_advanced; - -static GtkTextBuffer *buffer_comment; -static GtkTextIter start_comment, end_comment; - -static GtkWidget* parentwin; - -GtkWidget* bimp_changeformat_gui_new(changeformat_settings settings, GtkWidget* parent) -{ - GtkWidget *gui; - - parentwin = parent; - gui = gtk_vbox_new(FALSE, 5); - - combo_format = gtk_combo_box_new_text(); - for(int i = 0; i < FORMAT_END; i++) { - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_format), format_type_string[i][1]); - } - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_format), settings->format); - - frame_params = gtk_frame_new(_("Format settings")); - - gtk_box_pack_start(GTK_BOX(gui), combo_format, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(gui), frame_params, FALSE, FALSE, 0); - - update_frame_params(GTK_COMBO_BOX(combo_format), settings); - - g_signal_connect(G_OBJECT(combo_format), "changed", G_CALLBACK(update_frame_params), settings); - - return gui; -} - -static void update_frame_params(GtkComboBox *widget, changeformat_settings settings) -{ - update_window_size(); - format_type selected_format = (format_type)gtk_combo_box_get_active(widget); - - inner_widget = gtk_vbox_new(FALSE, 5); - gtk_container_set_border_width(GTK_CONTAINER(inner_widget), 8); - if (selected_format == FORMAT_GIF) { - check_interlace = gtk_check_button_new_with_label(_("Interlaced")); - - if (selected_format == settings->format) { - format_params_gif settings_gif = (format_params_gif)(settings->params); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), settings_gif->interlace); - } - else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), FALSE); - } - - gtk_box_pack_start(GTK_BOX(inner_widget), check_interlace, FALSE, FALSE, 0); - } - else if (selected_format == FORMAT_JPEG) { - GtkWidget *hbox_quality, *hbox_smoothing, *hbox_checks, *hbox_comment, *hbox_markers, *hbox_subsampling, *hbox_dct; - GtkWidget *vbox_advanced, *label_quality, *label_smoothing, *label_markers, *label_comment, *label_subsampling, *label_dct, *text_comment; - - hbox_quality = gtk_hbox_new(FALSE, 5); - label_quality = gtk_label_new(g_strconcat(_("Quality"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); - scale_quality = gtk_hscale_new_with_range(0, 100, 1); - - expander_advanced = gtk_expander_new(_("Advanced params")); - vbox_advanced = gtk_vbox_new(FALSE, 5); - - hbox_smoothing = gtk_hbox_new(FALSE, 5); - label_smoothing = gtk_label_new(g_strconcat(_("Smoothing"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_smoothing), 0.5, 0.8); - scale_smoothing = gtk_hscale_new_with_range(0, 1, 0.01); - - hbox_checks = gtk_hbox_new(FALSE, 5); - check_entrophy = gtk_check_button_new_with_label(_("Optimize")); - check_progressive = gtk_check_button_new_with_label(_("Progressive")); - check_baseline = gtk_check_button_new_with_label(_("Save baseline")); - - hbox_comment = gtk_hbox_new(FALSE, 5); - label_comment = gtk_label_new(g_strconcat(_("Comment"), ":", NULL)); - text_comment = gtk_text_view_new(); - buffer_comment = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_comment)); - - hbox_markers = gtk_hbox_new(FALSE, 5); - label_markers = gtk_label_new(g_strconcat(_("Markers rows"), ":", NULL)); - spin_markers = gtk_spin_button_new(NULL, 1, 0); - - hbox_subsampling = gtk_hbox_new(FALSE, 5); - label_subsampling = gtk_label_new(g_strconcat(_("Subsampling"), ":", NULL)); - combo_subsampling = gtk_combo_box_new_text(); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), g_strconcat("2x2, 1x1, 1x1 (", _("Small size"), ")", NULL)); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), "2x1, 1x1, 1x1 (4:2:2)"); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), g_strconcat("1x1, 1x1, 1x1 (", _("Quality"), ")", NULL)); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), "1x2, 1x1, 1x1"); - - - hbox_dct = gtk_hbox_new(FALSE, 5); - label_dct = gtk_label_new(g_strconcat(_("DCT algorithm"), ":", NULL)); - combo_dct = gtk_combo_box_new_text(); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Integer")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Fast integer")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Float")); - - if (selected_format == settings->format) { - format_params_jpeg settings_jpeg = (format_params_jpeg)(settings->params); - gtk_range_set_value(GTK_RANGE(scale_quality), settings_jpeg->quality); - gtk_range_set_value(GTK_RANGE(scale_smoothing), settings_jpeg->smoothing); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_entrophy), settings_jpeg->entropy); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_progressive), settings_jpeg->progressive); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_baseline), settings_jpeg->baseline); - - gtk_spin_button_configure (GTK_SPIN_BUTTON(spin_markers), GTK_ADJUSTMENT(gtk_adjustment_new (settings_jpeg->markers, 0, 64, 1, 1, 0)), 0, 0); - - buffer_comment = gtk_text_view_get_buffer(GTK_TEXT_VIEW (text_comment)); - gtk_text_buffer_set_text (buffer_comment, settings_jpeg->comment, -1); - gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_comment), buffer_comment); - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_subsampling), settings_jpeg->subsampling); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_dct), settings_jpeg->dct); - } - else { - gtk_range_set_value(GTK_RANGE(scale_quality), 85.0); - gtk_range_set_value(GTK_RANGE(scale_smoothing), 0.0); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_entrophy), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_progressive), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_baseline), FALSE); - - gtk_spin_button_configure (GTK_SPIN_BUTTON(spin_markers), GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 64, 1, 1, 0)), 0, 0); - - gtk_text_buffer_set_text (buffer_comment, "", -1); - gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_comment), buffer_comment); - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_subsampling), 2); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_dct), 1); - } - - gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(inner_widget), expander_advanced, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_smoothing), label_smoothing, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_smoothing), scale_smoothing, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_smoothing, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_checks), check_entrophy, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_checks), check_progressive, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_checks), check_baseline, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_checks, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_comment), label_comment, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_comment), text_comment, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_comment, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_markers), label_markers, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_markers), spin_markers, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_markers, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_subsampling), label_subsampling, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_subsampling), combo_subsampling, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_subsampling, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_dct), label_dct, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_dct), combo_dct, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_dct, FALSE, FALSE, 0); - - gtk_container_add (GTK_CONTAINER(expander_advanced), vbox_advanced); - - g_signal_connect(G_OBJECT(expander_advanced), "size-request", G_CALLBACK(adv_expanded), combo_format); - } - else if (selected_format == FORMAT_PNG) { - GtkWidget *hbox_compression, *label_compression; - GtkWidget *vbox_advanced; - - check_interlace = gtk_check_button_new_with_label(_("Interlace (Adam7)")); - hbox_compression = gtk_hbox_new(FALSE, 5); - label_compression = gtk_label_new(g_strconcat(_("Compression"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_compression), 0.5, 0.8); - scale_compression = gtk_hscale_new_with_range(0, 9, 1); - - expander_advanced = gtk_expander_new(_("Advanced params")); - vbox_advanced = gtk_vbox_new(FALSE, 5); - - check_savebgc = gtk_check_button_new_with_label(_("Save background color")); - check_savegamma = gtk_check_button_new_with_label(_("Save gamma")); - check_saveoff = gtk_check_button_new_with_label(_("Save layer offset")); - check_savephys = gtk_check_button_new_with_label(_("Save resolution")); - check_savetime = gtk_check_button_new_with_label(_("Save creation date")); - check_savecomm = gtk_check_button_new_with_label(_("Save comments")); - check_savetrans = gtk_check_button_new_with_label(_("Save color from transparent pixels")); - - if (selected_format == settings->format) { - format_params_png settings_png = (format_params_png)(settings->params); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), settings_png->interlace); - gtk_range_set_value(GTK_RANGE(scale_compression), settings_png->compression); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savebgc), settings_png->savebgc); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savegamma), settings_png->savegamma); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveoff), settings_png->saveoff); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savephys), settings_png->savephys); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetime), settings_png->savetime); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecomm), settings_png->savecomm); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetrans), settings_png->savetrans); - } - else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), FALSE); - gtk_range_set_value(GTK_RANGE(scale_compression), 9); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savebgc), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savegamma), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveoff), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savephys), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetime), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecomm), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetrans), TRUE); - } - - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savebgc, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savegamma, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_saveoff, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savephys, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savetime, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savecomm, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savetrans, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(inner_widget), check_interlace, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_compression), label_compression, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_compression), scale_compression, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_compression, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), expander_advanced, FALSE, FALSE, 0); - gtk_container_add (GTK_CONTAINER(expander_advanced), vbox_advanced); - - g_signal_connect(G_OBJECT(expander_advanced), "size-request", G_CALLBACK(adv_expanded), combo_format); - } - else if (selected_format == FORMAT_TGA) { - GtkWidget *hbox_origin, *label_origin; - - check_rle = gtk_check_button_new_with_label(_("RLE compression")); - - hbox_origin = gtk_hbox_new(FALSE, 5); - label_origin = gtk_label_new(g_strconcat(_("Image origin"), ":", NULL)); - - combo_origin = gtk_combo_box_new_text(); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_origin), _("Top-left")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_origin), _("Bottom-left")); - - if (selected_format == settings->format) { - format_params_tga settings_tga = (format_params_tga)(settings->params); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_rle), settings_tga->rle); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_origin), settings_tga->origin); - } - else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_rle), FALSE); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_origin), 0); - } - - gtk_box_pack_start(GTK_BOX(hbox_origin), label_origin, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_origin), combo_origin, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(inner_widget), check_rle, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_origin, TRUE, TRUE, 0); - } - else if (selected_format == FORMAT_TIFF) { - GtkWidget *hbox_compression, *label_compression; - - hbox_compression = gtk_hbox_new(FALSE, 5); - label_compression = gtk_label_new(g_strconcat(_("Compression"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_compression), 0.5, 0.5); - combo_compression = gtk_combo_box_new_text(); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("None")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("LZW")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("Pack bits")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("Deflate")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("JPEG")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("CCITT G3 Fax")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("CCITT G4 Fax")); - - if (selected_format == settings->format) { - format_params_tiff settings_tiff = (format_params_tiff)(settings->params); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_compression), settings_tiff->compression); - } - else { - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_compression), 0); - } - - gtk_box_pack_start(GTK_BOX(hbox_compression), label_compression, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_compression), combo_compression, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_compression, TRUE, TRUE, 0); - } - else if (selected_format == FORMAT_HEIF) { - GtkWidget *hbox_quality, *label_quality; - - check_lossless = gtk_check_button_new_with_label(_("Lossless")); - - hbox_quality = gtk_hbox_new(FALSE, 5); - label_quality = gtk_label_new(g_strconcat(_("Quality"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); - scale_quality = gtk_hscale_new_with_range(0, 100, 1); - - if (selected_format == settings->format) { - format_params_heif settings_heif = (format_params_heif)(settings->params); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), settings_heif->lossless); - - gtk_range_set_value(GTK_RANGE(scale_quality), settings_heif->quality); - } - else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), FALSE); - - gtk_range_set_value(GTK_RANGE(scale_quality), 85.0); - } - - gtk_box_pack_start(GTK_BOX(inner_widget), check_lossless, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, TRUE, TRUE, 0); - } - else if (selected_format == FORMAT_WEBP) { - GtkWidget *hbox_quality, *hbox_alpha_quality, *hbox_preset, *label_quality, *label_alpha_quality, *label_preset; - - check_lossless = gtk_check_button_new_with_label(_("Lossless")); - - hbox_quality = gtk_hbox_new(FALSE, 5); - label_quality = gtk_label_new(g_strconcat(_("Image quality"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); - scale_quality = gtk_hscale_new_with_range(0, 100, 1); - - hbox_alpha_quality = gtk_hbox_new(FALSE, 5); - label_alpha_quality = gtk_label_new(g_strconcat(_("Alpha quality"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_alpha_quality), 0.5, 0.8); - scale_alpha_quality = gtk_hscale_new_with_range(0, 100, 1); - - hbox_preset = gtk_hbox_new(FALSE, 5); - label_preset = gtk_label_new(g_strconcat(_("Preset"), ":", NULL)); - gtk_misc_set_alignment(GTK_MISC(label_preset), 0.5, 0.5); - combo_preset = gtk_combo_box_new_text(); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Default")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Picture")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Photo")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Drawing")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Icon")); - gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Text")); - - check_saveexif = gtk_check_button_new_with_label(_("Save EXIF data")); - check_savexmp = gtk_check_button_new_with_label(_("Save XMP data")); - check_savecp = gtk_check_button_new_with_label(_("Save color profile")); - - if (selected_format == settings->format) { - format_params_webp settings_webp = (format_params_webp)(settings->params); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), settings_webp->lossless); - - gtk_range_set_value(GTK_RANGE(scale_quality), settings_webp->quality); - gtk_range_set_value(GTK_RANGE(scale_alpha_quality), settings_webp->alpha_quality); - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_preset), settings_webp->preset); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveexif), settings_webp->exif); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savexmp), settings_webp->xmp); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecp), settings_webp->iptc); - } - else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), FALSE); - - gtk_range_set_value(GTK_RANGE(scale_quality), 90.0); - gtk_range_set_value(GTK_RANGE(scale_alpha_quality), 100.0); - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_preset), 0); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveexif), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savexmp), TRUE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecp), TRUE); - } - - gtk_box_pack_start(GTK_BOX(inner_widget), check_lossless, FALSE, FALSE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_alpha_quality), label_alpha_quality, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_alpha_quality), scale_alpha_quality, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_alpha_quality, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(hbox_preset), label_preset, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox_preset), combo_preset, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), hbox_preset, TRUE, TRUE, 0); - - gtk_box_pack_start(GTK_BOX(inner_widget), check_saveexif, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), check_savexmp, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(inner_widget), check_savecp, TRUE, TRUE, 0); - } - else { - GtkWidget *label_no_param ; - - label_no_param = gtk_label_new(_("This format has no params")); - - gtk_box_pack_start(GTK_BOX(inner_widget), label_no_param, FALSE, FALSE, 0); - } - - if (gtk_bin_get_child(GTK_BIN(frame_params)) != NULL) { - gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(frame_params))); - } - gtk_container_add(GTK_CONTAINER(frame_params), inner_widget); - gtk_widget_show_all(frame_params); -} - -static void adv_expanded (GtkWidget *expander, GtkRequisition *requisition, gpointer data) -{ - update_window_size(); -} - -static void update_window_size() { - gtk_window_resize(GTK_WINDOW(parentwin), 1, 1); -} - -void bimp_changeformat_save(changeformat_settings orig_settings) -{ - orig_settings->format = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_format)); - g_free(orig_settings->params); - - if (orig_settings->format == FORMAT_GIF) { - orig_settings->params = (format_params_gif) g_malloc(sizeof(struct changeformat_params_gif)); - ((format_params_gif)orig_settings->params)->interlace = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_interlace)); - } - else if (orig_settings->format == FORMAT_JPEG) { - orig_settings->params = (format_params_jpeg) g_malloc(sizeof(struct changeformat_params_jpeg)); - ((format_params_jpeg)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); - ((format_params_jpeg)orig_settings->params)->smoothing = gtk_range_get_value(GTK_RANGE(scale_smoothing)); - ((format_params_jpeg)orig_settings->params)->entropy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_entrophy)); - ((format_params_jpeg)orig_settings->params)->progressive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_progressive)); - ((format_params_jpeg)orig_settings->params)->baseline = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_baseline)); - - gtk_text_buffer_get_start_iter(buffer_comment, &start_comment); - gtk_text_buffer_get_end_iter(buffer_comment, &end_comment); - ((format_params_jpeg)orig_settings->params)->comment = g_strdup(gtk_text_buffer_get_text(buffer_comment, &start_comment, &end_comment, TRUE)); - - ((format_params_jpeg)orig_settings->params)->markers = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_markers)); - ((format_params_jpeg)orig_settings->params)->subsampling = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_subsampling)); - ((format_params_jpeg)orig_settings->params)->dct = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_dct)); - } - else if (orig_settings->format == FORMAT_PNG) { - orig_settings->params = (format_params_png) g_malloc(sizeof(struct changeformat_params_png)); - ((format_params_png)orig_settings->params)->interlace = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_interlace)); - ((format_params_png)orig_settings->params)->compression = gtk_range_get_value(GTK_RANGE(scale_compression)); - ((format_params_png)orig_settings->params)->savebgc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savebgc)); - ((format_params_png)orig_settings->params)->savegamma = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savegamma)); - ((format_params_png)orig_settings->params)->saveoff = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_saveoff)); - ((format_params_png)orig_settings->params)->savephys = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savephys)); - ((format_params_png)orig_settings->params)->savetime = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savetime)); - ((format_params_png)orig_settings->params)->savecomm = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savecomm)); - ((format_params_png)orig_settings->params)->savetrans = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savetrans)); - } - else if (orig_settings->format == FORMAT_TGA) { - orig_settings->params = (format_params_tga) g_malloc(sizeof(struct changeformat_params_tga)); - ((format_params_tga)orig_settings->params)->rle = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_rle)); - ((format_params_tga)orig_settings->params)->origin = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_origin)); - } - else if (orig_settings->format == FORMAT_TIFF) { - orig_settings->params = (format_params_tiff) g_malloc(sizeof(struct changeformat_params_tiff)); - ((format_params_tiff)orig_settings->params)->compression = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_compression)); - } - else if (orig_settings->format == FORMAT_HEIF) { - orig_settings->params = (format_params_heif) g_malloc(sizeof(struct changeformat_params_heif)); - ((format_params_heif)orig_settings->params)->lossless = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_lossless)); - ((format_params_heif)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); - } - else if (orig_settings->format == FORMAT_WEBP) { - orig_settings->params = (format_params_webp) g_malloc(sizeof(struct changeformat_params_webp)); - ((format_params_webp)orig_settings->params)->lossless = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_lossless)); - ((format_params_webp)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); - ((format_params_webp)orig_settings->params)->alpha_quality = gtk_range_get_value(GTK_RANGE(scale_alpha_quality)); - ((format_params_webp)orig_settings->params)->preset = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_preset)); - ((format_params_webp)orig_settings->params)->exif = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_saveexif)); - ((format_params_webp)orig_settings->params)->xmp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savexmp)); - ((format_params_webp)orig_settings->params)->iptc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savecp)); - - // also other "hidden" params - ((format_params_webp)orig_settings->params)->animation = FALSE; - ((format_params_webp)orig_settings->params)->anim_loop = TRUE; - ((format_params_webp)orig_settings->params)->minimize_size = TRUE; - ((format_params_webp)orig_settings->params)->kf_distance = 50; - ((format_params_webp)orig_settings->params)->delay = 200; - ((format_params_webp)orig_settings->params)->force_delay = FALSE; - } - else { - orig_settings->params = NULL; - } -} - - - +#include +#include +#include "gui-changeformat.h" +#include "../bimp-manipulations.h" +#include "../plugin-intl.h" + +static void update_frame_params(GtkComboBox*, changeformat_settings); +static void adv_expanded (GtkWidget*, GtkRequisition*, gpointer); +static void update_window_size(); + +static GtkWidget *frame_params, *inner_widget; +static GtkWidget *combo_format, *scale_quality, *scale_alpha_quality, *scale_smoothing, *check_interlace, *scale_compression, *check_baseline; +static GtkWidget *check_rle, *check_progressive, *check_entrophy, *combo_compression, *spin_markers, *combo_subsampling, *combo_dct, *combo_origin; +static GtkWidget *check_savebgc, *check_savegamma, *check_saveoff, *check_savephys, *check_savetime, *check_savecomm, *check_savetrans, *check_lossless; +static GtkWidget *combo_preset, *check_saveexif, *check_savexmp, *check_savecp; +static GtkWidget *expander_advanced; + +static GtkTextBuffer *buffer_comment; +static GtkTextIter start_comment, end_comment; + +static GtkWidget* parentwin; + +GtkWidget* bimp_changeformat_gui_new(changeformat_settings settings, GtkWidget* parent) +{ + GtkWidget *gui; + + parentwin = parent; + gui = gtk_vbox_new(FALSE, 5); + + combo_format = gtk_combo_box_new_text(); + for(int i = 0; i < FORMAT_END; i++) { + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_format), format_type_string[i][1]); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_format), settings->format); + + frame_params = gtk_frame_new(_("Format settings")); + + gtk_box_pack_start(GTK_BOX(gui), combo_format, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(gui), frame_params, FALSE, FALSE, 0); + + update_frame_params(GTK_COMBO_BOX(combo_format), settings); + + g_signal_connect(G_OBJECT(combo_format), "changed", G_CALLBACK(update_frame_params), settings); + + return gui; +} + +static void update_frame_params(GtkComboBox *widget, changeformat_settings settings) +{ + update_window_size(); + format_type selected_format = (format_type)gtk_combo_box_get_active(widget); + + inner_widget = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(inner_widget), 8); + if (selected_format == FORMAT_GIF) { + check_interlace = gtk_check_button_new_with_label(_("Interlaced")); + + if (selected_format == settings->format) { + format_params_gif settings_gif = (format_params_gif)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), settings_gif->interlace); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), FALSE); + } + + gtk_box_pack_start(GTK_BOX(inner_widget), check_interlace, FALSE, FALSE, 0); + } + else if (selected_format == FORMAT_JPEG) { + GtkWidget *hbox_quality, *hbox_smoothing, *hbox_checks, *hbox_comment, *hbox_markers, *hbox_subsampling, *hbox_dct; + GtkWidget *vbox_advanced, *label_quality, *label_smoothing, *label_markers, *label_comment, *label_subsampling, *label_dct, *text_comment; + + hbox_quality = gtk_hbox_new(FALSE, 5); + label_quality = gtk_label_new(g_strconcat(_("Quality"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); + scale_quality = gtk_hscale_new_with_range(0, 100, 1); + + expander_advanced = gtk_expander_new(_("Advanced params")); + vbox_advanced = gtk_vbox_new(FALSE, 5); + + hbox_smoothing = gtk_hbox_new(FALSE, 5); + label_smoothing = gtk_label_new(g_strconcat(_("Smoothing"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_smoothing), 0.5, 0.8); + scale_smoothing = gtk_hscale_new_with_range(0, 1, 0.01); + + hbox_checks = gtk_hbox_new(FALSE, 5); + check_entrophy = gtk_check_button_new_with_label(_("Optimize")); + check_progressive = gtk_check_button_new_with_label(_("Progressive")); + check_baseline = gtk_check_button_new_with_label(_("Save baseline")); + + hbox_comment = gtk_hbox_new(FALSE, 5); + label_comment = gtk_label_new(g_strconcat(_("Comment"), ":", NULL)); + text_comment = gtk_text_view_new(); + buffer_comment = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_comment)); + + hbox_markers = gtk_hbox_new(FALSE, 5); + label_markers = gtk_label_new(g_strconcat(_("Markers rows"), ":", NULL)); + spin_markers = gtk_spin_button_new(NULL, 1, 0); + + hbox_subsampling = gtk_hbox_new(FALSE, 5); + label_subsampling = gtk_label_new(g_strconcat(_("Subsampling"), ":", NULL)); + combo_subsampling = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), g_strconcat("2x2, 1x1, 1x1 (", _("Small size"), ")", NULL)); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), "2x1, 1x1, 1x1 (4:2:2)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), g_strconcat("1x1, 1x1, 1x1 (", _("Quality"), ")", NULL)); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_subsampling), "1x2, 1x1, 1x1"); + + + hbox_dct = gtk_hbox_new(FALSE, 5); + label_dct = gtk_label_new(g_strconcat(_("DCT algorithm"), ":", NULL)); + combo_dct = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Integer")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Fast integer")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_dct), _("Float")); + + if (selected_format == settings->format) { + format_params_jpeg settings_jpeg = (format_params_jpeg)(settings->params); + gtk_range_set_value(GTK_RANGE(scale_quality), settings_jpeg->quality); + gtk_range_set_value(GTK_RANGE(scale_smoothing), settings_jpeg->smoothing); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_entrophy), settings_jpeg->entropy); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_progressive), settings_jpeg->progressive); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_baseline), settings_jpeg->baseline); + + gtk_spin_button_configure (GTK_SPIN_BUTTON(spin_markers), GTK_ADJUSTMENT(gtk_adjustment_new (settings_jpeg->markers, 0, 64, 1, 1, 0)), 0, 0); + + buffer_comment = gtk_text_view_get_buffer(GTK_TEXT_VIEW (text_comment)); + gtk_text_buffer_set_text (buffer_comment, settings_jpeg->comment, -1); + gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_comment), buffer_comment); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_subsampling), settings_jpeg->subsampling); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_dct), settings_jpeg->dct); + } + else { + gtk_range_set_value(GTK_RANGE(scale_quality), 85.0); + gtk_range_set_value(GTK_RANGE(scale_smoothing), 0.0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_entrophy), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_progressive), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_baseline), FALSE); + + gtk_spin_button_configure (GTK_SPIN_BUTTON(spin_markers), GTK_ADJUSTMENT(gtk_adjustment_new (0, 0, 64, 1, 1, 0)), 0, 0); + + gtk_text_buffer_set_text (buffer_comment, "", -1); + gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_comment), buffer_comment); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_subsampling), 2); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_dct), 1); + } + + gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(inner_widget), expander_advanced, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_smoothing), label_smoothing, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_smoothing), scale_smoothing, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_smoothing, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_checks), check_entrophy, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_checks), check_progressive, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_checks), check_baseline, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_checks, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_comment), label_comment, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_comment), text_comment, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_comment, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_markers), label_markers, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_markers), spin_markers, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_markers, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_subsampling), label_subsampling, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_subsampling), combo_subsampling, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_subsampling, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_dct), label_dct, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_dct), combo_dct, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), hbox_dct, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER(expander_advanced), vbox_advanced); + + g_signal_connect(G_OBJECT(expander_advanced), "size-request", G_CALLBACK(adv_expanded), combo_format); + } + else if (selected_format == FORMAT_PNG) { + GtkWidget *hbox_compression, *label_compression; + GtkWidget *vbox_advanced; + + check_interlace = gtk_check_button_new_with_label(_("Interlace (Adam7)")); + hbox_compression = gtk_hbox_new(FALSE, 5); + label_compression = gtk_label_new(g_strconcat(_("Compression"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_compression), 0.5, 0.8); + scale_compression = gtk_hscale_new_with_range(0, 9, 1); + + expander_advanced = gtk_expander_new(_("Advanced params")); + vbox_advanced = gtk_vbox_new(FALSE, 5); + + check_savebgc = gtk_check_button_new_with_label(_("Save background color")); + check_savegamma = gtk_check_button_new_with_label(_("Save gamma")); + check_saveoff = gtk_check_button_new_with_label(_("Save layer offset")); + check_savephys = gtk_check_button_new_with_label(_("Save resolution")); + check_savetime = gtk_check_button_new_with_label(_("Save creation date")); + check_savecomm = gtk_check_button_new_with_label(_("Save comments")); + check_savetrans = gtk_check_button_new_with_label(_("Save color from transparent pixels")); + + if (selected_format == settings->format) { + format_params_png settings_png = (format_params_png)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), settings_png->interlace); + gtk_range_set_value(GTK_RANGE(scale_compression), settings_png->compression); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savebgc), settings_png->savebgc); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savegamma), settings_png->savegamma); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveoff), settings_png->saveoff); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savephys), settings_png->savephys); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetime), settings_png->savetime); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecomm), settings_png->savecomm); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetrans), settings_png->savetrans); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_interlace), FALSE); + gtk_range_set_value(GTK_RANGE(scale_compression), 9); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savebgc), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savegamma), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveoff), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savephys), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetime), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecomm), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savetrans), TRUE); + } + + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savebgc, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savegamma, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_saveoff, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savephys, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savetime, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savecomm, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_advanced), check_savetrans, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(inner_widget), check_interlace, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_compression), label_compression, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_compression), scale_compression, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_compression, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), expander_advanced, FALSE, FALSE, 0); + gtk_container_add (GTK_CONTAINER(expander_advanced), vbox_advanced); + + g_signal_connect(G_OBJECT(expander_advanced), "size-request", G_CALLBACK(adv_expanded), combo_format); + } + else if (selected_format == FORMAT_TGA) { + GtkWidget *hbox_origin, *label_origin; + + check_rle = gtk_check_button_new_with_label(_("RLE compression")); + + hbox_origin = gtk_hbox_new(FALSE, 5); + label_origin = gtk_label_new(g_strconcat(_("Image origin"), ":", NULL)); + + combo_origin = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_origin), _("Top-left")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_origin), _("Bottom-left")); + + if (selected_format == settings->format) { + format_params_tga settings_tga = (format_params_tga)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_rle), settings_tga->rle); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_origin), settings_tga->origin); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_rle), FALSE); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_origin), 0); + } + + gtk_box_pack_start(GTK_BOX(hbox_origin), label_origin, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_origin), combo_origin, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(inner_widget), check_rle, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_origin, TRUE, TRUE, 0); + } + else if (selected_format == FORMAT_TIFF) { + GtkWidget *hbox_compression, *label_compression; + + hbox_compression = gtk_hbox_new(FALSE, 5); + label_compression = gtk_label_new(g_strconcat(_("Compression"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_compression), 0.5, 0.5); + combo_compression = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("None")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("LZW")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("Pack bits")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("Deflate")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("JPEG")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("CCITT G3 Fax")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_compression), _("CCITT G4 Fax")); + + if (selected_format == settings->format) { + format_params_tiff settings_tiff = (format_params_tiff)(settings->params); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_compression), settings_tiff->compression); + } + else { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_compression), 0); + } + + gtk_box_pack_start(GTK_BOX(hbox_compression), label_compression, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_compression), combo_compression, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_compression, TRUE, TRUE, 0); + } + else if (selected_format == FORMAT_HEIF) { + GtkWidget *hbox_quality, *label_quality; + + check_lossless = gtk_check_button_new_with_label(_("Lossless")); + + hbox_quality = gtk_hbox_new(FALSE, 5); + label_quality = gtk_label_new(g_strconcat(_("Quality"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); + scale_quality = gtk_hscale_new_with_range(0, 100, 1); + + if (selected_format == settings->format) { + format_params_heif settings_heif = (format_params_heif)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), settings_heif->lossless); + + gtk_range_set_value(GTK_RANGE(scale_quality), settings_heif->quality); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), FALSE); + + gtk_range_set_value(GTK_RANGE(scale_quality), 85.0); + } + + gtk_box_pack_start(GTK_BOX(inner_widget), check_lossless, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, TRUE, TRUE, 0); + } + else if (selected_format == FORMAT_WEBP) { + GtkWidget *hbox_quality, *hbox_alpha_quality, *hbox_preset, *label_quality, *label_alpha_quality, *label_preset; + + check_lossless = gtk_check_button_new_with_label(_("Lossless")); + + hbox_quality = gtk_hbox_new(FALSE, 5); + label_quality = gtk_label_new(g_strconcat(_("Image quality"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); + scale_quality = gtk_hscale_new_with_range(0, 100, 1); + + hbox_alpha_quality = gtk_hbox_new(FALSE, 5); + label_alpha_quality = gtk_label_new(g_strconcat(_("Alpha quality"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_alpha_quality), 0.5, 0.8); + scale_alpha_quality = gtk_hscale_new_with_range(0, 100, 1); + + hbox_preset = gtk_hbox_new(FALSE, 5); + label_preset = gtk_label_new(g_strconcat(_("Preset"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_preset), 0.5, 0.5); + combo_preset = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Default")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Picture")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Photo")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Drawing")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Icon")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo_preset), _("Text")); + + check_saveexif = gtk_check_button_new_with_label(_("Save EXIF data")); + check_savexmp = gtk_check_button_new_with_label(_("Save XMP data")); + check_savecp = gtk_check_button_new_with_label(_("Save color profile")); + + if (selected_format == settings->format) { + format_params_webp settings_webp = (format_params_webp)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), settings_webp->lossless); + + gtk_range_set_value(GTK_RANGE(scale_quality), settings_webp->quality); + gtk_range_set_value(GTK_RANGE(scale_alpha_quality), settings_webp->alpha_quality); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_preset), settings_webp->preset); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveexif), settings_webp->exif); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savexmp), settings_webp->xmp); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecp), settings_webp->iptc); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), FALSE); + + gtk_range_set_value(GTK_RANGE(scale_quality), 90.0); + gtk_range_set_value(GTK_RANGE(scale_alpha_quality), 100.0); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_preset), 0); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_saveexif), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savexmp), TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_savecp), TRUE); + } + + gtk_box_pack_start(GTK_BOX(inner_widget), check_lossless, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_alpha_quality), label_alpha_quality, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_alpha_quality), scale_alpha_quality, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_alpha_quality, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_preset), label_preset, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_preset), combo_preset, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_preset, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(inner_widget), check_saveexif, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), check_savexmp, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), check_savecp, TRUE, TRUE, 0); + } + else if (selected_format == FORMAT_AVIF) { + GtkWidget *hbox_quality, *label_quality; + + check_lossless = gtk_check_button_new_with_label(_("Nearly lossless")); + + hbox_quality = gtk_hbox_new(FALSE, 5); + label_quality = gtk_label_new(g_strconcat(_("Quality"), ":", NULL)); + gtk_misc_set_alignment(GTK_MISC(label_quality), 0.5, 0.8); + scale_quality = gtk_hscale_new_with_range(0, 100, 1); + + if (selected_format == settings->format) { + format_params_avif settings_avif = (format_params_avif)(settings->params); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), settings_avif->lossless); + + gtk_range_set_value(GTK_RANGE(scale_quality), settings_avif->quality); + } + else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_lossless), FALSE); + + gtk_range_set_value(GTK_RANGE(scale_quality), 50); + } + + gtk_box_pack_start(GTK_BOX(inner_widget), check_lossless, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hbox_quality), label_quality, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_quality), scale_quality, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(inner_widget), hbox_quality, TRUE, TRUE, 0); + } + else { + GtkWidget *label_no_param ; + + label_no_param = gtk_label_new(_("This format has no params")); + + gtk_box_pack_start(GTK_BOX(inner_widget), label_no_param, FALSE, FALSE, 0); + } + + if (gtk_bin_get_child(GTK_BIN(frame_params)) != NULL) { + gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(frame_params))); + } + gtk_container_add(GTK_CONTAINER(frame_params), inner_widget); + gtk_widget_show_all(frame_params); +} + +static void adv_expanded (GtkWidget *expander, GtkRequisition *requisition, gpointer data) +{ + update_window_size(); +} + +static void update_window_size() { + gtk_window_resize(GTK_WINDOW(parentwin), 1, 1); +} + +void bimp_changeformat_save(changeformat_settings orig_settings) +{ + orig_settings->format = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_format)); + g_free(orig_settings->params); + + if (orig_settings->format == FORMAT_GIF) { + orig_settings->params = (format_params_gif) g_malloc(sizeof(struct changeformat_params_gif)); + ((format_params_gif)orig_settings->params)->interlace = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_interlace)); + } + else if (orig_settings->format == FORMAT_JPEG) { + orig_settings->params = (format_params_jpeg) g_malloc(sizeof(struct changeformat_params_jpeg)); + ((format_params_jpeg)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); + ((format_params_jpeg)orig_settings->params)->smoothing = gtk_range_get_value(GTK_RANGE(scale_smoothing)); + ((format_params_jpeg)orig_settings->params)->entropy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_entrophy)); + ((format_params_jpeg)orig_settings->params)->progressive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_progressive)); + ((format_params_jpeg)orig_settings->params)->baseline = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_baseline)); + + gtk_text_buffer_get_start_iter(buffer_comment, &start_comment); + gtk_text_buffer_get_end_iter(buffer_comment, &end_comment); + ((format_params_jpeg)orig_settings->params)->comment = g_strdup(gtk_text_buffer_get_text(buffer_comment, &start_comment, &end_comment, TRUE)); + + ((format_params_jpeg)orig_settings->params)->markers = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_markers)); + ((format_params_jpeg)orig_settings->params)->subsampling = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_subsampling)); + ((format_params_jpeg)orig_settings->params)->dct = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_dct)); + } + else if (orig_settings->format == FORMAT_PNG) { + orig_settings->params = (format_params_png) g_malloc(sizeof(struct changeformat_params_png)); + ((format_params_png)orig_settings->params)->interlace = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_interlace)); + ((format_params_png)orig_settings->params)->compression = gtk_range_get_value(GTK_RANGE(scale_compression)); + ((format_params_png)orig_settings->params)->savebgc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savebgc)); + ((format_params_png)orig_settings->params)->savegamma = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savegamma)); + ((format_params_png)orig_settings->params)->saveoff = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_saveoff)); + ((format_params_png)orig_settings->params)->savephys = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savephys)); + ((format_params_png)orig_settings->params)->savetime = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savetime)); + ((format_params_png)orig_settings->params)->savecomm = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savecomm)); + ((format_params_png)orig_settings->params)->savetrans = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savetrans)); + } + else if (orig_settings->format == FORMAT_TGA) { + orig_settings->params = (format_params_tga) g_malloc(sizeof(struct changeformat_params_tga)); + ((format_params_tga)orig_settings->params)->rle = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_rle)); + ((format_params_tga)orig_settings->params)->origin = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_origin)); + } + else if (orig_settings->format == FORMAT_TIFF) { + orig_settings->params = (format_params_tiff) g_malloc(sizeof(struct changeformat_params_tiff)); + ((format_params_tiff)orig_settings->params)->compression = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_compression)); + } + else if (orig_settings->format == FORMAT_HEIF) { + orig_settings->params = (format_params_heif) g_malloc(sizeof(struct changeformat_params_heif)); + ((format_params_heif)orig_settings->params)->lossless = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_lossless)); + ((format_params_heif)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); + } + else if (orig_settings->format == FORMAT_WEBP) { + orig_settings->params = (format_params_webp) g_malloc(sizeof(struct changeformat_params_webp)); + ((format_params_webp)orig_settings->params)->lossless = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_lossless)); + ((format_params_webp)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); + ((format_params_webp)orig_settings->params)->alpha_quality = gtk_range_get_value(GTK_RANGE(scale_alpha_quality)); + ((format_params_webp)orig_settings->params)->preset = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_preset)); + ((format_params_webp)orig_settings->params)->exif = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_saveexif)); + ((format_params_webp)orig_settings->params)->xmp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savexmp)); + ((format_params_webp)orig_settings->params)->iptc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_savecp)); + + // also other "hidden" params + ((format_params_webp)orig_settings->params)->animation = FALSE; + ((format_params_webp)orig_settings->params)->anim_loop = TRUE; + ((format_params_webp)orig_settings->params)->minimize_size = TRUE; + ((format_params_webp)orig_settings->params)->kf_distance = 50; + ((format_params_webp)orig_settings->params)->delay = 200; + ((format_params_webp)orig_settings->params)->force_delay = FALSE; + } + else if (orig_settings->format == FORMAT_AVIF) { + orig_settings->params = (format_params_avif) g_malloc(sizeof(struct changeformat_params_avif)); + + ((format_params_avif)orig_settings->params)->lossless = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_lossless)); + ((format_params_avif)orig_settings->params)->quality = gtk_range_get_value(GTK_RANGE(scale_quality)); + } + else { + orig_settings->params = NULL; + } +} + + +