// This is basically the same as dt_image_remove() from common/image.c. // It just does the iteration over all images in the SQL statement void dt_film_remove(const int id) { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update tagxtag set count = count - 1 where " "(id2 in (select tagid from tagged_images where imgid in " "(select id from images where film_id = ?1))) or (id1 in " "(select tagid from tagged_images where imgid in " "(select id from images where film_id = ?1)))", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from tagged_images where imgid in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from history where imgid in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from color_labels where imgid in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from meta_data where id in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from selected_images where imgid in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); while(sqlite3_step(stmt) == SQLITE_ROW) { const uint32_t imgid = sqlite3_column_int(stmt, 0); dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); dt_image_cache_remove (darktable.image_cache, imgid); } sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from images where id in " "(select id from images where film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from film_rolls where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); // dt_control_update_recent_films(); dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_CHANGED); }
void dt_imageio_insert_storage(dt_imageio_module_storage_t* storage) { darktable.imageio->plugins_storage = g_list_insert_sorted(darktable.imageio->plugins_storage, storage, dt_imageio_sort_modules_storage); dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGEIO_STORAGE_CHANGE); }
void dt_control_queue_redraw_center() { dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_REDRAW_CENTER); }
static void delete_button_clicked (GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; int res = GTK_RESPONSE_YES; guint tagid; GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->related; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); // First check how many images are affected by the remove int count = dt_tag_remove(tagid,FALSE); if( count > 0 && dt_conf_get_bool("plugins/lighttable/tagging/ask_before_delete_tag") ) { GtkWidget *dialog; GtkWidget *win = dt_ui_main_window(darktable.gui->ui); gchar *tagname=dt_tag_get_name(tagid); dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, ngettext("do you really want to delete the tag `%s'?\n%d image is assigned this tag!", "do you really want to delete the tag `%s'?\n%d images are assigned this tag!", count), tagname,count); gtk_window_set_title(GTK_WINDOW(dialog), _("delete tag?")); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); free(tagname); } if(res != GTK_RESPONSE_YES) return; GList *tagged_images = NULL; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from tagged_images where tagid=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, tagid); while(sqlite3_step(stmt) == SQLITE_ROW) { tagged_images = g_list_append(tagged_images, GINT_TO_POINTER(sqlite3_column_int(stmt, 0))); } sqlite3_finalize(stmt); dt_tag_remove(tagid,TRUE); GList *list_iter; if((list_iter = g_list_first(tagged_images)) != NULL) { do { dt_image_synch_xmp(GPOINTER_TO_INT(list_iter->data)); } while((list_iter=g_list_next(list_iter)) != NULL); } g_list_free(g_list_first(tagged_images)); update(self, 0); update(self, 1); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
void gui_init(dt_lib_module_t *self) { /* initialize ui widgets */ dt_lib_keywords_t *d = (dt_lib_keywords_t *)g_malloc(sizeof(dt_lib_keywords_t)); memset(d,0,sizeof(dt_lib_keywords_t)); self->data = (void *)d; self->widget = gtk_vbox_new(FALSE, 5); /* Create a new scrolled window, with scrollbars only if needed */ GtkWidget *scrolled_window; scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); /* add the treeview to show hirarchy tags*/ GtkCellRenderer *renderer; d->view = GTK_TREE_VIEW (gtk_tree_view_new()); gtk_widget_set_size_request(GTK_WIDGET(d->view), -1, 300); gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(d->view)); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes(d->view, -1, "", renderer, "text", 0, NULL); gtk_tree_view_set_headers_visible(d->view, FALSE); /* setup dnd source and destination within treeview */ static const GtkTargetEntry dnd_target = { "keywords-reorganize", GTK_TARGET_SAME_WIDGET, 0 }; gtk_tree_view_enable_model_drag_source(d->view, GDK_BUTTON1_MASK, &dnd_target, 1, GDK_ACTION_MOVE); gtk_tree_view_enable_model_drag_dest(d->view, &dnd_target, 1, GDK_ACTION_MOVE); /* setup drag and drop signals */ g_signal_connect(G_OBJECT(d->view),"drag-data-received", G_CALLBACK(_lib_keywords_drag_data_received_callback), self); g_signal_connect(G_OBJECT(d->view),"drag-data-get", G_CALLBACK(_lib_keywords_drag_data_get_callback), self); /* add callback when keyword is activated */ g_signal_connect(G_OBJECT(d->view), "row-activated", G_CALLBACK(_lib_keywords_add_collection_rule), self); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(scrolled_window), TRUE, TRUE, 0); gtk_widget_show_all(GTK_WIDGET(d->view)); dt_control_signal_connect(darktable.signals, DT_SIGNAL_TAG_CHANGED, G_CALLBACK(_lib_tag_gui_update), self); /* raise signal of tags change to refresh keywords tree */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
void dt_styles_update(const char *name, const char *newname, const char *newdescription, GList *filter, int imgid, GList *update) { sqlite3_stmt *stmt; int id = 0; gchar *desc = NULL; id = dt_styles_get_id_by_name(name); if(id == 0) return; desc = dt_styles_get_description(name); if((g_strcmp0(name, newname)) || (g_strcmp0(desc, newdescription))) { DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "UPDATE data.styles SET name=?1, description=?2 WHERE id=?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, newname, -1, SQLITE_STATIC); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, newdescription, -1, SQLITE_STATIC); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id); sqlite3_step(stmt); sqlite3_finalize(stmt); } if(filter) { GList *list = filter; char tmp[64]; char include[2048] = { 0 }; g_strlcat(include, "num NOT IN (", sizeof(include)); do { if(list != g_list_first(list)) g_strlcat(include, ",", sizeof(include)); snprintf(tmp, sizeof(tmp), "%d", GPOINTER_TO_INT(list->data)); g_strlcat(include, tmp, sizeof(include)); } while((list = g_list_next(list))); g_strlcat(include, ")", sizeof(include)); char query[4096] = { 0 }; snprintf(query, sizeof(query), "DELETE FROM data.style_items WHERE styleid=?1 AND %s", include); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); } _dt_style_update_from_image(id, imgid, filter, update); _dt_style_cleanup_multi_instance(id); /* backup style to disk */ char stylesdir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(stylesdir, sizeof(stylesdir)); g_strlcat(stylesdir, "/styles", sizeof(stylesdir)); g_mkdir_with_parents(stylesdir, 00755); dt_styles_save_to_file(newname, stylesdir, TRUE); /* delete old accelerator and create a new one */ // TODO: should better use dt_accel_rename_global() to keep the old accel_key untouched, but it seems to be // buggy if(g_strcmp0(name, newname)) { char tmp_accel[1024]; snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), name); dt_accel_deregister_global(tmp_accel); gchar *tmp_name = g_strdup(newname); // freed by _destroy_style_shortcut_callback snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), newname); dt_accel_register_global(tmp_accel, 0, 0); GClosure *closure; closure = g_cclosure_new(G_CALLBACK(_apply_style_shortcut_callback), tmp_name, _destroy_style_shortcut_callback); dt_accel_connect_global(tmp_accel, closure); } dt_control_signal_raise(darktable.signals, DT_SIGNAL_STYLE_CHANGED); g_free(desc); }
void dt_styles_apply_to_image(const char *name, gboolean duplicate, int32_t imgid) { int id = 0; sqlite3_stmt *stmt; int32_t newimgid; if((id = dt_styles_get_id_by_name(name)) != 0) { /* check if we should make a duplicate before applying style */ if(duplicate) { newimgid = dt_image_duplicate(imgid); if(newimgid != -1) dt_history_copy_and_paste_on_image(imgid, newimgid, FALSE, NULL); } else newimgid = imgid; /* merge onto history stack, let's find history offest in destination image */ /* first trim the stack to get rid of whatever is above the selected entry */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid = ?1 AND num >= (SELECT history_end " "FROM main.images WHERE id = imgid)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); sqlite3_step(stmt); sqlite3_finalize(stmt); /* in sqlite ROWID starts at 1, while our num column starts at 0 */ int32_t offs = -1; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT IFNULL(MAX(num), -1) FROM main.history WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); if(sqlite3_step(stmt) == SQLITE_ROW) offs = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); /* delete all items from the temp styles_items, this table is used only to get a ROWNUM of the results */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.style_items", NULL, NULL, NULL); /* copy history items from styles onto temp table */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO memory.style_items SELECT * FROM " "data.style_items WHERE styleid=?1 ORDER BY " "multi_priority DESC", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); /* copy the style items into the history */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO main.history " "(imgid,num,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name) SELECT " "?1,?2+rowid,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name FROM memory.style_items", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs); sqlite3_step(stmt); sqlite3_finalize(stmt); /* always make the whole stack active */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "UPDATE main.images SET history_end = (SELECT MAX(num) + 1 FROM main.history " "WHERE imgid = ?1) WHERE id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); sqlite3_step(stmt); sqlite3_finalize(stmt); /* add tag */ guint tagid = 0; gchar ntag[512] = { 0 }; g_snprintf(ntag, sizeof(ntag), "darktable|style|%s", name); if(dt_tag_new(ntag, &tagid)) dt_tag_attach(tagid, newimgid); if(dt_tag_new("darktable|changed", &tagid)) dt_tag_attach(tagid, newimgid); /* if current image in develop reload history */ if(dt_dev_is_current_image(darktable.develop, newimgid)) { dt_dev_reload_history_items(darktable.develop); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); } /* update xmp file */ dt_image_synch_xmp(newimgid); /* remove old obsolete thumbnails */ dt_mipmap_cache_remove(darktable.mipmap_cache, newimgid); /* if we have created a duplicate, reset collected images */ if(duplicate) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); /* redraw center view to update visible mipmaps */ dt_control_queue_redraw_center(); } }
void dt_film_import1(dt_film_t *film) { gboolean recursive = dt_conf_get_bool("ui_last/import_recursive"); /* first of all gather all images to import */ GList *images = NULL; images = _film_recursive_get_files(film->dirname, recursive, &images); if(g_list_length(images) == 0) { dt_control_log(_("no supported images were found to be imported")); return; } /* we got ourself a list of images, lets sort and start import */ images = g_list_sort(images,(GCompareFunc)_film_filename_cmp); /* let's start import of images */ gchar message[512] = {0}; double fraction = 0; uint32_t total = g_list_length(images); g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image","importing %d images", total), total); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); /* loop thru the images and import to current film roll */ dt_film_t *cfr = film; GList *image = g_list_first(images); do { gchar *cdn = g_path_get_dirname((const gchar *)image->data); /* check if we need to initialize a new filmroll */ if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0) { #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the jsut imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif /* cleanup previously imported filmroll*/ if(cfr && cfr!=film) { if(dt_film_is_empty(cfr->id)) { dt_film_remove(cfr->id); } dt_film_cleanup(cfr); g_free(cfr); cfr = NULL; } /* initialize and create a new film to import to */ cfr = g_malloc(sizeof(dt_film_t)); dt_film_init(cfr); dt_film_new(cfr, cdn); } /* import image */ dt_image_import(cfr->id, (const gchar *)image->data, FALSE); fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } while( (image = g_list_next(image)) != NULL); // only redraw at the end, to not spam the cpu with exposure events dt_control_queue_redraw_center(); dt_control_signal_raise(darktable.signals,DT_SIGNAL_TAG_CHANGED); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED,film->id); #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the just imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif }
// This is basically the same as dt_image_remove() from common/image.c. // It just does the iteration over all images in the SQL statement void dt_film_remove(const int id) { // only allowed if local copies have their original accessible sqlite3_stmt *stmt; gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE film_id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if(!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if(!remove_ok) { dt_control_log(_("cannot remove film roll having local copies with non accessible originals")); return; } DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.tagged_images WHERE imgid IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.masks_history WHERE imgid IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.color_labels WHERE imgid IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.meta_data WHERE id IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.selected_images WHERE imgid IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE film_id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); while(sqlite3_step(stmt) == SQLITE_ROW) { const uint32_t imgid = sqlite3_column_int(stmt, 0); dt_image_local_copy_reset(imgid); dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); dt_image_cache_remove(darktable.image_cache, imgid); } sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.images WHERE id IN " "(SELECT id FROM main.images WHERE film_id = ?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.film_rolls WHERE id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step(stmt); sqlite3_finalize(stmt); // dt_control_update_recent_films(); dt_tag_update_used_tags(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); }
uint32_t dt_image_import(const int32_t film_id, const char *filename, gboolean override_ignore_jpegs) { if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) return 0; const char *cc = filename + strlen(filename); for(; *cc!='.'&&cc>filename; cc--); if(!strcmp(cc, ".dt")) return 0; if(!strcmp(cc, ".dttags")) return 0; if(!strcmp(cc, ".xmp")) return 0; char *ext = g_ascii_strdown(cc+1, -1); if(override_ignore_jpegs == FALSE && (!strcmp(ext, "jpg") || !strcmp(ext, "jpeg")) && dt_conf_get_bool("ui_last/import_ignore_jpegs")) return 0; int supported = 0; char **extensions = g_strsplit(dt_supported_extensions, ",", 100); for(char **i=extensions; *i!=NULL; i++) if(!strcmp(ext, *i)) { supported = 1; break; } g_strfreev(extensions); if(!supported) { g_free(ext); return 0; } int rc; uint32_t id = 0; // select from images; if found => return gchar *imgfname; imgfname = g_path_get_basename((const gchar*)filename); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) { id = sqlite3_column_int(stmt, 0); g_free(imgfname); sqlite3_finalize(stmt); g_free(ext); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->flags &= ~DT_IMAGE_REMOVE; dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); return id; } sqlite3_finalize(stmt); // also need to set the no-legacy bit, to make sure we get the right presets (new ones) uint32_t flags = dt_conf_get_int("ui_last/import_initial_rating"); if(flags > 5) { flags = 1; dt_conf_set_int("ui_last/import_initial_rating", 1); } flags |= DT_IMAGE_NO_LEGACY_PRESETS; // insert dummy image entry in database DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into images (id, film_id, filename, caption, description, " "license, sha1sum, flags) values (null, ?1, ?2, '', '', '', '', ?3)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, flags); rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) id = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // Try to find out if this should be grouped already. gchar *basename = g_strdup(imgfname); gchar *cc2 = basename + strlen(basename); for(; *cc2!='.'&&cc2>basename; cc2--); *cc2='\0'; gchar *sql_pattern = g_strconcat(basename, ".%", NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select group_id from images where film_id = ?1 and filename like ?2 and id != ?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, sql_pattern, -1, SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id); int group_id; if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0); else group_id = id; sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update images set group_id = ?1 where id = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id); sqlite3_step(stmt); sqlite3_finalize(stmt); // printf("[image_import] importing `%s' to img id %d\n", imgfname, id); // lock as shortly as possible: const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->group_id = group_id; // read dttags and exif for database queries! (void) dt_exif_read(img, filename); char dtfilename[DT_MAX_PATH_LEN]; g_strlcpy(dtfilename, filename, DT_MAX_PATH_LEN); dt_image_path_append_version(id, dtfilename, DT_MAX_PATH_LEN); char *c = dtfilename + strlen(dtfilename); sprintf(c, ".xmp"); (void)dt_exif_xmp_read(img, dtfilename, 0); // write through to db, but not to xmp. dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); // add a tag with the file extension guint tagid = 0; char tagname[512]; snprintf(tagname, 512, "darktable|format|%s", ext); g_free(ext); dt_tag_new(tagname, &tagid); dt_tag_attach(tagid,id); // Search for sidecar files and import them if found. glob_t *globbuf = g_malloc(sizeof(glob_t)); // Add version wildcard gchar *fname = g_strdup(filename); gchar pattern[DT_MAX_PATH_LEN]; g_snprintf(pattern, DT_MAX_PATH_LEN, "%s", filename); char *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + DT_MAX_PATH_LEN - c1, "_*"); char *c2 = fname + strlen(fname); while(*c2 != '.' && c2 > fname) c2--; snprintf(c1+2, pattern + DT_MAX_PATH_LEN - c1 - 2, "%s.xmp", c2); if (!glob(pattern, 0, NULL, globbuf)) { for (int i=0; i < globbuf->gl_pathc; i++) { int newid = -1; newid = dt_image_duplicate(id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, newid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); (void)dt_exif_xmp_read(img, globbuf->gl_pathv[i], 0); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); } globfree(globbuf); } g_free(imgfname); g_free(fname); g_free(basename); g_free(sql_pattern); g_free(globbuf); dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_IMPORT,id); return id; }
static gboolean async_redraw(gpointer data) { dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); // just for good measure dt_control_queue_redraw(); return false; }
int dt_view_manager_switch (dt_view_manager_t *vm, int k) { // Before switching views, restore accelerators if disabled if(!darktable.control->key_accelerators_on) dt_control_key_accelerators_on(darktable.control); // destroy old module list int error = 0; dt_view_t *v = vm->view + vm->current_view; /* Special case when entering nothing (just before leaving dt) */ if ( k==DT_MODE_NONE ) { /* iterator plugins and cleanup plugins in current view */ GList *plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if (!plugin->views) fprintf(stderr,"module %s doesnt have views flags\n",plugin->name()); /* does this module belong to current view ?*/ if (plugin->views() & v->view(v) ) { plugin->gui_cleanup(plugin); dt_accel_disconnect_list(plugin->accel_closures); plugin->accel_closures = NULL; } /* get next plugin */ plugins = g_list_previous(plugins); } /* leave the current view*/ if(vm->current_view >= 0 && v->leave) v->leave(v); /* remove all widets in all containers */ for(int l=0;l<DT_UI_CONTAINER_SIZE;l++) dt_ui_container_clear(darktable.gui->ui, l); vm->current_view = -1 ; return 0 ; } int newv = vm->current_view; if (k < vm->num_views) newv = k; dt_view_t *nv = vm->view + newv; if (nv->try_enter) error = nv->try_enter(nv); if (!error) { GList *plugins; /* cleanup current view before initialization of new */ if (vm->current_view >=0) { /* leave current view */ if(v->leave) v->leave(v); dt_accel_disconnect_list(v->accel_closures); v->accel_closures = NULL; /* iterator plugins and cleanup plugins in current view */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if (!plugin->views) fprintf(stderr,"module %s doesnt have views flags\n",plugin->name()); /* does this module belong to current view ?*/ if (plugin->views() & v->view(v) ) { plugin->gui_cleanup(plugin); dt_accel_disconnect_list(plugin->accel_closures); plugin->accel_closures = NULL; } /* get next plugin */ plugins = g_list_previous(plugins); } /* remove all widets in all containers */ for(int l=0;l<DT_UI_CONTAINER_SIZE;l++) dt_ui_container_clear(darktable.gui->ui, l); } /* change current view to the new view */ vm->current_view = newv; /* restore visible stat of panels for the new view */ dt_ui_restore_panels(darktable.gui->ui); /* lets add plugins related to new view into panels */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if( plugin->views() & nv->view(v) ) { /* module should be in this view, lets initialize */ plugin->gui_init(plugin); /* try get the module expander */ GtkWidget *w = NULL; w = dt_lib_gui_get_expander(plugin); if(plugin->connect_key_accels) plugin->connect_key_accels(plugin); dt_lib_connect_common_accels(plugin); /* if we dont got an expander lets add the widget */ if (!w) w = plugin->widget; /* add module to it's container */ dt_ui_container_add_widget(darktable.gui->ui, plugin->container(), w); } /* lets get next plugin */ plugins = g_list_previous(plugins); } /* hide/show modules as last config */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if(plugin->views() & nv->view(v)) { /* set expanded if last mode was that */ char var[1024]; gboolean expanded = FALSE; gboolean visible = dt_lib_is_visible(plugin); if (plugin->expandable()) { snprintf(var, 1024, "plugins/lighttable/%s/expanded", plugin->plugin_name); expanded = dt_conf_get_bool(var); /* show expander if visible */ if(visible) { gtk_widget_show_all(GTK_WIDGET(plugin->expander)); // gtk_widget_show_all(plugin->widget); } else { gtk_widget_hide(GTK_WIDGET(plugin->expander)); // gtk_widget_hide_all(plugin->widget); } dt_lib_gui_set_expanded(plugin, expanded); } else { /* show/hide plugin widget depending on expanded flag or if plugin not is expandeable() */ if(visible) gtk_widget_show_all(plugin->widget); else gtk_widget_hide_all(plugin->widget); } } /* lets get next plugin */ plugins = g_list_previous(plugins); } /* enter view. crucially, do this before initing the plugins below, as e.g. modulegroups requires the dr stuff to be inited. */ if(newv >= 0 && nv->enter) nv->enter(nv); if(newv >= 0 && nv->connect_key_accels) nv->connect_key_accels(nv); /* raise view changed signal */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED); /* add endmarkers to left and right center containers */ GtkWidget *endmarker = gtk_drawing_area_new(); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_LEFT_CENTER, endmarker); g_signal_connect (G_OBJECT (endmarker), "expose-event", G_CALLBACK (dt_control_expose_endmarker), 0); gtk_widget_set_size_request(endmarker, -1, 50); gtk_widget_show(endmarker); endmarker = gtk_drawing_area_new(); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, endmarker); g_signal_connect (G_OBJECT (endmarker), "expose-event", G_CALLBACK (dt_control_expose_endmarker), (gpointer)1); gtk_widget_set_size_request(endmarker, -1, 50); gtk_widget_show(endmarker); } return error; }
int32_t dt_control_delete_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(id) from images where filename in (select filename from images where id = ?1) and film_id in (select film_id from images where id = ?1)", -1, &stmt, NULL); while(t) { imgid = GPOINTER_TO_INT(t->data); char filename[DT_MAX_PATH_LEN]; gboolean from_cache = FALSE; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); int duplicates = 0; DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); // remove from disk: if(duplicates == 1) { // there are no further duplicates so we can remove the source data file (void)g_unlink(filename); dt_image_remove(imgid); // all sidecar files - including left-overs - can be deleted; // left-overs can result when previously duplicates have been REMOVED; // no need to keep them as the source data file is gone. const int len = DT_MAX_PATH_LEN + 30; gchar pattern[len]; // NULL terminated list of glob patterns; should include "" and can be extended if needed static const gchar *glob_patterns[] = { "", "_[0-9][0-9]", "_[0-9][0-9][0-9]", "_[0-9][0-9][0-9][0-9]", NULL }; const gchar **glob_pattern = glob_patterns; GList *files = NULL; while(*glob_pattern) { snprintf(pattern, len, "%s", filename); gchar *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + len - c1, "%s", *glob_pattern); const gchar *c2 = filename + strlen(filename); while(*c2 != '.' && c2 > filename) c2--; snprintf(c1+strlen(*glob_pattern), pattern + len - c1 - strlen(*glob_pattern), "%s.xmp", c2); #ifdef __WIN32__ WIN32_FIND_DATA data; HANDLE handle = FindFirstFile(pattern, &data); if(handle != INVALID_HANDLE_VALUE) { do files = g_list_append(files, g_strdup(data.cFileName)); while(FindNextFile(handle, &data)); } #else glob_t globbuf; if(!glob(pattern, 0, NULL, &globbuf)) { for(size_t i=0; i < globbuf.gl_pathc; i++) files = g_list_append(files, g_strdup(globbuf.gl_pathv[i])); globfree(&globbuf); } #endif glob_pattern++; } GList *file_iter = g_list_first(files); while(file_iter != NULL) { (void)g_unlink(file_iter->data); file_iter = g_list_next(file_iter); } g_list_free_full(files, g_free); } else { // don't remove the actual source data if there are further duplicates using it; // just delete the xmp file of the duplicate selected. dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); dt_image_remove(imgid); (void)g_unlink(filename); } t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } sqlite3_finalize(stmt); char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } g_list_free(list); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt = NULL; // check that we can safely remove the image gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (?2) AND flags&?1=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if (!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if (!remove_ok) { dt_control_log(_("cannot remove local copy when the original file is not accessible.")); dt_control_backgroundjobs_destroy(darktable.control, jid); free(imgs); return 0; } // update remove status _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); while(t) { imgid = GPOINTER_TO_INT(t->data); dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }
void dt_dev_add_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable) { if(darktable.gui->reset) return; dt_pthread_mutex_lock(&dev->history_mutex); if(dev->gui_attached) { GList *history = g_list_nth (dev->history, dev->history_end); while(history) { GList *next = g_list_next(history); dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data); // printf("removing obsoleted history item: %s\n", hist->module->op); free(hist->params); free(hist->blend_params); free(history->data); dev->history = g_list_delete_link(dev->history, history); history = next; } history = g_list_nth(dev->history, dev->history_end-1); if(!history || module->instance != ((dt_dev_history_item_t *)history->data)->module->instance) { // new operation, push new item // printf("adding new history item %d - %s\n", dev->history_end, module->op); // if(history) printf("because item %d - %s is different operation.\n", dev->history_end-1, ((dt_dev_history_item_t *)history->data)->module->op); dev->history_end++; dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t)); if (enable) { module->enabled = TRUE; if(module->off) { darktable.gui->reset = 1; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), module->enabled); darktable.gui->reset = 0; } } hist->enabled = module->enabled; hist->module = module; hist->params = malloc(module->params_size); /* allocate and set hist blend_params */ hist->blend_params = malloc(sizeof(dt_develop_blend_params_t)); memset(hist->blend_params, 0, sizeof(dt_develop_blend_params_t)); memcpy(hist->params, module->params, module->params_size); if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t)); dev->history = g_list_append(dev->history, hist); dev->pipe->changed |= DT_DEV_PIPE_SYNCH; dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // topology remains, as modules are fixed for now. } else { // same operation, change params // printf("changing same history item %d - %s\n", dev->history_end-1, module->op); dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data; memcpy(hist->params, module->params, module->params_size); if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING) memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t)); // if the user changed stuff and the module is still not enabled, do it: if(!hist->enabled && !module->enabled) { module->enabled = 1; if(module->off) { darktable.gui->reset = 1; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), module->enabled); darktable.gui->reset = 0; } } hist->enabled = module->enabled; dev->pipe->changed |= DT_DEV_PIPE_TOP_CHANGED; dev->preview_pipe->changed |= DT_DEV_PIPE_TOP_CHANGED; } } #if 0 { // debug: printf("remaining %d history items:\n", dev->history_end); GList *history = dev->history; int i = 0; while(history) { dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data); printf("%d %s\n", i, hist->module->op); history = g_list_next(history); i++; } } #endif /* invalidate image data*/ dt_similarity_image_dirty(dev->image_storage.id); // invalidate buffers and force redraw of darkroom dt_dev_invalidate_all(dev); dt_pthread_mutex_unlock(&dev->history_mutex); if(dev->gui_attached) { /* signal that history has changed */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_HISTORY_CHANGE); /* redraw */ dt_control_queue_redraw_center(); } }
int dt_lua_do_chunk(lua_State *L,int nargs,int nresults) { lua_State * new_thread = lua_newthread(L); lua_insert(L,-(nargs+2)); lua_xmove(L,new_thread,nargs+1); int thread_result = lua_resume(new_thread, L,nargs); do { switch(thread_result) { case LUA_OK: if(darktable.gui!=NULL) { dt_lua_unlock(false); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); // just for good measure dt_control_queue_redraw(); dt_lua_lock(); } if(nresults !=LUA_MULTRET) { lua_settop(new_thread,nresults); } int result= lua_gettop(new_thread); lua_pop(L,1); lua_xmove(new_thread,L,result); return result; case LUA_YIELD: { if(lua_gettop(new_thread) == 0) { lua_pushstring(new_thread,"no parameter passed to yield"); goto error; } yield_type type; lua_pushcfunction(new_thread,protected_to_yield); lua_pushvalue(new_thread,1); thread_result = lua_pcall(new_thread,1,1,0); if(thread_result!= LUA_OK) goto error; type = lua_tointeger(new_thread,-1); lua_pop(new_thread,1); switch(type) { case WAIT_MS: { lua_pushcfunction(new_thread,protected_to_int); lua_pushvalue(new_thread,2); thread_result = lua_pcall(new_thread,1,1,0); if(thread_result!= LUA_OK) goto error; int wait_time = lua_tointeger(new_thread,-1); lua_pop(new_thread,1); dt_lua_unlock(false); g_usleep(wait_time*1000); dt_lua_lock(); thread_result = lua_resume(new_thread,L,0); break; } case FILE_READABLE: { lua_pushcfunction(new_thread,protected_to_userdata); lua_pushvalue(new_thread,2); lua_pushstring(new_thread,LUA_FILEHANDLE); thread_result = lua_pcall(new_thread,2,1,0); if(thread_result!= LUA_OK) goto error; luaL_Stream * stream = lua_touserdata(new_thread,-1); lua_pop(new_thread,1); int myfileno = fileno(stream->f); fd_set fdset; FD_ZERO(&fdset); FD_SET(myfileno,&fdset); dt_lua_unlock(false); select(myfileno+1,&fdset,NULL,NULL,0); dt_lua_lock(); thread_result = lua_resume(new_thread,L,0); break; } case RUN_COMMAND: { lua_pushcfunction(new_thread,protected_to_string); lua_pushvalue(new_thread,2); thread_result = lua_pcall(new_thread,1,1,0); if(thread_result!= LUA_OK) goto error; const char* command = lua_tostring(new_thread,-1); lua_pop(L,1); dt_lua_unlock(false); int result = system(command); dt_lua_lock(); lua_pushinteger(L,result); thread_result = lua_resume(new_thread,L,1); break; } default: lua_pushstring(new_thread,"program error, shouldn't happen"); goto error; } break; } default: goto error; } }while(true); error: if(darktable.unmuted & DT_DEBUG_LUA) { dt_print(DT_DEBUG_LUA,"LUA ERROR : %s", lua_tostring(new_thread,-1)); luaL_traceback(L,new_thread,"",0); const char * error = lua_tostring(L,-1); dt_print(DT_DEBUG_LUA,error); lua_pop(L,1); } lua_pop(L,1); if(nresults !=LUA_MULTRET) { for(int i= 0 ; i < nresults; i++) { lua_pushnil(L); } return nresults; } return 0; }
void dt_dev_read_history(dt_develop_t *dev) { if(!dev->image_storage.id) return; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version from history where imgid = ?1 order by num", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->image_storage.id); dev->history_end = 0; while(sqlite3_step(stmt) == SQLITE_ROW) { // db record: // 0-img, 1-num, 2-module_instance, 3-operation char, 4-params blob, 5-enabled, 6-blend_params, 7-blendop_version dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t)); hist->enabled = sqlite3_column_int(stmt, 5); GList *modules = dev->iop; const char *opname = (const char *)sqlite3_column_text(stmt, 3); if(!opname) { fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n", dev->image_storage.filename); free(hist); continue; } hist->module = NULL; while(opname && modules) { dt_iop_module_t *module = (dt_iop_module_t *)modules->data; if(!strcmp(module->op, opname)) { hist->module = module; break; } modules = g_list_next(modules); } if(!hist->module) { fprintf(stderr, "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n", opname, dev->image_storage.filename); free(hist); continue; } int modversion = sqlite3_column_int(stmt, 2); assert(strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op) == 0); hist->params = malloc(hist->module->params_size); hist->blend_params = malloc(sizeof(dt_develop_blend_params_t)); if(hist->module->version() != modversion || hist->module->params_size != sqlite3_column_bytes(stmt, 4) || strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op)) { if(!hist->module->legacy_params || hist->module->legacy_params(hist->module, sqlite3_column_blob(stmt, 4), labs(modversion), hist->params, labs(hist->module->version()))) { free(hist->params); free(hist->blend_params); fprintf(stderr, "[dev_read_history] module `%s' version mismatch: history is %d, dt %d.\n", hist->module->op, modversion, hist->module->version()); const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename); while(fname > dev->image_storage.filename && *fname != '/') fname --; if(fname > dev->image_storage.filename) fname++; dt_control_log(_("%s: module `%s' version mismatch: %d != %d"), fname, hist->module->op, hist->module->version(), modversion); free(hist); continue; } } else { memcpy(hist->params, sqlite3_column_blob(stmt, 4), hist->module->params_size); } const void *blendop_params = sqlite3_column_blob(stmt, 6); int bl_length = sqlite3_column_bytes(stmt, 6); int blendop_version = sqlite3_column_int(stmt, 7); if (blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t))) { memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t)); } else if (blendop_params && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params, dt_develop_blend_version(), bl_length) == 0) { // do nothing } else { memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t)); } // memcpy(hist->module->params, hist->params, hist->module->params_size); // hist->module->enabled = hist->enabled; // printf("[dev read history] img %d number %d for operation %d - %s params %f %f\n", sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1), instance, hist->module->op, *(float *)hist->params, *(((float*)hist->params)+1)); dev->history = g_list_append(dev->history, hist); dev->history_end ++; } if(dev->gui_attached) { dev->pipe->changed |= DT_DEV_PIPE_SYNCH; dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now. dt_dev_invalidate_all(dev); /* signal history changed */ dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE); } sqlite3_finalize (stmt); }
void dt_mipmap_cache_read_get( dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const uint32_t imgid, const dt_mipmap_size_t mip, const dt_mipmap_get_flags_t flags) { const uint32_t key = get_key(imgid, mip); if(flags == DT_MIPMAP_TESTLOCK) { // simple case: only get and lock if it's there. struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_testget(&cache->mip[mip].cache, key); if(dsc) { buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; // skip to next 8-byte alignment, for sse buffers. buf->buf = (uint8_t *)(dsc+1); } else { // set to NULL if failed. buf->width = buf->height = 0; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->buf = NULL; } } else if(flags == DT_MIPMAP_PREFETCH) { // and opposite: prefetch without locking if(mip > DT_MIPMAP_FULL || mip < DT_MIPMAP_0) return; dt_job_t j; dt_image_load_job_init(&j, imgid, mip); // if the job already exists, make it high-priority, if not, add it: if(dt_control_revive_job(darktable.control, &j) < 0) dt_control_add_job(darktable.control, &j); } else if(flags == DT_MIPMAP_BLOCKING) { // simple case: blocking get struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_get(&cache->mip[mip].cache, key); if(!dsc) { // should never happen for anything but full images which have been moved. assert(mip == DT_MIPMAP_FULL || mip == DT_MIPMAP_F); // fprintf(stderr, "[mipmap cache get] no data in cache for imgid %u size %d!\n", imgid, mip); // sorry guys, no image for you :( buf->width = buf->height = 0; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->buf = NULL; } else { // fprintf(stderr, "[mipmap cache get] found data in cache for imgid %u size %d\n", imgid, mip); // uninitialized? //assert(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE || dsc->size == 0); if(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE) { __sync_fetch_and_add (&(cache->mip[mip].stats_fetches), 1); // fprintf(stderr, "[mipmap cache get] now initializing buffer for img %u mip %d!\n", imgid, mip); // we're write locked here, as requested by the alloc callback. // now fill it with data: if(mip == DT_MIPMAP_FULL) { // load the image: // make sure we access the r/w lock as shortly as possible! dt_image_t buffered_image; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); buffered_image = *cimg; // dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); // dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, cimg); char filename[DT_MAX_PATH_LEN]; gboolean from_cache = TRUE; dt_image_full_path(buffered_image.id, filename, DT_MAX_PATH_LEN, &from_cache); dt_mipmap_cache_allocator_t a = (dt_mipmap_cache_allocator_t)&dsc; struct dt_mipmap_buffer_dsc* prvdsc = dsc; dt_imageio_retval_t ret = dt_imageio_open(&buffered_image, filename, a); if(dsc != prvdsc) { // fprintf(stderr, "[mipmap cache] realloc %p\n", data); // write back to cache, too. // in case something went wrong, still keep the buffer and return it to the hashtable // so we don't produce mem leaks or unnecessary mem fragmentation. dt_cache_realloc(&cache->mip[mip].cache, key, 1, (void*)dsc); } if(ret != DT_IMAGEIO_OK) { // fprintf(stderr, "[mipmap read get] error loading image: %d\n", ret); // // we can only return a zero dimension buffer if the buffer has been allocated. // in case dsc couldn't be allocated and points to the static buffer, it contains // a dead image already. if((void *)dsc != (void *)dt_mipmap_cache_static_dead_image) dsc->width = dsc->height = 0; } else { // swap back new image data: cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); *img = buffered_image; // fprintf(stderr, "[mipmap read get] initializing full buffer img %u with %u %u -> %d %d (%p)\n", imgid, data[0], data[1], img->width, img->height, data); // don't write xmp for this (we only changed db stuff): dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); } } else if(mip == DT_MIPMAP_F) { _init_f((float *)(dsc+1), &dsc->width, &dsc->height, imgid); } else { // 8-bit thumbs, possibly need to be compressed: if(cache->compression_type) { // get per-thread temporary storage without malloc from a separate cache: const int key = dt_control_get_threadid(); // const void *cbuf = dt_cache_read_get(&cache->scratchmem.cache, key); uint8_t *scratchmem = (uint8_t *)dt_cache_write_get(&cache->scratchmem.cache, key); _init_8(scratchmem, &dsc->width, &dsc->height, imgid, mip); buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; buf->buf = (uint8_t *)(dsc+1); dt_mipmap_cache_compress(buf, scratchmem); dt_cache_write_release(&cache->scratchmem.cache, key); dt_cache_read_release(&cache->scratchmem.cache, key); } else { _init_8((uint8_t *)(dsc+1), &dsc->width, &dsc->height, imgid, mip); } } dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE; // drop the write lock dt_cache_write_release(&cache->mip[mip].cache, key); /* raise signal that mipmaps has been flushed to cache */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED); } buf->width = dsc->width; buf->height = dsc->height; buf->imgid = imgid; buf->size = mip; buf->buf = (uint8_t *)(dsc+1); if(dsc->width == 0 || dsc->height == 0) { // fprintf(stderr, "[mipmap cache get] got a zero-sized image for img %u mip %d!\n", imgid, mip); if(mip < DT_MIPMAP_F) dead_image_8(buf); else if(mip == DT_MIPMAP_F) dead_image_f(buf); else buf->buf = NULL; // full images with NULL buffer have to be handled, indicates `missing image' } } } else if(flags == DT_MIPMAP_BEST_EFFORT) { __sync_fetch_and_add (&(cache->mip[mip].stats_requests), 1); // best-effort, might also return NULL. // never decrease mip level for float buffer or full image: dt_mipmap_size_t min_mip = (mip >= DT_MIPMAP_F) ? mip : DT_MIPMAP_0; for(int k=mip; k>=min_mip && k>=0; k--) { // already loaded? dt_mipmap_cache_read_get(cache, buf, imgid, k, DT_MIPMAP_TESTLOCK); if(buf->buf && buf->width > 0 && buf->height > 0) { if(mip != k) __sync_fetch_and_add (&(cache->mip[k].stats_standin), 1); return; } // didn't succeed the first time? prefetch for later! if(mip == k) { __sync_fetch_and_add (&(cache->mip[mip].stats_near_match), 1); dt_mipmap_cache_read_get(cache, buf, imgid, mip, DT_MIPMAP_PREFETCH); } } __sync_fetch_and_add (&(cache->mip[mip].stats_misses), 1); // fprintf(stderr, "[mipmap cache get] image not found in cache: imgid %u mip %d!\n", imgid, mip); // nothing found :( buf->buf = NULL; buf->imgid = 0; buf->size = DT_MIPMAP_NONE; buf->width = buf->height = 0; } }
gboolean dt_styles_create_from_image(const char *name, const char *description, int32_t imgid, GList *filter) { int id = 0; sqlite3_stmt *stmt; /* first create the style header */ if(!dt_styles_create_style_header(name, description)) return FALSE; if((id = dt_styles_get_id_by_name(name)) != 0) { /* create the style_items from source image history stack */ if(filter) { GList *list = filter; char tmp[64]; char include[2048] = { 0 }; g_strlcat(include, "num IN (", sizeof(include)); do { if(list != g_list_first(list)) g_strlcat(include, ",", sizeof(include)); snprintf(tmp, sizeof(tmp), "%d", GPOINTER_TO_INT(list->data)); g_strlcat(include, tmp, sizeof(include)); } while((list = g_list_next(list))); g_strlcat(include, ")", sizeof(include)); char query[4096] = { 0 }; snprintf(query, sizeof(query), "INSERT INTO data.style_items " "(styleid,num,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name) SELECT ?1, " "num,module,operation,op_params,enabled,blendop_params,blendop_version," "multi_priority,multi_name FROM main.history WHERE imgid=?2 AND %s", include); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL); } else DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO data.style_items " "(styleid,num,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name) SELECT ?1, " "num,module,operation,op_params,enabled,blendop_params,blendop_version," "multi_priority,multi_name FROM main.history WHERE imgid=?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); _dt_style_cleanup_multi_instance(id); /* backup style to disk */ char stylesdir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(stylesdir, sizeof(stylesdir)); g_strlcat(stylesdir, "/styles", sizeof(stylesdir)); g_mkdir_with_parents(stylesdir, 00755); dt_styles_save_to_file(name, stylesdir, FALSE); char tmp_accel[1024]; gchar *tmp_name = g_strdup(name); // freed by _destroy_style_shortcut_callback snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), name); dt_accel_register_global(tmp_accel, 0, 0); GClosure *closure; closure = g_cclosure_new(G_CALLBACK(_apply_style_shortcut_callback), tmp_name, _destroy_style_shortcut_callback); dt_accel_connect_global(tmp_accel, closure); dt_control_signal_raise(darktable.signals, DT_SIGNAL_STYLE_CHANGED); return TRUE; } return FALSE; }
// internal function: to avoid exif blob reading + 8-bit byteorder flag + high-quality override int dt_imageio_export_with_flags(const uint32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const int32_t ignore_exif, const int32_t display_byteorder, const gboolean high_quality, const gboolean upscale, const int32_t thumbnail_export, const char *filter, const gboolean copy_metadata, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total) { dt_develop_t dev; dt_dev_init(&dev, 0); dt_dev_load_image(&dev, imgid); const int buf_is_downscaled = (thumbnail_export && dt_conf_get_bool("plugins/lighttable/low_quality_thumbnails")); dt_mipmap_buffer_t buf; if(buf_is_downscaled) dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_F, DT_MIPMAP_BLOCKING, 'r'); else dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r'); const dt_image_t *img = &dev.image_storage; if(!buf.buf || !buf.width || !buf.height) { fprintf(stderr, "allocation failed???\n"); dt_control_log(_("image `%s' is not available!"), img->filename); goto error_early; } const int wd = img->width; const int ht = img->height; const float max_scale = upscale ? 100.0 : 1.0; int res = 0; dt_times_t start; dt_get_times(&start); dt_dev_pixelpipe_t pipe; res = thumbnail_export ? dt_dev_pixelpipe_init_thumbnail(&pipe, wd, ht) : dt_dev_pixelpipe_init_export(&pipe, wd, ht, format->levels(format_params)); if(!res) { dt_control_log( _("failed to allocate memory for %s, please lower the threads used for export or buy more memory."), thumbnail_export ? C_("noun", "thumbnail export") : C_("noun", "export")); goto error; } // If a style is to be applied during export, add the iop params into the history if(!thumbnail_export && format_params->style[0] != '\0') { GList *style_items = dt_styles_get_item_list(format_params->style, TRUE, -1); if(!style_items) { dt_control_log(_("cannot find the style '%s' to apply during export."), format_params->style); goto error; } // remove everything above history_end GList *history = g_list_nth(dev.history, dev.history_end); while(history) { GList *next = g_list_next(history); dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data); free(hist->params); free(hist->blend_params); free(history->data); dev.history = g_list_delete_link(dev.history, history); history = next; } // Add each params for(GList *iter = style_items; iter; iter = g_list_next(iter)) { dt_style_item_t *s = (dt_style_item_t *)iter->data; for(GList *module = dev.iop; module; module = g_list_next(module)) { dt_iop_module_t *m = (dt_iop_module_t *)module->data; if(!strcmp(m->op, s->operation)) { dt_dev_history_item_t *h = malloc(sizeof(dt_dev_history_item_t)); dt_iop_module_t *style_module = m; if((format_params->style_append && !(m->flags() & IOP_FLAGS_ONE_INSTANCE)) || m->multi_priority != s->multi_priority) { // dt_dev_module_duplicate() doesn't work here, it's trying too hard to be clever style_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t)); if(style_module && !dt_iop_load_module(style_module, m->so, m->dev)) { style_module->instance = m->instance; style_module->multi_priority = s->multi_priority; snprintf(style_module->multi_name, sizeof(style_module->multi_name), "%s", s->name); dev.iop = g_list_insert_sorted(dev.iop, style_module, sort_plugins); } else { free(h); goto error; } } h->params = s->params; h->blend_params = s->blendop_params; h->enabled = s->enabled; h->module = style_module; h->multi_priority = s->multi_priority; g_strlcpy(h->multi_name, s->name, sizeof(h->multi_name)); if(m->legacy_params && (s->module_version != m->version())) { void *new_params = malloc(m->params_size); m->legacy_params(m, h->params, s->module_version, new_params, labs(m->version())); free(h->params); h->params = new_params; } dev.history_end++; dev.history = g_list_append(dev.history, h); // make sure that dt_style_item_free doesn't free data we still use s->params = NULL; s->blendop_params = NULL; break; } } } g_list_free_full(style_items, dt_style_item_free); } dt_dev_pixelpipe_set_icc(&pipe, icc_type, icc_filename, icc_intent); dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height, buf.iscale); dt_dev_pixelpipe_create_nodes(&pipe, &dev); dt_dev_pixelpipe_synch_all(&pipe, &dev); if(filter) { if(!strncmp(filter, "pre:", 4)) dt_dev_pixelpipe_disable_after(&pipe, filter + 4); if(!strncmp(filter, "post:", 5)) dt_dev_pixelpipe_disable_before(&pipe, filter + 5); } dt_dev_pixelpipe_get_dimensions(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width, &pipe.processed_height); dt_show_times(&start, "[export] creating pixelpipe", NULL); // find output color profile for this image: int sRGB = 1; if(icc_type == DT_COLORSPACE_SRGB) { sRGB = 1; } else if(icc_type == DT_COLORSPACE_NONE) { GList *modules = dev.iop; dt_iop_module_t *colorout = NULL; while(modules) { colorout = (dt_iop_module_t *)modules->data; if(colorout->get_p && strcmp(colorout->op, "colorout") == 0) { const dt_colorspaces_color_profile_type_t *type = colorout->get_p(colorout->params, "type"); sRGB = (!type || *type == DT_COLORSPACE_SRGB); break; // colorout can't have > 1 instance } modules = g_list_next(modules); } } else { sRGB = 0; } // get only once at the beginning, in case the user changes it on the way: const gboolean high_quality_processing = ((format_params->max_width == 0 || format_params->max_width >= pipe.processed_width) && (format_params->max_height == 0 || format_params->max_height >= pipe.processed_height)) ? FALSE : high_quality; const int width = format_params->max_width; const int height = format_params->max_height; const double scalex = width > 0 ? fminf(width / (double)pipe.processed_width, max_scale) : 1.0; const double scaley = height > 0 ? fminf(height / (double)pipe.processed_height, max_scale) : 1.0; const double scale = fminf(scalex, scaley); const int processed_width = scale * pipe.processed_width + .5f; const int processed_height = scale * pipe.processed_height + .5f; const int bpp = format->bpp(format_params); dt_get_times(&start); if(high_quality_processing) { /* * if high quality processing was requested, downsampling will be done * at the very end of the pipe (just before border and watermark) */ dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); } else { // else, downsampling will be right after demosaic // so we need to turn temporarily disable in-pipe late downsampling iop. // find the finalscale module dt_dev_pixelpipe_iop_t *finalscale = NULL; { GList *nodes = g_list_last(pipe.nodes); while(nodes) { dt_dev_pixelpipe_iop_t *node = (dt_dev_pixelpipe_iop_t *)(nodes->data); if(!strcmp(node->module->op, "finalscale")) { finalscale = node; break; } nodes = g_list_previous(nodes); } } if(finalscale) finalscale->enabled = 0; // do the processing (8-bit with special treatment, to make sure we can use openmp further down): if(bpp == 8) dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale); else dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); if(finalscale) finalscale->enabled = 1; } dt_show_times(&start, thumbnail_export ? "[dev_process_thumbnail] pixel pipeline processing" : "[dev_process_export] pixel pipeline processing", NULL); uint8_t *outbuf = pipe.backbuf; // downconversion to low-precision formats: if(bpp == 8) { if(display_byteorder) { if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff); outbuf[4 * k + 0] = r; outbuf[4 * k + 1] = g; outbuf[4 * k + 2] = b; } } // else processing output was 8-bit already, and no need to swap order } else // need to flip { // ldr output: char if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff); outbuf[4 * k + 0] = r; outbuf[4 * k + 1] = g; outbuf[4 * k + 2] = b; } } else { // !display_byteorder, need to swap: uint8_t *const buf8 = pipe.backbuf; #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) #endif // just flip byte order for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { uint8_t tmp = buf8[4 * k + 0]; buf8[4 * k + 0] = buf8[4 * k + 2]; buf8[4 * k + 2] = tmp; } } } } else if(bpp == 16) { // uint16_t per color channel float *buff = (float *)outbuf; uint16_t *buf16 = (uint16_t *)outbuf; for(int y = 0; y < processed_height; y++) for(int x = 0; x < processed_width; x++) { // convert in place const size_t k = (size_t)processed_width * y + x; for(int i = 0; i < 3; i++) buf16[4 * k + i] = CLAMP(buff[4 * k + i] * 0x10000, 0, 0xffff); } } // else output float, no further harm done to the pixels :) format_params->width = processed_width; format_params->height = processed_height; if(!ignore_exif) { int length; uint8_t *exif_profile = NULL; // Exif data should be 65536 bytes max, but if original size is close to that, // adding new tags could make it go over that... so let it be and see what // happens when we write the image char pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache); // last param is dng mode, it's false here length = dt_exif_read_blob(&exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0); res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, exif_profile, length, imgid, num, total); free(exif_profile); } else { res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, NULL, 0, imgid, num, total); } dt_dev_pixelpipe_cleanup(&pipe); dt_dev_cleanup(&dev); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); /* now write xmp into that container, if possible */ if(copy_metadata && (format->flags(format_params) & FORMAT_FLAGS_SUPPORT_XMP)) { dt_exif_xmp_attach(imgid, filename); // no need to cancel the export if this fail } if(!thumbnail_export && strcmp(format->mime(format_params), "memory") && !(format->flags(format_params) & FORMAT_FLAGS_NO_TMPFILE)) { #ifdef USE_LUA //Synchronous calling of lua intermediate-export-image events dt_lua_lock(); lua_State *L = darktable.lua_state.state; luaA_push(L, dt_lua_image_t, &imgid); lua_pushstring(L, filename); luaA_push_type(L, format->parameter_lua_type, format_params); if (storage) luaA_push_type(L, storage->parameter_lua_type, storage_params); else lua_pushnil(L); dt_lua_event_trigger(L, "intermediate-export-image", 4); dt_lua_unlock(); #endif dt_control_signal_raise(darktable.signals, DT_SIGNAL_IMAGE_EXPORT_TMPFILE, imgid, filename, format, format_params, storage, storage_params); } return res; error: dt_dev_pixelpipe_cleanup(&pipe); error_early: dt_dev_cleanup(&dev); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); return 1; }
void dt_similarity_match_image(uint32_t imgid,dt_similarity_t *data) { sqlite3_stmt *stmt; gboolean all_ok_for_match = TRUE; dt_similarity_histogram_t orginal_histogram,test_histogram; dt_similarity_lightmap_t orginal_lightmap,test_lightmap; /* create temporary mem table for matches */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "create temporary table if not exists similar_images (id integer,score real)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from similar_images", NULL, NULL, NULL); /* * get the histogram and lightmap data for image to match against */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select histogram,lightmap from images where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if (sqlite3_step(stmt) == SQLITE_ROW) { /* get the histogram data */ uint32_t size = sqlite3_column_bytes(stmt,0); if (size!=sizeof(dt_similarity_histogram_t)) { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } else memcpy(&orginal_histogram, sqlite3_column_blob(stmt, 0), sizeof(dt_similarity_histogram_t)); /* get the lightmap data */ size = sqlite3_column_bytes(stmt,1); if (size!=sizeof(dt_similarity_lightmap_t)) { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } else memcpy(&orginal_lightmap, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_lightmap_t)); } else { all_ok_for_match = FALSE; dt_control_log(_("this image has not been indexed yet.")); } sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); /* * if all ok lets begin matching */ if (all_ok_for_match) { char query[4096]={0}; /* add target image with 100.0 in score into result to ensure it always shown in top */ sprintf(query,"insert into similar_images(id,score) values(%d,%f)",imgid,100.0); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); /* set an extended collection query for viewing the result of match */ dt_collection_set_extended_where(darktable.collection, ", similar_images where images.id = similar_images.id order by similar_images.score desc"); dt_collection_set_query_flags( darktable.collection, dt_collection_get_query_flags(darktable.collection) | COLLECTION_QUERY_USE_ONLY_WHERE_EXT); dt_collection_update(darktable.collection); dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); /* loop thru images and generate score table */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id,histogram,lightmap from images", -1, &stmt, NULL); while (sqlite3_step(stmt) == SQLITE_ROW) { float score_histogram=0, score_lightmap=0, score_colormap=0; /* verify size of histogram and lightmap blob of test image */ if ( (sqlite3_column_bytes(stmt,1) == sizeof(dt_similarity_histogram_t)) && (sqlite3_column_bytes(stmt,2) == sizeof(dt_similarity_lightmap_t)) ) { /* * Get the histogram blob and calculate the similarity score */ memcpy(&test_histogram, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_histogram_t)); score_histogram = _similarity_match_histogram_rgb(data, &orginal_histogram, &test_histogram); /* * Get the lightmap blob and calculate the similarity score * 1.08 is a tuned constant that works well with threshold */ memcpy(&test_lightmap, sqlite3_column_blob(stmt, 2), sizeof(dt_similarity_lightmap_t)); score_lightmap = _similarity_match_lightmap(data, &orginal_lightmap, &test_lightmap); /* * then calculate the colormap similarity score */ score_colormap = _similarity_match_colormap(data, &orginal_lightmap, &test_lightmap); /* * calculate the similarity score */ float score = (pow(score_histogram, data->histogram_weight) * pow(score_lightmap, data->lightmap_weight) * pow(score_colormap, data->redmap_weight)); // fprintf(stderr,"score: %f, histo: %f, light: %f, color: %f\n",score,score_histogram,score_lightmap,score_colormap); /* * If current images scored, lets add it to similar_images table */ if(score >= 0.92) { sprintf(query,"insert into similar_images(id,score) values(%d,%f)",sqlite3_column_int(stmt, 0),score); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); /* lets redraw the view */ dt_control_queue_redraw_center(); } } else fprintf(stderr,"Image has inconsisten similarity matching data..\n"); } } sqlite3_finalize (stmt); }
void dt_collection_update_query(const dt_collection_t *collection) { char query[1024], confname[200]; gchar *complete_query = NULL; const int _n_r = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int num_rules = CLAMP(_n_r, 1, 10); char *conj[] = {"and", "or", "and not"}; complete_query = dt_util_dstrcat(complete_query, "("); for(int i=0; i<num_rules; i++) { snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", i); const int property = dt_conf_get_int(confname); snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", i); gchar *text = dt_conf_get_string(confname); if(!text) break; snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", i); const int mode = dt_conf_get_int(confname); gchar *escaped_text = dt_util_str_replace(text, "'", "''"); get_query_string(property, escaped_text, query, sizeof(query)); if(i > 0) complete_query = dt_util_dstrcat(complete_query, " %s %s", conj[mode], query); else complete_query = dt_util_dstrcat(complete_query, "%s", query); g_free(escaped_text); g_free(text); } complete_query = dt_util_dstrcat(complete_query, ")"); // printf("complete query: `%s'\n", complete_query); /* set the extended where and the use of it in the query */ dt_collection_set_extended_where (collection, complete_query); dt_collection_set_query_flags (collection, (dt_collection_get_query_flags (collection) | COLLECTION_QUERY_USE_WHERE_EXT)); /* remove film id from default filter */ dt_collection_set_filter_flags (collection, (dt_collection_get_filter_flags (collection) & ~COLLECTION_FILTER_FILM_ID)); /* update query and at last the visual */ dt_collection_update (collection); /* free string */ g_free(complete_query); // remove from selected images where not in this query. sqlite3_stmt *stmt = NULL; const gchar *cquery = dt_collection_get_query(collection); complete_query = NULL; if(cquery && cquery[0] != '\0') { complete_query = dt_util_dstrcat(complete_query, "delete from selected_images where imgid not in (%s)", cquery); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), complete_query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1); sqlite3_step(stmt); sqlite3_finalize(stmt); /* free allocated strings */ g_free(complete_query); } /* raise signal of collection change, only if this is an original */ if (!collection->clone) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); }
// internal function: to avoid exif blob reading + 8-bit byteorder flag + high-quality override int dt_imageio_export_with_flags( const uint32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const int32_t ignore_exif, const int32_t display_byteorder, const gboolean high_quality, const int32_t thumbnail_export, const char *filter, const gboolean copy_metadata, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params) { dt_develop_t dev; dt_dev_init(&dev, 0); dt_mipmap_buffer_t buf; if(thumbnail_export && dt_conf_get_bool("plugins/lighttable/low_quality_thumbnails")) dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_F, DT_MIPMAP_BLOCKING); else dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING); dt_dev_load_image(&dev, imgid); const dt_image_t *img = &dev.image_storage; const int wd = img->width; const int ht = img->height; int res = 0; dt_times_t start; dt_get_times(&start); dt_dev_pixelpipe_t pipe; res = thumbnail_export ? dt_dev_pixelpipe_init_thumbnail(&pipe, wd, ht) : dt_dev_pixelpipe_init_export(&pipe, wd, ht, format->levels(format_params)); if(!res) { dt_control_log(_("failed to allocate memory for %s, please lower the threads used for export or buy more memory."), thumbnail_export ? C_("noun", "thumbnail export") : C_("noun", "export")); dt_dev_cleanup(&dev); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); return 1; } if(!buf.buf) { dt_control_log(_("image `%s' is not available!"), img->filename); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); dt_dev_cleanup(&dev); return 1; } // If a style is to be applied during export, add the iop params into the history if (!thumbnail_export && format_params->style[0] != '\0') { GList *stls; GList *modules = dev.iop; dt_iop_module_t *m = NULL; if ((stls=dt_styles_get_item_list(format_params->style, TRUE, -1)) == 0) { dt_control_log(_("cannot find the style '%s' to apply during export."), format_params->style); dt_dev_cleanup(&dev); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); return 1; } // Add each params while (stls) { dt_style_item_t *s = (dt_style_item_t *) stls->data; modules = dev.iop; while (modules) { m = (dt_iop_module_t *)modules->data; // since the name in the style is returned with a possible multi-name, just check the start of the name if (strncmp(m->op, s->name, strlen(m->op)) == 0) { dt_dev_history_item_t *h = malloc(sizeof(dt_dev_history_item_t)); h->params = s->params; h->blend_params = s->blendop_params; h->enabled = s->enabled; h->module = m; h->multi_priority = 1; g_strlcpy(h->multi_name, "", sizeof(h->multi_name)); if(m->legacy_params && (s->module_version != m->version())) { void *new_params = malloc(m->params_size); m->legacy_params (m, h->params, s->module_version, new_params, labs(m->version())); free (h->params); h->params = new_params; } dev.history_end++; dev.history = g_list_append(dev.history, h); break; } modules = g_list_next(modules); } stls = g_list_next(stls); } } dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height, 1.0); dt_dev_pixelpipe_create_nodes(&pipe, &dev); dt_dev_pixelpipe_synch_all(&pipe, &dev); dt_dev_pixelpipe_get_dimensions(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width, &pipe.processed_height); if(filter) { if(!strncmp(filter, "pre:", 4)) dt_dev_pixelpipe_disable_after(&pipe, filter+4); if(!strncmp(filter, "post:", 5)) dt_dev_pixelpipe_disable_before(&pipe, filter+5); } dt_show_times(&start, "[export] creating pixelpipe", NULL); // find output color profile for this image: int sRGB = 1; gchar *overprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile"); if(overprofile && !strcmp(overprofile, "sRGB")) { sRGB = 1; } else if(!overprofile || !strcmp(overprofile, "image")) { GList *modules = dev.iop; dt_iop_module_t *colorout = NULL; while (modules) { colorout = (dt_iop_module_t *)modules->data; if(colorout->get_p && strcmp(colorout->op, "colorout") == 0) { const char *iccprofile = colorout->get_p(colorout->params, "iccprofile"); if(!strcmp(iccprofile, "sRGB")) sRGB = 1; else sRGB = 0; } modules = g_list_next(modules); } } else { sRGB = 0; } g_free(overprofile); // get only once at the beginning, in case the user changes it on the way: const gboolean high_quality_processing = ((format_params->max_width == 0 || format_params->max_width >= pipe.processed_width ) && (format_params->max_height == 0 || format_params->max_height >= pipe.processed_height)) ? FALSE : high_quality; const int width = high_quality_processing ? 0 : format_params->max_width; const int height = high_quality_processing ? 0 : format_params->max_height; const double scalex = width > 0 ? fminf(width /(double)pipe.processed_width, 1.0) : 1.0; const double scaley = height > 0 ? fminf(height/(double)pipe.processed_height, 1.0) : 1.0; const double scale = fminf(scalex, scaley); int processed_width = scale*pipe.processed_width + .5f; int processed_height = scale*pipe.processed_height + .5f; const int bpp = format->bpp(format_params); // downsampling done last, if high quality processing was requested: uint8_t *outbuf = pipe.backbuf; uint8_t *moutbuf = NULL; // keep track of alloc'ed memory dt_get_times(&start); if(high_quality_processing) { dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); const double scalex = format_params->max_width > 0 ? fminf(format_params->max_width /(double)pipe.processed_width, 1.0) : 1.0; const double scaley = format_params->max_height > 0 ? fminf(format_params->max_height/(double)pipe.processed_height, 1.0) : 1.0; const double scale = fminf(scalex, scaley); processed_width = scale*pipe.processed_width + .5f; processed_height = scale*pipe.processed_height + .5f; moutbuf = (uint8_t *)dt_alloc_align(64, (size_t)sizeof(float)*processed_width*processed_height*4); outbuf = moutbuf; // now downscale into the new buffer: dt_iop_roi_t roi_in, roi_out; roi_in.x = roi_in.y = roi_out.x = roi_out.y = 0; roi_in.scale = 1.0; roi_out.scale = scale; roi_in.width = pipe.processed_width; roi_in.height = pipe.processed_height; roi_out.width = processed_width; roi_out.height = processed_height; dt_iop_clip_and_zoom((float *)outbuf, (float *)pipe.backbuf, &roi_out, &roi_in, processed_width, pipe.processed_width); } else { // do the processing (8-bit with special treatment, to make sure we can use openmp further down): if(bpp == 8) dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale); else dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); outbuf = pipe.backbuf; } dt_show_times(&start, thumbnail_export ? "[dev_process_thumbnail] pixel pipeline processing" : "[dev_process_export] pixel pipeline processing", NULL); // downconversion to low-precision formats: if(bpp == 8) { if(display_byteorder) { if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k=0; k<(size_t)processed_width*processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4*k+2]*0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4*k+1]*0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4*k+0]*0xff, 0, 0xff); outbuf[4*k+0] = r; outbuf[4*k+1] = g; outbuf[4*k+2] = b; } } // else processing output was 8-bit already, and no need to swap order } else // need to flip { // ldr output: char if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k=0; k<(size_t)processed_width*processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4*k+0]*0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4*k+1]*0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4*k+2]*0xff, 0, 0xff); outbuf[4*k+0] = r; outbuf[4*k+1] = g; outbuf[4*k+2] = b; } } else { // !display_byteorder, need to swap: uint8_t *const buf8 = pipe.backbuf; #ifdef _OPENMP #pragma omp parallel for default(none) shared(processed_width, processed_height) schedule(static) #endif // just flip byte order for(size_t k=0; k<(size_t)processed_width*processed_height; k++) { uint8_t tmp = buf8[4*k+0]; buf8[4*k+0] = buf8[4*k+2]; buf8[4*k+2] = tmp; } } } } else if(bpp == 16) { // uint16_t per color channel float *buff = (float *) outbuf; uint16_t *buf16 = (uint16_t *)outbuf; for(int y=0; y<processed_height; y++) for(int x=0; x<processed_width ; x++) { // convert in place const size_t k = (size_t)processed_width*y + x; for(int i=0; i<3; i++) buf16[4*k+i] = CLAMP(buff[4*k+i]*0x10000, 0, 0xffff); } } // else output float, no further harm done to the pixels :) format_params->width = processed_width; format_params->height = processed_height; if(!ignore_exif) { int length; uint8_t exif_profile[65535]; // C++ alloc'ed buffer is uncool, so we waste some bits here. char pathname[PATH_MAX]; gboolean from_cache = TRUE; dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache); // last param is dng mode, it's false here length = dt_exif_read_blob(exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0); res = format->write_image (format_params, filename, outbuf, exif_profile, length, imgid); } else { res = format->write_image (format_params, filename, outbuf, NULL, 0, imgid); } dt_dev_pixelpipe_cleanup(&pipe); dt_dev_cleanup(&dev); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); dt_free_align(moutbuf); /* now write xmp into that container, if possible */ if(copy_metadata && (format->flags(format_params) & FORMAT_FLAGS_SUPPORT_XMP)) { dt_exif_xmp_attach(imgid, filename); // no need to cancel the export if this fail } if(!thumbnail_export && strcmp(format->mime(format_params), "memory")) { dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_EXPORT_TMPFILE,imgid,filename,format,format_params,storage,storage_params); } return res; }
void dt_lightroom_import (int imgid, dt_develop_t *dev, gboolean iauto) { gboolean refresh_needed = FALSE; char imported[256] = {0}; // Get full pathname char *pathname = dt_get_lightroom_xmp(imgid); if (!pathname) { if (!iauto) dt_control_log(_("cannot find lightroom XMP!")); return; } // Load LR xmp xmlDocPtr doc; xmlNodePtr entryNode; // Parse xml document doc = xmlParseEntity(pathname); if (doc == NULL) { g_free(pathname); return; } // Enter first node, xmpmeta entryNode = xmlDocGetRootElement(doc); if (entryNode == NULL) { g_free(pathname); xmlFreeDoc(doc); return; } if (xmlStrcmp(entryNode->name, (const xmlChar *)"xmpmeta")) { if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } // Check that this is really a Lightroom document xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); if (xpathCtx == NULL) { g_free(pathname); xmlFreeDoc(doc); return; } xmlXPathRegisterNs(xpathCtx, BAD_CAST "stEvt", BAD_CAST "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"); xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar *)"//@stEvt:softwareAgent", xpathCtx); if (xpathObj == NULL) { if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); xmlXPathFreeContext(xpathCtx); g_free(pathname); xmlFreeDoc(doc); return; } xmlNodeSetPtr xnodes = xpathObj->nodesetval; if (xnodes != NULL && xnodes->nodeNr > 0) { xmlNodePtr xnode = xnodes->nodeTab[0]; xmlChar *value = xmlNodeListGetString(doc, xnode->xmlChildrenNode, 1); if (!strstr((char *)value,"Lightroom")) { xmlXPathFreeContext(xpathCtx); xmlXPathFreeObject(xpathObj); xmlFreeDoc(doc); xmlFree(value); if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } xmlFree(value); } else { xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); // Go safely to Description node if (entryNode) entryNode = entryNode->xmlChildrenNode; if (entryNode) entryNode = entryNode->next; if (entryNode) entryNode = entryNode->xmlChildrenNode; if (entryNode) entryNode = entryNode->next; if (!entryNode || xmlStrcmp(entryNode->name, (const xmlChar *)"Description")) { if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } g_free(pathname); // Look for attributes in the Description dt_iop_clipping_params_t pc; memset(&pc, 0, sizeof(pc)); gboolean has_crop = FALSE; dt_iop_flip_params_t pf; memset(&pf, 0, sizeof(pf)); gboolean has_flip = FALSE; dt_iop_exposure_params_t pe; memset(&pe, 0, sizeof(pe)); gboolean has_exposure = FALSE; dt_iop_vignette_params_t pv; memset(&pv, 0, sizeof(pv)); gboolean has_vignette = FALSE; dt_iop_grain_params_t pg; memset(&pg, 0, sizeof(pg)); gboolean has_grain = FALSE; dt_iop_spots_params_t ps; memset(&ps, 0, sizeof(ps)); gboolean has_spots = FALSE; typedef enum lr_curve_kind_t { linear = 0, medium_contrast = 1, string_contrast = 2, custom = 3 } lr_curve_kind_t; #define MAX_PTS 20 dt_iop_tonecurve_params_t ptc; memset(&ptc, 0, sizeof(ptc)); int ptc_value[4] = {0, 0, 0, 0}; float ptc_split[3] = {0.0, 0.0, 0.0}; lr_curve_kind_t curve_kind = linear; int curve_pts[MAX_PTS][2]; int n_pts = 0; dt_iop_colorzones_params_t pcz; memset(&pcz, 0, sizeof(pcz)); gboolean has_colorzones = FALSE; dt_iop_splittoning_params_t pst; memset(&pst, 0, sizeof(pst)); gboolean has_splittoning = FALSE; dt_iop_bilat_params_t pbl; memset(&pbl, 0, sizeof(pbl)); gboolean has_bilat = FALSE; gboolean has_tags = FALSE; int rating = 0; gboolean has_rating = FALSE; gdouble lat = 0, lon = 0; gboolean has_gps = FALSE; int color = 0; gboolean has_colorlabel = FALSE; float fratio = 0; // factor ratio image float crop_roundness = 0; // from lightroom int n_import = 0; // number of iop imported const float hfactor = 3.0 / 9.0; // hue factor adjustment (use 3 out of 9 boxes in colorzones) const float lfactor = 4.0 / 9.0; // lightness factor adjustment (use 4 out of 9 boxes in colorzones) int iwidth = 0, iheight = 0; // image width / height int orientation = 1; xmlAttr* attribute = entryNode->properties; while(attribute && attribute->name && attribute->children) { xmlChar* value = xmlNodeListGetString(entryNode->doc, attribute->children, 1); if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropTop")) pc.cy = g_ascii_strtod((char *)value, NULL); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropRight")) pc.cw = g_ascii_strtod((char *)value, NULL); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropLeft")) pc.cx = g_ascii_strtod((char *)value, NULL); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropBottom")) pc.ch = g_ascii_strtod((char *)value, NULL); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropAngle")) pc.angle = -g_ascii_strtod((char *)value, NULL); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ImageWidth")) iwidth = atoi((char *)value); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ImageLength")) iheight = atoi((char *)value); else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Orientation")) { orientation = atoi((char *)value); if (dev!=NULL && ((dev->image_storage.orientation == 6 && orientation != 6) || (dev->image_storage.orientation == 5 && orientation != 8) || (dev->image_storage.orientation == 0 && orientation != 1))) has_flip = TRUE; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HasCrop")) { if (!xmlStrcmp(value, (const xmlChar *)"True")) has_crop = TRUE; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Blacks2012")) { int v = atoi((char *)value); if (v != 0) { has_exposure = TRUE; pe.black = lr2dt_blacks((float)v); } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Exposure2012")) { float v = g_ascii_strtod((char *)value, NULL); if (v != 0.0) { has_exposure = TRUE; pe.exposure = lr2dt_exposure(v); } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteAmount")) { int v = atoi((char *)value); if (v != 0) { has_vignette = TRUE; pv.brightness = lr2dt_vignette_gain((float)v); } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteMidpoint")) { int v = atoi((char *)value); pv.scale = lr2dt_vignette_midpoint((float)v); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteStyle")) { int v = atoi((char *)value); if (v == 1) // Highlight Priority pv.saturation = -0.300; else // Color Priority & Paint Overlay pv.saturation = -0.200; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteFeather")) { int v = atoi((char *)value); if (v != 0) pv.falloff_scale = (float)v; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteRoundness")) { int v = atoi((char *)value); crop_roundness = (float)v; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GrainAmount")) { int v = atoi((char *)value); if (v != 0) { has_grain = TRUE; pg.strength = lr2dt_grain_amount((float)v); } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GrainFrequency")) { int v = atoi((char *)value); if (v != 0) pg.scale = lr2dt_grain_frequency((float)v); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricShadows")) { ptc_value[0] = atoi((char *)value); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricDarks")) { ptc_value[1] = atoi((char *)value); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricLights")) { ptc_value[2] = atoi((char *)value); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricHighlights")) { ptc_value[3] = atoi((char *)value); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricShadowSplit")) { ptc_split[0] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricMidtoneSplit")) { ptc_split[1] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricHighlightSplit")) { ptc_split[2] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ToneCurveName2012")) { if (!xmlStrcmp(value, (const xmlChar *)"Linear")) curve_kind = linear; else if (!xmlStrcmp(value, (const xmlChar *)"Medium Contrast")) curve_kind = medium_contrast; else if (!xmlStrcmp(value, (const xmlChar *)"Strong Contrast")) curve_kind = medium_contrast; else if (!xmlStrcmp(value, (const xmlChar *)"Custom")) curve_kind = custom; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentRed")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][0] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentOrange")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][1] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentYellow")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][2] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentGreen")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][3] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentAqua")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][4] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentBlue")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][5] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentPurple")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][6] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentMagenta")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[1][7] = 0.5 + (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentRed")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][0] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentOrange")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][1] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentYellow")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][2] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentGreen")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][3] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentAqua")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][4] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentBlue")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][5] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentPurple")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][6] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentMagenta")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[0][7] = 0.5 + lfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentRed")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][0] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentOrange")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][1] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentYellow")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][2] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentGreen")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][3] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentAqua")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][4] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentBlue")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][5] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentPurple")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][6] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentMagenta")) { int v = atoi((char *)value); if (v!=0) has_colorzones = TRUE; pcz.equalizer_y[2][7] = 0.5 + hfactor * (float)v / 200.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningShawowHue")) { int v = atoi((char *)value); if (v!=0) has_splittoning = TRUE; pst.shadow_hue = (float)v / 255.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningShawowSaturation")) { int v = atoi((char *)value); if (v!=0) has_splittoning = TRUE; pst.shadow_saturation = (float)v / 100.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningHighlightHue")) { int v = atoi((char *)value); if (v!=0) has_splittoning = TRUE; pst.highlight_hue = (float)v / 255.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningHighlightSaturation")) { int v = atoi((char *)value); if (v!=0) has_splittoning = TRUE; pst.highlight_saturation = (float)v / 100.0; } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningBalance")) { float v = g_ascii_strtod((char *)value, NULL); pst.balance = lr2dt_splittoning_balance(v); } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Clarity2012")) { int v = atoi((char *)value); if (v!=0) { has_bilat = TRUE; pbl.detail = lr2dt_clarity((float)v); } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Rating")) { int v = atoi((char *)value); if (v!=0) { rating = v; has_rating = TRUE; } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GPSLatitude")) { int deg; double msec; char d; if (sscanf((const char *)value, "%d,%lf%c", °, &msec, &d)) { lat = deg + msec / 60.0; if (d == 'S') lat = -lat; has_gps = TRUE; } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GPSLongitude")) { int deg; double msec; char d; if (sscanf((const char *)value, "%d,%lf%c", °, &msec, &d)) { lon = deg + msec / 60.0; if (d == 'W') lon = -lon; has_gps = TRUE; } } else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Label")) { for(int i=0; value[i]; i++) value[i] = tolower(value[i]); if (!strcmp((char *)value, _("red"))) color = 0; else if (!strcmp((char *)value, _("yellow"))) color = 1; else if (!strcmp((char *)value, _("green"))) color = 2; else if (!strcmp((char *)value, _("blue"))) color = 3; else // just an else here to catch all other cases as on lightroom one can // change the names of labels. So purple and the user's defined labels // will be mapped to purple on darktable. color = 4; has_colorlabel = TRUE; } xmlFree(value); attribute = attribute->next; } // Look for tags (subject/Bag/* and RetouchInfo/seq/*) entryNode = entryNode->xmlChildrenNode; if (entryNode) entryNode = entryNode->next; while (entryNode) { if (dev == NULL && (!xmlStrcmp(entryNode->name, (const xmlChar *) "subject") ||!xmlStrcmp(entryNode->name, (const xmlChar *) "hierarchicalSubject"))) { xmlNodePtr tagNode = entryNode; tagNode = tagNode->xmlChildrenNode; tagNode = tagNode->next; tagNode = tagNode->xmlChildrenNode; tagNode = tagNode->next; while (tagNode) { if (!xmlStrcmp(tagNode->name, (const xmlChar *) "li")) { xmlChar *value= xmlNodeListGetString(doc, tagNode->xmlChildrenNode, 1); guint tagid = 0; if (!dt_tag_exists((char *)value, &tagid)) dt_tag_new((char *)value, &tagid); dt_tag_attach(tagid, imgid); has_tags = TRUE; xmlFree(value); } tagNode = tagNode->next; } } else if (dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *) "RetouchInfo")) { xmlNodePtr riNode = entryNode; riNode = riNode->xmlChildrenNode; riNode = riNode->next; riNode = riNode->xmlChildrenNode; riNode = riNode->next; while (riNode) { if (!xmlStrcmp(riNode->name, (const xmlChar *) "li")) { xmlChar *value= xmlNodeListGetString(doc, riNode->xmlChildrenNode, 1); spot_t *p = &ps.spot[ps.num_spots]; if (sscanf((const char *)value, "centerX = %f, centerY = %f, radius = %f, sourceState = %*[a-zA-Z], sourceX = %f, sourceY = %f", &(p->x), &(p->y), &(p->radius), &(p->xc), &(p->yc))) { ps.num_spots++; has_spots = TRUE; } xmlFree(value); } if (ps.num_spots == MAX_SPOTS) break; riNode = riNode->next; } } else if (dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *) "ToneCurvePV2012")) { xmlNodePtr tcNode = entryNode; tcNode = tcNode->xmlChildrenNode; tcNode = tcNode->next; tcNode = tcNode->xmlChildrenNode; tcNode = tcNode->next; while (tcNode) { if (!xmlStrcmp(tcNode->name, (const xmlChar *) "li")) { xmlChar *value= xmlNodeListGetString(doc, tcNode->xmlChildrenNode, 1); if (sscanf((const char *)value, "%d, %d", &(curve_pts[n_pts][0]), &(curve_pts[n_pts][1]))) n_pts++; xmlFree(value); } if (n_pts == MAX_PTS) break; tcNode = tcNode->next; } } entryNode = entryNode->next; } xmlFreeDoc(doc); // Integrates into the history all the imported iop if (dev != NULL && dt_image_is_raw(&dev->image_storage)) { // set colorin to cmatrix which is the default from Adobe (so closer to what Lightroom does) dt_iop_colorin_params_t pci = (dt_iop_colorin_params_t) { "cmatrix", DT_INTENT_PERCEPTUAL }; dt_add_hist (imgid, "colorin", (dt_iop_params_t *)&pci, sizeof(dt_iop_colorin_params_t), imported, sizeof(imported), LRDT_COLORIN_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_crop) { pc.k_sym = 0; pc.k_apply = 0; pc.crop_auto = 0; pc.k_h = pc.k_v = 0; pc.k_type = 0; pc.kxa = pc.kxd = 0.2f; pc.kxc = pc.kxb = 0.8f; pc.kya = pc.kyb = 0.2f; pc.kyc = pc.kyd = 0.8f; float tmp; if (has_crop) { // adjust crop data according to the rotation switch (dev->image_storage.orientation) { case 5: // portrait - counter-clockwise tmp = pc.ch; pc.ch = 1.0 - pc.cx; pc.cx = pc.cy; pc.cy = 1.0 - pc.cw; pc.cw = tmp; break; case 6: // portrait - clockwise tmp = pc.ch; pc.ch = pc.cw; pc.cw = 1.0 - pc.cy; pc.cy = pc.cx; pc.cx = 1.0 - tmp; break; default: break; } if (pc.angle != 0) { const float rangle = -pc.angle * (3.141592 / 180); float x, y; // do the rotation (rangle) using center of image (0.5, 0.5) x = pc.cx - 0.5; y = 0.5 - pc.cy; pc.cx = 0.5 + x * cos(rangle) - y * sin(rangle); pc.cy = 0.5 - (x * sin(rangle) + y * cos(rangle)); x = pc.cw - 0.5; y = 0.5 - pc.ch; pc.cw = 0.5 + x * cos(rangle) - y * sin(rangle); pc.ch = 0.5 - (x * sin(rangle) + y * cos(rangle)); } } else { pc.angle = 0; pc.cx = 0; pc.cy = 0; pc.cw = 1; pc.ch = 1; } fratio = (pc.cw - pc.cx) / (pc.ch - pc.cy); dt_add_hist (imgid, "clipping", (dt_iop_params_t *)&pc, sizeof(dt_iop_clipping_params_t), imported, sizeof(imported), LRDT_CLIPPING_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_flip) { pf.orientation = 0; if (dev->image_storage.orientation == 5) // portrait switch (orientation) { case 8: pf.orientation = 0; break; case 3: pf.orientation = 5; break; case 6: pf.orientation = 3; break; case 1: pf.orientation = 6; break; // with horizontal flip case 7: pf.orientation = 1; break; case 2: pf.orientation = 4; break; case 5: pf.orientation = 2; break; case 4: pf.orientation = 7; break; } else if (dev->image_storage.orientation == 6) // portrait switch (orientation) { case 8: pf.orientation = 3; break; case 3: pf.orientation = 6; break; case 6: pf.orientation = 0; break; case 1: pf.orientation = 5; break; // with horizontal flip case 7: pf.orientation = 2; break; case 2: pf.orientation = 7; break; case 5: pf.orientation = 1; break; case 4: pf.orientation = 4; break; } else // landscape switch (orientation) { case 8: pf.orientation = 5; break; case 3: pf.orientation = 3; break; case 6: pf.orientation = 6; break; case 1: pf.orientation = 0; break; // with horizontal flip case 7: pf.orientation = 7; break; case 2: pf.orientation = 1; break; case 5: pf.orientation = 4; break; case 4: pf.orientation = 2; break; } dt_add_hist (imgid, "flip", (dt_iop_params_t *)&pf, sizeof(dt_iop_flip_params_t), imported, sizeof(imported), LRDT_FLIP_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_exposure) { dt_add_hist (imgid, "exposure", (dt_iop_params_t *)&pe, sizeof(dt_iop_exposure_params_t), imported, sizeof(imported), LRDT_EXPOSURE_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_grain) { pg.channel = 0; dt_add_hist (imgid, "grain", (dt_iop_params_t *)&pg, sizeof(dt_iop_grain_params_t), imported, sizeof(imported), LRDT_GRAIN_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_vignette) { const float base_ratio = 1.325 / 1.5; pv.autoratio = FALSE; pv.dithering = DITHER_8BIT; pv.center.x = 0.0; pv.center.y = 0.0; pv.shape = 1.0; // defensive code, should not happen, but just in case future Lr version // has not ImageWidth/ImageLength XML tag. if (iwidth == 0 || iheight == 0) pv.whratio = base_ratio; else pv.whratio = base_ratio * ((float)iwidth / (float)iheight); if (has_crop) pv.whratio = pv.whratio * fratio; // Adjust scale and ratio based on the roundness. On Lightroom changing // the roundness change the width and the height of the vignette. if (crop_roundness > 0) { float newratio = pv.whratio - (pv.whratio - 1) * (crop_roundness / 100.0); float dscale = (1 - (newratio / pv.whratio)) / 2.0; pv.scale -= dscale * 100.0; pv.whratio = newratio; } dt_add_hist (imgid, "vignette", (dt_iop_params_t *)&pv, sizeof(dt_iop_vignette_params_t), imported, sizeof(imported), LRDT_VIGNETTE_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_spots) { // Check for orientation, rotate when in portrait mode if (orientation > 4) for (int k=0; k<ps.num_spots; k++) { float tmp = ps.spot[k].y; ps.spot[k].y = 1.0 - ps.spot[k].x; ps.spot[k].x = tmp; tmp = ps.spot[k].yc; ps.spot[k].yc = 1.0 - ps.spot[k].xc; ps.spot[k].xc = tmp; } dt_add_hist (imgid, "spots", (dt_iop_params_t *)&ps, sizeof(dt_iop_spots_params_t), imported, sizeof(imported), LRDT_SPOTS_VERSION, &n_import); refresh_needed=TRUE; } if (curve_kind != linear || ptc_value[0] != 0 || ptc_value[1] != 0 || ptc_value[2] != 0 || ptc_value[3] != 0) { ptc.tonecurve_nodes[ch_L] = 6; ptc.tonecurve_nodes[ch_a] = 7; ptc.tonecurve_nodes[ch_b] = 7; ptc.tonecurve_type[ch_L] = CUBIC_SPLINE; ptc.tonecurve_type[ch_a] = CUBIC_SPLINE; ptc.tonecurve_type[ch_b] = CUBIC_SPLINE; ptc.tonecurve_autoscale_ab = 1; ptc.tonecurve_preset = 0; float linear_ab[7] = {0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0}; // linear a, b curves for(int k=0; k<7; k++) ptc.tonecurve[ch_a][k].x = linear_ab[k]; for(int k=0; k<7; k++) ptc.tonecurve[ch_a][k].y = linear_ab[k]; for(int k=0; k<7; k++) ptc.tonecurve[ch_b][k].x = linear_ab[k]; for(int k=0; k<7; k++) ptc.tonecurve[ch_b][k].y = linear_ab[k]; // Set the base tonecurve if (curve_kind == linear) { ptc.tonecurve[ch_L][0].x = 0.0; ptc.tonecurve[ch_L][0].y = 0.0; ptc.tonecurve[ch_L][1].x = ptc_split[0] / 2.0; ptc.tonecurve[ch_L][1].y = ptc_split[0] / 2.0; ptc.tonecurve[ch_L][2].x = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0; ptc.tonecurve[ch_L][2].y = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0; ptc.tonecurve[ch_L][3].x = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0; ptc.tonecurve[ch_L][3].y = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0; ptc.tonecurve[ch_L][4].x = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0; ptc.tonecurve[ch_L][4].y = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0; ptc.tonecurve[ch_L][5].x = 1.0; ptc.tonecurve[ch_L][5].y = 1.0; } else { for (int k=0; k<6; k++) { ptc.tonecurve[ch_L][k].x = curve_pts[k][0] / 255.0; ptc.tonecurve[ch_L][k].y = curve_pts[k][1] / 255.0; } } if (curve_kind != custom) { // set shadows/darks/lights/highlight adjustments ptc.tonecurve[ch_L][1].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[0] / 100.0); ptc.tonecurve[ch_L][2].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[1] / 100.0); ptc.tonecurve[ch_L][3].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[2] / 100.0); ptc.tonecurve[ch_L][4].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[3] / 100.0); if (ptc.tonecurve[ch_L][1].y > ptc.tonecurve[ch_L][2].y) ptc.tonecurve[ch_L][1].y = ptc.tonecurve[ch_L][2].y; if (ptc.tonecurve[ch_L][3].y > ptc.tonecurve[ch_L][4].y) ptc.tonecurve[ch_L][4].y = ptc.tonecurve[ch_L][3].y; } dt_add_hist (imgid, "tonecurve", (dt_iop_params_t *)&ptc, sizeof(dt_iop_tonecurve_params_t), imported, sizeof(imported), LRDT_TONECURVE_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_colorzones) { pcz.channel = DT_IOP_COLORZONES_h; for (int i=0; i<3; i++) for (int k=0; k<8; k++) pcz.equalizer_x[i][k] = k/(DT_IOP_COLORZONES_BANDS-1.0); dt_add_hist (imgid, "colorzones", (dt_iop_params_t *)&pcz, sizeof(dt_iop_colorzones_params_t), imported, sizeof(imported), LRDT_COLORZONES_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_splittoning) { pst.compress = 50.0; dt_add_hist (imgid, "splittoning", (dt_iop_params_t *)&pst, sizeof(dt_iop_splittoning_params_t), imported, sizeof(imported), LRDT_SPLITTONING_VERSION, &n_import); refresh_needed=TRUE; } if (dev != NULL && has_bilat) { pbl.sigma_r = 100.0; pbl.sigma_s = 100.0; dt_add_hist (imgid, "bilat", (dt_iop_params_t *)&pbl, sizeof(dt_iop_bilat_params_t), imported, sizeof(imported), LRDT_BILAT_VERSION, &n_import); refresh_needed=TRUE; } if (has_tags) { if (imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("tags"), sizeof(imported)); n_import++; } if (dev == NULL && has_rating) { dt_ratings_apply_to_image(imgid, rating); if (imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("rating"), sizeof(imported)); n_import++; } if (dev == NULL && has_gps) { dt_image_set_location(imgid, lon, lat); if (imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("geotagging"), sizeof(imported)); n_import++; } if (dev == NULL && has_colorlabel) { dt_colorlabels_set_label(imgid, color); if (imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("color label"), sizeof(imported)); n_import++; } if(dev != NULL && refresh_needed && dev->gui_attached) { char message[512]; g_snprintf (message, sizeof(message), ngettext("%s has been imported", "%s have been imported", n_import), imported); dt_control_log(message); if (!iauto) { /* signal history changed */ dt_dev_reload_history_items(dev); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); /* update xmp file */ dt_image_synch_xmp(imgid); dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE); } } }
void dt_film_import1(dt_job_t *job, dt_film_t *film) { gboolean recursive = dt_conf_get_bool("ui_last/import_recursive"); /* first of all gather all images to import */ GList *images = NULL; images = _film_recursive_get_files(film->dirname, recursive, &images); if(g_list_length(images) == 0) { dt_control_log(_("no supported images were found to be imported")); return; } #ifdef USE_LUA /* pre-sort image list for easier handling in Lua code */ images = g_list_sort(images, (GCompareFunc)_film_filename_cmp); dt_lua_lock(); lua_State *L = darktable.lua_state.state; { GList *elt = images; lua_newtable(L); while(elt) { lua_pushstring(L, elt->data); luaL_ref(L, -2); elt = g_list_next(elt); } } lua_pushvalue(L, -1); dt_lua_event_trigger(L, "pre-import", 1); { g_list_free_full(images, g_free); // recreate list of images images = NULL; lua_pushnil(L); /* first key */ while(lua_next(L, -2) != 0) { /* uses 'key' (at index -2) and 'value' (at index -1) */ void *filename = strdup(luaL_checkstring(L, -1)); lua_pop(L, 1); images = g_list_prepend(images, filename); } } lua_pop(L, 1); // remove the table again from the stack dt_lua_unlock(); #endif if(g_list_length(images) == 0) { // no error message, lua probably emptied the list on purpose return; } /* we got ourself a list of images, lets sort and start import */ images = g_list_sort(images, (GCompareFunc)_film_filename_cmp); /* let's start import of images */ gchar message[512] = { 0 }; double fraction = 0; guint total = g_list_length(images); g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image", "importing %d images", total), total); dt_control_job_set_progress_message(job, message); /* loop thru the images and import to current film roll */ dt_film_t *cfr = film; GList *image = g_list_first(images); do { gchar *cdn = g_path_get_dirname((const gchar *)image->data); /* check if we need to initialize a new filmroll */ if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0) { // FIXME: maybe refactor into function and call it? if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the jsut imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ size_t len = strlen(dfn); if(strcmp(dfn + len - 4, ".gpx") == 0 || strcmp(dfn + len - 4, ".GPX") == 0) { gchar *gpx_file = g_build_path(G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); gchar *tz = dt_conf_get_string("plugins/lighttable/geotagging/tz"); dt_control_gpx_apply(gpx_file, cfr->id, tz); g_free(gpx_file); g_free(tz); } } } /* cleanup previously imported filmroll*/ if(cfr && cfr != film) { if(dt_film_is_empty(cfr->id)) { dt_film_remove(cfr->id); } dt_film_cleanup(cfr); free(cfr); cfr = NULL; } /* initialize and create a new film to import to */ cfr = malloc(sizeof(dt_film_t)); dt_film_init(cfr); dt_film_new(cfr, cdn); } g_free(cdn); /* import image */ dt_image_import(cfr->id, (const gchar *)image->data, FALSE); fraction += 1.0 / total; dt_control_job_set_progress(job, fraction); } while((image = g_list_next(image)) != NULL); g_list_free_full(images, g_free); // only redraw at the end, to not spam the cpu with exposure events dt_control_queue_redraw_center(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_IMPORTED, film->id); // FIXME: maybe refactor into function and call it? if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the just imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ size_t len = strlen(dfn); if(strcmp(dfn + len - 4, ".gpx") == 0 || strcmp(dfn + len - 4, ".GPX") == 0) { gchar *gpx_file = g_build_path(G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); gchar *tz = dt_conf_get_string("plugins/lighttable/geotagging/tz"); dt_control_gpx_apply(gpx_file, cfr->id, tz); g_free(gpx_file); g_free(tz); } } } /* cleanup previously imported filmroll*/ if(cfr && cfr != film) { dt_film_cleanup(cfr); free(cfr); } }
void dt_dev_read_history(dt_develop_t *dev) { if(dev->image_storage.id <= 0) return; if(!dev->iop) return; // maybe prepend auto-presets to history before loading it: auto_apply_presets(dev); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version, multi_priority, multi_name from history where imgid = ?1 order by num", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->image_storage.id); dev->history_end = 0; while(sqlite3_step(stmt) == SQLITE_ROW) { // db record: // 0-img, 1-num, 2-module_instance, 3-operation char, 4-params blob, 5-enabled, 6-blend_params, 7-blendop_version, 8 multi_priority, 9 multi_name dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t)); hist->enabled = sqlite3_column_int(stmt, 5); GList *modules = dev->iop; const char *opname = (const char *)sqlite3_column_text(stmt, 3); int multi_priority = sqlite3_column_int(stmt, 8); const char *multi_name = (const char *)sqlite3_column_text(stmt, 9); if(!opname) { fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n", dev->image_storage.filename); free(hist); continue; } hist->module = NULL; dt_iop_module_t *find_op = NULL; while(opname && modules) { dt_iop_module_t *module = (dt_iop_module_t *)modules->data; if(!strcmp(module->op, opname)) { if (module->multi_priority == multi_priority) { hist->module = module; if(multi_name && strcmp(module->multi_name, multi_name)) snprintf(module->multi_name, 128, "%s", multi_name); break; } else if (multi_priority > 0) { //we just say that we find the name, so we just have to add new instance of this module find_op = module; } } modules = g_list_next(modules); } if (!hist->module && find_op) { //we have to add a new instance of this module and set index to modindex dt_iop_module_t *new_module = (dt_iop_module_t *)malloc(sizeof(dt_iop_module_t)); if (!dt_iop_load_module(new_module, find_op->so, dev)) { new_module->multi_priority = multi_priority; snprintf(new_module->multi_name,128,"%s",multi_name); dev->iop = g_list_insert_sorted(dev->iop, new_module, sort_plugins); new_module->instance = find_op->instance; hist->module = new_module; } } if(!hist->module && opname) { fprintf(stderr, "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n", opname, dev->image_storage.filename); free(hist); continue; } if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK) { free(hist); continue; } int modversion = sqlite3_column_int(stmt, 2); assert(strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op) == 0); hist->params = malloc(hist->module->params_size); hist->blend_params = malloc(sizeof(dt_develop_blend_params_t)); snprintf(hist->multi_name,128,"%s",multi_name); hist->multi_priority = multi_priority; const void *blendop_params = sqlite3_column_blob(stmt, 6); int bl_length = sqlite3_column_bytes(stmt, 6); int blendop_version = sqlite3_column_int(stmt, 7); if (blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t))) { memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t)); } else if (blendop_params && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params, dt_develop_blend_version(), bl_length) == 0) { // do nothing } else { memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t)); } if(hist->module->version() != modversion || hist->module->params_size != sqlite3_column_bytes(stmt, 4) || strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op)) { if(!hist->module->legacy_params || hist->module->legacy_params(hist->module, sqlite3_column_blob(stmt, 4), labs(modversion), hist->params, labs(hist->module->version()))) { free(hist->params); free(hist->blend_params); fprintf(stderr, "[dev_read_history] module `%s' version mismatch: history is %d, dt %d.\n", hist->module->op, modversion, hist->module->version()); const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename); while(fname > dev->image_storage.filename && *fname != '/') fname --; if(fname > dev->image_storage.filename) fname++; dt_control_log(_("%s: module `%s' version mismatch: %d != %d"), fname, hist->module->op, hist->module->version(), modversion); free(hist); continue; } } else { memcpy(hist->params, sqlite3_column_blob(stmt, 4), hist->module->params_size); } // make sure that always-on modules are always on. duh. if(hist->module->default_enabled == 1 && hist->module->hide_enable_button == 1) { hist->enabled = 1; } // memcpy(hist->module->params, hist->params, hist->module->params_size); // hist->module->enabled = hist->enabled; // printf("[dev read history] img %d number %d for operation %d - %s params %f %f\n", sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1), instance, hist->module->op, *(float *)hist->params, *(((float*)hist->params)+1)); dev->history = g_list_append(dev->history, hist); dev->history_end ++; } if(dev->gui_attached) { dev->pipe->changed |= DT_DEV_PIPE_SYNCH; dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now. dt_dev_invalidate_all(dev); /* signal history changed */ dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE); } sqlite3_finalize (stmt); }
void dt_control_queue_redraw() { dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_REDRAW_ALL); }
static gboolean _lib_filmstrip_button_press_callback(GtkWidget *w, GdkEventButton *e, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data; int32_t mouse_over_id = strip->mouse_over_id; strip->select = DT_LIB_FILMSTRIP_SELECT_NONE; if (e->button == 1) { if(e->type == GDK_BUTTON_PRESS) { /* let check if any thumb controls was clicked */ switch(strip->image_over) { case DT_VIEW_DESERT: /* is this an activation of image */ if ((e->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0) strip->select = DT_LIB_FILMSTRIP_SELECT_SINGLE; else if ((e->state & (GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) strip->select = DT_LIB_FILMSTRIP_SELECT_TOGGLE; else if ((e->state & (GDK_SHIFT_MASK)) == GDK_SHIFT_MASK) strip->select = DT_LIB_FILMSTRIP_SELECT_RANGE; if(strip->select != DT_LIB_FILMSTRIP_SELECT_NONE) { strip->select_id = mouse_over_id; return TRUE; } break; case DT_VIEW_REJECT: case DT_VIEW_STAR_1: case DT_VIEW_STAR_2: case DT_VIEW_STAR_3: case DT_VIEW_STAR_4: case DT_VIEW_STAR_5: { int offset = 0; if(mouse_over_id == strip->activated_image) offset = dt_collection_image_offset(mouse_over_id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); if(strip->image_over == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else if(strip->image_over == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= strip->image_over; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); dt_collection_hint_message(darktable.collection); // More than this, we need to redraw all if(mouse_over_id == strip->activated_image) if(_lib_filmstrip_imgid_in_collection(darktable.collection, mouse_over_id) == 0) dt_view_filmstrip_scroll_relative(0, offset); gtk_widget_queue_draw(darktable.view_manager->proxy.filmstrip.module->widget); return TRUE; } default: return FALSE; } } else if(e->type == GDK_2BUTTON_PRESS) { if (mouse_over_id > 0) { strip->activated_image = mouse_over_id; dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE); return TRUE; } } } return FALSE; }
// Get the display ICC profile of the monitor associated with the widget. // For X display, uses the ICC profile specifications version 0.2 from // http://burtonini.com/blog/computers/xicc // Based on code from Gimp's modules/cdisplay_lcms.c void dt_ctl_set_display_profile() { if(!dt_control_running()) return; // make sure that no one gets a broken profile // FIXME: benchmark if the try is really needed when moving/resizing the window. Maybe we can just lock it and block if(pthread_rwlock_trywrlock(&darktable.control->xprofile_lock)) return; // we are already updating the profile. Or someone is reading right now. Too bad we can't distinguish that. Whatever ... GtkWidget *widget = dt_ui_center(darktable.gui->ui); guint8 *buffer = NULL; gint buffer_size = 0; gchar *profile_source = NULL; #if defined GDK_WINDOWING_X11 // we will use the xatom no matter what configured when compiled without colord gboolean use_xatom = TRUE; #if defined USE_COLORDGTK gboolean use_colord = TRUE; gchar *display_profile_source = dt_conf_get_string("ui_last/display_profile_source"); if(display_profile_source) { if(!strcmp(display_profile_source, "xatom")) use_colord = FALSE; else if(!strcmp(display_profile_source, "colord")) use_xatom = FALSE; g_free(display_profile_source); } #endif /* let's have a look at the xatom, just in case ... */ if(use_xatom) { GdkScreen *screen = gtk_widget_get_screen(widget); if ( screen==NULL ) screen = gdk_screen_get_default(); int monitor = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window(widget)); char *atom_name; if (monitor > 0) atom_name = g_strdup_printf("_ICC_PROFILE_%d", monitor); else atom_name = g_strdup("_ICC_PROFILE"); profile_source = g_strdup_printf("xatom %s", atom_name); GdkAtom type = GDK_NONE; gint format = 0; gdk_property_get(gdk_screen_get_root_window(screen), gdk_atom_intern(atom_name, FALSE), GDK_NONE, 0, 64 * 1024 * 1024, FALSE, &type, &format, &buffer_size, &buffer); g_free(atom_name); } #ifdef USE_COLORDGTK /* also try to get the profile from colord. this will set the value asynchronously! */ if(use_colord) { CdWindow *window = cd_window_new(); GtkWidget *center_widget = dt_ui_center(darktable.gui->ui); cd_window_get_profile(window, center_widget, NULL, dt_ctl_get_display_profile_colord_callback, NULL); } #endif #elif defined GDK_WINDOWING_QUARTZ GdkScreen *screen = gtk_widget_get_screen(widget); if ( screen==NULL ) screen = gdk_screen_get_default(); int monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(widget)); CGDirectDisplayID ids[monitor + 1]; uint32_t total_ids; CMProfileRef prof = NULL; if(CGGetOnlineDisplayList(monitor + 1, &ids[0], &total_ids) == kCGErrorSuccess && total_ids == monitor + 1) CMGetProfileByAVID(ids[monitor], &prof); if ( prof!=NULL ) { CFDataRef data; data = CMProfileCopyICCData(NULL, prof); CMCloseProfile(prof); UInt8 *tmp_buffer = (UInt8 *) g_malloc(CFDataGetLength(data)); CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), tmp_buffer); buffer = (guint8 *) tmp_buffer; buffer_size = CFDataGetLength(data); CFRelease(data); } profile_source = g_strdup("osx color profile api"); #elif defined G_OS_WIN32 (void)widget; HDC hdc = GetDC (NULL); if ( hdc!=NULL ) { DWORD len = 0; GetICMProfile (hdc, &len, NULL); gchar *path = g_new (gchar, len); if (GetICMProfile (hdc, &len, path)) { gsize size; g_file_get_contents(path, (gchar**)&buffer, &size, NULL); buffer_size = size; } g_free (path); ReleaseDC (NULL, hdc); } profile_source = g_strdup("windows color profile api"); #endif int profile_changed = buffer_size > 0 && (darktable.control->xprofile_size != buffer_size || memcmp(darktable.control->xprofile_data, buffer, buffer_size) != 0); if(profile_changed) { cmsHPROFILE profile = NULL; char name[512]; // thanks to ufraw for this! g_free(darktable.control->xprofile_data); darktable.control->xprofile_data = buffer; darktable.control->xprofile_size = buffer_size; profile = cmsOpenProfileFromMem(buffer, buffer_size); if(profile) { dt_colorspaces_get_profile_name(profile, "en", "US", name, sizeof(name)); cmsCloseProfile(profile); } dt_print(DT_DEBUG_CONTROL, "[color profile] we got a new screen profile `%s' from the %s (size: %d)\n", *name?name:"(unknown)", profile_source, buffer_size); } pthread_rwlock_unlock(&darktable.control->xprofile_lock); if(profile_changed) dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_PROFILE_CHANGED); g_free(profile_source); }
int dt_film_import(const char *dirname) { int v = dt_film_import_blocking(dirname,0); dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED); return v; }