1 // framedraw.c
2 // LiVES
3 // (c) G. Finch (salsaman+lives@gmail.com) 2002 - 2019
4 // see file COPYING for licensing details : released under the GNU GPL 3 or later
5
6 // functions for the 'framedraw' widget - lets users draw on frames :-)
7
8 #include "main.h"
9 #include "callbacks.h"
10 #include "interface.h"
11 #include "effects.h"
12 #include "cvirtual.h"
13 #include "framedraw.h"
14
15 // set by mouse button press
16 static double xstart, ystart;
17 static double xcurrent, ycurrent;
18 static double xinit, yinit;
19 static volatile boolean b1_held;
20
21 static volatile boolean noupdate = FALSE;
22
23 static LiVESWidget *fbord_eventbox;
24
25
calc_fd_scale(int width,int height)26 static double calc_fd_scale(int width, int height) {
27 double scale = 1.;
28
29 if (width < MIN_PRE_X) {
30 width = MIN_PRE_X;
31 }
32 if (height < MIN_PRE_Y) {
33 height = MIN_PRE_Y;
34 }
35
36 if (width > MAX_PRE_X) scale = (double)width / (double)MAX_PRE_X;
37 if (height > MAX_PRE_Y && (height / MAX_PRE_Y < scale)) scale = (double)height / (double)MAX_PRE_Y;
38 return scale;
39 }
40
41
invalidate_preview(lives_special_framedraw_rect_t * frame_draw)42 void invalidate_preview(lives_special_framedraw_rect_t *frame_draw) {
43 /// this is called when a parameter in a rendered effect is changed
44 /// the current preview is invalid and we must reset back to the start frame
45 ///
46 /// the exception is for effects which can resize, since we can only show
47 /// an approximate preview anyway
48
49 if (frame_draw->rfx->props & RFX_PROPS_MAY_RESIZE) return;
50
51 lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
52 lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
53 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start);
54 lives_range_set_value(LIVES_RANGE(mainw->framedraw_scale), cfile->start);
55 lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, cfile->start);
56 mainw->framedraw_frame = cfile->start;
57 lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
58 }
59
60
start_preview(LiVESButton * button,lives_rfx_t * rfx)61 static void start_preview(LiVESButton *button, lives_rfx_t *rfx) {
62 int i;
63 char *com;
64
65 if (fx_dialog[0]) {
66 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
67 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
68 }
69
70 if (!check_filewrite_overwrites()) {
71 if (fx_dialog[0]) {
72 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
73 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
74 }
75 return;
76 }
77
78 clear_widget_bg(LIVES_WIDGET(mainw->framedraw), mainw->fd_surface);
79 lives_widget_set_sensitive(mainw->framedraw_preview, FALSE);
80
81 if (mainw->did_rfx_preview) {
82 lives_kill_subprocesses(cfile->handle, TRUE);
83
84 if (cfile->start == 0) {
85 cfile->start = 1;
86 cfile->end = cfile->frames;
87 }
88
89 do_rfx_cleanup(rfx);
90 }
91
92 com = lives_strdup_printf("%s clear_pre_files \"%s\" 2>%s", prefs->backend_sync, cfile->handle, LIVES_DEVNULL);
93 lives_system(com, TRUE); // clear any .pre files from before
94
95 for (i = 0; i < rfx->num_params; i++) {
96 rfx->params[i].changed = FALSE;
97 }
98
99 mainw->cancelled = CANCEL_NONE;
100 mainw->error = FALSE;
101
102 if (rfx->num_in_channels == 0) {
103 // reset values to specified
104 cfile->hsize = cfile->ohsize;
105 cfile->vsize = cfile->ovsize;
106 cfile->fps = cfile->pb_fps;
107 }
108
109 // within do_effect() we check and if
110 do_effect(rfx, TRUE); // actually start effect processing in the background
111 load_rfx_preview(rfx);
112
113 lives_widget_set_sensitive(mainw->framedraw_spinbutton, TRUE);
114 lives_widget_set_sensitive(mainw->framedraw_scale, TRUE);
115 if (fx_dialog[0]) {
116 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
117 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
118 }
119 lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, cfile->start + 1);
120 mainw->did_rfx_preview = TRUE;
121 }
122
123
framedraw_redraw_cb(LiVESWidget * widget,lives_special_framedraw_rect_t * framedraw)124 static void framedraw_redraw_cb(LiVESWidget *widget, lives_special_framedraw_rect_t *framedraw) {
125 if (mainw->multitrack) return;
126 framedraw_redraw(framedraw, mainw->fd_layer_orig);
127 }
128
129
after_framedraw_frame_spinbutton_changed(LiVESSpinButton * spinbutton,lives_special_framedraw_rect_t * framedraw)130 static void after_framedraw_frame_spinbutton_changed(LiVESSpinButton *spinbutton, lives_special_framedraw_rect_t *framedraw) {
131 // update the single frame/framedraw preview
132 // after the "frame number" spinbutton has changed
133 lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
134 if (fx_dialog[0]) {
135 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
136 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
137 }
138 mainw->framedraw_frame = lives_spin_button_get_value_as_int(spinbutton);
139 if (!(framedraw->rfx->props & RFX_PROPS_MAY_RESIZE)) {
140 if (mainw->framedraw_preview) lives_widget_set_sensitive(mainw->framedraw_preview, FALSE);
141 //lives_widget_context_update();
142 load_rfx_preview(framedraw->rfx);
143 } else framedraw_redraw(framedraw, NULL);
144 if (fx_dialog[0]) {
145 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
146 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
147 }
148 lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
149 }
150
151
framedraw_connect_spinbutton(lives_special_framedraw_rect_t * framedraw,lives_rfx_t * rfx)152 void framedraw_connect_spinbutton(lives_special_framedraw_rect_t *framedraw, lives_rfx_t *rfx) {
153 framedraw->rfx = rfx;
154
155 mainw->fd_spin_func = lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw_spinbutton),
156 LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
157 LIVES_GUI_CALLBACK(after_framedraw_frame_spinbutton_changed), framedraw);
158 lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw_opscale), LIVES_WIDGET_VALUE_CHANGED_SIGNAL,
159 LIVES_GUI_CALLBACK(framedraw_redraw_cb), framedraw);
160 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw_cbutton), LIVES_WIDGET_COLOR_SET_SIGNAL,
161 LIVES_GUI_CALLBACK(framedraw_redraw_cb), framedraw);
162 }
163
164
framedraw_connect(lives_special_framedraw_rect_t * framedraw,int width,int height,lives_rfx_t * rfx)165 void framedraw_connect(lives_special_framedraw_rect_t *framedraw, int width, int height, lives_rfx_t *rfx) {
166 // add mouse fn's so we can draw on frames
167 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_MOTION_NOTIFY_EVENT,
168 LIVES_GUI_CALLBACK(on_framedraw_mouse_update), framedraw);
169 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_BUTTON_RELEASE_EVENT,
170 LIVES_GUI_CALLBACK(on_framedraw_mouse_reset), framedraw);
171 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_BUTTON_PRESS_EVENT,
172 LIVES_GUI_CALLBACK(on_framedraw_mouse_start), framedraw);
173 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_ENTER_EVENT,
174 LIVES_GUI_CALLBACK(on_framedraw_enter), framedraw);
175 lives_signal_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_LEAVE_NOTIFY_EVENT,
176 LIVES_GUI_CALLBACK(on_framedraw_leave), framedraw);
177 lives_signal_sync_connect(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_SCROLL_EVENT,
178 LIVES_GUI_CALLBACK(on_framedraw_scroll), NULL);
179
180 framedraw_connect_spinbutton(framedraw, rfx);
181
182 lives_widget_set_bg_color(mainw->fd_frame, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
183 lives_widget_set_bg_color(fbord_eventbox, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
184
185 if (mainw->fd_layer) framedraw_redraw(framedraw, mainw->fd_layer_orig);
186 }
187
188
framedraw_add_label(LiVESVBox * box)189 void framedraw_add_label(LiVESVBox *box) {
190 LiVESWidget *label;
191
192 // TRANSLATORS - Preview refers to preview window; keep this phrase short
193 widget_opts.justify = LIVES_JUSTIFY_CENTER;
194 label = lives_standard_label_new(_("You can click in Preview to change these values"));
195 lives_box_pack_start(LIVES_BOX(box), label, FALSE, FALSE, widget_opts.packing_height / 2.);
196 widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
197 }
198
199
framedraw_add_reset(LiVESVBox * box,lives_special_framedraw_rect_t * framedraw)200 void framedraw_add_reset(LiVESVBox *box, lives_special_framedraw_rect_t *framedraw) {
201 LiVESWidget *hbox_rst;
202
203 framedraw_add_label(box);
204
205 mainw->framedraw_reset = lives_standard_button_new_from_stock(LIVES_STOCK_REFRESH, NULL,
206 DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
207 hbox_rst = lives_hbox_new(FALSE, 0);
208 lives_box_pack_start(LIVES_BOX(box), hbox_rst, FALSE, FALSE, 4. * widget_opts.scale);
209
210 lives_button_set_label(LIVES_BUTTON(mainw->framedraw_reset), _("_Reset Values"));
211 lives_box_pack_start(LIVES_BOX(hbox_rst), mainw->framedraw_reset, TRUE, FALSE, 0);
212 lives_widget_set_sensitive(mainw->framedraw_reset, FALSE);
213
214 lives_signal_connect(mainw->framedraw_reset, LIVES_WIDGET_CLICKED_SIGNAL, LIVES_GUI_CALLBACK(on_framedraw_reset_clicked),
215 framedraw);
216 }
217
218
redraw_framedraw_image(weed_layer_t * layer)219 static void redraw_framedraw_image(weed_layer_t *layer) {
220 // layer should be mainw->fd_frame
221 // size layer, convert palette
222 // then draw it in mainw->fd_surface
223 // changes 'layer' itself
224
225 lives_painter_t *cr, *cr2;
226 LiVESWidget *fd_widget;
227
228 int fd_width, fd_height;
229 int width, height, cx, cy;
230
231 if (!layer) return;
232
233 if (!CURRENT_CLIP_IS_VALID) return;
234
235 if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
236 else fd_widget = mainw->framedraw;
237
238 if (!LIVES_IS_WIDGET(fd_widget)) return;
239
240 fd_width = lives_widget_get_allocation_width(fd_widget);
241 fd_height = lives_widget_get_allocation_height(fd_widget);
242
243 width = cfile->hsize;
244 height = cfile->vsize;
245
246 if (!mainw->multitrack || prefs->letterbox_mt)
247 calc_maxspect(fd_width, fd_height, &width, &height);
248
249 // resize to correct size
250 resize_layer(layer, width, height, LIVES_INTERP_BEST,
251 LIVES_PAINTER_COLOR_PALETTE(capable->byte_order), 0);
252
253 cr2 = lives_painter_create_from_surface(mainw->fd_surface);
254
255 cr = layer_to_lives_painter(layer);
256
257 cx = (fd_width - width) / 2;
258 cy = (fd_height - height) / 2;
259
260 lives_painter_set_source_surface(cr2, lives_painter_get_target(cr), cx, cy);
261 lives_painter_rectangle(cr2, cx, cy, width, height);
262 lives_painter_fill(cr2);
263 lives_painter_destroy(cr2);
264
265 lives_painter_to_layer(cr, layer);
266 lives_widget_queue_draw(fd_widget);
267 }
268
269
expose_fd_event(LiVESWidget * widget,lives_painter_t * cr,livespointer user_data)270 static boolean expose_fd_event(LiVESWidget *widget, lives_painter_t *cr, livespointer user_data) {
271 if (!LIVES_IS_WIDGET(widget)) return TRUE;
272 //redraw_framedraw_image(mainw->fd_layer, cr);
273 lives_painter_set_source_surface(cr, mainw->fd_surface, 0., 0.);
274 lives_painter_paint(cr);
275 return TRUE;
276 }
277
278
widget_add_framedraw(LiVESVBox * box,int start,int end,boolean add_preview_button,int width,int height,lives_rfx_t * rfx)279 void widget_add_framedraw(LiVESVBox *box, int start, int end, boolean add_preview_button, int width, int height,
280 lives_rfx_t *rfx) {
281 // adds the frame draw widget to box
282 // the redraw button should be connected to an appropriate redraw function
283 // after calling this function
284
285 LiVESAdjustment *spinbutton_adj;
286
287 LiVESWidget *vseparator;
288 LiVESWidget *vbox;
289 LiVESWidget *hbox;
290 LiVESWidget *label;
291 LiVESWidget *cbutton;
292 LiVESWidget *frame;
293 lives_colRGBA64_t opcol;
294
295 double fd_scale;
296
297 b1_held = FALSE;
298
299 mainw->framedraw_reset = NULL;
300
301 vseparator = lives_vseparator_new();
302 lives_box_pack_start(LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))), vseparator, FALSE, FALSE, 0);
303 lives_widget_show(vseparator);
304
305 vbox = lives_vbox_new(FALSE, 0);
306 lives_box_pack_start(LIVES_BOX(lives_widget_get_parent(LIVES_WIDGET(box))), vbox, FALSE, FALSE, 0);
307 lives_container_set_border_width(LIVES_CONTAINER(vbox), widget_opts.border_width);
308
309 fd_scale = calc_fd_scale(width, height);
310 width /= fd_scale;
311 height /= fd_scale;
312
313 hbox = lives_hbox_new(FALSE, 0);
314 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, 0);
315
316 fbord_eventbox = lives_event_box_new();
317 lives_container_set_border_width(LIVES_CONTAINER(fbord_eventbox), widget_opts.border_width);
318
319 frame = lives_standard_frame_new(_("Preview"), 0., TRUE);
320
321 lives_box_pack_start(LIVES_BOX(hbox), frame, TRUE, TRUE, 0);
322
323 mainw->fd_frame = frame;
324
325 hbox = lives_hbox_new(FALSE, 0);
326 lives_container_set_border_width(LIVES_CONTAINER(hbox), 0);
327 lives_container_add(LIVES_CONTAINER(frame), hbox);
328
329 if (palette->style & STYLE_1) {
330 lives_widget_set_bg_color(frame, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
331 lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
332 }
333
334 //mainw->framedraw = lives_event_box_new();
335 mainw->framedraw = lives_standard_drawing_area_new(LIVES_GUI_CALLBACK(expose_fd_event),
336 &mainw->fd_surface);
337 lives_widget_set_size_request(mainw->framedraw, width, height);
338
339 if (palette->style & STYLE_1) {
340 lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
341 }
342
343 lives_widget_set_events(mainw->framedraw, LIVES_BUTTON1_MOTION_MASK | LIVES_BUTTON_RELEASE_MASK |
344 LIVES_BUTTON_PRESS_MASK | LIVES_ENTER_NOTIFY_MASK | LIVES_LEAVE_NOTIFY_MASK);
345
346 mainw->framedraw_frame = start;
347
348 lives_box_pack_start(LIVES_BOX(hbox), fbord_eventbox, FALSE, FALSE, 0);
349
350 lives_container_add(LIVES_CONTAINER(fbord_eventbox), mainw->framedraw);
351
352 if (palette->style & STYLE_1) {
353 lives_widget_set_bg_color(mainw->framedraw, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
354 lives_widget_set_fg_color(mainw->framedraw, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
355 }
356
357 /* if (mainw->multitrack == NULL) */
358 /* lives_signal_connect_after(LIVES_GUI_OBJECT(mainw->framedraw), LIVES_WIDGET_EXPOSE_EVENT, */
359 /* LIVES_GUI_CALLBACK(expose_fd_event), NULL); */
360
361 // mask colour and opacity controls
362 mainw->framedraw_maskbox = lives_hbox_new(FALSE, 2);
363 lives_box_pack_start(LIVES_BOX(vbox), mainw->framedraw_maskbox, FALSE, FALSE, widget_opts.packing_height);
364 label = lives_standard_label_new(_("Mask: opacity"));
365 lives_box_pack_start(LIVES_BOX(mainw->framedraw_maskbox), label, FALSE, FALSE, widget_opts.packing_width);
366 spinbutton_adj = lives_adjustment_new(DEF_MASK_OPACITY, 0.0, 1.0, 0.1, 0.1, 0);
367 mainw->framedraw_opscale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
368 lives_box_pack_start(LIVES_BOX(mainw->framedraw_maskbox), mainw->framedraw_opscale, TRUE, TRUE, 0);
369 opcol = lives_rgba_col_new(0, 0, 0, 65535);
370 cbutton = lives_standard_color_button_new(LIVES_BOX(mainw->framedraw_maskbox), _("color"),
371 FALSE, &opcol, NULL, NULL, NULL, NULL);
372 mainw->framedraw_cbutton = cbutton;
373
374 hbox = lives_hbox_new(FALSE, 2);
375 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
376
377 mainw->framedraw_spinbutton = lives_standard_spin_button_new(_("_Frame"),
378 start, start, end, 1., 10., 0, LIVES_BOX(hbox), NULL);
379
380 spinbutton_adj = lives_spin_button_get_adjustment(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton));
381
382 mainw->framedraw_preview = lives_standard_button_new_from_stock(LIVES_STOCK_REFRESH, _("Preview"),
383 DEF_BUTTON_WIDTH, DEF_BUTTON_HEIGHT);
384
385 lives_box_pack_start(LIVES_BOX(hbox), mainw->framedraw_preview, TRUE, FALSE, 0);
386 lives_widget_set_no_show_all(mainw->framedraw_preview, TRUE);
387
388 hbox = lives_hbox_new(FALSE, 2);
389 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
390
391 mainw->framedraw_scale = lives_standard_hscale_new(LIVES_ADJUSTMENT(spinbutton_adj));
392 if (palette->style & STYLE_1) {
393 lives_widget_set_fg_color(mainw->framedraw_scale, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
394 }
395 lives_box_pack_start(LIVES_BOX(hbox), mainw->framedraw_scale, TRUE, TRUE, widget_opts.border_width);
396 if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
397 gtk_range_set_show_fill_level(GTK_RANGE(mainw->framedraw_scale), TRUE);
398 gtk_range_set_restrict_to_fill_level(GTK_RANGE(mainw->framedraw_scale), TRUE);
399 gtk_range_set_fill_level(GTK_RANGE(mainw->framedraw_scale), end > start ? (double)start + 1. :
400 (double)start);
401 lives_widget_set_sensitive(mainw->framedraw_spinbutton, FALSE);
402 lives_widget_set_sensitive(mainw->framedraw_scale, FALSE);
403 }
404 lives_signal_connect(mainw->framedraw_preview, LIVES_WIDGET_CLICKED_SIGNAL, LIVES_GUI_CALLBACK(start_preview), rfx);
405
406 lives_widget_show_all(vbox);
407
408 if (add_preview_button) {
409 lives_widget_set_no_show_all(mainw->framedraw_preview, FALSE);
410 lives_widget_show_all(mainw->framedraw_preview);
411 }
412
413 lives_widget_hide(mainw->framedraw_maskbox);
414
415 if (start == 0) {
416 mainw->fd_max_frame = 1;
417 } else {
418 mainw->fd_max_frame = start;
419 }
420 }
421
422
framedraw_redraw(lives_special_framedraw_rect_t * framedraw,weed_layer_t * layer)423 weed_plant_t *framedraw_redraw(lives_special_framedraw_rect_t *framedraw, weed_layer_t *layer) {
424 // overlay framedrawing on a layer
425 // if layer is NULL then we reload the frame from current file into mainw->fd_layer_orig
426 // if called from multitrack then the preview layer is passed in via mt_framedraw()
427 // for overlaying we pass in mainw->fd_layer_orig again
428 // from c.e, layer is mainw->fd_orig_layer
429 //
430 // the input layer is copied: (mainw->fd_layer_orig -> mainw->fd_layer)
431 // mainw->fd_layer is drawn over, resized, gamma corrected and painted
432 // the output layer is also returned (mainw->fd_layer)
433
434 lives_painter_t *cr;
435 LiVESWidget *fd_widget;
436
437 double xstartf, ystartf, xendf, yendf;
438
439 int fd_height;
440 int fd_width;
441 int width, height;
442
443 if (!CURRENT_CLIP_IS_VALID) return NULL;
444
445 if (framedraw->rfx->source_type == LIVES_RFX_SOURCE_RFX)
446 if (noupdate) return NULL; // static volatile
447
448 if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
449 else fd_widget = mainw->framedraw;
450 if (!LIVES_IS_WIDGET(fd_widget)) return NULL;
451
452 fd_width = lives_widget_get_allocation_width(fd_widget);
453 fd_height = lives_widget_get_allocation_height(fd_widget);
454
455 if (fd_width < 4 || fd_height < 4) return NULL;
456
457 width = cfile->hsize;
458 height = cfile->vsize;
459
460 if (!mainw->multitrack || prefs->letterbox_mt)
461 calc_maxspect(fd_width, fd_height, &width, &height);
462
463 // copy from orig, resize
464
465 if (!layer) {
466 // forced reload: get frame from clip, and set in mainw->fd_layer_orig
467 const char *img_ext;
468 if (framedraw->rfx->num_in_channels > 0 && !(framedraw->rfx->props & RFX_PROPS_MAY_RESIZE)) {
469 img_ext = LIVES_FILE_EXT_PRE;
470 } else {
471 img_ext = get_image_ext_for_type(cfile->img_type);
472 }
473
474 layer = lives_layer_new_for_frame(mainw->current_file, mainw->framedraw_frame);
475 if (!pull_frame(layer, img_ext, 0)) {
476 weed_plant_free(layer);
477 return NULL;
478 }
479 if (mainw->fd_layer_orig) weed_layer_free(mainw->fd_layer_orig);
480 mainw->fd_layer_orig = layer;
481 } else {
482 if (mainw->fd_layer_orig && mainw->fd_layer_orig != layer) {
483 weed_layer_free(mainw->fd_layer_orig);
484 mainw->fd_layer_orig = layer;
485 }
486 }
487
488 // copy orig layer to new layer
489 if (mainw->fd_layer) {
490 weed_layer_free(mainw->fd_layer);
491 mainw->fd_layer = NULL;
492 }
493
494 mainw->fd_layer = weed_layer_copy(NULL, layer);
495
496 // resize to correct size
497 resize_layer(mainw->fd_layer, width, height, LIVES_INTERP_BEST,
498 LIVES_PAINTER_COLOR_PALETTE(capable->byte_order), 0);
499
500 cr = layer_to_lives_painter(mainw->fd_layer);
501
502 // draw on the lives_painter
503
504 switch (framedraw->type) {
505 case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
506 case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
507 if (framedraw->xstart_param->dp == 0) {
508 xstartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
509 xstartf = xstartf / (double)cfile->hsize * (double)width;
510 } else {
511 xstartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
512 xstartf = xstartf * (double)width;
513 }
514
515 if (framedraw->xend_param->dp == 0) {
516 xendf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]));
517 xendf = xendf / (double)cfile->hsize * (double)width;
518 } else {
519 xendf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]));
520 xendf = xendf * (double)width;
521 }
522
523 if (framedraw->ystart_param->dp == 0) {
524 ystartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
525 ystartf = ystartf / (double)cfile->vsize * (double)height;
526 } else {
527 ystartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
528 ystartf = ystartf * (double)height;
529 }
530
531 if (framedraw->yend_param->dp == 0) {
532 yendf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]));
533 yendf = yendf / (double)cfile->vsize * (double)height;
534 } else {
535 yendf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]));
536 yendf = yendf * (double)height;
537 }
538
539 if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT) {
540 lives_painter_set_source_rgb(cr, 1., 0., 0.); // TODO - make colour configurable
541 lives_painter_rectangle(cr, xstartf - 1., ystartf - 1., xendf, yendf);
542 lives_painter_stroke(cr);
543 } else {
544 if (b1_held) {
545 lives_painter_set_source_rgb(cr, 1., 0., 0.); // TODO - make colour configurable
546 lives_painter_rectangle(cr, xstartf - 1., ystartf - 1., xendf - xstartf + 2., yendf - ystartf + 2.);
547 lives_painter_stroke(cr);
548 }
549 // create a mask which is only opaque within the clipping area
550 LiVESWidgetColor maskcol;
551 double opacity = lives_range_get_value(LIVES_RANGE(mainw->framedraw_opscale));
552 lives_color_button_get_color(LIVES_COLOR_BUTTON(mainw->framedraw_cbutton), &maskcol);
553 lives_painter_rectangle(cr, 0, 0, width, height);
554 lives_painter_rectangle(cr, xstartf, ystartf, xendf - xstartf + 1., yendf - ystartf + 1.);
555 lives_painter_set_source_rgba(cr, LIVES_WIDGET_COLOR_SCALE(maskcol.red), LIVES_WIDGET_COLOR_SCALE(maskcol.green),
556 LIVES_WIDGET_COLOR_SCALE(maskcol.blue), opacity);
557 lives_painter_set_fill_rule(cr, LIVES_PAINTER_FILL_RULE_EVEN_ODD);
558 lives_painter_fill(cr);
559 }
560 break;
561
562 case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
563 case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT:
564 if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT) {
565 if (framedraw->xstart_param->dp == 0) {
566 xstartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
567 xstartf = xstartf / (double)cfile->hsize * (double)width;
568 } else {
569 xstartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
570 xstartf *= (double)width;
571 }
572
573 if (framedraw->ystart_param->dp == 0) {
574 ystartf = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
575 ystartf = ystartf / (double)cfile->vsize * (double)height;
576 } else {
577 ystartf = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
578 ystartf *= (double)height;
579 }
580 } else {
581 if (b1_held) {
582 xstartf = xcurrent * (double)width;
583 ystartf = ycurrent * (double)height;
584 } else {
585 double xpos, ypos;
586 double scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
587 if (scale == 0.) break;
588
589 if (framedraw->xstart_param->dp > 0)
590 xpos = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
591 else
592 xpos = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
593 / (double)cfile->hsize;
594 if (framedraw->ystart_param->dp > 0)
595 ypos = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
596 else
597 ypos = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
598 / (double)cfile->vsize;
599
600 xstartf = 0.5;
601 ystartf = 0.5;
602
603 if (xpos - xstartf / scale < 0.) xstartf = xpos * scale;
604 else if (xpos + xstartf / scale > 1.) xstartf = 1. - (1. - xpos) * scale;
605 if (ypos - ystartf / scale < 0.) ystartf = ypos * scale;
606 else if (ypos + ystartf / scale > 1.) ystartf = 1. - (1. - ypos) * scale;
607 xstartf *= (double)width;
608 ystartf *= (double)height;
609 }
610 }
611
612 // draw a crosshair
613 lives_painter_set_source_rgb(cr, 1., 0., 0.); // TODO - make colour configurable
614
615 lives_painter_move_to(cr, xstartf, ystartf - CROSSHAIR_SIZE);
616 lives_painter_line_to(cr, xstartf, ystartf + CROSSHAIR_SIZE);
617
618 lives_painter_stroke(cr);
619
620 lives_painter_move_to(cr, xstartf - CROSSHAIR_SIZE, ystartf);
621 lives_painter_line_to(cr, xstartf + CROSSHAIR_SIZE, ystartf);
622
623 lives_painter_stroke(cr);
624 break;
625
626 default:
627 break;
628 }
629
630 lives_painter_to_layer(cr, mainw->fd_layer);
631
632 if (!mainw->multitrack)
633 redraw_framedraw_image(mainw->fd_layer);
634 else {
635 LiVESPixbuf *pixbuf;
636 int palette = weed_layer_get_palette(mainw->fd_layer);
637 if (weed_palette_has_alpha(palette)) {
638 palette = WEED_PALETTE_RGBA32;
639 } else {
640 palette = WEED_PALETTE_RGB24;
641 }
642
643 resize_layer(mainw->fd_layer, weed_layer_get_width(mainw->fd_layer_orig)
644 * weed_palette_get_pixels_per_macropixel(weed_layer_get_palette(mainw->fd_layer_orig)),
645 weed_layer_get_height(mainw->fd_layer_orig), LIVES_INTERP_BEST, palette, 0);
646
647 convert_layer_palette(mainw->fd_layer, palette, 0);
648 pixbuf = layer_to_pixbuf(mainw->fd_layer, TRUE, TRUE);
649 weed_layer_nullify_pixel_data(mainw->fd_layer);
650 weed_layer_free(mainw->fd_layer);
651 mainw->fd_layer = NULL;
652 if (pixbuf) {
653 if (mainw->multitrack->frame_pixbuf != mainw->imframe) {
654 if (mainw->multitrack->frame_pixbuf) lives_widget_object_unref(mainw->multitrack->frame_pixbuf);
655 }
656 // set frame_pixbuf, this gets painted in in expose_event
657 mainw->multitrack->frame_pixbuf = pixbuf;
658 set_drawing_area_from_pixbuf(mainw->play_image, pixbuf, mainw->play_surface);
659 lives_widget_queue_draw(mainw->multitrack->preview_eventbox);
660 lives_widget_object_unref(mainw->multitrack->frame_pixbuf);
661 mainw->multitrack->frame_pixbuf = NULL;
662 }
663 }
664
665 lives_widget_queue_draw(fd_widget);
666 // update the widget
667 return mainw->fd_layer;
668 }
669
670
load_rfx_preview(lives_rfx_t * rfx)671 void load_rfx_preview(lives_rfx_t *rfx) {
672 // load a preview of an rfx (rendered effect) in clip editor
673 weed_layer_t *layer;
674 FILE *infofile = NULL;
675 lives_alarm_t alarm_handle;
676 ticks_t timeout;
677 int tot_frames = 0;
678 int retval;
679 int current_file = mainw->current_file;
680
681 const char *img_ext;
682
683 if (mainw->framedraw_frame > mainw->fd_max_frame) {
684 lives_signal_handler_block(mainw->framedraw_spinbutton, mainw->fd_spin_func);
685 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), mainw->fd_max_frame);
686 lives_signal_handler_unblock(mainw->framedraw_spinbutton, mainw->fd_spin_func);
687 //lives_widget_context_update();
688 }
689
690 if (mainw->framedraw_frame == 0) mainw->framedraw_frame = 1;
691
692 lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
693
694 clear_mainw_msg();
695 THREADVAR(write_failed) = FALSE;
696
697 if (cfile->clip_type == CLIP_TYPE_FILE && cfile->fx_frame_pump && !cfile->pumper) {
698 // pull frames in background
699 cfile->pumper = lives_proc_thread_create(LIVES_THRDATTR_NONE, (lives_funcptr_t)virtual_to_images,
700 -1, "iiibV", mainw->current_file,
701 cfile->undo_start, cfile->undo_end, FALSE, NULL);
702 }
703
704 if (mainw->cancelled) {
705 if (cfile->pumper) {
706 lives_nanosleep_until_nonzero(lives_proc_thread_cancel(cfile->pumper));
707 weed_plant_free(cfile->pumper);
708 cfile->pumper = NULL;
709 }
710 lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
711 return;
712 }
713
714 // get message from back end processor
715 alarm_handle = lives_alarm_set(LIVES_LONGER_TIMEOUT);
716 while ((timeout = lives_alarm_check(alarm_handle)) > 0 && !(infofile = fopen(cfile->info_file, "r")) && !mainw->cancelled) {
717 // wait until we get at least 1 frame
718 //lives_widget_context_update();
719 lives_nanosleep(1000);
720 sched_yield();
721 }
722 lives_alarm_clear(alarm_handle);
723
724 if (!timeout) {
725 lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
726 return;
727 }
728
729 if (mainw->cancelled) {
730 if (infofile) fclose(infofile);
731 lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
732 if (cfile->pumper) {
733 lives_nanosleep_until_nonzero(lives_proc_thread_cancel(cfile->pumper));
734 weed_plant_free(cfile->pumper);
735 cfile->pumper = NULL;
736 }
737 return;
738 }
739
740 do {
741 retval = 0;
742 lives_fgets(mainw->msg, MAINW_MSG_SIZE, infofile);
743 if (THREADVAR(read_failed)) {
744 // TODO : should check fd
745 THREADVAR(read_failed) = 0;
746 retval = do_read_failed_error_s_with_retry(cfile->info_file, NULL);
747 }
748 } while (retval == LIVES_RESPONSE_RETRY);
749
750 fclose(infofile);
751
752 if (lives_strncmp(mainw->msg, "completed", 9)) {
753 if (rfx->num_in_channels > 0) {
754 mainw->fd_max_frame = atoi(mainw->msg);
755 tot_frames = cfile->end;
756 } else {
757 int numtok = get_token_count(mainw->msg, '|');
758 if (numtok > 4) {
759 char **array = lives_strsplit(mainw->msg, "|", numtok);
760 mainw->fd_max_frame = atoi(array[0]);
761 cfile->hsize = atoi(array[1]);
762 cfile->vsize = atoi(array[2]);
763 cfile->fps = cfile->pb_fps = strtod(array[3], NULL);
764 if (cfile->fps == 0) cfile->fps = cfile->pb_fps = prefs->default_fps;
765 tot_frames = atoi(array[4]);
766 lives_strfreev(array);
767 }
768 }
769 } else {
770 tot_frames = mainw->fd_max_frame = cfile->end;
771 }
772
773 if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
774 lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
775
776 if (mainw->fd_max_frame > 0) {
777 int maxlen = calc_spin_button_width(1., (double)tot_frames, 0);
778 lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), cfile->start, tot_frames);
779 lives_entry_set_width_chars(LIVES_ENTRY(mainw->framedraw_spinbutton), maxlen);
780 lives_widget_queue_draw(mainw->framedraw_spinbutton);
781 lives_widget_queue_draw(mainw->framedraw_scale);
782 if (rfx->num_in_channels == 0) {
783 cfile->frames = tot_frames;
784 }
785
786 if (!(rfx->props & RFX_PROPS_MAY_RESIZE)) {
787 gtk_range_set_fill_level(GTK_RANGE(mainw->framedraw_scale), (double)mainw->fd_max_frame);
788 if (mainw->framedraw_frame > mainw->fd_max_frame) {
789 lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), mainw->fd_max_frame);
790 lives_range_set_value(LIVES_RANGE(mainw->framedraw_scale), mainw->fd_max_frame);
791 mainw->current_file = current_file;
792 return;
793 // *INDENT-OFF*
794 }}}}
795 // *INDENT-ON*
796
797 layer = lives_layer_new_for_frame(mainw->current_file, mainw->framedraw_frame);
798 if (rfx->num_in_channels > 0 && !(rfx->props & RFX_PROPS_MAY_RESIZE)) {
799 img_ext = LIVES_FILE_EXT_PRE;
800 } else {
801 img_ext = get_image_ext_for_type(cfile->img_type);
802 }
803 if (!pull_frame(layer, img_ext, 0)) {
804 weed_plant_free(layer);
805 } else {
806 if (mainw->fd_layer_orig && mainw->fd_layer_orig != layer)
807 weed_layer_free(mainw->fd_layer_orig);
808 mainw->fd_layer_orig = layer;
809 if (mainw->fd_layer) weed_layer_free(mainw->fd_layer);
810 mainw->fd_layer = weed_layer_copy(NULL, mainw->fd_layer_orig);
811 redraw_framedraw_image(mainw->fd_layer);
812 }
813 mainw->current_file = current_file;
814 }
815
816
817 // change cursor maybe when we enter or leave the framedraw window
818
on_framedraw_enter(LiVESWidget * widget,LiVESXEventCrossing * event,lives_special_framedraw_rect_t * framedraw)819 boolean on_framedraw_enter(LiVESWidget * widget, LiVESXEventCrossing * event, lives_special_framedraw_rect_t *framedraw) {
820 if (!framedraw && mainw->multitrack) {
821 framedraw = mainw->multitrack->framedraw;
822 if (!framedraw && mainw->multitrack->cursor_style == LIVES_CURSOR_NORMAL)
823 lives_set_cursor_style(LIVES_CURSOR_NORMAL, mainw->multitrack->preview_eventbox);
824 }
825
826 if (!framedraw) return FALSE;
827 if (mainw->multitrack && (mainw->multitrack->track_index == -1 ||
828 mainw->multitrack->cursor_style != LIVES_CURSOR_NORMAL)) return FALSE;
829
830 switch (framedraw->type) {
831 case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
832 case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
833 if (!mainw->multitrack) {
834 lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->framedraw);
835 } else {
836 lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->multitrack->preview_eventbox);
837 }
838 break;
839
840 case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
841 case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT:
842 if (!mainw->multitrack) {
843 lives_set_cursor_style(LIVES_CURSOR_CROSSHAIR, mainw->framedraw);
844 } else {
845 lives_set_cursor_style(LIVES_CURSOR_CROSSHAIR, mainw->multitrack->preview_eventbox);
846 }
847 break;
848
849 default:
850 break;
851 }
852 return FALSE;
853 }
854
on_framedraw_leave(LiVESWidget * widget,LiVESXEventCrossing * event,lives_special_framedraw_rect_t * framedraw)855 boolean on_framedraw_leave(LiVESWidget * widget, LiVESXEventCrossing * event, lives_special_framedraw_rect_t *framedraw) {
856 LiVESWidget *fd_widget;
857 if (!framedraw) return FALSE;
858
859 if (mainw->multitrack) fd_widget = mainw->multitrack->preview_eventbox;
860 else fd_widget = mainw->framedraw;
861
862 if (!LIVES_IS_WIDGET(fd_widget)) return FALSE;
863
864 lives_set_cursor_style(LIVES_CURSOR_NORMAL, fd_widget);
865 return FALSE;
866 }
867
868
869 // using these 3 functions, the user can draw on frames
870
on_framedraw_mouse_start(LiVESWidget * widget,LiVESXEventButton * event,lives_special_framedraw_rect_t * framedraw)871 boolean on_framedraw_mouse_start(LiVESWidget * widget, LiVESXEventButton * event, lives_special_framedraw_rect_t *framedraw) {
872 // user clicked in the framedraw widget (or multitrack playback widget)
873 double xend, yend;
874
875 int fd_height;
876 int fd_width;
877
878 int width = cfile->hsize;
879 int height = cfile->vsize;
880
881 int xstarti, ystarti;
882
883 if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
884
885 if (!framedraw) return FALSE;
886 if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
887
888 if (!framedraw && mainw->multitrack && event->button == 3) {
889 // right click brings up context menu
890 frame_context(widget, event, LIVES_INT_TO_POINTER(0));
891 }
892
893 if (event->button != 1) return FALSE;
894
895 b1_held = TRUE;
896
897 lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
898 widget, &xstarti, &ystarti);
899 xstarti -= widget_opts.border_width;
900
901 if ((framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT ||
902 framedraw->type == LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK) &&
903 (!mainw->multitrack || mainw->multitrack->cursor_style == 0)) {
904 if (!mainw->multitrack) {
905 lives_set_cursor_style(LIVES_CURSOR_BOTTOM_RIGHT_CORNER, widget);
906 } else {
907 lives_set_cursor_style(LIVES_CURSOR_BOTTOM_RIGHT_CORNER, mainw->multitrack->preview_eventbox);
908 }
909 }
910
911 fd_width = lives_widget_get_allocation_width(widget);
912 fd_height = lives_widget_get_allocation_height(widget);
913
914 calc_maxspect(fd_width, fd_height, &width, &height);
915
916 xstart = (double)xstarti - (double)(fd_width - width) / 2.;
917 ystart = (double)ystarti - (double)(fd_height - height) / 2.;
918
919 xstart /= (double)(width - 1);
920 ystart /= (double)(height - 1);
921
922 xend = xcurrent = xstart;
923 yend = ycurrent = ystart;
924
925 noupdate = TRUE;
926
927 switch (framedraw->type) {
928 case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
929 // current center is xinit, yinit
930 // ystart, xstart equal cursor (crosshair posn.)
931
932 if (framedraw->xstart_param->dp > 0)
933 xinit = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
934 else
935 xinit = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
936 / (double)cfile->hsize;
937 if (framedraw->ystart_param->dp > 0)
938 yinit = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
939 else
940 yinit = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
941 / (double)cfile->vsize;
942 }
943 break;
944
945 case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
946 xend = yend = 0.;
947
948 case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
949 if (framedraw->xstart_param->dp > 0)
950 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xstart);
951 else
952 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
953 (int)(xstart * (double)cfile->hsize + .5));
954 if (framedraw->xstart_param->dp > 0)
955 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ystart);
956 else
957 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
958 (int)(ystart * (double)cfile->vsize + .5));
959
960 if (framedraw->xend_param->dp > 0)
961 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xend);
962 else
963 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), (int)(xend * (double)cfile->hsize + .5));
964 if (framedraw->xend_param->dp > 0)
965 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), yend);
966 else
967 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), (int)(yend * (double)cfile->vsize + .5));
968
969 break;
970
971 default:
972 break;
973 }
974
975 if (mainw->framedraw_reset) {
976 lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
977 }
978 if (mainw->framedraw_preview) {
979 lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
980 }
981
982 noupdate = FALSE;
983
984 framedraw_redraw(framedraw, mainw->fd_layer_orig);
985
986 return FALSE;
987 }
988
on_framedraw_mouse_update(LiVESWidget * widget,LiVESXEventMotion * event,lives_special_framedraw_rect_t * framedraw)989 boolean on_framedraw_mouse_update(LiVESWidget * widget, LiVESXEventMotion * event, lives_special_framedraw_rect_t *framedraw) {
990 // pointer moved in the framedraw widget
991 int xcurrenti, ycurrenti;
992
993 int fd_width, fd_height, width, height;
994
995 if (noupdate) return FALSE;
996
997 if (!b1_held) return FALSE;
998
999 if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1000 if (!framedraw) return FALSE;
1001 if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1002
1003 lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
1004 widget, &xcurrenti, &ycurrenti);
1005 xcurrenti -= widget_opts.border_width;
1006
1007 width = cfile->hsize;
1008 height = cfile->vsize;
1009
1010 fd_width = lives_widget_get_allocation_width(widget);
1011 fd_height = lives_widget_get_allocation_height(widget);
1012
1013 calc_maxspect(fd_width, fd_height, &width, &height);
1014
1015 xcurrent = (double)xcurrenti - (fd_width - width) / 2.;
1016 ycurrent = (double)ycurrenti - (fd_height - height) / 2.;
1017
1018 xcurrent /= (double)(width - 1);
1019 ycurrent /= (double)(height - 1);
1020
1021 noupdate = TRUE;
1022
1023 switch (framedraw->type) {
1024 case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT: {
1025 // end parameters provide a scale relative to the output channel
1026 double xscale, yscale;
1027
1028 xscale = xcurrent - xstart;
1029 yscale = ycurrent - ystart;
1030
1031 if (xscale > 0.) {
1032 if (framedraw->xend_param->dp > 0)
1033 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xscale);
1034 else
1035 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1036 (int)(xscale * (double)cfile->hsize + .5));
1037 } else {
1038 if (framedraw->xstart_param->dp > 0) {
1039 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), -xscale);
1040 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xcurrent);
1041 } else {
1042 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1043 (int)(-xscale * (double)cfile->hsize - .5));
1044 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1045 (int)(xcurrent * (double)cfile->hsize + .5));
1046 }
1047 }
1048
1049 if (yscale > 0.) {
1050 if (framedraw->yend_param->dp > 0)
1051 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), yscale);
1052 else
1053 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1054 (int)(yscale * (double)cfile->vsize + .5));
1055 } else {
1056 if (framedraw->xstart_param->dp > 0) {
1057 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), -yscale);
1058 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ycurrent);
1059 } else {
1060 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1061 (int)(-yscale * (double)cfile->vsize - .5));
1062 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1063 (int)(ycurrent * (double)cfile->vsize + .5));
1064 }
1065 }
1066 }
1067 break;
1068
1069 case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
1070 // end parameters provide the absolute point, in output channel ratio
1071 if (xcurrent > xstart) {
1072 if (framedraw->xend_param->dp > 0)
1073 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xcurrent);
1074 else
1075 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1076 (int)(xcurrent * (double)cfile->hsize + .5));
1077 } else {
1078 if (framedraw->xstart_param->dp > 0) {
1079 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), xstart);
1080 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xcurrent);
1081 } else {
1082 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1083 (int)(xstart * (double)cfile->hsize + .5));
1084 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1085 (int)(xcurrent * (double)cfile->hsize + .5));
1086 }
1087 }
1088
1089 if (ycurrent > ystart) {
1090 if (framedraw->yend_param->dp > 0)
1091 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), ycurrent);
1092 else
1093 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1094 (int)(ycurrent * (double)cfile->vsize + .5));
1095 } else {
1096 if (framedraw->xstart_param->dp > 0) {
1097 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), ystart);
1098 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ycurrent);
1099 } else {
1100 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1101 (int)(ystart * (double)cfile->vsize + .5));
1102 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1103 (int)(ycurrent * (double)cfile->vsize + .5));
1104 }
1105 }
1106 break;
1107
1108 case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
1109 // current center is xinit, yinit
1110 // ystart, xstart equal cursor (crosshair posn.)
1111 double offs_x, offs_y;
1112 double scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
1113 if (scale == 0.) break;
1114 offs_x = (xstart - xcurrent) / scale;
1115 offs_y = (ystart - ycurrent) / scale;
1116
1117 if (xinit + offs_x < .5 / scale) offs_x = .5 / scale - xinit;
1118 if (xinit + offs_x > 1. - .5 / scale) offs_x = 1. - .5 / scale - xinit;
1119 if (yinit + offs_y < .5 / scale) offs_y = .5 / scale - yinit;
1120 if (yinit + offs_y > 1. - .5 / scale) offs_y = 1. - .5 / scale - yinit;
1121
1122 if (framedraw->xstart_param->dp > 0)
1123 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xinit + offs_x);
1124 else
1125 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), (int)((xinit + offs_x)
1126 * (double)cfile->hsize + .5));
1127 if (framedraw->xstart_param->dp > 0)
1128 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), yinit + offs_y);
1129 else
1130 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), (int)((yinit + offs_y)
1131 * (double)cfile->vsize + .5));
1132 }
1133 break;
1134
1135 default:
1136 break;
1137 }
1138
1139 #if !GTK_CHECK_VERSION(3, 0, 0)
1140 lives_widget_context_update();
1141 #endif
1142
1143 if (mainw->framedraw_reset) {
1144 lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1145 }
1146 if (mainw->framedraw_preview) {
1147 lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1148 }
1149
1150 noupdate = FALSE;
1151 framedraw_redraw(framedraw, mainw->fd_layer_orig);
1152
1153 return FALSE;
1154 }
1155
on_framedraw_mouse_reset(LiVESWidget * widget,LiVESXEventButton * event,lives_special_framedraw_rect_t * framedraw)1156 boolean on_framedraw_mouse_reset(LiVESWidget * widget, LiVESXEventButton * event, lives_special_framedraw_rect_t *framedraw) {
1157 // user released the mouse button in framedraw widget
1158 int xcurrenti, ycurrenti;
1159 int fd_width, fd_height, width, height;
1160
1161 if (event->button != 1 || !b1_held) return FALSE;
1162
1163 b1_held = FALSE;
1164
1165 if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1166 if (!framedraw) return FALSE;
1167 if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1168
1169 lives_widget_get_pointer((LiVESXDevice *)mainw->mgeom[widget_opts.monitor].mouse_device,
1170 widget, &xcurrenti, &ycurrenti);
1171 xcurrenti -= widget_opts.border_width;
1172
1173 width = cfile->hsize;
1174 height = cfile->vsize;
1175
1176 fd_width = lives_widget_get_allocation_width(widget);
1177 fd_height = lives_widget_get_allocation_height(widget);
1178
1179 calc_maxspect(fd_width, fd_height, &width, &height);
1180
1181 xcurrent = (double)xcurrenti - (fd_width - width) / 2.;
1182 ycurrent = (double)ycurrenti - (fd_height - height) / 2.;
1183
1184 xcurrent /= (double)(width - 1);
1185 ycurrent /= (double)(height - 1);
1186
1187 switch (framedraw->type) {
1188 case LIVES_PARAM_SPECIAL_TYPE_RECT_MULTIRECT:
1189 case LIVES_PARAM_SPECIAL_TYPE_RECT_DEMASK:
1190 if (!mainw->multitrack) {
1191 lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, widget);
1192 } else if (mainw->multitrack->cursor_style == 0) {
1193 lives_set_cursor_style(LIVES_CURSOR_TOP_LEFT_CORNER, mainw->multitrack->preview_eventbox);
1194 }
1195 break;
1196
1197 case LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT: {
1198 double offs_x, offs_y, scale, xend, yend;
1199 if (noupdate) break;
1200
1201 scale = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->scale_param->widgets[0]));
1202 if (scale == 0.) break;
1203
1204 if (framedraw->xstart_param->dp > 0)
1205 xend = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]));
1206 else
1207 xend = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]))
1208 / (double)cfile->hsize;
1209 if (framedraw->ystart_param->dp > 0)
1210 yend = lives_spin_button_get_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]));
1211 else
1212 yend = (double)lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]))
1213 / (double)cfile->vsize;
1214
1215 if (xend == xinit && yend == yinit && xcurrent == xstart && ycurrent == ystart) {
1216 /// the focus is at xend, yend, but the crosshair may be offcenter. We need its position, and then we can find the offset to
1217 /// xcurrent, ycurrent. The offset is then added to the center.
1218 xstart = 0.5;
1219 ystart = 0.5;
1220
1221 if (xend - xstart / scale < 0.) xstart = xend * scale;
1222 else if (xend + xstart / scale > 1.) xstart = 1. - (1. - xend) * scale;
1223 if (yend - ystart / scale < 0.) ystart = yend * scale;
1224 else if (yend + ystart / scale > 1.) ystart = 1. - (1. - yend) * scale;
1225
1226 offs_x = (xcurrent - xstart) / scale;
1227 offs_y = (ycurrent - ystart) / scale;
1228
1229 if (framedraw->xstart_param->dp > 0)
1230 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xend + offs_x);
1231 else
1232 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), (int)((xend + offs_x)
1233 * (double)cfile->hsize + .5));
1234 if (framedraw->xstart_param->dp > 0)
1235 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), yend + offs_y);
1236 else
1237 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), (int)((yend + offs_y)
1238 * cfile->vsize + .5));
1239 }
1240 }
1241 break;
1242
1243 case LIVES_PARAM_SPECIAL_TYPE_SINGLEPOINT:
1244 if (framedraw->xstart_param->dp > 0)
1245 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]), xstart);
1246 else
1247 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1248 (int)(xstart * (double)cfile->hsize + .5));
1249 if (framedraw->xstart_param->dp > 0)
1250 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]), ystart);
1251 else
1252 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1253 (int)(ystart * (double)cfile->vsize + .5));
1254 break;
1255
1256 default:
1257 break;
1258 }
1259 framedraw_redraw(framedraw, mainw->fd_layer_orig);
1260 return FALSE;
1261 }
1262
on_framedraw_scroll(LiVESWidget * widget,LiVESXEventScroll * event,lives_special_framedraw_rect_t * framedraw)1263 boolean on_framedraw_scroll(LiVESWidget * widget, LiVESXEventScroll * event,
1264 lives_special_framedraw_rect_t *framedraw) {
1265 if (!framedraw && mainw->multitrack) framedraw = mainw->multitrack->framedraw;
1266 if (!framedraw) return FALSE;
1267 if (mainw->multitrack && mainw->multitrack->track_index == -1) return FALSE;
1268
1269 if (framedraw->type == LIVES_PARAM_SPECIAL_TYPE_SCALEDPOINT) {
1270 LiVESSpinButton *spb = (LiVESSpinButton *)framedraw->scale_param->widgets[0];
1271 LiVESAdjustment *adj = lives_spin_button_get_adjustment(spb);
1272 double step = lives_adjustment_get_step_increment(adj);
1273 double scale = lives_spin_button_get_value(spb);
1274 if (lives_get_scroll_direction(event) == LIVES_SCROLL_UP) scale += step;
1275 if (lives_get_scroll_direction(event) == LIVES_SCROLL_DOWN) scale -= step;
1276 lives_spin_button_set_value(spb, scale);
1277 }
1278 return FALSE;
1279 }
1280
1281
after_framedraw_widget_changed(LiVESWidget * widget,lives_special_framedraw_rect_t * framedraw)1282 void after_framedraw_widget_changed(LiVESWidget * widget, lives_special_framedraw_rect_t *framedraw) {
1283 if (mainw->block_param_updates || noupdate) return;
1284
1285 // redraw mask when spin values change
1286 framedraw_redraw(framedraw, mainw->fd_layer_orig);
1287 if (mainw->framedraw_reset) {
1288 lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1289 }
1290 if (mainw->framedraw_preview) {
1291 lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1292 }
1293 }
1294
1295
on_framedraw_reset_clicked(LiVESButton * button,lives_special_framedraw_rect_t * framedraw)1296 void on_framedraw_reset_clicked(LiVESButton * button, lives_special_framedraw_rect_t *framedraw) {
1297 // reset to defaults
1298 if (fx_dialog[0]) {
1299 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, FALSE);
1300 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, FALSE);
1301 }
1302
1303 noupdate = TRUE;
1304 if (framedraw->xend_param) {
1305 if (framedraw->xend_param->dp == 0)
1306 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]),
1307 (double)get_int_param(framedraw->xend_param->def));
1308 else
1309 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xend_param->widgets[0]), get_double_param(framedraw->xend_param->def));
1310 }
1311 if (framedraw->yend_param) {
1312 if (framedraw->yend_param->dp == 0)
1313 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]),
1314 (double)get_int_param(framedraw->yend_param->def));
1315 else
1316 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->yend_param->widgets[0]), get_double_param(framedraw->yend_param->def));
1317 }
1318 if (framedraw->xstart_param) {
1319 if (framedraw->xstart_param->dp == 0)
1320 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1321 (double)get_int_param(framedraw->xstart_param->def));
1322 else
1323 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->xstart_param->widgets[0]),
1324 get_double_param(framedraw->xstart_param->def));
1325 }
1326 if (framedraw->ystart_param) {
1327 if (framedraw->ystart_param->dp == 0)
1328 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1329 (double)get_int_param(framedraw->ystart_param->def));
1330 else
1331 lives_spin_button_set_value(LIVES_SPIN_BUTTON(framedraw->ystart_param->widgets[0]),
1332 get_double_param(framedraw->ystart_param->def));
1333 }
1334
1335 if (mainw->framedraw_reset) {
1336 lives_widget_set_sensitive(mainw->framedraw_reset, TRUE);
1337 }
1338 if (mainw->framedraw_preview) {
1339 lives_widget_set_sensitive(mainw->framedraw_preview, TRUE);
1340 }
1341
1342 // update widgets now
1343 //lives_widget_context_update();
1344
1345 noupdate = FALSE;
1346
1347 framedraw_redraw(framedraw, mainw->fd_layer_orig);
1348 if (fx_dialog[0]) {
1349 if (fx_dialog[0]->okbutton) lives_widget_set_sensitive(fx_dialog[0]->okbutton, TRUE);
1350 if (fx_dialog[0]->cancelbutton) lives_widget_set_sensitive(fx_dialog[0]->cancelbutton, TRUE);
1351 }
1352 }
1353