void dt_dev_reload_image(dt_develop_t *dev, const uint32_t imgid) { const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, imgid); dev->image_storage = *image; dt_image_cache_read_release(darktable.image_cache, image); dev->image_force_reload = dev->image_loading = dev->preview_loading = 1; dev->pipe->changed |= DT_DEV_PIPE_SYNCH; dt_dev_invalidate(dev); // only invalidate image, preview will follow once it's loaded. }
void dt_image_remove(const int32_t imgid) { sqlite3_stmt *stmt; const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid); int old_group_id = img->group_id; dt_image_cache_read_release(darktable.image_cache, img); // make sure we remove from the cache first, or else the cache will look for imgid in sql dt_image_cache_remove(darktable.image_cache, imgid); int new_group_id = dt_grouping_remove_from_group(imgid); if(darktable.gui && darktable.gui->expanded_group_id == old_group_id) darktable.gui->expanded_group_id = new_group_id; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from images where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(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 = ?1)) or " "(id1 in (select tagid from tagged_images where imgid = ?1))", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from tagged_images where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from history where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from color_labels where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from meta_data where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from selected_images where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); // also clear all thumbnails in mipmap_cache. dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); }
void dt_ratings_apply_to_image (int imgid, int rating) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); // one star is a toggle, so you can easily reject images by removing the last star: if(((image->flags & 0x7) == 1) && (rating == 1)) rating = 0; image->flags = (image->flags & ~0x7) | (0x7 & rating); // synch through: 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); }
static void remove_preset_flag(const int imgid) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); // clear flag image->flags &= ~DT_IMAGE_AUTO_PRESETS_APPLIED; // write through to sql+xmp dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, cimg); }
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; /* is this an activation of image */ if (e->button == 1 && 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; } /* let check if any thumb controls was clicked */ switch(strip->image_over) { case DT_VIEW_DESERT: 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: { 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); image->dirty = 1; 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); break; } default: return FALSE; } return TRUE; }
void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params) { dt_imageio_email_t *d = (dt_imageio_email_t *)params; // All images are exported, generate a mailto uri and startup default mail client gchar uri[4096]= {0}; gchar body[4096]= {0}; gchar attachments[4096]= {0}; gchar *uriFormat=NULL; gchar *subject=_("images exported from darktable"); gchar *imageBodyFormat="%s %s"; // filename, exif oneliner gchar *attachmentFormat=NULL; gchar *attachmentSeparator=""; // If no default handler detected above, we use gtk_show_uri with mailto:// and hopes things goes right.. uriFormat="xdg-email --subject \"%s\" --body \"%s\" %s &"; // subject, body, and list of attachments with format: attachmentFormat=" --attach \"%s\""; while( d->images ) { gchar exif[256]= {0}; _email_attachment_t *attachment=( _email_attachment_t *)d->images->data; gchar *filename = g_path_get_basename( attachment->file ); const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, attachment->imgid); dt_image_print_exif(img, exif, sizeof(exif)); g_snprintf(body+strlen(body),4096-strlen(body), imageBodyFormat, filename, exif ); g_free(filename); if( strlen( attachments ) ) g_snprintf(attachments+strlen(attachments),4096-strlen(attachments), "%s", attachmentSeparator ); g_snprintf(attachments+strlen(attachments),4096-strlen(attachments), attachmentFormat, attachment->file ); // Free attachment item and remove dt_image_cache_read_release(darktable.image_cache, img); g_free( d->images->data ); d->images = g_list_remove( d->images, d->images->data ); } // build uri and launch before we quit... g_snprintf(uri, sizeof(uri), uriFormat, subject, body, attachments ); fprintf(stderr, "[email] launching `%s'\n", uri); if(system( uri ) < 0) { // TODO: after string freeze is broken again, report to ui: // dt_control_log(_("could not launch email client!")); fprintf(stderr, "[email] could not launch subprocess!\n"); } }
static void _view_map_filmstrip_activate_callback(gpointer instance, gpointer user_data) { double longitude, latitude; int32_t imgid = 0; if ((imgid=dt_view_filmstrip_get_activated_imgid(darktable.view_manager))>0) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); longitude = cimg->longitude; latitude = cimg->latitude; dt_image_cache_read_release(darktable.image_cache, cimg); if(!isnan(longitude) && !isnan(latitude)) _view_map_center_on_location((dt_view_t*)user_data, longitude, latitude, 16); // TODO: is it better to keep the zoom level? } }
void dt_image_set_location(const int32_t imgid, double lon, double lat) { /* fetch image from cache */ const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); if (!cimg) return; dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); /* set image location */ image->longitude = lon; image->latitude = lat; /* store */ dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); }
int group_with(lua_State *L) { dt_lua_image_t first_image; luaA_to(L,dt_lua_image_t,&first_image,1); if(lua_isnoneornil(L,2)) { dt_grouping_remove_from_group(first_image); return 0; } dt_lua_image_t second_image; luaA_to(L,dt_lua_image_t,&second_image,2); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, second_image); int group_id = cimg->group_id; dt_image_cache_read_release(darktable.image_cache, cimg); dt_grouping_add_to_group(group_id,first_image); return 0; }
static void deflicker_prepare_histogram(dt_iop_module_t *self, uint32_t **histogram, dt_dev_histogram_stats_t *histogram_stats) { dt_mipmap_buffer_t buf; dt_mipmap_cache_get(darktable.mipmap_cache, &buf, self->dev->image_storage.id, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r'); const dt_image_t *img = dt_image_cache_get(darktable.image_cache, self->dev->image_storage.id, 'r'); dt_image_t image = *img; dt_image_cache_read_release(darktable.image_cache, img); if(!buf.buf) { dt_control_log(_("failed to get raw buffer from image `%s'"), image.filename); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); return; } dt_dev_histogram_collection_params_t histogram_params = { 0 }; dt_histogram_roi_t histogram_roi = {.width = image.width, .height = image.height, // FIXME: get those from rawprepare IOP somehow !!! .crop_x = image.crop_x, .crop_y = image.crop_y, .crop_width = image.crop_width, .crop_height = image.crop_height }; histogram_params.roi = &histogram_roi; histogram_params.bins_count = 16384; dt_histogram_worker(&histogram_params, histogram_stats, buf.buf, histogram, dt_histogram_helper_cs_RAW_uint16); histogram_stats->ch = 1u; dt_mipmap_cache_release(darktable.mipmap_cache, &buf); } /* input: 0 - 16384 (valid range: from black level to white level) */ /* output: -14 ... 0 */ static float raw_to_ev(uint32_t raw, uint32_t black_level, uint32_t white_level) { uint32_t raw_max = white_level - black_level; float raw_ev = -log2f(raw_max) + log2f(CLAMP(raw, 0.0f, 16384.0f)); return raw_ev; }
void dt_dev_load_image(dt_develop_t *dev, const uint32_t imgid) { const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, imgid); dev->image_storage = *image; dt_image_cache_read_release(darktable.image_cache, image); if(dev->pipe) { dev->pipe->processed_width = 0; dev->pipe->processed_height = 0; } dev->image_loading = 1; dev->preview_loading = 1; dev->image_dirty = dev->preview_dirty = 1; dev->iop = dt_iop_load_modules(dev); dt_dev_read_history(dev); }
void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params) { dt_imageio_email_t *d = (dt_imageio_email_t *)params; // All images are exported, generate a mailto uri and startup default mail client gchar uri[4096] = { 0 }; gchar body[4096] = { 0 }; gchar attachments[4096] = { 0 }; gchar *uriFormat = "xdg-email --subject \"%s\" --body \"%s\" %s &"; // subject, body format const gchar *subject = _("images exported from darktable"); const gchar *imageBodyFormat = " - %s (%s)\\n"; // filename, exif oneliner gchar *attachmentFormat = " --attach \"%s\""; // list of attachments format gchar *attachmentSeparator = ""; while(d->images) { gchar exif[256] = { 0 }; _email_attachment_t *attachment = (_email_attachment_t *)d->images->data; gchar *filename = g_path_get_basename(attachment->file); const dt_image_t *img = dt_image_cache_get(darktable.image_cache, attachment->imgid, 'r'); dt_image_print_exif(img, exif, sizeof(exif)); g_snprintf(body + strlen(body), sizeof(body) - strlen(body), imageBodyFormat, filename, exif); g_free(filename); if(*attachments) g_snprintf(attachments + strlen(attachments), sizeof(attachments) - strlen(attachments), "%s", attachmentSeparator); g_snprintf(attachments + strlen(attachments), sizeof(attachments) - strlen(attachments), attachmentFormat, attachment->file); // Free attachment item and remove dt_image_cache_read_release(darktable.image_cache, img); g_free(d->images->data); d->images = g_list_remove(d->images, d->images->data); } // build uri and launch before we quit... g_snprintf(uri, sizeof(uri), uriFormat, subject, body, attachments); fprintf(stderr, "[email] launching `%s'\n", uri); if(system(uri) < 0) { dt_control_log(_("could not launch email client!")); } }
/** remove an image from a group */ int dt_grouping_remove_from_group(int image_id) { sqlite3_stmt *stmt; int new_group_id = -1; const dt_image_t *img = dt_image_cache_get(darktable.image_cache, image_id, 'r'); int img_group_id = img->group_id; dt_image_cache_read_release(darktable.image_cache, img); if(img_group_id == image_id) { // get a new group_id for all the others in the group. also write it to the dt_image_t struct. DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where group_id = ?1 and id != ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, img_group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, image_id); while(sqlite3_step(stmt) == SQLITE_ROW) { int other_id = sqlite3_column_int(stmt, 0); if(new_group_id == -1) new_group_id = other_id; dt_image_t *other_img = dt_image_cache_get(darktable.image_cache, other_id, 'w'); other_img->group_id = new_group_id; dt_image_cache_write_release(darktable.image_cache, other_img, DT_IMAGE_CACHE_SAFE); } sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update images set group_id = ?1 where group_id = ?2 and id != ?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, new_group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, img_group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, image_id); sqlite3_step(stmt); sqlite3_finalize(stmt); } else { // change the group_id for this image. dt_image_t *wimg = dt_image_cache_get(darktable.image_cache, image_id, 'w'); new_group_id = wimg->group_id; wimg->group_id = image_id; dt_image_cache_write_release(darktable.image_cache, wimg, DT_IMAGE_CACHE_SAFE); } return new_group_id; }
static gboolean _lib_filmstrip_ratings_key_accel_callback(GtkAccelGroup *accel_group, GObject *aceeleratable, guint keyval, GdkModifierType modifier, gpointer data) { long int num = (long int)data; switch (num) { case DT_VIEW_DESERT: 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: case 666: { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if (mouse_over_id <= 0) return FALSE; /* get image from cache */ 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 (num == 666) image->flags &= ~0xf; else if (num == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7; else { image->flags &= ~0x7; image->flags |= num; } dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); /* redraw all */ dt_control_queue_redraw(); break; } default: break; } return TRUE; }
int32_t dt_control_write_sidecar_files_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; while(t) { imgid = (long int)t->data; const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid); char dtfilename[DT_MAX_PATH_LEN+4]; dt_image_full_path(img->id, dtfilename, DT_MAX_PATH_LEN); char *c = dtfilename + strlen(dtfilename); sprintf(c, ".xmp"); dt_exif_xmp_write(imgid, dtfilename); dt_image_cache_read_release(darktable.image_cache, img); t = g_list_delete_link(t, t); } return 0; }
static void _view_map_filmstrip_activate_callback(gpointer instance, gpointer user_data) { dt_view_t *self = (dt_view_t*)user_data; dt_map_t *lib = (dt_map_t*)self->data; double longitude, latitude; int32_t imgid = 0; if ((imgid=dt_view_filmstrip_get_activated_imgid(darktable.view_manager))>0) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); longitude = cimg->longitude; latitude = cimg->latitude; dt_image_cache_read_release(darktable.image_cache, cimg); if(!isnan(longitude) && !isnan(latitude)) { int zoom; g_object_get(G_OBJECT(lib->map), "zoom", &zoom, NULL); _view_map_center_on_location(self, longitude, latitude, zoom); // TODO: is it better to keep the zoom level or to zoom in? 16 is a nice close up. } } }
int dt_history_load_and_apply(int imgid, gchar *filename, int history_only) { int res = 0; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); if(img) { if(dt_exif_xmp_read(img, filename, history_only)) return 1; /* if current image in develop reload history */ if(dt_dev_is_current_image(darktable.develop, imgid)) dt_dev_reload_history_items (darktable.develop); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, img); dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); } return res; }
int get_group(lua_State *L) { dt_lua_image_t first_image; luaA_to(L,dt_lua_image_t,&first_image,1); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, first_image); int group_id = cimg->group_id; dt_image_cache_read_release(darktable.image_cache, cimg); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where group_id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id); lua_newtable(L); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); luaA_push(L,dt_lua_image_t,&imgid); luaL_ref(L,-2); } luaA_push(L,dt_lua_image_t,&group_id); lua_setfield(L,-2,"leader"); return 1; }
void dt_ratings_apply_to_selection (int rating) { uint32_t count = dt_collection_get_selected_count(darktable.collection); if (count) { dt_control_log(ngettext("applying rating %d to %d image", "applying rating %d to %d images", count), rating, count); #if 0 // not updating cache gchar query[1024]={0}; g_snprintf(query,1024, "update images set flags=(images.flags & ~7) | (7 & %d) where id in (select imgid from selected_images)", rating ); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); #endif /* for each selected image update rating */ sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, sqlite3_column_int(stmt, 0)); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); image->flags = (image->flags & ~0x7) | (0x7 & rating); // synch through: dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE); dt_image_cache_read_release(darktable.image_cache, image); } sqlite3_finalize(stmt); /* redraw view */ dt_control_queue_redraw_center(); } else dt_control_log(_("no images selected to apply rating")); }
static void _jump_to() { int32_t imgid = dt_control_get_mouse_over_id(); if(imgid == -1) { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) imgid = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); } if(imgid != -1) { char path[512]; const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r'); dt_image_film_roll_directory(img, path, sizeof(path)); dt_image_cache_read_release(darktable.image_cache, img); char collect[1024]; snprintf(collect, sizeof(collect), "1:0:0:%s$", path); dt_collection_deserialize(collect); } }
static void releasereadimage(lua_State*L,const dt_image_t* image) { dt_image_cache_read_release(darktable.image_cache,image); }
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); 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; }
int32_t dt_image_move(const int32_t imgid, const int32_t filmid) { //TODO: several places where string truncation could occur unnoticed int32_t result = -1; gchar oldimg[DT_MAX_PATH_LEN] = {0}; gchar newimg[DT_MAX_PATH_LEN] = {0}; dt_image_full_path(imgid, oldimg, DT_MAX_PATH_LEN); gchar *newdir = NULL; sqlite3_stmt *film_stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select folder from film_rolls where id = ?1", -1, &film_stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(film_stmt, 1, filmid); if(sqlite3_step(film_stmt) == SQLITE_ROW) newdir = g_strdup((gchar *) sqlite3_column_text(film_stmt, 0)); sqlite3_finalize(film_stmt); if(newdir) { gchar *imgbname = g_path_get_basename(oldimg); g_snprintf(newimg, DT_MAX_PATH_LEN, "%s%c%s", newdir, G_DIR_SEPARATOR, imgbname); g_free(imgbname); g_free(newdir); // statement for getting ids of the image to be moved and it's duplicates sqlite3_stmt *duplicates_stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select 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, &duplicates_stmt, NULL); // move image // TODO: Use gio's' g_file_move instead of g_rename? if (!g_file_test(newimg, G_FILE_TEST_EXISTS) && (g_rename(oldimg, newimg) == 0)) { // first move xmp files of image and duplicates GList *dup_list = NULL; DT_DEBUG_SQLITE3_BIND_INT(duplicates_stmt, 1, imgid); while (sqlite3_step(duplicates_stmt) == SQLITE_ROW) { int32_t id = sqlite3_column_int(duplicates_stmt, 0); dup_list = g_list_append(dup_list, GINT_TO_POINTER(id)); gchar oldxmp[512], newxmp[512]; g_strlcpy(oldxmp, oldimg, 512); g_strlcpy(newxmp, newimg, 512); dt_image_path_append_version(id, oldxmp, 512); dt_image_path_append_version(id, newxmp, 512); g_strlcat(oldxmp, ".xmp", 512); g_strlcat(newxmp, ".xmp", 512); if (g_file_test(oldxmp, G_FILE_TEST_EXISTS)) (void)g_rename(oldxmp, newxmp); } sqlite3_reset(duplicates_stmt); sqlite3_clear_bindings(duplicates_stmt); // then update database and cache // if update was performed in above loop, dt_image_path_append_version() // would return wrong version! while (dup_list) { long int id = GPOINTER_TO_INT(dup_list->data); 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->film_id = filmid; // 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); dup_list = g_list_delete_link(dup_list, dup_list); } g_list_free(dup_list); result = 0; } } return result; }
int32_t dt_control_merge_hdr_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("merging %d image", "merging %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 1, message); float *pixels = NULL; float *weight = NULL; int wd = 0, ht = 0, first_imgid = -1; uint32_t filter = 0; float whitelevel = 0.0f; total ++; while(t) { imgid = (long int)t->data; dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING); // just take a copy. also do it after blocking read, so filters and bpp will make sense. const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t image = *img; dt_image_cache_read_release(darktable.image_cache, img); if(image.filters == 0 || image.bpp != sizeof(uint16_t)) { dt_control_log(_("exposure bracketing only works on raw images")); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } filter = dt_image_flipped_filter(img); if(buf.size != DT_MIPMAP_FULL) { dt_control_log(_("failed to get raw buffer from image `%s'"), image.filename); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } if(!pixels) { first_imgid = imgid; pixels = (float *)malloc(sizeof(float)*image.width*image.height); weight = (float *)malloc(sizeof(float)*image.width*image.height); memset(pixels, 0x0, sizeof(float)*image.width*image.height); memset(weight, 0x0, sizeof(float)*image.width*image.height); wd = image.width; ht = image.height; } else if(image.width != wd || image.height != ht) { dt_control_log(_("images have to be of same size!")); free(pixels); free(weight); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); goto error; } // if no valid exif data can be found, assume peleng fisheye at f/16, 8mm, with half of the light lost in the system => f/22 const float eap = image.exif_aperture > 0.0f ? image.exif_aperture : 22.0f; const float efl = image.exif_focal_length > 0.0f ? image.exif_focal_length : 8.0f; const float rad = .5f * efl/eap; const float aperture = M_PI * rad * rad; const float iso = image.exif_iso > 0.0f ? image.exif_iso : 100.0f; const float exp = image.exif_exposure > 0.0f ? image.exif_exposure : 1.0f; const float cal = 100.0f/(aperture*exp*iso); // about proportional to how many photons we can expect from this shot: const float photoncnt = 100.0f*aperture*exp/iso; // stupid, but we don't know the real sensor saturation level: uint16_t saturation = 0; for(int k=0; k<wd*ht; k++) saturation = MAX(saturation, ((uint16_t *)buf.buf)[k]); // seems to be around 64500--64700 for 5dm2 // fprintf(stderr, "saturation: %u\n", saturation); whitelevel = fmaxf(whitelevel, saturation*cal); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(buf, pixels, weight, wd, ht, saturation) #endif for(int k=0; k<wd*ht; k++) { const uint16_t in = ((uint16_t *)buf.buf)[k]; // weights based on siggraph 12 poster // zijian zhu, zhengguo li, susanto rahardja, pasi fraenti // 2d denoising factor for high dynamic range imaging float w = envelope(in/(float)saturation) * photoncnt; // in case we are black and drop to zero weight, give it something // just so numerics don't collapse. blown out whites are handled below. if(w < 1e-3f && in < saturation/3) w = 1e-3f; pixels[k] += w * in * cal; weight[k] += w; } t = g_list_delete_link(t, t); /* update backgroundjob ui plate */ fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } // normalize by white level to make clipping at 1.0 work as expected (to be sure, scale down one more stop, thus the 0.5): #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(pixels, wd, ht, weight, whitelevel) #endif for(int k=0; k<wd*ht; k++) { // in case w == 0, all pixels were overexposed (too dark would have been clamped to w >= eps above) if(weight[k] < 1e-3f) pixels[k] = 1.f; // mark as blown out. else // normalize: pixels[k] = fmaxf(0.0f, pixels[k]/(whitelevel*weight[k])); } // output hdr as digital negative with exif data. uint8_t exif[65535]; char pathname[DT_MAX_PATH_LEN]; dt_image_full_path(first_imgid, pathname, DT_MAX_PATH_LEN); // last param is dng mode const int exif_len = dt_exif_read_blob(exif, pathname, first_imgid, 0, wd, ht, 1); char *c = pathname + strlen(pathname); while(*c != '.' && c > pathname) c--; g_strlcpy(c, "-hdr.dng", sizeof(pathname)-(c-pathname)); dt_imageio_write_dng(pathname, pixels, wd, ht, exif, exif_len, filter, 1.0f); dt_control_backgroundjobs_progress(darktable.control, jid, 1.0f); while(*c != '/' && c > pathname) c--; dt_control_log(_("wrote merged hdr `%s'"), c+1); // import new image gchar *directory = g_path_get_dirname((const gchar *)pathname); dt_film_t film; const int filmid = dt_film_new(&film, directory); dt_image_import(filmid, pathname, TRUE); g_free (directory); free(pixels); free(weight); error: dt_control_backgroundjobs_destroy(darktable.control, jid); dt_control_queue_redraw_center(); return 0; }
int32_t dt_image_duplicate(const int32_t imgid) { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into images " "(id, group_id, film_id, width, height, filename, maker, model, lens, exposure, " "aperture, iso, focal_length, focus_distance, datetime_taken, flags, " "output_width, output_height, crop, raw_parameters, raw_denoise_threshold, " "raw_auto_bright_threshold, raw_black, raw_maximum, orientation) " "select null, group_id, film_id, width, height, filename, maker, model, lens, " "exposure, aperture, iso, focal_length, focus_distance, datetime_taken, " "flags, width, height, crop, raw_parameters, raw_denoise_threshold, " "raw_auto_bright_threshold, raw_black, raw_maximum, orientation " "from images where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select a.id from images as a join images as b where " "a.film_id = b.film_id and a.filename = b.filename and " "b.id = ?1 order by a.id desc", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); int32_t newid = -1; if(sqlite3_step(stmt) == SQLITE_ROW) newid = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); if(newid != -1) { DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into color_labels (imgid, color) select ?1, color from " "color_labels where imgid = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into meta_data (id, key, value) select ?1, key, value " "from meta_data where id = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into tagged_images (imgid, tagid) select ?1, tagid from " "tagged_images where imgid = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update tagxtag set count = count + 1 where " "(id1 in (select tagid from tagged_images where imgid = ?1)) or " "(id2 in (select tagid from tagged_images where imgid = ?1))", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid); sqlite3_step(stmt); sqlite3_finalize(stmt); if(darktable.gui && darktable.gui->grouping) { const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, newid); darktable.gui->expanded_group_id = img->group_id; dt_image_cache_read_release(darktable.image_cache, img); dt_collection_update_query(darktable.collection); } } return newid; }
int main(int argc, char *arg[]) { bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); gtk_init (&argc, &arg); // parse command line arguments char *image_filename = NULL; char *xmp_filename = NULL; char *output_filename = NULL; int file_counter = 0; int width = 0, height = 0, bpp = 0; gboolean verbose = FALSE, high_quality = TRUE; for(int k=1; k<argc; k++) { if(arg[k][0] == '-') { if(!strcmp(arg[k], "--help")) { usage(arg[0]); exit(1); } else if(!strcmp(arg[k], "--version")) { printf("this is darktable-cli\ncopyright (c) 2012-2013 johannes hanika, tobias ellinghaus\n"); exit(1); } else if(!strcmp(arg[k], "--width")) { k++; width = MAX(atoi(arg[k]), 0); } else if(!strcmp(arg[k], "--height")) { k++; height = MAX(atoi(arg[k]), 0); } else if(!strcmp(arg[k], "--bpp")) { k++; bpp = MAX(atoi(arg[k]), 0); fprintf(stderr, "%s %d\n", _("TODO: sorry, due to api restrictions we currently cannot set the bpp to"), bpp); } else if(!strcmp(arg[k], "--hq")) { k++; gchar *str = g_ascii_strup(arg[k], -1); if(!g_strcmp0(str, "0") || !g_strcmp0(str, "FALSE")) high_quality = FALSE; else if(!g_strcmp0(str, "1") || !g_strcmp0(str, "TRUE")) high_quality = TRUE; else { fprintf(stderr, "%s: %s\n", _("Unknown option for --hq"), arg[k]); usage(arg[0]); exit(1); } g_free(str); } else if(!strcmp(arg[k], "-v") || !strcmp(arg[k], "--verbose")) { verbose = TRUE; } } else { if(file_counter == 0) image_filename = arg[k]; else if(file_counter == 1) xmp_filename = arg[k]; else if(file_counter == 2) output_filename = arg[k]; file_counter++; } } if(file_counter < 2 || file_counter > 3) { usage(arg[0]); exit(1); } else if(file_counter == 2) { // no xmp file given output_filename = xmp_filename; xmp_filename = NULL; } // the output file already exists, so there will be a sequence number added if(g_file_test(output_filename, G_FILE_TEST_EXISTS)) { fprintf(stderr, "%s\n", _("output file already exists, it will get renamed")); } char *m_arg[] = {"darktable-cli", "--library", ":memory:", NULL}; // init dt without gui: if(dt_init(3, m_arg, 0)) exit(1); dt_film_t film; int id = 0; int filmid = 0; gchar *directory = g_path_get_dirname(image_filename); filmid = dt_film_new(&film, directory); id = dt_image_import(filmid, image_filename, TRUE); if(!id) { fprintf(stderr, _("error: can't open file %s"), image_filename); fprintf(stderr, "\n"); exit(1); } g_free(directory); // attach xmp, if requested: if(xmp_filename) { const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg); dt_exif_xmp_read(image, xmp_filename, 1); // don't write new xmp: dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, image); } // print the history stack if(verbose) { gchar *history = dt_history_get_items_as_string(id); if(history) printf("%s\n", history); else printf("[%s]\n", _("empty history stack")); } // try to find out the export format from the output_filename char *ext = output_filename + strlen(output_filename); while(ext > output_filename && *ext != '.') ext--; *ext = '\0'; ext++; if(!strcmp(ext, "jpg")) ext = "jpeg"; // init the export data structures int size = 0, dat_size = 0; dt_imageio_module_format_t *format; dt_imageio_module_storage_t *storage; dt_imageio_module_data_t *sdata, *fdata; storage = dt_imageio_get_storage_by_name("disk"); // only exporting to disk makes sense if(storage == NULL) { fprintf(stderr, "%s\n", _("cannot find disk storage module. please check your installation, something seems to be broken.")); exit(1); } sdata = storage->get_params(storage, &size); if(sdata == NULL) { fprintf(stderr, "%s\n", _("failed to get parameters from storage module, aborting export ...")); exit(1); } // and now for the really ugly hacks. don't tell your children about this one or they won't sleep at night any longer ... g_strlcpy((char*)sdata, output_filename, DT_MAX_PATH_LEN); // all is good now, the last line didn't happen. format = dt_imageio_get_format_by_name(ext); if(format == NULL) { fprintf(stderr, _("unknown extension '.%s'"), ext); fprintf(stderr, "\n"); exit(1); } fdata = format->get_params(format, &dat_size); if(fdata == NULL) { fprintf(stderr, "%s\n", _("failed to get parameters from format module, aborting export ...")); exit(1); } uint32_t w,h,fw,fh,sw,sh; fw=fh=sw=sh=0; storage->dimension(storage, &sw, &sh); format->dimension(format, &fw, &fh); if( sw==0 || fw==0) w=sw>fw?sw:fw; else w=sw<fw?sw:fw; if( sh==0 || fh==0) h=sh>fh?sh:fh; else h=sh<fh?sh:fh; fdata->max_width = width; fdata->max_height = height; fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width; fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height; fdata->style[0] = '\0'; //TODO: add a callback to set the bpp without going through the config storage->store(storage,sdata, id, format, fdata, 1, 1, high_quality); // cleanup time if(storage->finalize_store) storage->finalize_store(storage, sdata); storage->free_params(storage, sdata); format->free_params(format, fdata); dt_cleanup(); }
gboolean _variable_get_value(dt_variables_params_t *params, gchar *variable, gchar *value, size_t value_len) { const gchar *file_ext = NULL; gboolean got_value = FALSE; struct tm tim; localtime_r(¶ms->data->time, &tim); const gchar *homedir = dt_loc_get_home_dir(NULL); gchar *pictures_folder = NULL; if(g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) == NULL) pictures_folder = g_build_path(G_DIR_SEPARATOR_S, homedir, "Pictures", (char *)NULL); else pictures_folder = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_PICTURES)); if(params->filename) { file_ext = (g_strrstr(params->filename, ".") + 1); if(file_ext == (gchar *)1) file_ext = params->filename + strlen(params->filename); } /* image exif time */ gboolean have_exif_tm = FALSE; int exif_iso = 100; char camera_maker[64]; char camera_alias[64]; int version = 0; int stars = 0; struct tm exif_tm = { 0 }; if(params->imgid) { const dt_image_t *img = dt_image_cache_get(darktable.image_cache, params->imgid, 'r'); if(sscanf(img->exif_datetime_taken, "%d:%d:%d %d:%d:%d", &exif_tm.tm_year, &exif_tm.tm_mon, &exif_tm.tm_mday, &exif_tm.tm_hour, &exif_tm.tm_min, &exif_tm.tm_sec) == 6) { exif_tm.tm_year -= 1900; exif_tm.tm_mon--; have_exif_tm = TRUE; } exif_iso = img->exif_iso; g_strlcpy(camera_maker, img->camera_maker, sizeof(camera_maker)); g_strlcpy(camera_alias, img->camera_alias, sizeof(camera_alias)); version = img->version; stars = (img->flags & 0x7); if(stars == 6) stars = -1; dt_image_cache_read_release(darktable.image_cache, img); } else if (params->data->exif_time) { localtime_r(¶ms->data->exif_time, &exif_tm); have_exif_tm = TRUE; } if(g_strcmp0(variable, "$(YEAR)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.4d", tim.tm_year + 1900); else if(g_strcmp0(variable, "$(MONTH)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", tim.tm_mon + 1); else if(g_strcmp0(variable, "$(DAY)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", tim.tm_mday); else if(g_strcmp0(variable, "$(HOUR)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", tim.tm_hour); else if(g_strcmp0(variable, "$(MINUTE)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", tim.tm_min); else if(g_strcmp0(variable, "$(SECOND)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", tim.tm_sec); else if(g_strcmp0(variable, "$(EXIF_YEAR)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.4d", (have_exif_tm ? exif_tm.tm_year : tim.tm_year) + 1900); else if(g_strcmp0(variable, "$(EXIF_MONTH)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", (have_exif_tm ? exif_tm.tm_mon : tim.tm_mon) + 1); else if(g_strcmp0(variable, "$(EXIF_DAY)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", (have_exif_tm ? exif_tm.tm_mday : tim.tm_mday)); else if(g_strcmp0(variable, "$(EXIF_HOUR)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", (have_exif_tm ? exif_tm.tm_hour : tim.tm_hour)); else if(g_strcmp0(variable, "$(EXIF_MINUTE)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", (have_exif_tm ? exif_tm.tm_min : tim.tm_min)); else if(g_strcmp0(variable, "$(EXIF_SECOND)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.2d", (have_exif_tm ? exif_tm.tm_sec : tim.tm_sec)); else if(g_strcmp0(variable, "$(EXIF_ISO)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%d", exif_iso); else if(g_strcmp0(variable, "$(MAKER)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", camera_maker); else if(g_strcmp0(variable, "$(MODEL)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", camera_alias); else if(g_strcmp0(variable, "$(ID)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%d", params->imgid); else if(g_strcmp0(variable, "$(VERSION)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%d", version); else if(g_strcmp0(variable, "$(JOBCODE)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", params->jobcode); else if(g_strcmp0(variable, "$(ROLL_NAME)") == 0 && params->filename && (got_value = TRUE)) { gchar *dirname = g_path_get_dirname(params->filename); gchar *basename = g_path_get_basename(dirname); snprintf(value, value_len, "%s", basename); g_free(basename); g_free(dirname); } else if(g_strcmp0(variable, "$(FILE_DIRECTORY)") == 0 && params->filename && (got_value = TRUE)) { gchar *dirname = g_path_get_dirname(params->filename); snprintf(value, value_len, "%s", dirname); g_free(dirname); } // undocumented : backward compatibility else if(g_strcmp0(variable, "$(FILE_FOLDER)") == 0 && params->filename && (got_value = TRUE)) { gchar *dirname = g_path_get_dirname(params->filename); snprintf(value, value_len, "%s", dirname); g_free(dirname); } else if(g_strcmp0(variable, "$(FILE_NAME)") == 0 && params->filename && (got_value = TRUE)) { gchar *basename = g_path_get_basename(params->filename); snprintf(value, value_len, "%s", basename); g_free(basename); if(g_strrstr(value, ".")) *(g_strrstr(value, ".")) = 0; } else if(g_strcmp0(variable, "$(FILE_EXTENSION)") == 0 && params->filename && (got_value = TRUE)) snprintf(value, value_len, "%s", file_ext); else if(g_strcmp0(variable, "$(SEQUENCE)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%.4d", params->sequence >= 0 ? params->sequence : params->data->sequence); else if(g_strcmp0(variable, "$(USERNAME)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", g_get_user_name()); else if(g_strcmp0(variable, "$(HOME_FOLDER)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", homedir); // undocumented : backward compatibility else if(g_strcmp0(variable, "$(HOME)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", homedir); else if(g_strcmp0(variable, "$(PICTURES_FOLDER)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", pictures_folder); else if(g_strcmp0(variable, "$(DESKTOP_FOLDER)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP)); // undocumented : backward compatibility else if(g_strcmp0(variable, "$(DESKTOP)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%s", g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP)); else if(g_strcmp0(variable, "$(STARS)") == 0 && (got_value = TRUE)) snprintf(value, value_len, "%d", stars); else if(g_strcmp0(variable, "$(LABELS)") == 0 && (got_value = TRUE)) { // TODO: currently we concatenate all the color labels with a ',' as a separator. Maybe it's better to // only use the first/last label? GList *res = dt_metadata_get(params->imgid, "Xmp.darktable.colorlabels", NULL); res = g_list_first(res); if(res != NULL) { GList *labels = NULL; do { labels = g_list_append(labels, (char *)(_(dt_colorlabels_to_string(GPOINTER_TO_INT(res->data))))); } while((res = g_list_next(res)) != NULL); char *str = dt_util_glist_to_str(",", labels); g_list_free(labels); snprintf(value, value_len, "%s", str); g_free(str); } else { snprintf(value, value_len, "%s", _("none")); } g_list_free(res); } else if(g_strcmp0(variable, "$(TITLE)") == 0 && params->filename && (got_value = TRUE)) { GList *res = dt_metadata_get(params->imgid, "Xmp.dc.title", NULL); res = g_list_first(res); if(res != NULL) { snprintf(value, value_len, "%s", (char *)res->data); } else { snprintf(value, value_len, "%s", _("none")); } g_list_free_full(res, &g_free); } else if(g_strcmp0(variable, "$(CREATOR)") == 0 && params->filename && (got_value = TRUE)) { GList *res = dt_metadata_get(params->imgid, "Xmp.dc.creator", NULL); res = g_list_first(res); if(res != NULL) { snprintf(value, value_len, "%s", (char *)res->data); } else { snprintf(value, value_len, "%s", _("none")); } g_list_free_full(res, &g_free); } else if(g_strcmp0(variable, "$(PUBLISHER)") == 0 && params->filename && (got_value = TRUE)) { GList *res = dt_metadata_get(params->imgid, "Xmp.dc.publisher", NULL); res = g_list_first(res); if(res != NULL) { snprintf(value, value_len, "%s", (char *)res->data); } else { snprintf(value, value_len, "%s", _("none")); } g_list_free_full(res, &g_free); } else if(g_strcmp0(variable, "$(RIGHTS)") == 0 && params->filename && (got_value = TRUE)) { GList *res = dt_metadata_get(params->imgid, "Xmp.dc.rights", NULL); res = g_list_first(res); if(res != NULL) { snprintf(value, value_len, "%s", (char *)res->data); } else { snprintf(value, value_len, "%s", _("none")); } g_list_free_full(res, &g_free); } g_free(pictures_folder); g_free((gchar *)homedir); return got_value; }
int store (dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality) { gint result=1; dt_storage_flickr_params_t *p=(dt_storage_flickr_params_t *)sdata; flickcurl_upload_status *photo_status; gint tags=0; const char *ext = format->extension(fdata); // Let's upload image... /* construct a temporary file name */ char fname[4096]= {0}; dt_loc_get_tmp_dir (fname,4096); g_strlcat (fname,"/darktable.XXXXXX.",4096); g_strlcat(fname,ext,4096); char *caption = NULL; char *description = NULL; gint fd=g_mkstemp(fname); fprintf(stderr,"tempfile: %s\n",fname); if(fd==-1) { dt_control_log("failed to create temporary image for flickr export"); return 1; } close(fd); const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid); // If title is not existing, then use the filename without extension. If not, then use title instead GList *title = dt_metadata_get(img->id, "Xmp.dc.title", NULL); if(title != NULL) { caption = g_strdup(title->data); g_list_free_full(title, &g_free); } else { caption = g_path_get_basename(img->filename); (g_strrstr(caption,"."))[0]='\0'; // chop extension... } GList *desc = dt_metadata_get(img->id, "Xmp.dc.description", NULL); if(desc != NULL) { description = desc->data; } dt_image_cache_read_release(darktable.image_cache, img); if(dt_imageio_export(imgid, fname, format, fdata, high_quality) != 0) { fprintf(stderr, "[imageio_storage_flickr] could not export to file: `%s'!\n", fname); dt_control_log(_("could not export to file `%s'!"), fname); result = 0; goto cleanup; } #ifdef _OPENMP #pragma omp critical #endif { //TODO: Check if this could be done in threads, so we enhance export time by using // upload time for one image to export another image to disk. // Upload image // Do we export tags? if( p->export_tags == TRUE ) tags = imgid; photo_status = _flickr_api_upload_photo( p, fname, caption, description, tags ); } if( !photo_status ) { result=0; goto cleanup; } // int fail = 0; // A photoset is only created if we have an album title set if( p->flickr_api->current_album == NULL && p->flickr_api->new_album == TRUE) { char *photoset_id; photoset_id = _flickr_api_create_photoset(p->flickr_api, photo_status->photoid); if( photoset_id == NULL) { dt_control_log("failed to create flickr album"); // fail = 1; } else { // p->flickr_api->new_album = FALSE; p->flickr_api->current_album = flickcurl_photosets_getInfo(p->flickr_api->fc,photoset_id); } } // if(fail) return 1; // TODO: What to do if photoset creation fails? // Add to gallery, if needed if (p->flickr_api->current_album != NULL && p->flickr_api->new_album != TRUE) { flickcurl_photosets_addPhoto (p->flickr_api->fc, p->flickr_api->current_album->id, photo_status->photoid); // TODO: Check for errors adding photo to gallery } else { if (p->flickr_api->current_album != NULL && p->flickr_api->new_album == TRUE) { p->flickr_api->new_album = FALSE; } } cleanup: // And remove from filesystem.. unlink( fname ); g_free( caption ); if(desc) g_list_free_full(desc, &g_free); if (result) { //this makes sense only if the export was successful dt_control_log(_("%d/%d exported to flickr webalbum"), num, total ); } return result; }
int32_t dt_control_export_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; dt_control_export_t *settings = (dt_control_export_t*)t1->data; GList *t = t1->index; const int total = g_list_length(t); int size = 0; dt_imageio_module_format_t *mformat = dt_imageio_get_format_by_index(settings->format_index); g_assert(mformat); dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(settings->storage_index); g_assert(mstorage); // Get max dimensions... uint32_t w,h,fw,fh,sw,sh; fw=fh=sw=sh=0; mstorage->dimension(mstorage, &sw,&sh); mformat->dimension(mformat, &fw,&fh); if( sw==0 || fw==0) w=sw>fw?sw:fw; else w=sw<fw?sw:fw; if( sh==0 || fh==0) h=sh>fh?sh:fh; else h=sh<fh?sh:fh; // get shared storage param struct (global sequence counter, one picasa connection etc) dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage, &size); if(sdata == NULL) { dt_control_log(_("failed to get parameters from storage module, aborting export..")); g_free(t1->data); return 1; } dt_control_log(ngettext ("exporting %d image..", "exporting %d images..", total), total); char message[512]= {0}; snprintf(message, 512, ngettext ("exporting %d image to %s", "exporting %d images to %s", total), total, mstorage->name() ); /* create a cancellable bgjob ui template */ const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message ); dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job); const dt_control_t *control = darktable.control; double fraction=0; #ifdef _OPENMP // limit this to num threads = num full buffers - 1 (keep one for darkroom mode) // use min of user request and mipmap cache entries const int full_entries = dt_conf_get_int ("parallel_export"); // GCC won't accept that this variable is used in a macro, considers // it set but not used, which makes for instance Fedora break. const __attribute__((__unused__)) int num_threads = MAX(1, MIN(full_entries, 8)); #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel default(none) private(imgid, size) shared(control, fraction, w, h, stderr, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1) #else #pragma omp parallel private(imgid, size) shared(control, fraction, w, h, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1) #endif { #endif // get a thread-safe fdata struct (one jpeg struct per thread etc): dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &size); fdata->max_width = settings->max_width; fdata->max_height = settings->max_height; fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width; fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height; strcpy(fdata->style,settings->style); int num = 0; // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a sensible assumption? guint tagid = 0, etagid = 0; dt_tag_new("darktable|changed",&tagid); dt_tag_new("darktable|exported",&etagid); while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED) { #ifdef _OPENMP #pragma omp critical #endif { if(!t) imgid = 0; else { imgid = (long int)t->data; t = g_list_delete_link(t, t); num = total - g_list_length(t); } } // remove 'changed' tag from image dt_tag_detach(tagid, imgid); // make sure the 'exported' tag is set on the image dt_tag_attach(etagid, imgid); // check if image still exists: char imgfilename[DT_MAX_PATH_LEN]; const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid); if(image) { dt_image_full_path(image->id, imgfilename, DT_MAX_PATH_LEN); if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR)) { dt_control_log(_("image `%s' is currently unavailable"), image->filename); fprintf(stderr, _("image `%s' is currently unavailable"), imgfilename); // dt_image_remove(imgid); dt_image_cache_read_release(darktable.image_cache, image); } else { dt_image_cache_read_release(darktable.image_cache, image); mstorage->store(sdata, imgid, mformat, fdata, num, total, settings->high_quality); } } #ifdef _OPENMP #pragma omp critical #endif { fraction+=1.0/total; dt_control_backgroundjobs_progress(control, jid, fraction); } } #ifdef _OPENMP #pragma omp barrier #pragma omp master #endif { dt_control_backgroundjobs_destroy(control, jid); if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata); mstorage->free_params(mstorage, sdata); } // all threads free their fdata mformat->free_params (mformat, fdata); #ifdef _OPENMP } #endif g_free(t1->data); return 0; }
int32_t dt_control_gpx_apply_job_run(dt_job_t *job) { dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; struct dt_gpx_t *gpx = NULL; uint32_t cntr = 0; const dt_control_gpx_apply_t *d = t1->data; const gchar *filename = d->filename; const gchar *tz = d->tz; /* do we have any selected images */ if (!t) goto bail_out; /* try parse the gpx data */ gpx = dt_gpx_new(filename); if (!gpx) { dt_control_log(_("failed to parse gpx file")); goto bail_out; } GTimeZone *tz_camera = (tz == NULL)?g_time_zone_new_utc():g_time_zone_new(tz); if(!tz_camera) goto bail_out; GTimeZone *tz_utc = g_time_zone_new_utc(); /* go thru each selected image and lookup location in gpx */ do { GTimeVal timestamp; GDateTime *exif_time, *utc_time; gdouble lon,lat; uint32_t imgid = (long int)t->data; /* get image */ const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); if (!cimg) continue; /* convert exif datetime TODO: exiv2 dates should be iso8601 and we are probably doing some ugly convertion before inserting into database. */ gint year; gint month; gint day; gint hour; gint minute; gint seconds; if (sscanf(cimg->exif_datetime_taken, "%d:%d:%d %d:%d:%d", (int*)&year, (int*)&month, (int*)&day, (int*)&hour,(int*)&minute,(int*)&seconds) != 6) { fprintf(stderr,"broken exif time in db, '%s'\n", cimg->exif_datetime_taken); dt_image_cache_read_release(darktable.image_cache, cimg); continue; } /* release the lock */ dt_image_cache_read_release(darktable.image_cache, cimg); exif_time = g_date_time_new(tz_camera, year, month, day, hour, minute, seconds); if(!exif_time) continue; utc_time = g_date_time_to_timezone(exif_time, tz_utc); g_date_time_unref(exif_time); if(!utc_time) continue; gboolean res = g_date_time_to_timeval(utc_time, ×tamp); g_date_time_unref(utc_time); if(!res) continue; /* only update image location if time is within gpx tack range */ if(dt_gpx_get_location(gpx, ×tamp, &lon, &lat)) { dt_image_set_location(imgid, lon, lat); cntr++; } } while((t = g_list_next(t)) != NULL); dt_control_log(_("applied matched gpx location onto %d image(s)"), cntr); g_time_zone_unref(tz_camera); g_time_zone_unref(tz_utc); dt_gpx_destroy(gpx); g_free(d->filename); g_free(d->tz); g_free(t1->data); return 0; bail_out: if (gpx) dt_gpx_destroy(gpx); g_free(d->filename); g_free(d->tz); g_free(t1->data); return 1; }