2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2011 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation,
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/space_clip/clip_ops.c
33 #include <sys/types.h>
42 #include "MEM_guardedalloc.h"
44 #include "DNA_userdef_types.h"
45 #include "DNA_scene_types.h" /* min/max frames */
47 #include "BLI_utildefines.h"
48 #include "BLI_fileops.h"
49 #include "BLI_path_util.h"
53 #include "BLI_string.h"
55 #include "BLT_translation.h"
57 #include "BKE_context.h"
58 #include "BKE_global.h"
59 #include "BKE_report.h"
61 #include "BKE_movieclip.h"
62 #include "BKE_sound.h"
63 #include "BKE_tracking.h"
68 #include "IMB_imbuf_types.h"
69 #include "IMB_imbuf.h"
71 #include "ED_screen.h"
74 #include "UI_interface.h"
76 #include "RNA_access.h"
77 #include "RNA_define.h"
78 #include "RNA_enum_types.h"
80 #include "UI_view2d.h"
84 #include "clip_intern.h" // own include
86 /******************** view navigation utilities *********************/
88 static void sclip_zoom_set(const bContext *C, float zoom, float location[2])
90 SpaceClip *sc = CTX_wm_space_clip(C);
91 ARegion *ar = CTX_wm_region(C);
93 float oldzoom = sc->zoom;
98 if (sc->zoom < 0.1f || sc->zoom > 4.0f) {
99 /* check zoom limits */
100 ED_space_clip_get_size(sc, &width, &height);
105 if ((width < 4) && (height < 4))
107 else if (BLI_rcti_size_x(&ar->winrct) <= sc->zoom)
109 else if (BLI_rcti_size_y(&ar->winrct) <= sc->zoom)
113 if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) && location) {
116 ED_space_clip_get_size(sc, &width, &height);
118 dx = ((location[0] - 0.5f) * width - sc->xof) * (sc->zoom - oldzoom) / sc->zoom;
119 dy = ((location[1] - 0.5f) * height - sc->yof) * (sc->zoom - oldzoom) / sc->zoom;
121 if (sc->flag & SC_LOCK_SELECTION) {
132 static void sclip_zoom_set_factor(const bContext *C, float zoomfac, float location[2])
134 SpaceClip *sc = CTX_wm_space_clip(C);
136 sclip_zoom_set(C, sc->zoom * zoomfac, location);
139 static void sclip_zoom_set_factor_exec(bContext *C, const wmEvent *event, float factor)
141 ARegion *ar = CTX_wm_region(C);
143 float location[2], *mpos = NULL;
146 SpaceClip *sc = CTX_wm_space_clip(C);
148 ED_clip_mouse_pos(sc, ar, event->mval, location);
152 sclip_zoom_set_factor(C, factor, mpos);
154 ED_region_tag_redraw(ar);
157 /******************** open clip operator ********************/
159 static void clip_filesel(bContext *C, wmOperator *op, const char *path)
161 RNA_string_set(op->ptr, "directory", path);
163 WM_event_add_fileselect(C, op);
166 static void open_init(bContext *C, wmOperator *op)
168 PropertyPointerRNA *pprop;
170 op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
171 UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop);
174 static void open_cancel(bContext *UNUSED(C), wmOperator *op)
176 MEM_freeN(op->customdata);
177 op->customdata = NULL;
180 static int open_exec(bContext *C, wmOperator *op)
182 SpaceClip *sc = CTX_wm_space_clip(C);
183 bScreen *screen = CTX_wm_screen(C);
184 Main *bmain = CTX_data_main(C);
185 PropertyPointerRNA *pprop;
187 MovieClip *clip = NULL;
190 if (RNA_collection_length(op->ptr, "files")) {
193 char dir_only[FILE_MAX], file_only[FILE_MAX];
194 bool relative = RNA_boolean_get(op->ptr, "relative_path");
196 RNA_string_get(op->ptr, "directory", dir_only);
198 BLI_path_rel(dir_only, G.main->name);
200 prop = RNA_struct_find_property(op->ptr, "files");
201 RNA_property_collection_lookup_int(op->ptr, prop, 0, &fileptr);
202 RNA_string_get(&fileptr, "name", file_only);
204 BLI_join_dirfile(str, sizeof(str), dir_only, file_only);
207 BKE_report(op->reports, RPT_ERROR, "No files selected to be opened");
209 return OPERATOR_CANCELLED;
212 /* default to frame 1 if there's no scene in context */
216 clip = BKE_movieclip_file_add(bmain, str);
220 MEM_freeN(op->customdata);
222 BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s", str,
223 errno ? strerror(errno) : TIP_("unsupported movie clip format"));
225 return OPERATOR_CANCELLED;
232 pprop = op->customdata;
235 /* when creating new ID blocks, use is already 1, but RNA
236 * pointer se also increases user, so this compensates it */
239 RNA_id_pointer_create(&clip->id, &idptr);
240 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
241 RNA_property_update(C, &pprop->ptr, pprop->prop);
244 ED_space_clip_set_clip(C, screen, sc, clip);
247 WM_event_add_notifier(C, NC_MOVIECLIP | NA_ADDED, clip);
249 MEM_freeN(op->customdata);
251 return OPERATOR_FINISHED;
254 static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
256 SpaceClip *sc = CTX_wm_space_clip(C);
258 MovieClip *clip = NULL;
261 clip = ED_space_clip_get_clip(sc);
264 BLI_strncpy(path, clip->name, sizeof(path));
266 BLI_path_abs(path, G.main->name);
267 BLI_parent_dir(path);
270 BLI_strncpy(path, U.textudir, sizeof(path));
273 if (RNA_struct_property_is_set(op->ptr, "files"))
274 return open_exec(C, op);
276 if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
277 RNA_boolean_set(op->ptr, "relative_path", (U.flag & USER_RELPATHS) != 0);
281 clip_filesel(C, op, path);
283 return OPERATOR_RUNNING_MODAL;
286 void CLIP_OT_open(wmOperatorType *ot)
289 ot->name = "Open Clip";
290 ot->description = "Load a sequence of frames or a movie file";
291 ot->idname = "CLIP_OT_open";
294 ot->exec = open_exec;
295 ot->invoke = open_invoke;
296 ot->cancel = open_cancel;
299 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
302 WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE,
303 WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
306 /******************* reload clip operator *********************/
308 static int reload_exec(bContext *C, wmOperator *UNUSED(op))
310 MovieClip *clip = CTX_data_edit_movieclip(C);
313 return OPERATOR_CANCELLED;
315 BKE_movieclip_reload(clip);
317 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
319 return OPERATOR_FINISHED;
322 void CLIP_OT_reload(wmOperatorType *ot)
325 ot->name = "Reload Clip";
326 ot->description = "Reload clip";
327 ot->idname = "CLIP_OT_reload";
330 ot->exec = reload_exec;
333 /********************** view pan operator *********************/
335 typedef struct ViewPanData {
337 float xof, yof, xorig, yorig;
342 static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event)
344 SpaceClip *sc = CTX_wm_space_clip(C);
347 op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ClipViewPanData");
348 WM_cursor_modal_set(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
353 if (sc->flag & SC_LOCK_SELECTION)
354 vpd->vec = &sc->xlockof;
358 copy_v2_v2(&vpd->xof, vpd->vec);
359 copy_v2_v2(&vpd->xorig, &vpd->xof);
361 vpd->event_type = event->type;
363 WM_event_add_modal_handler(C, op);
366 static void view_pan_exit(bContext *C, wmOperator *op, bool cancel)
368 ViewPanData *vpd = op->customdata;
371 copy_v2_v2(vpd->vec, &vpd->xorig);
373 ED_region_tag_redraw(CTX_wm_region(C));
376 WM_cursor_modal_restore(CTX_wm_window(C));
377 MEM_freeN(op->customdata);
380 static int view_pan_exec(bContext *C, wmOperator *op)
382 SpaceClip *sc = CTX_wm_space_clip(C);
385 RNA_float_get_array(op->ptr, "offset", offset);
387 if (sc->flag & SC_LOCK_SELECTION) {
388 sc->xlockof += offset[0];
389 sc->ylockof += offset[1];
392 sc->xof += offset[0];
393 sc->yof += offset[1];
396 ED_region_tag_redraw(CTX_wm_region(C));
398 return OPERATOR_FINISHED;
401 static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
403 if (event->type == MOUSEPAN) {
404 SpaceClip *sc = CTX_wm_space_clip(C);
407 offset[0] = (event->prevx - event->x) / sc->zoom;
408 offset[1] = (event->prevy - event->y) / sc->zoom;
410 RNA_float_set_array(op->ptr, "offset", offset);
412 view_pan_exec(C, op);
414 return OPERATOR_FINISHED;
417 view_pan_init(C, op, event);
419 return OPERATOR_RUNNING_MODAL;
423 static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
425 SpaceClip *sc = CTX_wm_space_clip(C);
426 ViewPanData *vpd = op->customdata;
429 switch (event->type) {
431 copy_v2_v2(vpd->vec, &vpd->xorig);
432 offset[0] = (vpd->x - event->x) / sc->zoom;
433 offset[1] = (vpd->y - event->y) / sc->zoom;
434 RNA_float_set_array(op->ptr, "offset", offset);
435 view_pan_exec(C, op);
438 view_pan_exit(C, op, 1);
440 return OPERATOR_CANCELLED;
442 view_pan_exit(C, op, 0);
444 return OPERATOR_FINISHED;
446 if (event->type == vpd->event_type && event->val == KM_RELEASE) {
447 view_pan_exit(C, op, 0);
449 return OPERATOR_FINISHED;
454 return OPERATOR_RUNNING_MODAL;
457 static void view_pan_cancel(bContext *C, wmOperator *op)
459 view_pan_exit(C, op, true);
462 void CLIP_OT_view_pan(wmOperatorType *ot)
465 ot->name = "View Pan";
466 ot->idname = "CLIP_OT_view_pan";
467 ot->description = "Pan the view";
470 ot->exec = view_pan_exec;
471 ot->invoke = view_pan_invoke;
472 ot->modal = view_pan_modal;
473 ot->cancel = view_pan_cancel;
474 ot->poll = ED_space_clip_view_clip_poll;
477 ot->flag = OPTYPE_BLOCKING;
480 RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
481 "Offset", "Offset in floating point units, 1.0 is the width and height of the image", -FLT_MAX, FLT_MAX);
484 /********************** view zoom operator *********************/
486 typedef struct ViewZoomData {
492 double timer_lastdraw;
495 static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event)
497 SpaceClip *sc = CTX_wm_space_clip(C);
498 ARegion *ar = CTX_wm_region(C);
501 op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData");
502 WM_cursor_modal_set(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
504 if (U.viewzoom == USER_ZOOM_CONT) {
505 /* needs a timer to continue redrawing */
506 vpd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
507 vpd->timer_lastdraw = PIL_check_seconds_timer();
512 vpd->zoom = sc->zoom;
513 vpd->event_type = event->type;
515 ED_clip_mouse_pos(sc, ar, event->mval, vpd->location);
517 WM_event_add_modal_handler(C, op);
520 static void view_zoom_exit(bContext *C, wmOperator *op, bool cancel)
522 SpaceClip *sc = CTX_wm_space_clip(C);
523 ViewZoomData *vpd = op->customdata;
526 sc->zoom = vpd->zoom;
527 ED_region_tag_redraw(CTX_wm_region(C));
531 WM_event_remove_timer(CTX_wm_manager(C), vpd->timer->win, vpd->timer);
534 WM_cursor_modal_restore(CTX_wm_window(C));
535 MEM_freeN(op->customdata);
538 static int view_zoom_exec(bContext *C, wmOperator *op)
540 sclip_zoom_set_factor(C, RNA_float_get(op->ptr, "factor"), NULL);
542 ED_region_tag_redraw(CTX_wm_region(C));
544 return OPERATOR_FINISHED;
547 static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
549 if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
552 delta = event->prevx - event->x + event->prevy - event->y;
554 if (U.uiflag & USER_ZOOM_INVERT)
557 factor = 1.0f + delta / 300.0f;
558 RNA_float_set(op->ptr, "factor", factor);
560 sclip_zoom_set_factor_exec(C, event, factor);
562 return OPERATOR_FINISHED;
565 view_zoom_init(C, op, event);
567 return OPERATOR_RUNNING_MODAL;
571 static void view_zoom_apply(bContext *C,
574 const wmEvent *event)
578 if (U.viewzoom == USER_ZOOM_CONT) {
579 SpaceClip *sclip = CTX_wm_space_clip(C);
580 double time = PIL_check_seconds_timer();
581 float time_step = (float)(time - vpd->timer_lastdraw);
585 if (U.uiflag & USER_ZOOM_HORIZ) {
586 fac = (float)(event->x - vpd->x);
589 fac = (float)(event->y - vpd->y);
592 if (U.uiflag & USER_ZOOM_INVERT) {
596 zfac = 1.0f + ((fac / 20.0f) * time_step);
597 vpd->timer_lastdraw = time;
598 factor = (sclip->zoom * zfac) / vpd->zoom;
601 float delta = event->x - vpd->x + event->y - vpd->y;
603 if (U.uiflag & USER_ZOOM_INVERT) {
607 factor = 1.0f + delta / 300.0f;
610 RNA_float_set(op->ptr, "factor", factor);
611 sclip_zoom_set(C, vpd->zoom * factor, vpd->location);
612 ED_region_tag_redraw(CTX_wm_region(C));
615 static int view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
617 ViewZoomData *vpd = op->customdata;
618 switch (event->type) {
620 if (event->customdata == vpd->timer) {
621 view_zoom_apply(C, vpd, op, event);
625 view_zoom_apply(C, vpd, op, event);
628 if (event->type == vpd->event_type && event->val == KM_RELEASE) {
629 view_zoom_exit(C, op, 0);
631 return OPERATOR_FINISHED;
636 return OPERATOR_RUNNING_MODAL;
639 static void view_zoom_cancel(bContext *C, wmOperator *op)
641 view_zoom_exit(C, op, true);
644 void CLIP_OT_view_zoom(wmOperatorType *ot)
649 ot->name = "View Zoom";
650 ot->idname = "CLIP_OT_view_zoom";
651 ot->description = "Zoom in/out the view";
654 ot->exec = view_zoom_exec;
655 ot->invoke = view_zoom_invoke;
656 ot->modal = view_zoom_modal;
657 ot->cancel = view_zoom_cancel;
658 ot->poll = ED_space_clip_view_clip_poll;
661 ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
664 prop = RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, "Factor",
665 "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX);
666 RNA_def_property_flag(prop, PROP_HIDDEN);
669 /********************** view zoom in/out operator *********************/
671 static int view_zoom_in_exec(bContext *C, wmOperator *op)
675 RNA_float_get_array(op->ptr, "location", location);
677 sclip_zoom_set_factor(C, powf(2.0f, 1.0f / 3.0f), location);
679 ED_region_tag_redraw(CTX_wm_region(C));
681 return OPERATOR_FINISHED;
684 static int view_zoom_in_invoke(bContext *C, wmOperator *op, const wmEvent *event)
686 SpaceClip *sc = CTX_wm_space_clip(C);
687 ARegion *ar = CTX_wm_region(C);
691 ED_clip_mouse_pos(sc, ar, event->mval, location);
692 RNA_float_set_array(op->ptr, "location", location);
694 return view_zoom_in_exec(C, op);
697 void CLIP_OT_view_zoom_in(wmOperatorType *ot)
702 ot->name = "View Zoom In";
703 ot->idname = "CLIP_OT_view_zoom_in";
704 ot->description = "Zoom in the view";
707 ot->exec = view_zoom_in_exec;
708 ot->invoke = view_zoom_in_invoke;
709 ot->poll = ED_space_clip_view_clip_poll;
712 prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
713 "Cursor location in screen coordinates", -10.0f, 10.0f);
714 RNA_def_property_flag(prop, PROP_HIDDEN);
717 static int view_zoom_out_exec(bContext *C, wmOperator *op)
721 RNA_float_get_array(op->ptr, "location", location);
723 sclip_zoom_set_factor(C, powf(0.5f, 1.0f / 3.0f), location);
725 ED_region_tag_redraw(CTX_wm_region(C));
727 return OPERATOR_FINISHED;
730 static int view_zoom_out_invoke(bContext *C, wmOperator *op, const wmEvent *event)
732 SpaceClip *sc = CTX_wm_space_clip(C);
733 ARegion *ar = CTX_wm_region(C);
737 ED_clip_mouse_pos(sc, ar, event->mval, location);
738 RNA_float_set_array(op->ptr, "location", location);
740 return view_zoom_out_exec(C, op);
743 void CLIP_OT_view_zoom_out(wmOperatorType *ot)
748 ot->name = "View Zoom Out";
749 ot->idname = "CLIP_OT_view_zoom_out";
750 ot->description = "Zoom out the view";
753 ot->exec = view_zoom_out_exec;
754 ot->invoke = view_zoom_out_invoke;
755 ot->poll = ED_space_clip_view_clip_poll;
758 prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
759 "Cursor location in normalized (0.0-1.0) coordinates", -10.0f, 10.0f);
760 RNA_def_property_flag(prop, PROP_HIDDEN);
763 /********************** view zoom ratio operator *********************/
765 static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
767 SpaceClip *sc = CTX_wm_space_clip(C);
769 sclip_zoom_set(C, RNA_float_get(op->ptr, "ratio"), NULL);
771 /* ensure pixel exact locations for draw */
772 sc->xof = (int) sc->xof;
773 sc->yof = (int) sc->yof;
775 ED_region_tag_redraw(CTX_wm_region(C));
777 return OPERATOR_FINISHED;
780 void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
783 ot->name = "View Zoom Ratio";
784 ot->idname = "CLIP_OT_view_zoom_ratio";
785 ot->description = "Set the zoom ratio (based on clip size)";
788 ot->exec = view_zoom_ratio_exec;
789 ot->poll = ED_space_clip_view_clip_poll;
792 RNA_def_float(ot->srna, "ratio", 0.0f, -FLT_MAX, FLT_MAX,
793 "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
796 /********************** view all operator *********************/
798 static int view_all_exec(bContext *C, wmOperator *op)
802 int w, h, width, height;
804 bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
808 sc = CTX_wm_space_clip(C);
809 ar = CTX_wm_region(C);
811 ED_space_clip_get_size(sc, &w, &h);
812 ED_space_clip_get_aspect(sc, &aspx, &aspy);
817 /* check if the image will fit in the image with zoom == 1 */
818 width = BLI_rcti_size_x(&ar->winrct) + 1;
819 height = BLI_rcti_size_y(&ar->winrct) + 1;
822 const int margin = 5; /* margin from border */
824 zoomx = (float) width / (w + 2 * margin);
825 zoomy = (float) height / (h + 2 * margin);
827 sclip_zoom_set(C, min_ff(zoomx, zoomy), NULL);
830 if ((w >= width || h >= height) && (width > 0 && height > 0)) {
831 zoomx = (float) width / w;
832 zoomy = (float) height / h;
834 /* find the zoom value that will fit the image in the image space */
835 sclip_zoom_set(C, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL);
838 sclip_zoom_set(C, 1.0f, NULL);
841 sc->xof = sc->yof = 0.0f;
843 ED_region_tag_redraw(ar);
845 return OPERATOR_FINISHED;
848 void CLIP_OT_view_all(wmOperatorType *ot)
853 ot->name = "View All";
854 ot->idname = "CLIP_OT_view_all";
855 ot->description = "View whole image with markers";
858 ot->exec = view_all_exec;
859 ot->poll = ED_space_clip_view_clip_poll;
862 prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
863 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
866 /********************** view selected operator *********************/
868 static int view_selected_exec(bContext *C, wmOperator *UNUSED(op))
870 SpaceClip *sc = CTX_wm_space_clip(C);
871 ARegion *ar = CTX_wm_region(C);
876 ED_clip_view_selection(C, ar, 1);
877 ED_region_tag_redraw(ar);
879 return OPERATOR_FINISHED;
882 void CLIP_OT_view_selected(wmOperatorType *ot)
885 ot->name = "View Selected";
886 ot->idname = "CLIP_OT_view_selected";
887 ot->description = "View all selected elements";
890 ot->exec = view_selected_exec;
891 ot->poll = ED_space_clip_view_clip_poll;
894 /********************** change frame operator *********************/
896 static int change_frame_poll(bContext *C)
898 /* prevent changes during render */
902 return ED_space_clip_poll(C);
905 static void change_frame_apply(bContext *C, wmOperator *op)
907 Scene *scene = CTX_data_scene(C);
909 /* set the new frame number */
910 CFRA = RNA_int_get(op->ptr, "frame");
911 FRAMENUMBER_MIN_CLAMP(CFRA);
915 BKE_sound_seek_scene(CTX_data_main(C), scene);
916 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
919 static int change_frame_exec(bContext *C, wmOperator *op)
921 change_frame_apply(C, op);
923 return OPERATOR_FINISHED;
926 static int frame_from_event(bContext *C, const wmEvent *event)
928 ARegion *ar = CTX_wm_region(C);
929 Scene *scene = CTX_data_scene(C);
932 if (ar->regiontype == RGN_TYPE_WINDOW) {
933 float sfra = SFRA, efra = EFRA, framelen = ar->winx / (efra - sfra + 1);
935 framenr = sfra + event->mval[0] / framelen;
940 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
942 framenr = iroundf(viewx);
948 static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event)
950 ARegion *ar = CTX_wm_region(C);
952 if (ar->regiontype == RGN_TYPE_WINDOW) {
953 if (event->mval[1] > 16)
954 return OPERATOR_PASS_THROUGH;
957 RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
959 change_frame_apply(C, op);
961 /* add temp handler */
962 WM_event_add_modal_handler(C, op);
964 return OPERATOR_RUNNING_MODAL;
967 static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
969 switch (event->type) {
971 return OPERATOR_FINISHED;
974 RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
975 change_frame_apply(C, op);
980 if (event->val == KM_RELEASE)
981 return OPERATOR_FINISHED;
985 return OPERATOR_RUNNING_MODAL;
988 void CLIP_OT_change_frame(wmOperatorType *ot)
991 ot->name = "Change Frame";
992 ot->idname = "CLIP_OT_change_frame";
993 ot->description = "Interactively change the current frame number";
996 ot->exec = change_frame_exec;
997 ot->invoke = change_frame_invoke;
998 ot->modal = change_frame_modal;
999 ot->poll = change_frame_poll;
1002 ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
1005 RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
1008 /********************** rebuild proxies operator *********************/
1010 typedef struct ProxyBuildJob {
1016 struct IndexBuildContext *index_context;
1019 static void proxy_freejob(void *pjv)
1026 static int proxy_bitflag_to_array(int size_flag, int build_sizes[4], int undistort)
1028 int build_count = 0;
1029 int size_flags[2][4] = {{MCLIP_PROXY_SIZE_25,
1030 MCLIP_PROXY_SIZE_50,
1031 MCLIP_PROXY_SIZE_75,
1032 MCLIP_PROXY_SIZE_100},
1033 {MCLIP_PROXY_UNDISTORTED_SIZE_25,
1034 MCLIP_PROXY_UNDISTORTED_SIZE_50,
1035 MCLIP_PROXY_UNDISTORTED_SIZE_75,
1036 MCLIP_PROXY_UNDISTORTED_SIZE_100}};
1037 int size_nr = undistort ? 1 : 0;
1039 if (size_flag & size_flags[size_nr][0])
1040 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_25;
1042 if (size_flag & size_flags[size_nr][1])
1043 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_50;
1045 if (size_flag & size_flags[size_nr][2])
1046 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_75;
1048 if (size_flag & size_flags[size_nr][3])
1049 build_sizes[build_count++] = MCLIP_PROXY_RENDER_SIZE_100;
1054 /* simple case for movies -- handle frame-by-frame, do threading within single frame */
1055 static void do_movie_proxy(void *pjv, int *UNUSED(build_sizes), int UNUSED(build_count),
1056 int *build_undistort_sizes, int build_undistort_count,
1057 short *stop, short *do_update, float *progress)
1060 Scene *scene = pj->scene;
1061 MovieClip *clip = pj->clip;
1062 struct MovieDistortion *distortion = NULL;
1063 int cfra, sfra = SFRA, efra = EFRA;
1065 if (pj->index_context)
1066 IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
1068 if (!build_undistort_count) {
1079 if (build_undistort_count) {
1080 int threads = BLI_system_thread_count();
1083 BKE_movieclip_get_size(clip, NULL, &width, &height);
1085 distortion = BKE_tracking_distortion_new(&clip->tracking, width, height);
1086 BKE_tracking_distortion_set_threads(distortion, threads);
1089 for (cfra = sfra; cfra <= efra; cfra++) {
1090 BKE_movieclip_build_proxy_frame(clip, pj->clip_flag, distortion, cfra,
1091 build_undistort_sizes, build_undistort_count, 1);
1093 if (*stop || G.is_break)
1097 *progress = ((float) cfra - sfra) / (efra - sfra);
1101 BKE_tracking_distortion_free(distortion);
1108 * special case for sequences -- handle different frames in different threads,
1109 * loading from disk happens in critical section, decoding frame happens from
1110 * thread for maximal speed
1113 typedef struct ProxyQueue {
1124 typedef struct ProxyThread {
1126 struct MovieDistortion *distortion;
1127 int *build_sizes, build_count;
1128 int *build_undistort_sizes, build_undistort_count;
1131 static unsigned char *proxy_thread_next_frame(ProxyQueue *queue, MovieClip *clip, size_t *size_r, int *cfra_r)
1133 unsigned char *mem = NULL;
1135 BLI_spin_lock(&queue->spin);
1136 if (!*queue->stop && queue->cfra <= queue->efra) {
1137 MovieClipUser user = {0};
1138 char name[FILE_MAX];
1142 user.framenr = queue->cfra;
1144 BKE_movieclip_filename_for_frame(clip, &user, name);
1146 file = BLI_open(name, O_BINARY | O_RDONLY, 0);
1148 BLI_spin_unlock(&queue->spin);
1152 size = BLI_file_descriptor_size(file);
1155 BLI_spin_unlock(&queue->spin);
1159 mem = MEM_mallocN(size, "movieclip proxy memory file");
1161 if (read(file, mem, size) != size) {
1163 BLI_spin_unlock(&queue->spin);
1169 *cfra_r = queue->cfra;
1174 *queue->do_update = 1;
1175 *queue->progress = (float)(queue->cfra - queue->sfra) / (queue->efra - queue->sfra);
1177 BLI_spin_unlock(&queue->spin);
1182 static void proxy_task_func(TaskPool *pool, void *task_data, int UNUSED(threadid))
1184 ProxyThread *data = (ProxyThread *)task_data;
1185 ProxyQueue *queue = (ProxyQueue *)BLI_task_pool_userdata(pool);
1190 while ((mem = proxy_thread_next_frame(queue, data->clip, &size, &cfra))) {
1193 ibuf = IMB_ibImageFromMemory(mem, size, IB_rect | IB_multilayer | IB_alphamode_detect,
1194 data->clip->colorspace_settings.name, "proxy frame");
1196 BKE_movieclip_build_proxy_frame_for_ibuf(data->clip, ibuf, NULL, cfra,
1197 data->build_sizes, data->build_count, false);
1199 BKE_movieclip_build_proxy_frame_for_ibuf(data->clip, ibuf, data->distortion, cfra,
1200 data->build_undistort_sizes, data->build_undistort_count, true);
1202 IMB_freeImBuf(ibuf);
1208 static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count,
1209 int *build_undistort_sizes, int build_undistort_count,
1210 short *stop, short *do_update, float *progress)
1213 MovieClip *clip = pj->clip;
1214 Scene *scene = pj->scene;
1215 TaskScheduler *task_scheduler = BLI_task_scheduler_get();
1216 TaskPool *task_pool;
1217 int sfra = SFRA, efra = EFRA;
1218 ProxyThread *handles;
1219 int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler);
1223 if (build_undistort_count) {
1224 BKE_movieclip_get_size(clip, NULL, &width, &height);
1227 BLI_spin_init(&queue.spin);
1233 queue.do_update = do_update;
1234 queue.progress = progress;
1236 task_pool = BLI_task_pool_create(task_scheduler, &queue);
1237 handles = MEM_callocN(sizeof(ProxyThread) * tot_thread,
1238 "proxy threaded handles");
1239 for (i = 0; i < tot_thread; i++) {
1240 ProxyThread *handle = &handles[i];
1242 handle->clip = clip;
1244 handle->build_count = build_count;
1245 handle->build_sizes = build_sizes;
1247 handle->build_undistort_count = build_undistort_count;
1248 handle->build_undistort_sizes = build_undistort_sizes;
1250 if (build_undistort_count) {
1251 handle->distortion = BKE_tracking_distortion_new(&clip->tracking,
1255 BLI_task_pool_push(task_pool,
1262 BLI_task_pool_work_and_wait(task_pool);
1263 BLI_task_pool_free(task_pool);
1265 if (build_undistort_count) {
1266 for (i = 0; i < tot_thread; i++) {
1267 ProxyThread *handle = &handles[i];
1268 BKE_tracking_distortion_free(handle->distortion);
1272 BLI_spin_end(&queue.spin);
1276 static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
1279 MovieClip *clip = pj->clip;
1282 int build_sizes[4], build_count = 0;
1283 int build_undistort_sizes[4], build_undistort_count = 0;
1285 size_flag = clip->proxy.build_size_flag;
1287 build_count = proxy_bitflag_to_array(size_flag, build_sizes, 0);
1288 build_undistort_count = proxy_bitflag_to_array(size_flag, build_undistort_sizes, 1);
1290 if (clip->source == MCLIP_SRC_MOVIE) {
1291 do_movie_proxy(pjv, build_sizes, build_count, build_undistort_sizes,
1292 build_undistort_count, stop, do_update, progress);
1295 do_sequence_proxy(pjv, build_sizes, build_count, build_undistort_sizes,
1296 build_undistort_count, stop, do_update, progress);
1300 static void proxy_endjob(void *pjv)
1305 IMB_close_anim_proxies(pj->clip->anim);
1307 if (pj->index_context)
1308 IMB_anim_index_rebuild_finish(pj->index_context, pj->stop);
1310 if (pj->clip->source == MCLIP_SRC_MOVIE) {
1311 /* Timecode might have changed, so do a full reload to deal with this. */
1312 BKE_movieclip_reload(pj->clip);
1315 /* For image sequences we'll preserve original cache. */
1316 BKE_movieclip_clear_proxy_cache(pj->clip);
1319 WM_main_add_notifier(NC_MOVIECLIP | ND_DISPLAY, pj->clip);
1322 static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
1326 Scene *scene = CTX_data_scene(C);
1327 ScrArea *sa = CTX_wm_area(C);
1328 SpaceClip *sc = CTX_wm_space_clip(C);
1329 MovieClip *clip = ED_space_clip_get_clip(sc);
1331 if ((clip->flag & MCLIP_USE_PROXY) == 0)
1332 return OPERATOR_CANCELLED;
1334 wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Building Proxies",
1335 WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_BUILD_PROXY);
1337 pj = MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
1339 pj->main = CTX_data_main(C);
1341 pj->clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
1344 pj->index_context = IMB_anim_index_rebuild_context(clip->anim, clip->proxy.build_tc_flag,
1345 clip->proxy.build_size_flag, clip->proxy.quality,
1349 WM_jobs_customdata_set(wm_job, pj, proxy_freejob);
1350 WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1351 WM_jobs_callbacks(wm_job, proxy_startjob, NULL, NULL, proxy_endjob);
1354 WM_jobs_start(CTX_wm_manager(C), wm_job);
1356 ED_area_tag_redraw(sa);
1358 return OPERATOR_FINISHED;
1361 void CLIP_OT_rebuild_proxy(wmOperatorType *ot)
1364 ot->name = "Rebuild Proxy and Timecode Indices";
1365 ot->idname = "CLIP_OT_rebuild_proxy";
1366 ot->description = "Rebuild all selected proxies and timecode indices in the background";
1369 ot->exec = clip_rebuild_proxy_exec;
1370 ot->poll = ED_space_clip_poll;
1373 ot->flag = OPTYPE_REGISTER;
1376 /********************** mode set operator *********************/
1378 static int mode_set_exec(bContext *C, wmOperator *op)
1380 SpaceClip *sc = CTX_wm_space_clip(C);
1381 int mode = RNA_enum_get(op->ptr, "mode");
1385 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1387 return OPERATOR_FINISHED;
1390 void CLIP_OT_mode_set(wmOperatorType *ot)
1393 ot->name = "Set Clip Mode";
1394 ot->description = "Set the clip interaction mode";
1395 ot->idname = "CLIP_OT_mode_set";
1398 ot->exec = mode_set_exec;
1400 ot->poll = ED_space_clip_poll;
1403 RNA_def_enum(ot->srna, "mode", clip_editor_mode_items, SC_MODE_TRACKING, "Mode", "");
1406 /********************** NDOF operator *********************/
1408 /* Combined pan/zoom from a 3D mouse device.
1410 * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
1411 * that explains the negative signs in the code below
1414 static int clip_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1416 if (event->type != NDOF_MOTION)
1417 return OPERATOR_CANCELLED;
1419 SpaceClip *sc = CTX_wm_space_clip(C);
1420 ARegion *ar = CTX_wm_region(C);
1423 const wmNDOFMotionData *ndof = event->customdata;
1424 const float speed = NDOF_PIXELS_PER_SECOND;
1426 WM_event_ndof_pan_get(ndof, pan_vec, true);
1428 mul_v2_fl(pan_vec, (speed * ndof->dt) / sc->zoom);
1429 pan_vec[2] *= -ndof->dt;
1431 sclip_zoom_set_factor(C, 1.0f + pan_vec[2], NULL);
1432 sc->xof += pan_vec[0];
1433 sc->yof += pan_vec[1];
1435 ED_region_tag_redraw(ar);
1437 return OPERATOR_FINISHED;
1441 void CLIP_OT_view_ndof(wmOperatorType *ot)
1444 ot->name = "NDOF Pan/Zoom";
1445 ot->idname = "CLIP_OT_view_ndof";
1446 ot->description = "Use a 3D mouse device to pan/zoom the view";
1449 ot->invoke = clip_view_ndof_invoke;
1450 ot->poll = ED_space_clip_view_clip_poll;
1453 /********************** Prefetch operator *********************/
1455 static int clip_prefetch_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1457 /* no running blender, remove handler and pass through */
1458 if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_CLIP_PREFETCH))
1459 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1461 /* running render */
1462 switch (event->type) {
1464 return OPERATOR_RUNNING_MODAL;
1467 return OPERATOR_PASS_THROUGH;
1470 static int clip_prefetch_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(_event))
1472 clip_start_prefetch_job(C);
1474 /* add modal handler for ESC */
1475 WM_event_add_modal_handler(C, op);
1477 return OPERATOR_RUNNING_MODAL;
1480 void CLIP_OT_prefetch(wmOperatorType *ot)
1483 ot->name = "Prefetch Frames";
1484 ot->idname = "CLIP_OT_prefetch";
1485 ot->description = "Prefetch frames from disk for faster playback/tracking";
1488 ot->poll = ED_space_clip_view_clip_poll;
1489 ot->invoke = clip_prefetch_invoke;
1490 ot->modal = clip_prefetch_modal;
1493 /********************** Set scene frames *********************/
1495 static int clip_set_scene_frames_exec(bContext *C, wmOperator *UNUSED(op))
1497 MovieClip *clip = CTX_data_edit_movieclip(C);
1498 Scene *scene = CTX_data_scene(C);
1501 if (ELEM(NULL, scene, clip))
1502 return OPERATOR_CANCELLED;
1504 clip_length = BKE_movieclip_get_duration(clip);
1506 scene->r.sfra = clip->start_frame;
1507 scene->r.efra = scene->r.sfra + clip_length - 1;
1509 scene->r.efra = max_ii(scene->r.sfra, scene->r.efra);
1511 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1513 return OPERATOR_FINISHED;
1516 void CLIP_OT_set_scene_frames(wmOperatorType *ot)
1519 ot->name = "Set Scene Frames";
1520 ot->idname = "CLIP_OT_set_scene_frames";
1521 ot->description = "Set scene's start and end frame to match clip's start frame and length";
1524 ot->poll = ED_space_clip_view_clip_poll;
1525 ot->exec = clip_set_scene_frames_exec;
1528 /******************** set 3d cursor operator ********************/
1530 static int clip_set_2d_cursor_exec(bContext *C, wmOperator *op)
1532 SpaceClip *sclip = CTX_wm_space_clip(C);
1533 bool show_cursor = false;
1535 show_cursor |= sclip->mode == SC_MODE_MASKEDIT;
1536 show_cursor |= sclip->around == V3D_CURSOR;
1539 return OPERATOR_CANCELLED;
1542 RNA_float_get_array(op->ptr, "location", sclip->cursor);
1544 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
1546 return OPERATOR_FINISHED;
1549 static int clip_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1551 ARegion *ar = CTX_wm_region(C);
1552 SpaceClip *sclip = CTX_wm_space_clip(C);
1555 ED_clip_mouse_pos(sclip, ar, event->mval, location);
1556 RNA_float_set_array(op->ptr, "location", location);
1558 return clip_set_2d_cursor_exec(C, op);
1561 void CLIP_OT_cursor_set(wmOperatorType *ot)
1564 ot->name = "Set 2D Cursor";
1565 ot->description = "Set 2D cursor location";
1566 ot->idname = "CLIP_OT_cursor_set";
1569 ot->exec = clip_set_2d_cursor_exec;
1570 ot->invoke = clip_set_2d_cursor_invoke;
1571 ot->poll = ED_space_clip_poll;
1574 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1577 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location",
1578 "Cursor location in normalized clip coordinates", -10.0f, 10.0f);
1581 /********************** macroses *********************/
1583 void ED_operatormacros_clip(void)
1586 wmOperatorTypeMacro *otmacro;
1588 ot = WM_operatortype_append_macro("CLIP_OT_add_marker_move", "Add Marker and Move",
1589 "Add new marker and move it on movie", OPTYPE_UNDO | OPTYPE_REGISTER);
1590 WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1591 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1592 RNA_struct_idprops_unset(otmacro->ptr, "release_confirm");
1594 ot = WM_operatortype_append_macro("CLIP_OT_add_marker_slide", "Add Marker and Slide",
1595 "Add new marker and slide it with mouse until mouse button release",
1596 OPTYPE_UNDO | OPTYPE_REGISTER);
1597 WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
1598 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1599 RNA_boolean_set(otmacro->ptr, "release_confirm", true);