int dt_masks_group_render(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *roi, float scale) { double start2 = dt_get_wtime(); if(!form) return 0; float *mask = *buffer; // we first reset the buffer to 0 memset(mask, 0, roi[2] * roi[3] * sizeof(float)); // we get the mask float *fm = NULL; int fx = roi[0], fy = roi[1], fw = roi[2], fh = roi[3]; if(!dt_masks_get_mask(module, piece, form, &fm, &fw, &fh, &fx, &fy)) return 0; if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks] get all masks took %0.04f sec\n", dt_get_wtime() - start2); start2 = dt_get_wtime(); // we don't want row which are outside the roi_out int fxx = fx * scale; int fww = fw * scale; int fyy = fy * scale; int fhh = fh * scale; if(fxx > roi[0] + roi[2]) { free(fm); return 1; } if(fxx < roi[0]) fww += fxx - roi[0], fxx = roi[0]; if(fww + fxx >= roi[0] + roi[2]) fww = roi[0] + roi[2] - fxx - 1; // we adjust to avoid rounding errors if(fyy / scale - fy < 0) fyy++, fhh--; if(fxx / scale - fx < 0) fxx++, fww--; if((fyy + fhh) / scale - fy >= fh) fhh--; if((fxx + fww) / scale - fx >= fw) fww--; // we apply the mask row by row for(int yy = fyy; yy < fyy + fhh; yy++) { if(yy < roi[1] || yy >= roi[1] + roi[3]) continue; for(int xx = fxx; xx < fxx + fww; xx++) { int a = (yy / scale - fy); int b = (xx / scale); mask[(yy - roi[1]) * roi[2] + xx - roi[0]] = fmaxf(mask[(yy - roi[1]) * roi[2] + xx - roi[0]], fm[a * fw + b - fx]); } } // we free the mask free(fm); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks] scale all masks took %0.04f sec\n", dt_get_wtime() - start2); return 1; }
void expose(dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { const int i = dt_conf_get_int("plugins/lighttable/layout"); const double start = dt_get_wtime(); // Let's show full preview if in that state... dt_library_t *lib = (dt_library_t *)self->data; int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if( lib->full_preview_id!=-1 ) { lib->image_over = DT_VIEW_DESERT; cairo_set_source_rgb (cr, .1, .1, .1); cairo_paint(cr); dt_view_image_expose(&(lib->image_over),mouse_over_id, cr, width, height, 1, pointerx, pointery); } else // we do pass on expose to manager or zoomable { switch(i) { case 1: // file manager expose_filemanager(self, cr, width, height, pointerx, pointery); break; default: // zoomable expose_zoomable(self, cr, width, height, pointerx, pointery); break; } } const double end = dt_get_wtime(); dt_print(DT_DEBUG_PERF, "[lighttable] expose took %0.04f sec\n", end-start); }
int32_t dt_control_run_job_res(dt_control_t *s, int32_t res) { assert(res < DT_CTL_WORKER_RESERVED && res >= 0); dt_job_t *j = NULL; dt_pthread_mutex_lock(&s->queue_mutex); if(s->new_res[res]) j = s->job_res + res; s->new_res[res] = 0; dt_pthread_mutex_unlock(&s->queue_mutex); if(!j) return -1; /* change state to running */ dt_pthread_mutex_lock (&j->wait_mutex); if (dt_control_job_get_state (j) == DT_JOB_STATE_QUEUED) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", res, dt_get_wtime()); dt_control_job_print(j); dt_print(DT_DEBUG_CONTROL, "\n"); _control_job_set_state (j,DT_JOB_STATE_RUNNING); /* execute job */ j->result = j->execute (j); _control_job_set_state (j,DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", res, dt_get_wtime()); dt_control_job_print(j); dt_print(DT_DEBUG_CONTROL, "\n"); } dt_pthread_mutex_unlock (&j->wait_mutex); return 0; }
static int32_t dt_control_run_job(dt_control_t *control) { _dt_job_t *job = dt_control_schedule_job(control); if(!job) return -1; /* change state to running */ dt_pthread_mutex_lock(&job->wait_mutex); if(dt_control_job_get_state(job) == DT_JOB_STATE_QUEUED) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); dt_control_job_set_state(job, DT_JOB_STATE_RUNNING); /* execute job */ job->result = job->execute(job); dt_control_job_set_state(job, DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); } /* free job */ dt_pthread_mutex_unlock(&job->wait_mutex); dt_control_job_dispose(job); return 0; }
int dt_masks_group_render_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float *buffer) { double start2 = dt_get_wtime(); if(!form) return 0; int ok = dt_masks_get_mask_roi(module, piece, form, roi, buffer); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks] render all masks took %0.04f sec\n", dt_get_wtime() - start2); return ok; }
// return read locked bucket, or NULL if it's not already there. // never attempt to allocate a new slot. dt_cache_entry_t *dt_cache_testget(dt_cache_t *cache, const uint32_t key, char mode) { gpointer orig_key, value; gboolean res; int result; double start = dt_get_wtime(); dt_pthread_mutex_lock(&cache->lock); res = g_hash_table_lookup_extended( cache->hashtable, GINT_TO_POINTER(key), &orig_key, &value); if(res) { dt_cache_entry_t *entry = (dt_cache_entry_t *)value; // lock the cache entry if(mode == 'w') result = dt_pthread_rwlock_trywrlock(&entry->lock); else result = dt_pthread_rwlock_tryrdlock(&entry->lock); if(result) { // need to give up mutex so other threads have a chance to get in between and // free the lock we're trying to acquire: dt_pthread_mutex_unlock(&cache->lock); return 0; } // bubble up in lru list: cache->lru = g_list_remove_link(cache->lru, entry->link); cache->lru = g_list_concat(cache->lru, entry->link); dt_pthread_mutex_unlock(&cache->lock); double end = dt_get_wtime(); if(end - start > 0.1) fprintf(stderr, "try+ wait time %.06fs mode %c \n", end - start, mode); if(mode == 'w') { assert(entry->data_size); ASAN_POISON_MEMORY_REGION(entry->data, entry->data_size); } // WARNING: do *NOT* unpoison here. it must be done by the caller! return entry; } dt_pthread_mutex_unlock(&cache->lock); double end = dt_get_wtime(); if(end - start > 0.1) fprintf(stderr, "try- wait time %.06fs\n", end - start); return 0; }
static void dt_control_job_execute(_dt_job_t *job) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); dt_control_job_set_state(job, DT_JOB_STATE_RUNNING); /* execute job */ job->result = job->execute(job); dt_control_job_set_state(job, DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); }
static int32_t dt_control_run_job(dt_control_t *control) { _dt_job_t *job = dt_control_schedule_job(control); if(!job) return -1; /* change state to running */ dt_pthread_mutex_lock(&job->wait_mutex); if(dt_control_job_get_state(job) == DT_JOB_STATE_QUEUED) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); dt_control_job_set_state(job, DT_JOB_STATE_RUNNING); /* execute job */ job->result = job->execute(job); dt_control_job_set_state(job, DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", DT_CTL_WORKER_RESERVED + dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); } dt_pthread_mutex_unlock(&job->wait_mutex); // remove the job from scheduled job array (for job deduping) dt_pthread_mutex_lock(&control->queue_mutex); control->job[dt_control_get_threadid()] = NULL; if(job->queue == DT_JOB_QUEUE_USER_EXPORT) control->export_scheduled = FALSE; dt_pthread_mutex_unlock(&control->queue_mutex); // and free it dt_control_job_dispose(job); return 0; }
static void *dt_camctl_camera_get_live_view(void* data) { dt_camctl_t *camctl = (dt_camctl_t*)data; dt_camera_t *cam = (dt_camera_t*)camctl->active_camera; dt_print (DT_DEBUG_CAMCTL,"[camera_control] live view thread started\n"); int frames= 0; double capture_time = dt_get_wtime(); while(cam->is_live_viewing == TRUE) { dt_pthread_mutex_lock(&cam->live_view_synch); // calculate FPS double current_time = dt_get_wtime(); if(current_time - capture_time >= 1.0) { // a second has passed dt_print(DT_DEBUG_CAMCTL, "%d fps\n", frames+1); frames = 0; capture_time = current_time; } else { // just increase the frame counter frames++; } _camctl_camera_job_t *job = g_malloc(sizeof(_camctl_camera_job_t)); job->type = _JOB_TYPE_EXECUTE_LIVE_VIEW; _camera_add_job( camctl, cam, job); g_usleep((1.0/15)*G_USEC_PER_SEC); // never update faster than 15 FPS. going too fast will result in too many redraws without a real benefit } dt_print (DT_DEBUG_CAMCTL,"[camera_control] live view thread stopped\n"); return NULL; }
static int32_t dt_control_run_job_res(dt_control_t *control, int32_t res) { if(((unsigned int)res) >= DT_CTL_WORKER_RESERVED) return -1; _dt_job_t *job = NULL; dt_pthread_mutex_lock(&control->queue_mutex); if(control->new_res[res]) { job = control->job_res[res]; control->job_res[res] = NULL; // this job belongs to us now, the queue may not touch it any longer } control->new_res[res] = 0; dt_pthread_mutex_unlock(&control->queue_mutex); if(!job) return -1; /* change state to running */ dt_pthread_mutex_lock(&job->wait_mutex); if(dt_control_job_get_state(job) == DT_JOB_STATE_QUEUED) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", res, dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); dt_control_job_set_state(job, DT_JOB_STATE_RUNNING); /* execute job */ job->result = job->execute(job); dt_control_job_set_state(job, DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", res, dt_get_wtime()); dt_control_job_print(job); dt_print(DT_DEBUG_CONTROL, "\n"); } dt_pthread_mutex_unlock(&job->wait_mutex); dt_control_job_dispose(job); return 0; }
static int dt_circle_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float **buffer) { double start2 = dt_get_wtime(); //we get the circle values dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *) (g_list_first(form->points)->data); //we create a buffer of mesh points for later interpolation. mainly in order to reduce memory footprint const int w = roi->width; const int h = roi->height; const int px = roi->x; const int py = roi->y; const float iscale = 1.0f/roi->scale; const int mesh = 4; const int mw = (w + mesh - 1) / mesh + 1; const int mh = (h + mesh - 1) / mesh + 1; float *points = malloc(mw*mh*2*sizeof(float)); if(points == NULL) return 0; #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points) #else #pragma omp parallel for shared(points) #endif #endif for (int j=0; j<mh; j++) for (int i=0; i<mw; i++) { points[(j*mw+i)*2] = (mesh*i+px)*iscale; points[(j*mw+i)*2+1] = (mesh*j+py)*iscale; } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle draw took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we back transform all these points if (!dt_dev_distort_backtransform_plus(module->dev,piece->pipe,0,module->priority,points,mw*mh)) { free(points); return 0; } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle transform took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we populate the buffer const int wi = piece->pipe->iwidth, hi=piece->pipe->iheight; const float center[2] = {circle->center[0]*wi, circle->center[1]*hi}; const float radius2 = circle->radius*MIN(wi,hi)*circle->radius*MIN(wi,hi); const float total2 = (circle->radius+circle->border)*MIN(wi,hi)*(circle->radius+circle->border)*MIN(wi,hi); #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points) #else #pragma omp parallel for shared(points) #endif #endif for (int i=0; i<mh; i++) for (int j=0; j<mw; j++) { float x = points[(i*mw+j)*2]; float y = points[(i*mw+j)*2+1]; float l2 = (x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1]); if (l2<radius2) points[(i*mw+j)*2] = 1.0f; else if (l2 < total2) { float f = (total2-l2)/(total2-radius2); points[(i*mw+j)*2] = f*f; } else points[(i*mw+j)*2] = 0.0f; } //we allocate the buffer *buffer = malloc(w*h*sizeof(float)); if(*buffer == NULL) { free(points); return 0; } memset(*buffer,0,w*h*sizeof(float)); //we fill the mask buffer by interpolation #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points,buffer) #else #pragma omp parallel for shared(points,buffer) #endif #endif for (int j=0; j<h; j++) { int jj = j % mesh; int mj = j / mesh; for (int i=0; i<w; i++) { int ii = i % mesh; int mi = i / mesh; (*buffer)[j*w+i] = ( points[(mj*mw+mi)*2] * (mesh-ii)*(mesh-jj) + points[(mj*mw+mi+1)*2] * ii*(mesh-jj) + points[((mj+1)*mw+mi)*2] * (mesh-ii)*jj + points[((mj+1)*mw+mi+1)*2] * ii*jj ) / (mesh*mesh); } } free(points); if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle fill took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); return 1; }
int32_t dt_control_run_job(dt_control_t *s) { dt_job_t *j=NULL,*bj=NULL; dt_pthread_mutex_lock(&s->queue_mutex); /* check if queue is empty */ if(g_list_length(s->queue) == 0) { dt_pthread_mutex_unlock(&s->queue_mutex); return -1; } /* go thru the queue and find one normal job and a background job that is up for execution.*/ time_t ts_now = time(NULL); GList *jobitem=g_list_first(s->queue); if(jobitem) do { dt_job_t *tj = jobitem->data; /* check if it's a scheduled job and is waiting to be executed */ if(!bj && (tj->ts_execute > tj->ts_added) && tj->ts_execute <= ts_now) bj = tj; else if ((tj->ts_execute < tj->ts_added) && !j) j = tj; /* if we got a normal job, and a background job, we are finished */ if(bj && j) break; } while ((jobitem = g_list_next(jobitem))); /* remove the found jobs from queue */ if (bj) s->queue = g_list_remove(s->queue, bj); if (j) s->queue = g_list_remove(s->queue, j); /* unlock the queue */ dt_pthread_mutex_unlock(&s->queue_mutex); /* push background job on reserved background worker */ if(bj) { dt_control_add_job_res(s,bj,DT_CTL_WORKER_7); g_free (bj); } /* don't continue if we don't have have a job to execute */ if(!j) return -1; /* change state to running */ dt_pthread_mutex_lock (&j->wait_mutex); if (dt_control_job_get_state (j) == DT_JOB_STATE_QUEUED) { dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", DT_CTL_WORKER_RESERVED+dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(j); dt_print(DT_DEBUG_CONTROL, "\n"); _control_job_set_state (j,DT_JOB_STATE_RUNNING); /* execute job */ j->result = j->execute (j); _control_job_set_state (j,DT_JOB_STATE_FINISHED); dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", DT_CTL_WORKER_RESERVED+dt_control_get_threadid(), dt_get_wtime()); dt_control_job_print(j); dt_print(DT_DEBUG_CONTROL, "\n"); /* free job */ dt_pthread_mutex_unlock (&j->wait_mutex); g_free(j); j = NULL; } if(j) dt_pthread_mutex_unlock (&j->wait_mutex); return 0; }
void enter(dt_view_t *self) { /* connect to ui pipe finished signal for redraw */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED,G_CALLBACK(_darkroom_ui_pipe_finish_signal_callback), (gpointer)self); dt_print(DT_DEBUG_CONTROL, "[run_job+] 11 %f in darkroom mode\n", dt_get_wtime()); dt_develop_t *dev = (dt_develop_t *)self->data; dev->gui_leaving = 0; dev->gui_module = NULL; select_this_image(dev->image_storage.id); DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FIT); DT_CTL_SET_GLOBAL(dev_zoom_x, 0); DT_CTL_SET_GLOBAL(dev_zoom_y, 0); DT_CTL_SET_GLOBAL(dev_closeup, 0); // take a copy of the image struct for convenience. dt_dev_load_image(darktable.develop, dev->image_storage.id); /* * Add view specific tool buttons */ /* create favorite plugin preset popup tool */ GtkWidget *favorite_presets = dtgtk_button_new(dtgtk_cairo_paint_presets, CPF_STYLE_FLAT|CPF_DO_NOT_USE_BORDER); g_object_set(G_OBJECT(favorite_presets), "tooltip-text", _("quick access to presets of your favorites"), (char *)NULL); g_signal_connect (G_OBJECT (favorite_presets), "clicked", G_CALLBACK (_darkroom_ui_favorite_presets_popupmenu), NULL); dt_view_manager_view_toolbox_add(darktable.view_manager, favorite_presets); /* add IOP modules to plugin list */ /* create quick styles popup menu tool */ GtkWidget *styles = dtgtk_button_new (dtgtk_cairo_paint_styles,CPF_STYLE_FLAT|CPF_DO_NOT_USE_BORDER); g_signal_connect (G_OBJECT (styles), "clicked", G_CALLBACK (_darkroom_ui_apply_style_popupmenu), NULL); g_object_set (G_OBJECT (styles), "tooltip-text", _("quick access for applying any of your styles"), (char *)NULL); dt_view_manager_view_toolbox_add(darktable.view_manager, styles); /* * add IOP modules to plugin list */ // avoid triggering of events before plugin is ready: darktable.gui->reset = 1; char option[1024]; GList *modules = g_list_last(dev->iop); while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); /* initialize gui if iop have one defined */ if (!dt_iop_is_hidden(module)) { module->gui_init(module); dt_iop_reload_defaults(module); /* add module to right panel */ GtkWidget *expander = dt_iop_gui_get_expander(module); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, expander); snprintf(option, 1024, "plugins/darkroom/%s/expanded", module->op); dt_iop_gui_set_expanded(module, dt_conf_get_bool(option)); } /* setup key accelerators */ module->accel_closures = NULL; if(module->connect_key_accels) module->connect_key_accels(module); dt_iop_connect_common_accels(module); modules = g_list_previous(modules); } darktable.gui->reset = 0; /* signal that darktable.develop is initialized and ready to be used */ dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_INITIALIZE); // synch gui and flag gegl pipe as dirty // this is done here and not in dt_read_history, as it would else be triggered before module->gui_init. dt_dev_pop_history_items(dev, dev->history_end); /* ensure that filmstrip shows current image */ dt_view_filmstrip_scroll_to_image(darktable.view_manager, dev->image_storage.id, FALSE); // switch on groups as they where last time: dt_dev_modulegroups_set(dev, dt_conf_get_int("plugins/darkroom/groups")); // make signals work again: darktable.gui->reset = 0; // get last active plugin: gchar *active_plugin = dt_conf_get_string("plugins/darkroom/active"); if(active_plugin) { GList *modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); if(!strcmp(module->op, active_plugin)) dt_iop_request_focus(module); modules = g_list_next(modules); } g_free(active_plugin); } // image should be there now. float zoom_x, zoom_y; dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, DT_ZOOM_FIT, 0, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); /* connect signal for filmstrip image activate */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE, G_CALLBACK(_view_darkroom_filmstrip_activate_callback), self); // prefetch next few from first selected image on. dt_view_filmstrip_prefetch(); }
void leave(dt_view_t *self) { /* disconnect from filmstrip image activate */ dt_control_signal_disconnect(darktable.signals, G_CALLBACK(_view_darkroom_filmstrip_activate_callback), (gpointer)self); /* disconnect from pipe finish signal */ dt_control_signal_disconnect(darktable.signals, G_CALLBACK(_darkroom_ui_pipe_finish_signal_callback), (gpointer)self); // store groups for next time: dt_conf_set_int("plugins/darkroom/groups", dt_dev_modulegroups_get(darktable.develop)); // store last active plugin: if(darktable.develop->gui_module) dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op); else dt_conf_set_string("plugins/darkroom/active", ""); dt_develop_t *dev = (dt_develop_t *)self->data; // tag image as changed // TODO: only tag the image when there was a real change. guint tagid = 0; dt_tag_new("darktable|changed",&tagid); dt_tag_attach(tagid, dev->image_storage.id); // commit image ops to db dt_dev_write_history(dev); // be sure light table will regenerate the thumbnail: // TODO: only if changed! // if() { dt_mipmap_cache_remove(darktable.mipmap_cache, dev->image_storage.id); // dump new xmp data dt_image_synch_xmp(dev->image_storage.id); } // clear gui. dev->gui_leaving = 1; dt_pthread_mutex_lock(&dev->history_mutex); dt_dev_pixelpipe_cleanup_nodes(dev->pipe); dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe); while(dev->history) { dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(dev->history->data); // printf("removing history item %d - %s, data %f %f\n", hist->module->instance, hist->module->op, *(float *)hist->params, *((float *)hist->params+1)); free(hist->params); hist->params = NULL; free(hist); dev->history = g_list_delete_link(dev->history, dev->history); } while(dev->iop) { dt_iop_module_t *module = (dt_iop_module_t *)(dev->iop->data); if (!dt_iop_is_hidden(module)) dt_iop_gui_cleanup_module(module); dt_dev_cleanup_module_accels(module); module->accel_closures = NULL; dt_iop_cleanup_module(module) ; free(module); dev->iop = g_list_delete_link(dev->iop, dev->iop); } dt_pthread_mutex_unlock(&dev->history_mutex); dt_print(DT_DEBUG_CONTROL, "[run_job-] 11 %f in darkroom mode\n", dt_get_wtime()); }
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 200 will be (re)set to 200 const int opencl_memory_requirement = max(200, 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; } dt_print(DT_DEBUG_OPENCL, "[opencl_init] found %d platform%s\n", num_platforms, num_platforms > 1 ? "s" : ""); 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); } } 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); } devs += all_num_devices[n]; } dt_print(DT_DEBUG_OPENCL, "[opencl_init] found %d device%s\n", num_devices, num_devices > 1 ? "s" : ""); if(num_devices == 0) goto finally; 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->dev[dev].nvidia_sm_20 = 0; cl_device_id devid = cl->dev[dev].devid = devices[k]; char infostr[1024]; char vendor[256]; 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_VENDOR, sizeof(vendor), &vendor, NULL); (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(!strncasecmp(vendor, "NVIDIA", 6)) { // very lame attemt to detect support for atomic float add in global memory. // we need compute model sm_20, but let's try for all nvidia devices :( cl->dev[dev].nvidia_sm_20 = dt_nvidia_gpu_supports_sm_20(infostr); dt_print(DT_DEBUG_OPENCL, "[opencl_init] device %d `%s' %s sm_20 support.\n", k, infostr, cl->dev[dev].nvidia_sm_20 ? "has" : "doesn't have"); } 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 dtcache[DT_MAX_PATH_LEN]; char cachedir[DT_MAX_PATH_LEN]; char devname[1024]; double tstart, tend, tdiff; dt_loc_get_user_cache_dir(dtcache, DT_MAX_PATH_LEN); int len = strlen(infostr); int j=0; // remove non-alphanumeric chars from device name for (int i=0; i < len; i++) if (isalnum(infostr[i])) devname[j++]=infostr[i]; devname[j] = 0; snprintf(cachedir, DT_MAX_PATH_LEN, "%s/cached_kernels_for_%s", dtcache, devname); if (mkdir(cachedir, 0700) && (errno != EEXIST)) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] failed to create directory `%s'!\n", cachedir); goto finally; } char dtpath[DT_MAX_PATH_LEN]; char filename[DT_MAX_PATH_LEN]; char programname[DT_MAX_PATH_LEN]; char binname[DT_MAX_PATH_LEN]; dt_loc_get_datadir(dtpath, DT_MAX_PATH_LEN); snprintf(filename, DT_MAX_PATH_LEN, "%s/kernels/programs.conf", dtpath); // now load all darktable cl kernels. // TODO: compile as a job? tstart = dt_get_wtime(); 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 pos=0; pos<strlen(programname); pos++) if(programname[pos] == '#') { programname[pos] = '\0'; for(int l=pos-1; l>=0; l--) { if (programname[l] == ' ') programname[l] = '\0'; else break; } break; } if(programname[0] == '\0') continue; snprintf(filename, DT_MAX_PATH_LEN, "%s/kernels/%s", dtpath, programname); snprintf(binname, DT_MAX_PATH_LEN, "%s/%s.bin", cachedir, programname); dt_print(DT_DEBUG_OPENCL, "[opencl_init] compiling program `%s' ..\n", programname); int loaded_cached; char md5sum[33]; const int prog = dt_opencl_load_program(dev, filename, binname, cachedir, md5sum, &loaded_cached); if(dt_opencl_build_program(dev, prog, binname, cachedir, md5sum, loaded_cached) != CL_SUCCESS) { dt_print(DT_DEBUG_OPENCL, "[opencl_init] failed to compile program `%s'!\n", programname); goto finally; } } fclose(f); tend = dt_get_wtime(); tdiff = tend - tstart; dt_print(DT_DEBUG_OPENCL, "[opencl_init] kernel loading time: %2.4lf \n", tdiff); } 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"); if(cl->inited) { dt_capabilities_add("opencl"); cl->bilateral = dt_bilateral_init_cl_global(); cl->gaussian = dt_gaussian_init_cl_global(); } return; }
static int dt_group_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float *buffer) { double start2 = dt_get_wtime(); const guint nb = g_list_length(form->points); if(nb == 0) return 0; int nb_ok = 0; const int width = roi->width; const int height = roi->height; // we need to allocate a temporary buffer for intermediate creation of individual shapes float *bufs = dt_alloc_align(64, (size_t)width * height * sizeof(float)); if(bufs == NULL) return 0; // empty the output buffer memset(buffer, 0, (size_t)width * height * sizeof(float)); // and we get all masks GList *fpts = g_list_first(form->points); while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(module->dev, fpt->formid); if(sel) { const int ok = dt_masks_get_mask_roi(module, piece, sel, roi, bufs); const float op = fpt->opacity; const int state = fpt->state; if(ok) { // first see if we need to invert this shape if(state & DT_MASKS_STATE_INVERSE) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs) #else #pragma omp parallel for shared(bufs) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; bufs[index] = 1.0f - bufs[index]; } } if(state & DT_MASKS_STATE_UNION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; buffer[index] = fmaxf(buffer[index], bufs[index] * op); } } else if(state & DT_MASKS_STATE_INTERSECTION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = b2 = bufs[index]; // FIXME: is this line correct? what it supposed to be doing? if(b1 > 0.0f && b2 > 0.0f) buffer[index] = fminf(b1, b2 * op); else buffer[index] = 0.0f; } } else if(state & DT_MASKS_STATE_DIFFERENCE) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = bufs[index] * op; if(b1 > 0.0f && b2 > 0.0f) buffer[index] = b1 * (1.0f - b2); } } else if(state & DT_MASKS_STATE_EXCLUSION) { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; float b1 = buffer[index]; float b2 = bufs[index] * op; if(b1 > 0.0f && b2 > 0.0f) buffer[index] = fmaxf((1.0f - b1) * b2, b1 * (1.0f - b2)); else buffer[index] = fmaxf(b1, b2); } } else // if we are here, this mean that we just have to copy the shape and null other parts { #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(bufs, buffer) #else #pragma omp parallel for shared(bufs, buffer) #endif #endif for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) { size_t index = (size_t)y * width + x; buffer[index] = bufs[index] * op; } } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %d] combine took %0.04f sec\n", nb_ok, dt_get_wtime() - start2); start2 = dt_get_wtime(); nb_ok++; } } fpts = g_list_next(fpts); } // and we free the intermediate buffer dt_free_align(bufs); return (nb_ok != 0); }
int dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data, lua_State *L) { double start_wtime = dt_get_wtime(); #ifndef __WIN32__ if(getuid() == 0 || geteuid() == 0) printf( "WARNING: either your user id or the effective user id are 0. are you running darktable as root?\n"); #endif #if defined(__SSE__) // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #endif dt_set_signal_handlers(); #include "is_supported_platform.h" int sse2_supported = 0; #ifdef HAVE_BUILTIN_CPU_SUPPORTS // NOTE: _may_i_use_cpu_feature() looks better, but only avaliable in ICC __builtin_cpu_init(); sse2_supported = __builtin_cpu_supports("sse2"); #else sse2_supported = dt_detect_cpu_features() & CPU_FLAG_SSE2; #endif if(!sse2_supported) { fprintf(stderr, "[dt_init] SSE2 instruction set is unavailable.\n"); fprintf(stderr, "[dt_init] expect a LOT of functionality to be broken. you have been warned.\n"); } #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD, 128 * 1024); /* use mmap() for large allocations */ #endif // make sure that stack/frame limits are good (musl) dt_set_rlimits(); // we have to have our share dir in XDG_DATA_DIRS, // otherwise GTK+ won't find our logo for the about screen (and maybe other things) { const gchar *xdg_data_dirs = g_getenv("XDG_DATA_DIRS"); gchar *new_xdg_data_dirs = NULL; gboolean set_env = TRUE; if(xdg_data_dirs != NULL && *xdg_data_dirs != '\0') { // check if DARKTABLE_SHAREDIR is already in there gboolean found = FALSE; gchar **tokens = g_strsplit(xdg_data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); // xdg_data_dirs is neither NULL nor empty => tokens != NULL for(char **iter = tokens; *iter != NULL; iter++) if(!strcmp(DARKTABLE_SHAREDIR, *iter)) { found = TRUE; break; } g_strfreev(tokens); if(found) set_env = FALSE; else new_xdg_data_dirs = g_strjoin(G_SEARCHPATH_SEPARATOR_S, DARKTABLE_SHAREDIR, xdg_data_dirs, NULL); } else { #ifndef _WIN32 // see http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html for a reason to use those as a // default if(!g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share/") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share/")) new_xdg_data_dirs = g_strdup("/usr/local/share/" G_SEARCHPATH_SEPARATOR_S "/usr/share/"); else new_xdg_data_dirs = g_strdup_printf("%s" G_SEARCHPATH_SEPARATOR_S "/usr/local/share/" G_SEARCHPATH_SEPARATOR_S "/usr/share/", DARKTABLE_SHAREDIR); #else set_env = FALSE; #endif } if(set_env) g_setenv("XDG_DATA_DIRS", new_xdg_data_dirs, 1); g_free(new_xdg_data_dirs); } setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.start_wtime = start_wtime; darktable.progname = argv[0]; // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)calloc(1, sizeof(dt_control_t)); // database char *dbfilename_from_command = NULL; char *noiseprofiles_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; #ifdef HAVE_OPENCL gboolean exclude_opencl = FALSE; gboolean print_statistics = strcmp(argv[0], "darktable-cltest"); #endif #ifdef USE_LUA char *lua_command = NULL; #endif darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *config_override = NULL; for(int k = 1; k < argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { #ifdef USE_LUA const char *lua_api_version = strcmp(LUA_API_VERSION_SUFFIX, "") ? STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH) "-" LUA_API_VERSION_SUFFIX : STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH); #endif printf("this is %s\ncopyright (c) 2009-%s johannes hanika\n" PACKAGE_BUGREPORT "\n\ncompile options:\n" " bit depth is %s\n" #ifdef _DEBUG " debug build\n" #else " normal build\n" #endif #if defined(__SSE2__) && defined(__SSE__) " SSE2 optimized codepath enabled\n" #else " SSE2 optimized codepath disabled\n" #endif #ifdef _OPENMP " OpenMP support enabled\n" #else " OpenMP support disabled\n" #endif #ifdef HAVE_OPENCL " OpenCL support enabled\n" #else " OpenCL support disabled\n" #endif #ifdef USE_LUA " Lua support enabled, API version %s\n" #else " Lua support disabled\n" #endif #ifdef USE_COLORDGTK " Colord support enabled\n" #else " Colord support disabled\n" #endif #ifdef HAVE_GPHOTO2 " gPhoto2 support enabled\n" #else " gPhoto2 support disabled\n" #endif #ifdef HAVE_GRAPHICSMAGICK " GraphicsMagick support enabled\n" #else " GraphicsMagick support disabled\n" #endif #ifdef HAVE_OPENEXR " OpenEXR support enabled\n" #else " OpenEXR support disabled\n" #endif , darktable_package_string, darktable_last_commit_year, (sizeof(void *) == 8 ? "64 bit" : sizeof(void *) == 4 ? "32 bit" : "unknown") #if USE_LUA , lua_api_version #endif ); return 1; } else if(!strcmp(argv[k], "--library") && argc > k + 1) { dbfilename_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--datadir") && argc > k + 1) { datadir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--moduledir") && argc > k + 1) { moduledir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--tmpdir") && argc > k + 1) { tmpdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--configdir") && argc > k + 1) { configdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--cachedir") && argc > k + 1) { cachedir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--localedir") && argc > k + 1) { bindtextdomain(GETTEXT_PACKAGE, argv[++k]); argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 'd' && argc > k + 1) { if(!strcmp(argv[k + 1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k + 1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k + 1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k + 1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k + 1], "input")) darktable.unmuted |= DT_DEBUG_INPUT; // input devices else if(!strcmp(argv[k + 1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k + 1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k + 1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k + 1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k + 1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k + 1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k + 1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k + 1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else if(!strcmp(argv[k + 1], "masks")) darktable.unmuted |= DT_DEBUG_MASKS; // masks related stuff. else if(!strcmp(argv[k + 1], "lua")) darktable.unmuted |= DT_DEBUG_LUA; // lua errors are reported on console else if(!strcmp(argv[k + 1], "print")) darktable.unmuted |= DT_DEBUG_PRINT; // print errors are reported on console else if(!strcmp(argv[k + 1], "camsupport")) darktable.unmuted |= DT_DEBUG_CAMERA_SUPPORT; // camera support warnings are reported on console else return usage(argv[0]); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 't' && argc > k + 1) { darktable.num_openmp_threads = CLAMP(atol(argv[k + 1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--conf") && argc > k + 1) { gchar *keyval = g_strdup(argv[++k]), *c = keyval; argv[k-1] = NULL; argv[k] = NULL; gchar *end = keyval + strlen(keyval); while(*c != '=' && c < end) c++; if(*c == '=' && *(c + 1) != '\0') { *c++ = '\0'; dt_conf_string_entry_t *entry = (dt_conf_string_entry_t *)g_malloc(sizeof(dt_conf_string_entry_t)); entry->key = g_strdup(keyval); entry->value = g_strdup(c); config_override = g_slist_append(config_override, entry); } g_free(keyval); } else if(!strcmp(argv[k], "--noiseprofiles") && argc > k + 1) { noiseprofiles_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--luacmd") && argc > k + 1) { #ifdef USE_LUA lua_command = argv[++k]; #else ++k; #endif argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--disable-opencl")) { #ifdef HAVE_OPENCL exclude_opencl = TRUE; #endif argv[k] = NULL; } else if(!strcmp(argv[k], "--")) { // "--" confuses the argument parser of glib/gtk. remove it. argv[k] = NULL; break; } else return usage(argv[0]); // fail on unrecognized options } } // remove the NULLs to not confuse gtk_init() later. for(int i = 1; i < argc; i++) { int k; for(k = i; k < argc; k++) if(argv[k] != NULL) break; if(k > i) { k -= i; for(int j = i + k; j < argc; j++) { argv[j-k] = argv[j]; argv[j] = NULL; } argc -= k; } } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } if(init_gui) { // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // make sure that we have no stale global progress bar visible. thus it's run as early is possible dt_control_progress_init(darktable.control); } #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { fprintf(stderr, "error: invalid temporary directory: %s\n", darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #ifdef USE_LUA dt_lua_init_early(L); #endif // thread-safe init: dt_exif_init(); char datadir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(datadir, sizeof(datadir)); char darktablerc[PATH_MAX] = { 0 }; snprintf(darktablerc, sizeof(darktablerc), "%s/darktablerc", datadir); // initialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, darktablerc, config_override); g_slist_free_full(config_override, g_free); // set the interface language const gchar *lang = dt_conf_get_string("ui_last/gui_language"); #if defined(_WIN32) // get the default locale if no language preference was specified in the config file if(lang == NULL || lang[0] == '\0') { const wchar_t *wcLocaleName = NULL; wcLocaleName = dtwin_get_locale(); if(wcLocaleName != NULL) { gchar *langLocale; langLocale = g_utf16_to_utf8(wcLocaleName, -1, NULL, NULL, NULL); if(langLocale != NULL) { g_free((gchar *)lang); lang = g_strdup(langLocale); } } } #endif // defined (_WIN32) if(lang != NULL && lang[0] != '\0') { g_setenv("LANGUAGE", lang, 1); if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); setlocale(LC_MESSAGES, lang); g_setenv("LANG", lang, 1); } g_free((gchar *)lang); // we need this REALLY early so that error messages can be shown, however after gtk_disable_setlocale if(init_gui) { #ifdef GDK_WINDOWING_WAYLAND // There are currently bad interactions with Wayland (drop-downs // are very narrow, scroll events lost). Until this is fixed, give // priority to the XWayland backend for Wayland users. gdk_set_allowed_backends("x11,*"); #endif gtk_init(&argc, &argv); } // detect cpu features and decide which codepaths to enable dt_codepaths_init(); // get the list of color profiles darktable.color_profiles = dt_colorspaces_init(); // initialize the database darktable.db = dt_database_init(dbfilename_from_command, load_data); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(!dt_database_get_lock_acquired(darktable.db)) { gboolean image_loaded_elsewhere = FALSE; #ifndef MAC_INTEGRATION // send the images to the other instance via dbus fprintf(stderr, "trying to open the images in the running instance\n"); GDBusConnection *connection = NULL; for(int i = 1; i < argc; i++) { // make the filename absolute ... if(argv[i] == NULL || *argv[i] == '\0') continue; gchar *filename = dt_util_normalize_path(argv[i]); if(filename == NULL) continue; if(!connection) connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); // ... and send it to the running instance of darktable image_loaded_elsewhere = g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL) != NULL; g_free(filename); } if(connection) g_object_unref(connection); #endif if(!image_loaded_elsewhere) dt_database_show_error(darktable.db); return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Make sure that the database and xmp files are in sync // We need conf and db to be up and running for that which is the case here. // FIXME: is this also useful in non-gui mode? GList *changed_xmp_files = NULL; if(init_gui && dt_conf_get_bool("run_crawler_on_start")) { changed_xmp_files = dt_control_crawler_run(); } if(init_gui) { dt_control_init(darktable.control); } else { if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) dt_gui_presets_init(); // init preset db schema. darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection = dt_collection_new(NULL); /* initialize selection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; // Initialize the password storage engine darktable.pwstorage = dt_pwstorage_new(); darktable.guides = dt_guides_init(); #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); // *SIGH* dt_set_signal_handlers(); #endif darktable.opencl = (dt_opencl_t *)calloc(1, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, exclude_opencl, print_statistics); #endif darktable.points = (dt_points_t *)calloc(1, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); darktable.noiseprofile_parser = dt_noiseprofile_init(noiseprofiles_from_command); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)calloc(1, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)calloc(1, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)calloc(1, sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)calloc(1, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); // check whether we were able to load darkroom view. if we failed, we'll crash everywhere later on. if(!darktable.develop) return 1; darktable.imageio = (dt_imageio_t *)calloc(1, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { #ifdef HAVE_GPHOTO2 // Initialize the camera control. // this is done late so that the gui can react to the signal sent but before switching to lighttable! darktable.camctl = dt_camctl_new(); #endif darktable.lib = (dt_lib_t *)calloc(1, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_gui_gtk_load_config(); // init the gui part of views dt_view_manager_gui_init(darktable.view_manager); // Loading the keybindings char keyfile[PATH_MAX] = { 0 }; // First dump the default keymapping snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // initialize undo struct darktable.undo = dt_undo_init(); } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } dt_image_local_copy_synch(); /* init lua last, since it's user made stuff it must be in the real environment */ #ifdef USE_LUA dt_lua_init(darktable.lua_state.state, lua_command); #endif if(init_gui) { const char *mode = "lighttable"; // april 1st: you have to earn using dt first! or know that you can switch views with keyboard shortcuts time_t now; time(&now); struct tm lt; localtime_r(&now, <); if(lt.tm_mon == 3 && lt.tm_mday == 1) mode = "knight"; // we have to call dt_ctl_switch_mode_to() here already to not run into a lua deadlock. // having another call later is ok dt_ctl_switch_mode_to(mode); #ifndef MAC_INTEGRATION // load image(s) specified on cmdline. // this has to happen after lua is initialized as image import can run lua code // If only one image is listed, attempt to load it in darkroom int last_id = 0; gboolean only_single_images = TRUE; int loaded_images = 0; for(int i = 1; i < argc; i++) { gboolean single_image = FALSE; if(argv[i] == NULL || *argv[i] == '\0') continue; int new_id = dt_load_from_string(argv[i], FALSE, &single_image); if(new_id > 0) { last_id = new_id; loaded_images++; if(!single_image) only_single_images = FALSE; } } if(loaded_images == 1 && only_single_images) { dt_control_set_mouse_over_id(last_id); dt_ctl_switch_mode_to("darkroom"); } #endif } // last but not least construct the popup that asks the user about images whose xmp files are newer than the // db entry if(init_gui && changed_xmp_files) { dt_control_crawler_show_image_list(changed_xmp_files); } dt_print(DT_DEBUG_CONTROL, "[init] startup took %f seconds\n", dt_get_wtime() - start_wtime); return 0; }
static int dt_group_get_mask(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *width, int *height, int *posx, int *posy) { double start2; // we allocate buffers and values const guint nb = g_list_length(form->points); if(nb == 0) return 0; float *bufs[nb]; int w[nb]; int h[nb]; int px[nb]; int py[nb]; int ok[nb]; int states[nb]; float op[nb]; // and we get all masks GList *fpts = g_list_first(form->points); int pos = 0; int nb_ok = 0; while(fpts) { dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data; dt_masks_form_t *sel = dt_masks_get_from_id(module->dev, fpt->formid); if(sel) { ok[pos] = dt_masks_get_mask(module, piece, sel, &bufs[pos], &w[pos], &h[pos], &px[pos], &py[pos]); if(fpt->state & DT_MASKS_STATE_INVERSE) { start2 = dt_get_wtime(); _inverse_mask(module, piece, sel, &bufs[pos], &w[pos], &h[pos], &px[pos], &py[pos]); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] inverse took %0.04f sec\n", sel->name, dt_get_wtime() - start2); // start2 = dt_get_wtime(); } op[pos] = fpt->opacity; states[pos] = fpt->state; if(ok[pos]) nb_ok++; } fpts = g_list_next(fpts); pos++; } if(nb_ok == 0) return 0; // now we get the min, max, width, height of the final mask int l, r, t, b; l = t = INT_MAX; r = b = INT_MIN; for(int i = 0; i < nb; i++) { l = MIN(l, px[i]); t = MIN(t, py[i]); r = MAX(r, px[i] + w[i]); b = MAX(b, py[i] + h[i]); } *posx = l; *posy = t; *width = r - l; *height = b - t; // we allocate the buffer *buffer = malloc(sizeof(float) * (r - l) * (b - t)); // and we copy each buffer inside, row by row for(int i = 0; i < nb; i++) { start2 = dt_get_wtime(); if(states[i] & DT_MASKS_STATE_UNION) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l], bufs[i][y * w[i] + x] * op[i]); } } } else if(states[i] & DT_MASKS_STATE_INTERSECTION) { for(int y = 0; y < b - t; y++) { for(int x = 0; x < r - l; x++) { float b1 = (*buffer)[y * (r - l) + x]; float b2 = 0.0f; if(y + t - py[i] >= 0 && y + t - py[i] < h[i] && x + l - px[i] >= 0 && x + l - px[i] < w[i]) b2 = bufs[i][(y + t - py[i]) * w[i] + x + l - px[i]]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[y * (r - l) + x] = fminf(b1, b2 * op[i]); else (*buffer)[y * (r - l) + x] = 0.0f; } } } else if(states[i] & DT_MASKS_STATE_DIFFERENCE) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { float b1 = (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l]; float b2 = bufs[i][y * w[i] + x] * op[i]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = b1 * (1.0f - b2); } } } else if(states[i] & DT_MASKS_STATE_EXCLUSION) { for(int y = 0; y < h[i]; y++) { for(int x = 0; x < w[i]; x++) { float b1 = (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l]; float b2 = bufs[i][y * w[i] + x] * op[i]; if(b1 > 0.0f && b2 > 0.0f) (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((1.0f - b1) * b2, b1 * (1.0f - b2)); else (*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l] = fmaxf((*buffer)[(py[i] + y - t) * (r - l) + px[i] + x - l], bufs[i][y * w[i] + x] * op[i]); } } } else // if we are here, this mean that we just have to copy the shape and null other parts { for(int y = 0; y < b - t; y++) { for(int x = 0; x < r - l; x++) { float b2 = 0.0f; if(y + t - py[i] >= 0 && y + t - py[i] < h[i] && x + l - px[i] >= 0 && x + l - px[i] < w[i]) b2 = bufs[i][(y + t - py[i]) * w[i] + x + l - px[i]]; (*buffer)[y * (r - l) + x] = b2 * op[i]; } } } // and we free the buffer free(bufs[i]); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %d] combine took %0.04f sec\n", i, dt_get_wtime() - start2); // start2 = dt_get_wtime(); } return 1; }
static int dt_gradient_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float **buffer) { double start2 = dt_get_wtime(); //we get the gradient values dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *) (g_list_first(form->points)->data); //we create a buffer of mesh points for later interpolation. mainly in order to reduce memory footprint const int w = roi->width; const int h = roi->height; const int px = roi->x; const int py = roi->y; const float iscale = 1.0f/roi->scale; const int mesh = 4; const int mw = (w + mesh - 1) / mesh + 1; const int mh = (h + mesh - 1) / mesh + 1; float *points = malloc(mw*mh*2*sizeof(float)); if(points == NULL) return 0; #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points) #else #pragma omp parallel for shared(points) #endif #endif for (int j=0; j<mh; j++) for (int i=0; i<mw; i++) { points[(j*mw+i)*2] = (mesh*i+px)*iscale; points[(j*mw+i)*2+1] = (mesh*j+py)*iscale; } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] gradient draw took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we backtransform all these points if (!dt_dev_distort_backtransform_plus(module->dev, piece->pipe, 0, module->priority, points, mw*mh)) { free(points); return 0; } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] gradient transform took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we calculate the mask at mesh points and recycle point buffer to store results const float wd = piece->pipe->iwidth; const float ht = piece->pipe->iheight; const float hwscale = 1.0f/sqrtf(wd*wd+ht*ht); const float v = (-gradient->rotation/180.0f)*M_PI; const float sinv = sin(v); const float cosv = cos(v); const float offset = sinv * gradient->anchor[0]*wd - cosv * gradient->anchor[1]*ht; const float compression = fmaxf(gradient->compression, 0.001f); const float cs = powf(10.0f, gradient->steepness); const float steepness = cs*cs - 1.0f; const float normf = 0.5f * cs / compression; #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points) #else #pragma omp parallel for shared(points) #endif #endif for (int j=0; j<mh; j++) { for (int i=0; i<mw; i++) { float x = points[(j*mw+i)*2]; float y = points[(j*mw+i)*2+1]; float distance = (sinv * x - cosv * y - offset) * hwscale; float value = normf * distance / sqrtf(1.0f + steepness*distance*distance) + 0.5f; points[(j*mw+i)*2] = (value < 0.0f) ? 0.0f : ((value > 1.0f) ? 1.0f : value); } } //we allocate the buffer *buffer = malloc(w*h*sizeof(float)); if(*buffer == NULL) { free(points); return 0; } memset(*buffer,0,w*h*sizeof(float)); //we fill the mask buffer by interpolation #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points,buffer) #else #pragma omp parallel for shared(points,buffer) #endif #endif for (int j=0; j<h; j++) { int jj = j % mesh; int mj = j / mesh; for (int i=0; i<w; i++) { int ii = i % mesh; int mi = i / mesh; (*buffer)[j*w+i] = ( points[(mj*mw+mi)*2] * (mesh-ii)*(mesh-jj) + points[(mj*mw+mi+1)*2] * ii*(mesh-jj) + points[((mj+1)*mw+mi)*2] * (mesh-ii)*jj + points[((mj+1)*mw+mi+1)*2] * ii*jj ) / (mesh*mesh); } } free(points); if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] gradient fill took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); return 1; }
void dt_view_image_expose( dt_view_image_over_t *image_over, uint32_t imgid, cairo_t *cr, int32_t width, int32_t height, int32_t zoom, int32_t px, int32_t py) { const double start = dt_get_wtime(); // some performance tuning stuff, for your pleasure. // on my machine with 7 image per row it seems grouping has the largest // impact from around 400ms -> 55ms per redraw. #define DRAW_THUMB 1 #define DRAW_COLORLABELS 1 #define DRAW_GROUPING 1 #define DRAW_SELECTED 1 #define DRAW_HISTORY 1 #if DRAW_THUMB == 1 // this function is not thread-safe (gui-thread only), so we // can safely allocate this leaking bit of memory to decompress thumbnails: static int first_time = 1; static uint8_t *scratchmem = NULL; if(first_time) { // scratchmem might still be NULL after this, if compression is off. scratchmem = dt_mipmap_cache_alloc_scratchmem(darktable.mipmap_cache); first_time = 0; } #endif cairo_save (cr); float bgcol = 0.4, fontcol = 0.425, bordercol = 0.1, outlinecol = 0.2; int selected = 0, altered = 0, imgsel = -1, is_grouped = 0; // this is a gui thread only thing. no mutex required: imgsel = darktable.control->global_settings.lib_image_mouse_over_id; #if DRAW_SELECTED == 1 /* clear and reset statements */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.is_selected); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.is_selected); /* bind imgid to prepared statments */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.is_selected, 1, imgid); /* lets check if imgid is selected */ if(sqlite3_step(darktable.view_manager->statements.is_selected) == SQLITE_ROW) selected = 1; #endif #if DRAW_HISTORY == 1 DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.have_history); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.have_history); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.have_history, 1, imgid); /* lets check if imgid has history */ if(sqlite3_step(darktable.view_manager->statements.have_history) == SQLITE_ROW) altered = 1; #endif const dt_image_t *img = dt_image_cache_read_testget(darktable.image_cache, imgid); #if DRAW_GROUPING == 1 DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.get_grouped); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.get_grouped); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_grouped, 1, imgid); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_grouped, 2, imgid); /* lets check if imgid is in a group */ if(sqlite3_step(darktable.view_manager->statements.get_grouped) == SQLITE_ROW) is_grouped = 1; else if(img && darktable.gui->expanded_group_id == img->group_id) darktable.gui->expanded_group_id = -1; #endif if(selected == 1) { outlinecol = 0.4; bgcol = 0.6; fontcol = 0.5; } if(imgsel == imgid) { bgcol = 0.8; // mouse over fontcol = 0.7; outlinecol = 0.6; // if the user points at this image, we really want it: if(!img) img = dt_image_cache_read_get(darktable.image_cache, imgid); } float imgwd = 0.90f; if(zoom == 1) { imgwd = .97f; // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); } else { double x0 = 1, y0 = 1, rect_width = width-2, rect_height = height-2, radius = 5; double x1, y1, off, off1; x1=x0+rect_width; y1=y0+rect_height; off=radius*0.666; off1 = radius-off; cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0+off1, x0+off1 , y0, x0 + radius, y0); cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1-off1, y0, x1, y0+off1, x1, y0 + radius); cairo_line_to (cr, x1 , y1 - radius); cairo_curve_to (cr, x1, y1-off1, x1-off1, y1, x1 - radius, y1); cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0+off1, y1, x0, y1-off1, x0, y1- radius); cairo_close_path (cr); cairo_set_source_rgb(cr, bgcol, bgcol, bgcol); cairo_fill_preserve(cr); cairo_set_line_width(cr, 0.005*width); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_stroke(cr); if(img) { const char *ext = img->filename + strlen(img->filename); while(ext > img->filename && *ext != '.') ext--; ext++; cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .25*width); cairo_text_extents_t text_extends; cairo_text_extents (cr, ext, &text_extends); cairo_move_to (cr, .025*width - text_extends.x_bearing, .24*height); cairo_show_text (cr, ext); } } dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size( darktable.mipmap_cache, imgwd*width, imgwd*height); dt_mipmap_cache_read_get( darktable.mipmap_cache, &buf, imgid, mip, 0); #if DRAW_THUMB == 1 float scale = 1.0; // decompress image, if necessary. if compression is off, scratchmem will be == NULL, // so get the real pointer back: uint8_t *buf_decompressed = dt_mipmap_cache_decompress(&buf, scratchmem); cairo_surface_t *surface = NULL; if(buf.buf) { const int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, buf.width); surface = cairo_image_surface_create_for_data (buf_decompressed, CAIRO_FORMAT_RGB24, buf.width, buf.height, stride); if(zoom == 1) { scale = fminf( fminf(darktable.thumbnail_width, width) / (float)buf.width, fminf(darktable.thumbnail_height, height) / (float)buf.height ); } else scale = fminf(width*imgwd/(float)buf.width, height*imgwd/(float)buf.height); } // draw centered and fitted: cairo_save(cr); cairo_translate(cr, width/2.0, height/2.0f); cairo_scale(cr, scale, scale); if(buf.buf) { cairo_translate(cr, -.5f*buf.width, -.5f*buf.height); cairo_set_source_surface (cr, surface, 0, 0); // set filter no nearest: // in skull mode, we want to see big pixels. // in 1 iir mode for the right mip, we want to see exactly what the pipe gave us, 1:1 pixel for pixel. // in between, filtering just makes stuff go unsharp. if((buf.width <= 8 && buf.height <= 8) || fabsf(scale - 1.0f) < 0.01f) cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_fill(cr); cairo_surface_destroy (surface); cairo_rectangle(cr, 0, 0, buf.width, buf.height); } // border around image const float border = zoom == 1 ? 16/scale : 2/scale; cairo_set_source_rgb(cr, bordercol, bordercol, bordercol); if(buf.buf && selected) { cairo_set_line_width(cr, 1./scale); if(zoom == 1) { // draw shadow around border cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_stroke(cr); // cairo_new_path(cr); cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); float alpha = 1.0f; for(int k=0; k<16; k++) { cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_new_sub_path(cr); cairo_rectangle(cr, -k/scale, -k/scale, buf.width+2.*k/scale, buf.height+2.*k/scale); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } } else { cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_new_sub_path(cr); cairo_rectangle(cr, -border, -border, buf.width+2.*border, buf.height+2.*border); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_fill(cr); } } else if(buf.buf) { cairo_set_line_width(cr, 1); cairo_stroke(cr); } cairo_restore(cr); #endif if(buf.buf) dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); const float fscale = fminf(width, height); if(imgsel == imgid) { // draw mouseover hover effects, set event hook for mouse button down! *image_over = DT_VIEW_DESERT; cairo_set_line_width(cr, 1.5); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); float r1, r2; if(zoom != 1) { r1 = 0.05*width; r2 = 0.022*width; } else { r1 = 0.015*fscale; r2 = 0.007*fscale; } float x, y; if(zoom != 1) y = 0.90*height; else y = .12*fscale; gboolean image_is_rejected = (img && ((img->flags & 0x7) == 6)); if(img) for(int k=0; k<5; k++) { if(zoom != 1) x = (0.41+k*0.12)*width; else x = (.08+k*0.04)*fscale; if(!image_is_rejected) //if rejected: draw no stars { dt_view_star(cr, x, y, r1, r2); if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_STAR_1 + k; cairo_fill(cr); } else if((img->flags & 0x7) > k) { cairo_fill_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); } else cairo_stroke(cr); } } //Image rejected? if(zoom !=1) x = 0.11*width; else x = .04*fscale; if (image_is_rejected) cairo_set_source_rgb(cr, 1., 0., 0.); if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_REJECT; //mouse sensitive cairo_new_sub_path(cr); cairo_arc(cr, x, y, (r1+r2)*.5, 0, 2.0f*M_PI); cairo_stroke(cr); } if (image_is_rejected) cairo_set_line_width(cr, 2.5); //reject cross: cairo_move_to(cr, x-r2, y-r2); cairo_line_to(cr, x+r2, y+r2); cairo_move_to(cr, x+r2, y-r2); cairo_line_to(cr, x-r2, y+r2); cairo_close_path(cr); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_width(cr, 1.5); // image part of a group? if(is_grouped && darktable.gui && darktable.gui->grouping) { // draw grouping icon and border if the current group is expanded // align to the right, left of altered float s = (r1+r2)*.75; float _x, _y; if(zoom != 1) { _x = width*0.9 - s*2.5; _y = height*0.1 - s*.4; } else { _x = (.04+7*0.04-1.1*.04)*fscale; _y = y - (.17*.04)*fscale; } cairo_save(cr); if(img && (imgid != img->group_id)) cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); dtgtk_cairo_paint_grouping(cr, _x, _y, s, s, 23); cairo_restore(cr); // mouse is over the grouping icon if(img && abs(px-_x-.5*s) <= .8*s && abs(py-_y-.5*s) <= .8*s) *image_over = DT_VIEW_GROUP; } // image altered? if(altered) { // align to right float s = (r1+r2)*.5; if(zoom != 1) { x = width*0.9; y = height*0.1; } else x = (.04+7*0.04)*fscale; dt_view_draw_altered(cr, x, y, s); //g_print("px = %d, x = %.4f, py = %d, y = %.4f\n", px, x, py, y); if(img && abs(px-x) <= 1.2*s && abs(py-y) <= 1.2*s) // mouse hovers over the altered-icon -> history tooltip! { darktable.gui->center_tooltip = 1; } } } // kill all paths, in case img was not loaded yet, or is blocked: cairo_new_path(cr); #if DRAW_COLORLABELS == 1 // TODO: make mouse sensitive, just as stars! // TODO: cache in image struct! { // color labels: const float x = zoom == 1 ? (0.07)*fscale : .21*width; const float y = zoom == 1 ? 0.17*fscale: 0.1*height; const float r = zoom == 1 ? 0.01*fscale : 0.03*width; /* clear and reset prepared statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.get_color); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.get_color); /* setup statement and iterate rows */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_color, 1, imgid); while(sqlite3_step(darktable.view_manager->statements.get_color) == SQLITE_ROW) { cairo_save(cr); int col = sqlite3_column_int(darktable.view_manager->statements.get_color, 0); // see src/dtgtk/paint.c dtgtk_cairo_paint_label(cr, x+(3*r*col)-5*r, y-r, r*2, r*2, col); cairo_restore(cr); } } #endif if(img && (zoom == 1)) { // some exif data cairo_set_source_rgb(cr, .7, .7, .7); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .025*fscale); cairo_move_to (cr, .02*fscale, .04*fscale); // cairo_show_text(cr, img->filename); cairo_text_path(cr, img->filename); char exifline[50]; cairo_move_to (cr, .02*fscale, .08*fscale); dt_image_print_exif(img, exifline, 50); cairo_text_path(cr, exifline); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); cairo_stroke(cr); } if(img) dt_image_cache_read_release(darktable.image_cache, img); cairo_restore(cr); // if(zoom == 1) cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); const double end = dt_get_wtime(); dt_print(DT_DEBUG_PERF, "[lighttable] image expose took %0.04f sec\n", end-start); }
static int dt_ellipse_get_mask(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *width, int *height, int *posx, int *posy) { double start2 = dt_get_wtime(); // we get the area if(!dt_ellipse_get_area(module, piece, form, width, height, posx, posy)) return 0; if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse area took %0.04f sec\n", form->name, dt_get_wtime() - start2); start2 = dt_get_wtime(); // we get the ellipse values dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); // we create a buffer of points with all points in the area int w = *width, h = *height; float *points = malloc(w * h * 2 * sizeof(float)); for(int i = 0; i < h; i++) for(int j = 0; j < w; j++) { points[(i * w + j) * 2] = (j + (*posx)); points[(i * w + j) * 2 + 1] = (i + (*posy)); } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse draw took %0.04f sec\n", form->name, dt_get_wtime() - start2); start2 = dt_get_wtime(); // we back transform all this points if(!dt_dev_distort_backtransform_plus(module->dev, piece->pipe, 0, module->priority, points, w * h)) { free(points); return 0; } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse transform took %0.04f sec\n", form->name, dt_get_wtime() - start2); start2 = dt_get_wtime(); // we allocate the buffer *buffer = calloc(w * h, sizeof(float)); // we populate the buffer const int wi = piece->pipe->iwidth, hi = piece->pipe->iheight; const float center[2] = { ellipse->center[0] * wi, ellipse->center[1] * hi }; const float radius[2] = { ellipse->radius[0] * MIN(wi, hi), ellipse->radius[1] * MIN(wi, hi) }; const float total[2] = { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wi, hi), (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wi, hi) }; float a, b, ta, tb, alpha; if(radius[0] >= radius[1]) { a = radius[0]; b = radius[1]; ta = total[0]; tb = total[1]; alpha = (ellipse->rotation / 180.0f) * M_PI; } else { a = radius[1]; b = radius[0]; ta = total[1]; tb = total[0]; alpha = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI; } for(int i = 0; i < h; i++) for(int j = 0; j < w; j++) { float x = points[(i * w + j) * 2] - center[0]; float y = points[(i * w + j) * 2 + 1] - center[1]; float v = atan2(y, x) - alpha; float cosv = cos(v); float sinv = sin(v); float radius2 = a * a * b * b / (a * a * sinv * sinv + b * b * cosv * cosv); float total2 = ta * ta * tb * tb / (ta * ta * sinv * sinv + tb * tb * cosv * cosv); float l2 = x * x + y * y; if(l2 < radius2) (*buffer)[i * w + j] = 1.0f; else if(l2 < total2) { float f = (total2 - l2) / (total2 - radius2); (*buffer)[i * w + j] = f * f; } else (*buffer)[i * w + j] = 0.0f; } free(points); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse fill took %0.04f sec\n", form->name, dt_get_wtime() - start2); // start2 = dt_get_wtime(); return 1; }
static int dt_circle_get_mask(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, float **buffer, int *width, int *height, int *posx, int *posy) { double start2 = dt_get_wtime(); //we get the area if (!dt_circle_get_area(module,piece,form,width,height,posx,posy)) return 0; if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle area took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we get the circle values dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *) (g_list_first(form->points)->data); //we create a buffer of points with all points in the area int w = *width, h = *height; float *points = malloc(w*h*2*sizeof(float)); for (int i=0; i<h; i++) for (int j=0; j<w; j++) { points[(i*w+j)*2] = (j+(*posx)); points[(i*w+j)*2+1] = (i+(*posy)); } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle draw took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we back transform all this points if (!dt_dev_distort_backtransform_plus(module->dev,piece->pipe,0,module->priority,points,w*h)) { free(points); return 0; } if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle transform took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); //we allocate the buffer *buffer = malloc(w*h*sizeof(float)); memset(*buffer,0,w*h*sizeof(float)); //we populate the buffer int wi = piece->pipe->iwidth, hi=piece->pipe->iheight; float center[2] = {circle->center[0]*wi, circle->center[1]*hi}; float radius2 = circle->radius*MIN(wi,hi)*circle->radius*MIN(wi,hi); float total2 = (circle->radius+circle->border)*MIN(wi,hi)*(circle->radius+circle->border)*MIN(wi,hi); for (int i=0; i<h; i++) for (int j=0; j<w; j++) { float x = points[(i*w+j)*2]; float y = points[(i*w+j)*2+1]; float l2 = (x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1]); if (l2<radius2) (*buffer)[i*w+j] = 1.0f; else if (l2 < total2) { float f = (total2-l2)/(total2-radius2); (*buffer)[i*w+j] = f*f; } else (*buffer)[i*w+j] = 0.0f; } free(points); if (darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] circle fill took %0.04f sec\n", form->name, dt_get_wtime()-start2); start2 = dt_get_wtime(); return 1; }
static int dt_ellipse_get_mask_roi(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, const dt_iop_roi_t *roi, float *buffer) { double start2 = dt_get_wtime(); // we get the ellipse values dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)(g_list_first(form->points)->data); // we create a buffer of mesh points for later interpolation. mainly in order to reduce memory footprint const int w = roi->width; const int h = roi->height; const int px = roi->x; const int py = roi->y; const float iscale = 1.0f / roi->scale; const int mesh = 4; const int mw = (w + mesh - 1) / mesh + 1; const int mh = (h + mesh - 1) / mesh + 1; float *points = malloc((size_t)mw * mh * 2 * sizeof(float)); if(points == NULL) return 0; #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points) #else #pragma omp parallel for shared(points) #endif #endif for(int j = 0; j < mh; j++) for(int i = 0; i < mw; i++) { size_t index = (size_t)j * mw + i; points[index * 2] = (mesh * i + px) * iscale; points[index * 2 + 1] = (mesh * j + py) * iscale; } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse draw took %0.04f sec\n", form->name, dt_get_wtime() - start2); start2 = dt_get_wtime(); // we back transform all these points if(!dt_dev_distort_backtransform_plus(module->dev, piece->pipe, 0, module->priority, points, (size_t)mw * mh)) { free(points); return 0; } if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse transform took %0.04f sec\n", form->name, dt_get_wtime() - start2); start2 = dt_get_wtime(); // we populate the buffer const int wi = piece->pipe->iwidth, hi = piece->pipe->iheight; const float center[2] = { ellipse->center[0] * wi, ellipse->center[1] * hi }; const float radius[2] = { ellipse->radius[0] * MIN(wi, hi), ellipse->radius[1] * MIN(wi, hi) }; const float total[2] = { (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[0] * (1.0f + ellipse->border) : ellipse->radius[0] + ellipse->border) * MIN(wi, hi), (ellipse->flags & DT_MASKS_ELLIPSE_PROPORTIONAL ? ellipse->radius[1] * (1.0f + ellipse->border) : ellipse->radius[1] + ellipse->border) * MIN(wi, hi) }; float a, b, ta, tb, alpha; if(radius[0] >= radius[1]) { a = radius[0]; b = radius[1]; ta = total[0]; tb = total[1]; alpha = (ellipse->rotation / 180.0f) * M_PI; } else { a = radius[1]; b = radius[0]; ta = total[1]; tb = total[0]; alpha = ((ellipse->rotation - 90.0f) / 180.0f) * M_PI; } #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points, a, b, ta, tb, alpha) #else #pragma omp parallel for shared(points, a, b, ta, tb, alpha) #endif #endif for(int i = 0; i < mh; i++) for(int j = 0; j < mw; j++) { size_t index = (size_t)i * mw + j; float x = points[index * 2] - center[0]; float y = points[index * 2 + 1] - center[1]; float v = atan2(y, x) - alpha; float cosv = cos(v); float sinv = sin(v); float radius2 = a * a * b * b / (a * a * sinv * sinv + b * b * cosv * cosv); float total2 = ta * ta * tb * tb / (ta * ta * sinv * sinv + tb * tb * cosv * cosv); float l2 = x * x + y * y; if(l2 < radius2) points[index * 2] = 1.0f; else if(l2 < total2) { float f = (total2 - l2) / (total2 - radius2); points[index * 2] = f * f; } else points[index * 2] = 0.0f; } // we fill the output buffer by interpolation #ifdef _OPENMP #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel for default(none) shared(points, buffer) #else #pragma omp parallel for shared(points, buffer) #endif #endif for(int j = 0; j < h; j++) { int jj = j % mesh; int mj = j / mesh; for(int i = 0; i < w; i++) { int ii = i % mesh; int mi = i / mesh; size_t mindex = (size_t)mj * mw + mi; buffer[(size_t)j * w + i] = (points[mindex * 2] * (mesh - ii) * (mesh - jj) + points[(mindex + 1) * 2] * ii * (mesh - jj) + points[(mindex + mw) * 2] * (mesh - ii) * jj + points[(mindex + mw + 1) * 2] * ii * jj) / (mesh * mesh); } } free(points); if(darktable.unmuted & DT_DEBUG_PERF) dt_print(DT_DEBUG_MASKS, "[masks %s] ellipse fill took %0.04f sec\n", form->name, dt_get_wtime() - start2); // start2 = dt_get_wtime(); return 1; }
// if found, the data void* is returned. if not, it is set to be // the given *data and a new hash table entry is created, which can be // found using the given key later on. dt_cache_entry_t *dt_cache_get_with_caller(dt_cache_t *cache, const uint32_t key, char mode, const char *file, int line) { gpointer orig_key, value; gboolean res; int result; double start = dt_get_wtime(); restart: dt_pthread_mutex_lock(&cache->lock); res = g_hash_table_lookup_extended( cache->hashtable, GINT_TO_POINTER(key), &orig_key, &value); if(res) { // yay, found. read lock and pass on. dt_cache_entry_t *entry = (dt_cache_entry_t *)value; if(mode == 'w') result = dt_pthread_rwlock_trywrlock_with_caller(&entry->lock, file, line); else result = dt_pthread_rwlock_tryrdlock_with_caller(&entry->lock, file, line); if(result) { // need to give up mutex so other threads have a chance to get in between and // free the lock we're trying to acquire: dt_pthread_mutex_unlock(&cache->lock); g_usleep(5); goto restart; } // bubble up in lru list: cache->lru = g_list_remove_link(cache->lru, entry->link); cache->lru = g_list_concat(cache->lru, entry->link); dt_pthread_mutex_unlock(&cache->lock); #ifdef _DEBUG const pthread_t writer = dt_pthread_rwlock_get_writer(&entry->lock); if(mode == 'w') { assert(pthread_equal(writer, pthread_self())); } else { assert(!pthread_equal(writer, pthread_self())); } #endif if(mode == 'w') { assert(entry->data_size); ASAN_POISON_MEMORY_REGION(entry->data, entry->data_size); } // WARNING: do *NOT* unpoison here. it must be done by the caller! return entry; } // else, not found, need to allocate. // first try to clean up. // also wait if we can't free more than the requested fill ratio. if(cache->cost > 0.8f * cache->cost_quota) { // need to roll back all the way to get a consistent lock state: dt_cache_gc(cache, 0.8f); } // here dies your 32-bit system: dt_cache_entry_t *entry = (dt_cache_entry_t *)g_slice_alloc(sizeof(dt_cache_entry_t)); int ret = dt_pthread_rwlock_init(&entry->lock, 0); if(ret) fprintf(stderr, "rwlock init: %d\n", ret); entry->data = 0; entry->data_size = cache->entry_size; entry->cost = 1; entry->link = g_list_append(0, entry); entry->key = key; entry->_lock_demoting = 0; g_hash_table_insert(cache->hashtable, GINT_TO_POINTER(key), entry); assert(cache->allocate || entry->data_size); if(cache->allocate) cache->allocate(cache->allocate_data, entry); else entry->data = dt_alloc_align(16, entry->data_size); assert(entry->data_size); ASAN_POISON_MEMORY_REGION(entry->data, entry->data_size); // if allocate callback is given, always return a write lock const int write = ((mode == 'w') || cache->allocate); // write lock in case the caller requests it: if(write) dt_pthread_rwlock_wrlock_with_caller(&entry->lock, file, line); else dt_pthread_rwlock_rdlock_with_caller(&entry->lock, file, line); cache->cost += entry->cost; // put at end of lru list (most recently used): cache->lru = g_list_concat(cache->lru, entry->link); dt_pthread_mutex_unlock(&cache->lock); double end = dt_get_wtime(); if(end - start > 0.1) fprintf(stderr, "wait time %.06fs\n", end - start); // WARNING: do *NOT* unpoison here. it must be done by the caller! return entry; }
static int _piwigo_api_post_internal(_piwigo_api_context_t *ctx, GList *args, char *filename, gboolean isauth) { curl_mime *form = NULL; GString *url = g_string_new(ctx->url); // send the requests GString *response = g_string_new(""); dt_curl_init(ctx->curl_ctx, piwigo_EXTRA_VERBOSE); curl_easy_setopt(ctx->curl_ctx, CURLOPT_URL, url->str); curl_easy_setopt(ctx->curl_ctx, CURLOPT_POST, 1); curl_easy_setopt(ctx->curl_ctx, CURLOPT_WRITEFUNCTION, curl_write_data_cb); curl_easy_setopt(ctx->curl_ctx, CURLOPT_WRITEDATA, response); if(isauth) { /* construct a temporary file name */ char cookie_fmt[PATH_MAX] = { 0 }; dt_loc_get_tmp_dir(cookie_fmt, sizeof(cookie_fmt)); g_strlcat(cookie_fmt, "/cookies.%.4lf.txt", sizeof(cookie_fmt)); ctx->cookie_file = g_strdup_printf(cookie_fmt, dt_get_wtime()); // not that this is safe as the cookie file is written only when the curl context is finalized. // At this stage we unlink the file. curl_easy_setopt(ctx->curl_ctx, CURLOPT_COOKIEJAR, ctx->cookie_file); } else { curl_easy_setopt(ctx->curl_ctx, CURLOPT_COOKIEFILE, ctx->cookie_file); } if(filename) { curl_mimepart *field = NULL; form = curl_mime_init(ctx->curl_ctx); GList *a = args; while (a) { _curl_args_t *ca = (_curl_args_t *)a->data; field = curl_mime_addpart(form); curl_mime_name(field, ca->name); curl_mime_data(field, ca->value, CURL_ZERO_TERMINATED); a = g_list_next(a); } field = curl_mime_addpart(form); curl_mime_name(field, "image"); curl_mime_filedata(field, filename); curl_easy_setopt(ctx->curl_ctx, CURLOPT_MIMEPOST, form); } else { GString *gargs = g_string_new(""); GList *a = args; while (a) { _curl_args_t *ca = (_curl_args_t *)a->data; if(a!=args) g_string_append(gargs, "&"); g_string_append(gargs, ca->name); g_string_append(gargs, "="); g_string_append(gargs, ca->value); a = g_list_next(a); } curl_easy_setopt(ctx->curl_ctx, CURLOPT_COPYPOSTFIELDS, gargs->str); g_string_free(gargs, TRUE); } int res = curl_easy_perform(ctx->curl_ctx); #if piwigo_EXTRA_VERBOSE == TRUE g_printf("curl_easy_perform status %d\n", res); #endif if(filename) curl_mime_free(form); g_string_free(url, TRUE); ctx->response = NULL; if(res == CURLE_OK) { GError *error = NULL; gboolean ret = json_parser_load_from_data(ctx->json_parser, response->str, response->len, &error); if(!ret) goto cleanup; JsonNode *root = json_parser_get_root(ctx->json_parser); // we should always have a dict if(json_node_get_node_type(root) != JSON_NODE_OBJECT) goto cleanup; ctx->response = json_node_get_object(root); const char *status = json_object_get_string_member(ctx->response, "stat"); ctx->error_occured = (status && (strcmp(status,"fail")==0)); } else ctx->error_occured = TRUE; cleanup: g_string_free(response, TRUE); return res; }