From 09fc6077c3dc9202baef12a2659f1e2883319a11 Mon Sep 17 00:00:00 2001 From: Guillaume Stutin Date: Wed, 12 Jun 2024 02:46:55 +0200 Subject: [PATCH 1/3] Export: Refactor export process functions. --- src/control/jobs/control_jobs.c | 377 ++++++++++++++++---------------- src/control/jobs/control_jobs.h | 31 ++- src/libs/export.c | 127 +++++++---- 3 files changed, 292 insertions(+), 243 deletions(-) diff --git a/src/control/jobs/control_jobs.c b/src/control/jobs/control_jobs.c index 9ee86f9b1aea..c457d6d7de33 100644 --- a/src/control/jobs/control_jobs.c +++ b/src/control/jobs/control_jobs.c @@ -76,21 +76,6 @@ typedef struct dt_control_gpx_apply_t gchar *tz; } dt_control_gpx_apply_t; -typedef struct dt_control_export_t -{ - int max_width, max_height, format_index, storage_index; - dt_imageio_module_data_t *sdata; // needed since the gui thread resets things like overwrite once the export - // is dispatched, but we have to keep that information - gboolean export_masks; - char style[128]; - gboolean style_append; - dt_colorspaces_color_profile_type_t icc_type; - gchar *icc_filename; - dt_iop_color_intent_t icc_intent; - gchar *metadata_export; -} dt_control_export_t; - - typedef struct dt_control_image_enumerator_t { GList *index; @@ -1313,38 +1298,60 @@ static int32_t dt_control_refresh_exif_run(dt_job_t *job) } -static int32_t dt_control_export_job_run(dt_job_t *job) +static int32_t _control_export_job_run(dt_job_t *job) { dt_control_image_enumerator_t *params = (dt_control_image_enumerator_t *)dt_control_job_get_params(job); - dt_control_export_t *settings = (dt_control_export_t *)params->data; + dt_control_export_t *data = (dt_control_export_t *)params->data; GList *t = params->index; - dt_imageio_module_format_t *mformat = dt_imageio_get_format_by_index(settings->format_index); - g_assert(mformat); - dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(settings->storage_index); - g_assert(mstorage); - dt_imageio_module_data_t *sdata = settings->sdata; + dt_imageio_module_format_t *module_format = dt_imageio_get_format_by_index(data->format_index); + g_assert(module_format); + dt_imageio_module_storage_t *module_storage = dt_imageio_get_storage_by_index(data->storage_index); + g_assert(module_storage); + dt_imageio_module_data_t *module_data = data->module_data; gboolean tag_change = FALSE; + fprintf(stdout, "\nEXPORT VAL:\n" + "First imgID: %i\n" + "max_width: %i, max_height: %i\n" + "format_index: %i\n" + "storage_index: %i\n" + "high_quality: %i\n" + "upscale: %i\n" + "export_masks: %i\n" + "style: %s\n" + "style_append: %i\n" + "icc_type: %i\n" + "icc_filename: %s\n" + "icc_intent: %i\n" + "metadata_export: %s\n\n", + GPOINTER_TO_INT(data->imgid_list->data), + data->max_width, data->max_height, + data->format_index, data->storage_index, + data->high_quality, data->upscale, data->export_masks, + data->style, data->style_append, + data->icc_type, data->icc_filename, data->icc_intent, + data->metadata_export); + // get a thread-safe fdata struct (one jpeg struct per thread etc): - dt_imageio_module_data_t *fdata = mformat->get_params(mformat); + dt_imageio_module_data_t *fdata = module_format->get_params(module_format); - if(mstorage->initialize_store) + if(module_storage->initialize_store) { - if(mstorage->initialize_store(mstorage, sdata, &mformat, &fdata, &t, TRUE)) + if(module_storage->initialize_store(module_storage, module_data, &module_format, &fdata, &t, TRUE)) { // bail out, something went wrong goto end; } - mformat->set_params(mformat, fdata, mformat->params_size(mformat)); - mstorage->set_params(mstorage, sdata, mstorage->params_size(mstorage)); + module_format->set_params(module_format, fdata, module_format->params_size(module_format)); + module_storage->set_params(module_storage, module_data, module_storage->params_size(module_storage)); } // Get max dimensions... uint32_t w, h, fw, fh, sw, sh; fw = fh = sw = sh = 0; - mstorage->dimension(mstorage, sdata, &sw, &sh); - mformat->dimension(mformat, fdata, &fw, &fh); + module_storage->dimension(module_storage, module_data, &sw, &sh); + module_format->dimension(module_format, fdata, &fw, &fh); if(sw == 0 || fw == 0) w = sw > fw ? sw : fw; @@ -1365,10 +1372,10 @@ static int32_t dt_control_export_job_run(dt_job_t *job) double fraction = 0; // set up the fdata struct - fdata->max_width = (settings->max_width != 0 && w != 0) ? MIN(w, settings->max_width) : MAX(w, settings->max_width); - fdata->max_height = (settings->max_height != 0 && h != 0) ? MIN(h, settings->max_height) : MAX(h, settings->max_height); - g_strlcpy(fdata->style, settings->style, sizeof(fdata->style)); - fdata->style_append = settings->style_append; + fdata->max_width = (data->max_width != 0 && w != 0) ? MIN(w, data->max_width) : MAX(w, data->max_width); + fdata->max_height = (data->max_height != 0 && h != 0) ? MIN(h, data->max_height) : MAX(h, data->max_height); + g_strlcpy(fdata->style, data->style, sizeof(fdata->style)); + fdata->style_append = data->style_append; // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a // sensible assumption? guint tagid = 0, etagid = 0; @@ -1376,10 +1383,10 @@ static int32_t dt_control_export_job_run(dt_job_t *job) dt_export_metadata_t metadata; metadata.flags = 0; - metadata.list = dt_util_str_to_glist("\1", settings->metadata_export); + metadata.list = dt_util_str_to_glist("\1", data->metadata_export); if(metadata.list) { - metadata.flags = strtol(metadata.list->data, NULL, 16); + metadata.flags = (int32_t) strtol(metadata.list->data, NULL, 16); metadata.list = g_list_remove(metadata.list, metadata.list->data); } @@ -1391,7 +1398,7 @@ static int32_t dt_control_export_job_run(dt_job_t *job) // progress message char message[512] = { 0 }; - snprintf(message, sizeof(message), _("exporting %d / %d to %s"), num, total, mstorage->name(mstorage)); + snprintf(message, sizeof(message), _("exporting %d / %d to %s"), num, total, module_storage->name(module_storage)); // update the message. initialize_store() might have changed the number of images dt_control_job_set_progress_message(job, message); @@ -1420,8 +1427,8 @@ static int32_t dt_control_export_job_run(dt_job_t *job) else { dt_image_cache_read_release(darktable.image_cache, image); - if(mstorage->store(mstorage, sdata, imgid, mformat, fdata, num, total, TRUE, - settings->export_masks, settings->icc_type, settings->icc_filename, settings->icc_intent, + if(module_storage->store(module_storage, module_data, imgid, module_format, fdata, num, total, TRUE, + data->export_masks, data->icc_type, data->icc_filename, data->icc_intent, &metadata) != 0) dt_control_job_cancel(job); } @@ -1433,16 +1440,17 @@ static int32_t dt_control_export_job_run(dt_job_t *job) } g_list_free_full(metadata.list, g_free); - if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata); + if(module_storage->finalize_store) module_storage->finalize_store(module_storage, module_data); end: // all threads free their fdata - mformat->free_params(mformat, fdata); + module_format->free_params(module_format, fdata); // notify the user via the window manager dt_ui_notify_user(); if(tag_change) DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED); + fprintf(stdout,"end export run\n"); return 0; } @@ -1799,85 +1807,163 @@ void dt_control_refresh_exif() NULL, PROGRESS_CANCELLABLE, FALSE)); } -static dt_control_image_enumerator_t *dt_control_export_alloc() +static void _control_export_job_cleanup(void *p) { - dt_control_image_enumerator_t *params = dt_control_image_enumerator_alloc(); - if(!params) return NULL; + dt_control_image_enumerator_t *params = (dt_control_image_enumerator_t *)p; + dt_control_export_t *data = (dt_control_export_t *)params->data; + dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(data->storage_index); + dt_imageio_module_data_t *sdata = data->module_data; + + mstorage->free_params(mstorage, sdata); - params->data = calloc(1, sizeof(dt_control_export_t)); - if(!params->data) - { - dt_control_image_enumerator_cleanup(params); - return NULL; - } + g_free(data->style); + g_free(data->icc_filename); + g_free(data->metadata_export); - return params; + g_list_free(data->imgid_list); + free(data); + dt_control_image_enumerator_cleanup(params); } -static void dt_control_export_cleanup(void *p) +static void _control_import_job_cleanup(void *p) { - dt_control_image_enumerator_t *params = p; + dt_control_image_enumerator_t *params = (dt_control_image_enumerator_t *)p; + dt_control_import_t *data = (dt_control_import_t *)params->data; + + // Display a recap of files that weren't copied + if(g_list_length(g_list_last(data->discarded)) > 0) + { + // Create the window + GtkWidget *dialog = gtk_dialog_new_with_buttons("Message", + GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), + GTK_DIALOG_DESTROY_WITH_PARENT, + _("_OK"), + GTK_RESPONSE_NONE, + NULL); + gtk_window_set_title(GTK_WINDOW(dialog), _("Some files have not been copied")); + gtk_window_set_default_size(GTK_WINDOW(dialog), DT_PIXEL_APPLY_DPI(800), DT_PIXEL_APPLY_DPI(800)); - dt_control_export_t *settings = (dt_control_export_t *)params->data; - dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(settings->storage_index); - dt_imageio_module_data_t *sdata = settings->sdata; + // Create the label + GtkWidget *label = gtk_label_new(_("The following source files have not been copied " + "because similarly-named files already exist on the destination. " + "This may be because the files have already been imported " + "or the naming pattern leads to non-unique file names.")); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); - mstorage->free_params(mstorage, sdata); + // Create the scrolled window internal container + GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(scrolled_window), TRUE); - g_free(settings->icc_filename); - g_free(settings->metadata_export); - free(params->data); + // Create the treeview model from the list of discarded file pathes + GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING); + GtkTreeIter iter; + for(GList *file = g_list_first(data->discarded); file; file = g_list_next(file)) + { + if(file->data) + { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, g_strdup((char *)file->data), -1); + } + } + + // Create the treeview view. Sooooo verbose... it's only a flat list. + GtkWidget *view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + GtkTreeViewColumn *col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, _("Origin path")); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_set_attributes(col, renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); + g_object_unref(store); + + // Pack widgets to an unified box + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), scrolled_window, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(scrolled_window), view); + + // Pack the box to the dialog internal container + GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(content_area), box); + gtk_widget_show_all(dialog); + +#ifdef GDK_WINDOWING_QUARTZ + dt_osx_disallow_fullscreen(dialog); +#endif + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + g_list_free_full(data->discarded, g_free); + + for(GList *img = g_list_first(data->imgs); img; img = g_list_next(img)) + free(img->data); + + free(data); dt_control_image_enumerator_cleanup(params); } -void dt_control_export(GList *imgid_list, int max_width, int max_height, int format_index, int storage_index, - gboolean high_quality, gboolean export_masks, char *style, gboolean style_append, - dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, - dt_iop_color_intent_t icc_intent, const gchar *metadata_export) +static void *_control_import_export_alloc(gboolean is_import) { - dt_job_t *job = dt_control_job_create(&dt_control_export_job_run, "export"); - if(!job) return; - dt_control_image_enumerator_t *params = dt_control_export_alloc(); - if(!params) + dt_control_image_enumerator_t *params = dt_control_image_enumerator_alloc(); + if(!params) return NULL; + + params->data = g_malloc0(sizeof(dt_control_import_t)); + if(!params->data) { - dt_control_job_dispose(job); - return; + if(is_import) + _control_import_job_cleanup(params); + else + _control_export_job_cleanup(params); + return NULL; } - dt_control_job_set_params(job, params, dt_control_export_cleanup); - - params->index = imgid_list; + return params; +} - dt_control_export_t *data = params->data; - data->max_width = max_width; - data->max_height = max_height; - data->format_index = format_index; - data->storage_index = storage_index; - dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(storage_index); - g_assert(mstorage); - // get shared storage param struct (global sequence counter, one picasa connection etc) - dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage); - if(sdata == NULL) +/** + * @brief + * @param data + * which contains: + * @param imgid_list + * @param max_width + * @param max_height + * @param format_index + * @param storage_index + * @param high_quality + * @param upscale + * @param export_masks + * @param style + * @param style_append + * @param icc_type + * @param icc_filename + * @param icc_intent + * @param metadata_export + */ +static dt_job_t *_control_export_job_create(dt_control_export_t data) +{ + dt_job_t *job = dt_control_job_create(&_control_export_job_run, "export"); + if(!job) return NULL; + dt_control_image_enumerator_t *params = _control_import_export_alloc(FALSE); + if(!params) { - dt_control_log(_("failed to get parameters from storage module `%s', aborting export.."), - mstorage->name(mstorage)); dt_control_job_dispose(job); - return; + return NULL; } - data->sdata = sdata; - data->export_masks = export_masks; - g_strlcpy(data->style, style, sizeof(data->style)); - data->style_append = style_append; - data->icc_type = icc_type; - data->icc_filename = g_strdup(icc_filename); - data->icc_intent = icc_intent; - data->metadata_export = g_strdup(metadata_export); - + memcpy(params->data, &data, sizeof(dt_control_export_t)); + params->index = ((dt_control_export_t *)params->data)->imgid_list; dt_control_job_add_progress(job, _("export images"), TRUE); - dt_control_add_job(darktable.control, DT_JOB_QUEUE_USER_EXPORT, job); + dt_control_job_set_params(job, params, _control_export_job_cleanup); + fprintf(stdout,"end create export\n"); + return job; +} - // tell the storage that we got its params for an export so it can reset itself to a safe state - mstorage->export_dispatched(mstorage); +void dt_control_export(dt_control_export_t data) +{ + dt_control_add_job(darktable.control, DT_JOB_QUEUE_USER_EXPORT, _control_export_job_create(data)); + fprintf(stdout,"end dt_control_export\n"); } static void _add_datetime_offset(const char *odt, const long int offset, char *ndt) @@ -2410,106 +2496,11 @@ static int32_t _control_import_job_run(dt_job_t *job) return data->total_imported_elements >= 1 ? 0 : 1; } -static void _control_import_job_cleanup(void *p) -{ - dt_control_image_enumerator_t *params = (dt_control_image_enumerator_t *)p; - dt_control_import_t *data = params->data; - - // Display a recap of files that weren't copied - if(g_list_length(g_list_last(data->discarded)) > 0) - { - // Create the window - GtkWidget *dialog = gtk_dialog_new_with_buttons("Message", - GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), - GTK_DIALOG_DESTROY_WITH_PARENT, - _("_OK"), - GTK_RESPONSE_NONE, - NULL); - gtk_window_set_title(GTK_WINDOW(dialog), _("Some files have not been copied")); - gtk_window_set_default_size(GTK_WINDOW(dialog), DT_PIXEL_APPLY_DPI(800), DT_PIXEL_APPLY_DPI(800)); - - // Create the label - GtkWidget *label = gtk_label_new(_("The following source files have not been copied " - "because similarly-named files already exist on the destination. " - "This may be because the files have already been imported " - "or the naming pattern leads to non-unique file names.")); - gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); - - // Create the scrolled window internal container - GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(scrolled_window), TRUE); - - // Create the treeview model from the list of discarded file pathes - GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING); - GtkTreeIter iter; - for(GList *file = g_list_first(data->discarded); file; file = g_list_next(file)) - { - if(file->data) - { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, g_strdup((char *)file->data), -1); - } - } - - // Create the treeview view. Sooooo verbose... it's only a flat list. - GtkWidget *view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - GtkTreeViewColumn *col = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(col, _("Origin path")); - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(col, renderer, TRUE); - gtk_tree_view_column_set_attributes(col, renderer, "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - g_object_unref(store); - - // Pack widgets to an unified box - GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(box), scrolled_window, TRUE, TRUE, 0); - gtk_container_add(GTK_CONTAINER(scrolled_window), view); - - // Pack the box to the dialog internal container - GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_container_add(GTK_CONTAINER(content_area), box); - gtk_widget_show_all(dialog); - -#ifdef GDK_WINDOWING_QUARTZ - dt_osx_disallow_fullscreen(dialog); -#endif - - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - } - - g_list_free_full(data->discarded, g_free); - - for(GList *img = g_list_first(data->imgs); img; img = g_list_next(img)) - free(img->data); - - free(data); - dt_control_image_enumerator_cleanup(params); -} - -static void *_control_import_alloc() -{ - dt_control_image_enumerator_t *params = dt_control_image_enumerator_alloc(); - if(!params) return NULL; - - params->data = g_malloc0(sizeof(dt_control_import_t)); - if(!params->data) - { - _control_import_job_cleanup(params); - return NULL; - } - return params; -} - static dt_job_t *_control_import_job_create(dt_control_import_t data) { dt_job_t *job = dt_control_job_create(&_control_import_job_run, "import"); if(!job) return NULL; - dt_control_image_enumerator_t *params = _control_import_alloc(); + dt_control_image_enumerator_t *params = _control_import_export_alloc(TRUE); if(!params) { dt_control_job_dispose(job); diff --git a/src/control/jobs/control_jobs.h b/src/control/jobs/control_jobs.h index 63eee34aff0a..fd01a82fa883 100644 --- a/src/control/jobs/control_jobs.h +++ b/src/control/jobs/control_jobs.h @@ -47,6 +47,31 @@ typedef struct dt_control_import_t } dt_control_import_t; +typedef struct dt_control_export_t +{ + GList *imgid_list; + int max_width; + int max_height; + int format_index; + int storage_index; + gboolean high_quality; + gboolean export_masks; + gchar *style; + gboolean style_append; + dt_colorspaces_color_profile_type_t icc_type; + gchar *icc_filename; + dt_iop_color_intent_t icc_intent; + + gchar *metadata_export; + + /** + * Needed since the gui thread resets things like overwrite once the export + * is dispatched, but we have to keep that information. + */ + dt_imageio_module_data_t *module_data; + +} dt_control_export_t; + void dt_control_gpx_apply(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs); void dt_control_datetime(const GTimeSpan offset, const char *datetime, GList *imgs); @@ -62,11 +87,7 @@ void dt_control_move_images(); void dt_control_copy_images(); void dt_control_set_local_copy_images(); void dt_control_reset_local_copy_images(); -void dt_control_export(GList *imgid_list, int max_width, int max_height, int format_index, int storage_index, - gboolean high_quality, gboolean export_masks, - char *style, gboolean style_append, - dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, - dt_iop_color_intent_t icc_intent, const gchar *metadata_export); +void dt_control_export(dt_control_export_t data); void dt_control_merge_hdr(); /** diff --git a/src/libs/export.c b/src/libs/export.c index f7e1f345fa2d..ce8804e5cc40 100644 --- a/src/libs/export.c +++ b/src/libs/export.c @@ -262,81 +262,118 @@ static void _scale_optim() free(scale_str); } -static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d) +/** + * @brief get style from config + * + * @return gchar* + */ +gchar *_conf_get_style() { - /* write current history changes so nothing gets lost, - do that only in the darkroom as there is nothing to be saved - when in the lighttable (and it would write over current history stack) */ - const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager); - if(cv->view(cv) == DT_VIEW_DARKROOM) dt_dev_write_history(darktable.develop); - - char style[128] = { 0 }; - - // get the format_name and storage_name settings which are plug-ins name and not necessary what is displayed on the combobox. - // note that we cannot take directly the combobox entry index as depending on the storage some format are not listed. - const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name"); - const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name"); - const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name)); - const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name)); - - if(format_index == -1) - { - dt_control_log("invalid format for export selected"); - return; - } - if(storage_index == -1) + gchar *style = NULL; + const gchar *tmp_style = dt_conf_get_string_const(CONFIG_PREFIX "style"); + if(tmp_style) { - dt_control_log("invalid storage for export selected"); - return; + style = g_strdup(tmp_style); } + return style; +} + + +gint _export_confirm_message() +{ + gint res = -1; char *confirm_message = NULL; dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage(); if(mstorage->ask_user_confirmation) confirm_message = mstorage->ask_user_confirmation(mstorage); + if(confirm_message) { const GtkWidget *win = dt_ui_main_window(darktable.gui->ui); GtkWidget *dialog = gtk_message_dialog_new( GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", confirm_message); + #ifdef GDK_WINDOWING_QUARTZ dt_osx_disallow_fullscreen(dialog); #endif gtk_window_set_title(GTK_WINDOW(dialog), _("export to disk")); - const gint res = gtk_dialog_run(GTK_DIALOG(dialog)); + res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); g_free(confirm_message); confirm_message = NULL; - - if(res != GTK_RESPONSE_YES) - { - return; - } } - // Let's get the max dimension restriction if any... - uint32_t max_width = dt_conf_get_int(CONFIG_PREFIX "width"); - uint32_t max_height = dt_conf_get_int(CONFIG_PREFIX "height"); + return res; +} - const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks"); - const gboolean style_append = dt_conf_get_bool(CONFIG_PREFIX "style_append"); - const char *tmp = dt_conf_get_string_const(CONFIG_PREFIX "style"); - if(tmp) +static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d) +{ + if(_export_confirm_message() == GTK_RESPONSE_NO) + return; + + /** + * do that only in the darkroom as there is nothing to be saved + * when in the lighttable (and it would write over current history stack) + */ + const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager); + if(cv->view(cv) == DT_VIEW_DARKROOM) dt_dev_write_history(darktable.develop); + + /** + * the format_name and storage_name settings which are plug-ins name and not necessary what is displayed on the combobox. + * note that we cannot take directly the combobox entry index as depending on the storage some format are not listed. + */ + const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name"); + const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name"); + const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name)); + const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name)); + + if(format_index == -1) { - g_strlcpy(style, tmp, sizeof(style)); + dt_control_log("invalid format for export selected"); + return; + } + if(storage_index == -1) + { + dt_control_log("invalid storage for export selected"); + return; } - - const dt_colorspaces_color_profile_type_t icc_type = sanitize_colorspaces(dt_conf_get_int(CONFIG_PREFIX "icctype")); - gchar *icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile"); - const dt_iop_color_intent_t icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent"); GList *list = dt_act_on_get_images(TRUE, TRUE, TRUE); - dt_control_export(list, max_width, max_height, format_index, storage_index, TRUE, export_masks, - style, style_append, icc_type, icc_filename, icc_intent, d->metadata_export); - g_free(icc_filename); + dt_imageio_module_storage_t *module_storage = dt_imageio_get_storage_by_index(storage_index); + g_assert(module_storage); + // get shared storage param struct (global sequence counter, one picasa connection etc) + dt_imageio_module_data_t *sdata = module_storage->get_params(module_storage); + if(sdata == NULL) + { + dt_control_log(_("failed to get parameters from storage module `%s', aborting export.."), + module_storage->name(module_storage)); + return; + } + + dt_control_export_t data = { .imgid_list = dt_act_on_get_images(TRUE, TRUE, TRUE), + .max_width = dt_conf_get_int(CONFIG_PREFIX "width"), + .max_height = dt_conf_get_int(CONFIG_PREFIX "height"), + .format_index = format_index, + .storage_index = storage_index, + .high_quality = TRUE, + .export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks"), + .style = _conf_get_style(), + .style_append = dt_conf_get_bool(CONFIG_PREFIX "style_append"), + .icc_type = sanitize_colorspaces(dt_conf_get_int(CONFIG_PREFIX "icctype")), + .icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile"), + .icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent"), + .metadata_export = d->metadata_export, + .module_data = sdata + }; + + dt_control_export(data); + + // tell the storage that we got its params for an export so it can reset itself to a safe state + module_storage->export_dispatched(module_storage); _scale_optim(); gtk_entry_set_text(GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor")); From 7d2c2884d8c21460bd795e86813414e9b699f1aa Mon Sep 17 00:00:00 2001 From: Guillaume Stutin Date: Sun, 23 Jun 2024 00:47:39 +0200 Subject: [PATCH 2/3] Export: reformating and unnest to functions --- src/control/jobs/control_jobs.c | 208 +++++++++++++++++++------------- src/control/jobs/control_jobs.h | 5 + src/libs/export.c | 23 +++- 3 files changed, 147 insertions(+), 89 deletions(-) diff --git a/src/control/jobs/control_jobs.c b/src/control/jobs/control_jobs.c index c457d6d7de33..e7988bfb72c1 100644 --- a/src/control/jobs/control_jobs.c +++ b/src/control/jobs/control_jobs.c @@ -64,6 +64,15 @@ // overall time for a large import. #define PROGRESS_UPDATE_INTERVAL 1 +typedef enum dt_job_type_t +{ + IMPORT = 0, + EXPORT = 1, +}dt_job_type_t; + +const char *job_type_string[2] = { "importing", "exporting" }; + + typedef struct dt_control_datetime_t { GTimeSpan offset; @@ -172,6 +181,22 @@ static void dt_control_image_enumerator_cleanup(void *p) typedef enum {PROGRESS_NONE, PROGRESS_SIMPLE, PROGRESS_CANCELLABLE} progress_type_t; +void _refresh_progress_counter(dt_job_t *job, const int elements, const int index, const dt_job_type_t job_type) +{ + gchar message[64] = { 0 }; + double fraction = 0.0f; + fraction += 1.0f / (double) elements; + + gchar *type = g_strdup(job_type_string[job_type]); + dt_capitalize_label(type); + + snprintf(message, sizeof(message), ngettext("%s %i/%i image", "%s %i/%i images", index), type, index, elements); + dt_control_job_set_progress_message(job, message); + dt_control_job_set_progress(job, fraction); + g_free(type); + g_usleep(100); +} + static dt_job_t *dt_control_generic_images_job_create(dt_job_execute_callback execute, const char *message, int flag, gpointer data, progress_type_t progress_type, gboolean only_visible) @@ -1297,21 +1322,75 @@ static int32_t dt_control_refresh_exif_run(dt_job_t *job) return 0; } +void _get_max_dimensions(uint32_t *w, uint32_t *h, dt_control_export_t *data, dt_imageio_module_data_t *fdata) +{ + uint32_t fw, fh, sw, sh; + fw = fh = sw = sh = 0; + data->module_storage->dimension(data->module_storage, data->module_data, &sw, &sh); + data->module_format->dimension(data->module_format, fdata, &fw, &fh); + + if(sw == 0 || fw == 0) + *w = sw > fw ? sw : fw; + else + *w = sw < fw ? sw : fw; + + if(sh == 0 || fh == 0) + *h = sh > fh ? sh : fh; + else + *h = sh < fh ? sh : fh; +} + +dt_export_metadata_t _export_init_metadata(const dt_control_export_t *data) +{ + dt_export_metadata_t metadata; + metadata.flags = 0; + metadata.list = dt_util_str_to_glist("\1", data->metadata_export); + if(metadata.list) + { + metadata.flags = (int32_t) strtol(metadata.list->data, NULL, 16); + metadata.list = g_list_remove(metadata.list, metadata.list->data); + } + return metadata; +} + +gboolean _init_module_storage(GList **param_index, dt_control_export_t *data) +{ + dt_imageio_module_format_t *module_format = data->module_format; + dt_imageio_module_storage_t *module_storage = data->module_storage; + dt_imageio_module_data_t *module_data = data->module_data; + dt_imageio_module_data_t *fdata = data->module_format->get_params(data->module_format); + + if(module_storage->initialize_store) + { + if(module_storage->initialize_store(module_storage, module_data, &module_format, &fdata, param_index, data->high_quality, data->upscale)) + { + // bail out, something went wrong + return 0; + } + module_format->set_params(module_format, fdata, (int)module_format->params_size(module_format)); + module_storage->set_params(module_storage, module_data, (int)module_storage->params_size(module_storage)); + } + return 1; +} static int32_t _control_export_job_run(dt_job_t *job) { dt_control_image_enumerator_t *params = (dt_control_image_enumerator_t *)dt_control_job_get_params(job); dt_control_export_t *data = (dt_control_export_t *)params->data; - GList *t = params->index; - dt_imageio_module_format_t *module_format = dt_imageio_get_format_by_index(data->format_index); + GList *param_index = params->index; + dt_imageio_module_format_t *module_format = data->module_format; + dt_imageio_module_storage_t *module_storage = data->module_storage; + dt_imageio_module_data_t *module_data = data->module_data; g_assert(module_format); - dt_imageio_module_storage_t *module_storage = dt_imageio_get_storage_by_index(data->storage_index); g_assert(module_storage); - dt_imageio_module_data_t *module_data = data->module_data; + + dt_imageio_module_data_t *fdata = data->module_format->get_params(data->module_format); + dt_export_metadata_t metadata = _export_init_metadata(data); gboolean tag_change = FALSE; fprintf(stdout, "\nEXPORT VAL:\n" + "Total img: %i\n" "First imgID: %i\n" "max_width: %i, max_height: %i\n" "format_index: %i\n" @@ -1325,55 +1404,33 @@ static int32_t _control_export_job_run(dt_job_t *job) "icc_filename: %s\n" "icc_intent: %i\n" "metadata_export: %s\n\n", + data->total, GPOINTER_TO_INT(data->imgid_list->data), data->max_width, data->max_height, data->format_index, data->storage_index, - data->high_quality, data->upscale, data->export_masks, + data->high_quality, data->export_masks, data->style, data->style_append, data->icc_type, data->icc_filename, data->icc_intent, data->metadata_export); // get a thread-safe fdata struct (one jpeg struct per thread etc): - dt_imageio_module_data_t *fdata = module_format->get_params(module_format); - if(module_storage->initialize_store) + const guint total = g_list_length(data->imgid_list); + + + if(!_init_module_storage(¶m_index, data)) { - if(module_storage->initialize_store(module_storage, module_data, &module_format, &fdata, &t, TRUE)) - { - // bail out, something went wrong - goto end; - } - module_format->set_params(module_format, fdata, module_format->params_size(module_format)); - module_storage->set_params(module_storage, module_data, module_storage->params_size(module_storage)); + // bail out, something went wrong + goto end; } - + // Get max dimensions... - uint32_t w, h, fw, fh, sw, sh; - fw = fh = sw = sh = 0; - module_storage->dimension(module_storage, module_data, &sw, &sh); - module_format->dimension(module_format, fdata, &fw, &fh); - - if(sw == 0 || fw == 0) - w = sw > fw ? sw : fw; - else - w = sw < fw ? sw : fw; - - if(sh == 0 || fh == 0) - h = sh > fh ? sh : fh; - else - h = sh < fh ? sh : fh; - - const guint total = g_list_length(t); - if(total > 0) - dt_control_log(ngettext("exporting %d image..", "exporting %d images..", total), total); - else - dt_control_log(_("no image to export")); - - double fraction = 0; + uint32_t img_width, img_height; + _get_max_dimensions(&img_width, &img_height, data, fdata); // set up the fdata struct - fdata->max_width = (data->max_width != 0 && w != 0) ? MIN(w, data->max_width) : MAX(w, data->max_width); - fdata->max_height = (data->max_height != 0 && h != 0) ? MIN(h, data->max_height) : MAX(h, data->max_height); + fdata->max_width = (data->max_width != 0 && img_width != 0) ? MIN(img_width, data->max_width) : MAX(img_width, data->max_width); + fdata->max_height = (data->max_height != 0 && img_height != 0) ? MIN(img_height, data->max_height) : MAX(img_height, data->max_height); g_strlcpy(fdata->style, data->style, sizeof(fdata->style)); fdata->style_append = data->style_append; // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a @@ -1381,27 +1438,14 @@ static int32_t _control_export_job_run(dt_job_t *job) guint tagid = 0, etagid = 0; dt_tag_new("darktable|exported", &etagid); - dt_export_metadata_t metadata; - metadata.flags = 0; - metadata.list = dt_util_str_to_glist("\1", data->metadata_export); - if(metadata.list) - { - metadata.flags = (int32_t) strtol(metadata.list->data, NULL, 16); - metadata.list = g_list_remove(metadata.list, metadata.list->data); - } + int index = 0; - while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED) + for(GList *img = g_list_first(data->imgid_list); img && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED; img = g_list_next(img)) { - const int imgid = GPOINTER_TO_INT(t->data); - t = g_list_next(t); - const guint num = total - g_list_length(t); - - // progress message - char message[512] = { 0 }; - snprintf(message, sizeof(message), _("exporting %d / %d to %s"), num, total, module_storage->name(module_storage)); - // update the message. initialize_store() might have changed the number of images - dt_control_job_set_progress_message(job, message); + const int imgid = GPOINTER_TO_INT(img->data); + _refresh_progress_counter(job, total, index, EXPORT); + // remove 'changed' tag from image if(dt_tag_detach(tagid, imgid, FALSE, FALSE)) tag_change = TRUE; // make sure the 'exported' tag is set on the image @@ -1411,7 +1455,7 @@ static int32_t _control_export_job_run(dt_job_t *job) dt_image_cache_set_export_timestamp(darktable.image_cache, imgid); // check if image still exists: - const dt_image_t *image = dt_image_cache_get(darktable.image_cache, (int32_t)imgid, 'r'); + const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r'); if(image) { char imgfilename[PATH_MAX] = { 0 }; @@ -1427,29 +1471,29 @@ static int32_t _control_export_job_run(dt_job_t *job) else { dt_image_cache_read_release(darktable.image_cache, image); - if(module_storage->store(module_storage, module_data, imgid, module_format, fdata, num, total, TRUE, - data->export_masks, data->icc_type, data->icc_filename, data->icc_intent, - &metadata) != 0) + if(module_storage->store(module_storage, module_data, imgid, module_format, fdata, index, total, data->high_quality, + data->export_masks, data->icc_type, data->icc_filename, data->icc_intent, + &metadata) != 0) dt_control_job_cancel(job); } } - - fraction += 1.0 / total; - if(fraction > 1.0) fraction = 1.0; - dt_control_job_set_progress(job, fraction); + index++; } + g_list_free_full(metadata.list, g_free); - if(module_storage->finalize_store) module_storage->finalize_store(module_storage, module_data); + if(data->module_storage->finalize_store) + data->module_storage->finalize_store(data->module_storage, data->module_data); end: // all threads free their fdata - module_format->free_params(module_format, fdata); + data->module_format->free_params(data->module_format, fdata); // notify the user via the window manager dt_ui_notify_user(); - if(tag_change) DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED); + if(tag_change) + DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED); fprintf(stdout,"end export run\n"); return 0; } @@ -1906,18 +1950,23 @@ static void _control_import_job_cleanup(void *p) dt_control_image_enumerator_cleanup(params); } -static void *_control_import_export_alloc(gboolean is_import) +static void *_control_import_export_alloc(dt_job_type_t job_type) { dt_control_image_enumerator_t *params = dt_control_image_enumerator_alloc(); if(!params) return NULL; - params->data = g_malloc0(sizeof(dt_control_import_t)); + if(job_type == IMPORT) + params->data = g_malloc0(sizeof(dt_control_import_t)); + else if(job_type == EXPORT) + params->data = g_malloc0(sizeof(dt_control_export_t)); + if(!params->data) { - if(is_import) + if(job_type == IMPORT) _control_import_job_cleanup(params); - else + else if(job_type == EXPORT) _control_export_job_cleanup(params); + return NULL; } return params; @@ -1946,7 +1995,7 @@ static dt_job_t *_control_export_job_create(dt_control_export_t data) { dt_job_t *job = dt_control_job_create(&_control_export_job_run, "export"); if(!job) return NULL; - dt_control_image_enumerator_t *params = _control_import_export_alloc(FALSE); + dt_control_image_enumerator_t *params = _control_import_export_alloc(EXPORT); if(!params) { dt_control_job_dispose(job); @@ -2440,17 +2489,6 @@ gboolean _import_image(const GList *img, dt_control_import_t *data, const int in return process_error; } -void _refresh_progress_counter(dt_job_t *job, const int elements, const int index) -{ - gchar message[32] = { 0 }; - double fraction = 0.0f; - fraction += 1.0f / (double) elements; - snprintf(message, sizeof(message), ngettext("importing %i/%i image", "importing %i/%i images", index), index, elements); - dt_control_job_set_progress_message(job, message); - dt_control_job_set_progress(job, fraction); - g_usleep(100); -} - static int32_t _control_import_job_run(dt_job_t *job) { fprintf(stdout,"\n:::Control_Job_run:::\n"); @@ -2463,7 +2501,7 @@ static int32_t _control_import_job_run(dt_job_t *job) { fprintf(stdout, "\nIMG %i.\n", index + 1); - _refresh_progress_counter(job, data->elements, index); + _refresh_progress_counter(job, data->elements, index, IMPORT); if(_import_image(img, data, index, &data->discarded)) fprintf(stderr, "Skipping this one.\n"); diff --git a/src/control/jobs/control_jobs.h b/src/control/jobs/control_jobs.h index fd01a82fa883..800b68adb205 100644 --- a/src/control/jobs/control_jobs.h +++ b/src/control/jobs/control_jobs.h @@ -54,6 +54,8 @@ typedef struct dt_control_export_t int max_height; int format_index; int storage_index; + int total; + gboolean high_quality; gboolean export_masks; gchar *style; @@ -64,6 +66,9 @@ typedef struct dt_control_export_t gchar *metadata_export; + dt_imageio_module_format_t *module_format; + dt_imageio_module_storage_t *module_storage; + /** * Needed since the gui thread resets things like overwrite once the export * is dispatched, but we have to keep that information. diff --git a/src/libs/export.c b/src/libs/export.c index ce8804e5cc40..d1f375ec6368 100644 --- a/src/libs/export.c +++ b/src/libs/export.c @@ -341,8 +341,6 @@ static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d) return; } - GList *list = dt_act_on_get_images(TRUE, TRUE, TRUE); - dt_imageio_module_storage_t *module_storage = dt_imageio_get_storage_by_index(storage_index); g_assert(module_storage); // get shared storage param struct (global sequence counter, one picasa connection etc) @@ -367,13 +365,30 @@ static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d) .icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile"), .icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent"), .metadata_export = d->metadata_export, - .module_data = sdata + + .module_format = dt_imageio_get_format_by_index(format_index), + .module_storage = dt_imageio_get_storage_by_index(storage_index), + .module_data = data.module_storage->get_params(data.module_storage) }; + // get shared storage param struct (global sequence counter, one picasa connection etc) + if(data.total > 0) + dt_control_log(ngettext("exporting %d image..", "exporting %d images..", data.total), data.total); + else + dt_control_log(_("no image to export")); + + + if(data.module_data == NULL) + { + dt_control_log(_("failed to get parameters from storage module `%s', aborting export.."), + data.module_storage->name(data.module_storage)); + return; + } + dt_control_export(data); // tell the storage that we got its params for an export so it can reset itself to a safe state - module_storage->export_dispatched(module_storage); + data.module_storage->export_dispatched(data.module_storage); _scale_optim(); gtk_entry_set_text(GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor")); From 2045baf231d1d5d88118fb96401038241931ff7a Mon Sep 17 00:00:00 2001 From: Guillaume Stutin Date: Fri, 10 Jan 2025 22:04:58 +0100 Subject: [PATCH 3/3] fix rebase errors --- src/control/jobs/control_jobs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/control/jobs/control_jobs.c b/src/control/jobs/control_jobs.c index e7988bfb72c1..1ac6d862b4e2 100644 --- a/src/control/jobs/control_jobs.c +++ b/src/control/jobs/control_jobs.c @@ -1362,7 +1362,7 @@ gboolean _init_module_storage(GList **param_index, dt_control_export_t *data) if(module_storage->initialize_store) { - if(module_storage->initialize_store(module_storage, module_data, &module_format, &fdata, param_index, data->high_quality, data->upscale)) + if(module_storage->initialize_store(module_storage, module_data, &module_format, &fdata, param_index, data->high_quality)) { // bail out, something went wrong return 0; @@ -1396,7 +1396,6 @@ static int32_t _control_export_job_run(dt_job_t *job) "format_index: %i\n" "storage_index: %i\n" "high_quality: %i\n" - "upscale: %i\n" "export_masks: %i\n" "style: %s\n" "style_append: %i\n"