static inline float curve_scalar( const float x, const float g, const float sigma, const float shadows, const float highlights, const float clarity) { const float c = x-g; float val; // blend in via quadratic bezier if (c > 2*sigma) val = g + sigma + shadows * (c-sigma); else if(c < -2*sigma) val = g - sigma + highlights * (c+sigma); else if(c > 0.0f) { // shadow contrast const float t = CLAMPS(c / (2.0f*sigma), 0.0f, 1.0f); const float t2 = t * t; const float mt = 1.0f-t; val = g + sigma * 2.0f*mt*t + t2*(sigma + sigma*shadows); } else { // highlight contrast const float t = CLAMPS(-c / (2.0f*sigma), 0.0f, 1.0f); const float t2 = t * t; const float mt = 1.0f-t; val = g - sigma * 2.0f*mt*t + t2*(- sigma - sigma*highlights); } // midtone local contrast val += clarity * c * dt_fast_expf(-c*c/(2.0*sigma*sigma/3.0f)); return val; }
static inline float ll_laplacian( const float *const coarse, // coarse res gaussian const float *const fine, // fine res gaussian const int i, // fine index const int j, const int wd, // fine width const int ht) // fine height { const float c = ll_expand_gaussian(coarse, CLAMPS(i, 1, ((wd-1)&~1)-1), CLAMPS(j, 1, ((ht-1)&~1)-1), wd, ht); return fine[j*wd+i] - c; }
void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { const dt_iop_zonesystem_data_t *const d = (const dt_iop_zonesystem_data_t *const)piece->data; process_common_setup(self, piece, ivoid, ovoid, roi_in, roi_out); const int ch = piece->colors; const int size = d->params.size; #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) #endif for(int j = 0; j < roi_out->height; j++) { for(int i = 0; i < roi_out->width; i++) { /* remap lightness into zonemap and apply lightness */ const float *in = (float *)ivoid + ch * ((size_t)j * roi_out->width + i); float *out = (float *)ovoid + ch * ((size_t)j * roi_out->width + i); const int rz = CLAMPS(in[0] * d->rzscale, 0, size - 2); // zone index const float zs = ((rz > 0) ? (d->zonemap_offset[rz] / in[0]) : 0) + d->zonemap_scale[rz]; _mm_stream_ps(out, _mm_mul_ps(_mm_load_ps(in), _mm_set1_ps(zs))); } } _mm_sfence(); process_common_cleanup(self, piece, ivoid, ovoid, roi_in, roi_out); }
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { const dt_iop_zonesystem_data_t *const d = (const dt_iop_zonesystem_data_t *const)piece->data; process_common_setup(self, piece, ivoid, ovoid, roi_in, roi_out); const int ch = piece->colors; const int size = d->params.size; const float *const in = (const float *const)ivoid; float *const out = (float *const)ovoid; #ifdef _OPENMP #pragma omp parallel for SIMD() default(none) schedule(static) collapse(2) #endif for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch) { for(int c = 0; c < 3; c++) { /* remap lightness into zonemap and apply lightness */ const int rz = CLAMPS(in[k] * d->rzscale, 0, size - 2); // zone index const float zs = ((rz > 0) ? (d->zonemap_offset[rz] / in[k]) : 0) + d->zonemap_scale[rz]; const size_t p = (size_t)k + c; out[p] = in[p] * zs; } } process_common_cleanup(self, piece, ivoid, ovoid, roi_in, roi_out); }
static float lerp_lut(const float *const lut, const float v) { // TODO: check if optimization is worthwhile! const float ft = CLAMPS(v * (LUT_SAMPLES - 1), 0, LUT_SAMPLES - 1); const int t = ft < LUT_SAMPLES - 2 ? ft : LUT_SAMPLES - 2; const float f = ft - t; const float l1 = lut[t]; const float l2 = lut[t + 1]; return l1 * (1.0f - f) + l2 * f; }
int mouse_moved(dt_lib_module_t *self, double x, double y, double pressure, int which) { dt_lib_live_view_t *lib = (dt_lib_live_view_t *)self->data; int result = 0; if(lib->splitline_dragging) { const double width = lib->overlay_x1 - lib->overlay_x0; const double height = lib->overlay_y1 - lib->overlay_y0; // absolute coords to splitline position: lib->splitline_x = CLAMPS((x - lib->overlay_x0) / width, 0.0, 1.0); lib->splitline_y = CLAMPS((y - lib->overlay_y0) / height, 0.0, 1.0); result = 1; } return result; }
static float dt_lut_lookup_2d_1c(const float *grain_lut, const float x, const float y) { const float _x = CLAMPS((x + 0.5) * (GRAIN_LUT_SIZE - 1), 0, GRAIN_LUT_SIZE - 1); const float _y = CLAMPS(y * (GRAIN_LUT_SIZE - 1), 0, GRAIN_LUT_SIZE - 1); const int _x0 = _x < GRAIN_LUT_SIZE - 2 ? _x : GRAIN_LUT_SIZE - 2; const int _y0 = _y < GRAIN_LUT_SIZE - 2 ? _y : GRAIN_LUT_SIZE - 2; const int _x1 = _x0 + 1; const int _y1 = _y0 + 1; const float x_diff = _x - _x0; const float y_diff = _y - _y0; const float l00 = grain_lut[_y0 * GRAIN_LUT_SIZE + _x0]; const float l01 = grain_lut[_y0 * GRAIN_LUT_SIZE + _x1]; const float l10 = grain_lut[_y1 * GRAIN_LUT_SIZE + _x0]; const float l11 = grain_lut[_y1 * GRAIN_LUT_SIZE + _x1]; const float xy0 = (1.0 - y_diff) * l00 + l10 * y_diff; const float xy1 = (1.0 - y_diff) * l01 + l11 * y_diff; return xy0 * (1.0f - x_diff) + xy1 * x_diff; }
static float envelope(const float L) { const float x = CLAMPS(L / 100.0f, 0.0f, 1.0f); // const float alpha = 2.0f; const float beta = 0.6f; if(x < beta) { // return 1.0f-fabsf(x/beta-1.0f)^2 const float tmp = fabsf(x / beta - 1.0f); return 1.0f - tmp * tmp; } else { const float tmp1 = (1.0f - x) / (1.0f - beta); const float tmp2 = tmp1 * tmp1; const float tmp3 = tmp2 * tmp1; return 3.0f * tmp2 - 2.0f * tmp3; } }
const char * dt_image_film_roll_name(const char *path) { const char *folder = path + strlen(path); int numparts = CLAMPS(dt_conf_get_int("show_folder_levels"), 1, 5); int count = 0; if (numparts < 1) numparts = 1; while (folder > path) { if (*folder == '/') if (++count >= numparts) { ++folder; break; } --folder; } return folder; }
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 8G just still reasonable. size_t max_mem = CLAMPS(dt_conf_get_int64("cache_memory"), 100u<<20, ((uint64_t)8)<<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--) { // clear stats: cache->mip[k].stats_requests = 0; cache->mip[k].stats_near_match = 0; cache->mip[k].stats_misses = 0; cache->mip[k].stats_fetches = 0; cache->mip[k].stats_standin = 0; // 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. // XXX this needs adjustment for video mode (more full-res thumbs for replay) // TODO: collect hit/miss stats and auto-adjust to user browsing behaviour // TODO: can #prefetches be collected this way, too? const size_t max_mem2 = MAX(0, (k == 0) ? (max_mem) : (max_mem/(k+4))); uint32_t thumbnails = MAX(2, nearest_power_of_two((uint32_t)((double)max_mem2/cache->mip[k].buffer_size))); while(thumbnails > parallel && (size_t)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); }
// open a raw file, libraw path: dt_imageio_retval_t dt_imageio_open_raw( dt_image_t *img, const char *filename, dt_mipmap_cache_allocator_t a) { if(!img->exif_inited) (void) dt_exif_read(img, filename); int ret; libraw_data_t *raw = libraw_init(0); libraw_processed_image_t *image = NULL; raw->params.half_size = 0; /* dcraw -h */ raw->params.use_camera_wb = 0; raw->params.use_auto_wb = 0; raw->params.med_passes = 0;//img->raw_params.med_passes; raw->params.no_auto_bright = 1; // raw->params.filtering_mode |= LIBRAW_FILTERING_NOBLACKS; // raw->params.document_mode = 2; // no color scaling, no black, no max, no wb..? raw->params.document_mode = 2; // color scaling (clip,wb,max) and black point, but no demosaic raw->params.output_color = 0; raw->params.output_bps = 16; raw->params.user_flip = -1; // -1 means: use orientation from raw raw->params.gamm[0] = 1.0; raw->params.gamm[1] = 1.0; // raw->params.user_qual = img->raw_params.demosaic_method; // 3: AHD, 2: PPG, 1: VNG raw->params.user_qual = 0; // raw->params.four_color_rgb = img->raw_params.four_color_rgb; raw->params.four_color_rgb = 0; raw->params.use_camera_matrix = 0; raw->params.green_matching = 0; raw->params.highlight = 1; raw->params.threshold = 0; // raw->params.auto_bright_thr = img->raw_auto_bright_threshold; // raw->params.amaze_ca_refine = 0; raw->params.fbdd_noiserd = 0; ret = libraw_open_file(raw, filename); HANDLE_ERRORS(ret, 0); raw->params.user_qual = 0; raw->params.half_size = 0; ret = libraw_unpack(raw); // img->black = raw->color.black/65535.0; // img->maximum = raw->color.maximum/65535.0; img->bpp = sizeof(uint16_t); // printf("black, max: %d %d %f %f\n", raw->color.black, raw->color.maximum, img->black, img->maximum); HANDLE_ERRORS(ret, 1); ret = libraw_dcraw_process(raw); // ret = libraw_dcraw_document_mode_processing(raw); HANDLE_ERRORS(ret, 1); image = libraw_dcraw_make_mem_image(raw, &ret); HANDLE_ERRORS(ret, 1); // fallback for broken exif read in case of phase one H25 if(!strncmp(img->exif_maker, "Phase One", 9)) img->orientation = raw->sizes.flip; // filters seem only ever to take a useful value after unpack/process img->filters = raw->idata.filters; img->width = (img->orientation & 4) ? raw->sizes.height : raw->sizes.width; img->height = (img->orientation & 4) ? raw->sizes.width : raw->sizes.height; img->exif_iso = raw->other.iso_speed; img->exif_exposure = raw->other.shutter; img->exif_aperture = raw->other.aperture; img->exif_focal_length = raw->other.focal_len; g_strlcpy(img->exif_maker, raw->idata.make, sizeof(img->exif_maker)); img->exif_maker[sizeof(img->exif_maker) - 1] = 0x0; g_strlcpy(img->exif_model, raw->idata.model, sizeof(img->exif_model)); img->exif_model[sizeof(img->exif_model) - 1] = 0x0; dt_gettime_t(img->exif_datetime_taken, raw->other.timestamp); void *buf = dt_mipmap_cache_alloc(img, DT_MIPMAP_FULL, a); if(!buf) { libraw_recycle(raw); libraw_close(raw); free(image); return DT_IMAGEIO_CACHE_FULL; } #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(img, image, raw, buf) #endif for(int k=0; k<img->width*img->height; k++) ((uint16_t *)buf)[k] = CLAMPS((((uint16_t *)image->data)[k] - raw->color.black)*65535.0f/(float)(raw->color.maximum - raw->color.black), 0, 0xffff); // clean up raw stuff. libraw_recycle(raw); libraw_close(raw); free(image); raw = NULL; image = NULL; img->flags &= ~DT_IMAGE_LDR; img->flags &= ~DT_IMAGE_HDR; img->flags |= DT_IMAGE_RAW; return DT_IMAGEIO_OK; }
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *ivoid, void *ovoid, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { float *in; float *out; dt_iop_zonesystem_gui_data_t *g = NULL; dt_iop_zonesystem_data_t *data = (dt_iop_zonesystem_data_t*)piece->data; guchar *buffer = NULL; if( self->dev->gui_attached && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW ) { g = (dt_iop_zonesystem_gui_data_t *)self->gui_data; dt_pthread_mutex_lock(&g->lock); if(g->preview_buffer) g_free (g->preview_buffer); buffer = g->preview_buffer = g_malloc (roi_in->width*roi_in->height); g->preview_width=roi_out->width; g->preview_height=roi_out->height; } /* calculate zonemap */ const int size = data->size; float zonemap[MAX_ZONE_SYSTEM_SIZE]= {-1}; _iop_zonesystem_calculate_zonemap (data, zonemap); const int ch = piece->colors; /* if gui and have buffer lets gaussblur and fill buffer with zone indexes */ if( self->dev->gui_attached && g && buffer) { /* setup gaussian kernel */ const int radius = 8; const int rad = MIN(radius, ceilf(radius * roi_in->scale / piece->iscale)); const int wd = 2*rad+1; float mat[wd*wd]; float *m; const float sigma2 = (2.5*2.5)*(radius*roi_in->scale/piece->iscale)*(radius*roi_in->scale/piece->iscale); float weight = 0.0f; memset(mat, 0, wd*wd*sizeof(float)); m = mat; for(int l=-rad; l<=rad; l++) for(int k=-rad; k<=rad; k++,m++) weight += *m = expf(- (l*l + k*k)/(2.f*sigma2)); m = mat; for(int l=-rad; l<=rad; l++) for(int k=-rad; k<=rad; k++,m++) *m /= weight; /* gauss blur the L channel */ #ifdef _OPENMP #pragma omp parallel for default(none) private(in, out, m) shared(mat, ivoid, ovoid, roi_out, roi_in) schedule(static) #endif for(int j=rad; j<roi_out->height-rad; j++) { in = ((float *)ivoid) + ch*(j*roi_in->width + rad); out = ((float *)ovoid) + ch*(j*roi_out->width + rad); for(int i=rad; i<roi_out->width-rad; i++) { for(int c=0; c<3; c++) out[c] = 0.0f; float sum = 0.0; m = mat; for(int l=-rad; l<=rad; l++) { float *inrow = in + ch*(l*roi_in->width-rad); for(int k=-rad; k<=rad; k++,inrow+=ch,m++) sum += *m * inrow[0]; } out[0] = sum; out += ch; in += ch; } } /* create zonemap preview */ // in = (float *)ivoid; out = (float *)ovoid; #ifdef _OPENMP #pragma omp parallel for default(none) shared(roi_out,out,buffer,g,zonemap) schedule(static) #endif for (int k=0; k<roi_out->width*roi_out->height; k++) { buffer[k] = _iop_zonesystem_zone_index_from_lightness (out[ch*k]/100.0f, zonemap, size); } dt_pthread_mutex_unlock(&g->lock); } /* process the image */ in = (float *)ivoid; out = (float *)ovoid; const float rzscale = (size-1)/100.0f; float zonemap_offset[MAX_ZONE_SYSTEM_SIZE]= {-1}; float zonemap_scale[MAX_ZONE_SYSTEM_SIZE]= {-1}; // precompute scale and offset for (int k=0; k < size-1; k++) zonemap_scale[k] = (zonemap[k+1]-zonemap[k])*(size-1); for (int k=0; k < size-1; k++) zonemap_offset[k] = 100.0f * ((k+1)*zonemap[k] - k*zonemap[k+1]) ; #ifdef _OPENMP #pragma omp parallel for default(none) shared(roi_out, in, out, zonemap_scale,zonemap_offset) schedule(static) #endif for (int j=0; j<roi_out->height; j++) for (int i=0; i<roi_out->width; i++) { /* remap lightness into zonemap and apply lightness */ const float *inp = in + ch*(j*roi_out->width+i); float *outp = out + ch*(j*roi_out->width+i); const int rz = CLAMPS(inp[0]*rzscale, 0, size-2); // zone index const float zs = ((rz > 0) ? (zonemap_offset[rz]/inp[0]) : 0) + zonemap_scale[rz]; _mm_stream_ps(outp,_mm_mul_ps(_mm_load_ps(inp),_mm_set1_ps(zs))); } _mm_sfence(); if(piece->pipe->mask_display) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height); }
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *ivoid, void *ovoid, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { float *in; float *out; dt_iop_zonesystem_gui_data_t *g = NULL; dt_iop_zonesystem_data_t *data = (dt_iop_zonesystem_data_t*)piece->data; const int width = roi_out->width; const int height = roi_out->height; guchar *in_buffer = NULL, *out_buffer = NULL; if( self->dev->gui_attached && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW ) { g = (dt_iop_zonesystem_gui_data_t *)self->gui_data; dt_pthread_mutex_lock(&g->lock); if(g->in_preview_buffer) g_free (g->in_preview_buffer); if(g->out_preview_buffer) g_free (g->out_preview_buffer); in_buffer = g->in_preview_buffer = g_malloc ((size_t)width*height); out_buffer = g->out_preview_buffer = g_malloc ((size_t)width*height); g->preview_width = width; g->preview_height = height; dt_pthread_mutex_unlock(&g->lock); } /* calculate zonemap */ const int size = data->size; float zonemap[MAX_ZONE_SYSTEM_SIZE]= {-1}; _iop_zonesystem_calculate_zonemap (data, zonemap); const int ch = piece->colors; /* process the image */ in = (float *)ivoid; out = (float *)ovoid; const float rzscale = (size-1)/100.0f; float zonemap_offset[MAX_ZONE_SYSTEM_SIZE]= {-1}; float zonemap_scale[MAX_ZONE_SYSTEM_SIZE]= {-1}; // precompute scale and offset for (int k=0; k < size-1; k++) zonemap_scale[k] = (zonemap[k+1]-zonemap[k])*(size-1); for (int k=0; k < size-1; k++) zonemap_offset[k] = 100.0f * ((k+1)*zonemap[k] - k*zonemap[k+1]) ; #ifdef _OPENMP #pragma omp parallel for default(none) shared(in, out, zonemap_scale,zonemap_offset) schedule(static) #endif for (int j=0; j<height; j++) for (int i=0; i<width; i++) { /* remap lightness into zonemap and apply lightness */ const float *inp = in + ch*((size_t)j*width+i); float *outp = out + ch*((size_t)j*width+i); const int rz = CLAMPS(inp[0]*rzscale, 0, size-2); // zone index const float zs = ((rz > 0) ? (zonemap_offset[rz]/inp[0]) : 0) + zonemap_scale[rz]; _mm_stream_ps(outp,_mm_mul_ps(_mm_load_ps(inp),_mm_set1_ps(zs))); } _mm_sfence(); if(piece->pipe->mask_display) dt_iop_alpha_copy(ivoid, ovoid, width, height); /* if gui and have buffer lets gaussblur and fill buffer with zone indexes */ if( self->dev->gui_attached && g && in_buffer && out_buffer) { float Lmax[] = { 100.0f }; float Lmin[] = { 0.0f }; /* setup gaussian kernel */ const int radius = 8; const float sigma = 2.5*(radius*roi_in->scale/piece->iscale); dt_gaussian_t *gauss = dt_gaussian_init(width, height, 1, Lmax, Lmin, sigma, DT_IOP_GAUSSIAN_ZERO); float *tmp = g_malloc((size_t)width*height*sizeof(float)); if(gauss && tmp) { #ifdef _OPENMP #pragma omp parallel for default(none) shared(ivoid, tmp) schedule(static) #endif for(size_t k=0; k<(size_t)width*height; k++) tmp[k] = ((float *)ivoid)[ch*k]; dt_gaussian_blur(gauss, tmp, tmp); /* create zonemap preview for input */ dt_pthread_mutex_lock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp,in_buffer) schedule(static) #endif for (size_t k=0; k<(size_t)width*height; k++) { in_buffer[k] = CLAMPS(tmp[k]*(size-1)/100.0f, 0, size-2); } dt_pthread_mutex_unlock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(ovoid, tmp) schedule(static) #endif for(size_t k=0; k<(size_t)width*height; k++) tmp[k] = ((float *)ovoid)[ch*k]; dt_gaussian_blur(gauss, tmp, tmp); /* create zonemap preview for output */ dt_pthread_mutex_lock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp,out_buffer) schedule(static) #endif for (size_t k=0; k<(size_t)width*height; k++) { out_buffer[k] = CLAMPS(tmp[k]*(size-1)/100.0f, 0, size-2); } dt_pthread_mutex_unlock(&g->lock); } if (tmp) g_free(tmp); if (gauss) dt_gaussian_free(gauss); } }
void local_laplacian_internal( const float *const input, // input buffer in some Labx or yuvx format float *const out, // output buffer with colour const int wd, // width and const int ht, // height of the input buffer const float sigma, // user param: separate shadows/midtones/highlights const float shadows, // user param: lift shadows const float highlights, // user param: compress highlights const float clarity, // user param: increase clarity/local contrast const int use_sse2) // flag whether to use SSE version { #define max_levels 30 #define num_gamma 6 // don't divide by 2 more often than we can: const int num_levels = MIN(max_levels, 31-__builtin_clz(MIN(wd,ht))); const int max_supp = 1<<(num_levels-1); int w, h; float *padded[max_levels] = {0}; padded[0] = ll_pad_input(input, wd, ht, max_supp, &w, &h); // allocate pyramid pointers for padded input for(int l=1;l<num_levels;l++) padded[l] = dt_alloc_align(16, sizeof(float)*dl(w,l)*dl(h,l)); // allocate pyramid pointers for output float *output[max_levels] = {0}; for(int l=0;l<num_levels;l++) output[l] = dt_alloc_align(16, sizeof(float)*dl(w,l)*dl(h,l)); // create gauss pyramid of padded input, write coarse directly to output #if defined(__SSE2__) if(use_sse2) { for(int l=1;l<num_levels-1;l++) gauss_reduce_sse2(padded[l-1], padded[l], dl(w,l-1), dl(h,l-1)); gauss_reduce_sse2(padded[num_levels-2], output[num_levels-1], dl(w,num_levels-2), dl(h,num_levels-2)); } else #endif { for(int l=1;l<num_levels-1;l++) gauss_reduce(padded[l-1], padded[l], dl(w,l-1), dl(h,l-1)); gauss_reduce(padded[num_levels-2], output[num_levels-1], dl(w,num_levels-2), dl(h,num_levels-2)); } // evenly sample brightness [0,1]: float gamma[num_gamma] = {0.0f}; for(int k=0;k<num_gamma;k++) gamma[k] = (k+.5f)/(float)num_gamma; // for(int k=0;k<num_gamma;k++) gamma[k] = k/(num_gamma-1.0f); // allocate memory for intermediate laplacian pyramids float *buf[num_gamma][max_levels] = {{0}}; for(int k=0;k<num_gamma;k++) for(int l=0;l<num_levels;l++) buf[k][l] = dt_alloc_align(16, sizeof(float)*dl(w,l)*dl(h,l)); // the paper says remapping only level 3 not 0 does the trick, too // (but i really like the additional octave of sharpness we get, // willing to pay the cost). for(int k=0;k<num_gamma;k++) { // process images #if defined(__SSE2__) if(use_sse2) apply_curve_sse2(buf[k][0], padded[0], w, h, max_supp, gamma[k], sigma, shadows, highlights, clarity); else // brackets in next line needed for silly gcc warning: #endif {apply_curve(buf[k][0], padded[0], w, h, max_supp, gamma[k], sigma, shadows, highlights, clarity);} // create gaussian pyramids for(int l=1;l<num_levels;l++) #if defined(__SSE2__) if(use_sse2) gauss_reduce_sse2(buf[k][l-1], buf[k][l], dl(w,l-1), dl(h,l-1)); else #endif gauss_reduce(buf[k][l-1], buf[k][l], dl(w,l-1), dl(h,l-1)); } // assemble output pyramid coarse to fine for(int l=num_levels-2;l >= 0; l--) { const int pw = dl(w,l), ph = dl(h,l); gauss_expand(output[l+1], output[l], pw, ph); // go through all coefficients in the upsampled gauss buffer: #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) collapse(2) shared(w,h,buf,output,l,gamma,padded) #endif for(int j=0;j<ph;j++) for(int i=0;i<pw;i++) { const float v = padded[l][j*pw+i]; int hi = 1; for(;hi<num_gamma-1 && gamma[hi] <= v;hi++); int lo = hi-1; const float a = CLAMPS((v - gamma[lo])/(gamma[hi]-gamma[lo]), 0.0f, 1.0f); const float l0 = ll_laplacian(buf[lo][l+1], buf[lo][l], i, j, pw, ph); const float l1 = ll_laplacian(buf[hi][l+1], buf[hi][l], i, j, pw, ph); output[l][j*pw+i] += l0 * (1.0f-a) + l1 * a; // we could do this to save on memory (no need for finest buf[][]). // unfortunately it results in a quite noticable loss of sharpness, i think // the extra level is worth it. // else if(l == 0) // use finest scale from input to not amplify noise (and use less memory) // output[l][j*pw+i] += ll_laplacian(padded[l+1], padded[l], i, j, pw, ph); } } #ifdef _OPENMP #pragma omp parallel for default(none) schedule(dynamic) collapse(2) shared(w,output,buf) #endif for(int j=0;j<ht;j++) for(int i=0;i<wd;i++) { out[4*(j*wd+i)+0] = 100.0f * output[0][(j+max_supp)*w+max_supp+i]; // [0,1] -> L out[4*(j*wd+i)+1] = input[4*(j*wd+i)+1]; // copy original colour channels out[4*(j*wd+i)+2] = input[4*(j*wd+i)+2]; } // free all buffers! for(int l=0;l<max_levels;l++) { dt_free_align(padded[l]); dt_free_align(output[l]); for(int k = 0; k < num_gamma; k++) dt_free_align(buf[k][l]); } #undef num_levels #undef num_gamma }
int dt_init(int argc, char *argv[], const int init_gui) { // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #ifndef __APPLE__ _dt_sigsegv_old_handler = signal(SIGSEGV,&_dt_sigsegv_handler); #endif #ifndef __SSE2__ fprintf(stderr, "[dt_init] unfortunately we depend on SSE2 instructions at this time.\n"); fprintf(stderr, "[dt_init] please contribute a backport patch (or buy a newer processor).\n"); return 1; #endif #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD,128*1024) ; /* use mmap() for large allocations */ #endif bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.progname = argv[0]; // database gchar *dbfilename_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *images_to_load = NULL; for(int k=1; k<argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { printf("this is "PACKAGE_STRING"\ncopyright (c) 2009-2013 johannes hanika\n"PACKAGE_BUGREPORT"\n"); return 1; } else if(!strcmp(argv[k], "--library")) { dbfilename_from_command = argv[++k]; } else if(!strcmp(argv[k], "--datadir")) { datadir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--moduledir")) { moduledir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--tmpdir")) { tmpdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--configdir")) { configdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--cachedir")) { cachedir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--localedir")) { bindtextdomain (GETTEXT_PACKAGE, argv[++k]); } else if(argv[k][1] == 'd' && argc > k+1) { if(!strcmp(argv[k+1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k+1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k+1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k+1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k+1], "fswatch")) darktable.unmuted |= DT_DEBUG_FSWATCH; // fswatch module else if(!strcmp(argv[k+1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k+1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k+1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k+1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k+1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k+1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k+1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k+1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else return usage(argv[0]); k ++; } else if(argv[k][1] == 't' && argc > k+1) { darktable.num_openmp_threads = CLAMP(atol(argv[k+1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k ++; } } #ifndef MAC_INTEGRATION else { images_to_load = g_slist_append(images_to_load, argv[k]); } #endif } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { printf(_("ERROR : invalid temporary directory : %s\n"),darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); #endif // does not work, as gtk is not inited yet. // even if it were, it's a super bad idea to invoke gtk stuff from // a signal handler. /* check cput caps */ // dt_check_cpu(argc,argv); #ifdef HAVE_GEGL char geglpath[DT_MAX_PATH_LEN]; char datadir[DT_MAX_PATH_LEN]; dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN); snprintf(geglpath, DT_MAX_PATH_LEN, "%s/gegl:/usr/lib/gegl-0.0", datadir); (void)setenv("GEGL_PATH", geglpath, 1); gegl_init(&argc, &argv); #endif // thread-safe init: dt_exif_init(); char datadir[DT_MAX_PATH_LEN]; dt_loc_get_user_config_dir (datadir,DT_MAX_PATH_LEN); char filename[DT_MAX_PATH_LEN]; snprintf(filename, DT_MAX_PATH_LEN, "%s/darktablerc", datadir); // intialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)malloc(sizeof(dt_conf_t)); memset(darktable.conf, 0, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, filename); // set the interface language const gchar* lang = dt_conf_get_string("ui_last/gui_language"); if(lang != NULL && lang[0] != '\0') { if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); } // initialize the database darktable.db = dt_database_init(dbfilename_from_command); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(dt_database_get_already_locked(darktable.db)) { // send the images to the other instance via dbus if(images_to_load) { GSList *p = images_to_load; // get a connection! GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION,NULL, NULL); while (p != NULL) { // make the filename absolute ... gchar *filename = dt_make_path_absolute((gchar*)p->data); if(filename == NULL) continue; // ... and send it to the running instance of darktable g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new ("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); p = g_slist_next(p); g_free(filename); } g_slist_free(images_to_load); g_object_unref(connection); } return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Initialize the filesystem watcher darktable.fswatch=dt_fswatch_new(); #ifdef HAVE_GPHOTO2 // Initialize the camera control darktable.camctl=dt_camctl_new(); #endif // get max lighttable thumbnail size: darktable.thumbnail_width = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_width"), 200, 3000); darktable.thumbnail_height = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_height"), 200, 3000); // and make sure it can be mip-mapped all the way from mip4 to mip0 darktable.thumbnail_width /= 16; darktable.thumbnail_width *= 16; darktable.thumbnail_height /= 16; darktable.thumbnail_height *= 16; // Initialize the password storage engine darktable.pwstorage=dt_pwstorage_new(); // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)malloc(sizeof(dt_control_t)); memset(darktable.control, 0, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else { // this is in memory, so schema can't exist yet. if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) { dt_control_create_database_schema(); dt_gui_presets_init(); // also init preset db schema. } darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection_listeners = NULL; darktable.collection = dt_collection_new(NULL); /* initialize sellection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); #endif darktable.opencl = (dt_opencl_t *)malloc(sizeof(dt_opencl_t)); memset(darktable.opencl, 0, sizeof(dt_opencl_t)); dt_opencl_init(darktable.opencl, argc, argv); darktable.blendop = (dt_blendop_t *)malloc(sizeof(dt_blendop_t)); memset(darktable.blendop, 0, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)malloc(sizeof(dt_points_t)); memset(darktable.points, 0, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)malloc(sizeof(dt_image_cache_t)); memset(darktable.image_cache, 0, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)malloc(sizeof(dt_mipmap_cache_t)); memset(darktable.mipmap_cache, 0, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)malloc(sizeof(dt_gui_gtk_t)); memset(darktable.gui,0,sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui, argc, argv)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)malloc(sizeof(dt_view_manager_t)); memset(darktable.view_manager, 0, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { darktable.lib = (dt_lib_t *)malloc(sizeof(dt_lib_t)); memset(darktable.lib, 0, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); g_strlcpy(darktable.control->global_settings.dbname, filename, 512); // overwrite if relocated. } darktable.imageio = (dt_imageio_t *)malloc(sizeof(dt_imageio_t)); memset(darktable.imageio, 0, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); if(init_gui) { // Loading the keybindings char keyfile[DT_MAX_PATH_LEN]; // First dump the default keymapping snprintf(keyfile, DT_MAX_PATH_LEN, "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, DT_MAX_PATH_LEN, "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // initialize undo struct darktable.undo = dt_undo_init(); // load image(s) specified on cmdline int id = 0; if(images_to_load) { // If only one image is listed, attempt to load it in darkroom gboolean load_in_dr = (g_slist_next(images_to_load) == NULL); GSList *p = images_to_load; while (p != NULL) { // don't put these function calls into MAX(), the macro will evaluate // it twice (and happily deadlock, in this particular case) int newid = dt_load_from_string((gchar*)p->data, load_in_dr); id = MAX(id, newid); p = g_slist_next(p); } if (!load_in_dr || id == 0) dt_ctl_switch_mode_to(DT_LIBRARY); g_slist_free(images_to_load); } else dt_ctl_switch_mode_to(DT_LIBRARY); } /* start the indexer background job */ dt_control_start_indexer(); if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } return 0; }
int dt_init(int argc, char *argv[], const int init_gui,lua_State *L) { #ifndef __WIN32__ if(getuid() == 0 || geteuid() == 0) printf("WARNING: either your user id or the effective user id are 0. are you running darktable as root?\n"); #endif // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #if !defined __APPLE__ && !defined __WIN32__ _dt_sigsegv_old_handler = signal(SIGSEGV,&_dt_sigsegv_handler); #endif #ifndef __GNUC_PREREQ // on OSX, gcc-4.6 and clang chokes if this is not here. #if defined __GNUC__ && defined __GNUC_MINOR__ # define __GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define __GNUC_PREREQ(maj, min) 0 #endif #endif #ifndef __has_builtin // http://clang.llvm.org/docs/LanguageExtensions.html#feature-checking-macros #define __has_builtin(x) false #endif #ifndef __SSE3__ #error "Unfortunately we depend on SSE3 instructions at this time." #error "Please contribute a backport patch (or buy a newer processor)." #else #if (__GNUC_PREREQ(4,8) || __has_builtin(__builtin_cpu_supports)) //FIXME: check will work only in GCC 4.8+ !!! implement manual cpuid check !!! //NOTE: _may_i_use_cpu_feature() looks better, but only avaliable in ICC if (!__builtin_cpu_supports("sse3")) { fprintf(stderr, "[dt_init] unfortunately we depend on SSE3 instructions at this time.\n"); fprintf(stderr, "[dt_init] please contribute a backport patch (or buy a newer processor).\n"); return 1; } #else //FIXME: no way to check for SSE3 in runtime, implement manual cpuid check !!! #endif #endif #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD,128*1024) ; /* use mmap() for large allocations */ #endif // we have to have our share dir in XDG_DATA_DIRS, // otherwise GTK+ won't find our logo for the about screen (and maybe other things) { const gchar *xdg_data_dirs = g_getenv("XDG_DATA_DIRS"); gchar *new_xdg_data_dirs = NULL; gboolean set_env = TRUE; if(xdg_data_dirs != NULL && *xdg_data_dirs != '\0') { // check if DARKTABLE_SHAREDIR is already in there gboolean found = FALSE; gchar **tokens = g_strsplit(xdg_data_dirs, ":", 0); // xdg_data_dirs is neither NULL nor empty => tokens != NULL for(char **iter = tokens; *iter != NULL; iter++) if(!strcmp(DARKTABLE_SHAREDIR, *iter)) { found = TRUE; break; } g_strfreev(tokens); if(found) set_env = FALSE; else new_xdg_data_dirs = g_strjoin(":", DARKTABLE_SHAREDIR, xdg_data_dirs, NULL); } else { // see http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html for a reason to use those as a default if(!g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share/") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share/")) new_xdg_data_dirs = g_strdup("/usr/local/share/:/usr/share/"); else new_xdg_data_dirs = g_strdup_printf("%s:/usr/local/share/:/usr/share/", DARKTABLE_SHAREDIR); } if(set_env) g_setenv("XDG_DATA_DIRS", new_xdg_data_dirs, 1); g_free(new_xdg_data_dirs); } setlocale(LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.progname = argv[0]; // database gchar *dbfilename_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; #ifdef USE_LUA char *lua_command = NULL; #endif darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *images_to_load = NULL, *config_override = NULL; for(int k=1; k<argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { printf("this is "PACKAGE_STRING"\ncopyright (c) 2009-2014 johannes hanika\n"PACKAGE_BUGREPORT"\n" #ifdef _OPENMP "OpenMP support enabled\n" #else "OpenMP support disabled\n" #endif ); return 1; } else if(!strcmp(argv[k], "--library")) { dbfilename_from_command = argv[++k]; } else if(!strcmp(argv[k], "--datadir")) { datadir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--moduledir")) { moduledir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--tmpdir")) { tmpdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--configdir")) { configdir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--cachedir")) { cachedir_from_command = argv[++k]; } else if(!strcmp(argv[k], "--localedir")) { bindtextdomain (GETTEXT_PACKAGE, argv[++k]); } else if(argv[k][1] == 'd' && argc > k+1) { if(!strcmp(argv[k+1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k+1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k+1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k+1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k+1], "fswatch")) darktable.unmuted |= DT_DEBUG_FSWATCH; // fswatch module else if(!strcmp(argv[k+1], "input")) darktable.unmuted |= DT_DEBUG_INPUT; // input devices else if(!strcmp(argv[k+1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k+1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k+1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k+1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k+1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k+1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k+1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k+1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else if(!strcmp(argv[k+1], "masks")) darktable.unmuted |= DT_DEBUG_MASKS; // masks related stuff. else if(!strcmp(argv[k+1], "lua")) darktable.unmuted |= DT_DEBUG_LUA; // lua errors are reported on console else return usage(argv[0]); k ++; } else if(argv[k][1] == 't' && argc > k+1) { darktable.num_openmp_threads = CLAMP(atol(argv[k+1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k ++; } else if(!strcmp(argv[k], "--conf")) { gchar *keyval = g_strdup(argv[++k]), *c = keyval; gchar *end = keyval + strlen(keyval); while(*c != '=' && c < end) c++; if(*c == '=' && *(c+1) != '\0') { *c++ = '\0'; dt_conf_string_entry_t *entry = (dt_conf_string_entry_t*)g_malloc(sizeof(dt_conf_string_entry_t)); entry->key = g_strdup(keyval); entry->value = g_strdup(c); config_override = g_slist_append(config_override, entry); } g_free(keyval); } else if(!strcmp(argv[k], "--luacmd")) { #ifdef USE_LUA lua_command = argv[++k]; #else ++k; #endif } } #ifndef MAC_INTEGRATION else { images_to_load = g_slist_append(images_to_load, argv[k]); } #endif } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { printf(_("ERROR : invalid temporary directory : %s\n"),darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); #endif // does not work, as gtk is not inited yet. // even if it were, it's a super bad idea to invoke gtk stuff from // a signal handler. /* check cput caps */ // dt_check_cpu(argc,argv); #ifdef HAVE_GEGL char geglpath[PATH_MAX]; char datadir[PATH_MAX]; dt_loc_get_datadir(datadir, sizeof(datadir)); snprintf(geglpath, sizeof(geglpath), "%s/gegl:/usr/lib/gegl-0.0", datadir); (void)setenv("GEGL_PATH", geglpath, 1); gegl_init(&argc, &argv); #endif #ifdef USE_LUA dt_lua_init_early(L); #endif // thread-safe init: dt_exif_init(); char datadir[PATH_MAX]; dt_loc_get_user_config_dir (datadir, sizeof(datadir)); char filename[PATH_MAX]; snprintf(filename, sizeof(filename), "%s/darktablerc", datadir); // initialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, filename, config_override); g_slist_free_full(config_override, g_free); // set the interface language const gchar* lang = dt_conf_get_string("ui_last/gui_language"); // we may not g_free 'lang' since it is owned by setlocale afterwards if(lang != NULL && lang[0] != '\0') { if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); } // initialize the database darktable.db = dt_database_init(dbfilename_from_command); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(!dt_database_get_lock_acquired(darktable.db)) { // send the images to the other instance via dbus if(images_to_load) { GSList *p = images_to_load; // get a connection! GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION,NULL, NULL); while (p != NULL) { // make the filename absolute ... gchar *filename = dt_make_path_absolute((gchar*)p->data); if(filename == NULL) continue; // ... and send it to the running instance of darktable g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new ("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); p = g_slist_next(p); g_free(filename); } g_slist_free(images_to_load); g_object_unref(connection); } return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Make sure that the database and xmp files are in sync before starting the fswatch. // We need conf and db to be up and running for that which is the case here. // FIXME: is this also useful in non-gui mode? GList *changed_xmp_files = NULL; if(init_gui && dt_conf_get_bool("run_crawler_on_start")) { changed_xmp_files = dt_control_crawler_run(); } // Initialize the filesystem watcher darktable.fswatch=dt_fswatch_new(); #ifdef HAVE_GPHOTO2 // Initialize the camera control darktable.camctl=dt_camctl_new(); #endif // get max lighttable thumbnail size: darktable.thumbnail_width = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_width"), 200, 3000); darktable.thumbnail_height = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_height"), 200, 3000); // and make sure it can be mip-mapped all the way from mip4 to mip0 darktable.thumbnail_width /= 16; darktable.thumbnail_width *= 16; darktable.thumbnail_height /= 16; darktable.thumbnail_height *= 16; // Initialize the password storage engine darktable.pwstorage=dt_pwstorage_new(); // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)calloc(1, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else { if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) dt_gui_presets_init(); // init preset db schema. darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection_listeners = NULL; darktable.collection = dt_collection_new(NULL); /* initialize selection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); #endif darktable.opencl = (dt_opencl_t *)calloc(1, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, argc, argv); #endif darktable.blendop = (dt_blendop_t *)calloc(1, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)calloc(1, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)calloc(1, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)calloc(1, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)calloc(1, sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui, argc, argv)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)calloc(1, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); darktable.imageio = (dt_imageio_t *)calloc(1, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { darktable.lib = (dt_lib_t *)calloc(1, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); } if(init_gui) { // Loading the keybindings char keyfile[PATH_MAX]; // First dump the default keymapping snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // initialize undo struct darktable.undo = dt_undo_init(); // load image(s) specified on cmdline int id = 0; if(images_to_load) { // If only one image is listed, attempt to load it in darkroom gboolean load_in_dr = (g_slist_next(images_to_load) == NULL); GSList *p = images_to_load; while (p != NULL) { // don't put these function calls into MAX(), the macro will evaluate // it twice (and happily deadlock, in this particular case) int newid = dt_load_from_string((gchar*)p->data, load_in_dr); id = MAX(id, newid); p = g_slist_next(p); } if (!load_in_dr || id == 0) dt_ctl_switch_mode_to(DT_LIBRARY); g_slist_free(images_to_load); } else dt_ctl_switch_mode_to(DT_LIBRARY); } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } dt_image_local_copy_synch(); /* init lua last, since it's user made stuff it must be in the real environment */ #ifdef USE_LUA dt_lua_init(darktable.lua_state.state,lua_command); #endif // last but not least construct the popup that asks the user about images whose xmp files are newer than the db entry if(init_gui && changed_xmp_files) { dt_control_crawler_show_image_list(changed_xmp_files); } return 0; }
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 = 0; if(d->inited) { curr_pos = dt_view_lighttable_get_position(darktable.view_manager); dt_conf_set_int("plugins/lighttable/recentcollect/pos0", curr_pos); } else { curr_pos = dt_conf_get_int("plugins/lighttable/recentcollect/pos0"); d->inited = 1; } uint32_t new_pos = 0; 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, 200, "plugins/lighttable/recentcollect/line%1d", k); gchar *line = dt_conf_get_string(confname); if(!line) continue; if(!strcmp(line, buf)) { snprintf(confname, 200, "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, 200, "plugins/lighttable/recentcollect/line%1d", k-1); gchar *line1 = dt_conf_get_string(confname); snprintf(confname, 200, "plugins/lighttable/recentcollect/pos%1d", k-1); uint32_t pos1 = dt_conf_get_int(confname); if(line1 && line1[0] != '\0') { snprintf(confname, 200, "plugins/lighttable/recentcollect/line%1d", k); dt_conf_set_string(confname, line1); snprintf(confname, 200, "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); } // update button descriptions: for(int k=0; k<NUM_LINES; k++) { char str[200] = {0}; char str_cut[200] = {0}; char str_pretty[200] = {0}; snprintf(confname, 200, "plugins/lighttable/recentcollect/line%1d", k); gchar *buf = dt_conf_get_string(confname); if(buf && buf[0] != '\0') { pretty_print(buf, str); g_free(buf); } g_object_set(G_OBJECT(d->item[k].button), "tooltip-text", str, (char *)NULL); const int cut = 45; if (g_utf8_strlen(str, -1) > cut) { g_utf8_strncpy(str_cut, str, cut); snprintf(str_pretty, 200, "%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); } 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); } dt_view_lighttable_set_position(darktable.view_manager, new_pos); }
static float color_filter(const float ai, const float bi, const float a, const float b, const float size) { return dt_fast_expf(-CLAMPS(((ai-a)*(ai-a) + (bi-b)*(bi-b))/(2.0*size), 0.0f, 1.0f)); }
static void process_common_cleanup(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { dt_iop_zonesystem_data_t *d = (dt_iop_zonesystem_data_t *)piece->data; dt_iop_zonesystem_gui_data_t *g = (dt_iop_zonesystem_gui_data_t *)self->gui_data; const int width = roi_out->width; const int height = roi_out->height; const int ch = piece->colors; const int size = d->params.size; if(piece->pipe->mask_display) dt_iop_alpha_copy(ivoid, ovoid, width, height); /* if gui and have buffer lets gaussblur and fill buffer with zone indexes */ if(self->dev->gui_attached && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW && g && g->in_preview_buffer && g->out_preview_buffer) { float Lmax[] = { 100.0f }; float Lmin[] = { 0.0f }; /* setup gaussian kernel */ const int radius = 8; const float sigma = 2.5 * (radius * roi_in->scale / piece->iscale); dt_gaussian_t *gauss = dt_gaussian_init(width, height, 1, Lmax, Lmin, sigma, DT_IOP_GAUSSIAN_ZERO); float *tmp = g_malloc_n((size_t)width * height, sizeof(float)); if(gauss && tmp) { #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp) schedule(static) #endif for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ivoid)[ch * k]; dt_gaussian_blur(gauss, tmp, tmp); /* create zonemap preview for input */ dt_pthread_mutex_lock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp, g) schedule(static) #endif for(size_t k = 0; k < (size_t)width * height; k++) { g->in_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2); } dt_pthread_mutex_unlock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp) schedule(static) #endif for(size_t k = 0; k < (size_t)width * height; k++) tmp[k] = ((float *)ovoid)[ch * k]; dt_gaussian_blur(gauss, tmp, tmp); /* create zonemap preview for output */ dt_pthread_mutex_lock(&g->lock); #ifdef _OPENMP #pragma omp parallel for default(none) shared(tmp, g) schedule(static) #endif for(size_t k = 0; k < (size_t)width * height; k++) { g->out_preview_buffer[k] = CLAMPS(tmp[k] * (size - 1) / 100.0f, 0, size - 2); } dt_pthread_mutex_unlock(&g->lock); } g_free(tmp); if(gauss) dt_gaussian_free(gauss); } }