static void _lib_recentcollection_updated(gpointer instance, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_recentcollect_t *d = (dt_lib_recentcollect_t *)self->data; // serialize, check for recently used char confname[200]; const int bufsize = 4096; char buf[bufsize]; if(dt_collection_serialize(buf, bufsize)) return; // is the current position, i.e. the one to be stored with the old collection (pos0, pos1-to-be) uint32_t curr_pos = dt_view_lighttable_get_position(darktable.view_manager); uint32_t new_pos = -1; if(!d->inited) { new_pos = dt_conf_get_int("plugins/lighttable/recentcollect/pos0"); d->inited = 1; dt_view_lighttable_set_position(darktable.view_manager, new_pos); } else if(curr_pos != -1) { dt_conf_set_int("plugins/lighttable/recentcollect/pos0", curr_pos); } int n = -1; for(int k = 0; k < CLAMPS(dt_conf_get_int("plugins/lighttable/recentcollect/num_items"), 0, NUM_LINES); k++) { // is it already in the current list? snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", k); gchar *line = dt_conf_get_string(confname); if(!line) continue; if(!strcmp(line, buf)) { snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/pos%1d", k); new_pos = dt_conf_get_int(confname); n = k; break; } g_free(line); } if(n < 0) { const int num_items = CLAMPS(dt_conf_get_int("plugins/lighttable/recentcollect/num_items"), 0, NUM_LINES); if(num_items < NUM_LINES) { // new, unused entry n = num_items; dt_conf_set_int("plugins/lighttable/recentcollect/num_items", num_items + 1); } else { // kill least recently used entry: n = num_items - 1; } } if(n >= 0 && n < NUM_LINES) { // sort n to the top for(int k = n; k > 0; k--) { snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", k - 1); gchar *line1 = dt_conf_get_string(confname); snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/pos%1d", k - 1); uint32_t pos1 = dt_conf_get_int(confname); if(line1 && line1[0] != '\0') { snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", k); dt_conf_set_string(confname, line1); snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/pos%1d", k); dt_conf_set_int(confname, pos1); } g_free(line1); } dt_conf_set_string("plugins/lighttable/recentcollect/line0", buf); dt_conf_set_int("plugins/lighttable/recentcollect/pos0", (new_pos != -1 ? new_pos : (curr_pos != -1 ? curr_pos : 0))); } // update button descriptions: for(int k = 0; k < NUM_LINES; k++) { char str[2048] = { 0 }; char str_cut[200] = { 0 }; char str_pretty[200] = { 0 }; snprintf(confname, sizeof(confname), "plugins/lighttable/recentcollect/line%1d", k); gchar *line2 = dt_conf_get_string(confname); if(line2 && line2[0] != '\0') pretty_print(line2, str, sizeof(str)); g_free(line2); g_object_set(G_OBJECT(d->item[k].button), "tooltip-text", str, (char *)NULL); const int cut = 45; if(g_utf8_validate(str, -1, NULL)) { if(g_utf8_strlen(str, -1) > cut) { g_utf8_strncpy(str_cut, str, cut); snprintf(str_pretty, sizeof(str_pretty), "%s...", str_cut); gtk_button_set_label(GTK_BUTTON(d->item[k].button), str_pretty); } else { gtk_button_set_label(GTK_BUTTON(d->item[k].button), str); } } else if(strlen(str) > cut) { g_strlcpy(str_cut, str, cut); snprintf(str_pretty, sizeof(str_pretty), "%s...", str_cut); gtk_button_set_label(GTK_BUTTON(d->item[k].button), str_pretty); } else { gtk_button_set_label(GTK_BUTTON(d->item[k].button), str); } GtkWidget *child = gtk_bin_get_child(GTK_BIN(d->item[k].button)); if(child) gtk_widget_set_halign(child, GTK_ALIGN_START); gtk_widget_set_no_show_all(d->item[k].button, TRUE); gtk_widget_set_visible(d->item[k].button, FALSE); } for(int k = 0; k < CLAMPS(dt_conf_get_int("plugins/lighttable/recentcollect/num_items"), 0, NUM_LINES); k++) { gtk_widget_set_no_show_all(d->item[k].button, FALSE); gtk_widget_set_visible(d->item[k].button, TRUE); } if((new_pos != -1) && (new_pos != curr_pos)) dt_view_lighttable_set_position(darktable.view_manager, new_pos); }
int32_t dt_control_export_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; const int total = g_list_length(t); int size = 0; dt_imageio_module_format_t *mformat = dt_imageio_get_format(); g_assert(mformat); dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage(); g_assert(mstorage); // Get max dimensions... uint32_t w,h,fw,fh,sw,sh; fw=fh=sw=sh=0; mstorage->dimension(mstorage, &sw,&sh); mformat->dimension(mformat, &fw,&fh); if( sw==0 || fw==0) w=sw>fw?sw:fw; else w=sw<fw?sw:fw; if( sh==0 || fh==0) h=sh>fh?sh:fh; else h=sh<fh?sh:fh; // get shared storage param struct (global sequence counter, one picasa connection etc) dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage, &size); if(sdata == NULL) { dt_control_log(_("failed to get parameters from storage module, aborting export..")); return 1; } dt_control_log(ngettext ("exporting %d image..", "exporting %d images..", total), total); char message[512]= {0}; snprintf(message, 512, ngettext ("exporting %d image to %s", "exporting %d images to %s", total), total, mstorage->name() ); /* create a cancellable bgjob ui template */ const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message ); dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job); const dt_control_t *control = darktable.control; double fraction=0; #ifdef _OPENMP // limit this to num threads = num full buffers - 1 (keep one for darkroom mode) // use min of user request and mipmap cache entries const int full_entries = dt_conf_get_int ("parallel_export"); // GCC won't accept that this variable is used in a macro, considers // it set but not used, which makes for instance Fedora break. const __attribute__((__unused__)) int num_threads = MAX(1, MIN(full_entries, 8)); #if !defined(__SUNOS__) #pragma omp parallel default(none) private(imgid, size) shared(control, fraction, w, h, stderr, mformat, mstorage, t, sdata, job, jid, darktable) num_threads(num_threads) if(num_threads > 1) #else #pragma omp parallel private(imgid, size) shared(control, fraction, w, h, mformat, mstorage, t, sdata, job, jid, darktable) num_threads(num_threads) if(num_threads > 1) #endif { #endif // get a thread-safe fdata struct (one jpeg struct per thread etc): dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &size); fdata->max_width = dt_conf_get_int ("plugins/lighttable/export/width"); fdata->max_height = dt_conf_get_int ("plugins/lighttable/export/height"); fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width; fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height; int num = 0; // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a sensible assumption? guint tagid = 0; dt_tag_new("darktable|changed",&tagid); while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED) { #ifdef _OPENMP #pragma omp critical #endif { if(!t) imgid = 0; else { imgid = (long int)t->data; t = g_list_delete_link(t, t); num = total - g_list_length(t); } } // remove 'changed' tag from image dt_tag_detach(tagid, imgid); // check if image still exists: char imgfilename[DT_MAX_PATH_LEN]; const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid); if(image) { dt_image_full_path(image->id, imgfilename, DT_MAX_PATH_LEN); if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR)) { dt_control_log(_("image `%s' is currently unavailable"), image->filename); fprintf(stderr, _("image `%s' is currently unavailable"), imgfilename); // dt_image_remove(imgid); dt_image_cache_read_release(darktable.image_cache, image); } else { dt_image_cache_read_release(darktable.image_cache, image); mstorage->store(sdata, imgid, mformat, fdata, num, total); } } #ifdef _OPENMP #pragma omp critical #endif { fraction+=1.0/total; dt_control_backgroundjobs_progress(control, jid, fraction); } } #ifdef _OPENMP #pragma omp barrier #pragma omp master #endif { dt_control_backgroundjobs_destroy(control, jid); if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata); mstorage->free_params(mstorage, sdata); } // all threads free their fdata mformat->free_params (mformat, fdata); #ifdef _OPENMP } #endif return 0; }
int write_image (dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void, void *exif, int exif_len, int imgid) { dt_imageio_tiff_t *d=(dt_imageio_tiff_t*)d_tmp; uint8_t* profile = NULL; uint32_t profile_len = 0; TIFF* tif = NULL; void* rowdata = NULL; uint32_t rowsize = 0; uint32_t stripesize = 0; uint32_t stripe = 0; int rc = 1; // default to error if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_create_output_profile(imgid); cmsSaveProfileToMem(out_profile, 0, &profile_len); if (profile_len > 0) { profile = malloc(profile_len); if (!profile) { rc = 1; goto exit; } cmsSaveProfileToMem(out_profile, profile, &profile_len); } dt_colorspaces_cleanup_profile(out_profile); } // Create little endian tiff image tif = TIFFOpen(filename,"wl"); if (!tif) { rc = 1; goto exit; } // http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf (dated 2002) // "A proprietary ZIP/Flate compression code (0x80b2) has been used by some" // "software vendors. This code should be considered obsolete. We recommend" // "that TIFF implentations recognize and read the obsolete code but only" // "write the official compression code (0x0008)." // http://www.awaresystems.be/imaging/tiff/tifftags/compression.html // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html if (d->compress == 1) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)1); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9); } else if (d->compress == 2) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)2); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9); } else if (d->compress == 3) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); if (d->bpp == 32) TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)3); else TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)2); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9); } else // (d->compress == 0) { TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); } TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16_t)FILLORDER_MSB2LSB); if (profile != NULL) { TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32_t)profile_len, profile); } TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16_t)3); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp); TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (uint16_t)(d->bpp == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT)); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)d->width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)d->height); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16_t)PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, (uint16_t)PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32_t)DT_TIFFIO_STRIPE); TIFFSetField(tif, TIFFTAG_ORIENTATION, (uint16_t)ORIENTATION_TOPLEFT); int resolution = dt_conf_get_int("metadata/resolution"); if (resolution > 0) { TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16_t)RESUNIT_INCH); } rowsize = (d->width*3) * d->bpp / 8; stripesize = rowsize * DT_TIFFIO_STRIPE; stripe = 0; rowdata = malloc(stripesize); if (!rowdata) { rc = 1; goto exit; } if (d->bpp == 32) { float* wdata = rowdata; for (int y = 0; y < d->height; y++) { float* in = (float*)in_void + (size_t)4*y*d->width; for (int x = 0; x < d->width; x++, in+=4, wdata+=3) { memcpy(wdata, in, 3*sizeof(float)); } if ((uintptr_t)wdata - (uintptr_t)rowdata == (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe++, rowdata, (size_t)(rowsize * DT_TIFFIO_STRIPE)); wdata = rowdata; } } if ((uintptr_t)wdata - (uintptr_t)rowdata != (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe++, rowdata, (size_t)((uintptr_t)wdata - (uintptr_t)rowdata)); } } else if (d->bpp == 16) { uint16_t* wdata = rowdata; for (int y = 0; y < d->height; y++) { uint16_t* in = (uint16_t*)in_void + (size_t)4*y*d->width; for(int x = 0; x < d->width; x++, in+=4, wdata+=3) { memcpy(wdata, in, 3*sizeof(uint16_t)); } if((uintptr_t)wdata - (uintptr_t)rowdata == (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe++, rowdata, (size_t)(rowsize * DT_TIFFIO_STRIPE)); wdata = rowdata; } } if ((uintptr_t)wdata - (uintptr_t)rowdata != (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe, rowdata, (size_t)((uintptr_t)wdata - (uintptr_t)rowdata)); } } else { uint8_t* wdata = rowdata; for (int y = 0; y < d->height; y++) { uint8_t* in = (uint8_t*)in_void + (size_t)4*y*d->width; for(int x = 0; x < d->width; x++, in+=4, wdata+=3) { memcpy(wdata, in, 3*sizeof(uint8_t)); } if((uintptr_t)wdata - (uintptr_t)rowdata == (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe++, rowdata, (size_t)(rowsize * DT_TIFFIO_STRIPE)); wdata = rowdata; } } if((uintptr_t)wdata - (uintptr_t)rowdata != (uintptr_t)stripesize) { TIFFWriteEncodedStrip(tif, stripe, rowdata, (size_t)((uintptr_t)wdata - (uintptr_t)rowdata)); } } // success rc = 0; exit: // close the file before adding exif data if (tif) { TIFFClose(tif); tif = NULL; } if(!rc && exif) { rc = dt_exif_write_blob(exif,exif_len,filename); // Until we get symbolic error status codes, if rc is 1, return 0 rc = (rc == 1) ? 0 : 1; } free(profile); profile = NULL; free(rowdata); rowdata = NULL; return rc; }
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); }
void dt_collection_update_query(const dt_collection_t *collection) { char query[1024], confname[200]; gchar *complete_query = NULL; const int _n_r = dt_conf_get_int("plugins/lighttable/collect/num_rules"); const int num_rules = CLAMP(_n_r, 1, 10); char *conj[] = {"and", "or", "and not"}; complete_query = dt_util_dstrcat(complete_query, "("); for(int i=0; i<num_rules; i++) { snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i); const int property = dt_conf_get_int(confname); snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i); gchar *text = dt_conf_get_string(confname); if(!text) break; snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i); const int mode = dt_conf_get_int(confname); gchar *escaped_text = dt_util_str_replace(text, "'", "''"); get_query_string(property, escaped_text, query); if(i > 0) complete_query = dt_util_dstrcat(complete_query, " %s %s", conj[mode], query); else complete_query = dt_util_dstrcat(complete_query, "%s", query); g_free(escaped_text); g_free(text); } complete_query = dt_util_dstrcat(complete_query, ")"); // printf("complete query: `%s'\n", complete_query); /* set the extended where and the use of it in the query */ dt_collection_set_extended_where (collection, complete_query); dt_collection_set_query_flags (collection, (dt_collection_get_query_flags (collection) | COLLECTION_QUERY_USE_WHERE_EXT)); /* remove film id from default filter */ dt_collection_set_filter_flags (collection, (dt_collection_get_filter_flags (collection) & ~COLLECTION_FILTER_FILM_ID)); /* update query and at last the visual */ dt_collection_update (collection); /* free string */ g_free(complete_query); // remove from selected images where not in this query. sqlite3_stmt *stmt = NULL; const gchar *cquery = dt_collection_get_query(collection); complete_query = NULL; if(cquery && cquery[0] != '\0') { complete_query = dt_util_dstrcat(complete_query, "delete from selected_images where imgid not in (%s)", cquery); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), complete_query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1); sqlite3_step(stmt); sqlite3_finalize(stmt); /* free allocated strings */ g_free(complete_query); } /* raise signal of collection change, only if this is an original */ if (!collection->clone) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); }
void dt_mipmap_cache_init(dt_mipmap_cache_t *cache) { // make sure static memory is initialized struct dt_mipmap_buffer_dsc *dsc = (struct dt_mipmap_buffer_dsc *)dt_mipmap_cache_static_dead_image; dead_image_f((dt_mipmap_buffer_t *)(dsc+1)); cache->compression_type = 0; gchar *compression = dt_conf_get_string("cache_compression"); if(compression) { if(!strcmp(compression, "low quality (fast)")) cache->compression_type = 1; else if(!strcmp(compression, "high quality (slow)")) cache->compression_type = 2; g_free(compression); } dt_print(DT_DEBUG_CACHE, "[mipmap_cache_init] using %s\n", cache->compression_type == 0 ? "no compression" : (cache->compression_type == 1 ? "low quality compression" : "slow high quality compression")); // adjust numbers to be large enough to hold what mem limit suggests. // we want at least 100MB, and consider 2G just still reasonable. uint32_t max_mem = CLAMPS(dt_conf_get_int("cache_memory"), 100u<<20, 2u<<30); const uint32_t parallel = CLAMP(dt_conf_get_int ("worker_threads")*dt_conf_get_int("parallel_export"), 1, 8); const int32_t max_size = 2048, min_size = 32; int32_t wd = darktable.thumbnail_width; int32_t ht = darktable.thumbnail_height; wd = CLAMPS(wd, min_size, max_size); ht = CLAMPS(ht, min_size, max_size); // round up to a multiple of 8, so we can divide by two 3 times if(wd & 0xf) wd = (wd & ~0xf) + 0x10; if(ht & 0xf) ht = (ht & ~0xf) + 0x10; // cache these, can't change at runtime: cache->mip[DT_MIPMAP_F].max_width = wd; cache->mip[DT_MIPMAP_F].max_height = ht; cache->mip[DT_MIPMAP_F-1].max_width = wd; cache->mip[DT_MIPMAP_F-1].max_height = ht; for(int k=DT_MIPMAP_F-2; k>=DT_MIPMAP_0; k--) { cache->mip[k].max_width = cache->mip[k+1].max_width / 2; cache->mip[k].max_height = cache->mip[k+1].max_height / 2; } // initialize some per-thread cached scratchmem for uncompressed buffers during thumb creation: if(cache->compression_type) { cache->scratchmem.max_width = wd; cache->scratchmem.max_height = ht; cache->scratchmem.buffer_size = wd*ht*sizeof(uint32_t); cache->scratchmem.size = DT_MIPMAP_3; // at max. // TODO: use thread local storage instead (zero performance penalty on linux) dt_cache_init(&cache->scratchmem.cache, parallel, parallel, 64, 0.9f*parallel*wd*ht*sizeof(uint32_t)); // might have been rounded to power of two: const int cnt = dt_cache_capacity(&cache->scratchmem.cache); cache->scratchmem.buf = dt_alloc_align(64, cnt * wd*ht*sizeof(uint32_t)); dt_cache_static_allocation(&cache->scratchmem.cache, (uint8_t *)cache->scratchmem.buf, wd*ht*sizeof(uint32_t)); dt_cache_set_allocate_callback(&cache->scratchmem.cache, scratchmem_allocate, &cache->scratchmem); dt_print(DT_DEBUG_CACHE, "[mipmap_cache_init] cache has % 5d entries for temporary compression buffers (% 4.02f MB).\n", cnt, cnt* wd*ht*sizeof(uint32_t)/(1024.0*1024.0)); } for(int k=DT_MIPMAP_3; k>=0; k--) { // buffer stores width and height + actual data const int width = cache->mip[k].max_width; const int height = cache->mip[k].max_height; // header + adjusted for dxt compression: cache->mip[k].buffer_size = 4*sizeof(uint32_t) + compressed_buffer_size(cache->compression_type, width, height); cache->mip[k].size = k; // level of parallelism also gives minimum size (which is twice that) // is rounded to a power of two by the cache anyways, we might as well. const uint32_t max_mem2 = MAX(0, (k == 0) ? (max_mem) : (max_mem/(k+4))); uint32_t thumbnails = MAX(2, nearest_power_of_two((uint32_t)((float)max_mem2/cache->mip[k].buffer_size))); while(thumbnails > parallel && thumbnails * cache->mip[k].buffer_size > max_mem2) thumbnails /= 2; // try to utilize that memory well (use 90% quota), the hopscotch paper claims good scalability up to // even more than that. dt_cache_init(&cache->mip[k].cache, thumbnails, parallel, 64, 0.9f*thumbnails*cache->mip[k].buffer_size); // might have been rounded to power of two: thumbnails = dt_cache_capacity(&cache->mip[k].cache); max_mem -= thumbnails * cache->mip[k].buffer_size; // dt_print(DT_DEBUG_CACHE, "[mipmap mem] %4.02f left\n", max_mem/(1024.0*1024.0)); cache->mip[k].buf = dt_alloc_align(64, thumbnails * cache->mip[k].buffer_size); dt_cache_static_allocation(&cache->mip[k].cache, (uint8_t *)cache->mip[k].buf, cache->mip[k].buffer_size); dt_cache_set_allocate_callback(&cache->mip[k].cache, dt_mipmap_cache_allocate, &cache->mip[k]); // dt_cache_set_cleanup_callback(&cache->mip[k].cache, // &dt_mipmap_cache_deallocate, &cache->mip[k]); dt_print(DT_DEBUG_CACHE, "[mipmap_cache_init] cache has % 5d entries for mip %d (% 4.02f MB).\n", thumbnails, k, thumbnails * cache->mip[k].buffer_size/(1024.0*1024.0)); } // full buffer needs dynamic alloc: const int full_entries = MAX(2, parallel); // even with one thread you want two buffers. one for dr one for thumbs. int32_t max_mem_bufs = nearest_power_of_two(full_entries); // for this buffer, because it can be very busy during import, we want the minimum // number of entries in the hashtable to be 16, but leave the quota as is. the dynamic // alloc/free properties of this cache take care that no more memory is required. dt_cache_init(&cache->mip[DT_MIPMAP_FULL].cache, max_mem_bufs, parallel, 64, max_mem_bufs); dt_cache_set_allocate_callback(&cache->mip[DT_MIPMAP_FULL].cache, dt_mipmap_cache_allocate_dynamic, &cache->mip[DT_MIPMAP_FULL]); // dt_cache_set_cleanup_callback(&cache->mip[DT_MIPMAP_FULL].cache, // &dt_mipmap_cache_deallocate_dynamic, &cache->mip[DT_MIPMAP_FULL]); cache->mip[DT_MIPMAP_FULL].buffer_size = 0; cache->mip[DT_MIPMAP_FULL].size = DT_MIPMAP_FULL; cache->mip[DT_MIPMAP_FULL].buf = NULL; // same for mipf: dt_cache_init(&cache->mip[DT_MIPMAP_F].cache, max_mem_bufs, parallel, 64, max_mem_bufs); dt_cache_set_allocate_callback(&cache->mip[DT_MIPMAP_F].cache, dt_mipmap_cache_allocate_dynamic, &cache->mip[DT_MIPMAP_F]); dt_cache_set_cleanup_callback(&cache->mip[DT_MIPMAP_F].cache, dt_mipmap_cache_deallocate_dynamic, &cache->mip[DT_MIPMAP_F]); cache->mip[DT_MIPMAP_F].buffer_size = 4*sizeof(uint32_t) + 4*sizeof(float) * cache->mip[DT_MIPMAP_F].max_width * cache->mip[DT_MIPMAP_F].max_height; cache->mip[DT_MIPMAP_F].size = DT_MIPMAP_F; cache->mip[DT_MIPMAP_F].buf = NULL; dt_mipmap_cache_deserialize(cache); }
static gboolean _lib_tagging_tag_show(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, dt_lib_module_t *self) { int mouse_over_id = -1; int zoom = dt_conf_get_int("plugins/lighttable/images_in_row"); // the order is: // if(zoom == 1) => currently shown image // else if(selection not empty) => selected images // else if(cursor over image) => hovered image // else => return if(zoom == 1 || dt_collection_get_selected_count(darktable.collection) == 0) { mouse_over_id = dt_control_get_mouse_over_id(); if(mouse_over_id < 0) return TRUE; } dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; d->floating_tag_imgid = mouse_over_id; gint x, y; gint px, py, w, h; GtkWidget *window = dt_ui_main_window(darktable.gui->ui); GtkWidget *center = dt_ui_center(darktable.gui->ui); gdk_window_get_origin(gtk_widget_get_window(center), &px, &py); w = gdk_window_get_width(gtk_widget_get_window(center)); h = gdk_window_get_height(gtk_widget_get_window(center)); x = px + 0.5 * (w - FLOATING_ENTRY_WIDTH); y = py + h - 50; /* put the floating box at the mouse pointer */ // gint pointerx, pointery; // GdkDevice *device = // gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(widget))); // gdk_window_get_device_position (gtk_widget_get_window (widget), device, &pointerx, &pointery, NULL); // x = px + pointerx + 1; // y = py + pointery + 1; d->floating_tag_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* stackoverflow.com/questions/1925568/how-to-give-keyboard-focus-to-a-pop-up-gtk-window */ gtk_widget_set_can_focus(d->floating_tag_window, TRUE); gtk_window_set_decorated(GTK_WINDOW(d->floating_tag_window), FALSE); gtk_window_set_type_hint(GTK_WINDOW(d->floating_tag_window), GDK_WINDOW_TYPE_HINT_POPUP_MENU); gtk_window_set_transient_for(GTK_WINDOW(d->floating_tag_window), GTK_WINDOW(window)); gtk_widget_set_opacity(d->floating_tag_window, 0.8); gtk_window_move(GTK_WINDOW(d->floating_tag_window), x, y); GtkWidget *entry = gtk_entry_new(); gtk_widget_set_size_request(entry, FLOATING_ENTRY_WIDTH, -1); gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK); GtkEntryCompletion *completion = gtk_entry_completion_new(); gtk_entry_completion_set_model(completion, gtk_tree_view_get_model(GTK_TREE_VIEW(d->related))); gtk_entry_completion_set_text_column(completion, 0); gtk_entry_completion_set_inline_completion(completion, TRUE); gtk_entry_completion_set_popup_set_width(completion, FALSE); gtk_entry_completion_set_match_func(completion, _completion_match_func, NULL, NULL); gtk_entry_set_completion(GTK_ENTRY(entry), completion); gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); gtk_container_add(GTK_CONTAINER(d->floating_tag_window), entry); g_signal_connect(entry, "focus-out-event", G_CALLBACK(_lib_tagging_tag_destroy), d->floating_tag_window); g_signal_connect(entry, "key-press-event", G_CALLBACK(_lib_tagging_tag_key_press), self); gtk_widget_show_all(d->floating_tag_window); gtk_widget_grab_focus(entry); gtk_window_present(GTK_WINDOW(d->floating_tag_window)); return TRUE; }
int write_image(dt_imageio_module_data_t *jpg_tmp, const char *filename, const void *in_tmp, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe) { dt_imageio_jpeg_t *jpg = (dt_imageio_jpeg_t *)jpg_tmp; const uint8_t *in = (const uint8_t *)in_tmp; struct dt_imageio_jpeg_error_mgr jerr; jpg->cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = dt_imageio_jpeg_error_exit; if(setjmp(jerr.setjmp_buffer)) { jpeg_destroy_compress(&(jpg->cinfo)); return 1; } jpeg_create_compress(&(jpg->cinfo)); FILE *f = g_fopen(filename, "wb"); if(!f) return 1; jpeg_stdio_dest(&(jpg->cinfo), f); jpg->cinfo.image_width = jpg->global.width; jpg->cinfo.image_height = jpg->global.height; jpg->cinfo.input_components = 3; jpg->cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&(jpg->cinfo)); jpeg_set_quality(&(jpg->cinfo), jpg->quality, TRUE); if(jpg->quality > 90) jpg->cinfo.comp_info[0].v_samp_factor = 1; if(jpg->quality > 92) jpg->cinfo.comp_info[0].h_samp_factor = 1; if(jpg->quality > 95) jpg->cinfo.dct_method = JDCT_FLOAT; if(jpg->quality < 50) jpg->cinfo.dct_method = JDCT_IFAST; if(jpg->quality < 80) jpg->cinfo.smoothing_factor = 20; if(jpg->quality < 60) jpg->cinfo.smoothing_factor = 40; if(jpg->quality < 40) jpg->cinfo.smoothing_factor = 60; jpg->cinfo.optimize_coding = 1; // according to specs density_unit = 0, X_density = 1, Y_density = 1 should be fine and valid since it // describes an image with unknown unit and square pixels. // however, some applications (like the Telekom cloud thingy) seem to be confused by that, so let's set // these calues to the same as stored in exiv :/ const int resolution = dt_conf_get_int("metadata/resolution"); if(resolution > 0) { jpg->cinfo.density_unit = 1; jpg->cinfo.X_density = resolution; jpg->cinfo.Y_density = resolution; } else { jpg->cinfo.density_unit = 0; jpg->cinfo.X_density = 1; jpg->cinfo.Y_density = 1; } jpeg_start_compress(&(jpg->cinfo), TRUE); if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, over_type, over_filename)->profile; uint32_t len = 0; cmsSaveProfileToMem(out_profile, 0, &len); if(len > 0) { unsigned char *buf = malloc(len * sizeof(unsigned char)); cmsSaveProfileToMem(out_profile, buf, &len); write_icc_profile(&(jpg->cinfo), buf, len); free(buf); } } uint8_t *row = malloc((size_t)3 * jpg->global.width * sizeof(uint8_t)); const uint8_t *buf; while(jpg->cinfo.next_scanline < jpg->cinfo.image_height) { JSAMPROW tmp[1]; buf = in + (size_t)jpg->cinfo.next_scanline * jpg->cinfo.image_width * 4; for(int i = 0; i < jpg->global.width; i++) for(int k = 0; k < 3; k++) row[3 * i + k] = buf[4 * i + k]; tmp[0] = row; jpeg_write_scanlines(&(jpg->cinfo), tmp, 1); } jpeg_finish_compress(&(jpg->cinfo)); free(row); jpeg_destroy_compress(&(jpg->cinfo)); fclose(f); dt_exif_write_blob(exif, exif_len, filename, 1); return 0; }
void gui_init(struct dt_iop_module_t *self) { self->gui_data = malloc(sizeof(dt_iop_colorzones_gui_data_t)); dt_iop_colorzones_gui_data_t *c = (dt_iop_colorzones_gui_data_t *)self->gui_data; dt_iop_colorzones_params_t *p = (dt_iop_colorzones_params_t *)self->params; // c->channel = DT_IOP_COLORZONES_C; c->channel = dt_conf_get_int("plugins/darkroom/colorzones/gui_channel"); int ch = (int)c->channel; c->minmax_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM); (void)dt_draw_curve_add_point(c->minmax_curve, p->equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p->equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) (void)dt_draw_curve_add_point(c->minmax_curve, p->equalizer_x[ch][k], p->equalizer_y[ch][k]); (void)dt_draw_curve_add_point(c->minmax_curve, p->equalizer_x[ch][1]+1.0, p->equalizer_y[ch][1]); c->mouse_x = c->mouse_y = c->mouse_pick = -1.0; c->dragging = 0; c->x_move = -1; c->mouse_radius = 1.0/DT_IOP_COLORZONES_BANDS; self->widget = GTK_WIDGET(gtk_vbox_new(FALSE, DT_GUI_IOP_MODULE_CONTROL_SPACING)); // select by which dimension GtkHBox *hbox = GTK_HBOX(gtk_hbox_new(FALSE, 5)); GtkWidget *label = gtk_label_new(_("select by")); gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f); c->select_by = gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_combo_box_append_text(GTK_COMBO_BOX(c->select_by), _("hue")); gtk_combo_box_append_text(GTK_COMBO_BOX(c->select_by), _("saturation")); gtk_combo_box_append_text(GTK_COMBO_BOX(c->select_by), _("lightness")); gtk_box_pack_start(GTK_BOX(hbox), c->select_by, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (c->select_by), "changed", G_CALLBACK (select_by_changed), (gpointer)self); GtkWidget *tb = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, CPF_STYLE_FLAT); g_object_set(G_OBJECT(tb), "tooltip-text", _("pick gui color from image"), (char *)NULL); g_signal_connect(G_OBJECT(tb), "toggled", G_CALLBACK(request_pick_toggled), self); gtk_box_pack_start(GTK_BOX(hbox), tb, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, FALSE, 0); // tabs GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));//DT_GUI_IOP_MODULE_CONTROL_SPACING)); c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new()); gtk_notebook_append_page(GTK_NOTEBOOK(c->channel_tabs), GTK_WIDGET(gtk_hbox_new(FALSE,0)), gtk_label_new(_("lightness"))); gtk_notebook_append_page(GTK_NOTEBOOK(c->channel_tabs), GTK_WIDGET(gtk_hbox_new(FALSE,0)), gtk_label_new(_("saturation"))); gtk_notebook_append_page(GTK_NOTEBOOK(c->channel_tabs), GTK_WIDGET(gtk_hbox_new(FALSE,0)), gtk_label_new(_("hue"))); gtk_widget_show_all(GTK_WIDGET(gtk_notebook_get_nth_page(c->channel_tabs, c->channel))); gtk_notebook_set_current_page(GTK_NOTEBOOK(c->channel_tabs), c->channel); g_object_set(G_OBJECT(c->channel_tabs), "homogeneous", TRUE, (char *)NULL); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0); g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK (colorzones_tab_switch), self); // the nice graph c->area = GTK_DRAWING_AREA(gtk_drawing_area_new()); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(c->area), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(vbox), TRUE, TRUE, 5); gtk_drawing_area_size(c->area, 195, 195); gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK); g_signal_connect (G_OBJECT (c->area), "expose-event", G_CALLBACK (colorzones_expose), self); g_signal_connect (G_OBJECT (c->area), "button-press-event", G_CALLBACK (colorzones_button_press), self); g_signal_connect (G_OBJECT (c->area), "button-release-event", G_CALLBACK (colorzones_button_release), self); g_signal_connect (G_OBJECT (c->area), "motion-notify-event", G_CALLBACK (colorzones_motion_notify), self); g_signal_connect (G_OBJECT (c->area), "leave-notify-event", G_CALLBACK (colorzones_leave_notify), self); g_signal_connect (G_OBJECT (c->area), "enter-notify-event", G_CALLBACK (colorzones_enter_notify), self); g_signal_connect (G_OBJECT (c->area), "scroll-event", G_CALLBACK (colorzones_scrolled), self); c->hsRGB = dt_colorspaces_create_srgb_profile(); c->hLab = dt_colorspaces_create_lab_profile(); c->xform = cmsCreateTransform(c->hLab, TYPE_Lab_DBL, c->hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0); }
int dt_gui_gtk_init(dt_gui_gtk_t *gui, int argc, char *argv[]) { // unset gtk rc from kde: char gtkrc[PATH_MAX], path[PATH_MAX], datadir[PATH_MAX], configdir[PATH_MAX]; dt_loc_get_datadir(datadir, PATH_MAX); dt_loc_get_user_config_dir(configdir, PATH_MAX); g_snprintf(gtkrc, PATH_MAX, "%s/darktable.gtkrc", configdir); if (!g_file_test(gtkrc, G_FILE_TEST_EXISTS)) g_snprintf(gtkrc, PATH_MAX, "%s/darktable.gtkrc", datadir); if (g_file_test(gtkrc, G_FILE_TEST_EXISTS)) (void)setenv("GTK2_RC_FILES", gtkrc, 1); else fprintf(stderr, "[gtk_init] could not found darktable.gtkrc"); /* lets zero mem */ memset(gui,0,sizeof(dt_gui_gtk_t)); #if GLIB_MAJOR_VERSION <= 2 #if GLIB_MINOR_VERSION < 31 if (!g_thread_supported ()) g_thread_init(NULL); #endif #endif gdk_threads_init(); gdk_threads_enter(); gtk_init (&argc, &argv); GtkWidget *widget; gui->ui = dt_ui_initialize(argc,argv); gui->pixmap = NULL; gui->center_tooltip = 0; gui->presets_popup_menu = NULL; if(g_file_test(gtkrc, G_FILE_TEST_EXISTS)) gtk_rc_parse (gtkrc); // Initializing the shortcut groups darktable.control->accelerators = gtk_accel_group_new(); darktable.control->accelerator_list = NULL; // Connecting the callback to update keyboard accels for key_pressed g_signal_connect(G_OBJECT(gtk_accel_map_get()), "changed", G_CALLBACK(key_accel_changed), NULL); // Initializing widgets init_widgets(); // Adding the global shortcut group to the main window gtk_window_add_accel_group(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), darktable.control->accelerators); // get the screen resolution gui->dpi = gdk_screen_get_resolution(gtk_widget_get_screen(GTK_WIDGET(dt_ui_main_window(darktable.gui->ui)))); // set constant width from conf key int panel_width = dt_conf_get_int("panel_width"); if(panel_width < 20 || panel_width > 500) { // fix for unset/insane values. panel_width = 300; dt_conf_set_int("panel_width", panel_width); } // dt_gui_background_jobs_init(); /* Have the delete event (window close) end the program */ dt_loc_get_datadir(datadir, PATH_MAX); snprintf(path, PATH_MAX, "%s/icons", datadir); gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), path); widget = dt_ui_center(darktable.gui->ui); g_signal_connect (G_OBJECT (widget), "key-press-event", G_CALLBACK (key_pressed), NULL); g_signal_connect (G_OBJECT (widget), "configure-event", G_CALLBACK (configure), NULL); g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (expose), NULL); g_signal_connect (G_OBJECT (widget), "motion-notify-event", G_CALLBACK (mouse_moved), NULL); g_signal_connect (G_OBJECT (widget), "leave-notify-event", G_CALLBACK (center_leave), NULL); g_signal_connect (G_OBJECT (widget), "enter-notify-event", G_CALLBACK (center_enter), NULL); g_signal_connect (G_OBJECT (widget), "button-press-event", G_CALLBACK (button_pressed), NULL); g_signal_connect (G_OBJECT (widget), "button-release-event", G_CALLBACK (button_released), NULL); g_signal_connect (G_OBJECT (widget), "scroll-event", G_CALLBACK (scrolled), NULL); // TODO: left, right, top, bottom: //leave-notify-event widget = darktable.gui->widgets.left_border; g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (expose_borders), (gpointer)0); g_signal_connect (G_OBJECT (widget), "button-press-event", G_CALLBACK (borders_button_pressed), darktable.gui->ui); g_signal_connect (G_OBJECT (widget), "scroll-event", G_CALLBACK (borders_scrolled), (gpointer)0); g_object_set_data(G_OBJECT (widget), "border", (gpointer)0); widget = darktable.gui->widgets.right_border; g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (expose_borders), (gpointer)1); g_signal_connect (G_OBJECT (widget), "button-press-event", G_CALLBACK (borders_button_pressed), darktable.gui->ui); g_signal_connect (G_OBJECT (widget), "scroll-event", G_CALLBACK (borders_scrolled), (gpointer)1); g_object_set_data(G_OBJECT (widget), "border", (gpointer)1); widget = darktable.gui->widgets.top_border; g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (expose_borders), (gpointer)2); g_signal_connect (G_OBJECT (widget), "button-press-event", G_CALLBACK (borders_button_pressed), darktable.gui->ui); g_signal_connect (G_OBJECT (widget), "scroll-event", G_CALLBACK (borders_scrolled), (gpointer)2); g_object_set_data(G_OBJECT (widget), "border", (gpointer)2); widget = darktable.gui->widgets.bottom_border; g_signal_connect (G_OBJECT (widget), "expose-event", G_CALLBACK (expose_borders), (gpointer)3); g_signal_connect (G_OBJECT (widget), "button-press-event", G_CALLBACK (borders_button_pressed), darktable.gui->ui); g_signal_connect (G_OBJECT (widget), "scroll-event", G_CALLBACK (borders_scrolled), (gpointer)3); g_object_set_data(G_OBJECT (widget), "border", (gpointer)3); dt_gui_presets_init(); widget = dt_ui_center(darktable.gui->ui); GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED); // GTK_WIDGET_SET_FLAGS (widget, GTK_DOUBLE_BUFFERED); GTK_WIDGET_SET_FLAGS (widget, GTK_APP_PAINTABLE); // TODO: make this work as: libgnomeui testgnome.c /* GtkContainer *box = GTK_CONTAINER(darktable.gui->widgets.plugins_vbox); GtkScrolledWindow *swin = GTK_SCROLLED_WINDOW(darktable.gui-> widgets.right_scrolled_window); gtk_container_set_focus_vadjustment (box, gtk_scrolled_window_get_vadjustment (swin)); */ dt_ctl_get_display_profile(widget, &darktable.control->xprofile_data, &darktable.control->xprofile_size); // register keys for view switching dt_accel_register_global(NC_("accel", "capture view"), GDK_t, 0); dt_accel_register_global(NC_("accel", "lighttable view"), GDK_l, 0); dt_accel_register_global(NC_("accel", "darkroom view"), GDK_d, 0); dt_accel_connect_global( "capture view", g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), (gpointer)DT_GUI_VIEW_SWITCH_TO_TETHERING, NULL)); dt_accel_connect_global( "lighttable view", g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), (gpointer)DT_GUI_VIEW_SWITCH_TO_LIBRARY, NULL)); dt_accel_connect_global( "darkroom view", g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), (gpointer)DT_GUI_VIEW_SWITCH_TO_DARKROOM, NULL)); // register_keys for applying styles init_styles_key_accels(); connect_styles_key_accels(); // register ctrl-q to quit: dt_accel_register_global(NC_("accel", "quit"), GDK_q, GDK_CONTROL_MASK); dt_accel_connect_global( "quit", g_cclosure_new(G_CALLBACK(quit_callback), NULL, NULL)); // Contrast and brightness accelerators dt_accel_register_global(NC_("accel", "increase brightness"), GDK_F10, 0); dt_accel_register_global(NC_("accel", "decrease brightness"), GDK_F9, 0); dt_accel_register_global(NC_("accel", "increase contrast"), GDK_F8, 0); dt_accel_register_global(NC_("accel", "decrease contrast"), GDK_F7, 0); dt_accel_connect_global( "increase brightness", g_cclosure_new(G_CALLBACK(brightness_key_accel_callback), (gpointer)1, NULL)); dt_accel_connect_global( "decrease brightness", g_cclosure_new(G_CALLBACK(brightness_key_accel_callback), (gpointer)0, NULL)); dt_accel_connect_global( "increase contrast", g_cclosure_new(G_CALLBACK(contrast_key_accel_callback), (gpointer)1, NULL)); dt_accel_connect_global( "decrease contrast", g_cclosure_new(G_CALLBACK(contrast_key_accel_callback), (gpointer)0, NULL)); // Full-screen accelerators dt_accel_register_global(NC_("accel", "toggle fullscreen"), GDK_F11, 0); dt_accel_register_global(NC_("accel", "leave fullscreen"), GDK_Escape, 0); dt_accel_connect_global( "toggle fullscreen", g_cclosure_new(G_CALLBACK(fullscreen_key_accel_callback), (gpointer)1, NULL)); dt_accel_connect_global( "leave fullscreen", g_cclosure_new(G_CALLBACK(fullscreen_key_accel_callback), (gpointer)0, NULL)); // Side-border hide/show dt_accel_register_global(NC_("accel", "toggle side borders"), GDK_Tab, 0); // toggle view of header dt_accel_register_global(NC_("accel", "toggle header"), GDK_h, GDK_CONTROL_MASK); // View-switch dt_accel_register_global(NC_("accel", "switch view"), GDK_period, 0); dt_accel_connect_global( "switch view", g_cclosure_new(G_CALLBACK(view_switch_key_accel_callback), NULL, NULL)); darktable.gui->reset = 0; for(int i=0; i<3; i++) darktable.gui->bgcolor[i] = 0.1333; /* apply contrast to theme */ dt_gui_contrast_init (); return 0; }
static int dt_ellipse_events_button_pressed(struct dt_iop_module_t *module, float pzx, float pzy, double pressure, int which, int type, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index) { if(!gui) return 0; if(gui->source_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; // we start the source dragging gui->source_dragging = TRUE; gui->posx = pzx * darktable.develop->preview_pipe->backbuf_width; gui->posy = pzy * darktable.develop->preview_pipe->backbuf_height; gui->dx = gpt->source[0] - gui->posx; gui->dy = gpt->source[1] - gui->posy; return 1; } else if(gui->point_selected >= 1 && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL && !((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; gui->point_dragging = gui->point_selected; gui->posx = pzx * darktable.develop->preview_pipe->backbuf_width; gui->posy = pzy * darktable.develop->preview_pipe->backbuf_height; gui->dx = gpt->points[0] - gui->posx; gui->dy = gpt->points[1] - gui->posy; return 1; } else if(gui->form_selected && !gui->creation && gui->edit_mode == DT_MASKS_EDIT_FULL && !((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; // we start the form dragging or rotating if((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) gui->form_rotating = TRUE; else gui->form_dragging = TRUE; gui->posx = pzx * darktable.develop->preview_pipe->backbuf_width; gui->posy = pzy * darktable.develop->preview_pipe->backbuf_height; gui->dx = gpt->points[0] - gui->posx; gui->dy = gpt->points[1] - gui->posy; return 1; } else if(gui->form_selected && !gui->creation && ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) { dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index); if(!gpt) return 0; gui->border_toggling = TRUE; return 1; } else if(gui->creation && (which == 3)) { dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL); dt_masks_iop_update(module); dt_control_queue_redraw_center(); return 1; } else if(gui->creation) { dt_iop_module_t *crea_module = gui->creation_module; // we create the ellipse dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(malloc(sizeof(dt_masks_point_ellipse_t))); // we change the center value float wd = darktable.develop->preview_pipe->backbuf_width; float ht = darktable.develop->preview_pipe->backbuf_height; float pts[2] = { pzx * wd, pzy * ht }; dt_dev_distort_backtransform(darktable.develop, pts, 1); ellipse->center[0] = pts[0] / darktable.develop->preview_pipe->iwidth; ellipse->center[1] = pts[1] / darktable.develop->preview_pipe->iheight; if(form->type & DT_MASKS_CLONE) { const float a = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_a"); const float b = dt_conf_get_float("plugins/darkroom/spots/ellipse_radius_b"); const float ellipse_border = dt_conf_get_float("plugins/darkroom/spots/ellipse_border"); const float rotation = dt_conf_get_float("plugins/darkroom/spots/ellipse_rotation"); const int flags = dt_conf_get_int("plugins/darkroom/spots/ellipse_flags"); ellipse->radius[0] = MAX(0.01f, MIN(0.5f, a)); ellipse->radius[1] = MAX(0.01f, MIN(0.5f, b)); ellipse->flags = flags; ellipse->rotation = rotation; const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]); const float reference = (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f/min_radius : 1.0f); ellipse->border = MAX(0.005f * reference, MIN(0.5f * reference, ellipse_border)); form->source[0] = ellipse->center[0] + 0.02f; form->source[1] = ellipse->center[1] + 0.02f; } else { const float a = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_a"); const float b = dt_conf_get_float("plugins/darkroom/masks/ellipse/radius_b"); const float ellipse_border = dt_conf_get_float("plugins/darkroom/masks/ellipse/border"); const float rotation = dt_conf_get_float("plugins/darkroom/masks/ellipse/rotation"); const int flags = dt_conf_get_int("plugins/darkroom/masks/ellipse/flags"); ellipse->radius[0] = MAX(0.01f, MIN(0.5f, a)); ellipse->radius[1] = MAX(0.01f, MIN(0.5f, b)); ellipse->flags = flags; ellipse->rotation = rotation; const float min_radius = fmin(ellipse->radius[0], ellipse->radius[1]); const float reference = (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? 1.0f/min_radius : 1.0f); ellipse->border = MAX(0.005f * reference, MIN(0.5f * reference, ellipse_border)); // not used for masks form->source[0] = form->source[1] = 0.0f; } form->points = g_list_append(form->points, ellipse); dt_masks_gui_form_save_creation(crea_module, form, gui); if(crea_module) { // we save the move dt_dev_add_history_item(darktable.develop, crea_module, TRUE); // and we switch in edit mode to show all the forms dt_masks_set_edit_mode(crea_module, DT_MASKS_EDIT_FULL); dt_masks_iop_update(crea_module); gui->creation_module = NULL; } else { // we select the new form dt_dev_masks_selection_change(darktable.develop, form->formid, TRUE); } // if we draw a clone ellipse, we start now the source dragging if(form->type & DT_MASKS_CLONE) { dt_masks_form_t *grp = darktable.develop->form_visible; if(!grp || !(grp->type & DT_MASKS_GROUP)) return 1; int pos3 = 0, pos2 = -1; GList *fs = g_list_first(grp->points); while(fs) { dt_masks_point_group_t *pt = (dt_masks_point_group_t *)fs->data; if(pt->formid == form->formid) { pos2 = pos3; break; } pos3++; fs = g_list_next(fs); } if(pos2 < 0) return 1; dt_masks_form_gui_t *gui2 = darktable.develop->form_gui; if(!gui2) return 1; gui2->source_dragging = TRUE; gui2->group_edited = gui2->group_selected = pos2; gui2->posx = pzx * darktable.develop->preview_pipe->backbuf_width; gui2->posy = pzy * darktable.develop->preview_pipe->backbuf_height; gui2->dx = 0.0; gui2->dy = 0.0; gui2->scrollx = pzx; gui2->scrolly = pzy; } return 1; } 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_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 *stls; 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); 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 while(stls) { dt_style_item_t *s = (dt_style_item_t *)stls->data; gboolean module_found = FALSE; GList *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)); dt_iop_module_t *sty_module = m; if(format_params->style_append && !(m->flags() & IOP_FLAGS_ONE_INSTANCE)) { sty_module = dt_dev_module_duplicate(m->dev, m, 0); if(!sty_module) { free(h); goto error; } } h->params = s->params; h->blend_params = s->blendop_params; h->enabled = s->enabled; h->module = sty_module; h->multi_priority = 1; g_strlcpy(h->multi_name, "<style>", 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); module_found = TRUE; g_free(s->name); break; } modules = g_list_next(modules); } if(!module_found) dt_style_item_free(s); stls = g_list_next(stls); } g_list_free(stls); } 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; int icctype = dt_conf_get_int("plugins/lighttable/export/icctype"); if(icctype == DT_COLORSPACE_SRGB) { sRGB = 1; } else if(icctype == 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, exif_profile, length, imgid, num, total); free(exif_profile); } else { res = format->write_image(format_params, filename, outbuf, 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)) { dt_control_signal_raise(darktable.signals, DT_SIGNAL_IMAGE_EXPORT_TMPFILE, imgid, filename, format, format_params, storage, storage_params); } return res; error: dt_dev_pixelpipe_cleanup(&pipe); error_early: dt_dev_cleanup(&dev); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); return 1; }
void dt_opencl_init(dt_opencl_t *cl, const int argc, char *argv[]) { dt_pthread_mutex_init(&cl->lock, NULL); cl->inited = 0; cl->enabled = 0; cl->dlocl = NULL; int exclude_opencl = 0; // user selectable parameter defines minimum requirement on GPU memory // default is 768MB // values below 256 will be (re)set to 256 const int opencl_memory_requirement = max(256, dt_conf_get_int("opencl_memory_requirement")); dt_conf_set_int("opencl_memory_requirement", opencl_memory_requirement); for(int k=0; k<argc; k++) if(!strcmp(argv[k], "--disable-opencl")) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] do not try to find and use an opencl runtime library due to explicit user request\n"); exclude_opencl = 1; } if(exclude_opencl) goto finally; // look for explicit definition of opencl_runtime library in preferences const char *library = dt_conf_get_string("opencl_library"); dt_print(DT_DEBUG_OPENCL, "[opencl_init] trying to load opencl library: '%s'\n", library && strlen(library) != 0 ? library : "<system default>"); // dynamically load opencl runtime if(!dt_dlopencl_init(library, &cl->dlocl)) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] no working opencl library found. Continue with opencl disabled\n"); goto finally; } else { dt_print(DT_DEBUG_OPENCL, "[opencl_init] opencl library '%s' found on your system and loaded\n", cl->dlocl->library); } cl_int err; cl_platform_id all_platforms[DT_OPENCL_MAX_PLATFORMS]; cl_uint all_num_devices[DT_OPENCL_MAX_PLATFORMS]; cl_uint num_platforms = DT_OPENCL_MAX_PLATFORMS; err = (cl->dlocl->symbols->dt_clGetPlatformIDs) (DT_OPENCL_MAX_PLATFORMS, all_platforms, &num_platforms); if(err != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not get platforms: %d\n", err); goto finally; } if(num_platforms == 0) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] no opencl platform available\n"); goto finally; } for(int n=0; n < num_platforms; n++) { cl_platform_id platform = all_platforms[n]; // get the number of GPU devices available to the platforms // the other common option is CL_DEVICE_TYPE_GPU/CPU (but the latter doesn't work with the nvidia drivers) err = (cl->dlocl->symbols->dt_clGetDeviceIDs)(platform, CL_DEVICE_TYPE_ALL, 0, NULL, &(all_num_devices[n])); if(err != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not get device id size: %d\n", err); goto finally; } } cl_uint num_devices = 0; for(int n=0; n < num_platforms; n++) num_devices += all_num_devices[n]; // create the device list cl->dev = (dt_opencl_device_t *)malloc(sizeof(dt_opencl_device_t)*num_devices); cl_device_id *devices = (cl_device_id *)malloc(sizeof(cl_device_id)*num_devices); cl_device_id *devs = devices; for(int n=0; n < num_platforms; n++) { cl_platform_id platform = all_platforms[n]; err = (cl->dlocl->symbols->dt_clGetDeviceIDs)(platform, CL_DEVICE_TYPE_ALL, all_num_devices[n], devs, NULL); if(err != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not get devices list: %d\n", err); goto finally; } devs += all_num_devices[n]; } dt_print(DT_DEBUG_OPENCL, "[opencl_init] found %d device%s\n", num_devices, num_devices > 1 ? "s" : ""); int dev = 0; for(int k=0; k<num_devices; k++) { memset(cl->dev[dev].program_used, 0x0, sizeof(int)*DT_OPENCL_MAX_PROGRAMS); memset(cl->dev[dev].kernel_used, 0x0, sizeof(int)*DT_OPENCL_MAX_KERNELS); cl->dev[dev].eventlist = NULL; cl->dev[dev].eventtags = NULL; cl->dev[dev].numevents = 0; cl->dev[dev].eventsconsolidated = 0; cl->dev[dev].maxevents = 0; cl->dev[dev].lostevents = 0; cl->dev[dev].summary=CL_COMPLETE; cl->dev[dev].used_global_mem = 0; cl_device_id devid = cl->dev[dev].devid = devices[k]; char infostr[1024]; size_t infoint; size_t infointtab[1024]; cl_device_type type; cl_bool image_support = 0; // test GPU memory and image support: (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_NAME, sizeof(infostr), &infostr, NULL); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_TYPE, sizeof(cl_device_type), &type, NULL); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_IMAGE_SUPPORT, sizeof(cl_bool), &image_support, NULL); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(size_t), &(cl->dev[dev].max_image_height), NULL); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(size_t), &(cl->dev[dev].max_image_width), NULL); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &(cl->dev[dev].max_mem_alloc), NULL); if(type == CL_DEVICE_TYPE_CPU) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] discarding CPU device %d `%s' as it will not deliver any performance gain.\n", k, infostr); continue; } if(!image_support) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] discarding device %d `%s' due to missing image support.\n", k, infostr); continue; } (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &(cl->dev[dev].max_global_mem), NULL); if(cl->dev[dev].max_global_mem < opencl_memory_requirement*1024*1024) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] discarding device %d `%s' due to insufficient global memory (%luMB).\n", k, infostr, cl->dev[dev].max_global_mem/1024/1024); continue; } dt_print(DT_DEBUG_OPENCL, "[opencl_init] device %d `%s' supports image sizes of %zd x %zd\n", k, infostr, cl->dev[dev].max_image_width, cl->dev[dev].max_image_height); dt_print(DT_DEBUG_OPENCL, "[opencl_init] device %d `%s' allows GPU memory allocations of up to %luMB\n", k, infostr, cl->dev[dev].max_mem_alloc/1024/1024); if(darktable.unmuted & DT_DEBUG_OPENCL) { printf("[opencl_init] device %d: %s \n", k, infostr); printf(" GLOBAL_MEM_SIZE: %.0fMB\n", (double)cl->dev[dev].max_global_mem/1024.0/1024.0); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(infoint), &infoint, NULL); printf(" MAX_WORK_GROUP_SIZE: %zd\n", infoint); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(infoint), &infoint, NULL); printf(" MAX_WORK_ITEM_DIMENSIONS: %zd\n", infoint); printf(" MAX_WORK_ITEM_SIZES: [ "); (cl->dlocl->symbols->dt_clGetDeviceInfo)(devid, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(infointtab), infointtab, NULL); for (int i=0; i<infoint; i++) printf("%zd ", infointtab[i]); printf("]\n"); } dt_pthread_mutex_init(&cl->dev[dev].lock, NULL); cl->dev[dev].context = (cl->dlocl->symbols->dt_clCreateContext)(0, 1, &devid, NULL, NULL, &err); if(err != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not create context for device %d: %d\n", k, err); goto finally; } // create a command queue for first device the context reported cl->dev[dev].cmd_queue = (cl->dlocl->symbols->dt_clCreateCommandQueue)(cl->dev[dev].context, devid, (darktable.unmuted & DT_DEBUG_PERF) ? CL_QUEUE_PROFILING_ENABLE : 0, &err); if(err != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not create command queue for device %d: %d\n", k, err); goto finally; } char dtpath[1024], filename[1024], programname[1024]; dt_loc_get_datadir(dtpath, 1024); snprintf(filename, 1024, "%s/kernels/programs.conf", dtpath); // now load all darktable cl kernels. // TODO: compile as a job? FILE *f = fopen(filename, "rb"); if(f) { while(!feof(f)) { int rd = fscanf(f, "%[^\n]\n", programname); if(rd != 1) continue; // remove comments: for(int k=0; k<strlen(programname); k++) if(programname[k] == '#') { programname[k] = '\0'; for(int l=k-1; l>=0; l--) { if (programname[l] == ' ') programname[l] = '\0'; else break; } break; } if(programname[0] == '\0') continue; snprintf(filename, 1024, "%s/kernels/%s", dtpath, programname); dt_print(DT_DEBUG_OPENCL, "[opencl_init] compiling program `%s' ..\n", programname); const int prog = dt_opencl_load_program(dev, filename); if(dt_opencl_build_program(dev, prog)) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] failed to compile program `%s'!\n", programname); goto finally; } } fclose(f); } else { dt_print(DT_DEBUG_OPENCL, "[opencl_init] could not open `%s'!\n", filename); goto finally; } ++dev; } free(devices); if(dev > 0) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] successfully initialized.\n"); cl->num_devs = dev; cl->inited = 1; cl->enabled = dt_conf_get_bool("opencl"); } else { dt_print(DT_DEBUG_OPENCL, "[opencl_init] no suitable devices found.\n"); } finally: dt_print(DT_DEBUG_OPENCL, "[opencl_init] FINALLY: opencl is %sAVAILABLE on this system.\n", cl->inited ? "" : "NOT "); dt_print(DT_DEBUG_OPENCL, "[opencl_init] initial status of opencl enabled flag is %s.\n", cl->enabled ? "ON" : "OFF"); return; }
/** Initializes a new pwstorage context. */ const dt_pwstorage_t* dt_pwstorage_new() { dt_pwstorage_t *pwstorage = g_malloc(sizeof(dt_pwstorage_t)); dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] Creating new context %lx\n",(unsigned long int)pwstorage); if(pwstorage == NULL) return NULL; gint _backend = dt_conf_get_int( "plugins/pwstorage/pwstorage_backend" ); switch(_backend) { default: dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] unknown storage backend. Using none.\n"); case PW_STORAGE_BACKEND_NONE: pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE; pwstorage->backend_context = NULL; dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] no storage backend. not storing username/password. please change in gconf: \"plugins/pwstorage/pwstorage_backend\".\n"); break; case PW_STORAGE_BACKEND_GCONF: // this is so important that I want it to be printed in any case. so g_printerr() instead of dt_print() g_printerr("[pwstorage_new] WARNING: you are using gconf for username/password storage! they are being stored unencrypted on your hard disk.\n"); pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_GCONF; pwstorage->backend_context = NULL; break; case PW_STORAGE_BACKEND_KWALLET: #ifdef HAVE_KWALLET dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] using kwallet backend for username/password storage"); pwstorage->backend_context = (void*)dt_pwstorage_kwallet_new(); if(pwstorage->backend_context == NULL) { dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] error starting kwallet. using no storage backend.\n"); pwstorage->backend_context = NULL; pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE; } else { pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_KWALLET; } dt_print(DT_DEBUG_PWSTORAGE," done.\n"); #else dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] kwallet storage not available. using no storage backend.\n"); pwstorage->backend_context = NULL; pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE; #endif break; #ifdef HAVE_GKEYRING case PW_STORAGE_BACKEND_GNOME_KEYRING: dt_print (DT_DEBUG_PWSTORAGE,"[pwstorage_new] using gnome keyring backend for usersname/password storage.\n"); pwstorage->backend_context = (void*)dt_pwstorage_gkeyring_new (); if (pwstorage->backend_context == NULL) { dt_print (DT_DEBUG_PWSTORAGE,"[pwstorage_new] error starting gnome keyring. using no storage backend.\n"); pwstorage->backend_context = NULL; pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE; } else pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_GNOME_KEYRING; #else dt_print(DT_DEBUG_PWSTORAGE,"[pwstorage_new] gnome keyring storage not available. using no storage backend.\n"); pwstorage->backend_context = NULL; pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE; #endif break; } dt_conf_set_int( "plugins/pwstorage/pwstorage_backend", pwstorage->pw_storage_backend ); return pwstorage; }
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); if(out_profile) output = out_profile->profile; else { output = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; dt_control_log(_("missing output profile has been replaced by sRGB!")); fprintf(stderr, "missing output profile `%s' has been replaced by sRGB!\n", dt_colorspaces_get_name(out_type, out_filename)); } /* creating softproof profile if softproof is enabled */ if(d->mode != DT_PROFILE_NORMAL && pipe->type == DT_DEV_PIXELPIPE_FULL) { const dt_colorspaces_color_profile_t *p = dt_colorspaces_get_profile(darktable.color_profiles->softproof_type, darktable.color_profiles->softproof_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY); if(p) softproof = p->profile; else { softproof = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; dt_control_log(_("missing output profile has been replaced by sRGB!")); fprintf(stderr, "missing output profile `%s' has been replaced by sRGB!\n", dt_colorspaces_get_name(out_type, out_filename)); } /* 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); }
int key_pressed(dt_view_t *self, guint key, guint state) { dt_library_t *lib = (dt_library_t *)self->data; dt_control_accels_t *accels = &darktable.control->accels; if(!darktable.control->key_accelerators_on) return 0; int zoom = dt_conf_get_int("plugins/lighttable/images_in_row"); const int layout = dt_conf_get_int("plugins/lighttable/layout"); if(key == accels->lighttable_preview.accel_key && state == accels->lighttable_preview.accel_mods) { int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if(lib->full_preview_id == -1 && mouse_over_id != -1 ) { // encode panel visibility into full_preview lib->full_preview = 0; lib->full_preview_id = mouse_over_id; // let's hide some gui components lib->full_preview |= (dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_LEFT)&1) << 0; dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_LEFT, FALSE); lib->full_preview |= (dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_RIGHT)&1) << 1; dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_RIGHT, FALSE); lib->full_preview |= (dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_CENTER_BOTTOM)&1) << 2; dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_CENTER_BOTTOM, FALSE); lib->full_preview |= (dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_CENTER_TOP)&1) << 3; dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_CENTER_TOP, FALSE); //dt_dev_invalidate(darktable.develop); } return 1; } if(key == accels->lighttable_left.accel_key && state == accels->lighttable_left.accel_mods) { if(layout == 1 && zoom == 1) lib->track = -DT_LIBRARY_MAX_ZOOM; else lib->track = -1; return 1; } if(key == accels->lighttable_right.accel_key && state == accels->lighttable_right.accel_mods) { if(layout == 1 && zoom == 1) lib->track = DT_LIBRARY_MAX_ZOOM; else lib->track = 1; return 1; } if(key == accels->lighttable_up.accel_key && state == accels->lighttable_up.accel_mods) { lib->track = -DT_LIBRARY_MAX_ZOOM; return 1; } if(key == accels->lighttable_down.accel_key && state == accels->lighttable_down.accel_mods) { lib->track = DT_LIBRARY_MAX_ZOOM; return 1; } if(key == accels->lighttable_center.accel_key && state == accels->lighttable_center.accel_mods) { lib->center = 1; return 1; } return 0; }
void gui_reset (dt_imageio_module_format_t *self) { dt_imageio_jpeg_gui_data_t *g = (dt_imageio_jpeg_gui_data_t *)self->gui_data; dtgtk_slider_set_value(g->quality, dt_conf_get_int("plugins/imageio/format/jpeg/quality")); }
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 = (int*)calloc(max_rows*max_cols, sizeof(int)); if(!query_ids) goto after_drawing; 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 escape_image_loop; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } escape_image_loop: cairo_restore(cr); // and now the group borders cairo_save(cr); 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 escape_border_loop; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } escape_border_loop: cairo_restore(cr); after_drawing: /* 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); } } if(query_ids) 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); } }
void* get_params (dt_lib_module_t *self, int *size) { // concat storage and format, size is max + header dt_imageio_module_format_t *mformat = dt_imageio_get_format(); dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage(); if(!mformat || !mstorage) return NULL; int32_t fsize = 0, ssize = 0; // size will be only as large as to remove random pointers from params (stored at the end). dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &fsize); void *sdata = mstorage->get_params(mstorage, &ssize); // we allow null pointers (plugin not ready for export in current state), and just dont copy back the settings later: if(!sdata) ssize = 0; if(!fdata) fsize = 0; if(fdata) { // clean up format global params (need to set all bytes to reliably detect which preset is active). // we happen to want to set it all to 0 memset(fdata, 0, sizeof(dt_imageio_module_data_t)); } // FIXME: also the web preset has to be applied twice to be known as preset! (other dimension magic going on here?) // TODO: get this stuff from gui and not from conf, so it will be sanity-checked (you can never delete an insane preset)? // also store icc profile/intent here. int32_t iccintent = dt_conf_get_int("plugins/lighttable/export/iccintent"); int32_t max_width = dt_conf_get_int ("plugins/lighttable/export/width"); int32_t max_height = dt_conf_get_int ("plugins/lighttable/export/height"); gchar *iccprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile"); gchar *style = dt_conf_get_string("plugins/lighttable/export/style"); if (fdata) { strncpy(fdata->style, style, 128); } if(!iccprofile) { iccprofile = (char *)g_malloc(1); iccprofile[0] = '\0'; } char *fname = mformat->plugin_name, *sname = mstorage->plugin_name; int32_t fname_len = strlen(fname), sname_len = strlen(sname); *size = fname_len + sname_len + 2 + 2*sizeof(int32_t) + fsize + ssize + 3*sizeof(int32_t) + strlen(iccprofile) + 1; char *params = (char *)malloc(*size); memset(params, 0, *size); int pos = 0; memcpy(params+pos, &max_width, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &max_height, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &iccintent, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, iccprofile, strlen(iccprofile)+1); pos += strlen(iccprofile) + 1; memcpy(params+pos, fname, fname_len+1); pos += fname_len+1; memcpy(params+pos, sname, sname_len+1); pos += sname_len+1; memcpy(params+pos, &fsize, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &ssize, sizeof(int32_t)); pos += sizeof(int32_t); if(fdata != NULL) // otherwise fsize == 0, but clang doesn't like it ... { memcpy(params+pos, fdata, fsize); pos += fsize; } if(sdata != NULL) // see above { memcpy(params+pos, sdata, ssize); pos += ssize; } g_assert(pos == *size); g_free(iccprofile); g_free(style); if(fdata) mformat->free_params(mformat, fdata); if(sdata) mstorage->free_params(mstorage, sdata); return params; }
static void expose_zoomable (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; float zoom, zoom_x, zoom_y; int32_t mouse_over_id, pan, track, center; /* query new collection count */ lib->collection_count = dt_collection_get_count (darktable.collection); DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); zoom = dt_conf_get_int("plugins/lighttable/images_in_row"); zoom_x = lib->zoom_x; zoom_y = lib->zoom_y; pan = lib->pan; center = lib->center; track = lib->track; lib->image_over = DT_VIEW_DESERT; cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); const float wd = width/zoom; const float ht = width/zoom; static int oldpan = 0; static float oldzoom = -1; if(oldzoom < 0) oldzoom = zoom; // TODO: exaggerate mouse gestures to pan when zoom == 1 if(pan)// && mouse_over_id >= 0) { zoom_x = lib->select_offset_x - /* (zoom == 1 ? 2. : 1.)*/pointerx; zoom_y = lib->select_offset_y - /* (zoom == 1 ? 2. : 1.)*/pointery; } if(!lib->statements.main_query) return; if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; if(zoom > DT_LIBRARY_MAX_ZOOM) { // double speed. if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; if(zoom > 1.5*DT_LIBRARY_MAX_ZOOM) { // quad speed. if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; } } if(oldzoom != zoom) { float oldx = (pointerx + zoom_x)*oldzoom/width; float oldy = (pointery + zoom_y)*oldzoom/width; if(zoom == 1) { zoom_x = (int)oldx*wd; zoom_y = (int)oldy*ht; lib->offset = 0x7fffffff; } else { zoom_x = oldx*wd - pointerx; zoom_y = oldy*ht - pointery; } } oldzoom = zoom; // TODO: replace this with center on top of selected/developed image if(center) { if(mouse_over_id >= 0) { zoom_x = wd*((int)(zoom_x)/(int)wd); zoom_y = ht*((int)(zoom_y)/(int)ht); } else zoom_x = zoom_y = 0.0; center = 0; } // mouse left the area, but we leave mouse over as it was, especially during panning // if(!pan && pointerx > 0 && pointerx < width && pointery > 0 && pointery < height) DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); if(!pan && zoom != 1) DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); // set scrollbar positions, clamp zoom positions if(lib->collection_count == 0) { zoom_x = zoom_y = 0.0f; } else if(zoom < 1.01) { if(zoom_x < 0) zoom_x = 0; if(zoom_x > wd*DT_LIBRARY_MAX_ZOOM-wd) zoom_x = wd*DT_LIBRARY_MAX_ZOOM-wd; if(zoom_y < 0) zoom_y = 0; if(zoom_y > ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht) zoom_y = ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht; } else { if(zoom_x < -wd*DT_LIBRARY_MAX_ZOOM/2) zoom_x = -wd*DT_LIBRARY_MAX_ZOOM/2; if(zoom_x > wd*DT_LIBRARY_MAX_ZOOM-wd) zoom_x = wd*DT_LIBRARY_MAX_ZOOM-wd; if(zoom_y < -height+ht) zoom_y = -height+ht; if(zoom_y > ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht) zoom_y = ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht; } int offset_i = (int)(zoom_x/wd); int offset_j = (int)(zoom_y/ht); if(lib->first_visible_filemanager >= 0) { offset_i = lib->first_visible_filemanager % DT_LIBRARY_MAX_ZOOM; offset_j = lib->first_visible_filemanager / DT_LIBRARY_MAX_ZOOM; } lib->first_visible_filemanager = -1; lib->first_visible_zoomable = offset_i + DT_LIBRARY_MAX_ZOOM*offset_j; // arbitrary 1000 to avoid bug due to round towards zero using (int) int seli = zoom == 1 ? 0 : ((int)(1000 + (pointerx + zoom_x)/wd) - MAX(offset_i, 0) - 1000); int selj = zoom == 1 ? 0 : ((int)(1000 + (pointery + zoom_y)/ht) - offset_j - 1000); float offset_x = (zoom == 1) ? 0.0 : (zoom_x/wd - (int)(zoom_x/wd)); float offset_y = (zoom == 1) ? 0.0 : (zoom_y/ht - (int)(zoom_y/ht)); const int max_rows = (zoom == 1) ? 1 : (2 + (int)((height)/ht + .5)); const int max_cols = (zoom == 1) ? 1 : (MIN(DT_LIBRARY_MAX_ZOOM - MAX(0, offset_i), 1 + (int)(zoom+.5))); int offset = MAX(0, offset_i) + DT_LIBRARY_MAX_ZOOM*offset_j; int img_pointerx = zoom == 1 ? pointerx : fmodf(pointerx + zoom_x, wd); int img_pointery = zoom == 1 ? pointery : fmodf(pointery + zoom_y, ht); // assure 1:1 is not switching images on resize/tab events: if(!track && lib->offset != 0x7fffffff && zoom == 1) { offset = lib->offset; zoom_x = wd*(offset % DT_LIBRARY_MAX_ZOOM); zoom_y = ht*(offset / DT_LIBRARY_MAX_ZOOM); } else lib->offset = offset; int id, clicked1, last_seli = 1<<30, last_selj = 1<<30; clicked1 = (oldpan == 0 && pan == 1 && lib->button == 1); dt_view_set_scrollbar(self, MAX(0, offset_i), DT_LIBRARY_MAX_ZOOM, zoom, DT_LIBRARY_MAX_ZOOM*offset_j, lib->collection_count, DT_LIBRARY_MAX_ZOOM*max_cols); cairo_translate(cr, -offset_x*wd, -offset_y*ht); cairo_translate(cr, -MIN(offset_i*wd, 0.0), 0.0); for(int row = 0; row < max_rows; row++) { if(offset < 0) { cairo_translate(cr, 0, ht); offset += DT_LIBRARY_MAX_ZOOM; continue; } /* clear and reset main query */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, max_cols); for(int col = 0; col < max_cols; col++) { if(sqlite3_step(lib->statements.main_query) == SQLITE_ROW) { id = sqlite3_column_int(lib->statements.main_query, 0); // set mouse over id if((zoom == 1 && mouse_over_id < 0) || ((!pan || track) && seli == col && selj == row)) { mouse_over_id = id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); } // add clicked image to selected table if(clicked1) { if((lib->modifiers & GDK_SHIFT_MASK) == 0 && (lib->modifiers & GDK_CONTROL_MASK) == 0 && seli == col && selj == row) { /* clear selection except id */ /* clear and resest statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.delete_except_arg); DT_DEBUG_SQLITE3_RESET(lib->statements.delete_except_arg); /* reuse statment */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.delete_except_arg, 1, id); sqlite3_step(lib->statements.delete_except_arg); } // FIXME: whatever comes first assumtion is broken! // if((lib->modifiers & GDK_SHIFT_MASK) && (last_seli == (1<<30)) && // (image->id == lib->last_selected_id || image->id == mouse_over_id)) { last_seli = col; last_selj = row; } // if(last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= MIN(last_seli,seli) && row >= MIN(last_selj,selj) && // col <= MAX(last_seli,seli) && row <= MAX(last_selj,selj)) && (col != last_seli || row != last_selj)) || if((lib->modifiers & GDK_SHIFT_MASK) && id == lib->last_selected_idx) { last_seli = col; last_selj = row; } if((last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= last_seli && row >= last_selj && col <= seli && row <= selj) && (col != last_seli || row != last_selj))) || (seli == col && selj == row)) { // insert all in range if shift, or only the one the mouse is over for ctrl or plain click. dt_view_toggle_selection(id); lib->last_selected_idx = id; } } cairo_save(cr); // if(zoom == 1) dt_image_prefetch(image, DT_IMAGE_MIPF); dt_view_image_expose(&(lib->image_over), id, cr, wd, zoom == 1 ? height : ht, zoom, img_pointerx, img_pointery); cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); offset += DT_LIBRARY_MAX_ZOOM; } failure: oldpan = pan; lib->zoom_x = zoom_x; lib->zoom_y = zoom_y; lib->track = 0; lib->center = center; if(darktable.unmuted & DT_DEBUG_CACHE) dt_mipmap_cache_print(darktable.mipmap_cache); }
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); #if !defined __APPLE__ && !defined __WIN32__ _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 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-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], "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[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 #ifdef USE_LUA dt_lua_init_early(NULL); #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); // initialize 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, 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(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(); // 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 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 *)malloc(sizeof(dt_opencl_t)); memset(darktable.opencl, 0, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, argc, argv); #endif 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); } 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 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); }
void gui_init(dt_lib_module_t *self) { /* initialize ui widgets */ dt_lib_filmstrip_t *d = (dt_lib_filmstrip_t *)g_malloc(sizeof(dt_lib_filmstrip_t)); self->data = (void *)d; memset(d,0,sizeof(dt_lib_filmstrip_t)); d->last_selected_id = -1; d->history_copy_imgid = -1; d->activated_image = -1; d->mouse_over_id = -1; /* create drawingarea */ self->widget = gtk_vbox_new(FALSE,0); /* createing filmstrip box*/ d->filmstrip = gtk_event_box_new(); gtk_widget_add_events(d->filmstrip, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_LEAVE_NOTIFY_MASK); /* connect callbacks */ g_signal_connect (G_OBJECT (d->filmstrip), "expose-event", G_CALLBACK (_lib_filmstrip_expose_callback), self); g_signal_connect (G_OBJECT (d->filmstrip), "button-press-event", G_CALLBACK (_lib_filmstrip_button_press_callback), self); g_signal_connect (G_OBJECT (d->filmstrip), "scroll-event", G_CALLBACK (_lib_filmstrip_scroll_callback), self); g_signal_connect (G_OBJECT (d->filmstrip), "motion-notify-event", G_CALLBACK(_lib_filmstrip_motion_notify_callback), self); g_signal_connect (G_OBJECT (d->filmstrip), "leave-notify-event", G_CALLBACK(_lib_filmstrip_mouse_leave_callback), self); /* set size of filmstrip */ int32_t height = dt_conf_get_int("plugins/lighttable/filmstrip/height"); gtk_widget_set_size_request(d->filmstrip, -1, CLAMP(height,64,400)); /* create the resize handle */ GtkWidget *size_handle = gtk_event_box_new(); gtk_widget_set_size_request(size_handle,-1,10); gtk_widget_add_events(size_handle, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); g_signal_connect (G_OBJECT (size_handle), "button-press-event", G_CALLBACK (_lib_filmstrip_size_handle_button_callback), self); g_signal_connect (G_OBJECT (size_handle), "button-release-event", G_CALLBACK (_lib_filmstrip_size_handle_button_callback), self); g_signal_connect (G_OBJECT (size_handle), "motion-notify-event", G_CALLBACK (_lib_filmstrip_size_handle_motion_notify_callback), self); g_signal_connect (G_OBJECT (size_handle), "leave-notify-event", G_CALLBACK(_lib_filmstrip_size_handle_cursor_callback), self); g_signal_connect (G_OBJECT (size_handle), "enter-notify-event", G_CALLBACK(_lib_filmstrip_size_handle_cursor_callback), self); gtk_box_pack_start(GTK_BOX(self->widget), size_handle, FALSE, FALSE,0); gtk_box_pack_start(GTK_BOX(self->widget), d->filmstrip, FALSE, FALSE,0); /* initialize view manager proxy */ darktable.view_manager->proxy.filmstrip.module = self; darktable.view_manager->proxy.filmstrip.scroll_to_image = _lib_filmstrip_scroll_to_image; darktable.view_manager->proxy.filmstrip.activated_image = _lib_filmstrip_get_activated_imgid; /* connect signal handler */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED, G_CALLBACK(_lib_filmstrip_collection_changed_callback), (gpointer)self); dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED, G_CALLBACK(_lib_filmstrip_collection_changed_callback), (gpointer)self); }
void _capture_view_set_jobcode(const dt_view_t *view, const char *name) { g_assert( view != NULL ); dt_capture_t *cv=(dt_capture_t *)view->data; /* take care of previous capture filmroll */ if( cv->film ) { if( dt_film_is_empty(cv->film->id) ) dt_film_remove(cv->film->id ); else dt_film_cleanup( cv->film ); } /* lets initialize a new filmroll for the capture... */ cv->film = (dt_film_t*)malloc(sizeof(dt_film_t)); if(!cv->film) return; dt_film_init(cv->film); int current_filmroll = dt_conf_get_int("plugins/capture/current_filmroll"); if(current_filmroll >= 0) { /* open existing filmroll and import captured images into this roll */ cv->film->id = current_filmroll; if (dt_film_open2 (cv->film) !=0) { /* failed to open the current filmroll, let's reset and create a new one */ dt_conf_set_int ("plugins/capture/current_filmroll",-1); } else cv->path = g_strdup(cv->film->dirname); } if (dt_conf_get_int ("plugins/capture/current_filmroll") == -1) { if(cv->jobcode) g_free(cv->jobcode); cv->jobcode = g_strdup(name); // Setup variables jobcode... cv->vp->jobcode = cv->jobcode; /* reset session sequence number */ dt_variables_reset_sequence (cv->vp); // Construct the directory for filmroll... gchar* path = g_build_path(G_DIR_SEPARATOR_S,cv->basedirectory,cv->subdirectory, (char *)NULL); cv->path = dt_util_fix_path(path); g_free(path); dt_variables_expand( cv->vp, cv->path, FALSE ); sprintf(cv->film->dirname,"%s",dt_variables_get_result(cv->vp)); // Create recursive directories, abort if no access if( g_mkdir_with_parents(cv->film->dirname,0755) == -1 ) { dt_control_log(_("failed to create session path %s."), cv->film->dirname); if(cv->film) { free( cv->film ); cv->film = NULL; } return; } if(dt_film_new(cv->film,cv->film->dirname) > 0) { // Switch to new filmroll dt_film_open(cv->film->id); /* store current filmroll */ dt_conf_set_int("plugins/capture/current_filmroll",cv->film->id); } dt_control_log(_("new session initiated '%s'"),cv->jobcode,cv->film->id); } }
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); }
void gui_init(dt_lib_module_t *self) { self->data = calloc(1, sizeof(dt_lib_live_view_t)); // Setup lib data dt_lib_live_view_t *lib = self->data; lib->splitline_x = lib->splitline_y = 0.5; // Setup gui self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); dt_gui_add_help_link(self->widget, "live_view.html#live_view"); GtkWidget *box; box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(self->widget), box, TRUE, TRUE, 0); lib->live_view = dtgtk_togglebutton_new(dtgtk_cairo_paint_eye, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); lib->live_view_zoom = dtgtk_button_new( dtgtk_cairo_paint_zoom, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); // TODO: see _zoom_live_view_clicked lib->rotate_ccw = dtgtk_button_new(dtgtk_cairo_paint_refresh, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); lib->rotate_cw = dtgtk_button_new(dtgtk_cairo_paint_refresh, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_UP, NULL); lib->flip = dtgtk_togglebutton_new(dtgtk_cairo_paint_flip, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_UP, NULL); gtk_box_pack_start(GTK_BOX(box), lib->live_view, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->live_view_zoom, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->rotate_ccw, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->rotate_cw, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->flip, TRUE, TRUE, 0); gtk_widget_set_tooltip_text(lib->live_view, _("toggle live view")); gtk_widget_set_tooltip_text(lib->live_view_zoom, _("zoom live view")); gtk_widget_set_tooltip_text(lib->rotate_ccw, _("rotate 90 degrees ccw")); gtk_widget_set_tooltip_text(lib->rotate_cw, _("rotate 90 degrees cw")); gtk_widget_set_tooltip_text(lib->flip, _("flip live view horizontally")); g_signal_connect(G_OBJECT(lib->live_view), "clicked", G_CALLBACK(_toggle_live_view_clicked), lib); g_signal_connect(G_OBJECT(lib->live_view_zoom), "clicked", G_CALLBACK(_zoom_live_view_clicked), lib); g_signal_connect(G_OBJECT(lib->rotate_ccw), "clicked", G_CALLBACK(_rotate_ccw), lib); g_signal_connect(G_OBJECT(lib->rotate_cw), "clicked", G_CALLBACK(_rotate_cw), lib); g_signal_connect(G_OBJECT(lib->flip), "clicked", G_CALLBACK(_toggle_flip_clicked), lib); // focus buttons box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(self->widget), box, TRUE, TRUE, 0); lib->focus_in_big = dtgtk_button_new(dtgtk_cairo_paint_solid_triangle, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_LEFT, NULL); lib->focus_in_small = dtgtk_button_new(dtgtk_cairo_paint_arrow, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_LEFT, NULL); // TODO icon not centered lib->focus_out_small = dtgtk_button_new(dtgtk_cairo_paint_arrow, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_RIGHT, NULL); // TODO same here lib->focus_out_big = dtgtk_button_new(dtgtk_cairo_paint_solid_triangle, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | CPF_DIRECTION_RIGHT, NULL); gtk_box_pack_start(GTK_BOX(box), lib->focus_in_big, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->focus_in_small, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->focus_out_small, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), lib->focus_out_big, TRUE, TRUE, 0); gtk_widget_set_tooltip_text(lib->focus_in_big, _("move focus point in (big steps)")); gtk_widget_set_tooltip_text(lib->focus_in_small, _("move focus point in (small steps)")); gtk_widget_set_tooltip_text(lib->focus_out_small, _("move focus point out (small steps)")); gtk_widget_set_tooltip_text(lib->focus_out_big, _("move focus point out (big steps)")); // Near 3 g_signal_connect(G_OBJECT(lib->focus_in_big), "clicked", G_CALLBACK(_focus_button_clicked), GINT_TO_POINTER(2)); // Near 1 g_signal_connect(G_OBJECT(lib->focus_in_small), "clicked", G_CALLBACK(_focus_button_clicked), GINT_TO_POINTER(0)); // Far 1 g_signal_connect(G_OBJECT(lib->focus_out_small), "clicked", G_CALLBACK(_focus_button_clicked), GINT_TO_POINTER(4)); // Far 3 g_signal_connect(G_OBJECT(lib->focus_out_big), "clicked", G_CALLBACK(_focus_button_clicked), GINT_TO_POINTER(6)); // Guides lib->guide_selector = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(lib->guide_selector, NULL, _("guides")); gtk_box_pack_start(GTK_BOX(self->widget), lib->guide_selector, TRUE, TRUE, 0); lib->guides_widgets = gtk_stack_new(); gtk_stack_set_homogeneous(GTK_STACK(lib->guides_widgets), FALSE); gtk_box_pack_start(GTK_BOX(self->widget), lib->guides_widgets, TRUE, TRUE, 0); dt_bauhaus_combobox_add(lib->guide_selector, _("none")); int i = 0; for(GList *iter = darktable.guides; iter; iter = g_list_next(iter), i++) { GtkWidget *widget = NULL; dt_guides_t *guide = (dt_guides_t *)iter->data; dt_bauhaus_combobox_add(lib->guide_selector, _(guide->name)); if(guide->widget) { // generate some unique name so that we can have the same name several times char name[5]; snprintf(name, sizeof(name), "%d", i); widget = guide->widget(NULL, guide->user_data); gtk_widget_show_all(widget); gtk_stack_add_named(GTK_STACK(lib->guides_widgets), widget, name); } lib->guides_widgets_list = g_list_append(lib->guides_widgets_list, widget); } gtk_widget_set_no_show_all(lib->guides_widgets, TRUE); gtk_widget_set_tooltip_text(lib->guide_selector, _("display guide lines to help compose your photograph")); g_signal_connect(G_OBJECT(lib->guide_selector), "value-changed", G_CALLBACK(guides_presets_changed), lib); lib->flip_guides = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(lib->flip_guides, NULL, _("flip")); dt_bauhaus_combobox_add(lib->flip_guides, _("none")); dt_bauhaus_combobox_add(lib->flip_guides, _("horizontally")); dt_bauhaus_combobox_add(lib->flip_guides, _("vertically")); dt_bauhaus_combobox_add(lib->flip_guides, _("both")); gtk_widget_set_tooltip_text(lib->flip_guides, _("flip guides")); gtk_box_pack_start(GTK_BOX(self->widget), lib->flip_guides, TRUE, TRUE, 0); lib->overlay = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(lib->overlay, NULL, _("overlay")); dt_bauhaus_combobox_add(lib->overlay, _("none")); dt_bauhaus_combobox_add(lib->overlay, _("selected image")); dt_bauhaus_combobox_add(lib->overlay, _("id")); gtk_widget_set_tooltip_text(lib->overlay, _("overlay another image over the live view")); g_signal_connect(G_OBJECT(lib->overlay), "value-changed", G_CALLBACK(overlay_changed), lib); gtk_box_pack_start(GTK_BOX(self->widget), lib->overlay, TRUE, TRUE, 0); lib->overlay_id_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *label = gtk_label_new(_("image id")); gtk_widget_set_halign(label, GTK_ALIGN_START); lib->overlay_id = gtk_spin_button_new_with_range(0, 1000000000, 1); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(lib->overlay_id), 0); gtk_widget_set_tooltip_text(lib->overlay_id, _("enter image id of the overlay manually")); g_signal_connect(G_OBJECT(lib->overlay_id), "value-changed", G_CALLBACK(_overlay_id_changed), lib); gtk_spin_button_set_value(GTK_SPIN_BUTTON(lib->overlay_id), dt_conf_get_int("plugins/lighttable/live_view/overlay_imgid")); gtk_box_pack_start(GTK_BOX(lib->overlay_id_box), label, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(lib->overlay_id_box), lib->overlay_id, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), lib->overlay_id_box, TRUE, TRUE, 0); gtk_widget_show(lib->overlay_id); gtk_widget_show(label); lib->overlay_mode = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(lib->overlay_mode, NULL, _("overlay mode")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "normal")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "xor")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "add")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "saturate")); #if(CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0)) dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "multiply")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "screen")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "overlay")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "darken")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "lighten")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "color dodge")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "color burn")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "hard light")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "soft light")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "difference")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "exclusion")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "HSL hue")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "HSL saturation")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "HSL color")); dt_bauhaus_combobox_add(lib->overlay_mode, C_("blendmode", "HSL luminosity")); #endif gtk_widget_set_tooltip_text(lib->overlay_mode, _("mode of the overlay")); dt_bauhaus_combobox_set(lib->overlay_mode, dt_conf_get_int("plugins/lighttable/live_view/overlay_mode")); g_signal_connect(G_OBJECT(lib->overlay_mode), "value-changed", G_CALLBACK(_overlay_mode_changed), lib); gtk_box_pack_start(GTK_BOX(self->widget), lib->overlay_mode, TRUE, TRUE, 0); lib->overlay_splitline = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(lib->overlay_splitline, NULL, _("split line")); dt_bauhaus_combobox_add(lib->overlay_splitline, _("off")); dt_bauhaus_combobox_add(lib->overlay_splitline, _("on")); gtk_widget_set_tooltip_text(lib->overlay_splitline, _("only draw part of the overlay")); dt_bauhaus_combobox_set(lib->overlay_splitline, dt_conf_get_int("plugins/lighttable/live_view/splitline")); g_signal_connect(G_OBJECT(lib->overlay_splitline), "value-changed", G_CALLBACK(_overlay_splitline_changed), lib); gtk_box_pack_start(GTK_BOX(self->widget), lib->overlay_splitline, TRUE, TRUE, 0); gtk_widget_set_visible(GTK_WIDGET(lib->overlay_mode), FALSE); gtk_widget_set_visible(GTK_WIDGET(lib->overlay_id_box), FALSE); gtk_widget_set_visible(GTK_WIDGET(lib->overlay_splitline), FALSE); gtk_widget_set_no_show_all(GTK_WIDGET(lib->overlay_mode), TRUE); gtk_widget_set_no_show_all(GTK_WIDGET(lib->overlay_id_box), TRUE); gtk_widget_set_no_show_all(GTK_WIDGET(lib->overlay_splitline), TRUE); guides_presets_set_visibility(lib, 0); }
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 force_lcms2 = 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 = 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(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 || force_lcms2 || 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_LabA_FLT, d->output, TYPE_RGBA_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_LabA_FLT, d->output, TYPE_RGBA_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; }
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); }
int write_image(dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int imgid, int num, int total, dt_dev_pixelpipe_t *pipe) { const dt_imageio_tiff_t *d = (dt_imageio_tiff_t *)d_tmp; uint8_t *profile = NULL; uint32_t profile_len = 0; TIFF *tif = NULL; void *rowdata = NULL; int rc = 1; // default to error if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, over_type, over_filename)->profile; cmsSaveProfileToMem(out_profile, 0, &profile_len); if(profile_len > 0) { profile = malloc(profile_len); if(!profile) { rc = 1; goto exit; } cmsSaveProfileToMem(out_profile, profile, &profile_len); } } // Create little endian tiff image #ifdef _WIN32 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL); tif = TIFFOpenW(wfilename, "wl"); g_free(wfilename); #else tif = TIFFOpen(filename, "wl"); #endif if(!tif) { rc = 1; goto exit; } // http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf (dated 2002) // "A proprietary ZIP/Flate compression code (0x80b2) has been used by some" // "software vendors. This code should be considered obsolete. We recommend" // "that TIFF implementations recognize and read the obsolete code but only" // "write the official compression code (0x0008)." // http://www.awaresystems.be/imaging/tiff/tifftags/compression.html // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html if(d->compress == 1) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_NONE); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else if(d->compress == 2) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_HORIZONTAL); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else if(d->compress == 3) { TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE); if(d->bpp == 32) TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_FLOATINGPOINT); else TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)PREDICTOR_HORIZONTAL); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel); } else // (d->compress == 0) { TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); } TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16_t)FILLORDER_MSB2LSB); if(profile != NULL) { TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32_t)profile_len, profile); } TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16_t)3); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp); TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (uint16_t)(d->bpp == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT)); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)d->global.width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)d->global.height); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16_t)PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, (uint16_t)PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32_t)1); TIFFSetField(tif, TIFFTAG_ORIENTATION, (uint16_t)ORIENTATION_TOPLEFT); int resolution = dt_conf_get_int("metadata/resolution"); if(resolution > 0) { TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16_t)RESUNIT_INCH); } const size_t rowsize = (d->global.width * 3) * d->bpp / 8; if((rowdata = malloc(rowsize)) == NULL) { rc = 1; goto exit; } if(d->bpp == 32) { for(int y = 0; y < d->global.height; y++) { float *in = (float *)in_void + (size_t)4 * y * d->global.width; float *out = (float *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(float)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } else if(d->bpp == 16) { for(int y = 0; y < d->global.height; y++) { uint16_t *in = (uint16_t *)in_void + (size_t)4 * y * d->global.width; uint16_t *out = (uint16_t *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(uint16_t)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } else { for(int y = 0; y < d->global.height; y++) { uint8_t *in = (uint8_t *)in_void + (size_t)4 * y * d->global.width; uint8_t *out = (uint8_t *)rowdata; for(int x = 0; x < d->global.width; x++, in += 4, out += 3) { memcpy(out, in, 3 * sizeof(uint8_t)); } if(TIFFWriteScanline(tif, rowdata, y, 0) == -1) { rc = 1; goto exit; } } } // success rc = 0; exit: // close the file before adding exif data if(tif) { TIFFClose(tif); tif = NULL; } if(!rc && exif) { rc = dt_exif_write_blob(exif, exif_len, filename, d->compress > 0); // Until we get symbolic error status codes, if rc is 1, return 0 rc = (rc == 1) ? 0 : 1; } free(profile); profile = NULL; free(rowdata); rowdata = NULL; return rc; }