static void _lib_filmstrip_dnd_get_callback(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint target_type, guint time, 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; g_assert(selection_data != NULL); int mouse_over_id = strip->mouse_over_id; int count = dt_collection_get_selected_count(NULL); switch(target_type) { case DND_TARGET_IMGID: { int id = ((count == 1) ? mouse_over_id : -1); gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _DWORD, (guchar *)&id, sizeof(id)); break; } default: // return the location of the file as a last resort case DND_TARGET_URI: // TODO: add all images from the selection { if(count == 1) { gchar pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(mouse_over_id, pathname, sizeof(pathname), &from_cache); gchar *uri = g_strdup_printf("file://%s", pathname); // TODO: should we add the host? gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _BYTE, (guchar *)uri, strlen(uri)); g_free(uri); } else { sqlite3_stmt *stmt; GList *images = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT imgid FROM main.selected_images", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { int id = sqlite3_column_int(stmt, 0); gchar pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(id, pathname, sizeof(pathname), &from_cache); gchar *uri = g_strdup_printf("file://%s", pathname); // TODO: should we add the host? images = g_list_append(images, uri); } sqlite3_finalize(stmt); gchar *uri_list = dt_util_glist_to_str("\r\n", images); g_list_free_full(images, g_free); gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), _BYTE, (guchar *)uri_list, strlen(uri_list)); g_free(uri_list); } break; } } }
void dt_fswatch_add(const dt_fswatch_t * fswatch,dt_fswatch_type_t type, void *data) { char filename[DT_MAX_PATH_LEN]; uint32_t mask=0; dt_fswatch_t *ctx=(dt_fswatch_t *)fswatch; filename[0] = '\0'; switch(type) { case DT_FSWATCH_IMAGE: mask=IN_ALL_EVENTS; dt_image_full_path(((dt_image_t *)data)->id, filename, DT_MAX_PATH_LEN); break; case DT_FSWATCH_CURVE_DIRECTORY: break; default: dt_print(DT_DEBUG_FSWATCH,"[fswatch_add] Unhandled object type %d\n",type); break; } if(filename[0] != '\0') { dt_pthread_mutex_lock(&ctx->mutex); _watch_t *item = g_malloc(sizeof(_watch_t)); item->type=type; item->data=data; ctx->items=g_list_append(fswatch->items, item); item->descriptor=inotify_add_watch(fswatch->inotify_fd,filename,mask); dt_pthread_mutex_unlock(&ctx->mutex); dt_print(DT_DEBUG_FSWATCH,"[fswatch_add] Watch on object %p added on file %s\n", data,filename); } else dt_print(DT_DEBUG_FSWATCH,"[fswatch_add] No watch added, failed to get related filename of object type %d\n",type); }
char *dt_get_lightroom_xmp(int imgid) { char pathname[DT_MAX_FILENAME_LEN]; struct stat buf; gboolean from_cache = TRUE; // Get full pathname dt_image_full_path(imgid, pathname, DT_MAX_FILENAME_LEN, &from_cache); // Look for extension char *pos = strrchr(pathname, '.'); if(pos == NULL) { return NULL; } // If found, replace extension with xmp strncpy(pos + 1, "xmp", 4); if(!stat(pathname, &buf)) return g_strdup(pathname); else return NULL; }
static void _view_map_dnd_get_callback(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint target_type, guint time, dt_view_t *self) { dt_map_t *lib = (dt_map_t*)self->data; g_assert (selection_data != NULL); int imgid = lib->selected_image; switch (target_type) { case DND_TARGET_IMGID: gtk_selection_data_set(selection_data, selection_data-> target, _DWORD, (guchar*) &imgid, sizeof(imgid)); break; default: // return the location of the file as a last resort case DND_TARGET_URI: { gchar pathname[DT_MAX_PATH_LEN] = {0}; dt_image_full_path(imgid, pathname, DT_MAX_PATH_LEN); gchar *uri = g_strdup_printf("file://%s", pathname); // TODO: should we add the host? gtk_selection_data_set(selection_data, selection_data-> target, _BYTE, (guchar*) uri, strlen(uri)); g_free(uri); break; } } }
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, const gboolean upscale, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent) { dt_imageio_email_t *d = (dt_imageio_email_t *)sdata; _email_attachment_t *attachment = (_email_attachment_t *)g_malloc(sizeof(_email_attachment_t)); attachment->imgid = imgid; /* construct a temporary file name */ char tmpdir[PATH_MAX] = { 0 }; dt_loc_get_tmp_dir(tmpdir, sizeof(tmpdir)); char dirname[PATH_MAX] = { 0 }; gboolean from_cache = FALSE; dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache); gchar *filename = g_path_get_basename(dirname); g_strlcpy(dirname, filename, sizeof(dirname)); dt_image_path_append_version(imgid, dirname, sizeof(dirname)); gchar *end = g_strrstr(dirname, ".") + 1; if(end) *end = '\0'; g_strlcat(dirname, format->extension(fdata), sizeof(dirname)); // set exported filename attachment->file = g_build_filename(tmpdir, dirname, (char *)NULL); if(dt_imageio_export(imgid, attachment->file, format, fdata, high_quality, upscale, FALSE, icc_type, icc_filename, icc_intent, self, sdata, num, total) != 0) { fprintf(stderr, "[imageio_storage_email] could not export to file: `%s'!\n", attachment->file); dt_control_log(_("could not export to file `%s'!"), attachment->file); g_free(attachment); g_free(filename); return 1; } char *trunc = attachment->file + strlen(attachment->file) - 32; if(trunc < attachment->file) trunc = attachment->file; dt_control_log(ngettext("%d/%d exported to `%s%s'", "%d/%d exported to `%s%s'", num), num, total, trunc != filename ? ".." : "", trunc); #ifdef _OPENMP // store can be called in parallel, so synch access to shared memory #pragma omp critical #endif d->images = g_list_append(d->images, attachment); g_free(filename); return 0; }
static int image_tostring(lua_State *L) { const dt_image_t * my_image=checkreadimage(L,-1); char image_name[PATH_MAX]; dt_image_full_path(my_image->id,image_name,PATH_MAX); dt_image_path_append_version(my_image->id,image_name,PATH_MAX); lua_pushstring(L,image_name); releasereadimage(L,my_image); return 1; }
static int store_wrapper(struct dt_imageio_module_storage_t *self,struct dt_imageio_module_data_t *self_data, 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) { /* construct a temporary file name */ char tmpdir[DT_MAX_PATH_LEN]= {0}; gboolean from_cache = FALSE; dt_loc_get_tmp_dir (tmpdir,DT_MAX_PATH_LEN); char dirname[DT_MAX_PATH_LEN]; dt_image_full_path(imgid, dirname, DT_MAX_PATH_LEN, &from_cache); const gchar * filename = g_path_get_basename( dirname ); gchar * end = g_strrstr( filename,".")+1; g_strlcpy( end, format->extension(fdata), sizeof(dirname)-(end-dirname)); gchar* complete_name = g_build_filename( tmpdir, filename, (char *)NULL ); if(dt_imageio_export(imgid, complete_name, format, fdata, high_quality) != 0) { fprintf(stderr, "[%s] could not export to file: `%s'!\n", self->name(self),complete_name); g_free(complete_name); return 1; } lua_storage_t *d = (lua_storage_t*) self_data; d->imgids = g_list_prepend(d->imgids,(void*)(intptr_t)imgid); d->file_names = g_list_prepend(d->file_names,complete_name); lua_State *L =darktable.lua_state; lua_getfield(L,LUA_REGISTRYINDEX,"dt_lua_storages"); lua_getfield(L,-1,self->plugin_name); lua_getfield(L,-1,"store"); if(lua_isnil(L,-1)) { lua_pop(L,3); return 1; } luaA_push_typeid(L,self->parameter_lua_type,self_data); luaA_push(L,dt_lua_image_t,&imgid); luaA_push_typeid(L,format->parameter_lua_type,fdata); lua_pushstring(L,complete_name); lua_pushnumber(L,num); lua_pushnumber(L,total); lua_pushboolean(L,high_quality); lua_pushlightuserdata(L,self_data); lua_gettable(L,LUA_REGISTRYINDEX); dt_lua_do_chunk(L,8,1); int result = lua_toboolean(L,-1); lua_pop(L,2); return result; }
static int image_tostring(lua_State *L) { const dt_image_t * my_image=checkreadimage(L,-1); char image_name[PATH_MAX]; gboolean from_cache = FALSE; dt_image_full_path(my_image->id, image_name, sizeof(image_name), &from_cache); dt_image_path_append_version(my_image->id, image_name, sizeof(image_name)); lua_pushstring(L,image_name); releasereadimage(L,my_image); return 1; }
static int sidecar_member(lua_State *L) { const dt_image_t *my_image = checkreadimage(L, 1); gboolean from_cache = TRUE; char filename[PATH_MAX] = { 0 }; dt_image_full_path(my_image->id, filename, sizeof(filename), &from_cache); dt_image_path_append_version(my_image->id, filename, sizeof(filename)); g_strlcat(filename, ".xmp", sizeof(filename)); lua_pushstring(L, filename); releasereadimage(L, my_image); return 1; }
int try_enter(dt_view_t *self) { dt_print_t *prt=(dt_print_t*)self->data; // now check that there is at least one selected image prt->image_id = -1; int selected = dt_control_get_mouse_over_id(); if(selected < 0) { // try last selected sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select * from selected_images", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) selected = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // Leave as selected only the image being edited DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert or ignore into selected_images values (?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, selected); sqlite3_step(stmt); sqlite3_finalize(stmt); } if(selected < 0) { // fail :( dt_control_log(_("no image selected!")); return 1; } // this loads the image from db if needed: const dt_image_t *img = dt_image_cache_get(darktable.image_cache, selected, 'r'); // get image and check if it has been deleted from disk first! char imgfilename[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(img->id, imgfilename, sizeof(imgfilename), &from_cache); if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR)) { dt_control_log(_("image `%s' is currently unavailable"), img->filename); // dt_image_remove(selected); dt_image_cache_read_release(darktable.image_cache, img); return 1; } // and drop the lock again. dt_image_cache_read_release(darktable.image_cache, img); prt->image_id = selected; return 0; }
// FIXME: we can't rely on darktable to avoid file overwriting -- it doesn't know the filename (extension). int write_image(dt_imageio_module_data_t *data, const char *filename, const void *in, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe) { int status = 1; gboolean from_cache = TRUE; char sourcefile[PATH_MAX]; char *targetfile = NULL; char *xmpfile = NULL; char *content = NULL; FILE *fin = NULL; FILE *fout = NULL; dt_image_full_path(imgid, sourcefile, sizeof(sourcefile), &from_cache); char *extension = g_strrstr(sourcefile, "."); if(extension == NULL) goto END; targetfile = g_strconcat(filename, ++extension, NULL); if(!strcmp(sourcefile, targetfile)) goto END; fin = g_fopen(sourcefile, "rb"); fout = g_fopen(targetfile, "wb"); if(fin == NULL || fout == NULL) goto END; fseek(fin, 0, SEEK_END); size_t end = ftell(fin); rewind(fin); content = (char *)g_malloc_n(end, sizeof(char)); if(content == NULL) goto END; if(fread(content, sizeof(char), end, fin) != end) goto END; if(fwrite(content, sizeof(char), end, fout) != end) goto END; // we got a copy of the file, now write the xmp data xmpfile = g_strconcat(targetfile, ".xmp", NULL); if(dt_exif_xmp_write(imgid, xmpfile) != 0) { // something went wrong, unlink the copied image. g_unlink(targetfile); goto END; } status = 0; END: g_free(targetfile); g_free(xmpfile); g_free(content); if(fin) fclose(fin); if(fout) fclose(fout); return status; }
void dt_image_write_sidecar_file(int imgid) { // TODO: compute hash and don't write if not needed! // write .xmp file if(imgid > 0 && dt_conf_get_bool("write_sidecar_files")) { char filename[DT_MAX_PATH_LEN+8]; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN); dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); dt_exif_xmp_write(imgid, filename); } }
int try_enter(dt_view_t *self) { int selected; DT_CTL_GET_GLOBAL(selected, lib_image_mouse_over_id); if(selected < 0) { // try last selected sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select * from selected_images", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) selected = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // Leave as selected only the image being edited DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from selected_images", NULL, NULL, NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert or ignore into selected_images values (?1)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, selected); sqlite3_step(stmt); sqlite3_finalize(stmt); } if(selected < 0) { // fail :( dt_control_log(_("no image selected!")); return 1; } // this loads the image from db if needed: const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, selected); // get image and check if it has been deleted from disk first! char imgfilename[DT_MAX_PATH_LEN]; dt_image_full_path(img->id, imgfilename, DT_MAX_PATH_LEN); if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR)) { dt_control_log(_("image `%s' is currently unavailable"), img->filename); // dt_image_remove(selected); dt_image_cache_read_release(darktable.image_cache, img); return 1; } // and drop the lock again. dt_image_cache_read_release(darktable.image_cache, img); darktable.develop->image_storage.id = selected; return 0; }
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; }
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) { dt_imageio_email_t *d = (dt_imageio_email_t *)sdata; _email_attachment_t *attachment = ( _email_attachment_t *)g_malloc(sizeof(_email_attachment_t)); attachment->imgid = imgid; /* construct a temporary file name */ char tmpdir[4096]= {0}; dt_loc_get_tmp_dir (tmpdir,4096); char dirname[4096]; dt_image_full_path(imgid, dirname, 1024); const gchar * filename = g_path_get_basename( dirname ); gchar * end = g_strrstr( filename,".")+1; g_strlcpy( end, format->extension(fdata), sizeof(dirname)-(end-dirname)); attachment->file = g_build_filename( tmpdir, filename, (char *)NULL ); if(dt_imageio_export(imgid, attachment->file, format, fdata, high_quality) != 0) { fprintf(stderr, "[imageio_storage_email] could not export to file: `%s'!\n", attachment->file); dt_control_log(_("could not export to file `%s'!"), attachment->file); g_free(attachment); return 1; } char *trunc = attachment->file + strlen(attachment->file) - 32; if(trunc < attachment->file) trunc = attachment->file; dt_control_log(_("%d/%d exported to `%s%s'"), num, total, trunc != filename ? ".." : "", trunc); #ifdef _OPENMP // store can be called in parallel, so synch access to shared memory #pragma omp critical #endif d->images = g_list_append( d->images, attachment ); return 0; }
/* update all values to reflect mouse over image id or no data at all */ static void _metadata_view_update_values(dt_lib_module_t *self) { dt_lib_metadata_view_t *d = (dt_lib_metadata_view_t *)self->data; int32_t mouse_over_id = -1; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if(mouse_over_id >= 0) { const int vl = 512; char value[vl]; const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); if(!img) goto fill_minuses; if(img->film_id == -1) { dt_image_cache_read_release(darktable.image_cache, img); goto fill_minuses; } /* update all metadata */ dt_image_film_roll(img, value, vl); _metadata_update_value(d->metadata[md_internal_filmroll], value); snprintf(value,vl,"%d", img->id); _metadata_update_value(d->metadata[md_internal_imgid], value); _metadata_update_value(d->metadata[md_internal_filename], img->filename); dt_image_full_path(img->id, value, MAXPATHLEN); _metadata_update_value(d->metadata[md_internal_fullpath], value); /* EXIF */ _metadata_update_value_end(d->metadata[md_exif_model], img->exif_model); _metadata_update_value_end(d->metadata[md_exif_lens], img->exif_lens); _metadata_update_value_end(d->metadata[md_exif_maker], img->exif_maker); snprintf(value, vl, "F/%.1f", img->exif_aperture); _metadata_update_value(d->metadata[md_exif_aperture], value); if(img->exif_exposure <= 0.5) snprintf(value, vl, "1/%.0f", 1.0/img->exif_exposure); else snprintf(value, vl, "%.1f''", img->exif_exposure); _metadata_update_value(d->metadata[md_exif_exposure], value); snprintf(value, vl, "%.0f", img->exif_focal_length); _metadata_update_value(d->metadata[md_exif_focal_length], value); snprintf(value, vl, "%.0f", img->exif_focus_distance); _metadata_update_value(d->metadata[md_exif_focus_distance], value); snprintf(value, vl, "%.0f", img->exif_iso); _metadata_update_value(d->metadata[md_exif_iso], value); _metadata_update_value(d->metadata[md_exif_datetime], img->exif_datetime_taken); snprintf(value, vl, "%d", img->height); _metadata_update_value(d->metadata[md_exif_height], value); snprintf(value, vl, "%d", img->width); _metadata_update_value(d->metadata[md_exif_width], value); /* XMP */ GList *res; if((res = dt_metadata_get(img->id, "Xmp.dc.title", NULL))!=NULL) { snprintf(value, vl, "%s", (char*)res->data); _filter_non_printable(value, vl); g_free(res->data); g_list_free(res); } else snprintf(value, vl, NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_title], value); if((res = dt_metadata_get(img->id, "Xmp.dc.creator", NULL))!=NULL) { snprintf(value, vl, "%s", (char*)res->data); _filter_non_printable(value, vl); g_free(res->data); g_list_free(res); } else snprintf(value, vl, NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_creator], value); if((res = dt_metadata_get(img->id, "Xmp.dc.rights", NULL))!=NULL) { snprintf(value, vl, "%s", (char*)res->data); _filter_non_printable(value, vl); g_free(res->data); g_list_free(res); } else snprintf(value, vl, NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_rights], value); /* geotagging */ /* latitude */ if(isnan(img->latitude)) { _metadata_update_value(d->metadata[md_geotagging_lat], NODATA_STRING); } else { gchar *latitude = osd_latitude_str(img->latitude); _metadata_update_value(d->metadata[md_geotagging_lat], latitude); g_free(latitude); } /* longitude */ if(isnan(img->longitude)) { _metadata_update_value(d->metadata[md_geotagging_lon], NODATA_STRING); } else { gchar *longitude = osd_longitude_str(img->longitude); _metadata_update_value(d->metadata[md_geotagging_lon], longitude); g_free(longitude); } /* release img */ dt_image_cache_read_release(darktable.image_cache, img); } return; /* reset */ fill_minuses: for(int k=0; k<md_size; k++) _metadata_update_value(d->metadata[k],NODATA_STRING); }
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_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_image_copy(const int32_t imgid, const int32_t filmid) { int32_t newid = -1; sqlite3_stmt *stmt; gchar srcpath[DT_MAX_PATH_LEN] = {0}; gchar *newdir = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select folder from film_rolls where id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid); if(sqlite3_step(stmt) == SQLITE_ROW) newdir = g_strdup((gchar *) sqlite3_column_text(stmt, 0)); sqlite3_finalize(stmt); if(newdir) { dt_image_full_path(imgid, srcpath, DT_MAX_PATH_LEN); gchar *imgbname = g_path_get_basename(srcpath); gchar *destpath = g_build_filename(newdir, imgbname, NULL); GFile *src = g_file_new_for_path(srcpath); GFile *dest = g_file_new_for_path(destpath); g_free(imgbname); imgbname = NULL; g_free(newdir); newdir = NULL; g_free(destpath); destpath = NULL; // copy image to new folder // if image file already exists, continue GError *gerror = NULL; g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror); if((gerror == NULL) || (gerror != NULL && gerror->code == G_IO_ERROR_EXISTS)) { // update database 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, ?1 as 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 = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, 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 = ?1 and a.filename = b.filename and " "b.id = ?2 order by a.id desc", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); 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); // write xmp file dt_image_write_sidecar_file(newid); } } else { fprintf(stderr, "Failed to copy image %s: %s\n", srcpath, gerror->message); } g_object_unref(dest); g_object_unref(src); g_clear_error(&gerror); } return newid; }
int store (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) { dt_imageio_gallery_t *d = (dt_imageio_gallery_t *)sdata; char filename[DT_MAX_PATH_LEN]= {0}; char dirname[DT_MAX_PATH_LEN]= {0}; dt_image_full_path(imgid, dirname, DT_MAX_PATH_LEN); // we're potentially called in parallel. have sequence number synchronized: dt_pthread_mutex_lock(&darktable.plugin_threadsafe); { char tmp_dir[DT_MAX_PATH_LEN]; dt_variables_expand(d->vp, d->filename, TRUE); g_strlcpy(tmp_dir, dt_variables_get_result(d->vp), DT_MAX_PATH_LEN); // if filenamepattern is a directory just let att ${FILE_NAME} as default.. if ( g_file_test(tmp_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) || ((d->filename+strlen(d->filename)-1)[0]=='/' || (d->filename+strlen(d->filename)-1)[0]=='\\') ) snprintf (d->filename+strlen(d->filename), DT_MAX_PATH_LEN-strlen(d->filename), "/$(FILE_NAME)"); // avoid braindead export which is bound to overwrite at random: if(total > 1 && !g_strrstr(d->filename, "$")) { snprintf(d->filename+strlen(d->filename), DT_MAX_PATH_LEN-strlen(d->filename), "_$(SEQUENCE)"); } gchar* fixed_path = dt_util_fix_path(d->filename); g_strlcpy(d->filename, fixed_path, DT_MAX_PATH_LEN); g_free(fixed_path); d->vp->filename = dirname; d->vp->jobcode = "export"; d->vp->imgid = imgid; d->vp->sequence = num; dt_variables_expand(d->vp, d->filename, TRUE); g_strlcpy(filename, dt_variables_get_result(d->vp), DT_MAX_PATH_LEN); g_strlcpy(dirname, filename, DT_MAX_PATH_LEN); const char *ext = format->extension(fdata); char *c = dirname + strlen(dirname); for(; c>dirname && *c != '/'; c--); if(*c == '/') *c = '\0'; if(g_mkdir_with_parents(dirname, 0755)) { fprintf(stderr, "[imageio_storage_gallery] could not create directory: `%s'!\n", dirname); dt_control_log(_("could not create directory `%s'!"), dirname); dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); return 1; } // store away dir. snprintf(d->cached_dirname, DT_MAX_PATH_LEN, "%s", dirname); c = filename + strlen(filename); for(; c>filename && *c != '.' && *c != '/' ; c--); if(c <= filename || *c=='/') c = filename + strlen(filename); sprintf(c,".%s",ext); // save image to list, in order: pair_t *pair = malloc(sizeof(pair_t)); char *title = NULL, *description = NULL, *tags = NULL; GList *res; res = dt_metadata_get(imgid, "Xmp.dc.title", NULL); if(res) { title = res->data; g_list_free(res); } res = dt_metadata_get(imgid, "Xmp.dc.description", NULL); if(res) { description = res->data; g_list_free(res); } unsigned int count = 0; res = dt_metadata_get(imgid, "Xmp.dc.subject", &count); if(res) { // don't show the internal tags (darktable|...) res = g_list_first(res); GList *iter = res; while(iter) { GList *next = g_list_next(iter); if(g_str_has_prefix(iter->data, "darktable|")) { g_free(iter->data); res = g_list_delete_link(res, iter); count--; } iter = next; } tags = dt_util_glist_to_str(", ", res, count); } char relfilename[256], relthumbfilename[256]; c = filename + strlen(filename); for(; c>filename && *c != '/' ; c--); if(*c == '/') c++; if(c <= filename) c = filename; snprintf(relfilename, 256, "%s", c); snprintf(relthumbfilename, 256, "%s", relfilename); c = relthumbfilename + strlen(relthumbfilename); for(; c>relthumbfilename && *c != '.'; c--); if(c <= relthumbfilename) c = relthumbfilename + strlen(relthumbfilename); sprintf(c, "-thumb.%s", ext); char subfilename[DT_MAX_PATH_LEN], relsubfilename[256]; snprintf(subfilename, DT_MAX_PATH_LEN, "%s", d->cached_dirname); char* sc = subfilename + strlen(subfilename); sprintf(sc, "/img_%d.html", num); sprintf(relsubfilename, "img_%d.html", num); snprintf(pair->line, 4096, "\n" " <div><a class=\"dia\" rel=\"lightbox[viewer]\" title=\"%s - %s\" href=\"%s\"><span></span><img src=\"%s\" alt=\"img%d\" class=\"img\"/></a>\n" " <h1>%s</h1>\n" " %s<br/><span class=\"tags\">%s</span></div>\n", title?title:relfilename, description?description:" ", relfilename, relthumbfilename, num, title?title:" ", description?description:" ", tags?tags:" "); char next[256]; sprintf(next, "img_%d.html", (num)%total+1); char prev[256]; sprintf(prev, "img_%d.html", (num==1)?total:num-1); /* Becomes unecessary with the Lightbox image viewer overlay FILE* subfile = fopen(subfilename, "wb"); fprintf(subfile, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" " <head>\n" " <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n" " <link rel=\"shortcut icon\" href=\"style/favicon.ico\" />\n" " <link rel=\"stylesheet\" href=\"style/style.css\" type=\"text/css\" />\n" " <title>%s</title>\n" " </head>\n" " <body>\n" " <div class=\"title\"><a href=\"index.html\">%s</a></div>\n" " <div class=\"page\">\n" " <div style=\"width: 692px; max-width: 692px; height: 10px;\">\n" " <a style=\"float: left;\" href=\"%s\"><h1>prev</h1></a>\n" " <a style=\"float: right;\"href=\"%s\"><h1>next</h1></a>\n" " </div>\n" " <a href=\"%s\"><img src=\"%s\" width=\"692\" class=\"img\"/></a>\n" " %s<br/><span class=\"tags\">%s</span></div>\n" " <p style=\"clear:both;\"></p>\n" " </div>\n" " <div class=\"footer\">\n" " created with darktable "PACKAGE_VERSION"\n" " </div>\n" " </body>\n" "</html>\n", relfilename, title?title:relfilename, prev, next, relfilename, relfilename, description?description:" ", tags?tags:" "); fclose(subfile); */ pair->pos = num; g_free(title); g_free(description); g_free(tags); d->l = g_list_insert_sorted(d->l, pair, (GCompareFunc)sort_pos); } // end of critical block dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); /* export image to file */ if(dt_imageio_export(imgid, filename, format, fdata, high_quality) != 0) { fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename); dt_control_log(_("could not export to file `%s'!"), filename); return 1; } /* also export thumbnail: */ // write with reduced resolution: const int max_width = fdata->max_width; const int max_height = fdata->max_height; fdata->max_width = 200; fdata->max_height = 200; // alter filename with -thumb: char *c = filename + strlen(filename); for(; c>filename && *c != '.' && *c != '/' ; c--); if(c <= filename || *c=='/') c = filename + strlen(filename); const char *ext = format->extension(fdata); sprintf(c,"-thumb.%s",ext); if(dt_imageio_export(imgid, filename, format, fdata, FALSE) != 0) { fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename); dt_control_log(_("could not export to file `%s'!"), filename); return 1; } // restore for next image: fdata->max_width = max_width; fdata->max_height = max_height; printf("[export_job] exported to `%s'\n", filename); char *trunc = filename + strlen(filename) - 32; if(trunc < filename) trunc = filename; dt_control_log(_("%d/%d exported to `%s%s'"), num, total, trunc != filename ? ".." : "", trunc); return 0; }
int store (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) { dt_imageio_disk_t *d = (dt_imageio_disk_t *)sdata; char filename[1024]= {0}; char dirname[1024]= {0}; dt_image_full_path(imgid, dirname, 1024); int fail = 0; // we're potentially called in parallel. have sequence number synchronized: dt_pthread_mutex_lock(&darktable.plugin_threadsafe); { // if filenamepattern is a directory just let att ${FILE_NAME} as default.. if ( g_file_test(d->filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) || ((d->filename+strlen(d->filename))[0]=='/' || (d->filename+strlen(d->filename))[0]=='\\') ) snprintf (d->filename+strlen(d->filename), 1024-strlen(d->filename), "$(FILE_NAME)"); // avoid braindead export which is bound to overwrite at random: if(total > 1 && !g_strrstr(d->filename, "$")) { snprintf(d->filename+strlen(d->filename), 1024-strlen(d->filename), "_$(SEQUENCE)"); } gchar* fixed_path = dt_util_fix_path(d->filename); g_strlcpy(d->filename, fixed_path, 1024); g_free(fixed_path); d->vp->filename = dirname; d->vp->jobcode = "export"; d->vp->imgid = imgid; d->vp->sequence = num; dt_variables_expand(d->vp, d->filename, TRUE); g_strlcpy(filename, dt_variables_get_result(d->vp), 1024); g_strlcpy(dirname, filename, 1024); const char *ext = format->extension(fdata); char *c = dirname + strlen(dirname); for(; c>dirname && *c != '/'; c--); if(*c == '/') *c = '\0'; if(g_mkdir_with_parents(dirname, 0755)) { fprintf(stderr, "[imageio_storage_disk] could not create directory: `%s'!\n", dirname); dt_control_log(_("could not create directory `%s'!"), dirname); fail = 1; goto failed; } c = filename + strlen(filename); for(; c>filename && *c != '.' && *c != '/' ; c--); if(c <= filename || *c=='/') c = filename + strlen(filename); sprintf(c,".%s",ext); /* prevent overwrite of files */ int seq=1; failed: if (!fail && g_file_test (filename,G_FILE_TEST_EXISTS)) { do { sprintf(c,"_%.2d.%s",seq,ext); seq++; } while (g_file_test (filename,G_FILE_TEST_EXISTS)); } } // end of critical block dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); if(fail) return 1; /* export image to file */ dt_imageio_export(imgid, filename, format, fdata); /* now write xmp into that container, if possible */ dt_exif_xmp_attach(imgid, filename); printf("[export_job] exported to `%s'\n", filename); char *trunc = filename + strlen(filename) - 32; if(trunc < filename) trunc = filename; dt_control_log(_("%d/%d exported to `%s%s'"), num, total, trunc != filename ? ".." : "", trunc); return 0; }
// 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 int32_t high_quality, const int32_t thumbnail_export) { dt_develop_t dev; dt_dev_init(&dev, 0); dt_mipmap_buffer_t buf; 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); if(!res) { dt_control_log(_("failed to allocate memory for export, please lower the threads used for export or buy more memory.")); dt_dev_cleanup(&dev); if(buf.buf) 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_dev_cleanup(&dev); return 1; } 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); 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 (strcmp(colorout->op, "colorout") == 0) { dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)colorout->params; if(!strcmp(p->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 int 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)) ? 0 : 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 float scalex = width > 0 ? fminf(width /(float)pipe.processed_width, 1.0) : 1.0; const float scaley = height > 0 ? fminf(height/(float)pipe.processed_height, 1.0) : 1.0; const float scale = fminf(scalex, scaley); int processed_width = scale*pipe.processed_width; int processed_height = scale*pipe.processed_height; 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 if(high_quality_processing) { dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); const float scalex = format_params->max_width > 0 ? fminf(format_params->max_width /(float)pipe.processed_width, 1.0) : 1.0; const float scaley = format_params->max_height > 0 ? fminf(format_params->max_height/(float)pipe.processed_height, 1.0) : 1.0; const float 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, 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; } // downconversion to low-precision formats: if(bpp == 8 && !display_byteorder) { // ldr output: char if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(int k=0; k<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 { 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(int k=0; k<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 int k = x + processed_width*y; 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[1024]; dt_image_full_path(imgid, pathname, 1024); length = dt_exif_read_blob(exif_profile, pathname, sRGB, imgid); 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); free(moutbuf); return res; }
/* update all values to reflect mouse over image id or no data at all */ static void _metadata_view_update_values(dt_lib_module_t *self) { dt_lib_metadata_view_t *d = (dt_lib_metadata_view_t *)self->data; int32_t mouse_over_id = dt_control_get_mouse_over_id(); if (mouse_over_id == -1) { const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager); if(cv->view((dt_view_t*)cv) == DT_VIEW_DARKROOM) { mouse_over_id = darktable.develop->image_storage.id; } else { sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from selected_images limit 1", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) mouse_over_id = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); } } if(mouse_over_id >= 0) { char value[512]; char pathname[PATH_MAX]; const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); if(!img) goto fill_minuses; if(img->film_id == -1) { dt_image_cache_read_release(darktable.image_cache, img); goto fill_minuses; } /* update all metadata */ dt_image_film_roll(img, value, sizeof(value)); _metadata_update_value(d->metadata[md_internal_filmroll], value); const int tp = 512; char tooltip[tp]; snprintf(tooltip, tp, _("double click to jump to film roll\n%s"), value); g_object_set(G_OBJECT(d->metadata[md_internal_filmroll]), "tooltip-text", tooltip, (char *)NULL); snprintf(value,sizeof(value),"%d", img->id); _metadata_update_value(d->metadata[md_internal_imgid], value); snprintf(value,sizeof(value),"%d", img->group_id); _metadata_update_value(d->metadata[md_internal_groupid], value); _metadata_update_value(d->metadata[md_internal_filename], img->filename); snprintf(value,sizeof(value),"%d", img->version); _metadata_update_value(d->metadata[md_internal_version], value); gboolean from_cache = FALSE; dt_image_full_path(img->id, pathname, sizeof(pathname), &from_cache); _metadata_update_value(d->metadata[md_internal_fullpath], pathname); snprintf(value, sizeof(value), "%s", (img->flags & DT_IMAGE_LOCAL_COPY)?_("yes"):_("no")); _metadata_update_value(d->metadata[md_internal_local_copy], value); // TODO: decide if this should be removed for a release. maybe #ifdef'ing to only add it to git compiles? // the bits of the flags { #define EMPTY_FIELD '.' #define FALSE_FIELD '.' #define TRUE_FIELD '!' char *tooltip = NULL; char *flag_descriptions[] = { N_("unused"), N_("unused/deprecated"), N_("ldr"), N_("raw"), N_("hdr"), N_("marked for deletion"), N_("auto-applying presets applied"), N_("legacy flag. set for all new images"), N_("local copy"), N_("has .txt"), N_("has .wav") }; char *tooltip_parts[13] = { 0 }; int next_tooltip_part = 0; memset(value, EMPTY_FIELD, sizeof(value)); int stars = img->flags & 0x7; char *star_string = NULL; if(stars == 6) { value[0] = 'x'; tooltip_parts[next_tooltip_part++] = _("image rejected"); } else { value[0] = '0' + stars; tooltip_parts[next_tooltip_part++] = star_string = g_strdup_printf(ngettext("image has %d star", "image has %d stars", stars), stars); } if(img->flags & 8) { value[1] = TRUE_FIELD; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[0]); } else value[1] = FALSE_FIELD; if(img->flags & DT_IMAGE_THUMBNAIL_DEPRECATED) { value[2] = TRUE_FIELD; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[1]); } else value[2] = FALSE_FIELD; if(img->flags & DT_IMAGE_LDR) { value[3] = 'l'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[2]); } if(img->flags & DT_IMAGE_RAW) { value[4] = 'r'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[3]); } if(img->flags & DT_IMAGE_HDR) { value[5] = 'h'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[4]); } if(img->flags & DT_IMAGE_REMOVE) { value[6] = 'd'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[5]); } if(img->flags & DT_IMAGE_AUTO_PRESETS_APPLIED) { value[7] = 'a'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[6]); } if(img->flags & DT_IMAGE_NO_LEGACY_PRESETS) { value[8] = 'p'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[7]); } if(img->flags & DT_IMAGE_LOCAL_COPY) { value[9] = 'c'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[8]); } if(img->flags & DT_IMAGE_HAS_TXT) { value[10] = 't'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[9]); } if(img->flags & DT_IMAGE_HAS_WAV) { value[11] = 'w'; tooltip_parts[next_tooltip_part++] = _(flag_descriptions[10]); } value[12] = '\0'; tooltip = g_strjoinv("\n", tooltip_parts); _metadata_update_value(d->metadata[md_internal_flags], value); g_object_set(G_OBJECT(d->metadata[md_internal_flags]), "tooltip-text", tooltip, (char *)NULL); g_free(star_string); g_free(tooltip); #undef EMPTY_FIELD #undef FALSE_FIELD #undef TRUE_FIELD } /* EXIF */ _metadata_update_value_end(d->metadata[md_exif_model], img->exif_model); _metadata_update_value_end(d->metadata[md_exif_lens], img->exif_lens); _metadata_update_value_end(d->metadata[md_exif_maker], img->exif_maker); snprintf(value, sizeof(value), "F/%.1f", img->exif_aperture); _metadata_update_value(d->metadata[md_exif_aperture], value); if(img->exif_exposure <= 0.5) snprintf(value, sizeof(value), "1/%.0f", 1.0/img->exif_exposure); else snprintf(value, sizeof(value), "%.1f''", img->exif_exposure); _metadata_update_value(d->metadata[md_exif_exposure], value); snprintf(value, sizeof(value), "%.0f mm", img->exif_focal_length); _metadata_update_value(d->metadata[md_exif_focal_length], value); if (isnan(img->exif_focus_distance) || fpclassify(img->exif_focus_distance) == FP_ZERO) { _metadata_update_value(d->metadata[md_exif_focus_distance], NODATA_STRING); } else { snprintf(value, sizeof(value), "%.2f m", img->exif_focus_distance); _metadata_update_value(d->metadata[md_exif_focus_distance], value); } snprintf(value, sizeof(value), "%.0f", img->exif_iso); _metadata_update_value(d->metadata[md_exif_iso], value); _metadata_update_value(d->metadata[md_exif_datetime], img->exif_datetime_taken); snprintf(value, sizeof(value), "%d", img->height); _metadata_update_value(d->metadata[md_exif_height], value); snprintf(value, sizeof(value), "%d", img->width); _metadata_update_value(d->metadata[md_exif_width], value); /* XMP */ GList *res; if((res = dt_metadata_get(img->id, "Xmp.dc.title", NULL))!=NULL) { snprintf(value, sizeof(value), "%s", (char*)res->data); _filter_non_printable(value, sizeof(value)); g_list_free_full(res, &g_free); } else snprintf(value, sizeof(value), NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_title], value); if((res = dt_metadata_get(img->id, "Xmp.dc.creator", NULL))!=NULL) { snprintf(value, sizeof(value), "%s", (char*)res->data); _filter_non_printable(value, sizeof(value)); g_list_free_full(res, &g_free); } else snprintf(value, sizeof(value), NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_creator], value); if((res = dt_metadata_get(img->id, "Xmp.dc.rights", NULL))!=NULL) { snprintf(value, sizeof(value), "%s", (char*)res->data); _filter_non_printable(value, sizeof(value)); g_list_free_full(res, &g_free); } else snprintf(value, sizeof(value), NODATA_STRING); _metadata_update_value(d->metadata[md_xmp_rights], value); /* geotagging */ /* latitude */ if(isnan(img->latitude)) { _metadata_update_value(d->metadata[md_geotagging_lat], NODATA_STRING); } else { #ifdef HAVE_MAP if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location")) { gchar *latitude = osd_latitude_str(img->latitude); _metadata_update_value(d->metadata[md_geotagging_lat], latitude); g_free(latitude); } else { #endif gchar NS = img->latitude<0?'S':'N'; snprintf(value, sizeof(value), "%c %09.6f", NS, fabs(img->latitude)); _metadata_update_value(d->metadata[md_geotagging_lat], value); #ifdef HAVE_MAP } #endif } /* longitude */ if(isnan(img->longitude)) { _metadata_update_value(d->metadata[md_geotagging_lon], NODATA_STRING); } else { #ifdef HAVE_MAP if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location")) { gchar *longitude = osd_longitude_str(img->longitude); _metadata_update_value(d->metadata[md_geotagging_lon], longitude); g_free(longitude); } else { #endif gchar EW = img->longitude<0?'W':'E'; snprintf(value, sizeof(value), "%c %010.6f", EW, fabs(img->longitude)); _metadata_update_value(d->metadata[md_geotagging_lon], value); #ifdef HAVE_MAP } #endif } /* release img */ dt_image_cache_read_release(darktable.image_cache, img); } return; /* reset */ fill_minuses: for(int k=0; k<md_size; k++) _metadata_update_value(d->metadata[k],NODATA_STRING); }
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, const gboolean upscale) { dt_imageio_gallery_t *d = (dt_imageio_gallery_t *)sdata; char filename[PATH_MAX] = { 0 }; char dirname[PATH_MAX] = { 0 }; gboolean from_cache = FALSE; dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache); // we're potentially called in parallel. have sequence number synchronized: dt_pthread_mutex_lock(&darktable.plugin_threadsafe); { char tmp_dir[PATH_MAX] = { 0 }; d->vp->filename = dirname; d->vp->jobcode = "export"; d->vp->imgid = imgid; d->vp->sequence = num; dt_variables_expand(d->vp, d->filename, TRUE); gchar *result_tmp_dir = dt_variables_get_result(d->vp); g_strlcpy(tmp_dir, result_tmp_dir, sizeof(tmp_dir)); g_free(result_tmp_dir); // if filenamepattern is a directory just let att ${FILE_NAME} as default.. if(g_file_test(tmp_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) || ((d->filename + strlen(d->filename) - 1)[0] == '/' || (d->filename + strlen(d->filename) - 1)[0] == '\\')) snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "/$(FILE_NAME)"); // avoid braindead export which is bound to overwrite at random: if(total > 1 && !g_strrstr(d->filename, "$")) { snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "_$(SEQUENCE)"); } gchar *fixed_path = dt_util_fix_path(d->filename); g_strlcpy(d->filename, fixed_path, sizeof(d->filename)); g_free(fixed_path); dt_variables_expand(d->vp, d->filename, TRUE); gchar *result_filename = dt_variables_get_result(d->vp); g_strlcpy(filename, result_filename, sizeof(filename)); g_free(result_filename); g_strlcpy(dirname, filename, sizeof(dirname)); const char *ext = format->extension(fdata); char *c = dirname + strlen(dirname); for(; c > dirname && *c != '/'; c--) ; if(*c == '/') *c = '\0'; if(g_mkdir_with_parents(dirname, 0755)) { fprintf(stderr, "[imageio_storage_gallery] could not create directory: `%s'!\n", dirname); dt_control_log(_("could not create directory `%s'!"), dirname); dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); return 1; } // store away dir. snprintf(d->cached_dirname, sizeof(d->cached_dirname), "%s", dirname); c = filename + strlen(filename); for(; c > filename && *c != '.' && *c != '/'; c--) ; if(c <= filename || *c == '/') c = filename + strlen(filename); sprintf(c, ".%s", ext); // save image to list, in order: pair_t *pair = malloc(sizeof(pair_t)); char *title = NULL, *description = NULL; GList *res_title, *res_desc; res_title = dt_metadata_get(imgid, "Xmp.dc.title", NULL); if(res_title) { title = res_title->data; } res_desc = dt_metadata_get(imgid, "Xmp.dc.description", NULL); if(res_desc) { description = res_desc->data; } char relfilename[PATH_MAX] = { 0 }, relthumbfilename[PATH_MAX] = { 0 }; c = filename + strlen(filename); for(; c > filename && *c != '/'; c--) ; if(*c == '/') c++; if(c <= filename) c = filename; snprintf(relfilename, sizeof(relfilename), "%s", c); snprintf(relthumbfilename, sizeof(relthumbfilename), "%s", relfilename); c = relthumbfilename + strlen(relthumbfilename); for(; c > relthumbfilename && *c != '.'; c--) ; if(c <= relthumbfilename) c = relthumbfilename + strlen(relthumbfilename); sprintf(c, "-thumb.%s", ext); char subfilename[PATH_MAX] = { 0 }, relsubfilename[PATH_MAX] = { 0 }; snprintf(subfilename, sizeof(subfilename), "%s", d->cached_dirname); char *sc = subfilename + strlen(subfilename); sprintf(sc, "/img_%d.html", num); snprintf(relsubfilename, sizeof(relsubfilename), "img_%d.html", num); snprintf(pair->line, sizeof(pair->line), "\n" " <div><a class=\"dia\" rel=\"lightbox[viewer]\" title=\"%s - %s\" " "href=\"%s\"><span></span><img src=\"%s\" alt=\"img%d\" class=\"img\"/></a>\n" " <h1>%s</h1>\n" " %s</div>\n", title ? title : relfilename, description ? description : " ", relfilename, relthumbfilename, num, title ? title : " ", description ? description : " "); char next[PATH_MAX] = { 0 }; snprintf(next, sizeof(next), "img_%d.html", (num) % total + 1); char prev[PATH_MAX] = { 0 }; snprintf(prev, sizeof(prev), "img_%d.html", (num == 1) ? total : num - 1); pair->pos = num; if(res_title) g_list_free_full(res_title, &g_free); if(res_desc) g_list_free_full(res_desc, &g_free); d->l = g_list_insert_sorted(d->l, pair, (GCompareFunc)sort_pos); } // end of critical block dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); /* export image to file */ if(dt_imageio_export(imgid, filename, format, fdata, high_quality, upscale, FALSE, self, sdata, num, total) != 0) { fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename); dt_control_log(_("could not export to file `%s'!"), filename); return 1; } /* also export thumbnail: */ // write with reduced resolution: const int max_width = fdata->max_width; const int max_height = fdata->max_height; fdata->max_width = 200; fdata->max_height = 200; // alter filename with -thumb: char *c = filename + strlen(filename); for(; c > filename && *c != '.' && *c != '/'; c--) ; if(c <= filename || *c == '/') c = filename + strlen(filename); const char *ext = format->extension(fdata); sprintf(c, "-thumb.%s", ext); if(dt_imageio_export(imgid, filename, format, fdata, FALSE, TRUE, FALSE, self, sdata, num, total) != 0) { fprintf(stderr, "[imageio_storage_gallery] could not export to file: `%s'!\n", filename); dt_control_log(_("could not export to file `%s'!"), filename); return 1; } // restore for next image: fdata->max_width = max_width; fdata->max_height = max_height; printf("[export_job] exported to `%s'\n", filename); char *trunc = filename + strlen(filename) - 32; if(trunc < filename) trunc = filename; dt_control_log(ngettext("%d/%d exported to `%s%s'", "%d/%d exported to `%s%s'", num), num, total, trunc != filename ? ".." : "", trunc); return 0; }
int store (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) { dt_imageio_disk_t *d = (dt_imageio_disk_t *)sdata; char filename[DT_MAX_PATH_LEN]= {0}; char dirname[DT_MAX_PATH_LEN]= {0}; dt_image_full_path(imgid, dirname, DT_MAX_PATH_LEN); int fail = 0; // we're potentially called in parallel. have sequence number synchronized: dt_pthread_mutex_lock(&darktable.plugin_threadsafe); { // if filenamepattern is a directory just let att ${FILE_NAME} as default.. if ( g_file_test(d->filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) || ((d->filename+strlen(d->filename))[0]=='/' || (d->filename+strlen(d->filename))[0]=='\\') ) snprintf (d->filename+strlen(d->filename), DT_MAX_PATH_LEN-strlen(d->filename), "$(FILE_NAME)"); // avoid braindead export which is bound to overwrite at random: if(total > 1 && !g_strrstr(d->filename, "$")) { snprintf(d->filename+strlen(d->filename), DT_MAX_PATH_LEN-strlen(d->filename), "_$(SEQUENCE)"); } gchar* fixed_path = dt_util_fix_path(d->filename); g_strlcpy(d->filename, fixed_path, DT_MAX_PATH_LEN); g_free(fixed_path); d->vp->filename = dirname; d->vp->jobcode = "export"; d->vp->imgid = imgid; d->vp->sequence = num; dt_variables_expand(d->vp, d->filename, TRUE); g_strlcpy(filename, dt_variables_get_result(d->vp), DT_MAX_PATH_LEN); g_strlcpy(dirname, filename, DT_MAX_PATH_LEN); const char *ext = format->extension(fdata); char *c = dirname + strlen(dirname); for(; c>dirname && *c != '/'; c--); if(*c == '/') { if(c > dirname) // /.../.../foo c[0] = '\0'; else // /foo c[1] = '\0'; } else if(c == dirname) // foo { c[0] = '.'; c[1] = '\0'; } if(g_mkdir_with_parents(dirname, 0755)) { fprintf(stderr, "[imageio_storage_disk] could not create directory: `%s'!\n", dirname); dt_control_log(_("could not create directory `%s'!"), dirname); fail = 1; goto failed; } if(g_access(dirname, W_OK) != 0) { fprintf(stderr, "[imageio_storage_disk] could not write to directory: `%s'!\n", dirname); dt_control_log(_("could not write to directory `%s'!"), dirname); fail = 1; goto failed; } c = filename + strlen(filename); // remove everything after the last '.'. this destroys any file name with dots in it since $(FILE_NAME) already comes without the original extension. // for(; c>filename && *c != '.' && *c != '/' ; c--); // if(c <= filename || *c=='/') c = filename + strlen(filename); sprintf(c,".%s",ext); /* prevent overwrite of files */ int seq=1; failed: if (!fail && g_file_test (filename,G_FILE_TEST_EXISTS)) { do { sprintf(c,"_%.2d.%s",seq,ext); seq++; } while (g_file_test (filename,G_FILE_TEST_EXISTS)); } } // end of critical block dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); if(fail) return 1; /* export image to file */ if(dt_imageio_export(imgid, filename, format, fdata, high_quality) != 0) { fprintf(stderr, "[imageio_storage_disk] could not export to file: `%s'!\n", filename); dt_control_log(_("could not export to file `%s'!"), filename); return 1; } /* now write xmp into that container, if possible */ if((format->flags() & FORMAT_FLAGS_SUPPORT_XMP) && dt_exif_xmp_attach(imgid, filename) != 0) { fprintf(stderr, "[imageio_storage_disk] could not attach xmp data to file: `%s'!\n", filename); // don't report that one to gui, as some formats (pfm, ppm, exr) just don't support // writing xmp via exiv2, so it might not be to worry. return 1; } printf("[export_job] exported to `%s'\n", filename); char *trunc = filename + strlen(filename) - 32; if(trunc < filename) trunc = filename; dt_control_log(_("%d/%d exported to `%s%s'"), num, total, trunc != filename ? ".." : "", trunc); return 0; }
void reload_defaults(dt_iop_module_t *module) { // raw images need wb (to convert from uint16_t to float): if(dt_image_is_raw(&module->dev->image_storage)) { module->default_enabled = 1; module->hide_enable_button = 1; } else module->default_enabled = 0; dt_iop_temperature_params_t tmp = (dt_iop_temperature_params_t) { 5000.0, {1.0, 1.0, 1.0} }; // get white balance coefficients, as shot char filename[DT_MAX_PATH_LEN]; int ret=0; /* check if file is raw / hdr */ if(dt_image_is_raw(&module->dev->image_storage)) { dt_image_full_path(module->dev->image_storage.id, filename, DT_MAX_PATH_LEN); libraw_data_t *raw = libraw_init(0); ret = libraw_open_file(raw, filename); if(!ret) { module->default_enabled = 1; for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.cam_mul[k]; if(tmp.coeffs[0] <= 0.0) { for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.pre_mul[k]; } if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0) { // could not get useful info, try presets: char makermodel[1024]; char *model = makermodel; dt_colorspaces_get_makermodel_split(makermodel, 1024, &model, module->dev->image_storage.exif_maker, module->dev->image_storage.exif_model); for(int i=0; i<wb_preset_count; i++) { if(!strcmp(wb_preset[i].make, makermodel) && !strcmp(wb_preset[i].model, model)) { // just take the first preset we find for this camera for(int k=0; k<3; k++) tmp.coeffs[k] = wb_preset[i].channel[k]; break; } } if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0) { // final security net: hardcoded default that fits most cams. tmp.coeffs[0] = 2.0f; tmp.coeffs[1] = 1.0f; tmp.coeffs[2] = 1.5f; } } else { tmp.coeffs[0] /= tmp.coeffs[1]; tmp.coeffs[2] /= tmp.coeffs[1]; tmp.coeffs[1] = 1.0f; } } libraw_close(raw); } memcpy(module->params, &tmp, sizeof(dt_iop_temperature_params_t)); memcpy(module->default_params, &tmp, sizeof(dt_iop_temperature_params_t)); }
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; }
// 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; }
int32_t dt_control_delete_images_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 ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; char query[1024]; sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); 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 = (long int)t->data; char filename[DT_MAX_PATH_LEN]; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN); 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) // don't remove the actual data if there are (other) duplicates using it (void)g_unlink(filename); dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); (void)g_unlink(filename); dt_image_remove(imgid); 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_queue_redraw_center(); return 0; }
void reload_defaults(dt_iop_module_t *module) { // raw images need wb (to convert from uint16_t to float): if(dt_image_is_raw(&module->dev->image_storage)) { module->default_enabled = 1; module->hide_enable_button = 1; } else module->default_enabled = 0; dt_iop_temperature_params_t tmp = (dt_iop_temperature_params_t) { 5000.0, {1.0, 1.0, 1.0} }; // get white balance coefficients, as shot char filename[DT_MAX_PATH_LEN]; int ret=0; /* check if file is raw / hdr */ if(dt_image_is_raw(&module->dev->image_storage)) { gboolean from_cache = TRUE; dt_image_full_path(module->dev->image_storage.id, filename, DT_MAX_PATH_LEN, &from_cache); libraw_data_t *raw = libraw_init(0); ret = libraw_open_file(raw, filename); if(!ret) { module->default_enabled = 1; for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.cam_mul[k]; if(tmp.coeffs[0] <= 0.0) { for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.pre_mul[k]; } if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0) { // could not get useful info, try presets: char makermodel[1024]; char *model = makermodel; dt_colorspaces_get_makermodel_split(makermodel, 1024, &model, module->dev->image_storage.exif_maker, module->dev->image_storage.exif_model); for(int i=0; i<wb_preset_count; i++) { if(!strcmp(wb_preset[i].make, makermodel) && !strcmp(wb_preset[i].model, model)) { // just take the first preset we find for this camera for(int k=0; k<3; k++) tmp.coeffs[k] = wb_preset[i].channel[k]; break; } } if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0) { // final security net: hardcoded default that fits most cams. tmp.coeffs[0] = 2.0f; tmp.coeffs[1] = 1.0f; tmp.coeffs[2] = 1.5f; } } else { tmp.coeffs[0] /= tmp.coeffs[1]; tmp.coeffs[2] /= tmp.coeffs[1]; tmp.coeffs[1] = 1.0f; } // remember daylight wb used for temperature/tint conversion, // assuming it corresponds to CIE daylight (D65) if(module->gui_data) { dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)module->gui_data; for(int c = 0; c < 3; c++) g->daylight_wb[c] = raw->color.pre_mul[c]; if(g->daylight_wb[0] == 1.0f && g->daylight_wb[1] == 1.0f && g->daylight_wb[2] == 1.0f) { // if we didn't find anything for daylight wb, look for a wb preset with appropriate name. // we're normalising that to be D65 char makermodel[1024]; char *model = makermodel; dt_colorspaces_get_makermodel_split(makermodel, 1024, &model, module->dev->image_storage.exif_maker, module->dev->image_storage.exif_model); for(int i=0; i<wb_preset_count; i++) { if(!strcmp(wb_preset[i].make, makermodel) && !strcmp(wb_preset[i].model, model) && !strncasecmp(wb_preset[i].name, "daylight", 8)) { for(int k=0; k<3; k++) g->daylight_wb[k] = wb_preset[i].channel[k]; break; } } } float temp, tint, mul[3]; for(int k=0; k<3; k++) mul[k] = g->daylight_wb[k]/tmp.coeffs[k]; convert_rgb_to_k(mul, &temp, &tint); dt_bauhaus_slider_set_default(g->scale_k, temp); dt_bauhaus_slider_set_default(g->scale_tint, tint); } } libraw_close(raw); } memcpy(module->params, &tmp, sizeof(dt_iop_temperature_params_t)); memcpy(module->default_params, &tmp, sizeof(dt_iop_temperature_params_t)); }