37
36
#include "dtgtk/resetlabel.h"
38
37
#include "dtgtk/togglebutton.h"
39
38
#include "dtgtk/button.h"
39
#include "gui/accelerators.h"
40
40
#include "gui/gtk.h"
41
41
#include "gui/draw.h"
42
42
#include "gui/presets.h"
46
// number of gui ratios in combo box
46
49
/** flip H/V, rotate an image, then clip the buffer. */
47
50
typedef enum dt_iop_clipping_flags_t
89
92
typedef struct dt_iop_clipping_gui_data_t
91
94
GtkDarktableSlider *scale5, *keystone_h,*keystone_v;
95
GtkWidget *swap_button;
92
96
GtkDarktableToggleButton *hflip,*vflip;
93
97
GtkComboBoxEntry *aspect_presets;
94
98
GtkComboBox *guide_lines;
96
100
GtkDarktableToggleButton *flipHorGoldenGuide, *flipVerGoldenGuide;
97
101
GtkCheckButton *goldenSectionBox, *goldenSpiralSectionBox, *goldenSpiralBox, *goldenTriangleBox;
98
GClosure *commit_callback;
99
GClosure *undo_callback;
101
103
float button_down_zoom_x, button_down_zoom_y, button_down_angle; // position in image where the button has been pressed.
102
104
float clip_x, clip_y, clip_w, clip_h, handle_x, handle_y;
103
105
float old_clip_x, old_clip_y, old_clip_w, old_clip_h;
104
int cropping, straightening;
105
float aspect_ratios[9];
106
int cropping, straightening, applied;
107
float aspect_ratios[NUM_RATIOS];
106
108
float current_aspect;
108
110
dt_iop_clipping_gui_data_t;
163
165
return IOP_TAG_DISTORT;
169
operation_tags_filter ()
171
// switch off watermark, it gets confused.
172
return IOP_TAG_DECORATION;
177
gui_has_focus(struct dt_iop_module_t *self)
179
return self->dev->gui_module == self;
167
183
backtransform(float *x, float *o, const float *m, const float t_h, const float t_v)
407
423
void commit_params (struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
409
425
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)p1;
411
// pull in new params to gegl
412
#error "clipping needs to be ported to GEGL!"
414
426
dt_iop_clipping_data_t *d = (dt_iop_clipping_data_t *)piece->data;
415
427
// pull in bit from weird p->k => d->keystone = 1
421
433
if(p->k_v >= -1.0 && p->k_v <= 1.0) d->ki_v = p->k_v;
422
434
else d->ki_v = 0.0f;
423
435
d->angle = M_PI/180.0 * p->angle;
426
d->cw = fabsf(p->cw);
427
d->ch = fabsf(p->ch);
436
if(gui_has_focus(self))
447
d->cw = fabsf(p->cw);
448
d->ch = fabsf(p->ch);
428
450
d->flags = (p->ch < 0 ? FLAG_FLIP_VERTICAL : 0) | (p->cw < 0 ? FLAG_FLIP_HORIZONTAL : 0);
453
void gui_focus (struct dt_iop_module_t *self, gboolean in)
455
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
456
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)self->params;
461
// got focus. make it redraw in full and grab stuff to gui:
462
// need to get gui stuff for the first time for this image,
463
// and advice the pipe to redraw in full:
465
g->clip_w = p->cw - p->cx;
467
g->clip_h = p->ch - p->cy;
468
// flip one bit to trigger the cache:
469
uint32_t hack = *(uint32_t*)&p->cy;
471
p->cy = *(float *)&hack;
472
if(!darktable.gui->reset)
473
dt_dev_add_history_item(darktable.develop, self, TRUE);
477
// lost focus, commit current params:
478
commit_box (self, g, p);
432
483
void init_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
435
#error "clipping needs to be ported to GEGL!"
437
485
piece->data = malloc(sizeof(dt_iop_clipping_data_t));
438
486
self->commit_params(self, self->default_params, pipe, piece);
442
489
void cleanup_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
445
#error "clipping needs to be ported to GEGL!"
447
491
free(piece->data);
452
495
apply_box_aspect(dt_iop_module_t *self, int grab)
454
497
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
455
float wd = self->dev->preview_pipe->backbuf_width;
456
float ht = self->dev->preview_pipe->backbuf_height;
499
dt_dev_get_processed_size(darktable.develop, &iwd, &iht);
500
float wd = iwd, ht = iht;
457
501
// enforce aspect ratio.
458
502
const float aspect = g->current_aspect;
459
503
// const float aspect = gtk_spin_button_get_value(g->aspect);
548
592
DT_DEBUG_SQLITE3_EXEC(darktable.db, "begin", NULL, NULL, NULL);
550
dt_gui_presets_add_generic(_("rotate by 90"), self->op, &p, sizeof(p), 1);
594
dt_gui_presets_add_generic(_("rotate by 90"), self->op, self->version(), &p, sizeof(p), 1);
551
595
p.angle = -90.0f;
552
dt_gui_presets_add_generic(_("rotate by -90"), self->op, &p, sizeof(p), 1);
596
dt_gui_presets_add_generic(_("rotate by -90"), self->op, self->version(), &p, sizeof(p), 1);
553
597
p.angle = 180.0f;
554
dt_gui_presets_add_generic(_("rotate by 180"), self->op, &p, sizeof(p), 1);
598
dt_gui_presets_add_generic(_("rotate by 180"), self->op, self->version(), &p, sizeof(p), 1);
555
599
DT_DEBUG_SQLITE3_EXEC(darktable.db, "commit", NULL, NULL, NULL);
623
aspect_free_activated (GtkEntry *entry, dt_iop_module_t *self)
625
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
626
gchar *text = g_strdup(gtk_entry_get_text(entry));
630
while(*c != ':' && *c != '/' && c < text + strlen(text)) c++;
631
if(c < text + strlen(text) - 1)
635
g->current_aspect = atof(text) / atof(c);
636
apply_box_aspect(self, 5);
637
dt_control_queue_draw_all();
638
dt_iop_request_focus(self);
579
645
aspect_presets_changed (GtkComboBox *combo, dt_iop_module_t *self)
581
647
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
582
648
int which = gtk_combo_box_get_active(combo);
651
// parse config param:
652
if(g->current_aspect == -1.0f)
654
g->current_aspect = dt_conf_get_float("plugins/darkroom/clipping/custom_aspect");
655
if(g->current_aspect <= 0.0f) g->current_aspect = 1.5f;
657
snprintf(text, 128, "%.3f:1", g->current_aspect);
658
gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo))), text);
659
apply_box_aspect(self, 5);
660
dt_control_queue_draw_all();
662
// user is typing, don't overwrite it.
663
g->current_aspect = -2.0f;
585
664
// reset to free aspect ratio:
586
g->current_aspect = -1.0;
587
665
dt_conf_set_int("plugins/darkroom/clipping/aspect_preset", -1);
588
gchar *text = gtk_combo_box_get_active_text(combo);
592
while(*c != ':' && *c != '/' && c < text + strlen(text)) c++;
593
if(c < text + strlen(text) - 1)
597
g->current_aspect = atof(text) / atof(c);
598
apply_box_aspect(self, 5);
599
dt_control_queue_draw_all();
600
dt_iop_request_focus(self);
667
else if (which < NUM_RATIOS)
607
669
dt_conf_set_int("plugins/darkroom/clipping/aspect_preset", which);
608
670
if(which > 1 && self->dev->image->height > self->dev->image->width)
619
681
angle_callback (GtkDarktableSlider *slider, dt_iop_module_t *self)
621
683
if(self->dt->gui->reset) return;
684
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
622
685
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)self->params;
623
686
p->angle = dtgtk_slider_get_value(slider);
624
dt_dev_add_history_item(darktable.develop, self, TRUE);
687
commit_box (self, g, p);
632
695
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)self->params;
633
696
// we need k to be abs(k) < 2, so the second bit will always be zero (except we set it:).
634
697
p->k_h = fmaxf(-1.9, fminf(1.9, dtgtk_slider_get_value(g->keystone_h)));
635
dt_dev_add_history_item(darktable.develop, self, TRUE);
698
commit_box (self, g, p);
638
701
keystone_callback_v (GtkWidget *widget, dt_iop_module_t *self)
642
705
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)self->params;
643
706
// we need k to be abs(k) < 2, so the second bit will always be zero (except we set it:).
644
707
p->k_v = fmaxf(-1.9, fminf(1.9, dtgtk_slider_get_value(g->keystone_v)));
645
dt_dev_add_history_item(darktable.develop, self, TRUE);
708
commit_box (self, g, p);
648
711
void gui_update(struct dt_iop_module_t *self)
655
718
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->hflip), p->cw < 0);
656
719
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->vflip), p->ch < 0);
657
720
int act = dt_conf_get_int("plugins/darkroom/clipping/aspect_preset");
658
if(act < 0 || act > 7) act = 0;
721
if(act < -1 || act >= NUM_RATIOS) act = 0;
659
722
gtk_combo_box_set_active(GTK_COMBO_BOX(g->aspect_presets), act);
724
// reset gui draw box to what we have in the parameters:
727
g->clip_w = p->cw - p->cx;
729
g->clip_h = p->ch - p->cy;
662
732
void init(dt_iop_module_t *module)
724
794
commit_box(self, g, p);
727
static void key_undo_callback(GtkAccelGroup *accel_group,
728
GObject *acceleratable,
729
guint keyval, GdkModifierType modifier,
732
dt_iop_module_t* self = (dt_iop_module_t*)data;
733
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t *)self->gui_data;
734
dt_iop_clipping_params_t *p = (dt_iop_clipping_params_t *)self->params;
736
// reverse cropping to where it was before.
737
p->cx = p->cy = 0.0f;
738
p->cw = p->ch = 1.0f;
739
g->clip_x = g->old_clip_x;
740
g->clip_y = g->old_clip_y;
741
g->clip_w = g->old_clip_w;
742
g->clip_h = g->old_clip_h;
743
dt_dev_add_history_item(darktable.develop, self, TRUE);
744
dt_control_queue_draw_all();
748
798
aspect_flip(GtkWidget *button, dt_iop_module_t *self)
844
895
g_signal_connect (G_OBJECT (g->scale5), "value-changed",
845
896
G_CALLBACK (angle_callback), self);
846
897
g_object_set(G_OBJECT(g->scale5), "tooltip-text", _("right-click and drag a line on the image to drag a straight line"), (char *)NULL);
847
dtgtk_slider_set_accel(g->scale5,darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/angle");
848
898
gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(g->scale5), 0, 6, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
853
903
g_object_set(G_OBJECT(g->keystone_h), "tooltip-text", _("adjust perspective for horizontal keystone distortion"), (char *)NULL);
854
904
g_signal_connect (G_OBJECT (g->keystone_h), "value-changed",
855
905
G_CALLBACK (keystone_callback_h), self);
856
dtgtk_slider_set_accel(g->keystone_h,darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/keystone h");
857
906
gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(g->keystone_h), 0, 6, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
859
908
g->keystone_v = DTGTK_SLIDER(dtgtk_slider_new_with_range(DARKTABLE_SLIDER_BAR, -1.0, 1.0, 0.01, 0.0, 2));
861
910
g_object_set(G_OBJECT(g->keystone_v), "tooltip-text", _("adjust perspective for vertical keystone distortion"), (char *)NULL);
862
911
g_signal_connect (G_OBJECT (g->keystone_v), "value-changed",
863
912
G_CALLBACK (keystone_callback_v), self);
864
dtgtk_slider_set_accel(g->keystone_v,darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/keystone v");
865
913
gtk_table_attach(GTK_TABLE(self->widget), GTK_WIDGET(g->keystone_v), 0, 6, 3, 4, GTK_EXPAND|GTK_FILL, 0, 0, 0);
867
915
label = gtk_label_new(_("aspect"));
878
926
gtk_combo_box_append_text(GTK_COMBO_BOX(g->aspect_presets), _("square"));
879
927
gtk_combo_box_append_text(GTK_COMBO_BOX(g->aspect_presets), _("DIN"));
880
928
gtk_combo_box_append_text(GTK_COMBO_BOX(g->aspect_presets), _("16:9"));
881
g->commit_callback = g_cclosure_new(G_CALLBACK(key_commit_callback),
882
(gpointer)self, NULL);
883
g->undo_callback = g_cclosure_new(G_CALLBACK(key_undo_callback),
884
(gpointer)self, NULL);
885
dt_accel_group_connect_by_path(darktable.control->accels_darkroom,
886
"<Darktable>/darkroom/plugins/clipping/commit",
888
dt_accel_group_connect_by_path(darktable.control->accels_darkroom,
889
"<Darktable>/darkroom/plugins/clipping/undo",
929
dt_gui_key_accel_block_on_focus(gtk_bin_get_child(GTK_BIN(g->aspect_presets)));
891
930
int act = dt_conf_get_int("plugins/darkroom/clipping/aspect_preset");
892
931
if(act < 0 || act >= 9) act = 0;
893
932
gtk_combo_box_set_active(GTK_COMBO_BOX(g->aspect_presets), act);
894
933
g_signal_connect (G_OBJECT (g->aspect_presets), "changed",
895
934
G_CALLBACK (aspect_presets_changed), self);
935
g_signal_connect (G_OBJECT (gtk_bin_get_child(GTK_BIN(g->aspect_presets))), "activate",
936
G_CALLBACK (aspect_free_activated), self);
896
937
g_object_set(G_OBJECT(g->aspect_presets), "tooltip-text", _("set the aspect ratio (w:h)\npress ctrl-x to swap sides"), (char *)NULL);
898
939
GtkBox *hbox = GTK_BOX(gtk_hbox_new(FALSE, 5));
899
940
gtk_box_pack_start(hbox, GTK_WIDGET(g->aspect_presets), TRUE, TRUE, 0);
900
941
GtkWidget *button = dtgtk_button_new(dtgtk_cairo_paint_aspectflip, CPF_STYLE_FLAT);
901
dtgtk_button_set_accel(DTGTK_BUTTON(button),darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/swap the aspect ratio");
942
g->swap_button = GTK_WIDGET(button);
902
943
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (aspect_flip), self);
903
944
g_object_set(G_OBJECT(button), "tooltip-text", _("swap the aspect ratio (ctrl-x)"), (char *)NULL);
904
945
gtk_box_pack_start(hbox, button, TRUE, FALSE, 0);
990
1031
g->aspect_ratios[6] = 1.0;
991
1032
g->aspect_ratios[7] = sqrtf(2.0);
992
1033
g->aspect_ratios[8] = 16.0f/9.0f;
1034
// if adding new presets, make sure to change this as well:
1035
assert(NUM_RATIOS == 9);
994
1037
if(act> 0 && self->dev->image->height > self->dev->image->width)
995
1038
g->current_aspect = 1.0/g->aspect_ratios[act];
1000
1043
void gui_cleanup(struct dt_iop_module_t *self)
1002
dt_accel_group_disconnect(darktable.control->accels_darkroom,
1003
((dt_iop_clipping_gui_data_t*)(self->gui_data))->
1005
dt_accel_group_disconnect(darktable.control->accels_darkroom,
1006
((dt_iop_clipping_gui_data_t*)(self->gui_data))->
1008
1045
free(self->gui_data);
1009
1046
self->gui_data = NULL;
1222
1260
cairo_set_dash (cr, &dashes, 0, 0);
1223
cairo_set_source_rgba(cr, .3, .3, .3, .8);
1262
cairo_set_source_rgba(cr, .2, .2, .2, .8);
1264
cairo_set_source_rgba(cr, .3, .3, .3, .5);
1224
1265
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1225
1266
cairo_rectangle (cr, -1, -1, wd+2, ht+2);
1226
1267
cairo_rectangle (cr, g->clip_x*wd, g->clip_y*ht, g->clip_w*wd, g->clip_h*ht);
1389
1430
else if(darktable.control->button_down && darktable.control->button_down_which == 1)
1432
// draw a light gray frame, to show it's not stored yet:
1391
1434
// first mouse button, adjust cropping frame, but what do we do?
1392
1435
float bzx = g->button_down_zoom_x + .5f, bzy = g->button_down_zoom_y + .5f;
1393
1436
if(!g->cropping && !g->straightening)
1471
1514
commit_box (dt_iop_module_t *self, dt_iop_clipping_gui_data_t *g, dt_iop_clipping_params_t *p)
1516
if(darktable.gui->reset) return;
1473
1517
g->old_clip_x = g->clip_x;
1474
1518
g->old_clip_y = g->clip_y;
1475
1519
g->old_clip_w = g->clip_w;
1481
1525
p->cx = p->cy = 0.0f;
1482
1526
p->cw = p->ch = 1.0f;
1484
const float cx = p->cx, cy = p->cy;
1485
const float cw = fabsf(p->cw), ch = fabsf(p->ch);
1486
p->cx += g->clip_x*(cw-cx);
1487
p->cy += g->clip_y*(ch-cy);
1488
p->cw = copysignf(p->cx + (cw - cx)*g->clip_w, p->cw);
1489
p->ch = copysignf(p->cy + (ch - cy)*g->clip_h, p->ch);
1490
g->clip_x = g->clip_y = 0.0f;
1491
g->clip_w = g->clip_h = 1.0;
1492
darktable.gui->reset = 1;
1493
self->gui_update(self);
1494
darktable.gui->reset = 0;
1530
p->cw = copysignf(p->cx + g->clip_w, p->cw);
1531
p->ch = copysignf(p->cy + g->clip_h, p->ch);
1495
1532
if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
1496
1534
dt_dev_add_history_item(darktable.develop, self, TRUE);
1497
// loose focus, continue with other plugins?
1498
darktable.develop->gui_module = NULL;
1501
1537
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
1547
void init_key_accels()
1549
gtk_accel_map_add_entry("<Darktable>/darkroom/plugins/clipping/commit",
1551
gtk_accel_map_add_entry("<Darktable>/darkroom/plugins/clipping/undo",
1552
GDK_z, GDK_CONTROL_MASK);
1554
// Making sure these get into the accelerator lists as well
1555
dt_accel_group_connect_by_path(darktable.control->accels_darkroom,
1556
"<Darktable>/darkroom/plugins/clipping/commit",
1558
dt_accel_group_connect_by_path(darktable.control->accels_darkroom,
1559
"<Darktable>/darkroom/plugins/clipping/undo",
1561
dtgtk_slider_init_accel(darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/angle");
1562
dtgtk_slider_init_accel(darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/keystone h");
1563
dtgtk_slider_init_accel(darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/keystone v");
1564
dtgtk_button_init_accel(darktable.control->accels_darkroom,"<Darktable>/darkroom/plugins/clipping/swap the aspect ratio");
1583
void init_key_accels(dt_iop_module_so_t *self)
1585
dt_accel_register_iop(self, TRUE, NC_("accel", "commit"),
1587
dt_accel_register_iop(self, TRUE, NC_("accel", "swap the aspect ratio"),
1589
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "angle"));
1590
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "keystone h"));
1591
dt_accel_register_slider_iop(self, FALSE, NC_("accel", "keystone v"));
1594
void connect_key_accels(dt_iop_module_t *self)
1596
dt_iop_clipping_gui_data_t *g = (dt_iop_clipping_gui_data_t*)self->gui_data;
1599
closure = g_cclosure_new(G_CALLBACK(key_commit_callback),
1600
(gpointer)self, NULL);
1601
dt_accel_connect_iop(self, "commit", closure);
1603
dt_accel_connect_button_iop(self, "swap the aspect ratio", g->swap_button);
1604
dt_accel_connect_slider_iop(self, "angle", GTK_WIDGET(g->scale5));
1605
dt_accel_connect_slider_iop(self, "keystone h", GTK_WIDGET(g->keystone_h));
1606
dt_accel_connect_slider_iop(self, "keystone v", GTK_WIDGET(g->keystone_v));