1 /*
2 * This file is part of Siril, an astronomy image processor.
3 * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4 * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5 * Reference site is https://free-astro.org/index.php/Siril
6 *
7 * Siril is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Siril is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "core/siril.h"
22 #include "core/proto.h"
23 #include "io/sequence.h"
24 #include "gui/utils.h"
25 #include "gui/callbacks.h"
26 #include "gui/progress_and_log.h"
27 #include "gui/image_interactions.h"
28 #include "gui/sequence_list.h"
29 #include "registration/registration.h"
30
redraw_preview(GtkWidget * widget,cairo_t * cr,gpointer data)31 gboolean redraw_preview(GtkWidget *widget, cairo_t *cr, gpointer data) {
32 int current_preview, shiftx = 0, shifty = 0;
33 static GtkToggleButton *check_display_ref = NULL;
34 static GtkWidget *labelRegRef = NULL;
35 guint area_width = gtk_widget_get_allocated_width (widget);
36 guint area_height = gtk_widget_get_allocated_height (widget);
37 gboolean display_ref_image;
38
39 if (check_display_ref == NULL) {
40 check_display_ref = GTK_TOGGLE_BUTTON(lookup_widget("checkbutton_displayref"));
41 labelRegRef = lookup_widget("labelRegRef");
42 }
43 display_ref_image = com.refimage_regbuffer && com.refimage_surface
44 && gtk_toggle_button_get_active(check_display_ref)
45 && !gtk_widget_get_visible(labelRegRef);
46
47 if (widget == com.preview_area[0]) current_preview = 0;
48 else if (widget == com.preview_area[1]) current_preview = 1;
49 else {
50 fprintf(stderr, "Uninitialized com.preview_area or unknown drawing area!\n");
51 return TRUE;
52 }
53 // update previewW and previewH in case the drawing area was resized
54 com.seq.previewW[current_preview] = area_width;
55 com.seq.previewH[current_preview] = area_height;
56
57 /* fill preview bacground */
58 cairo_rectangle(cr, 0, 0, area_width, area_height);
59 cairo_fill(cr);
60
61 /* display current image with shifts */
62 if (!com.preview_surface[current_preview]) {
63 GtkStyleContext *context = gtk_widget_get_style_context(widget);
64 GtkStateFlags state = gtk_widget_get_state_flags(widget);
65 PangoLayout *layout;
66 gchar *msg;
67 GtkAllocation allocation;
68 gdouble scale;
69 GdkRGBA color;
70 gint w, h;
71
72 layout = gtk_widget_create_pango_layout(widget, NULL);
73
74 if (sequence_is_loaded()) {
75 msg = g_strdup_printf(_("Preview %d"), current_preview + 1);
76 } else {
77 msg = g_strdup(_("Load\nsequences"));
78 }
79 pango_layout_set_markup(layout, msg, -1);
80 g_free(msg);
81 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
82
83 pango_layout_get_pixel_size(layout, &w, &h);
84 gtk_widget_get_allocation(widget, &allocation);
85
86 scale = MIN(((gdouble) allocation.width / 3.0) / (gdouble ) w,
87 ((gdouble) allocation.height / 3.0) / (gdouble ) h);
88
89 gtk_style_context_get_color(context, state, &color);
90 gdk_cairo_set_source_rgba(cr, &color);
91
92 cairo_move_to(cr, (allocation.width - (w * scale)) / 2,
93 (allocation.height - (h * scale)) / 2);
94
95 cairo_scale(cr, scale, scale);
96
97 pango_cairo_show_layout(cr, layout);
98
99 g_object_unref(layout);
100 return TRUE;
101 }
102 cairo_translate(cr, area_width / 2.0 - com.seq.previewX[current_preview],
103 area_height/2.0-com.seq.previewY[current_preview]);
104 if (com.seq.regparam && com.seq.regparam[com.cvport]) {
105 shiftx = roundf_to_int(com.seq.regparam[com.cvport][com.seq.current].shiftx);
106 shifty = roundf_to_int(com.seq.regparam[com.cvport][com.seq.current].shifty);
107 }
108 if (shiftx || shifty)
109 cairo_translate(cr, shiftx, -shifty);
110 cairo_set_source_surface(cr, com.preview_surface[current_preview], 0, 0);
111 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST);
112 cairo_paint(cr);
113
114 /* display reference image */
115 if (display_ref_image) {
116 /* already translated above but undo the shift translate */
117 if (shiftx || shifty)
118 cairo_translate(cr, -shiftx, shifty);
119 cairo_set_source_surface(cr, com.refimage_surface, 0, 0);
120 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST);
121 cairo_paint_with_alpha(cr, 0.5);
122 }
123
124 return FALSE;
125 }
126
127 /* vport can be -1 if the correct viewport should be tested */
test_and_allocate_reference_image(int vport)128 void test_and_allocate_reference_image(int vport) {
129 static GtkComboBox *cbbt_layers = NULL;
130 if (cbbt_layers == NULL) {
131 cbbt_layers = GTK_COMBO_BOX(lookup_widget("comboboxreglayer"));
132 }
133 if (vport == -1)
134 vport = gtk_combo_box_get_active(cbbt_layers);
135
136 if (sequence_is_loaded() && com.seq.current == com.seq.reference_image
137 && gtk_combo_box_get_active(cbbt_layers) == vport && vport < gfit.naxes[2]) {
138 /* this is the registration layer and the reference frame,
139 * save the buffer for alignment preview */
140 if (!com.refimage_regbuffer || !com.refimage_surface) {
141 guchar *oldbuf = com.refimage_regbuffer;
142 com.refimage_regbuffer = realloc(com.refimage_regbuffer,
143 com.surface_stride[vport] * gfit.ry * sizeof(guchar));
144 if (com.refimage_regbuffer == NULL) {
145 PRINT_ALLOC_ERR;
146 if (oldbuf)
147 free(oldbuf);
148 return;
149 }
150
151 if (com.refimage_surface)
152 cairo_surface_destroy(com.refimage_surface);
153 com.refimage_surface = cairo_image_surface_create_for_data(
154 com.refimage_regbuffer, CAIRO_FORMAT_RGB24, gfit.rx,
155 gfit.ry, com.surface_stride[vport]);
156 if (cairo_surface_status(com.refimage_surface)
157 != CAIRO_STATUS_SUCCESS) {
158 fprintf(stderr,
159 "Error creating the Cairo image surface for the reference image.\n");
160 cairo_surface_destroy(com.refimage_surface);
161 com.refimage_surface = NULL;
162 } else {
163 fprintf(stdout,
164 "Saved the reference frame buffer for alignment preview.\n");
165 enable_view_reference_checkbox(TRUE);
166 }
167 }
168 memcpy(com.refimage_regbuffer, com.graybuf[vport],
169 com.surface_stride[vport] * gfit.ry * sizeof(guchar));
170 cairo_surface_flush(com.refimage_surface);
171 cairo_surface_mark_dirty(com.refimage_surface);
172 }
173 }
174
175
redraw_previews()176 void redraw_previews() {
177 int i;
178 if (com.script) return;
179 for (i = 0; i < PREVIEW_NB; i++)
180 gtk_widget_queue_draw(com.preview_area[i]);
181 }
182
set_preview_area(int preview_area,int centerX,int centerY)183 void set_preview_area(int preview_area, int centerX, int centerY) {
184 /* equivalent to remap() for full image visualization, called in the
185 * mouse click callback and image change.
186 * load the preview area from reference image, from current image,
187 * sets the cairo_surface_t */
188 guint area_width = gtk_widget_get_allocated_width (com.preview_area[preview_area]);
189 guint area_height = gtk_widget_get_allocated_height (com.preview_area[preview_area]);
190
191 com.seq.previewX[preview_area] = centerX;
192 com.seq.previewY[preview_area] = centerY;
193 com.seq.previewW[preview_area] = area_width;
194 com.seq.previewH[preview_area] = area_height;
195
196 if (cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, gfit.rx) !=
197 com.surface_stride[com.cvport] ||
198 gfit.ry != com.surface_height[com.cvport] ||
199 !com.preview_surface[preview_area]) {
200 if (com.preview_surface[preview_area])
201 cairo_surface_destroy(com.preview_surface[preview_area]);
202 com.preview_surface[preview_area] =
203 cairo_image_surface_create_for_data(com.graybuf[com.cvport],
204 CAIRO_FORMAT_RGB24,
205 gfit.rx, gfit.ry,
206 com.surface_stride[com.cvport]);
207 if (cairo_surface_status(com.preview_surface[preview_area]) != CAIRO_STATUS_SUCCESS) {
208 fprintf(stderr, "Error creating the Cairo image surface for preview %d\n", preview_area);
209 cairo_surface_destroy(com.preview_surface[preview_area]);
210 com.preview_surface[preview_area] = NULL;
211 }
212 }
213 // queue for redraw
214 gtk_widget_queue_draw(com.preview_area[preview_area]);
215 // flush to ensure all writing to the image was done and redraw the surface
216 //cairo_surface_flush (com.preview_surface[preview_area]);
217 //cairo_surface_mark_dirty (com.preview_surface[preview_area]);
218 }
219
on_toggle_preview_toggled(GtkToggleButton * toggle,gpointer user_data)220 void on_toggle_preview_toggled(GtkToggleButton *toggle, gpointer user_data) {
221 static GtkToggleButton *preview1 = NULL;
222
223 if (sequence_is_loaded()) {
224
225 if (preview1 == NULL)
226 preview1 = GTK_TOGGLE_BUTTON(lookup_widget("togglebutton2"));
227 if (gtk_toggle_button_get_active(toggle)) {
228 if (toggle == preview1)
229 mouse_status = MOUSE_ACTION_SELECT_PREVIEW1;
230 else
231 mouse_status = MOUSE_ACTION_SELECT_PREVIEW2;
232 } else {
233 /* deactivate preview */
234 int preview_area;
235 mouse_status = MOUSE_ACTION_SELECT_REG_AREA;
236 if (toggle == preview1)
237 preview_area = 0;
238 else
239 preview_area = 1;
240 cairo_surface_destroy(com.preview_surface[preview_area]);
241 com.preview_surface[preview_area] = NULL;
242 com.seq.previewX[preview_area] = -1;
243 com.seq.previewY[preview_area] = -1;
244 com.seq.previewH[preview_area] = 0;
245 com.seq.previewW[preview_area] = 0;
246 // queue for redraw
247 gtk_widget_queue_draw(com.preview_area[preview_area]);
248 }
249 }
250 }
251
on_checkbutton_displayref_toggled(GtkToggleButton * togglebutton,gpointer user_data)252 void on_checkbutton_displayref_toggled(GtkToggleButton *togglebutton, gpointer user_data) {
253 redraw_previews();
254 }
255
init_mouse()256 void init_mouse() {
257 mouse_status = MOUSE_ACTION_SELECT_REG_AREA;
258 }
259
260 /* display registration data (shift{x|y} for now) in the manual adjustments */
adjust_reginfo()261 void adjust_reginfo() {
262 GtkSpinButton *spin_shiftx, *spin_shifty;
263 GtkComboBoxText *seqcombo;
264 gboolean set_sensitive;
265 gint cvport;
266
267 spin_shiftx = GTK_SPIN_BUTTON(lookup_widget("spinbut_shiftx"));
268 spin_shifty = GTK_SPIN_BUTTON(lookup_widget("spinbut_shifty"));
269 seqcombo = GTK_COMBO_BOX_TEXT(lookup_widget("seqlist_dialog_combo"));
270
271 cvport = gtk_combo_box_get_active(GTK_COMBO_BOX(seqcombo));
272 if (cvport < 0) return;
273
274 g_signal_handlers_block_by_func(spin_shiftx, on_spinbut_shift_value_change, NULL);
275 g_signal_handlers_block_by_func(spin_shifty, on_spinbut_shift_value_change, NULL);
276 if (com.seq.regparam == NULL || com.seq.regparam[cvport] == NULL) {
277 gtk_spin_button_set_value(spin_shiftx, 0.);
278 gtk_spin_button_set_value(spin_shifty, 0.);
279 } else {
280 gtk_spin_button_set_value(spin_shiftx,
281 roundf_to_int(com.seq.regparam[cvport][com.seq.current].shiftx));
282 gtk_spin_button_set_value(spin_shifty,
283 roundf_to_int(com.seq.regparam[cvport][com.seq.current].shifty));
284 }
285 g_signal_handlers_unblock_by_func(spin_shiftx, on_spinbut_shift_value_change, NULL);
286 g_signal_handlers_unblock_by_func(spin_shifty, on_spinbut_shift_value_change, NULL);
287 set_sensitive = (com.seq.current != com.seq.reference_image);
288 gtk_widget_set_sensitive(GTK_WIDGET(spin_shiftx), set_sensitive);
289 gtk_widget_set_sensitive(GTK_WIDGET(spin_shifty), set_sensitive);
290 }
291
on_spinbut_shift_value_change(GtkSpinButton * spinbutton,gpointer user_data)292 void on_spinbut_shift_value_change(GtkSpinButton *spinbutton, gpointer user_data) {
293 static GtkSpinButton *spin_shiftx = NULL;
294 static GtkComboBox *cbbt_layers = NULL;
295 int current_layer, new_value;
296 if (spin_shiftx == NULL) {
297 spin_shiftx = GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "spinbut_shiftx"));
298 cbbt_layers = GTK_COMBO_BOX(gtk_builder_get_object(builder, "comboboxreglayer"));
299 }
300 if (com.seq.regparam == NULL) {
301 /* allocated when the number of layers is loaded from the sequence,
302 * = shouldn't happen */
303 fprintf(stderr, "regparam not allocated, sequence not loaded, never displayed or malformed\n");
304 return;
305 }
306
307 current_layer = gtk_combo_box_get_active(cbbt_layers);
308 activate_tab(current_layer);
309
310 if (com.seq.regparam[current_layer] == NULL) {
311 printf("Allocating registration data for this layer\n");
312 com.seq.regparam[current_layer] = calloc(com.seq.number, sizeof(regdata));
313 if (com.seq.regparam[current_layer] == NULL) {
314 PRINT_ALLOC_ERR;
315 return;
316 }
317 }
318
319 new_value = gtk_spin_button_get_value_as_int(spinbutton);
320 if (spinbutton == spin_shiftx)
321 com.seq.regparam[current_layer][com.seq.current].shiftx = (float) new_value;
322 else com.seq.regparam[current_layer][com.seq.current].shifty = (float) new_value;
323 writeseqfile(&com.seq);
324 update_seqlist();
325 fill_sequence_list(&com.seq, current_layer, FALSE); // update list with new regparam
326 redraw_previews();
327 }
328
329 /* enables or disables the "display reference" checkbox in registration preview */
enable_view_reference_checkbox(gboolean status)330 void enable_view_reference_checkbox(gboolean status) {
331 static GtkToggleButton *check_display_ref = NULL;
332 static GtkWidget *widget = NULL, *labelRegRef = NULL;
333 if (check_display_ref == NULL) {
334 check_display_ref = GTK_TOGGLE_BUTTON(lookup_widget("checkbutton_displayref"));
335 widget = GTK_WIDGET(check_display_ref);
336 labelRegRef = lookup_widget("labelRegRef");
337 }
338 if (status && gtk_widget_get_sensitive(widget))
339 return; // may be already enabled but deactivated by user, don't force it again
340 gtk_widget_set_sensitive(widget, status);
341 gtk_widget_set_visible(labelRegRef, !status);
342 gtk_toggle_button_set_active(check_display_ref, status);
343 }
344
345