int dt_init(int argc, char *argv[], const int init_gui) { // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #ifndef __APPLE__ _dt_sigsegv_old_handler = signal(SIGSEGV,&_dt_sigsegv_handler); #endif #ifndef __SSE2__ fprintf(stderr, "[dt_init] unfortunately we depend on SSE2 instructions at this time.\n"); fprintf(stderr, "[dt_init] please contribute a backport patch (or buy a newer processor).\n"); return 1; #endif #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD,128*1024) ; /* use mmap() for large allocations */ #endif 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; 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-2013 johannes hanika\n"PACKAGE_BUGREPORT"\n"); 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], "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 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 ++; } } #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[DT_MAX_PATH_LEN]; char datadir[DT_MAX_PATH_LEN]; dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN); snprintf(geglpath, DT_MAX_PATH_LEN, "%s/gegl:/usr/lib/gegl-0.0", datadir); (void)setenv("GEGL_PATH", geglpath, 1); gegl_init(&argc, &argv); #endif // thread-safe init: dt_exif_init(); char datadir[DT_MAX_PATH_LEN]; dt_loc_get_user_config_dir (datadir,DT_MAX_PATH_LEN); char filename[DT_MAX_PATH_LEN]; snprintf(filename, DT_MAX_PATH_LEN, "%s/darktablerc", datadir); // intialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)malloc(sizeof(dt_conf_t)); memset(darktable.conf, 0, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, filename); // set the interface language const gchar* lang = dt_conf_get_string("ui_last/gui_language"); 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_already_locked(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(); // 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 *)malloc(sizeof(dt_control_t)); memset(darktable.control, 0, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else { // this is in memory, so schema can't exist yet. if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) { dt_control_create_database_schema(); dt_gui_presets_init(); // also 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 sellection */ 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 *)malloc(sizeof(dt_opencl_t)); memset(darktable.opencl, 0, sizeof(dt_opencl_t)); dt_opencl_init(darktable.opencl, argc, argv); darktable.blendop = (dt_blendop_t *)malloc(sizeof(dt_blendop_t)); memset(darktable.blendop, 0, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)malloc(sizeof(dt_points_t)); memset(darktable.points, 0, 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 *)malloc(sizeof(dt_image_cache_t)); memset(darktable.image_cache, 0, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)malloc(sizeof(dt_mipmap_cache_t)); memset(darktable.mipmap_cache, 0, 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 *)malloc(sizeof(dt_gui_gtk_t)); memset(darktable.gui,0,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 *)malloc(sizeof(dt_view_manager_t)); memset(darktable.view_manager, 0, 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 *)malloc(sizeof(dt_lib_t)); memset(darktable.lib, 0, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); g_strlcpy(darktable.control->global_settings.dbname, filename, 512); // overwrite if relocated. } darktable.imageio = (dt_imageio_t *)malloc(sizeof(dt_imageio_t)); memset(darktable.imageio, 0, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); if(init_gui) { // Loading the keybindings char keyfile[DT_MAX_PATH_LEN]; // First dump the default keymapping snprintf(keyfile, DT_MAX_PATH_LEN, "%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, DT_MAX_PATH_LEN, "%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); } /* start the indexer background job */ dt_control_start_indexer(); if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } return 0; }
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; }