/* 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] = { 0 }; const dt_image_t *img = dt_image_cache_get(darktable.image_cache, mouse_over_id, 'r'); 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); gtk_widget_set_tooltip_text(GTK_WIDGET(d->metadata[md_internal_filmroll]), tooltip); 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 #if SHOW_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[14] = { 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]); } static const struct { char *tooltip; char flag; } loaders[] = { { N_("unknown"), EMPTY_FIELD}, { N_("tiff"), 't'}, { N_("png"), 'p'}, { N_("j2k"), 'J'}, { N_("jpeg"), 'j'}, { N_("exr"), 'e'}, { N_("rgbe"), 'R'}, { N_("pfm"), 'P'}, { N_("GraphicsMagick"), 'g'}, { N_("rawspeed"), 'r'} }; int loader = (unsigned int)img->loader < sizeof(loaders) / sizeof(*loaders) ? img->loader : 0; value[12] = loaders[loader].flag; char *loader_tooltip = g_strdup_printf(_("loader: %s"), _(loaders[loader].tooltip)); tooltip_parts[next_tooltip_part++] = loader_tooltip; value[13] = '\0'; tooltip = g_strjoinv("\n", tooltip_parts); g_free(loader_tooltip); _metadata_update_value(d->metadata[md_internal_flags], value); gtk_widget_set_tooltip_text(GTK_WIDGET(d->metadata[md_internal_flags]), tooltip); g_free(star_string); g_free(tooltip); #undef EMPTY_FIELD #undef FALSE_FIELD #undef TRUE_FIELD } #endif // SHOW_FLAGS /* EXIF */ _metadata_update_value_end(d->metadata[md_exif_model], img->camera_alias); _metadata_update_value_end(d->metadata[md_exif_lens], img->exif_lens); _metadata_update_value_end(d->metadata[md_exif_maker], img->camera_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); struct tm tt_exif = { 0 }; if(sscanf(img->exif_datetime_taken, "%d:%d:%d %d:%d:%d", &tt_exif.tm_year, &tt_exif.tm_mon, &tt_exif.tm_mday, &tt_exif.tm_hour, &tt_exif.tm_min, &tt_exif.tm_sec) == 6) { char datetime[200]; tt_exif.tm_year -= 1900; tt_exif.tm_mon--; tt_exif.tm_isdst = -1; mktime(&tt_exif); // just %c is too long and includes a time zone that we don't know from exif strftime(datetime, sizeof(datetime), "%a %x %X", &tt_exif); _metadata_update_value(d->metadata[md_exif_datetime], datetime); } else _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 { if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location")) { gchar *latitude = dt_util_latitude_str(img->latitude); _metadata_update_value(d->metadata[md_geotagging_lat], latitude); g_free(latitude); } else { 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); } } /* longitude */ if(isnan(img->longitude)) { _metadata_update_value(d->metadata[md_geotagging_lon], NODATA_STRING); } else { if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location")) { gchar *longitude = dt_util_longitude_str(img->longitude); _metadata_update_value(d->metadata[md_geotagging_lon], longitude); g_free(longitude); } else { 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); } } /* elevation */ if(isnan(img->elevation)) { _metadata_update_value(d->metadata[md_geotagging_ele], NODATA_STRING); } else { if(dt_conf_get_bool("plugins/lighttable/metadata_view/pretty_location")) { gchar *elevation = dt_util_elevation_str(img->elevation); _metadata_update_value(d->metadata[md_geotagging_ele], elevation); g_free(elevation); } else { snprintf(value, sizeof(value), "%.2f %s", img->elevation, _("m")); _metadata_update_value(d->metadata[md_geotagging_ele], value); } } /* release img */ dt_image_cache_read_release(darktable.image_cache, img); #ifdef USE_LUA dt_lua_async_call_alien(lua_update_metadata, 0,NULL,NULL, LUA_ASYNC_TYPENAME,"void*",self, LUA_ASYNC_TYPENAME,"int32_t",mouse_over_id,LUA_ASYNC_DONE); #endif } return; /* reset */ fill_minuses: for(int k = 0; k < md_size; k++) _metadata_update_value(d->metadata[k], NODATA_STRING); #ifdef USE_LUA dt_lua_async_call_alien(lua_update_metadata, 0,NULL,NULL, LUA_ASYNC_TYPENAME,"void*",self, LUA_ASYNC_TYPENAME,"int32_t",-1,LUA_ASYNC_DONE); #endif }
// 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; }
/* 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 dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data, lua_State *L) { double start_wtime = dt_get_wtime(); #ifndef __WIN32__ if(getuid() == 0 || geteuid() == 0) printf( "WARNING: either your user id or the effective user id are 0. are you running darktable as root?\n"); #endif #if defined(__SSE__) // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #endif dt_set_signal_handlers(); #include "is_supported_platform.h" int sse2_supported = 0; #ifdef HAVE_BUILTIN_CPU_SUPPORTS // NOTE: _may_i_use_cpu_feature() looks better, but only avaliable in ICC __builtin_cpu_init(); sse2_supported = __builtin_cpu_supports("sse2"); #else sse2_supported = dt_detect_cpu_features() & CPU_FLAG_SSE2; #endif if(!sse2_supported) { fprintf(stderr, "[dt_init] SSE2 instruction set is unavailable.\n"); fprintf(stderr, "[dt_init] expect a LOT of functionality to be broken. you have been warned.\n"); } #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD, 128 * 1024); /* use mmap() for large allocations */ #endif // make sure that stack/frame limits are good (musl) dt_set_rlimits(); // we have to have our share dir in XDG_DATA_DIRS, // otherwise GTK+ won't find our logo for the about screen (and maybe other things) { const gchar *xdg_data_dirs = g_getenv("XDG_DATA_DIRS"); gchar *new_xdg_data_dirs = NULL; gboolean set_env = TRUE; if(xdg_data_dirs != NULL && *xdg_data_dirs != '\0') { // check if DARKTABLE_SHAREDIR is already in there gboolean found = FALSE; gchar **tokens = g_strsplit(xdg_data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); // xdg_data_dirs is neither NULL nor empty => tokens != NULL for(char **iter = tokens; *iter != NULL; iter++) if(!strcmp(DARKTABLE_SHAREDIR, *iter)) { found = TRUE; break; } g_strfreev(tokens); if(found) set_env = FALSE; else new_xdg_data_dirs = g_strjoin(G_SEARCHPATH_SEPARATOR_S, DARKTABLE_SHAREDIR, xdg_data_dirs, NULL); } else { #ifndef _WIN32 // see http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html for a reason to use those as a // default if(!g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share/") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share/")) new_xdg_data_dirs = g_strdup("/usr/local/share/" G_SEARCHPATH_SEPARATOR_S "/usr/share/"); else new_xdg_data_dirs = g_strdup_printf("%s" G_SEARCHPATH_SEPARATOR_S "/usr/local/share/" G_SEARCHPATH_SEPARATOR_S "/usr/share/", DARKTABLE_SHAREDIR); #else set_env = FALSE; #endif } if(set_env) g_setenv("XDG_DATA_DIRS", new_xdg_data_dirs, 1); g_free(new_xdg_data_dirs); } setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.start_wtime = start_wtime; darktable.progname = argv[0]; // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)calloc(1, sizeof(dt_control_t)); // database char *dbfilename_from_command = NULL; char *noiseprofiles_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; #ifdef HAVE_OPENCL gboolean exclude_opencl = FALSE; gboolean print_statistics = strcmp(argv[0], "darktable-cltest"); #endif #ifdef USE_LUA char *lua_command = NULL; #endif darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *config_override = NULL; for(int k = 1; k < argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { #ifdef USE_LUA const char *lua_api_version = strcmp(LUA_API_VERSION_SUFFIX, "") ? STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH) "-" LUA_API_VERSION_SUFFIX : STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH); #endif printf("this is %s\ncopyright (c) 2009-%s johannes hanika\n" PACKAGE_BUGREPORT "\n\ncompile options:\n" " bit depth is %s\n" #ifdef _DEBUG " debug build\n" #else " normal build\n" #endif #if defined(__SSE2__) && defined(__SSE__) " SSE2 optimized codepath enabled\n" #else " SSE2 optimized codepath disabled\n" #endif #ifdef _OPENMP " OpenMP support enabled\n" #else " OpenMP support disabled\n" #endif #ifdef HAVE_OPENCL " OpenCL support enabled\n" #else " OpenCL support disabled\n" #endif #ifdef USE_LUA " Lua support enabled, API version %s\n" #else " Lua support disabled\n" #endif #ifdef USE_COLORDGTK " Colord support enabled\n" #else " Colord support disabled\n" #endif #ifdef HAVE_GPHOTO2 " gPhoto2 support enabled\n" #else " gPhoto2 support disabled\n" #endif #ifdef HAVE_GRAPHICSMAGICK " GraphicsMagick support enabled\n" #else " GraphicsMagick support disabled\n" #endif #ifdef HAVE_OPENEXR " OpenEXR support enabled\n" #else " OpenEXR support disabled\n" #endif , darktable_package_string, darktable_last_commit_year, (sizeof(void *) == 8 ? "64 bit" : sizeof(void *) == 4 ? "32 bit" : "unknown") #if USE_LUA , lua_api_version #endif ); return 1; } else if(!strcmp(argv[k], "--library") && argc > k + 1) { dbfilename_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--datadir") && argc > k + 1) { datadir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--moduledir") && argc > k + 1) { moduledir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--tmpdir") && argc > k + 1) { tmpdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--configdir") && argc > k + 1) { configdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--cachedir") && argc > k + 1) { cachedir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--localedir") && argc > k + 1) { bindtextdomain(GETTEXT_PACKAGE, argv[++k]); argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 'd' && argc > k + 1) { if(!strcmp(argv[k + 1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k + 1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k + 1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k + 1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k + 1], "input")) darktable.unmuted |= DT_DEBUG_INPUT; // input devices else if(!strcmp(argv[k + 1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k + 1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k + 1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k + 1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k + 1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k + 1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k + 1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k + 1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else if(!strcmp(argv[k + 1], "masks")) darktable.unmuted |= DT_DEBUG_MASKS; // masks related stuff. else if(!strcmp(argv[k + 1], "lua")) darktable.unmuted |= DT_DEBUG_LUA; // lua errors are reported on console else if(!strcmp(argv[k + 1], "print")) darktable.unmuted |= DT_DEBUG_PRINT; // print errors are reported on console else if(!strcmp(argv[k + 1], "camsupport")) darktable.unmuted |= DT_DEBUG_CAMERA_SUPPORT; // camera support warnings are reported on console else return usage(argv[0]); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 't' && argc > k + 1) { darktable.num_openmp_threads = CLAMP(atol(argv[k + 1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--conf") && argc > k + 1) { gchar *keyval = g_strdup(argv[++k]), *c = keyval; argv[k-1] = NULL; argv[k] = NULL; gchar *end = keyval + strlen(keyval); while(*c != '=' && c < end) c++; if(*c == '=' && *(c + 1) != '\0') { *c++ = '\0'; dt_conf_string_entry_t *entry = (dt_conf_string_entry_t *)g_malloc(sizeof(dt_conf_string_entry_t)); entry->key = g_strdup(keyval); entry->value = g_strdup(c); config_override = g_slist_append(config_override, entry); } g_free(keyval); } else if(!strcmp(argv[k], "--noiseprofiles") && argc > k + 1) { noiseprofiles_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--luacmd") && argc > k + 1) { #ifdef USE_LUA lua_command = argv[++k]; #else ++k; #endif argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--disable-opencl")) { #ifdef HAVE_OPENCL exclude_opencl = TRUE; #endif argv[k] = NULL; } else if(!strcmp(argv[k], "--")) { // "--" confuses the argument parser of glib/gtk. remove it. argv[k] = NULL; break; } else return usage(argv[0]); // fail on unrecognized options } } // remove the NULLs to not confuse gtk_init() later. for(int i = 1; i < argc; i++) { int k; for(k = i; k < argc; k++) if(argv[k] != NULL) break; if(k > i) { k -= i; for(int j = i + k; j < argc; j++) { argv[j-k] = argv[j]; argv[j] = NULL; } argc -= k; } } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } if(init_gui) { // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // make sure that we have no stale global progress bar visible. thus it's run as early is possible dt_control_progress_init(darktable.control); } #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { fprintf(stderr, "error: invalid temporary directory: %s\n", darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #ifdef USE_LUA dt_lua_init_early(L); #endif // thread-safe init: dt_exif_init(); char datadir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(datadir, sizeof(datadir)); char darktablerc[PATH_MAX] = { 0 }; snprintf(darktablerc, sizeof(darktablerc), "%s/darktablerc", datadir); // initialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, darktablerc, config_override); g_slist_free_full(config_override, g_free); // set the interface language const gchar *lang = dt_conf_get_string("ui_last/gui_language"); #if defined(_WIN32) // get the default locale if no language preference was specified in the config file if(lang == NULL || lang[0] == '\0') { const wchar_t *wcLocaleName = NULL; wcLocaleName = dtwin_get_locale(); if(wcLocaleName != NULL) { gchar *langLocale; langLocale = g_utf16_to_utf8(wcLocaleName, -1, NULL, NULL, NULL); if(langLocale != NULL) { g_free((gchar *)lang); lang = g_strdup(langLocale); } } } #endif // defined (_WIN32) if(lang != NULL && lang[0] != '\0') { g_setenv("LANGUAGE", lang, 1); if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); setlocale(LC_MESSAGES, lang); g_setenv("LANG", lang, 1); } g_free((gchar *)lang); // we need this REALLY early so that error messages can be shown, however after gtk_disable_setlocale if(init_gui) { #ifdef GDK_WINDOWING_WAYLAND // There are currently bad interactions with Wayland (drop-downs // are very narrow, scroll events lost). Until this is fixed, give // priority to the XWayland backend for Wayland users. gdk_set_allowed_backends("x11,*"); #endif gtk_init(&argc, &argv); } // detect cpu features and decide which codepaths to enable dt_codepaths_init(); // get the list of color profiles darktable.color_profiles = dt_colorspaces_init(); // initialize the database darktable.db = dt_database_init(dbfilename_from_command, load_data); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(!dt_database_get_lock_acquired(darktable.db)) { gboolean image_loaded_elsewhere = FALSE; #ifndef MAC_INTEGRATION // send the images to the other instance via dbus fprintf(stderr, "trying to open the images in the running instance\n"); GDBusConnection *connection = NULL; for(int i = 1; i < argc; i++) { // make the filename absolute ... if(argv[i] == NULL || *argv[i] == '\0') continue; gchar *filename = dt_util_normalize_path(argv[i]); if(filename == NULL) continue; if(!connection) connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); // ... and send it to the running instance of darktable image_loaded_elsewhere = g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL) != NULL; g_free(filename); } if(connection) g_object_unref(connection); #endif if(!image_loaded_elsewhere) dt_database_show_error(darktable.db); return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Make sure that the database and xmp files are in sync // We need conf and db to be up and running for that which is the case here. // FIXME: is this also useful in non-gui mode? GList *changed_xmp_files = NULL; if(init_gui && dt_conf_get_bool("run_crawler_on_start")) { changed_xmp_files = dt_control_crawler_run(); } if(init_gui) { dt_control_init(darktable.control); } else { if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) dt_gui_presets_init(); // init preset db schema. darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection = dt_collection_new(NULL); /* initialize selection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; // Initialize the password storage engine darktable.pwstorage = dt_pwstorage_new(); darktable.guides = dt_guides_init(); #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); // *SIGH* dt_set_signal_handlers(); #endif darktable.opencl = (dt_opencl_t *)calloc(1, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, exclude_opencl, print_statistics); #endif darktable.points = (dt_points_t *)calloc(1, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); darktable.noiseprofile_parser = dt_noiseprofile_init(noiseprofiles_from_command); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)calloc(1, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)calloc(1, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)calloc(1, sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)calloc(1, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); // check whether we were able to load darkroom view. if we failed, we'll crash everywhere later on. if(!darktable.develop) return 1; darktable.imageio = (dt_imageio_t *)calloc(1, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { #ifdef HAVE_GPHOTO2 // Initialize the camera control. // this is done late so that the gui can react to the signal sent but before switching to lighttable! darktable.camctl = dt_camctl_new(); #endif darktable.lib = (dt_lib_t *)calloc(1, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_gui_gtk_load_config(); // init the gui part of views dt_view_manager_gui_init(darktable.view_manager); // Loading the keybindings char keyfile[PATH_MAX] = { 0 }; // First dump the default keymapping snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // initialize undo struct darktable.undo = dt_undo_init(); } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } dt_image_local_copy_synch(); /* init lua last, since it's user made stuff it must be in the real environment */ #ifdef USE_LUA dt_lua_init(darktable.lua_state.state, lua_command); #endif if(init_gui) { const char *mode = "lighttable"; // april 1st: you have to earn using dt first! or know that you can switch views with keyboard shortcuts time_t now; time(&now); struct tm lt; localtime_r(&now, <); if(lt.tm_mon == 3 && lt.tm_mday == 1) mode = "knight"; // we have to call dt_ctl_switch_mode_to() here already to not run into a lua deadlock. // having another call later is ok dt_ctl_switch_mode_to(mode); #ifndef MAC_INTEGRATION // load image(s) specified on cmdline. // this has to happen after lua is initialized as image import can run lua code // If only one image is listed, attempt to load it in darkroom int last_id = 0; gboolean only_single_images = TRUE; int loaded_images = 0; for(int i = 1; i < argc; i++) { gboolean single_image = FALSE; if(argv[i] == NULL || *argv[i] == '\0') continue; int new_id = dt_load_from_string(argv[i], FALSE, &single_image); if(new_id > 0) { last_id = new_id; loaded_images++; if(!single_image) only_single_images = FALSE; } } if(loaded_images == 1 && only_single_images) { dt_control_set_mouse_over_id(last_id); dt_ctl_switch_mode_to("darkroom"); } #endif } // last but not least construct the popup that asks the user about images whose xmp files are newer than the // db entry if(init_gui && changed_xmp_files) { dt_control_crawler_show_image_list(changed_xmp_files); } dt_print(DT_DEBUG_CONTROL, "[init] startup took %f seconds\n", dt_get_wtime() - start_wtime); 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 gboolean upscale, const int32_t thumbnail_export, const char *filter, const gboolean copy_metadata, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total) { dt_develop_t dev; dt_dev_init(&dev, 0); dt_dev_load_image(&dev, imgid); const int buf_is_downscaled = (thumbnail_export && dt_conf_get_bool("plugins/lighttable/low_quality_thumbnails")); dt_mipmap_buffer_t buf; if(buf_is_downscaled) dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_F, DT_MIPMAP_BLOCKING, 'r'); else dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r'); const dt_image_t *img = &dev.image_storage; if(!buf.buf || !buf.width || !buf.height) { fprintf(stderr, "allocation failed???\n"); dt_control_log(_("image `%s' is not available!"), img->filename); goto error_early; } const int wd = img->width; const int ht = img->height; const float max_scale = upscale ? 100.0 : 1.0; int res = 0; dt_times_t start; dt_get_times(&start); dt_dev_pixelpipe_t pipe; res = thumbnail_export ? dt_dev_pixelpipe_init_thumbnail(&pipe, wd, ht) : dt_dev_pixelpipe_init_export(&pipe, wd, ht, format->levels(format_params)); if(!res) { dt_control_log( _("failed to allocate memory for %s, please lower the threads used for export or buy more memory."), thumbnail_export ? C_("noun", "thumbnail export") : C_("noun", "export")); goto error; } // If a style is to be applied during export, add the iop params into the history if(!thumbnail_export && format_params->style[0] != '\0') { GList *style_items = dt_styles_get_item_list(format_params->style, TRUE, -1); if(!style_items) { dt_control_log(_("cannot find the style '%s' to apply during export."), format_params->style); goto error; } // remove everything above history_end GList *history = g_list_nth(dev.history, dev.history_end); while(history) { GList *next = g_list_next(history); dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data); free(hist->params); free(hist->blend_params); free(history->data); dev.history = g_list_delete_link(dev.history, history); history = next; } // Add each params for(GList *iter = style_items; iter; iter = g_list_next(iter)) { dt_style_item_t *s = (dt_style_item_t *)iter->data; for(GList *module = dev.iop; module; module = g_list_next(module)) { dt_iop_module_t *m = (dt_iop_module_t *)module->data; if(!strcmp(m->op, s->operation)) { dt_dev_history_item_t *h = malloc(sizeof(dt_dev_history_item_t)); dt_iop_module_t *style_module = m; if((format_params->style_append && !(m->flags() & IOP_FLAGS_ONE_INSTANCE)) || m->multi_priority != s->multi_priority) { // dt_dev_module_duplicate() doesn't work here, it's trying too hard to be clever style_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t)); if(style_module && !dt_iop_load_module(style_module, m->so, m->dev)) { style_module->instance = m->instance; style_module->multi_priority = s->multi_priority; snprintf(style_module->multi_name, sizeof(style_module->multi_name), "%s", s->name); dev.iop = g_list_insert_sorted(dev.iop, style_module, sort_plugins); } else { free(h); goto error; } } h->params = s->params; h->blend_params = s->blendop_params; h->enabled = s->enabled; h->module = style_module; h->multi_priority = s->multi_priority; g_strlcpy(h->multi_name, s->name, sizeof(h->multi_name)); if(m->legacy_params && (s->module_version != m->version())) { void *new_params = malloc(m->params_size); m->legacy_params(m, h->params, s->module_version, new_params, labs(m->version())); free(h->params); h->params = new_params; } dev.history_end++; dev.history = g_list_append(dev.history, h); // make sure that dt_style_item_free doesn't free data we still use s->params = NULL; s->blendop_params = NULL; break; } } } g_list_free_full(style_items, dt_style_item_free); } dt_dev_pixelpipe_set_icc(&pipe, icc_type, icc_filename, icc_intent); dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height, buf.iscale); dt_dev_pixelpipe_create_nodes(&pipe, &dev); dt_dev_pixelpipe_synch_all(&pipe, &dev); if(filter) { if(!strncmp(filter, "pre:", 4)) dt_dev_pixelpipe_disable_after(&pipe, filter + 4); if(!strncmp(filter, "post:", 5)) dt_dev_pixelpipe_disable_before(&pipe, filter + 5); } dt_dev_pixelpipe_get_dimensions(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width, &pipe.processed_height); dt_show_times(&start, "[export] creating pixelpipe", NULL); // find output color profile for this image: int sRGB = 1; if(icc_type == DT_COLORSPACE_SRGB) { sRGB = 1; } else if(icc_type == DT_COLORSPACE_NONE) { GList *modules = dev.iop; dt_iop_module_t *colorout = NULL; while(modules) { colorout = (dt_iop_module_t *)modules->data; if(colorout->get_p && strcmp(colorout->op, "colorout") == 0) { const dt_colorspaces_color_profile_type_t *type = colorout->get_p(colorout->params, "type"); sRGB = (!type || *type == DT_COLORSPACE_SRGB); break; // colorout can't have > 1 instance } modules = g_list_next(modules); } } else { sRGB = 0; } // get only once at the beginning, in case the user changes it on the way: const gboolean high_quality_processing = ((format_params->max_width == 0 || format_params->max_width >= pipe.processed_width) && (format_params->max_height == 0 || format_params->max_height >= pipe.processed_height)) ? FALSE : high_quality; const int width = format_params->max_width; const int height = format_params->max_height; const double scalex = width > 0 ? fminf(width / (double)pipe.processed_width, max_scale) : 1.0; const double scaley = height > 0 ? fminf(height / (double)pipe.processed_height, max_scale) : 1.0; const double scale = fminf(scalex, scaley); const int processed_width = scale * pipe.processed_width + .5f; const int processed_height = scale * pipe.processed_height + .5f; const int bpp = format->bpp(format_params); dt_get_times(&start); if(high_quality_processing) { /* * if high quality processing was requested, downsampling will be done * at the very end of the pipe (just before border and watermark) */ dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); } else { // else, downsampling will be right after demosaic // so we need to turn temporarily disable in-pipe late downsampling iop. // find the finalscale module dt_dev_pixelpipe_iop_t *finalscale = NULL; { GList *nodes = g_list_last(pipe.nodes); while(nodes) { dt_dev_pixelpipe_iop_t *node = (dt_dev_pixelpipe_iop_t *)(nodes->data); if(!strcmp(node->module->op, "finalscale")) { finalscale = node; break; } nodes = g_list_previous(nodes); } } if(finalscale) finalscale->enabled = 0; // do the processing (8-bit with special treatment, to make sure we can use openmp further down): if(bpp == 8) dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale); else dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale); if(finalscale) finalscale->enabled = 1; } dt_show_times(&start, thumbnail_export ? "[dev_process_thumbnail] pixel pipeline processing" : "[dev_process_export] pixel pipeline processing", NULL); uint8_t *outbuf = pipe.backbuf; // downconversion to low-precision formats: if(bpp == 8) { if(display_byteorder) { if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff); outbuf[4 * k + 0] = r; outbuf[4 * k + 1] = g; outbuf[4 * k + 2] = b; } } // else processing output was 8-bit already, and no need to swap order } else // need to flip { // ldr output: char if(high_quality_processing) { const float *const inbuf = (float *)outbuf; for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { // convert in place, this is unfortunately very serial.. const uint8_t r = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff); const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff); const uint8_t b = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff); outbuf[4 * k + 0] = r; outbuf[4 * k + 1] = g; outbuf[4 * k + 2] = b; } } else { // !display_byteorder, need to swap: uint8_t *const buf8 = pipe.backbuf; #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) #endif // just flip byte order for(size_t k = 0; k < (size_t)processed_width * processed_height; k++) { uint8_t tmp = buf8[4 * k + 0]; buf8[4 * k + 0] = buf8[4 * k + 2]; buf8[4 * k + 2] = tmp; } } } } else if(bpp == 16) { // uint16_t per color channel float *buff = (float *)outbuf; uint16_t *buf16 = (uint16_t *)outbuf; for(int y = 0; y < processed_height; y++) for(int x = 0; x < processed_width; x++) { // convert in place const size_t k = (size_t)processed_width * y + x; for(int i = 0; i < 3; i++) buf16[4 * k + i] = CLAMP(buff[4 * k + i] * 0x10000, 0, 0xffff); } } // else output float, no further harm done to the pixels :) format_params->width = processed_width; format_params->height = processed_height; if(!ignore_exif) { int length; uint8_t *exif_profile = NULL; // Exif data should be 65536 bytes max, but if original size is close to that, // adding new tags could make it go over that... so let it be and see what // happens when we write the image char pathname[PATH_MAX] = { 0 }; gboolean from_cache = TRUE; dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache); // last param is dng mode, it's false here length = dt_exif_read_blob(&exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0); res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, exif_profile, length, imgid, num, total); free(exif_profile); } else { res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, NULL, 0, imgid, num, total); } dt_dev_pixelpipe_cleanup(&pipe); dt_dev_cleanup(&dev); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); /* now write xmp into that container, if possible */ if(copy_metadata && (format->flags(format_params) & FORMAT_FLAGS_SUPPORT_XMP)) { dt_exif_xmp_attach(imgid, filename); // no need to cancel the export if this fail } if(!thumbnail_export && strcmp(format->mime(format_params), "memory") && !(format->flags(format_params) & FORMAT_FLAGS_NO_TMPFILE)) { #ifdef USE_LUA //Synchronous calling of lua intermediate-export-image events dt_lua_lock(); lua_State *L = darktable.lua_state.state; luaA_push(L, dt_lua_image_t, &imgid); lua_pushstring(L, filename); luaA_push_type(L, format->parameter_lua_type, format_params); if (storage) luaA_push_type(L, storage->parameter_lua_type, storage_params); else lua_pushnil(L); dt_lua_event_trigger(L, "intermediate-export-image", 4); dt_lua_unlock(); #endif dt_control_signal_raise(darktable.signals, DT_SIGNAL_IMAGE_EXPORT_TMPFILE, imgid, filename, format, format_params, storage, storage_params); } return res; error: dt_dev_pixelpipe_cleanup(&pipe); error_early: dt_dev_cleanup(&dev); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); return 1; }
uint32_t views(dt_lib_module_t *self) { uint32_t v = DT_VIEW_LIGHTTABLE | DT_VIEW_MAP | DT_VIEW_TETHERING; if(dt_conf_get_bool("plugins/darkroom/tagging/visible")) v |= DT_VIEW_DARKROOM; return v; }
void commit_params (struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)p1; dt_iop_colorout_data_t *d = (dt_iop_colorout_data_t *)piece->data; gchar *overprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile"); const int overintent = dt_conf_get_int("plugins/lighttable/export/iccintent"); const int high_quality_processing = dt_conf_get_bool("plugins/lighttable/export/force_lcms2"); gchar *outprofile=NULL; int outintent = 0; /* cleanup profiles */ if (d->output) dt_colorspaces_cleanup_profile(d->output); d->output = NULL; if (d->softproof_enabled) dt_colorspaces_cleanup_profile(d->softproof); d->softproof = NULL; d->softproof_enabled = p->softproof_enabled; if(self->dev->gui_attached && self->gui_data != NULL) { dt_iop_colorout_gui_data_t *g = (dt_iop_colorout_gui_data_t *)self->gui_data; g->softproof_enabled = p->softproof_enabled; } if (d->xform) { cmsDeleteTransform(d->xform); d->xform = 0; } d->cmatrix[0] = NAN; d->lut[0][0] = -1.0f; d->lut[1][0] = -1.0f; d->lut[2][0] = -1.0f; piece->process_cl_ready = 1; /* if we are exporting then check and set usage of override profile */ if (pipe->type == DT_DEV_PIXELPIPE_EXPORT) { if (overprofile && strcmp(overprofile, "image")) snprintf(p->iccprofile, DT_IOP_COLOR_ICC_LEN, "%s", overprofile); if (overintent >= 0) p->intent = overintent; outprofile = p->iccprofile; outintent = p->intent; } else { /* we are not exporting, using display profile as output */ outprofile = p->displayprofile; outintent = p->displayintent; } /* * Setup transform flags */ uint32_t transformFlags = 0; /* creating output profile */ d->output = _create_profile(outprofile); /* creating softproof profile if softproof is enabled */ if (d->softproof_enabled && pipe->type == DT_DEV_PIXELPIPE_FULL) { d->softproof = _create_profile(p->softproofprofile); /* TODO: the use of bpc should be userconfigurable either from module or preference pane */ /* softproof flag and black point compensation */ transformFlags |= cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE|cmsFLAGS_BLACKPOINTCOMPENSATION; if(d->softproof_enabled == DT_SOFTPROOF_GAMUTCHECK) transformFlags |= cmsFLAGS_GAMUTCHECK; } /* get matrix from profile, if softproofing or high quality exporting always go xform codepath */ if (d->softproof_enabled || (pipe->type == DT_DEV_PIXELPIPE_EXPORT && high_quality_processing) || dt_colorspaces_get_matrix_from_output_profile (d->output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(d->Lab, TYPE_Lab_FLT, d->output, TYPE_RGB_FLT, d->softproof, outintent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } // user selected a non-supported output profile, check that: if (!d->xform && isnan(d->cmatrix[0])) { dt_control_log(_("unsupported output profile has been replaced by sRGB!")); if (d->output) dt_colorspaces_cleanup_profile(d->output); d->output = dt_colorspaces_create_srgb_profile(); if (d->softproof_enabled || dt_colorspaces_get_matrix_from_output_profile (d->output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(d->Lab, TYPE_Lab_FLT, d->output, TYPE_RGB_FLT, d->softproof, outintent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } } // now try to initialize unbounded mode: // we do extrapolation for input values above 1.0f. // unfortunately we can only do this if we got the computation // in our hands, i.e. for the fast builtin-dt-matrix-profile path. for(int k=0; k<3; k++) { // omit luts marked as linear (negative as marker) if(d->lut[k][0] >= 0.0f) { const float x[4] = {0.7f, 0.8f, 0.9f, 1.0f}; const float y[4] = {lerp_lut(d->lut[k], x[0]), lerp_lut(d->lut[k], x[1]), lerp_lut(d->lut[k], x[2]), lerp_lut(d->lut[k], x[3]) }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs[k]); } else d->unbounded_coeffs[k][0] = -1.0f; } //fprintf(stderr, " Output profile %s, softproof %s%s%s\n", outprofile, d->softproof_enabled?"enabled ":"disabled",d->softproof_enabled?"using profile ":"",d->softproof_enabled?p->softproofprofile:""); g_free(overprofile); }
uint32_t dt_image_import(const int32_t film_id, const char *filename, gboolean override_ignore_jpegs) { if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) return 0; const char *cc = filename + strlen(filename); for(; *cc!='.'&&cc>filename; cc--); if(!strcmp(cc, ".dt")) return 0; if(!strcmp(cc, ".dttags")) return 0; if(!strcmp(cc, ".xmp")) return 0; char *ext = g_ascii_strdown(cc+1, -1); if(override_ignore_jpegs == FALSE && (!strcmp(ext, "jpg") || !strcmp(ext, "jpeg")) && dt_conf_get_bool("ui_last/import_ignore_jpegs")) return 0; int supported = 0; char **extensions = g_strsplit(dt_supported_extensions, ",", 100); for(char **i=extensions; *i!=NULL; i++) if(!strcmp(ext, *i)) { supported = 1; break; } g_strfreev(extensions); if(!supported) { g_free(ext); return 0; } int rc; uint32_t id = 0; // select from images; if found => return gchar *imgfname; imgfname = g_path_get_basename((const gchar*)filename); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) { id = sqlite3_column_int(stmt, 0); g_free(imgfname); sqlite3_finalize(stmt); g_free(ext); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->flags &= ~DT_IMAGE_REMOVE; dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); return id; } sqlite3_finalize(stmt); // also need to set the no-legacy bit, to make sure we get the right presets (new ones) uint32_t flags = dt_conf_get_int("ui_last/import_initial_rating"); if(flags > 5) { flags = 1; dt_conf_set_int("ui_last/import_initial_rating", 1); } flags |= DT_IMAGE_NO_LEGACY_PRESETS; // insert dummy image entry in database DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into images (id, film_id, filename, caption, description, " "license, sha1sum, flags) values (null, ?1, ?2, '', '', '', '', ?3)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, flags); rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id from images where film_id = ?1 and filename = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC); if(sqlite3_step(stmt) == SQLITE_ROW) id = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); // Try to find out if this should be grouped already. gchar *basename = g_strdup(imgfname); gchar *cc2 = basename + strlen(basename); for(; *cc2!='.'&&cc2>basename; cc2--); *cc2='\0'; gchar *sql_pattern = g_strconcat(basename, ".%", NULL); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select group_id from images where film_id = ?1 and filename like ?2 and id != ?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, sql_pattern, -1, SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id); int group_id; if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0); else group_id = id; sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update images set group_id = ?1 where id = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id); sqlite3_step(stmt); sqlite3_finalize(stmt); // printf("[image_import] importing `%s' to img id %d\n", imgfname, id); // lock as shortly as possible: const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); img->group_id = group_id; // read dttags and exif for database queries! (void) dt_exif_read(img, filename); char dtfilename[DT_MAX_PATH_LEN]; g_strlcpy(dtfilename, filename, DT_MAX_PATH_LEN); dt_image_path_append_version(id, dtfilename, DT_MAX_PATH_LEN); char *c = dtfilename + strlen(dtfilename); sprintf(c, ".xmp"); (void)dt_exif_xmp_read(img, dtfilename, 0); // write through to db, but not to xmp. dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); // add a tag with the file extension guint tagid = 0; char tagname[512]; snprintf(tagname, 512, "darktable|format|%s", ext); g_free(ext); dt_tag_new(tagname, &tagid); dt_tag_attach(tagid,id); // Search for sidecar files and import them if found. glob_t *globbuf = g_malloc(sizeof(glob_t)); // Add version wildcard gchar *fname = g_strdup(filename); gchar pattern[DT_MAX_PATH_LEN]; g_snprintf(pattern, DT_MAX_PATH_LEN, "%s", filename); char *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + DT_MAX_PATH_LEN - c1, "_*"); char *c2 = fname + strlen(fname); while(*c2 != '.' && c2 > fname) c2--; snprintf(c1+2, pattern + DT_MAX_PATH_LEN - c1 - 2, "%s.xmp", c2); if (!glob(pattern, 0, NULL, globbuf)) { for (int i=0; i < globbuf->gl_pathc; i++) { int newid = -1; newid = dt_image_duplicate(id); const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, newid); dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg); (void)dt_exif_xmp_read(img, globbuf->gl_pathv[i], 0); dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED); dt_image_cache_read_release(darktable.image_cache, img); } globfree(globbuf); } g_free(imgfname); g_free(fname); g_free(basename); g_free(sql_pattern); g_free(globbuf); dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_IMPORT,id); return id; }
int32_t dt_camera_import_job_run(dt_job_t *job) { dt_camera_import_t *t = (dt_camera_import_t *)job->param; dt_control_log(_("starting to import images from camera")); // Setup a new filmroll to import images to.... t->film=(dt_film_t*)g_malloc(sizeof(dt_film_t)); dt_film_init(t->film); gchar* fixed_path = dt_util_fix_path(t->path); g_free(t->path); t->path = fixed_path; dt_variables_expand( t->vp, t->path, FALSE ); sprintf(t->film->dirname,"%s",dt_variables_get_result(t->vp)); dt_pthread_mutex_lock(&t->film->images_mutex); t->film->ref++; dt_pthread_mutex_unlock(&t->film->images_mutex); // Create recursive directories, abort if no access if( g_mkdir_with_parents(t->film->dirname,0755) == -1 ) { dt_control_log(_("failed to create import path `%s', import aborted."), t->film->dirname); return 1; } // Import path is ok, lets actually create the filmroll in database.. if(dt_film_new(t->film,t->film->dirname) > 0) { int total = g_list_length( t->images ); char message[512]= {0}; sprintf(message, ngettext ("importing %d image from camera", "importing %d images from camera", total), total ); t->bgj = dt_control_backgroundjobs_create(darktable.control, 0, message); // Switch to new filmroll dt_film_open(t->film->id); dt_ctl_switch_mode_to(DT_LIBRARY); // register listener dt_camctl_listener_t listener= {0}; listener.data=t; listener.image_downloaded=_camera_image_downloaded; listener.request_image_path=_camera_import_request_image_path; listener.request_image_filename=_camera_import_request_image_filename; // start download of images dt_camctl_register_listener(darktable.camctl,&listener); dt_camctl_import(darktable.camctl,t->camera,t->images,dt_conf_get_bool("plugins/capture/camera/import/delete_originals")); dt_camctl_unregister_listener(darktable.camctl,&listener); dt_control_backgroundjobs_destroy(darktable.control, t->bgj); dt_variables_params_destroy(t->vp); } else dt_control_log(_("failed to create filmroll for camera import, import aborted.")); dt_pthread_mutex_lock(&t->film->images_mutex); t->film->ref--; dt_pthread_mutex_unlock(&t->film->images_mutex); return 0; }
void dt_mipmap_cache_deallocate_dynamic(void *data, dt_cache_entry_t *entry) { dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data; const dt_mipmap_size_t mip = get_size(entry->key); if(mip < DT_MIPMAP_F) { struct dt_mipmap_buffer_dsc *dsc = (struct dt_mipmap_buffer_dsc *)entry->data; // don't write skulls: if(dsc->width > 8 && dsc->height > 8) { if(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_INVALIDATE) { // also remove jpg backing (always try to do that, in case user just temporarily switched it off, // to avoid inconsistencies. // if(dt_conf_get_bool("cache_disk_backend")) if(cache->cachedir[0]) { char filename[PATH_MAX] = {0}; snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", cache->cachedir, mip, get_imgid(entry->key)); g_unlink(filename); } } else if(cache->cachedir[0] && dt_conf_get_bool("cache_disk_backend")) { // serialize to disk char filename[PATH_MAX] = {0}; snprintf(filename, sizeof(filename), "%s.d/%d", cache->cachedir, mip); int mkd = g_mkdir_with_parents(filename, 0750); if(!mkd) { snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", cache->cachedir, mip, get_imgid(entry->key)); // Don't write existing files as both performance and quality (lossy jpg) suffer FILE *f = NULL; if (!g_file_test(filename, G_FILE_TEST_EXISTS) && (f = fopen(filename, "wb"))) { // first check the disk isn't full struct statvfs vfsbuf; if (!statvfs(filename, &vfsbuf)) { int64_t free_mb = ((vfsbuf.f_frsize * vfsbuf.f_bavail) >> 20); if (free_mb < 100) { fprintf(stderr, "Aborting image write as only %" PRId64 " MB free to write %s\n", free_mb, filename); goto write_error; } } else { fprintf(stderr, "Aborting image write since couldn't determine free space available to write %s\n", filename); goto write_error; } const int cache_quality = dt_conf_get_int("database_cache_quality"); const uint8_t *exif = NULL; int exif_len = 0; if(dsc->color_space == DT_COLORSPACE_SRGB) { exif = dt_mipmap_cache_exif_data_srgb; exif_len = dt_mipmap_cache_exif_data_srgb_length; } else if(dsc->color_space == DT_COLORSPACE_ADOBERGB) { exif = dt_mipmap_cache_exif_data_adobergb; exif_len = dt_mipmap_cache_exif_data_adobergb_length; } if(dt_imageio_jpeg_write(filename, entry->data + sizeof(*dsc), dsc->width, dsc->height, MIN(100, MAX(10, cache_quality)), exif, exif_len)) { write_error: g_unlink(filename); } } if(f) fclose(f); }
int dt_view_manager_switch (dt_view_manager_t *vm, int k) { // Before switching views, restore accelerators if disabled if(!darktable.control->key_accelerators_on) dt_control_key_accelerators_on(darktable.control); // destroy old module list int error = 0; dt_view_t *v = vm->view + vm->current_view; /* Special case when entering nothing (just before leaving dt) */ if ( k==DT_MODE_NONE ) { /* iterator plugins and cleanup plugins in current view */ GList *plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if (!plugin->views) fprintf(stderr,"module %s doesnt have views flags\n",plugin->name()); /* does this module belong to current view ?*/ if (plugin->views() & v->view(v) ) { plugin->gui_cleanup(plugin); dt_accel_disconnect_list(plugin->accel_closures); plugin->accel_closures = NULL; } /* get next plugin */ plugins = g_list_previous(plugins); } /* leave the current view*/ if(vm->current_view >= 0 && v->leave) v->leave(v); /* remove all widets in all containers */ for(int l=0;l<DT_UI_CONTAINER_SIZE;l++) dt_ui_container_clear(darktable.gui->ui, l); vm->current_view = -1 ; return 0 ; } int newv = vm->current_view; if (k < vm->num_views) newv = k; dt_view_t *nv = vm->view + newv; if (nv->try_enter) error = nv->try_enter(nv); if (!error) { GList *plugins; /* cleanup current view before initialization of new */ if (vm->current_view >=0) { /* leave current view */ if(v->leave) v->leave(v); dt_accel_disconnect_list(v->accel_closures); v->accel_closures = NULL; /* iterator plugins and cleanup plugins in current view */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if (!plugin->views) fprintf(stderr,"module %s doesnt have views flags\n",plugin->name()); /* does this module belong to current view ?*/ if (plugin->views() & v->view(v) ) { plugin->gui_cleanup(plugin); dt_accel_disconnect_list(plugin->accel_closures); plugin->accel_closures = NULL; } /* get next plugin */ plugins = g_list_previous(plugins); } /* remove all widets in all containers */ for(int l=0;l<DT_UI_CONTAINER_SIZE;l++) dt_ui_container_clear(darktable.gui->ui, l); } /* change current view to the new view */ vm->current_view = newv; /* restore visible stat of panels for the new view */ dt_ui_restore_panels(darktable.gui->ui); /* lets add plugins related to new view into panels */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if( plugin->views() & nv->view(v) ) { /* module should be in this view, lets initialize */ plugin->gui_init(plugin); /* try get the module expander */ GtkWidget *w = NULL; w = dt_lib_gui_get_expander(plugin); if(plugin->connect_key_accels) plugin->connect_key_accels(plugin); dt_lib_connect_common_accels(plugin); /* if we dont got an expander lets add the widget */ if (!w) w = plugin->widget; /* add module to it's container */ dt_ui_container_add_widget(darktable.gui->ui, plugin->container(), w); } /* lets get next plugin */ plugins = g_list_previous(plugins); } /* hide/show modules as last config */ plugins = g_list_last(darktable.lib->plugins); while (plugins) { dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data); if(plugin->views() & nv->view(v)) { /* set expanded if last mode was that */ char var[1024]; gboolean expanded = FALSE; gboolean visible = dt_lib_is_visible(plugin); if (plugin->expandable()) { snprintf(var, 1024, "plugins/lighttable/%s/expanded", plugin->plugin_name); expanded = dt_conf_get_bool(var); /* show expander if visible */ if(visible) { gtk_widget_show_all(GTK_WIDGET(plugin->expander)); // gtk_widget_show_all(plugin->widget); } else { gtk_widget_hide(GTK_WIDGET(plugin->expander)); // gtk_widget_hide_all(plugin->widget); } dt_lib_gui_set_expanded(plugin, expanded); } else { /* show/hide plugin widget depending on expanded flag or if plugin not is expandeable() */ if(visible) gtk_widget_show_all(plugin->widget); else gtk_widget_hide_all(plugin->widget); } } /* lets get next plugin */ plugins = g_list_previous(plugins); } /* enter view. crucially, do this before initing the plugins below, as e.g. modulegroups requires the dr stuff to be inited. */ if(newv >= 0 && nv->enter) nv->enter(nv); if(newv >= 0 && nv->connect_key_accels) nv->connect_key_accels(nv); /* raise view changed signal */ dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED); /* add endmarkers to left and right center containers */ GtkWidget *endmarker = gtk_drawing_area_new(); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_LEFT_CENTER, endmarker); g_signal_connect (G_OBJECT (endmarker), "expose-event", G_CALLBACK (dt_control_expose_endmarker), 0); gtk_widget_set_size_request(endmarker, -1, 50); gtk_widget_show(endmarker); endmarker = gtk_drawing_area_new(); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, endmarker); g_signal_connect (G_OBJECT (endmarker), "expose-event", G_CALLBACK (dt_control_expose_endmarker), (gpointer)1); gtk_widget_set_size_request(endmarker, -1, 50); gtk_widget_show(endmarker); } return error; }
// callback for the cache backend to initialize payload pointers void dt_mipmap_cache_allocate_dynamic(void *data, dt_cache_entry_t *entry) { dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data; // for full image buffers struct dt_mipmap_buffer_dsc *dsc = entry->data; const dt_mipmap_size_t mip = get_size(entry->key); // alloc mere minimum for the header + broken image buffer: if(!dsc) { if(mip <= DT_MIPMAP_F) { // these are fixed-size: entry->data = dt_alloc_align(16, cache->buffer_size[mip]); } else { entry->data = dt_alloc_align(16, sizeof(*dsc) + sizeof(float) * 4 * 64); } // fprintf(stderr, "[mipmap cache] alloc dynamic for key %u %p\n", key, *buf); if(!(entry->data)) { fprintf(stderr, "[mipmap cache] memory allocation failed!\n"); exit(1); } dsc = entry->data; if(mip <= DT_MIPMAP_F) { dsc->width = cache->max_width[mip]; dsc->height = cache->max_height[mip]; dsc->size = cache->buffer_size[mip]; dsc->color_space = DT_COLORSPACE_NONE; } else { dsc->width = 0; dsc->height = 0; dsc->color_space = DT_COLORSPACE_NONE; dsc->size = sizeof(*dsc) + sizeof(float) * 4 * 64; } } assert(dsc->size >= sizeof(*dsc)); int loaded_from_disk = 0; if(mip < DT_MIPMAP_F) { if(cache->cachedir[0] && dt_conf_get_bool("cache_disk_backend")) { // try and load from disk, if successful set flag char filename[PATH_MAX] = {0}; snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", cache->cachedir, mip, get_imgid(entry->key)); FILE *f = fopen(filename, "rb"); if(f) { long len = 0; uint8_t *blob = 0; fseek(f, 0, SEEK_END); len = ftell(f); if(len <= 0) goto read_error; // coverity madness blob = (uint8_t *)malloc(len); if(!blob) goto read_error; fseek(f, 0, SEEK_SET); int rd = fread(blob, sizeof(uint8_t), len, f); if(rd != len) goto read_error; dt_colorspaces_color_profile_type_t color_space; dt_imageio_jpeg_t jpg; if(dt_imageio_jpeg_decompress_header(blob, len, &jpg) || (jpg.width > cache->max_width[mip] || jpg.height > cache->max_height[mip]) || ((color_space = dt_imageio_jpeg_read_color_space(&jpg)) == DT_COLORSPACE_NONE) // pointless test to keep it in the if clause || dt_imageio_jpeg_decompress(&jpg, entry->data + sizeof(*dsc))) { fprintf(stderr, "[mipmap_cache] failed to decompress thumbnail for image %d from `%s'!\n", get_imgid(entry->key), filename); goto read_error; } dsc->width = jpg.width; dsc->height = jpg.height; dsc->color_space = color_space; loaded_from_disk = 1; if(0) { read_error: g_unlink(filename); } free(blob); fclose(f); } } } if(!loaded_from_disk) dsc->flags = DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE; else dsc->flags = 0; // cost is just flat one for the buffer, as the buffers might have different sizes, // to make sure quota is meaningful. if(mip >= DT_MIPMAP_F) entry->cost = 1; else entry->cost = cache->buffer_size[mip]; }
/* 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); _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); /* 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); }
/* 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 == -1) mouse_over_id = darktable.develop->image_storage.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 { #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, vl, "%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, vl, "%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); }
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)p1; dt_iop_colorout_data_t *d = (dt_iop_colorout_data_t *)piece->data; const dt_colorspaces_color_profile_type_t over_type = dt_conf_get_int("plugins/lighttable/export/icctype"); gchar *over_filename = dt_conf_get_string("plugins/lighttable/export/iccprofile"); const dt_iop_color_intent_t over_intent = dt_conf_get_int("plugins/lighttable/export/iccintent"); const int force_lcms2 = dt_conf_get_bool("plugins/lighttable/export/force_lcms2"); dt_colorspaces_color_profile_type_t out_type = DT_COLORSPACE_SRGB; gchar *out_filename = NULL; dt_iop_color_intent_t out_intent = DT_INTENT_PERCEPTUAL; const cmsHPROFILE Lab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile; cmsHPROFILE output = NULL; cmsHPROFILE softproof = NULL; d->mode = pipe->type == DT_DEV_PIXELPIPE_FULL ? darktable.color_profiles->mode : DT_PROFILE_NORMAL; if(d->xform) { cmsDeleteTransform(d->xform); d->xform = NULL; } d->cmatrix[0] = NAN; d->lut[0][0] = -1.0f; d->lut[1][0] = -1.0f; d->lut[2][0] = -1.0f; piece->process_cl_ready = 1; /* if we are exporting then check and set usage of override profile */ if(pipe->type == DT_DEV_PIXELPIPE_EXPORT) { if(over_type != DT_COLORSPACE_NONE) { p->type = over_type; g_strlcpy(p->filename, over_filename, sizeof(p->filename)); } if((unsigned int)over_intent < DT_INTENT_LAST) p->intent = over_intent; out_type = p->type; out_filename = p->filename; out_intent = p->intent; } else if(pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL) { out_type = dt_mipmap_cache_get_colorspace(); out_filename = (out_type == DT_COLORSPACE_DISPLAY ? darktable.color_profiles->display_filename : ""); out_intent = darktable.color_profiles->display_intent; } else { /* we are not exporting, using display profile as output */ out_type = darktable.color_profiles->display_type; out_filename = darktable.color_profiles->display_filename; out_intent = darktable.color_profiles->display_intent; } /* * Setup transform flags */ uint32_t transformFlags = 0; /* creating output profile */ if(out_type == DT_COLORSPACE_DISPLAY) pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock); const dt_colorspaces_color_profile_t *out_profile = dt_colorspaces_get_profile(out_type, out_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY); output = out_profile->profile; /* creating softproof profile if softproof is enabled */ if(d->mode != DT_PROFILE_NORMAL && pipe->type == DT_DEV_PIXELPIPE_FULL) { softproof = dt_colorspaces_get_profile(darktable.color_profiles->softproof_type, darktable.color_profiles->softproof_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; /* TODO: the use of bpc should be userconfigurable either from module or preference pane */ /* softproof flag and black point compensation */ transformFlags |= cmsFLAGS_SOFTPROOFING | cmsFLAGS_NOCACHE | cmsFLAGS_BLACKPOINTCOMPENSATION; if(d->mode == DT_PROFILE_GAMUTCHECK) transformFlags |= cmsFLAGS_GAMUTCHECK; } /* * NOTE: theoretically, we should be passing * UsedDirection = LCMS_USED_AS_PROOF into * dt_colorspaces_get_matrix_from_output_profile() so that * dt_colorspaces_get_matrix_from_profile() knows it, but since we do not try * to use our matrix codepath when softproof is enabled, this seemed redundant. */ /* get matrix from profile, if softproofing or high quality exporting always go xform codepath */ if(d->mode != DT_PROFILE_NORMAL || force_lcms2 || dt_colorspaces_get_matrix_from_output_profile(output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES, out_intent)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(Lab, TYPE_LabA_FLT, output, TYPE_RGBA_FLT, softproof, out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } // user selected a non-supported output profile, check that: if(!d->xform && isnan(d->cmatrix[0])) { dt_control_log(_("unsupported output profile has been replaced by sRGB!")); fprintf(stderr, "unsupported output profile `%s' has been replaced by sRGB!\n", out_profile->name); output = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT)->profile; if(d->mode != DT_PROFILE_NORMAL || dt_colorspaces_get_matrix_from_output_profile(output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES, out_intent)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(Lab, TYPE_LabA_FLT, output, TYPE_RGBA_FLT, softproof, out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } } if(out_type == DT_COLORSPACE_DISPLAY) pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock); // now try to initialize unbounded mode: // we do extrapolation for input values above 1.0f. // unfortunately we can only do this if we got the computation // in our hands, i.e. for the fast builtin-dt-matrix-profile path. for(int k = 0; k < 3; k++) { // omit luts marked as linear (negative as marker) if(d->lut[k][0] >= 0.0f) { const float x[4] = { 0.7f, 0.8f, 0.9f, 1.0f }; const float y[4] = { lerp_lut(d->lut[k], x[0]), lerp_lut(d->lut[k], x[1]), lerp_lut(d->lut[k], x[2]), lerp_lut(d->lut[k], x[3]) }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs[k]); } else d->unbounded_coeffs[k][0] = -1.0f; } g_free(over_filename); }
static void _lib_location_parser_start_element(GMarkupParseContext *cxt, const char *element_name, const char **attribute_names, const gchar **attribute_values, gpointer user_data, GError **e) { dt_lib_location_t *lib = (dt_lib_location_t *)user_data; /* only interested in place element */ if(strcmp(element_name, "place") != 0) return; /* create new place */ _lib_location_result_t *place = g_malloc0(sizeof(_lib_location_result_t)); if(!place) return; place->lon = NAN; place->lat = NAN; place->bbox_lon1 = NAN; place->bbox_lat1 = NAN; place->bbox_lon2 = NAN; place->bbox_lat2 = NAN; place->marker_type = MAP_DISPLAY_NONE; place->marker_points = NULL; gboolean show_outline = dt_conf_get_bool("plugins/map/show_outline"); int max_outline_nodes = dt_conf_get_int("plugins/map/max_outline_nodes"); /* handle the element attribute values */ const gchar **aname = attribute_names; const gchar **avalue = attribute_values; if(*aname) { while(*aname) { if(strcmp(*aname, "display_name") == 0) { place->name = g_strdup(*avalue); if(!(place->name)) goto bail_out; } else if(strcmp(*aname, "lon") == 0) place->lon = g_strtod(*avalue, NULL); else if(strcmp(*aname, "lat") == 0) place->lat = g_strtod(*avalue, NULL); else if(strcmp(*aname, "boundingbox") == 0) { char *endptr; float lon1, lat1, lon2, lat2; lat1 = g_ascii_strtod(*avalue, &endptr); if(*endptr != ',') goto broken_bbox; endptr++; lat2 = g_ascii_strtod(endptr, &endptr); if(*endptr != ',') goto broken_bbox; endptr++; lon1 = g_ascii_strtod(endptr, &endptr); if(*endptr != ',') goto broken_bbox; endptr++; lon2 = g_ascii_strtod(endptr, &endptr); if(*endptr != '\0') goto broken_bbox; place->bbox_lon1 = lon1; place->bbox_lat1 = lat1; place->bbox_lon2 = lon2; place->bbox_lat2 = lat2; broken_bbox: ; } // only use the first 'geotext' entry else if(show_outline && strcmp(*aname, "geotext") == 0 && place->marker_type == MAP_DISPLAY_NONE) { if(g_str_has_prefix(*avalue, "POINT")) { char *endptr; float lon = g_ascii_strtod(*avalue + strlen("POINT("), &endptr); float lat = g_ascii_strtod(endptr, &endptr); if(*endptr == ')') { place->marker_type = MAP_DISPLAY_POINT; dt_geo_map_display_point_t *p = malloc(sizeof(dt_geo_map_display_point_t)); p->lon = lon; p->lat = lat; place->marker_points = g_list_append(place->marker_points, p); } } else if(g_str_has_prefix(*avalue, "LINESTRING") #ifdef HAVE_OSMGPSMAP_110_OR_NEWER || g_str_has_prefix(*avalue, "POLYGON") || g_str_has_prefix(*avalue, "MULTIPOLYGON") #endif ) { gboolean error = FALSE; const char *startptr = *avalue; char *endptr; while(startptr && (*startptr == ' ' || *startptr == '(' || (*startptr >= 'A' && *startptr <= 'Z'))) startptr++; int i = 0; while(1) { float lon = g_ascii_strtod(startptr, &endptr); float lat = g_ascii_strtod(endptr, &endptr); if(*endptr == ')') break; // TODO: support holes in POLYGON and several forms in MULTIPOLYGON? if(*endptr != ',' || i > max_outline_nodes) // don't go too big for speed reasons { error = TRUE; break; } dt_geo_map_display_point_t *p = malloc(sizeof(dt_geo_map_display_point_t)); p->lon = lon; p->lat = lat; place->marker_points = g_list_append(place->marker_points, p); startptr = endptr+1; i++; } if(error) { g_list_free_full(place->marker_points, free); place->marker_points = NULL; } else { place->marker_type = g_str_has_prefix(*avalue, "LINESTRING") ? MAP_DISPLAY_TRACK : MAP_DISPLAY_POLYGON; } } else { gchar *s = g_strndup(*avalue, 100); fprintf(stderr, "unsupported outline: %s%s\n", s, strlen(s) == strlen(*avalue) ? "" : " ..."); g_free(s); } } else if(strcmp(*aname, "type") == 0) { if(strcmp(*avalue, "village") == 0) place->type = LOCATION_TYPE_RESIDENTAL; else if(strcmp(*avalue, "hamlet") == 0) place->type = LOCATION_TYPE_HAMLET; else if(strcmp(*avalue, "city") == 0) place->type = LOCATION_TYPE_CITY; else if(strcmp(*avalue, "administrative") == 0) place->type = LOCATION_TYPE_ADMINISTRATIVE; else if(strcmp(*avalue, "residental") == 0) place->type = LOCATION_TYPE_RESIDENTAL; } aname++; avalue++; } } /* check if we got sane data */ if(isnan(place->lon) || isnan(place->lat)) goto bail_out; /* add place to result list */ lib->places = g_list_append(lib->places, place); return; bail_out: g_free(place->name); g_free(place); }
static void delete_button_clicked(GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; int res = GTK_RESPONSE_YES; guint tagid; GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->related; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return; gtk_tree_model_get(model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); // First check how many images are affected by the remove int count = dt_tag_remove(tagid, FALSE); if(count > 0 && dt_conf_get_bool("plugins/lighttable/tagging/ask_before_delete_tag")) { GtkWidget *dialog; GtkWidget *win = dt_ui_main_window(darktable.gui->ui); gchar *tagname = dt_tag_get_name(tagid); dialog = gtk_message_dialog_new( GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, ngettext("do you really want to delete the tag `%s'?\n%d image is assigned this tag!", "do you really want to delete the tag `%s'?\n%d images are assigned this tag!", count), tagname, count); gtk_window_set_title(GTK_WINDOW(dialog), _("delete tag?")); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); free(tagname); } if(res != GTK_RESPONSE_YES) return; GList *tagged_images = NULL; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from tagged_images where tagid=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, tagid); while(sqlite3_step(stmt) == SQLITE_ROW) { tagged_images = g_list_append(tagged_images, GINT_TO_POINTER(sqlite3_column_int(stmt, 0))); } sqlite3_finalize(stmt); dt_tag_remove(tagid, TRUE); GList *list_iter; if((list_iter = g_list_first(tagged_images)) != NULL) { do { dt_image_synch_xmp(GPOINTER_TO_INT(list_iter->data)); } while((list_iter = g_list_next(list_iter)) != NULL); } g_list_free(g_list_first(tagged_images)); update(self, 0); update(self, 1); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
void init(dt_view_t *self) { self->data = malloc(sizeof(dt_map_t)); memset(self->data,0,sizeof(dt_map_t)); dt_map_t *lib = (dt_map_t *)self->data; OsmGpsMapSource_t map_source = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; const gchar *old_map_source = dt_conf_get_string("plugins/map/map_source"); if(old_map_source && old_map_source[0] != '\0') { // find the number of the stored map_source for(int i=0; i<=OSM_GPS_MAP_SOURCE_LAST; i++) { const gchar *new_map_source = osm_gps_map_source_get_friendly_name(i); if(!g_strcmp0(old_map_source, new_map_source)) { if(osm_gps_map_source_is_valid(i)) map_source = i; break; } } } else // open street map should be a nice default ... dt_conf_set_string("plugins/map/map_source", osm_gps_map_source_get_friendly_name(OSM_GPS_MAP_SOURCE_OPENSTREETMAP)); lib->map = g_object_new (OSM_TYPE_GPS_MAP, "map-source", map_source, "proxy-uri",g_getenv("http_proxy"), NULL); GtkWidget *parent = gtk_widget_get_parent(dt_ui_center(darktable.gui->ui)); gtk_box_pack_start(GTK_BOX(parent), GTK_WIDGET(lib->map) ,TRUE, TRUE, 0); lib->osd = g_object_new (OSM_TYPE_GPS_MAP_OSD, "show-scale",TRUE, "show-coordinates",TRUE, "show-dpad",TRUE, "show-zoom",TRUE, NULL); if(dt_conf_get_bool("plugins/map/show_map_osd")) { osm_gps_map_layer_add(OSM_GPS_MAP(lib->map), lib->osd); } /* build the query string */ int max_images_drawn = dt_conf_get_int("plugins/map/max_images_drawn"); if(max_images_drawn == 0) max_images_drawn = 100; char *geo_query = g_strdup_printf("select * from (select id from images where \ longitude >= ?1 and longitude <= ?2 and latitude <= ?3 and latitude >= ?4 \ and longitude not NULL and latitude not NULL order by abs(latitude - ?5), abs(longitude - ?6) \ limit 0, %d) order by id", max_images_drawn); /* prepare the main query statement */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), geo_query, -1, &lib->statements.main_query, NULL); g_free(geo_query); /* allow drag&drop of images from filmstrip */ gtk_drag_dest_set(GTK_WIDGET(lib->map), GTK_DEST_DEFAULT_ALL, target_list_internal, n_targets_internal, GDK_ACTION_COPY); g_signal_connect(GTK_WIDGET(lib->map), "drag-data-received", G_CALLBACK(drag_and_drop_received), self); g_signal_connect(GTK_WIDGET(lib->map), "changed", G_CALLBACK(_view_map_changed_callback), self); g_signal_connect(G_OBJECT(lib->map), "button-press-event", G_CALLBACK(_view_map_button_press_callback), self); g_signal_connect (G_OBJECT(lib->map), "motion-notify-event", G_CALLBACK(_view_map_motion_notify_callback), self); /* allow drag&drop of images from the map, too */ g_signal_connect(GTK_WIDGET(lib->map), "drag-data-get", G_CALLBACK(_view_map_dnd_get_callback), self); g_signal_connect(GTK_WIDGET(lib->map), "drag-failed", G_CALLBACK(_view_map_dnd_failed_callback), self); }
static void expose_filemanager (dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { dt_library_t *lib = (dt_library_t *)self->data; gboolean offset_changed = FALSE; /* query new collection count */ lib->collection_count = dt_collection_get_count (darktable.collection); if(darktable.gui->center_tooltip == 1) darktable.gui->center_tooltip = 2; /* get grid stride */ const int iir = dt_conf_get_int("plugins/lighttable/images_in_row"); /* get image over id */ lib->image_over = DT_VIEW_DESERT; int32_t mouse_over_id, mouse_over_group = -1; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); /* fill background */ cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); if(lib->first_visible_zoomable >= 0) { lib->offset = lib->first_visible_zoomable; } lib->first_visible_zoomable = -1; /* check if offset has been changed */ if(lib->track > 2) lib->offset += iir; if(lib->track < -2) lib->offset -= iir; lib->track = 0; if(lib->center) lib->offset = 0; lib->center = 0; int offset = lib->offset; /* if offset differs then flag as changed */ if (offset != lib->first_visible_filemanager) offset_changed = TRUE; lib->first_visible_filemanager = offset; static int oldpan = 0; const int pan = lib->pan; const float wd = width/(float)iir; const float ht = width/(float)iir; int pi = pointerx / (float)wd; int pj = pointery / (float)ht; if(pointerx < 0 || pointery < 0) pi = pj = -1; //const int pidx = grid_to_index(pj, pi, iir, offset); const int img_pointerx = iir == 1 ? pointerx : fmodf(pointerx, wd); const int img_pointery = iir == 1 ? pointery : fmodf(pointery, ht); const int max_rows = 1 + (int)((height)/ht + .5); const int max_cols = iir; int id; int clicked1 = (oldpan == 0 && pan == 1 && lib->button == 1); /* get the count of current collection */ if(lib->collection_count == 0) { const float fs = 15.0f; const float ls = 1.5f*fs; const float offy = height*0.2f; const float offx = 60; const float at = 0.3f; cairo_set_font_size(cr, fs); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_move_to(cr, offx, offy); cairo_show_text(cr, _("there are no images in this collection")); cairo_move_to(cr, offx, offy + 2*ls); cairo_show_text(cr, _("if you have not imported any images yet")); cairo_move_to(cr, offx, offy + 3*ls); cairo_show_text(cr, _("you can do so in the import module")); cairo_move_to(cr, offx - 10.0f, offy + 3*ls - ls*.25f); cairo_line_to(cr, 0.0f, 10.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); cairo_move_to(cr, offx, offy + 5*ls); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_show_text(cr, _("try to relax the filter settings in the top panel")); cairo_rel_move_to(cr, 10.0f, -ls*.25f); cairo_line_to(cr, width*0.5f, 0.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); cairo_move_to(cr, offx, offy + 6*ls); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_show_text(cr, _("or add images in the collection module in the left panel")); cairo_move_to(cr, offx - 10.0f, offy + 6*ls - ls*0.25f); cairo_rel_line_to(cr, - offx + 10.0f, 0.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); return; } /* do we have a main query collection statement */ if(!lib->statements.main_query) return; if(offset < 0) lib->offset = offset = 0; while(offset >= lib->collection_count) lib->offset = (offset -= iir); /* update scroll borders */ dt_view_set_scrollbar(self, 0, 1, 1, offset, lib->collection_count, max_rows*iir); /* let's reset and reuse the main_query statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); /* setup offset and row for the main query */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, max_rows*iir); if(mouse_over_id != -1) { const dt_image_t *mouse_over_image = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); mouse_over_group = mouse_over_image->group_id; dt_image_cache_read_release(darktable.image_cache, mouse_over_image); DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.is_grouped); DT_DEBUG_SQLITE3_RESET(lib->statements.is_grouped); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.is_grouped, 1, mouse_over_group); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.is_grouped, 2, mouse_over_id); if(sqlite3_step(lib->statements.is_grouped) != SQLITE_ROW) mouse_over_group = -1; } // prefetch the ids so that we can peek into the future to see if there are adjacent images in the same group. int *query_ids = g_malloc0(max_rows*max_cols*sizeof(int)); for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { if(sqlite3_step(lib->statements.main_query) == SQLITE_ROW) query_ids[row*iir+col] = sqlite3_column_int(lib->statements.main_query, 0); else goto end_query_cache; } } end_query_cache: cairo_save(cr); for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { //curidx = grid_to_index(row, col, iir, offset); id = query_ids[row*iir+col]; if(id > 0) { if (iir == 1 && row) continue; /* set mouse over id if pointer is in current row / col */ if(pi == col && pj == row) { mouse_over_id = id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); } /* handle mouse click on current row / col this could easily and preferable be moved to button_pressed() */ if (clicked1 && (pi == col && pj == row)) { if ((lib->modifiers & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0) dt_selection_select_single(darktable.selection, id); else if ((lib->modifiers & (GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) dt_selection_toggle(darktable.selection, id); else if ((lib->modifiers & (GDK_SHIFT_MASK)) == GDK_SHIFT_MASK) dt_selection_select_range(darktable.selection, id); } cairo_save(cr); // if(iir == 1) dt_image_prefetch(image, DT_IMAGE_MIPF); dt_view_image_expose(&(lib->image_over), id, cr, wd, iir == 1 ? height : ht, iir, img_pointerx, img_pointery); cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } cairo_restore(cr); // and now the group borders for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { id = query_ids[row*iir+col]; if(id > 0) { const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, id); int group_id = -1; if(image) group_id = image->group_id; dt_image_cache_read_release(darktable.image_cache, image); if (iir == 1 && row) continue; cairo_save(cr); gboolean paint_border = FALSE; // regular highlight border if(group_id != -1) { if(mouse_over_group == group_id && iir > 1 && ((!darktable.gui->grouping && dt_conf_get_bool("plugins/lighttable/draw_group_borders")) || group_id == darktable.gui->expanded_group_id)) { cairo_set_source_rgb(cr, 1, 0.8, 0); paint_border = TRUE; } // border of expanded group else if(darktable.gui->grouping && group_id == darktable.gui->expanded_group_id && iir > 1) { cairo_set_source_rgb(cr, 0, 0, 1); paint_border = TRUE; } } if(paint_border) { int neighbour_group = -1; // top border if(row > 0) { int _id = query_ids[(row-1)*iir+col]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, 0); cairo_line_to(cr, wd, 0); } // left border neighbour_group = -1; if(col > 0) { int _id = query_ids[row*iir+(col-1)]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, 0); cairo_line_to(cr, 0, ht); } // bottom border neighbour_group = -1; if(row < max_rows-1) { int _id = query_ids[(row+1)*iir+col]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, ht); cairo_line_to(cr, wd, ht); } // right border neighbour_group = -1; if(col < max_cols-1) { int _id = query_ids[row*iir+(col+1)]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, wd, 0); cairo_line_to(cr, wd, ht); } cairo_set_line_width(cr, 0.01*wd); cairo_stroke(cr); } cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } /* check if offset was changed and we need to prefetch thumbs */ if (offset_changed) { int32_t imgids_num = 0; const int prefetchrows = .5*max_rows+1; int32_t imgids[prefetchrows*iir]; /* clear and reset main query */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); /* setup offest and row for prefetch */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset + max_rows*iir); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, prefetchrows*iir); // prefetch jobs in inverse order: supersede previous jobs: most important last while(sqlite3_step(lib->statements.main_query) == SQLITE_ROW && imgids_num < prefetchrows*iir) imgids[imgids_num++] = sqlite3_column_int(lib->statements.main_query, 0); float imgwd = iir == 1 ? 0.97 : 0.8; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size( darktable.mipmap_cache, imgwd*wd, imgwd*(iir==1?height:ht)); while(imgids_num > 0) { imgids_num --; dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get( darktable.mipmap_cache, &buf, imgids[imgids_num], mip, DT_MIPMAP_PREFETCH); } } failure: g_free(query_ids); oldpan = pan; if(darktable.unmuted & DT_DEBUG_CACHE) dt_mipmap_cache_print(darktable.mipmap_cache); if(darktable.gui->center_tooltip == 1) // set in this round { char* tooltip = dt_history_get_items_as_string(mouse_over_id); if(tooltip != NULL) { g_object_set(G_OBJECT(dt_ui_center(darktable.gui->ui)), "tooltip-text", tooltip, (char *)NULL); g_free(tooltip); } } else if(darktable.gui->center_tooltip == 2) // not set in this round { darktable.gui->center_tooltip = 0; g_object_set(G_OBJECT(dt_ui_center(darktable.gui->ui)), "tooltip-text", "", (char *)NULL); } }
int32_t dt_control_indexer_job_run(dt_job_t *job) { // if no indexing was requested, bail out: if(!dt_conf_get_bool("run_similarity_indexer")) return 0; /* * First pass run thru ALL images and collect the ones who needs to update * \TODO in the future lets have a indexer table with ids filed with images * thats need some kind of reindexing.. all mark dirty functions adds image * to this table-- */ // temp memory for uncompressed images: uint8_t *scratchmem = dt_mipmap_cache_alloc_scratchmem(darktable.mipmap_cache); GList *images=NULL; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select images.id,film_rolls.folder||'/'||images.filename,images.histogram,images.lightmap from images,film_rolls where film_rolls.id = images.film_id", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { _control_indexer_img_t *idximg=g_malloc(sizeof( _control_indexer_img_t)); memset(idximg,0,sizeof(_control_indexer_img_t)); idximg->id = sqlite3_column_int(stmt,0); /* first check if image file exists on disk */ const char *filename = (const char *)sqlite3_column_text(stmt, 1); if (filename && !g_file_test(filename, G_FILE_TEST_IS_REGULAR)) idximg->flags |= _INDEXER_IMAGE_FILE_REMOVED; /* check if histogram should be updated */ if (sqlite3_column_bytes(stmt, 2) != sizeof(dt_similarity_histogram_t)) idximg->flags |= _INDEXER_UPDATE_HISTOGRAM; /* check if lightmap should be updated */ if (sqlite3_column_bytes(stmt, 3) != sizeof(dt_similarity_lightmap_t)) idximg->flags |= _INDEXER_UPDATE_LIGHTMAP; /* if image is flagged add to collection */ if (idximg->flags != 0) images = g_list_append(images, idximg); else g_free(idximg); } sqlite3_finalize(stmt); /* * Second pass, run thru collected images thats * need reindexing... */ GList *imgitem = g_list_first(images); if(imgitem) { char message[512]= {0}; double fraction=0; int total = g_list_length(images); guint *jid = NULL; /* background job plate only if more then one image is reindexed */ if (total > 1) { snprintf(message, 512, ngettext ("re-indexing %d image", "re-indexing %d images", total), total ); jid = (guint *)dt_control_backgroundjobs_create(darktable.control, 0, message); } do { // bail out if we're shutting down: if(!dt_control_running()) break; // if indexer was switched off during runtime, respect that as soon as we can: if(!dt_conf_get_bool("run_similarity_indexer")) break; /* get the _control_indexer_img_t pointer */ _control_indexer_img_t *idximg = imgitem->data; /* * Check if image has been delete from disk */ if ((idximg->flags&_INDEXER_IMAGE_FILE_REMOVED)) { /* file does not exist on disk lets delete image reference from database */ //char query[512]={0}; // \TODO dont delete move to an temp table and let user to revalidate /*sprintf(query,"delete from history where imgid=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL); sprintf(query,"delete from tagged_images where imgid=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL); sprintf(query,"delete from images where id=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL);*/ /* no need to additional work */ continue; } /* * Check if image histogram or lightmap should be updated. * those indexing that involves a image pipe should fall into this */ if ( (idximg->flags&_INDEXER_UPDATE_HISTOGRAM) || (idximg->flags&_INDEXER_UPDATE_LIGHTMAP) ) { /* get a mipmap of image to analyse */ dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, idximg->id, DT_MIPMAP_2, DT_MIPMAP_BLOCKING); // pointer owned by the cache or == scratchmem, no need to free this one: uint8_t *buf_decompressed = dt_mipmap_cache_decompress(&buf, scratchmem); if (!(buf.width * buf.height)) continue; /* * Generate similarity histogram data if requested */ if ( (idximg->flags&_INDEXER_UPDATE_HISTOGRAM) ) { dt_similarity_histogram_t histogram; float bucketscale = (float)DT_SIMILARITY_HISTOGRAM_BUCKETS/(float)0xff; for(int j=0; j<(4*buf.width*buf.height); j+=4) { /* swap rgb and scale to bucket index */ uint8_t rgb[3]; for(int k=0; k<3; k++) rgb[k] = (int)((buf_decompressed[j+2-k]/(float)0xff) * bucketscale); /* distribute rgb into buckets */ for(int k=0; k<3; k++) histogram.rgbl[rgb[k]][k]++; /* distribute lum into buckets */ uint8_t lum = MAX(MAX(rgb[0], rgb[1]), rgb[2]); histogram.rgbl[lum][3]++; } for(int k=0; k<DT_SIMILARITY_HISTOGRAM_BUCKETS; k++) for (int j=0; j<4; j++) histogram.rgbl[k][j] /= (buf.width*buf.height); /* store the histogram data */ dt_similarity_histogram_store(idximg->id, &histogram); } /* * Generate scaledowned similarity lightness map if requested */ if ( (idximg->flags&_INDEXER_UPDATE_LIGHTMAP) ) { dt_similarity_lightmap_t lightmap; memset(&lightmap,0,sizeof(dt_similarity_lightmap_t)); /* * create a pixbuf out of the image for downscaling */ /* first of setup a standard rgb buffer, swap bgr in same routine */ uint8_t *rgbbuf = g_malloc(buf.width*buf.height*3); for(int j=0; j<(buf.width*buf.height); j++) for(int k=0; k<3; k++) rgbbuf[3*j+k] = buf_decompressed[4*j+2-k]; /* then create pixbuf and scale down to lightmap size */ GdkPixbuf *source = gdk_pixbuf_new_from_data(rgbbuf,GDK_COLORSPACE_RGB,FALSE,8,buf.width,buf.height,(buf.width*3),NULL,NULL); GdkPixbuf *scaled = gdk_pixbuf_scale_simple(source,DT_SIMILARITY_LIGHTMAP_SIZE,DT_SIMILARITY_LIGHTMAP_SIZE,GDK_INTERP_HYPER); /* copy scaled data into lightmap */ uint8_t min=0xff,max=0; uint8_t *spixels = gdk_pixbuf_get_pixels(scaled); for(int j=0; j<(DT_SIMILARITY_LIGHTMAP_SIZE*DT_SIMILARITY_LIGHTMAP_SIZE); j++) { /* copy rgb */ for(int k=0; k<3; k++) lightmap.pixels[4*j+k] = spixels[3*j+k]; /* average intensity into 4th channel */ lightmap.pixels[4*j+3] = (lightmap.pixels[4*j+0]+ lightmap.pixels[4*j+1]+ lightmap.pixels[4*j+2])/3.0; min = MIN(min, lightmap.pixels[4*j+3]); max = MAX(max, lightmap.pixels[4*j+3]); } /* contrast stretch each channel in lightmap * TODO: do we want this... */ float scale=0; int range = max-min; if(range==0) scale = 1.0; else scale = 0xff/range; for(int j=0; j<(DT_SIMILARITY_LIGHTMAP_SIZE*DT_SIMILARITY_LIGHTMAP_SIZE); j++) { for(int k=0; k<4; k++) lightmap.pixels[4*j+k] = (lightmap.pixels[4*j+k]-min)*scale; } /* free some resources */ g_object_unref(scaled); g_object_unref(source); g_free(rgbbuf); /* store the lightmap */ dt_similarity_lightmap_store(idximg->id, &lightmap); } /* no use for buffer anymore release the mipmap */ dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } /* update background progress */ if (jid) { fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } } while ((imgitem=g_list_next(imgitem)) && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED); /* cleanup */ if (jid) dt_control_backgroundjobs_destroy(darktable.control, jid); } free(scratchmem); /* * Indexing opertions finished, lets reschedule the indexer * unless control is shutting down... */ if(dt_control_running()) dt_control_start_indexer(); return 0; }
static void _init_8( uint8_t *buf, uint32_t *width, uint32_t *height, const uint32_t imgid, const dt_mipmap_size_t size) { const uint32_t wd = *width, ht = *height; char filename[DT_MAX_PATH_LEN] = {0}; gboolean from_cache = TRUE; /* do not even try to process file if it isnt available */ dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); if (strlen(filename) == 0 || !g_file_test(filename, G_FILE_TEST_EXISTS)) { *width = *height = 0; return; } const int altered = dt_image_altered(imgid); int res = 1; const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid); const int orientation = dt_image_orientation(cimg); // the orientation for this camera is not read correctly from exiv2, so we need // to go the full libraw path (as the thumbnail will be flipped the wrong way round) const int incompatible = !strncmp(cimg->exif_maker, "Phase One", 9); dt_image_cache_read_release(darktable.image_cache, cimg); // first try exif thumbnail, that's smaller and thus faster to load: if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !dt_exif_thumbnail(filename, buf, wd, ht, orientation, width, height)) { res = 0; } else if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !incompatible) { // try to load the embedded thumbnail in raw gboolean from_cache = TRUE; memset(filename, 0, DT_MAX_PATH_LEN); dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); const char *c = filename + strlen(filename); while(*c != '.' && c > filename) c--; if(!strcasecmp(c, ".jpg")) { // try to load jpg dt_imageio_jpeg_t jpg; if(!dt_imageio_jpeg_read_header(filename, &jpg)) { uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4); if(!dt_imageio_jpeg_read(&jpg, tmp)) { // scale to fit dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height); res = 0; } free(tmp); } } else { uint8_t *tmp = 0; int32_t thumb_width, thumb_height, orientation; res = dt_imageio_large_thumbnail(filename, &tmp, &thumb_width, &thumb_height, &orientation); if(!res) { // scale to fit dt_iop_flip_and_zoom_8(tmp, thumb_width, thumb_height, buf, wd, ht, orientation, width, height); free(tmp); } } } if(res) { // try the real thing: rawspeed + pixelpipe dt_imageio_module_format_t format; _dummy_data_t dat; format.bpp = _bpp; format.write_image = _write_image; format.levels = _levels; dat.head.max_width = wd; dat.head.max_height = ht; dat.buf = buf; // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing, and signal we want thumbnail export res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, 1, 1, 0, 1, NULL,FALSE,NULL,NULL); if(!res) { // might be smaller, or have a different aspect than what we got as input. *width = dat.head.width; *height = dat.head.height; } } // fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, dat.head.width, dat.head.height); // any errors? if(res) { // fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n"); *width = *height = 0; return; } // TODO: various speed optimizations: // TODO: also init all smaller mips! // TODO: use mipf, but: // TODO: if output is cropped, don't use mipf! }
void gui_init(struct dt_iop_module_t *self) { const int force_lcms2 = dt_conf_get_bool("plugins/lighttable/export/force_lcms2"); self->gui_data = calloc(1, sizeof(dt_iop_colorout_gui_data_t)); dt_iop_colorout_gui_data_t *g = (dt_iop_colorout_gui_data_t *)self->gui_data; g->profiles = NULL; dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "sRGB", sizeof(prof->filename)); g_strlcpy(prof->name, "sRGB", sizeof(prof->name)); int pos; int display_pos; prof->pos = 0; prof->display_pos = 0; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "adobergb", sizeof(prof->filename)); g_strlcpy(prof->name, "adobergb", sizeof(prof->name)); prof->pos = 1; prof->display_pos = 1; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "X profile", sizeof(prof->filename)); g_strlcpy(prof->name, "X profile", sizeof(prof->name)); prof->pos = -1; prof->display_pos = 2; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "linear_rgb", sizeof(prof->filename)); g_strlcpy(prof->name, "linear_rgb", sizeof(prof->name)); pos = prof->pos = 2; display_pos = prof->display_pos = 3; g->profiles = g_list_append(g->profiles, prof); prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); g_strlcpy(prof->filename, "linear_rec2020_rgb", sizeof(prof->filename)); g_strlcpy(prof->name, "linear_rec2020_rgb", sizeof(prof->name)); pos = prof->pos = 3; display_pos = prof->display_pos = 4; g->profiles = g_list_append(g->profiles, prof); // read {conf,data}dir/color/out/*.icc char datadir[PATH_MAX] = { 0 }; char confdir[PATH_MAX] = { 0 }; char dirname[PATH_MAX] = { 0 }; char filename[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(confdir, sizeof(confdir)); dt_loc_get_datadir(datadir, sizeof(datadir)); snprintf(dirname, sizeof(dirname), "%s/color/out", confdir); if(!g_file_test(dirname, G_FILE_TEST_IS_DIR)) snprintf(dirname, sizeof(dirname), "%s/color/out", datadir); cmsHPROFILE tmpprof; const gchar *d_name; GDir *dir = g_dir_open(dirname, 0, NULL); if(dir) { while((d_name = g_dir_read_name(dir))) { snprintf(filename, sizeof(filename), "%s/%s", dirname, d_name); tmpprof = cmsOpenProfileFromFile(filename, "r"); if(tmpprof) { char *lang = getenv("LANG"); if(!lang) lang = "en_US"; dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)g_malloc0(sizeof(dt_iop_color_profile_t)); dt_colorspaces_get_profile_name(tmpprof, lang, lang + 3, prof->name, sizeof(prof->name)); g_strlcpy(prof->filename, d_name, sizeof(prof->filename)); prof->pos = ++pos; prof->display_pos = ++display_pos; cmsCloseProfile(tmpprof); g->profiles = g_list_append(g->profiles, prof); } } g_dir_close(dir); } self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE); // TODO: g->cbox1 = dt_bauhaus_combobox_new(self); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox1, TRUE, TRUE, 0); dt_bauhaus_widget_set_label(g->cbox1, NULL, _("output intent")); dt_bauhaus_combobox_add(g->cbox1, _("perceptual")); dt_bauhaus_combobox_add(g->cbox1, _("relative colorimetric")); dt_bauhaus_combobox_add(g->cbox1, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(g->cbox1, _("absolute colorimetric")); g->cbox4 = dt_bauhaus_combobox_new(self); dt_bauhaus_widget_set_label(g->cbox4, NULL, _("display intent")); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox4, TRUE, TRUE, 0); dt_bauhaus_combobox_add(g->cbox4, _("perceptual")); dt_bauhaus_combobox_add(g->cbox4, _("relative colorimetric")); dt_bauhaus_combobox_add(g->cbox4, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(g->cbox4, _("absolute colorimetric")); if(!force_lcms2) { gtk_widget_set_no_show_all(g->cbox1, TRUE); gtk_widget_set_visible(g->cbox1, FALSE); gtk_widget_set_no_show_all(g->cbox4, TRUE); gtk_widget_set_visible(g->cbox4, FALSE); } g->cbox2 = dt_bauhaus_combobox_new(self); g->cbox3 = dt_bauhaus_combobox_new(self); g->cbox5 = dt_bauhaus_combobox_new(self); dt_bauhaus_widget_set_label(g->cbox2, NULL, _("output profile")); dt_bauhaus_widget_set_label(g->cbox5, NULL, _("softproof profile")); dt_bauhaus_widget_set_label(g->cbox3, NULL, _("display profile")); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox2, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox5, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), g->cbox3, TRUE, TRUE, 0); GList *l = g->profiles; while(l) { dt_iop_color_profile_t *prof = (dt_iop_color_profile_t *)l->data; if(!strcmp(prof->name, "X profile")) { // the system display profile is only suitable for display purposes dt_bauhaus_combobox_add(g->cbox3, _("system display profile")); } else if(!strcmp(prof->name, "linear_rec709_rgb") || !strcmp(prof->name, "linear_rgb")) { dt_bauhaus_combobox_add(g->cbox2, _("linear Rec709 RGB")); dt_bauhaus_combobox_add(g->cbox3, _("linear Rec709 RGB")); dt_bauhaus_combobox_add(g->cbox5, _("linear Rec709 RGB")); } else if(!strcmp(prof->name, "linear_rec2020_rgb")) { dt_bauhaus_combobox_add(g->cbox2, _("linear Rec2020 RGB")); dt_bauhaus_combobox_add(g->cbox3, _("linear Rec2020 RGB")); dt_bauhaus_combobox_add(g->cbox5, _("linear Rec2020 RGB")); } else if(!strcmp(prof->name, "sRGB")) { dt_bauhaus_combobox_add(g->cbox2, _("sRGB (web-safe)")); dt_bauhaus_combobox_add(g->cbox3, _("sRGB (web-safe)")); dt_bauhaus_combobox_add(g->cbox5, _("sRGB (web-safe)")); } else if(!strcmp(prof->name, "adobergb")) { dt_bauhaus_combobox_add(g->cbox2, _("Adobe RGB (compatible)")); dt_bauhaus_combobox_add(g->cbox3, _("Adobe RGB (compatible)")); dt_bauhaus_combobox_add(g->cbox5, _("Adobe RGB (compatible)")); } else { dt_bauhaus_combobox_add(g->cbox2, prof->name); dt_bauhaus_combobox_add(g->cbox3, prof->name); dt_bauhaus_combobox_add(g->cbox5, prof->name); } l = g_list_next(l); } char tooltip[1024]; g_object_set(G_OBJECT(g->cbox1), "tooltip-text", _("rendering intent"), (char *)NULL); snprintf(tooltip, sizeof(tooltip), _("ICC profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox2), "tooltip-text", tooltip, (char *)NULL); snprintf(tooltip, sizeof(tooltip), _("display ICC profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox3), "tooltip-text", tooltip, (char *)NULL); snprintf(tooltip, sizeof(tooltip), _("softproof ICC profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(g->cbox5), "tooltip-text", tooltip, (char *)NULL); g_signal_connect(G_OBJECT(g->cbox1), "value-changed", G_CALLBACK(intent_changed), (gpointer)self); g_signal_connect(G_OBJECT(g->cbox4), "value-changed", G_CALLBACK(display_intent_changed), (gpointer)self); g_signal_connect(G_OBJECT(g->cbox2), "value-changed", G_CALLBACK(output_profile_changed), (gpointer)self); g_signal_connect(G_OBJECT(g->cbox3), "value-changed", G_CALLBACK(display_profile_changed), (gpointer)self); g_signal_connect(G_OBJECT(g->cbox5), "value-changed", G_CALLBACK(softproof_profile_changed), (gpointer)self); // reload the profiles when the display profile changed! dt_control_signal_connect(darktable.signals, DT_SIGNAL_CONTROL_PROFILE_CHANGED, G_CALLBACK(_signal_profile_changed), self->dev); }
int dt_init(int argc, char *argv[], const int init_gui) { #ifndef __WIN32__ if(getuid() == 0 || geteuid() == 0) printf("WARNING: either your user id or the effective user id are 0. are you running darktable as root?\n"); #endif // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #if !defined __APPLE__ && !defined __WIN32__ _dt_sigsegv_old_handler = signal(SIGSEGV,&_dt_sigsegv_handler); #endif #ifndef __GNUC_PREREQ // on OSX, gcc-4.6 and clang chokes if this is not here. #if defined __GNUC__ && defined __GNUC_MINOR__ # define __GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define __GNUC_PREREQ(maj, min) 0 #endif #endif #ifndef __has_builtin // http://clang.llvm.org/docs/LanguageExtensions.html#feature-checking-macros #define __has_builtin(x) false #endif #ifndef __SSE3__ #error "Unfortunately we depend on SSE3 instructions at this time." #error "Please contribute a backport patch (or buy a newer processor)." #else #if (__GNUC_PREREQ(4,8) || __has_builtin(__builtin_cpu_supports)) //FIXME: check will work only in GCC 4.8+ !!! implement manual cpuid check !!! //NOTE: _may_i_use_cpu_feature() looks better, but only avaliable in ICC if (!__builtin_cpu_supports("sse3")) { fprintf(stderr, "[dt_init] unfortunately we depend on SSE3 instructions at this time.\n"); fprintf(stderr, "[dt_init] please contribute a backport patch (or buy a newer processor).\n"); return 1; } #else //FIXME: no way to check for SSE3 in runtime, implement manual cpuid check !!! #endif #endif #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD,128*1024) ; /* use mmap() for large allocations */ #endif // we have to have our share dir in XDG_DATA_DIRS, // otherwise GTK+ won't find our logo for the about screen (and maybe other things) { const gchar *xdg_data_dirs = g_getenv("XDG_DATA_DIRS"); gchar *new_xdg_data_dirs = NULL; gboolean set_env = TRUE; if(xdg_data_dirs != NULL && *xdg_data_dirs != '\0') { // check if DARKTABLE_SHAREDIR is already in there gboolean found = FALSE; gchar **tokens = g_strsplit(xdg_data_dirs, ":", 0); // xdg_data_dirs is neither NULL nor empty => tokens != NULL for(char **iter = tokens; *iter != NULL; iter++) if(!strcmp(DARKTABLE_SHAREDIR, *iter)) { found = TRUE; break; } g_strfreev(tokens); if(found) set_env = FALSE; else new_xdg_data_dirs = g_strjoin(":", DARKTABLE_SHAREDIR, xdg_data_dirs, NULL); } else new_xdg_data_dirs = g_strdup(DARKTABLE_SHAREDIR); if(set_env) g_setenv("XDG_DATA_DIRS", new_xdg_data_dirs, 1); g_free(new_xdg_data_dirs); } setlocale(LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.progname = argv[0]; // database gchar *dbfilename_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *images_to_load = NULL, *config_override = NULL; for(int k=1; k<argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { printf("this is "PACKAGE_STRING"\ncopyright (c) 2009-2014 johannes hanika\n"PACKAGE_BUGREPORT"\n" #ifdef _OPENMP "OpenMP support enabled\n" #else "OpenMP support disabled\n" #endif ); return 1; } else if(!strcmp(argv[k], "--library")) { dbfilename_from_command = argv[++k]; } else if(!strcmp(argv[k], "--datadir")) { datadir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--moduledir")) { moduledir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--tmpdir")) { tmpdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--configdir")) { configdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--cachedir")) { cachedir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--localedir")) { bindtextdomain (GETTEXT_PACKAGE, argv[++k]); } else if(argv[k][1] == 'd' && argc > k+1) { if(!strcmp(argv[k+1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k+1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k+1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k+1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k+1], "fswatch")) darktable.unmuted |= DT_DEBUG_FSWATCH; // fswatch module else if(!strcmp(argv[k+1], "input")) darktable.unmuted |= DT_DEBUG_INPUT; // input devices else if(!strcmp(argv[k+1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k+1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k+1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k+1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k+1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k+1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k+1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k+1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else if(!strcmp(argv[k+1], "masks")) darktable.unmuted |= DT_DEBUG_MASKS; // masks related stuff. else if(!strcmp(argv[k+1], "lua")) darktable.unmuted |= DT_DEBUG_LUA; // lua errors are reported on console else return usage(argv[0]); k ++; } else if(argv[k][1] == 't' && argc > k+1) { darktable.num_openmp_threads = CLAMP(atol(argv[k+1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k ++; } else if(!strcmp(argv[k], "--conf")) { gchar *keyval = g_strdup(argv[++k]), *c = keyval; while(*c != '=' && c < keyval + strlen(keyval)) c++; if(*c == '=' && *(c+1) != '\0') { *c++ = '\0'; dt_conf_string_entry_t *entry = (dt_conf_string_entry_t*)g_malloc(sizeof(dt_conf_string_entry_t)); entry->key = g_strdup(keyval); entry->value = g_strdup(c); config_override = g_slist_append(config_override, entry); } g_free(keyval); } } #ifndef MAC_INTEGRATION else { images_to_load = g_slist_append(images_to_load, argv[k]); } #endif } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { printf(_("ERROR : invalid temporary directory : %s\n"),darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); #endif // does not work, as gtk is not inited yet. // even if it were, it's a super bad idea to invoke gtk stuff from // a signal handler. /* check cput caps */ // dt_check_cpu(argc,argv); #ifdef HAVE_GEGL char geglpath[PATH_MAX]; char datadir[PATH_MAX]; dt_loc_get_datadir(datadir, sizeof(datadir)); snprintf(geglpath, sizeof(geglpath), "%s/gegl:/usr/lib/gegl-0.0", datadir); (void)setenv("GEGL_PATH", geglpath, 1); gegl_init(&argc, &argv); #endif #ifdef USE_LUA dt_lua_init_early(NULL); #endif // thread-safe init: dt_exif_init(); char datadir[PATH_MAX]; dt_loc_get_user_config_dir (datadir, sizeof(datadir)); char filename[PATH_MAX]; snprintf(filename, sizeof(filename), "%s/darktablerc", datadir); // initialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, filename, config_override); g_slist_free_full(config_override, g_free); // set the interface language const gchar* lang = dt_conf_get_string("ui_last/gui_language"); // we may not g_free 'lang' since it is owned by setlocale afterwards if(lang != NULL && lang[0] != '\0') { if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); } // initialize the database darktable.db = dt_database_init(dbfilename_from_command); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(!dt_database_get_lock_acquired(darktable.db)) { // send the images to the other instance via dbus if(images_to_load) { GSList *p = images_to_load; // get a connection! GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION,NULL, NULL); while (p != NULL) { // make the filename absolute ... gchar *filename = dt_make_path_absolute((gchar*)p->data); if(filename == NULL) continue; // ... and send it to the running instance of darktable g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new ("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); p = g_slist_next(p); g_free(filename); } g_slist_free(images_to_load); g_object_unref(connection); } return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Make sure that the database and xmp files are in sync before starting the fswatch. // We need conf and db to be up and running for that which is the case here. // FIXME: is this also useful in non-gui mode? GList *changed_xmp_files = NULL; if(init_gui && dt_conf_get_bool("run_crawler_on_start")) { changed_xmp_files = dt_control_crawler_run(); } // Initialize the filesystem watcher darktable.fswatch=dt_fswatch_new(); #ifdef HAVE_GPHOTO2 // Initialize the camera control darktable.camctl=dt_camctl_new(); #endif // get max lighttable thumbnail size: darktable.thumbnail_width = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_width"), 200, 3000); darktable.thumbnail_height = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_height"), 200, 3000); // and make sure it can be mip-mapped all the way from mip4 to mip0 darktable.thumbnail_width /= 16; darktable.thumbnail_width *= 16; darktable.thumbnail_height /= 16; darktable.thumbnail_height *= 16; // Initialize the password storage engine darktable.pwstorage=dt_pwstorage_new(); // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)calloc(1, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else { if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) dt_gui_presets_init(); // init preset db schema. darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection_listeners = NULL; darktable.collection = dt_collection_new(NULL); /* initialize selection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); #endif darktable.opencl = (dt_opencl_t *)calloc(1, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, argc, argv); #endif darktable.blendop = (dt_blendop_t *)calloc(1, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)calloc(1, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)calloc(1, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)calloc(1, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)calloc(1, sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui, argc, argv)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)calloc(1, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { darktable.lib = (dt_lib_t *)calloc(1, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); } darktable.imageio = (dt_imageio_t *)calloc(1, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); if(init_gui) { // Loading the keybindings char keyfile[PATH_MAX]; // First dump the default keymapping snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // initialize undo struct darktable.undo = dt_undo_init(); // load image(s) specified on cmdline int id = 0; if(images_to_load) { // If only one image is listed, attempt to load it in darkroom gboolean load_in_dr = (g_slist_next(images_to_load) == NULL); GSList *p = images_to_load; while (p != NULL) { // don't put these function calls into MAX(), the macro will evaluate // it twice (and happily deadlock, in this particular case) int newid = dt_load_from_string((gchar*)p->data, load_in_dr); id = MAX(id, newid); p = g_slist_next(p); } if (!load_in_dr || id == 0) dt_ctl_switch_mode_to(DT_LIBRARY); g_slist_free(images_to_load); } else dt_ctl_switch_mode_to(DT_LIBRARY); } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } dt_image_local_copy_synch(); /* init lua last, since it's user made stuff it must be in the real environment */ #ifdef USE_LUA dt_lua_init(darktable.lua_state.state,init_gui); #endif // last but not least construct the popup that asks the user about images whose xmp files are newer than the db entry if(init_gui && changed_xmp_files) { dt_control_crawler_show_image_list(changed_xmp_files); } return 0; }
void _camera_import_dialog_new(_camera_import_dialog_t *data) { data->dialog=gtk_dialog_new_with_buttons(_("import images from camera"),NULL,GTK_DIALOG_MODAL,_("cancel"),GTK_RESPONSE_NONE,C_("camera import", "import"),GTK_RESPONSE_ACCEPT,NULL); GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (data->dialog)); // List - setup store data->store = gtk_list_store_new (2,GDK_TYPE_PIXBUF,G_TYPE_STRING); // IMPORT PAGE data->import.page=gtk_vbox_new(FALSE,5); gtk_container_set_border_width(GTK_CONTAINER(data->import.page),5); // Top info data->import.info=gtk_label_new( _("please wait while prefetching thumbnails of images from camera...") ); gtk_label_set_single_line_mode( GTK_LABEL(data->import.info) , FALSE ); gtk_misc_set_alignment(GTK_MISC(data->import.info), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->import.page),data->import.info,FALSE,FALSE,0); // jobcode data->import.jobname=_camera_import_gconf_widget(data,_("jobcode"),"plugins/capture/camera/import/jobcode"); gtk_box_pack_start(GTK_BOX(data->import.page),GTK_WIDGET(data->import.jobname->widget),FALSE,FALSE,0); // Create the treview with list model data store data->import.treeview=gtk_scrolled_window_new(NULL,NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(data->import.treeview),GTK_POLICY_NEVER,GTK_POLICY_ALWAYS); gtk_container_add(GTK_CONTAINER(data->import.treeview), gtk_tree_view_new()); GtkTreeView *treeview=GTK_TREE_VIEW(gtk_bin_get_child(GTK_BIN(data->import.treeview))); GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new( ); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( _("thumbnail") , renderer,"pixbuf",0, (char *)NULL); gtk_tree_view_append_column( treeview , column); renderer = gtk_cell_renderer_text_new( ); column = gtk_tree_view_column_new_with_attributes( _("storage file"), renderer, "text", 1, (char *)NULL); gtk_tree_view_append_column( treeview , column); gtk_tree_view_column_set_expand( column, TRUE); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); gtk_tree_selection_set_mode(selection,GTK_SELECTION_MULTIPLE); gtk_tree_view_set_model(treeview,GTK_TREE_MODEL(data->store)); gtk_tree_view_set_headers_visible(treeview,FALSE); gtk_box_pack_start(GTK_BOX(data->import.page),data->import.treeview,TRUE,TRUE,0); // SETTINGS PAGE data->settings.page=gtk_vbox_new(FALSE,5); gtk_container_set_border_width(GTK_CONTAINER(data->settings.page),5); // general settings gtk_box_pack_start(GTK_BOX(data->settings.page),dtgtk_label_new(_("general"),DARKTABLE_LABEL_TAB|DARKTABLE_LABEL_ALIGN_RIGHT),FALSE,FALSE,0); // ignoring of jpegs. hack while we don't handle raw+jpeg in the same directories. data->settings.general.ignore_jpeg = gtk_check_button_new_with_label (_("ignore JPEG files")); g_object_set(data->settings.general.ignore_jpeg, "tooltip-text", _("do not load files with an extension of .jpg or .jpeg. this can be useful when there are raw+JPEG in a directory."), NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->settings.general.ignore_jpeg), dt_conf_get_bool("ui_last/import_ignore_jpegs")); gtk_box_pack_start(GTK_BOX(data->settings.page), data->settings.general.ignore_jpeg, FALSE, FALSE, 0); g_signal_connect (G_OBJECT(data->settings.general.ignore_jpeg), "clicked",G_CALLBACK (_check_button_callback),data); GtkWidget *hbox=gtk_hbox_new(FALSE,5); data->settings.general.date_override=gtk_check_button_new_with_label(_("override today's date")); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_override,FALSE,FALSE,0); g_object_set(data->settings.general.date_override,"tooltip-text",_("check this, if you want to override the timestamp used when expanding variables:\n$(YEAR), $(MONTH), $(DAY),\n$(HOUR), $(MINUTE), $(SECONDS)"),(char *)NULL); data->settings.general.date_entry=gtk_entry_new(); gtk_widget_set_sensitive( data->settings.general.date_entry, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->settings.general.date_override))); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_entry,TRUE,TRUE,0); g_signal_connect (G_OBJECT (data->settings.general.date_override), "clicked",G_CALLBACK (_check_button_callback),data); gtk_box_pack_start(GTK_BOX(data->settings.page),hbox,FALSE,FALSE,0); // THE NOTEBOOK data->notebook=gtk_notebook_new(); gtk_notebook_append_page(GTK_NOTEBOOK(data->notebook),data->import.page,gtk_label_new(_("images"))); gtk_notebook_append_page(GTK_NOTEBOOK(data->notebook),data->settings.page,gtk_label_new(_("settings"))); // end gtk_box_pack_start(GTK_BOX(content),data->notebook,TRUE,TRUE,0); //gtk_widget_set_size_request(content,400,400); }
int dt_control_key_pressed_override(guint key, guint state) { dt_control_accels_t *accels = &darktable.control->accels; // TODO: if darkroom mode // did a : vim-style command start? static GList *autocomplete = NULL; static char vimkey_input[256]; if(darktable.control->vimkey_cnt) { guchar unichar = gdk_keyval_to_unicode(key); if(key == GDK_KEY_Return) { if(!strcmp(darktable.control->vimkey, ":q")) { dt_control_quit(); } else { dt_bauhaus_vimkey_exec(darktable.control->vimkey); } darktable.control->vimkey[0] = 0; darktable.control->vimkey_cnt = 0; dt_control_log_ack_all(); g_list_free(autocomplete); autocomplete = NULL; } else if(key == GDK_KEY_Escape) { darktable.control->vimkey[0] = 0; darktable.control->vimkey_cnt = 0; dt_control_log_ack_all(); g_list_free(autocomplete); autocomplete = NULL; } else if(key == GDK_KEY_BackSpace) { darktable.control->vimkey_cnt -= (darktable.control->vimkey + darktable.control->vimkey_cnt) - g_utf8_prev_char(darktable.control->vimkey + darktable.control->vimkey_cnt); darktable.control->vimkey[darktable.control->vimkey_cnt] = 0; if(darktable.control->vimkey_cnt == 0) dt_control_log_ack_all(); else dt_control_log(darktable.control->vimkey); g_list_free(autocomplete); autocomplete = NULL; } else if(key == GDK_KEY_Tab) { // TODO: also support :preset and :get? // auto complete: if(darktable.control->vimkey_cnt < 5) { snprintf(darktable.control->vimkey, sizeof(darktable.control->vimkey), ":set "); darktable.control->vimkey_cnt = 5; } else if(!autocomplete) { // TODO: handle '.'-separated things separately // this is a static list, and tab cycles through the list g_strlcpy(vimkey_input, darktable.control->vimkey + 5, sizeof(vimkey_input)); autocomplete = dt_bauhaus_vimkey_complete(darktable.control->vimkey + 5); autocomplete = g_list_append(autocomplete, vimkey_input); // remember input to cycle back } if(autocomplete) { // pop first. // the paths themselves are owned by bauhaus, // no free required. snprintf(darktable.control->vimkey, sizeof(darktable.control->vimkey), ":set %s", (char *)autocomplete->data); autocomplete = g_list_remove(autocomplete, autocomplete->data); darktable.control->vimkey_cnt = strlen(darktable.control->vimkey); } dt_control_log(darktable.control->vimkey); } else if(g_unichar_isprint(unichar)) // printable unicode character { gchar utf8[6]; gint char_width = g_unichar_to_utf8(unichar, utf8); if(darktable.control->vimkey_cnt + 1 + char_width < 256) { g_utf8_strncpy(darktable.control->vimkey + darktable.control->vimkey_cnt, utf8, 1); darktable.control->vimkey_cnt += char_width; darktable.control->vimkey[darktable.control->vimkey_cnt] = 0; dt_control_log(darktable.control->vimkey); g_list_free(autocomplete); autocomplete = NULL; } } else if(key == GDK_KEY_Up) { // TODO: step history up and copy to vimkey } else if(key == GDK_KEY_Down) { // TODO: step history down and copy to vimkey } return 1; } else if(key == ':' && darktable.control->key_accelerators_on) { darktable.control->vimkey[0] = ':'; darktable.control->vimkey[1] = 0; darktable.control->vimkey_cnt = 1; dt_control_log(darktable.control->vimkey); return 1; } /* check if key accelerators are enabled*/ if(darktable.control->key_accelerators_on != 1) return 0; if(key == accels->global_sideborders.accel_key && state == accels->global_sideborders.accel_mods) { /* toggle panel viewstate */ dt_ui_toggle_panels_visibility(darktable.gui->ui); /* trigger invalidation of centerview to reprocess pipe */ dt_dev_invalidate(darktable.develop); gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui)); return 1; } else if(key == accels->global_header.accel_key && state == accels->global_header.accel_mods) { char key[512]; const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager); /* do nothing if in collapse panel state TODO: reconsider adding this check to ui api */ g_snprintf(key, sizeof(key), "%s/ui/panel_collaps_state", cv->module_name); if(dt_conf_get_int(key)) return 0; /* toggle the header visibility state */ g_snprintf(key, sizeof(key), "%s/ui/show_header", cv->module_name); gboolean header = !dt_conf_get_bool(key); dt_conf_set_bool(key, header); /* show/hide the actual header panel */ dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_TOP, header, TRUE); gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui)); return 1; } return 0; }
void dt_film_import1(dt_film_t *film) { gboolean recursive = dt_conf_get_bool("ui_last/import_recursive"); /* first of all gather all images to import */ GList *images = NULL; images = _film_recursive_get_files(film->dirname, recursive, &images); if(g_list_length(images) == 0) { dt_control_log(_("no supported images were found to be imported")); return; } /* we got ourself a list of images, lets sort and start import */ images = g_list_sort(images,(GCompareFunc)_film_filename_cmp); /* let's start import of images */ gchar message[512] = {0}; double fraction = 0; uint32_t total = g_list_length(images); g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image","importing %d images", total), total); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); /* loop thru the images and import to current film roll */ dt_film_t *cfr = film; GList *image = g_list_first(images); do { gchar *cdn = g_path_get_dirname((const gchar *)image->data); /* check if we need to initialize a new filmroll */ if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0) { #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the jsut imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif /* cleanup previously imported filmroll*/ if(cfr && cfr!=film) { if(dt_film_is_empty(cfr->id)) { dt_film_remove(cfr->id); } dt_film_cleanup(cfr); g_free(cfr); cfr = NULL; } /* initialize and create a new film to import to */ cfr = g_malloc(sizeof(dt_film_t)); dt_film_init(cfr); dt_film_new(cfr, cdn); } /* import image */ dt_image_import(cfr->id, (const gchar *)image->data, FALSE); fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } while( (image = g_list_next(image)) != NULL); // only redraw at the end, to not spam the cpu with exposure events dt_control_queue_redraw_center(); dt_control_signal_raise(darktable.signals,DT_SIGNAL_TAG_CHANGED); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED,film->id); #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the just imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif }
void gui_init(dt_lib_module_t *self) { dt_lib_styles_t *d = (dt_lib_styles_t *)malloc(sizeof(dt_lib_styles_t)); self->data = (void *)d; d->edit_button = NULL; self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); GtkWidget *w; GtkWidget *scrolled; /* list */ d->list = GTK_TREE_VIEW(gtk_tree_view_new()); gtk_tree_view_set_headers_visible(d->list, FALSE); GtkListStore *liststore = gtk_list_store_new(DT_STYLES_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING); GtkTreeViewColumn *col = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(d->list), col); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", DT_STYLES_COL_NAME); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->list)), GTK_SELECTION_SINGLE); gtk_tree_view_set_model(GTK_TREE_VIEW(d->list), GTK_TREE_MODEL(liststore)); g_object_unref(liststore); g_object_set(G_OBJECT(d->list), "tooltip-text", _("available styles,\ndoubleclick to apply"), (char *)NULL); g_signal_connect(d->list, "row-activated", G_CALLBACK(_styles_row_activated_callback), d); /* filter entry */ w = gtk_entry_new(); d->entry = GTK_ENTRY(w); g_object_set(G_OBJECT(w), "tooltip-text", _("enter style name"), (char *)NULL); g_signal_connect(d->entry, "changed", G_CALLBACK(entry_callback), d); g_signal_connect(d->entry, "activate", G_CALLBACK(entry_activated), d); dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(d->entry)); scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), DT_PIXEL_APPLY_DPI(150)); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->entry), TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(scrolled), TRUE, FALSE, 0); gtk_container_add(GTK_CONTAINER(scrolled), GTK_WIDGET(d->list)); d->duplicate = gtk_check_button_new_with_label(_("create duplicate")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->duplicate), TRUE, FALSE, 0); g_signal_connect(d->duplicate, "toggled", G_CALLBACK(duplicate_callback), d); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->duplicate), dt_conf_get_bool("ui_last/styles_create_duplicate")); g_object_set(d->duplicate, "tooltip-text", _("creates a duplicate of the image before applying style"), (char *)NULL); GtkWidget *hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); GtkWidget *hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start(GTK_BOX(self->widget), hbox1, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(self->widget), hbox2, TRUE, FALSE, 0); // create GtkWidget *cbutton = gtk_button_new_with_label(_("create")); g_signal_connect(G_OBJECT(cbutton), "clicked", G_CALLBACK(create_clicked), d); g_object_set(G_OBJECT(cbutton), "tooltip-text", _("create styles from history stack of selected images"), (char *)NULL); gtk_box_pack_start(GTK_BOX(hbox1), cbutton, TRUE, TRUE, 0); // edit GtkWidget *widget = gtk_button_new_with_label(_("edit")); d->edit_button = widget; g_signal_connect(widget, "clicked", G_CALLBACK(edit_clicked), d); g_object_set(widget, "tooltip-text", _("edit the selected style in list above"), (char *)NULL); gtk_box_pack_start(GTK_BOX(hbox1), widget, TRUE, TRUE, 0); // delete widget = gtk_button_new_with_label(_("delete")); d->delete_button = widget; g_signal_connect(widget, "clicked", G_CALLBACK(delete_clicked), d); g_object_set(widget, "tooltip-text", _("deletes the selected style in list above"), (char *)NULL); gtk_box_pack_start(GTK_BOX(hbox1), widget, TRUE, TRUE, 0); // import button GtkWidget *importButton = gtk_button_new_with_label(C_("styles", "import")); d->import_button = importButton; g_object_set(importButton, "tooltip-text", _("import style from a style file"), (char *)NULL); g_signal_connect(importButton, "clicked", G_CALLBACK(import_clicked), d); gtk_box_pack_start(GTK_BOX(hbox2), importButton, TRUE, TRUE, 0); // export button GtkWidget *exportButton = gtk_button_new_with_label(_("export")); d->export_button = exportButton; g_object_set(exportButton, "tooltip-text", _("export the selected style into a style file"), (char *)NULL); g_signal_connect(exportButton, "clicked", G_CALLBACK(export_clicked), d); gtk_box_pack_start(GTK_BOX(hbox2), exportButton, TRUE, TRUE, 0); // add entry completion GtkEntryCompletion *completion = gtk_entry_completion_new(); gtk_entry_completion_set_model(completion, gtk_tree_view_get_model(GTK_TREE_VIEW(d->list))); gtk_entry_completion_set_text_column(completion, 0); gtk_entry_completion_set_inline_completion(completion, TRUE); gtk_entry_set_completion(d->entry, completion); /* update filtered list */ _gui_styles_update_view(d); }
void enter(dt_view_t *self) { dt_map_t *lib = (dt_map_t *)self->data; lib->map = g_object_new (OSM_TYPE_GPS_MAP, "map-source", OSM_GPS_MAP_SOURCE_OPENSTREETMAP, "proxy-uri",g_getenv("http_proxy"), NULL); if(dt_conf_get_bool("plugins/map/show_map_osd")) { OsmGpsMapLayer *osd = g_object_new (OSM_TYPE_GPS_MAP_OSD, "show-scale",TRUE, "show-coordinates",TRUE, "show-dpad",TRUE, "show-zoom",TRUE, NULL); osm_gps_map_layer_add(OSM_GPS_MAP(lib->map), osd); g_object_unref(G_OBJECT(osd)); } /* replace center widget */ GtkWidget *parent = gtk_widget_get_parent(dt_ui_center(darktable.gui->ui)); gtk_widget_hide(dt_ui_center(darktable.gui->ui)); gtk_box_pack_start(GTK_BOX(parent), GTK_WIDGET(lib->map) ,TRUE, TRUE, 0); gtk_box_reorder_child(GTK_BOX(parent), GTK_WIDGET(lib->map), 2); gtk_widget_show_all(GTK_WIDGET(lib->map)); /* setup proxy functions */ darktable.view_manager->proxy.map.view = self; darktable.view_manager->proxy.map.center_on_location = _view_map_center_on_location; /* setup collection listener and initialize main_query statement */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED, G_CALLBACK(_view_map_collection_changed), (gpointer) self); osm_gps_map_set_post_expose_callback(lib->map, _view_map_post_expose, lib); /* restore last zoom,location in map */ OsmGpsMapPoint *pt; float lon, lat; const float rlon = dt_conf_get_float("plugins/map/longitude"); const float rlat = dt_conf_get_float("plugins/map/latitude"); const int zoom = dt_conf_get_int("plugins/map/zoom"); pt = osm_gps_map_point_new_radians(rlat,rlon); osm_gps_map_point_get_degrees (pt, &lat, &lon); osm_gps_map_set_center_and_zoom(lib->map, lat, lon, zoom); osm_gps_map_point_free(pt); _view_map_collection_changed(NULL, self); /* connect signal for filmstrip image activate */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE, G_CALLBACK(_view_map_filmstrip_activate_callback), self); /* allow drag&drop of images from filmstrip */ gtk_drag_dest_set(GTK_WIDGET(lib->map), GTK_DEST_DEFAULT_ALL, target_list, n_targets, GDK_ACTION_COPY); g_signal_connect(GTK_WIDGET(lib->map), "drag-data-received", G_CALLBACK(drag_and_drop_received), self); }
static GtkWidget *_lib_import_get_extra_widget(dt_lib_import_metadata_t *data, gboolean import_folder) { // add extra lines to 'extra'. don't forget to destroy the widgets later. GtkWidget *expander = gtk_expander_new(_("import options")); gtk_expander_set_expanded(GTK_EXPANDER(expander), dt_conf_get_bool("ui_last/import_options_expanded")); GtkWidget *frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_widget_set_hexpand(frame, TRUE); GtkWidget *event_box = gtk_event_box_new(); gtk_widget_set_margin_start(event_box, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_end(event_box, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_top(event_box, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_bottom(event_box, DT_PIXEL_APPLY_DPI(8)); gtk_container_add(GTK_CONTAINER(frame), event_box); gtk_container_add(GTK_CONTAINER(event_box), expander); GtkWidget *extra; extra = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_container_add(GTK_CONTAINER(expander), extra); gtk_widget_set_margin_start(extra, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_end(extra, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_top(extra, DT_PIXEL_APPLY_DPI(8)); gtk_widget_set_margin_bottom(extra, DT_PIXEL_APPLY_DPI(8)); GtkWidget *recursive = NULL, *ignore_jpeg = NULL; if(import_folder == TRUE) { // recursive opening. recursive = gtk_check_button_new_with_label(_("import directories recursively")); g_object_set(recursive, "tooltip-text", _("recursively import subdirectories. each directory goes into a new film roll."), NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(recursive), dt_conf_get_bool("ui_last/import_recursive")); gtk_box_pack_start(GTK_BOX(extra), recursive, FALSE, FALSE, 0); // ignoring of jpegs. hack while we don't handle raw+jpeg in the same directories. ignore_jpeg = gtk_check_button_new_with_label(_("ignore JPEG files")); g_object_set(ignore_jpeg, "tooltip-text", _("do not load files with an extension of .jpg or .jpeg. this " "can be useful when there are raw+JPEG in a directory."), NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ignore_jpeg), dt_conf_get_bool("ui_last/import_ignore_jpegs")); gtk_box_pack_start(GTK_BOX(extra), ignore_jpeg, FALSE, FALSE, 0); } // default metadata GtkWidget *apply_metadata; GtkWidget *grid, *label, *creator, *publisher, *rights, *tags; apply_metadata = gtk_check_button_new_with_label(_("apply metadata on import")); g_object_set(apply_metadata, "tooltip-text", _("apply some metadata to all newly imported images."), NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(apply_metadata), dt_conf_get_bool("ui_last/import_apply_metadata")); gtk_box_pack_start(GTK_BOX(extra), apply_metadata, FALSE, FALSE, 0); GValue value = { 0, }; g_value_init(&value, G_TYPE_INT); gtk_widget_style_get_property(apply_metadata, "indicator-size", &value); gint indicator_size = g_value_get_int(&value); gtk_widget_style_get_property(apply_metadata, "indicator-spacing", &value); gint indicator_spacing = g_value_get_int(&value); g_value_unset(&value); grid = gtk_grid_new(); gtk_grid_set_row_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(5)); gtk_grid_set_column_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(10)); gtk_widget_set_margin_start(grid, 2 * (indicator_spacing + indicator_size)); gtk_box_pack_start(GTK_BOX(extra), grid, FALSE, FALSE, 0); creator = gtk_entry_new(); gtk_widget_set_size_request(creator, DT_PIXEL_APPLY_DPI(300), -1); gchar *str = dt_conf_get_string("ui_last/import_last_creator"); gtk_entry_set_text(GTK_ENTRY(creator), str); g_free(str); publisher = gtk_entry_new(); str = dt_conf_get_string("ui_last/import_last_publisher"); gtk_entry_set_text(GTK_ENTRY(publisher), str); g_free(str); rights = gtk_entry_new(); str = dt_conf_get_string("ui_last/import_last_rights"); gtk_entry_set_text(GTK_ENTRY(rights), str); g_free(str); tags = gtk_entry_new(); str = dt_conf_get_string("ui_last/import_last_tags"); g_object_set(tags, "tooltip-text", _("comma separated list of tags"), NULL); gtk_entry_set_text(GTK_ENTRY(tags), str); g_free(str); // presets from the metadata plugin GtkCellRenderer *renderer; GtkTreeIter iter; GtkListStore *model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING /*name*/, G_TYPE_STRING /*creator*/, G_TYPE_STRING /*publisher*/, G_TYPE_STRING /*rights*/); GtkWidget *presets = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(presets), renderer, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(presets), renderer, "text", NAME_COLUMN, NULL); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select name, op_params from presets where operation = \"metadata\"", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { void *op_params = (void *)sqlite3_column_blob(stmt, 1); int32_t op_params_size = sqlite3_column_bytes(stmt, 1); char *buf = (char *)op_params; char *title = buf; buf += strlen(title) + 1; char *description = buf; buf += strlen(description) + 1; char *rights = buf; buf += strlen(rights) + 1; char *creator = buf; buf += strlen(creator) + 1; char *publisher = buf; if(op_params_size == strlen(title) + strlen(description) + strlen(rights) + strlen(creator) + strlen(publisher) + 5) { gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, NAME_COLUMN, (char *)sqlite3_column_text(stmt, 0), CREATOR_COLUMN, creator, PUBLISHER_COLUMN, publisher, RIGHTS_COLUMN, rights, -1); } } sqlite3_finalize(stmt); g_object_unref(model); int line = 0; label = gtk_label_new(_("preset")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), label, 0, line++, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), presets, label, GTK_POS_RIGHT, 1, 1); label = gtk_label_new(_("creator")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), label, 0, line++, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), creator, label, GTK_POS_RIGHT, 1, 1); label = gtk_label_new(_("publisher")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), label, 0, line++, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), publisher, label, GTK_POS_RIGHT, 1, 1); label = gtk_label_new(_("rights")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), label, 0, line++, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), rights, label, GTK_POS_RIGHT, 1, 1); label = gtk_label_new(_("tags")); gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_grid_attach(GTK_GRID(grid), label, 0, line, 1, 1); gtk_grid_attach_next_to(GTK_GRID(grid), tags, label, GTK_POS_RIGHT, 1, 1); gtk_widget_show_all(frame); if(data != NULL) { data->frame = frame; data->recursive = recursive; data->ignore_jpeg = ignore_jpeg; data->expander = expander; data->apply_metadata = apply_metadata; data->presets = presets; data->creator = creator; data->publisher = publisher; data->rights = rights; data->tags = tags; } g_signal_connect(apply_metadata, "toggled", G_CALLBACK(_lib_import_apply_metadata_toggled), grid); // needed since the apply_metadata starts being turned off, and setting it to off doesn't emit the 'toggled' signal ... _lib_import_apply_metadata_toggled(apply_metadata, grid); g_signal_connect(presets, "changed", G_CALLBACK(_lib_import_presets_changed), data); g_signal_connect(GTK_ENTRY(creator), "changed", G_CALLBACK(_lib_import_metadata_changed), presets); g_signal_connect(GTK_ENTRY(publisher), "changed", G_CALLBACK(_lib_import_metadata_changed), presets); g_signal_connect(GTK_ENTRY(rights), "changed", G_CALLBACK(_lib_import_metadata_changed), presets); return frame; }
void _camera_import_dialog_new(_camera_import_dialog_t *data) { data->dialog=gtk_dialog_new_with_buttons(_("import images from camera"),NULL,GTK_DIALOG_MODAL,_("cancel"),GTK_RESPONSE_NONE,C_("camera import", "import"),GTK_RESPONSE_ACCEPT,NULL); GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (data->dialog)); // List - setup store data->store = gtk_list_store_new (2,GDK_TYPE_PIXBUF,G_TYPE_STRING); // Setup variables dt_variables_params_init(&data->vp); data->vp->jobcode=_("my jobcode"); data->vp->filename="DSC_0235.JPG"; // IMPORT PAGE data->import.page=gtk_vbox_new(FALSE,5); gtk_container_set_border_width(GTK_CONTAINER(data->import.page),5); // Top info data->import.info=gtk_label_new( _("please wait while prefetching thumbnails of images from camera...") ); gtk_label_set_single_line_mode( GTK_LABEL(data->import.info) , FALSE ); gtk_misc_set_alignment(GTK_MISC(data->import.info), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->import.page),data->import.info,FALSE,FALSE,0); // jobcode data->import.jobname=_camera_import_gconf_widget(data,_("jobcode"),"plugins/capture/camera/import/jobcode"); gtk_box_pack_start(GTK_BOX(data->import.page),GTK_WIDGET(data->import.jobname->widget),FALSE,FALSE,0); // Create the treview with list model data store data->import.treeview=gtk_scrolled_window_new(NULL,NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(data->import.treeview),GTK_POLICY_NEVER,GTK_POLICY_ALWAYS); gtk_container_add(GTK_CONTAINER(data->import.treeview), gtk_tree_view_new()); GtkTreeView *treeview=GTK_TREE_VIEW(gtk_bin_get_child(GTK_BIN(data->import.treeview))); GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new( ); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( _("thumbnail") , renderer,"pixbuf",0, (char *)NULL); gtk_tree_view_append_column( treeview , column); renderer = gtk_cell_renderer_text_new( ); column = gtk_tree_view_column_new_with_attributes( _("storage file"), renderer, "text", 1, (char *)NULL); gtk_tree_view_append_column( treeview , column); gtk_tree_view_column_set_expand( column, TRUE); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); gtk_tree_selection_set_mode(selection,GTK_SELECTION_MULTIPLE); gtk_tree_view_set_model(treeview,GTK_TREE_MODEL(data->store)); gtk_tree_view_set_headers_visible(treeview,FALSE); gtk_box_pack_start(GTK_BOX(data->import.page),data->import.treeview,TRUE,TRUE,0); // SETTINGS PAGE data->settings.page=gtk_vbox_new(FALSE,5); gtk_container_set_border_width(GTK_CONTAINER(data->settings.page),5); // general settings gtk_box_pack_start(GTK_BOX(data->settings.page),dtgtk_label_new(_("general"),DARKTABLE_LABEL_TAB|DARKTABLE_LABEL_ALIGN_RIGHT),FALSE,FALSE,0); // ignoring of jpegs. hack while we don't handle raw+jpeg in the same directories. data->settings.general.ignore_jpeg = gtk_check_button_new_with_label (_("ignore JPEG files")); g_object_set(data->settings.general.ignore_jpeg, "tooltip-text", _("do not load files with an extension of .jpg or .jpeg. this can be useful when there are raw+JPEG in a directory."), NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->settings.general.ignore_jpeg), dt_conf_get_bool("ui_last/import_ignore_jpegs")); gtk_box_pack_start(GTK_BOX(data->settings.page), data->settings.general.ignore_jpeg, FALSE, FALSE, 0); g_signal_connect (G_OBJECT(data->settings.general.ignore_jpeg), "clicked",G_CALLBACK (_check_button_callback),data); GtkWidget *hbox=gtk_hbox_new(FALSE,5); data->settings.general.date_override=gtk_check_button_new_with_label(_("override today's date")); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_override,FALSE,FALSE,0); g_object_set(data->settings.general.date_override,"tooltip-text",_("check this, if you want to override the timestamp used when expanding variables:\n$(YEAR), $(MONTH), $(DAY),\n$(HOUR), $(MINUTE), $(SECONDS)"),(char *)NULL); data->settings.general.date_entry=gtk_entry_new(); gtk_widget_set_sensitive( data->settings.general.date_entry, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->settings.general.date_override))); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_entry,TRUE,TRUE,0); g_signal_connect (G_OBJECT (data->settings.general.date_override), "clicked",G_CALLBACK (_check_button_callback),data); gtk_box_pack_start(GTK_BOX(data->settings.page),hbox,FALSE,FALSE,0); // Storage structure gtk_box_pack_start(GTK_BOX(data->settings.page),dtgtk_label_new(_("storage structure"),DARKTABLE_LABEL_TAB|DARKTABLE_LABEL_ALIGN_RIGHT),FALSE,FALSE,0); GtkWidget *l=gtk_label_new(_("the following three settings describe the directory structure and file renaming for import storage and images; if you don't know how to use this, keep the default settings.")); gtk_label_set_line_wrap(GTK_LABEL(l),TRUE); gtk_widget_set_size_request(l,400,-1); gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->settings.page),l,FALSE,FALSE,0); data->settings.basedirectory=_camera_import_gconf_widget(data,_("storage directory"),"plugins/capture/storage/basedirectory"); gtk_box_pack_start(GTK_BOX(data->settings.page),GTK_WIDGET(data->settings.basedirectory->widget),FALSE,FALSE,0); data->settings.subdirectory=_camera_import_gconf_widget(data,_("directory structure"),"plugins/capture/storage/subpath"); gtk_box_pack_start(GTK_BOX(data->settings.page),GTK_WIDGET(data->settings.subdirectory->widget),FALSE,FALSE,0); data->settings.namepattern=_camera_import_gconf_widget(data,_("filename structure"),"plugins/capture/storage/namepattern"); gtk_box_pack_start(GTK_BOX(data->settings.page),GTK_WIDGET(data->settings.namepattern->widget),FALSE,FALSE,0); // Add example l=gtk_label_new(_("above settings expands to:")); gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->settings.page),l,FALSE,FALSE,0); data->settings.example=gtk_label_new(""); gtk_label_set_line_wrap(GTK_LABEL(data->settings.example),TRUE); gtk_widget_set_size_request(data->settings.example,400,-1); gtk_misc_set_alignment(GTK_MISC(data->settings.example), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->settings.page),data->settings.example,FALSE,FALSE,0); // External backup gtk_box_pack_start(GTK_BOX(data->settings.page),dtgtk_label_new(_("external backup"),DARKTABLE_LABEL_TAB|DARKTABLE_LABEL_ALIGN_RIGHT),FALSE,FALSE,0); l=gtk_label_new(_("external backup is an option to automatic do a backup of the imported image(s) to another physical location, when activated it does looks for specified backup foldername of mounted devices on your system... each found folder is used as basedirectory in the above storage structure and when a image are downloaded from camera it is replicated to found backup destinations.")); gtk_label_set_line_wrap(GTK_LABEL(l),TRUE); gtk_widget_set_size_request(l,400,-1); gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.0); gtk_box_pack_start(GTK_BOX(data->settings.page),l,FALSE,FALSE,0); data->settings.backup.enable=gtk_check_button_new_with_label(_("enable backup")); gtk_box_pack_start(GTK_BOX(data->settings.page),data->settings.backup.enable,FALSE,FALSE,0); g_object_set(data->settings.backup.enable,"tooltip-text",_("check this option to enable automatic backup of imported images"),(char *)NULL); data->settings.backup.warn=gtk_check_button_new_with_label(_("warn if no backup destinations are present")); gtk_box_pack_start(GTK_BOX(data->settings.page),data->settings.backup.warn,FALSE,FALSE,0); g_object_set(data->settings.backup.warn,"tooltip-text",_("check this option to get an interactive warning if no backupdestinations are present"),(char *)NULL); data->settings.backup.foldername=(_camera_import_gconf_widget(data,_("backup foldername"),"plugins/capture/camera/backup/foldername"))->widget; gtk_box_pack_start(GTK_BOX(data->settings.page),data->settings.backup.foldername,FALSE,FALSE,0); g_object_set(data->settings.backup.foldername,"tooltip-text",_("this is the name of folder that indicates a backup destination,\nif such a folder is found in any mounter media it is used as a backup destination."),(char *)NULL); if( dt_conf_get_bool("plugins/capture/backup/enable") ) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( data->settings.backup.enable ), TRUE); else { gtk_widget_set_sensitive( data->settings.backup.warn, FALSE); gtk_widget_set_sensitive( data->settings.backup.foldername, FALSE); } if( dt_conf_get_bool("plugins/capture/backup/warn") ) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( data->settings.backup.warn ), TRUE); g_signal_connect (G_OBJECT (data->settings.backup.enable), "clicked",G_CALLBACK (_check_button_callback),data); g_signal_connect (G_OBJECT(data->settings.backup.warn), "clicked",G_CALLBACK (_check_button_callback),data); // THE NOTEBOOK data->notebook=gtk_notebook_new(); gtk_notebook_append_page(GTK_NOTEBOOK(data->notebook),data->import.page,gtk_label_new(_("images"))); gtk_notebook_append_page(GTK_NOTEBOOK(data->notebook),data->settings.page,gtk_label_new(_("settings"))); // end gtk_box_pack_start(GTK_BOX(content),data->notebook,TRUE,TRUE,0); //gtk_widget_set_size_request(content,400,400); _update_example(data); }