61#define DT_LIB_SNAPSHOTS_COUNT 4
62#define SNAP_LOG(...) dt_print(DT_DEBUG_DEV, __VA_ARGS__)
63#define HANDLE_SIZE DT_PIXEL_APPLY_DPI_DPP(36)
141 SNAP_LOG(
"[snapshots] capture failed: invalid inputs snapshot=%p source=%p\n", (
void *)snapshot,
165 GList *history_copy = NULL;
166 GList *iop_order_copy = NULL;
167 int32_t history_end = 0;
176 frozen->
history = history_copy;
180 for(GList *history = g_list_first(frozen->
history); history; history = g_list_next(history))
191 SNAP_LOG(
"[snapshots] capture failed: unresolved module op=%s multi=%s priority=%d for imgid=%d\n",
205 SNAP_LOG(
"[snapshots] capture success: imgid=%d history_end=%d items=%d\n", snapshot->
imgid,
225 gboolean pipe_ready =
FALSE;
228 gboolean input_ready =
FALSE;
229 const char *fail_reason =
"unknown";
236 SNAP_LOG(
"[snapshots] refresh failed: darkroom dev unavailable\n");
241 SNAP_LOG(
"[snapshots] refresh failed: snapshot dev unavailable selected=%u imgid=%d\n",
d->selected,
248 SNAP_LOG(
"[snapshots] refresh failed: preview pipe unavailable\n");
261 fail_reason =
"mipmap full unavailable";
267 fail_reason =
"pixelpipe init preview failed";
291 fail_reason =
"invalid output roi";
297 fail_reason =
"pixelpipe process failed";
304 fail_reason =
"backbuffer hash invalid";
317 snapshot_pipe.
devid, NULL)
320 fail_reason =
"cache peek failed";
326 if(bw <= 0 || bh <= 0)
328 fail_reason =
"invalid backbuffer size";
332 const int src_stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, bw);
333 const size_t required = (size_t)src_stride * (
size_t)bh;
336 fail_reason =
"cache entry too small";
340 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, bw, bh);
343 fail_reason =
"cairo surface create failed";
349 uint8_t *dst = cairo_image_surface_get_data(surface);
350 const int dst_stride = cairo_image_surface_get_stride(surface);
351 for(
int y = 0; y < bh; y++)
352 memcpy(dst + (
size_t)y * dst_stride, (
const uint8_t *)data + (
size_t)y * src_stride, (
size_t)src_stride);
353 cairo_surface_mark_dirty(surface);
356 if(
d->snapshot_image) cairo_surface_destroy(
d->snapshot_image);
357 d->snapshot_image = surface;
367 SNAP_LOG(
"[snapshots] refresh failed: reason=%s snapshot_imgid=%d selected=%u frozen_imgid=%d in=%ux%u "
368 "pipe_in=%dx%d pipe_out=%dx%d\n",
380 return _(
"Snapshots");
385 static const char *
v[] = {
"darkroom", NULL};
400static void _draw_sym(cairo_t *cr,
float x,
float y, gboolean vertical, gboolean inverted)
402 const double inv = inverted ? -0.1 : 1.0;
406 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
408 PangoLayout *layout = pango_cairo_create_layout(cr);
409 pango_layout_set_font_description(layout, desc);
410 pango_layout_set_text(layout, C_(
"snapshot sign",
"S"), -1);
411 pango_layout_get_pixel_extents(layout, &ink, NULL);
419 pango_cairo_show_layout(cr, layout);
420 pango_font_description_free(desc);
421 g_object_unref(layout);
434 if(
d->selected >= 1 &&
d->selected <=
d->size)
438 if(
d->snapshot_imgid != s->
imgid
439 || fabsf(
d->snapshot_zoom_level - current_zoom_level) > 1e-6f)
442 if(!
d->snapshot_image)
return;
446 float snapshot_scale = 1.0f;
447 if(
d->selected >= 1 &&
d->selected <=
d->size)
453 const float render_scale = zoom_level / snapshot_scale;
454 const float surface_width = cairo_image_surface_get_width(
d->snapshot_image) /
darktable.
gui->
ppd;
455 const float surface_height = cairo_image_surface_get_height(
d->snapshot_image) /
darktable.
gui->
ppd;
456 const double tx = 0.5 *
width - dev->
roi.
x * surface_width * render_scale;
457 const double ty = 0.5 *
height - dev->
roi.
y * surface_height * render_scale;
459 float image_box[4] = { 0.0f };
461 if(image_box[2] <= 0.0f || image_box[3] <= 0.0f)
return;
462 d->vp_x = image_box[0];
463 d->vp_y = image_box[1];
464 d->vp_width = image_box[2];
465 d->vp_height = image_box[3];
466 const double split_x = CLAMP(
d->vp_xpointer, 0.0, 1.0);
467 const double split_y = CLAMP(
d->vp_ypointer, 0.0, 1.0);
472 double w =
d->vp_width;
473 double h =
d->vp_height;
476 x =
d->inverted ?
d->vp_x +
d->vp_width * split_x :
d->vp_x;
477 w =
d->inverted ?
d->vp_width * (1.0 - split_x) :
d->vp_width * split_x;
481 y =
d->inverted ?
d->vp_y +
d->vp_height * split_y :
d->vp_y;
482 h =
d->inverted ?
d->vp_height * (1.0 - split_y) :
d->vp_height * split_y;
488 cairo_rectangle(cri,
x, y, w, h);
490 cairo_translate(cri, tx, ty);
491 cairo_scale(cri, render_scale, render_scale);
492 cairo_set_source_surface(cri,
d->snapshot_image, 0.0, 0.0);
493 cairo_pattern_set_filter(cairo_get_source(cri), CAIRO_FILTER_NEAREST);
500 cairo_set_line_width(cri, 1.);
504 const double lx =
d->vp_x +
d->vp_width * split_x;
505 const double center =
d->vp_y + 0.5 *
d->vp_height;
508 cairo_move_to(cri, lx,
d->vp_y);
509 cairo_line_to(cri, lx,
d->vp_y +
d->vp_height);
515 cairo_move_to(cri, lx, center -
size);
516 cairo_line_to(cri, lx - (
size * 1.2), center);
517 cairo_line_to(cri, lx, center +
size);
518 cairo_close_path(cri);
527 const double ly =
d->vp_y +
d->vp_height * split_y;
528 const double center =
d->vp_x + 0.5 *
d->vp_width;
531 cairo_move_to(cri,
d->vp_x, ly);
532 cairo_line_to(cri,
d->vp_x +
d->vp_width, ly);
538 cairo_move_to(cri, center -
size, ly);
539 cairo_line_to(cri, center, ly - (
size * 1.2));
540 cairo_line_to(cri, center +
size, ly);
541 cairo_close_path(cri);
553 const gint rx = (
d->vertical ?
d->vp_x +
d->vp_width * split_x :
d->vp_x +
d->vp_width * 0.5)
555 const gint ry = (
d->vertical ?
d->vp_y +
d->vp_height * 0.5 :
d->vp_y +
d->vp_height * split_y)
560 cairo_set_line_width(cri, 0.5);
583 if(
d->snapshot_image && which == 1)
588 d->hover_rotation =
FALSE;
602 if(which != 1)
return 0;
606 if(
d->snapshot_image)
608 if(
d->on_going)
return 1;
609 if(
d->vp_width <= 0.0 ||
d->vp_height <= 0.0)
return 0;
610 if(x < d->vp_x ||
x >
d->vp_x +
d->vp_width || y < d->vp_y || y >
d->vp_y +
d->vp_height)
return 0;
612 const double xp = CLAMP((
x -
d->vp_x) /
d->vp_width, 0.0, 1.0);
613 const double yp = CLAMP((y -
d->vp_y) /
d->vp_height, 0.0, 1.0);
615 if(
d->hover_rotation)
620 d->vertical = !
d->vertical;
649 if(
d->snapshot_image)
651 if(
d->vp_width <= 0.0 ||
d->vp_height <= 0.0)
return 0;
652 const double xp = CLAMP((
x -
d->vp_x) /
d->vp_width, 0.0, 1.0);
653 const double yp = CLAMP((y -
d->vp_y) /
d->vp_height, 0.0, 1.0);
654 d->hover_rotation =
FALSE;
658 const double split_x = CLAMP(
d->vp_xpointer, 0.0, 1.0);
659 const double split_y = CLAMP(
d->vp_ypointer, 0.0, 1.0);
661 const double rxc =
d->vertical ?
d->vp_x +
d->vp_width * split_x :
d->vp_x +
d->vp_width * 0.5;
662 const double ryc =
d->vertical ?
d->vp_y +
d->vp_height * 0.5 :
d->vp_y +
d->vp_height * split_y;
663 const double dx =
x - rxc;
664 const double dy = y - ryc;
665 d->hover_rotation = (dx * dx + dy * dy) < (handle_mouse * handle_mouse);
683 d->num_snapshots = 0;
684 d->hover_rotation =
FALSE;
685 if(
d->snapshot_image)
687 cairo_surface_destroy(
d->snapshot_image);
688 d->snapshot_image = NULL;
691 d->snapshot_zoom_level = -1.0f;
693 for(uint32_t
k = 0;
k <
d->size;
k++)
696 gtk_widget_hide(
d->snapshot[
k].button);
697 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->snapshot[
k].button),
FALSE);
707 self->
data = (
void *)
d;
716 d->vp_xpointer = 0.5;
717 d->vp_ypointer = 0.5;
722 d->hover_rotation =
FALSE;
724 d->snapshot_zoom_level = -1.0f;
732 _(
"take snapshot to compare with another image "
733 "or the same image at another stage of development"), 0, 0);
738 char wdname[32] = { 0 };
742 for(
int k = 0;
k <
d->size;
k++)
745 d->snapshot[
k].button = gtk_toggle_button_new_with_label(wdname);
746 GtkWidget *label = gtk_bin_get_child(GTK_BIN(
d->snapshot[
k].button));
747 gtk_widget_set_halign(label, GTK_ALIGN_START);
748 gtk_label_set_xalign(GTK_LABEL(label), 0);
749 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
751 g_signal_connect(G_OBJECT(
d->snapshot[
k].button),
"clicked",
755 g_object_set_data(G_OBJECT(
d->snapshot[
k].button),
"snapshot", GINT_TO_POINTER(
k + 1));
758 gchar *snapshot_file = g_strdup_printf(
"dt_snapshot_%d.png",
k);
763 gtk_box_pack_start(GTK_BOX(
d->snapshots_box), GTK_WIDGET(
d->snapshot[
k].button),
FALSE,
FALSE, 0);
766 gtk_widget_set_no_show_all(
d->snapshot[
k].button,
TRUE);
770 gtk_box_pack_start(GTK_BOX(self->
widget),
773 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
d->take_button),
TRUE,
TRUE, 0);
783 cairo_surface_destroy(
d->snapshot_image);
784 d->snapshot_image = NULL;
800 if(
d->size <= 0)
return;
804 for(
int k =
d->size - 1;
k > 0;
k--)
807 d->snapshot[
k] =
d->snapshot[
k - 1];
808 d->snapshot[
k].button = b;
809 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
d->snapshot[
k].button))),
810 gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
d->snapshot[
k - 1].button)))));
816 d->snapshot[0] = last;
818 const gchar *
name = _(
"original");
819 gchar *dynamic_name = NULL;
834 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
d->snapshot[0].button))), label);
848 if(
d->num_snapshots !=
d->size)
d->num_snapshots++;
851 for(uint32_t
k = 0;
k <
d->num_snapshots;
k++) gtk_widget_show(
d->snapshot[
k].button);
860 int which = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"snapshot"));
863 if(gtk_toggle_button_get_active(widget))
868 cairo_surface_destroy(
d->snapshot_image);
869 d->snapshot_image = NULL;
873 for(uint32_t
k = 0;
k <
d->size;
k++)
874 if(GTK_WIDGET(widget) !=
d->snapshot[
k].button)
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->snapshot[
k].button),
FALSE);
884 else if(
d->selected == (uint32_t)which)
889 cairo_surface_destroy(
d->snapshot_image);
890 d->snapshot_image = NULL;
void cleanup(dt_imageio_module_format_t *self)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
void dt_control_change_cursor_by_name_and_flush(const char *curs_str)
Apply a named cursor immediatelly and flush display updates for immediate feedback.
void dt_control_commit_cursor()
void dt_control_queue_cursor_by_name(const char *curs_str)
Queue a GTK named cursor for the next cursor commit.
uint32_t view(const dt_view_t *self)
void dt_concat_path_file(char destination[PATH_MAX], const char path[PATH_MAX], const char *const file)
#define DT_MODULE(MODVER)
static void dt_free_gpointer(gpointer ptr)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
dt_iop_module_t * dt_dev_get_module_instance(dt_develop_t *dev, const char *op, const char *multi_name, const int multi_priority)
Find a module instance by op name and instance metadata.
dt_iop_module_t * dt_dev_create_module_instance(dt_develop_t *dev, const char *op, const char *multi_name, const int multi_priority, gboolean use_next_priority)
Create a new module instance from an existing base .so.
void dt_dev_history_free_history(dt_develop_t *dev)
Free the whole history list attached to dev->history.
uint64_t dt_dev_history_compute_hash(dt_develop_t *dev)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
void dt_dev_set_history_end_ext(struct dt_develop_t *dev, const uint32_t index)
Set the history end index (GUI perspective).
int32_t dt_dev_get_history_end_ext(struct dt_develop_t *dev)
Get the current history end index (GUI perspective).
void dt_dev_pixelpipe_propagate_formats(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_get_roi_out(dt_dev_pixelpipe_t *pipe, const int width_in, const int height_in, int *width, int *height)
void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
Get the displayed image rectangle in darkroom widget coordinates.
void dt_dev_cleanup(dt_develop_t *dev)
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
float dt_dev_get_zoom_level(const dt_develop_t *dev)
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
void dtgtk_cairo_paint_refresh(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
#define dt_pthread_rwlock_unlock
#define dt_pthread_rwlock_rdlock
void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
#define DT_GUI_MOUSE_EFFECT_RADIUS
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
GList * dt_history_duplicate(GList *hist)
Deep-copy a history list.
dt_iop_module_t * dt_iop_get_module_by_op_priority(GList *modules, const char *operation, const int multi_priority)
GList * dt_ioppr_iop_order_copy_deep(GList *iop_order_list)
Deep-copy an order list.
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
float *const restrict const size_t k
#define dt_mipmap_cache_get(A, B, C, D, E, F)
#define dt_mipmap_cache_release(A, B)
gboolean dt_dev_pixelpipe_cache_peek(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry, const int preferred_devid, void **cl_mem_output)
Non-owning lookup of an existing cache line.
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache entry.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height, float iscale, dt_mipmap_size_t size)
void dt_dev_pixelpipe_set_icc(dt_dev_pixelpipe_t *pipe, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
int dt_dev_pixelpipe_init_preview(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
void dt_dev_pixelpipe_create_nodes(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe)
int dt_dev_pixelpipe_process(dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
#define dt_dev_pixelpipe_synch_all(pipe)
static uint64_t dt_dev_backbuf_get_hash(const dt_backbuf_t *backbuf)
static int _lib_snapshot_rotation_cnt
void gui_reset(dt_lib_module_t *self)
static void _draw_sym(cairo_t *cr, float x, float y, gboolean vertical, gboolean inverted)
static void _lib_snapshots_add_button_clicked_callback(GtkWidget *widget, gpointer user_data)
static int _lib_snapshot_capture_state(dt_lib_snapshot_t *snapshot, dt_develop_t *source)
Freeze the current darkroom develop state into one snapshot-local develop context.
static void _lib_snapshots_toggled_callback(GtkToggleButton *widget, gpointer user_data)
void gui_cleanup(dt_lib_module_t *self)
static void _lib_snapshot_clear_state(dt_lib_snapshot_t *snap)
int mouse_moved(dt_lib_module_t *self, double x, double y, double pressure, int which)
static int _lib_snapshots_refresh_pipe_image(dt_lib_module_t *self, dt_lib_snapshot_t *snap)
Recompute the selected snapshot image from a dedicated preview pipe at one history fence.
uint32_t container(dt_lib_module_t *self)
int button_pressed(struct dt_lib_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
void gui_init(dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
void gui_post_expose(dt_lib_module_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
int button_released(struct dt_lib_module_t *self, double x, double y, int which, uint32_t state)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
unsigned __int64 uint64_t
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
struct dt_gui_gtk_t * gui
struct dt_mipmap_cache_t * mipmap_cache
struct dt_bauhaus_t * bauhaus
struct dt_develop_t * develop
struct dt_view_manager_t * view_manager
PangoFontDescription * pango_font_desc
dt_colorspaces_color_profile_type_t icc_type
dt_iop_color_intent_t icc_intent
dt_pthread_rwlock_t history_mutex
struct dt_dev_pixelpipe_t * preview_pipe
struct dt_develop_t::@17 roi
Region of interest passed through the pixelpipe.
dt_lib_snapshot_t * snapshot
cairo_surface_t * snapshot_image
float snapshot_zoom_level
GtkWidget * snapshots_box
struct dt_view_manager_t::@67 proxy
void(* set_default_cursor)(struct dt_view_t *view, double x, double y)
struct dt_view_manager_t::@67::@70 darkroom
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER