void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe) { free(pipe->backbuf); pipe->backbuf = NULL; pipe->backbuf_size = 0; pthread_mutex_destroy(&(pipe->backbuf_mutex)); dt_dev_pixelpipe_cleanup_nodes(pipe); // gegl_buffer_destroy(pipe->input_buffer); // TODO: necessary? g_object_unref(pipe->gegl); // should destroy all gegl related stuff. pipe->gegl = NULL; pipe->input = NULL; pipe->output = NULL; pipe->input_buffer = NULL; pipe->nodes = NULL; }
void dt_dev_pixelpipe_change(dt_dev_pixelpipe_t *pipe, struct dt_develop_t *dev) { pthread_mutex_lock(&dev->history_mutex); switch (pipe->changed) { case DT_DEV_PIPE_UNCHANGED: break; case DT_DEV_PIPE_TOP_CHANGED: // only top history item changed. dt_dev_pixelpipe_synch_top(pipe, dev); break; case DT_DEV_PIPE_SYNCH: // pipeline topology remains intact, only change all params. dt_dev_pixelpipe_synch_all(pipe, dev); break; default: // DT_DEV_PIPE_REMOVE // modules have been added in between or removed. need to rebuild the whole pipeline. dt_dev_pixelpipe_cleanup_nodes(pipe); dt_dev_pixelpipe_create_nodes(pipe, dev); break; } pipe->changed = DT_DEV_PIPE_UNCHANGED; pthread_mutex_unlock(&dev->history_mutex); }
void dt_dev_process_image_job(dt_develop_t *dev) { dt_control_log_busy_enter(); // let gui know to draw preview instead of us, if it's there: dev->image_dirty = 1; dt_mipmap_buffer_t buf; dt_times_t start; dt_get_times(&start); dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, dev->image_storage.id, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING); dt_show_times(&start, "[dev]", "to load the image."); // copy over image now that width and height are sure to be correct: const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, dev->image_storage.id); dev->image_storage = *img; // but don't lock the real thing, as that would avoid any writers to change stuff. // (such as raw loading or star rating changes) dt_image_cache_read_release(darktable.image_cache, img); // failed to load raw? if(!buf.buf) return; dt_dev_pixelpipe_set_input(dev->pipe, dev, (float *)buf.buf, buf.width, buf.height, 1.0); if(dev->image_loading) { // init pixel pipeline dt_dev_pixelpipe_cleanup_nodes(dev->pipe); dt_dev_pixelpipe_create_nodes(dev->pipe, dev); if(dev->image_force_reload) dt_dev_pixelpipe_flush_caches(dev->pipe); dev->image_dirty = 1; dev->image_force_reload = 0; if(dev->gui_attached) { // during load, a mipf update could have been issued. dev->preview_input_changed = 1; dev->preview_dirty = 1; dev->gui_synch = 1; // notify gui thread we want to synch (call gui_update in the modules) dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; } dev->pipe->changed |= DT_DEV_PIPE_SYNCH; } dt_dev_zoom_t zoom; float zoom_x, zoom_y, scale; int x, y; // adjust pipeline according to changed flag set by {add,pop}_history_item. restart: if(dev->gui_leaving) { dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); dt_control_log_busy_leave(); return; } dev->pipe->input_timestamp = dev->timestamp; // this locks dev->history_mutex. dt_dev_pixelpipe_change(dev->pipe, dev); // determine scale according to new dimensions DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); scale = dt_dev_get_zoom_scale(dev, zoom, 1.0f, 0); dev->capwidth = MIN(MIN(dev->width, dev->pipe->processed_width *scale), darktable.thumbnail_width); dev->capheight = MIN(MIN(dev->height, dev->pipe->processed_height*scale), darktable.thumbnail_height); x = MAX(0, scale*dev->pipe->processed_width *(.5+zoom_x)-dev->capwidth/2); y = MAX(0, scale*dev->pipe->processed_height*(.5+zoom_y)-dev->capheight/2); dt_get_times(&start); if(dt_dev_pixelpipe_process(dev->pipe, dev, x, y, dev->capwidth, dev->capheight, scale)) { // interrupted because image changed? if(dev->image_force_reload) { dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); dt_control_log_busy_leave(); return; } // or because the pipeline changed? else goto restart; } dt_show_times(&start, "[dev_process_image] pixel pipeline processing", NULL); // maybe we got zoomed/panned in the meantime? if(dev->pipe->changed != DT_DEV_PIPE_UNCHANGED) goto restart; // cool, we got a new image! dev->image_dirty = 0; dev->image_loading = 0; dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); dt_control_queue_redraw_center(); dt_control_log_busy_leave(); }
void dt_dev_process_preview_job(dt_develop_t *dev) { dt_mipmap_buffer_t buf; if(dev->image_loading) { // raw is already loading, no use starting another file access, we wait. return; } dt_control_log_busy_enter(); dev->preview_pipe->input_timestamp = dev->timestamp; dev->preview_dirty = 1; // lock if there, issue a background load, if not (best-effort for mip f). dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, dev->image_storage.id, DT_MIPMAP_F, 0); if(!buf.buf) { dt_control_log_busy_leave(); return; // not loaded yet. load will issue a gtk redraw on completion, which in turn will trigger us again later. } // init pixel pipeline for preview. dt_dev_pixelpipe_set_input(dev->preview_pipe, dev, (float *)buf.buf, buf.width, buf.height, dev->image_storage.width/(float)buf.width); if(dev->preview_loading) { dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe); dt_dev_pixelpipe_create_nodes(dev->preview_pipe, dev); dt_dev_pixelpipe_flush_caches(dev->preview_pipe); dev->preview_loading = 0; } // if raw loaded, get new mipf if(dev->preview_input_changed) { dt_dev_pixelpipe_flush_caches(dev->preview_pipe); dev->preview_input_changed = 0; } // always process the whole downsampled mipf buffer, to allow for fast scrolling and mip4 write-through. restart: if(dev->gui_leaving) { dt_control_log_busy_leave(); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); return; } // adjust pipeline according to changed flag set by {add,pop}_history_item. // this locks dev->history_mutex. dt_times_t start; dt_get_times(&start); dt_dev_pixelpipe_change(dev->preview_pipe, dev); if(dt_dev_pixelpipe_process(dev->preview_pipe, dev, 0, 0, dev->preview_pipe->processed_width*dev->preview_downsampling, dev->preview_pipe->processed_height*dev->preview_downsampling, dev->preview_downsampling)) { if(dev->preview_loading || dev->preview_input_changed) { dt_control_log_busy_leave(); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); return; } else goto restart; } dt_show_times(&start, "[dev_process_preview] pixel pipeline processing", NULL); dev->preview_dirty = 0; dt_control_log_busy_leave(); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); }
static void dt_dev_change_image(dt_develop_t *dev, const uint32_t imgid) { // stop crazy users from sleeping on key-repeat spacebar: if(dev->image_loading) return; // get last active plugin, make sure focus out is called: gchar *active_plugin = dt_conf_get_string("plugins/darkroom/active"); dt_iop_request_focus(NULL); // store last active group dt_conf_set_int("plugins/darkroom/groups", dt_dev_modulegroups_get(dev)); // 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", ""); g_assert(dev->gui_attached); // commit image ops to db dt_dev_write_history(dev); // be sure light table will update the thumbnail // TODO: only if image changed! // if() { dt_mipmap_cache_remove(darktable.mipmap_cache, dev->image_storage.id); dt_image_synch_xmp(dev->image_storage.id); } select_this_image(imgid); while(dev->history) { // clear history of old image free(((dt_dev_history_item_t *)dev->history->data)->params); free( (dt_dev_history_item_t *)dev->history->data); dev->history = g_list_delete_link(dev->history, dev->history); } // get new image: dt_dev_reload_image(dev, imgid); // make sure no signals propagate here: darktable.gui->reset = 1; GList *modules = g_list_last(dev->iop); int nb_iop = g_list_length(dev->iop); dt_dev_pixelpipe_cleanup_nodes(dev->pipe); dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe); for (int i=nb_iop-1; i>0; i--) { dt_iop_module_t *module = (dt_iop_module_t *)(g_list_nth_data(dev->iop,i)); if (module->multi_priority == 0) //if the module is the "base" instance, we keep it { dt_iop_reload_defaults(module); dt_iop_gui_update(module); } else //else we delete it and remove it from the panel { if (!dt_iop_is_hidden(module)) { gtk_container_remove (GTK_CONTAINER(dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER)),module->expander); dt_iop_gui_cleanup_module(module); } //we remove the module from the list dev->iop = g_list_remove_link(dev->iop,g_list_nth(dev->iop,i)); //we cleanup the module dt_accel_disconnect_list(module->accel_closures); dt_accel_cleanup_locals_iop(module); module->accel_closures = NULL; dt_iop_cleanup_module(module); free(module); } } dt_dev_pixelpipe_create_nodes(dev->pipe, dev); dt_dev_pixelpipe_create_nodes(dev->preview_pipe, dev); dt_dev_read_history(dev); //we have to init all module instances other than "base" instance modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); if(module->multi_priority > 0) { if (!dt_iop_is_hidden(module)) { module->gui_init(module); dt_iop_reload_defaults(module); //we search the base iop corresponding GList *mods = g_list_first(dev->iop); dt_iop_module_t *base = NULL; int pos_module = 0; int pos_base = 0; int pos = 0; while (mods) { dt_iop_module_t *mod = (dt_iop_module_t *)(mods->data); if (mod->multi_priority == 0 && mod->instance == module->instance) { base = mod; pos_base = pos; } else if (mod == module) pos_module = pos; mods = g_list_next(mods); pos++; } if (!base) continue; /* 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); GValue gv = { 0, { { 0 } } }; g_value_init(&gv,G_TYPE_INT); gtk_container_child_get_property(GTK_CONTAINER(dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER)),base->expander,"position",&gv); gtk_box_reorder_child (dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER),expander,g_value_get_int(&gv)+pos_base-pos_module); dt_iop_gui_set_expanded(module, TRUE); dt_iop_gui_update_blending(module); } /* setup key accelerators */ module->accel_closures = NULL; if(module->connect_key_accels) module->connect_key_accels(module); dt_iop_connect_common_accels(module); //we update show params for multi-instances for each other instances dt_dev_modules_update_multishow(module->dev); } modules = g_list_next(modules); } dt_dev_pop_history_items(dev, dev->history_end); if(active_plugin) { 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); } /* last set the group to update visibility of iop modules for new pipe */ dt_dev_modulegroups_set(dev,dt_conf_get_int("plugins/darkroom/groups")); // make signals work again, but only after focus event, // to avoid crop/rotate for example to add another history item. darktable.gui->reset = 0; // Signal develop initialize dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_IMAGE_CHANGED); // 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()); }