static void menuitem_pick_preset(GtkMenuItem *menuitem, dt_iop_module_t *module) { gchar *name = get_preset_name(menuitem); sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select op_params, enabled, blendop_params, blendop_version, writeprotect from " "presets where operation = ?1 and op_version = ?2 and name = ?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->op, -1, SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module->version()); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, name, -1, SQLITE_TRANSIENT); if(sqlite3_step(stmt) == SQLITE_ROW) { const void *op_params = sqlite3_column_blob(stmt, 0); int op_length = sqlite3_column_bytes(stmt, 0); int enabled = sqlite3_column_int(stmt, 1); const void *blendop_params = sqlite3_column_blob(stmt, 2); int bl_length = sqlite3_column_bytes(stmt, 2); int blendop_version = sqlite3_column_int(stmt, 3); int writeprotect = sqlite3_column_int(stmt, 4); if(op_params && (op_length == module->params_size)) { memcpy(module->params, op_params, op_length); module->enabled = enabled; } if(blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t))) { memcpy(module->blend_params, blendop_params, sizeof(dt_develop_blend_params_t)); } else if(blendop_params && dt_develop_blend_legacy_params(module, blendop_params, blendop_version, module->blend_params, dt_develop_blend_version(), bl_length) == 0) { // do nothing } else { memcpy(module->blend_params, module->default_blendop_params, sizeof(dt_develop_blend_params_t)); } if(!writeprotect) dt_gui_store_last_preset(name); } sqlite3_finalize(stmt); g_free(name); dt_iop_gui_update(module); dt_dev_add_history_item(darktable.develop, module, FALSE); gtk_widget_queue_draw(module->widget); }
static gboolean preset_iop_module_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, gpointer data) { preset_iop_module_callback_description *callback_description = (preset_iop_module_callback_description*)data; dt_iop_module_t *module = callback_description->module; const char* name = callback_description->name; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select op_params, enabled, blendop_params, blendop_version from presets where operation = ?1 and name = ?2", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, module->op, strlen(module->op), SQLITE_TRANSIENT); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, name, strlen(name), SQLITE_TRANSIENT); if(sqlite3_step(stmt) == SQLITE_ROW) { const void *op_params = sqlite3_column_blob(stmt, 0); int op_length = sqlite3_column_bytes(stmt, 0); int enabled = sqlite3_column_int(stmt, 1); const void *blendop_params = sqlite3_column_blob(stmt, 2); int bl_length = sqlite3_column_bytes(stmt, 2); int blendop_version = sqlite3_column_int(stmt, 3); if(op_params && (op_length == module->params_size)) { memcpy(module->params, op_params, op_length); module->enabled = enabled; } if (blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t))) { memcpy(module->blend_params, blendop_params, sizeof(dt_develop_blend_params_t)); } else if (blendop_params && dt_develop_blend_legacy_params(module, blendop_params, blendop_version, module->blend_params, dt_develop_blend_version(), bl_length) == 0) { // do nothing } else { memcpy(module->blend_params, module->default_blendop_params, sizeof(dt_develop_blend_params_t)); } } sqlite3_finalize(stmt); dt_iop_gui_update(module); dt_dev_add_history_item(darktable.develop, module, FALSE); gtk_widget_queue_draw(module->widget); return TRUE; }
void dt_dev_pop_history_items(dt_develop_t *dev, int32_t cnt) { // printf("dev popping all history items >= %d\n", cnt); dt_pthread_mutex_lock(&dev->history_mutex); darktable.gui->reset = 1; dev->history_end = cnt; // reset gui params for all modules GList *modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); memcpy(module->params, module->default_params, module->params_size); memcpy(module->blend_params, module->default_blendop_params,sizeof(dt_develop_blend_params_t)); module->enabled = module->default_enabled; modules = g_list_next(modules); } // go through history and set gui params GList *history = dev->history; for(int i=0; i<cnt && history; i++) { dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data); memcpy(hist->module->params, hist->params, hist->module->params_size); memcpy(hist->module->blend_params, hist->blend_params, sizeof(dt_develop_blend_params_t)); hist->module->enabled = hist->enabled; history = g_list_next(history); } // update all gui modules modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); dt_iop_gui_update(module); modules = g_list_next(modules); } dev->pipe->changed |= DT_DEV_PIPE_SYNCH; dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now. darktable.gui->reset = 0; dt_dev_invalidate_all(dev); dt_pthread_mutex_unlock(&dev->history_mutex); dt_control_queue_redraw_center(); }
void expose(dt_view_t *self, cairo_t *cri, int32_t width_i, int32_t height_i, int32_t pointerx, int32_t pointery) { // startup-time conf parameter: const int32_t capwd = darktable.thumbnail_width; const int32_t capht = darktable.thumbnail_height; // if width or height > max pipeline pixels: center the view and clamp. int32_t width = MIN(width_i, capwd); int32_t height = MIN(height_i, capht); cairo_set_source_rgb (cri, .2, .2, .2); cairo_save(cri); cairo_set_fill_rule(cri, CAIRO_FILL_RULE_EVEN_ODD); cairo_rectangle(cri, 0, 0, width_i, height_i); cairo_rectangle(cri, MAX(1.0, width_i -capwd) *.5f, MAX(1.0, height_i-capht) *.5f, MIN(width, width_i-1), MIN(height, height_i-1)); cairo_fill (cri); cairo_restore(cri); if(width_i > capwd) cairo_translate(cri, -(capwd-width_i) *.5f, 0.0f); if(height_i > capht) cairo_translate(cri, 0.0f, -(capht-height_i)*.5f); cairo_save(cri); dt_develop_t *dev = (dt_develop_t *)self->data; if(dev->gui_synch && !dev->image_loading) { // synch module guis from gtk thread: darktable.gui->reset = 1; GList *modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); dt_iop_gui_update(module); modules = g_list_next(modules); } darktable.gui->reset = 0; dev->gui_synch = 0; } if(dev->image_dirty || dev->pipe->input_timestamp < dev->preview_pipe->input_timestamp) dt_dev_process_image(dev); if(dev->preview_dirty || dev->pipe->input_timestamp > dev->preview_pipe->input_timestamp) dt_dev_process_preview(dev); dt_pthread_mutex_t *mutex = NULL; int wd, ht, stride, closeup; int32_t zoom; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); static cairo_surface_t *image_surface = NULL; static int image_surface_width = 0, image_surface_height = 0, image_surface_imgid = -1; if(image_surface_width != width || image_surface_height != height || image_surface == NULL) { // create double-buffered image to draw on, to make modules draw more fluently. image_surface_width = width; image_surface_height = height; if(image_surface) cairo_surface_destroy(image_surface); image_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); image_surface_imgid = -1; // invalidate old stuff } cairo_surface_t *surface; cairo_t *cr = cairo_create(image_surface); // adjust scroll bars { float zx = zoom_x, zy = zoom_y, boxw = 1., boxh = 1.; dt_dev_check_zoom_bounds(dev, &zx, &zy, zoom, closeup, &boxw, &boxh); dt_view_set_scrollbar(self, zx+.5-boxw*.5, 1.0, boxw, zy+.5-boxh*.5, 1.0, boxh); } if(!dev->image_dirty && dev->pipe->input_timestamp >= dev->preview_pipe->input_timestamp) { // draw image mutex = &dev->pipe->backbuf_mutex; dt_pthread_mutex_lock(mutex); wd = dev->pipe->backbuf_width; ht = dev->pipe->backbuf_height; stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, wd); surface = cairo_image_surface_create_for_data (dev->pipe->backbuf, CAIRO_FORMAT_RGB24, wd, ht, stride); cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, .5f*(width-wd), .5f*(height-ht)); if(closeup) { const float closeup_scale = 2.0; cairo_scale(cr, closeup_scale, closeup_scale); float boxw = 1, boxh = 1, zx0 = zoom_x, zy0 = zoom_y, zx1 = zoom_x, zy1 = zoom_y, zxm = -1.0, zym = -1.0; dt_dev_check_zoom_bounds(dev, &zx0, &zy0, zoom, 0, &boxw, &boxh); dt_dev_check_zoom_bounds(dev, &zx1, &zy1, zoom, 1, &boxw, &boxh); dt_dev_check_zoom_bounds(dev, &zxm, &zym, zoom, 1, &boxw, &boxh); const float fx = 1.0 - fmaxf(0.0, (zx0 - zx1)/(zx0 - zxm)), fy = 1.0 - fmaxf(0.0, (zy0 - zy1)/(zy0 - zym)); cairo_translate(cr, -wd/(2.0*closeup_scale) * fx, -ht/(2.0*closeup_scale) * fy); } cairo_rectangle(cr, 0, 0, wd, ht); cairo_set_source_surface (cr, surface, 0, 0); cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .3, .3, .3); cairo_stroke(cr); cairo_surface_destroy (surface); dt_pthread_mutex_unlock(mutex); image_surface_imgid = dev->image_storage.id; } else if(!dev->preview_dirty) { // draw preview mutex = &dev->preview_pipe->backbuf_mutex; dt_pthread_mutex_lock(mutex); wd = dev->preview_pipe->backbuf_width; ht = dev->preview_pipe->backbuf_height; float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_rectangle(cr, 0, 0, width, height); cairo_clip(cr); stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, wd); surface = cairo_image_surface_create_for_data (dev->preview_pipe->backbuf, CAIRO_FORMAT_RGB24, wd, ht, stride); cairo_translate(cr, width/2.0, height/2.0f); cairo_scale(cr, zoom_scale, zoom_scale); cairo_translate(cr, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); // avoid to draw the 1px garbage that sometimes shows up in the preview :( cairo_rectangle(cr, 0, 0, wd-1, ht-1); cairo_set_source_surface (cr, surface, 0, 0); cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); cairo_fill(cr); cairo_surface_destroy (surface); dt_pthread_mutex_unlock(mutex); image_surface_imgid = dev->image_storage.id; } cairo_restore(cri); if(image_surface_imgid == dev->image_storage.id) { cairo_destroy(cr); cairo_set_source_surface(cri, image_surface, 0, 0); cairo_paint(cri); } /* check if we should create a snapshot of view */ if(darktable.develop->proxy.snapshot.request) { /* reset the request */ darktable.develop->proxy.snapshot.request = FALSE; /* validation of snapshot filename */ g_assert(darktable.develop->proxy.snapshot.filename != NULL); /* Store current image surface to snapshot file. FIXME: add checks so that we dont make snapshots of preview pipe image surface. */ cairo_surface_write_to_png(image_surface, darktable.develop->proxy.snapshot.filename); } // Displaying sample areas if enabled if(darktable.lib->proxy.colorpicker.live_samples && darktable.lib->proxy.colorpicker.display_samples) { GSList *samples = darktable.lib->proxy.colorpicker.live_samples; dt_colorpicker_sample_t *sample = NULL; cairo_save(cri); int32_t zoom, closeup; float zoom_x, zoom_y; float wd = dev->preview_pipe->backbuf_width; float ht = dev->preview_pipe->backbuf_height; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_translate(cri, width/2.0, height/2.0f); cairo_scale(cri, zoom_scale, zoom_scale); cairo_translate(cri, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); while(samples) { sample = samples->data; cairo_set_line_width(cri, 1.0/zoom_scale); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .2, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .2); float *box = sample->box; float *point = sample->point; if(sample->size == DT_COLORPICKER_SIZE_BOX) { cairo_rectangle(cri, box[0]*wd, box[1]*ht, (box[2] - box[0])*wd, (box[3] - box[1])*ht); cairo_stroke(cri); cairo_translate(cri, 1.0/zoom_scale, 1.0/zoom_scale); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .8, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .8); cairo_rectangle(cri, box[0]*wd + 1.0/zoom_scale, box[1]*ht, (box[2] - box[0])*wd - 3./zoom_scale, (box[3] - box[1])*ht - 2./zoom_scale); cairo_stroke(cri); } else { cairo_rectangle(cri, point[0] * wd - .01 * wd, point[1] * ht - .01 * wd, .02 * wd, .02 * wd); cairo_stroke(cri); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .8, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .8); cairo_rectangle(cri, (point[0] - 0.01) * wd + 1.0/zoom_scale, point[1] * ht - 0.01 * wd + 1.0/zoom_scale, .02 * wd - 2./zoom_scale, .02 * wd - 2./zoom_scale); cairo_move_to(cri, point[0] * wd, point[1] * ht - .01 * wd + 1./zoom_scale); cairo_line_to(cri, point[0] * wd, point[1] * ht + .01 * wd - 1./zoom_scale); cairo_move_to(cri, point[0] * wd - .01 * wd + 1./zoom_scale, point[1] * ht); cairo_line_to(cri, point[0] * wd + .01 * wd - 1./zoom_scale, point[1] * ht); cairo_stroke(cri); } samples = g_slist_next(samples); } cairo_restore(cri); } // execute module callback hook. if(dev->gui_module && dev->gui_module->request_color_pick) { int32_t zoom, closeup; float zoom_x, zoom_y; float wd = dev->preview_pipe->backbuf_width; float ht = dev->preview_pipe->backbuf_height; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_translate(cri, width/2.0, height/2.0f); cairo_scale(cri, zoom_scale, zoom_scale); cairo_translate(cri, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); // cairo_set_operator(cri, CAIRO_OPERATOR_XOR); cairo_set_line_width(cri, 1.0/zoom_scale); cairo_set_source_rgb(cri, .2, .2, .2); float *box = dev->gui_module->color_picker_box; float *point = dev->gui_module->color_picker_point; if(darktable.lib->proxy.colorpicker.size) { cairo_rectangle(cri, box[0]*wd, box[1]*ht, (box[2] - box[0])*wd, (box[3] - box[1])*ht); cairo_stroke(cri); cairo_translate(cri, 1.0/zoom_scale, 1.0/zoom_scale); cairo_set_source_rgb(cri, .8, .8, .8); cairo_rectangle(cri, box[0]*wd + 1.0/zoom_scale, box[1]*ht, (box[2] - box[0])*wd - 3./zoom_scale, (box[3] - box[1])*ht - 2./zoom_scale); cairo_stroke(cri); } else { cairo_rectangle(cri, point[0] * wd - .01 * wd, point[1] * ht - .01 * wd, .02 * wd, .02 * wd); cairo_stroke(cri); cairo_set_source_rgb(cri, .8, .8, .8); cairo_rectangle(cri, (point[0] - 0.01) * wd + 1.0/zoom_scale, point[1] * ht - 0.01 * wd + 1.0/zoom_scale, .02 * wd - 2./zoom_scale, .02 * wd - 2./zoom_scale); cairo_move_to(cri, point[0] * wd, point[1] * ht - .01 * wd + 1./zoom_scale); cairo_line_to(cri, point[0] * wd, point[1] * ht + .01 * wd - 1./zoom_scale); cairo_move_to(cri, point[0] * wd - .01 * wd + 1./zoom_scale, point[1] * ht); cairo_line_to(cri, point[0] * wd + .01 * wd - 1./zoom_scale, point[1] * ht); cairo_stroke(cri); } } else if(dev->gui_module && dev->gui_module->gui_post_expose) { if(width_i > capwd) pointerx += (capwd-width_i) *.5f; if(height_i > capht) pointery += (capht-height_i)*.5f; dev->gui_module->gui_post_expose(dev->gui_module, cri, width, height, pointerx, pointery); } }
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(); }