1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2011 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup spclip
22 */
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stddef.h>
27 #include <sys/types.h>
28
29 #ifndef WIN32
30 # include <unistd.h>
31 #else
32 # include <io.h>
33 #endif
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_mask_types.h"
38
39 #include "BLI_fileops.h"
40 #include "BLI_listbase.h"
41 #include "BLI_math.h"
42 #include "BLI_rect.h"
43 #include "BLI_task.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_context.h"
47 #include "BKE_global.h"
48 #include "BKE_lib_id.h"
49 #include "BKE_main.h"
50 #include "BKE_mask.h"
51 #include "BKE_movieclip.h"
52 #include "BKE_tracking.h"
53
54 #include "IMB_colormanagement.h"
55 #include "IMB_imbuf.h"
56 #include "IMB_imbuf_types.h"
57
58 #include "ED_clip.h"
59 #include "ED_mask.h"
60 #include "ED_screen.h"
61 #include "ED_select_utils.h"
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66 #include "UI_view2d.h"
67
68 #include "clip_intern.h" /* own include */
69
70 /* ******** operactor poll functions ******** */
71
ED_space_clip_poll(bContext * C)72 bool ED_space_clip_poll(bContext *C)
73 {
74 SpaceClip *sc = CTX_wm_space_clip(C);
75
76 if (sc && sc->clip) {
77 return true;
78 }
79
80 return false;
81 }
82
ED_space_clip_view_clip_poll(bContext * C)83 bool ED_space_clip_view_clip_poll(bContext *C)
84 {
85 SpaceClip *sc = CTX_wm_space_clip(C);
86
87 if (sc) {
88 return sc->view == SC_VIEW_CLIP;
89 }
90
91 return false;
92 }
93
ED_space_clip_tracking_poll(bContext * C)94 bool ED_space_clip_tracking_poll(bContext *C)
95 {
96 SpaceClip *sc = CTX_wm_space_clip(C);
97
98 if (sc && sc->clip) {
99 return ED_space_clip_check_show_trackedit(sc);
100 }
101
102 return false;
103 }
104
ED_space_clip_maskedit_poll(bContext * C)105 bool ED_space_clip_maskedit_poll(bContext *C)
106 {
107 SpaceClip *sc = CTX_wm_space_clip(C);
108
109 if (sc && sc->clip) {
110 return ED_space_clip_check_show_maskedit(sc);
111 }
112
113 return false;
114 }
115
ED_space_clip_maskedit_mask_poll(bContext * C)116 bool ED_space_clip_maskedit_mask_poll(bContext *C)
117 {
118 if (ED_space_clip_maskedit_poll(C)) {
119 MovieClip *clip = CTX_data_edit_movieclip(C);
120
121 if (clip) {
122 SpaceClip *sc = CTX_wm_space_clip(C);
123
124 return sc->mask_info.mask != NULL;
125 }
126 }
127
128 return false;
129 }
130
131 /* ******** common editing functions ******** */
132
ED_space_clip_get_size(SpaceClip * sc,int * width,int * height)133 void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
134 {
135 if (sc->clip) {
136 BKE_movieclip_get_size(sc->clip, &sc->user, width, height);
137 }
138 else {
139 *width = *height = IMG_SIZE_FALLBACK;
140 }
141 }
142
ED_space_clip_get_size_fl(SpaceClip * sc,float size[2])143 void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
144 {
145 int size_i[2];
146 ED_space_clip_get_size(sc, &size_i[0], &size_i[1]);
147 size[0] = size_i[0];
148 size[1] = size_i[1];
149 }
150
ED_space_clip_get_zoom(SpaceClip * sc,ARegion * region,float * zoomx,float * zoomy)151 void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float *zoomy)
152 {
153 int width, height;
154
155 ED_space_clip_get_size(sc, &width, &height);
156
157 *zoomx = (float)(BLI_rcti_size_x(®ion->winrct) + 1) /
158 (BLI_rctf_size_x(®ion->v2d.cur) * width);
159 *zoomy = (float)(BLI_rcti_size_y(®ion->winrct) + 1) /
160 (BLI_rctf_size_y(®ion->v2d.cur) * height);
161 }
162
ED_space_clip_get_aspect(SpaceClip * sc,float * aspx,float * aspy)163 void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
164 {
165 MovieClip *clip = ED_space_clip_get_clip(sc);
166
167 if (clip) {
168 BKE_movieclip_get_aspect(clip, aspx, aspy);
169 }
170 else {
171 *aspx = *aspy = 1.0f;
172 }
173
174 if (*aspx < *aspy) {
175 *aspy = *aspy / *aspx;
176 *aspx = 1.0f;
177 }
178 else {
179 *aspx = *aspx / *aspy;
180 *aspy = 1.0f;
181 }
182 }
183
ED_space_clip_get_aspect_dimension_aware(SpaceClip * sc,float * aspx,float * aspy)184 void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float *aspy)
185 {
186 int w, h;
187
188 /* most of tools does not require aspect to be returned with dimensions correction
189 * due to they're invariant to this stuff, but some transformation tools like rotation
190 * should be aware of aspect correction caused by different resolution in different
191 * directions.
192 * mainly this is used for transformation stuff
193 */
194
195 if (!sc->clip) {
196 *aspx = 1.0f;
197 *aspy = 1.0f;
198
199 return;
200 }
201
202 ED_space_clip_get_aspect(sc, aspx, aspy);
203 BKE_movieclip_get_size(sc->clip, &sc->user, &w, &h);
204
205 *aspx *= (float)w;
206 *aspy *= (float)h;
207
208 if (*aspx < *aspy) {
209 *aspy = *aspy / *aspx;
210 *aspx = 1.0f;
211 }
212 else {
213 *aspx = *aspx / *aspy;
214 *aspy = 1.0f;
215 }
216 }
217
218 /* return current frame number in clip space */
ED_space_clip_get_clip_frame_number(SpaceClip * sc)219 int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
220 {
221 MovieClip *clip = ED_space_clip_get_clip(sc);
222
223 /* Caller must ensure space does have a valid clip, otherwise it will crash, see T45017. */
224 return BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
225 }
226
ED_space_clip_get_buffer(SpaceClip * sc)227 ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
228 {
229 if (sc->clip) {
230 ImBuf *ibuf;
231
232 ibuf = BKE_movieclip_get_postprocessed_ibuf(sc->clip, &sc->user, sc->postproc_flag);
233
234 if (ibuf && (ibuf->rect || ibuf->rect_float)) {
235 return ibuf;
236 }
237
238 if (ibuf) {
239 IMB_freeImBuf(ibuf);
240 }
241 }
242
243 return NULL;
244 }
245
ED_space_clip_get_stable_buffer(SpaceClip * sc,float loc[2],float * scale,float * angle)246 ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale, float *angle)
247 {
248 if (sc->clip) {
249 ImBuf *ibuf;
250
251 ibuf = BKE_movieclip_get_stable_ibuf(
252 sc->clip, &sc->user, loc, scale, angle, sc->postproc_flag);
253
254 if (ibuf && (ibuf->rect || ibuf->rect_float)) {
255 return ibuf;
256 }
257
258 if (ibuf) {
259 IMB_freeImBuf(ibuf);
260 }
261 }
262
263 return NULL;
264 }
265
266 /* Returns color in linear space, matching ED_space_image_color_sample(). */
ED_space_clip_color_sample(SpaceClip * sc,ARegion * region,int mval[2],float r_col[3])267 bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, int mval[2], float r_col[3])
268 {
269 ImBuf *ibuf;
270 float fx, fy, co[2];
271 bool ret = false;
272
273 ibuf = ED_space_clip_get_buffer(sc);
274 if (!ibuf) {
275 return false;
276 }
277
278 /* map the mouse coords to the backdrop image space */
279 ED_clip_mouse_pos(sc, region, mval, co);
280
281 fx = co[0];
282 fy = co[1];
283
284 if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
285 const float *fp;
286 uchar *cp;
287 int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
288
289 CLAMP(x, 0, ibuf->x - 1);
290 CLAMP(y, 0, ibuf->y - 1);
291
292 if (ibuf->rect_float) {
293 fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
294 copy_v3_v3(r_col, fp);
295 ret = true;
296 }
297 else if (ibuf->rect) {
298 cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
299 rgb_uchar_to_float(r_col, cp);
300 IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace);
301 ret = true;
302 }
303 }
304
305 IMB_freeImBuf(ibuf);
306
307 return ret;
308 }
309
ED_clip_update_frame(const Main * mainp,int cfra)310 void ED_clip_update_frame(const Main *mainp, int cfra)
311 {
312 /* image window, compo node users */
313 for (wmWindowManager *wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */
314 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
315 bScreen *screen = WM_window_get_active_screen(win);
316
317 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
318 if (area->spacetype == SPACE_CLIP) {
319 SpaceClip *sc = area->spacedata.first;
320
321 sc->scopes.ok = false;
322
323 BKE_movieclip_user_set_frame(&sc->user, cfra);
324 }
325 }
326 }
327 }
328 }
329
selected_tracking_boundbox(SpaceClip * sc,float min[2],float max[2])330 static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2])
331 {
332 MovieClip *clip = ED_space_clip_get_clip(sc);
333 MovieTrackingTrack *track;
334 int width, height;
335 bool ok = false;
336 ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
337 int framenr = ED_space_clip_get_clip_frame_number(sc);
338
339 INIT_MINMAX2(min, max);
340
341 ED_space_clip_get_size(sc, &width, &height);
342
343 track = tracksbase->first;
344 while (track) {
345 if (TRACK_VIEW_SELECTED(sc, track)) {
346 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
347
348 if (marker) {
349 float pos[3];
350
351 pos[0] = marker->pos[0] + track->offset[0];
352 pos[1] = marker->pos[1] + track->offset[1];
353 pos[2] = 0.0f;
354
355 /* undistortion happens for normalized coords */
356 if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
357 /* undistortion happens for normalized coords */
358 ED_clip_point_undistorted_pos(sc, pos, pos);
359 }
360
361 pos[0] *= width;
362 pos[1] *= height;
363
364 mul_v3_m4v3(pos, sc->stabmat, pos);
365
366 minmax_v2v2_v2(min, max, pos);
367
368 ok = true;
369 }
370 }
371
372 track = track->next;
373 }
374
375 return ok;
376 }
377
selected_boundbox(const bContext * C,float min[2],float max[2])378 static bool selected_boundbox(const bContext *C, float min[2], float max[2])
379 {
380 SpaceClip *sc = CTX_wm_space_clip(C);
381 if (sc->mode == SC_MODE_TRACKING) {
382 return selected_tracking_boundbox(sc, min, max);
383 }
384
385 if (ED_mask_selected_minmax(C, min, max)) {
386 MovieClip *clip = ED_space_clip_get_clip(sc);
387 int width, height;
388 ED_space_clip_get_size(sc, &width, &height);
389 BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
390 BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
391 min[0] *= width;
392 min[1] *= height;
393 max[0] *= width;
394 max[1] *= height;
395 return true;
396 }
397 return false;
398 }
399
ED_clip_view_selection(const bContext * C,ARegion * region,bool fit)400 bool ED_clip_view_selection(const bContext *C, ARegion *region, bool fit)
401 {
402 SpaceClip *sc = CTX_wm_space_clip(C);
403 int w, h, frame_width, frame_height;
404 float min[2], max[2];
405
406 ED_space_clip_get_size(sc, &frame_width, &frame_height);
407
408 if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) {
409 return false;
410 }
411
412 if (!selected_boundbox(C, min, max)) {
413 return false;
414 }
415
416 /* center view */
417 clip_view_center_to_point(
418 sc, (max[0] + min[0]) / (2 * frame_width), (max[1] + min[1]) / (2 * frame_height));
419
420 w = max[0] - min[0];
421 h = max[1] - min[1];
422
423 /* set zoom to see all selection */
424 if (w > 0 && h > 0) {
425 int width, height;
426 float zoomx, zoomy, newzoom, aspx, aspy;
427
428 ED_space_clip_get_aspect(sc, &aspx, &aspy);
429
430 width = BLI_rcti_size_x(®ion->winrct) + 1;
431 height = BLI_rcti_size_y(®ion->winrct) + 1;
432
433 zoomx = (float)width / w / aspx;
434 zoomy = (float)height / h / aspy;
435
436 newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy));
437
438 if (fit || sc->zoom > newzoom) {
439 sc->zoom = newzoom;
440 }
441 }
442
443 return true;
444 }
445
ED_clip_select_all(SpaceClip * sc,int action,bool * r_has_selection)446 void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
447 {
448 MovieClip *clip = ED_space_clip_get_clip(sc);
449 const int framenr = ED_space_clip_get_clip_frame_number(sc);
450 MovieTracking *tracking = &clip->tracking;
451 MovieTrackingTrack *track = NULL; /* selected track */
452 MovieTrackingPlaneTrack *plane_track = NULL; /* selected plane track */
453 MovieTrackingMarker *marker;
454 ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
455 ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
456 bool has_selection = false;
457
458 if (action == SEL_TOGGLE) {
459 action = SEL_SELECT;
460
461 for (track = tracksbase->first; track; track = track->next) {
462 if (TRACK_VIEW_SELECTED(sc, track)) {
463 marker = BKE_tracking_marker_get(track, framenr);
464
465 if (MARKER_VISIBLE(sc, track, marker)) {
466 action = SEL_DESELECT;
467 break;
468 }
469 }
470 }
471
472 for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
473 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
474 action = SEL_DESELECT;
475 break;
476 }
477 }
478 }
479
480 for (track = tracksbase->first; track; track = track->next) {
481 if ((track->flag & TRACK_HIDDEN) == 0) {
482 marker = BKE_tracking_marker_get(track, framenr);
483
484 if (MARKER_VISIBLE(sc, track, marker)) {
485 switch (action) {
486 case SEL_SELECT:
487 track->flag |= SELECT;
488 track->pat_flag |= SELECT;
489 track->search_flag |= SELECT;
490 break;
491 case SEL_DESELECT:
492 track->flag &= ~SELECT;
493 track->pat_flag &= ~SELECT;
494 track->search_flag &= ~SELECT;
495 break;
496 case SEL_INVERT:
497 track->flag ^= SELECT;
498 track->pat_flag ^= SELECT;
499 track->search_flag ^= SELECT;
500 break;
501 }
502 }
503 }
504
505 if (TRACK_VIEW_SELECTED(sc, track)) {
506 has_selection = true;
507 }
508 }
509
510 for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
511 if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
512 switch (action) {
513 case SEL_SELECT:
514 plane_track->flag |= SELECT;
515 break;
516 case SEL_DESELECT:
517 plane_track->flag &= ~SELECT;
518 break;
519 case SEL_INVERT:
520 plane_track->flag ^= SELECT;
521 break;
522 }
523 if (plane_track->flag & SELECT) {
524 has_selection = true;
525 }
526 }
527 }
528
529 if (r_has_selection) {
530 *r_has_selection = has_selection;
531 }
532 }
533
ED_clip_point_undistorted_pos(SpaceClip * sc,const float co[2],float r_co[2])534 void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[2])
535 {
536 copy_v2_v2(r_co, co);
537
538 if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
539 MovieClip *clip = ED_space_clip_get_clip(sc);
540 float aspy = 1.0f / clip->tracking.camera.pixel_aspect;
541 int width, height;
542
543 BKE_movieclip_get_size(sc->clip, &sc->user, &width, &height);
544
545 r_co[0] *= width;
546 r_co[1] *= height * aspy;
547
548 BKE_tracking_undistort_v2(&clip->tracking, width, height, r_co, r_co);
549
550 r_co[0] /= width;
551 r_co[1] /= height * aspy;
552 }
553 }
554
ED_clip_point_stable_pos(SpaceClip * sc,ARegion * region,float x,float y,float * xr,float * yr)555 void ED_clip_point_stable_pos(
556 SpaceClip *sc, ARegion *region, float x, float y, float *xr, float *yr)
557 {
558 int sx, sy, width, height;
559 float zoomx, zoomy, pos[3], imat[4][4];
560
561 ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
562 ED_space_clip_get_size(sc, &width, &height);
563
564 UI_view2d_view_to_region(®ion->v2d, 0.0f, 0.0f, &sx, &sy);
565
566 pos[0] = (x - sx) / zoomx;
567 pos[1] = (y - sy) / zoomy;
568 pos[2] = 0.0f;
569
570 invert_m4_m4(imat, sc->stabmat);
571 mul_v3_m4v3(pos, imat, pos);
572
573 *xr = pos[0] / width;
574 *yr = pos[1] / height;
575
576 if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
577 MovieClip *clip = ED_space_clip_get_clip(sc);
578 MovieTracking *tracking = &clip->tracking;
579 float aspy = 1.0f / tracking->camera.pixel_aspect;
580 float tmp[2] = {*xr * width, *yr * height * aspy};
581
582 BKE_tracking_distort_v2(tracking, width, height, tmp, tmp);
583
584 *xr = tmp[0] / width;
585 *yr = tmp[1] / (height * aspy);
586 }
587 }
588
589 /**
590 * \brief the reverse of #ED_clip_point_stable_pos(), gets the marker region coords.
591 * better name here? view_to_track / track_to_view or so?
592 */
ED_clip_point_stable_pos__reverse(SpaceClip * sc,ARegion * region,const float co[2],float r_co[2])593 void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
594 ARegion *region,
595 const float co[2],
596 float r_co[2])
597 {
598 float zoomx, zoomy;
599 float pos[3];
600 int width, height;
601 int sx, sy;
602
603 UI_view2d_view_to_region(®ion->v2d, 0.0f, 0.0f, &sx, &sy);
604 ED_space_clip_get_size(sc, &width, &height);
605 ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
606
607 ED_clip_point_undistorted_pos(sc, co, pos);
608 pos[2] = 0.0f;
609
610 /* untested */
611 mul_v3_m4v3(pos, sc->stabmat, pos);
612
613 r_co[0] = (pos[0] * width * zoomx) + (float)sx;
614 r_co[1] = (pos[1] * height * zoomy) + (float)sy;
615 }
616
617 /* takes event->mval */
ED_clip_mouse_pos(SpaceClip * sc,ARegion * region,const int mval[2],float co[2])618 void ED_clip_mouse_pos(SpaceClip *sc, ARegion *region, const int mval[2], float co[2])
619 {
620 ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &co[0], &co[1]);
621 }
622
ED_space_clip_check_show_trackedit(SpaceClip * sc)623 bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
624 {
625 if (sc) {
626 return sc->mode == SC_MODE_TRACKING;
627 }
628
629 return false;
630 }
631
ED_space_clip_check_show_maskedit(SpaceClip * sc)632 bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
633 {
634 if (sc) {
635 return sc->mode == SC_MODE_MASKEDIT;
636 }
637
638 return false;
639 }
640
641 /* ******** clip editing functions ******** */
642
ED_space_clip_get_clip(SpaceClip * sc)643 MovieClip *ED_space_clip_get_clip(SpaceClip *sc)
644 {
645 return sc->clip;
646 }
647
ED_space_clip_set_clip(bContext * C,bScreen * screen,SpaceClip * sc,MovieClip * clip)648 void ED_space_clip_set_clip(bContext *C, bScreen *screen, SpaceClip *sc, MovieClip *clip)
649 {
650 MovieClip *old_clip;
651 bool old_clip_visible = false;
652
653 if (!screen && C) {
654 screen = CTX_wm_screen(C);
655 }
656
657 old_clip = sc->clip;
658 sc->clip = clip;
659
660 id_us_ensure_real((ID *)sc->clip);
661
662 if (screen && sc->view == SC_VIEW_CLIP) {
663 ScrArea *area;
664 SpaceLink *sl;
665
666 for (area = screen->areabase.first; area; area = area->next) {
667 for (sl = area->spacedata.first; sl; sl = sl->next) {
668 if (sl->spacetype == SPACE_CLIP) {
669 SpaceClip *cur_sc = (SpaceClip *)sl;
670
671 if (cur_sc != sc) {
672 if (cur_sc->view == SC_VIEW_CLIP) {
673 if (cur_sc->clip == old_clip) {
674 old_clip_visible = true;
675 }
676 }
677 else {
678 if (cur_sc->clip == old_clip || cur_sc->clip == NULL) {
679 cur_sc->clip = clip;
680 }
681 }
682 }
683 }
684 }
685 }
686 }
687
688 /* If clip is no longer visible on screen, free memory used by its cache */
689 if (old_clip && old_clip != clip && !old_clip_visible) {
690 BKE_movieclip_clear_cache(old_clip);
691 }
692
693 if (C) {
694 WM_event_add_notifier(C, NC_MOVIECLIP | NA_SELECTED, sc->clip);
695 }
696 }
697
698 /* ******** masking editing functions ******** */
699
ED_space_clip_get_mask(SpaceClip * sc)700 Mask *ED_space_clip_get_mask(SpaceClip *sc)
701 {
702 return sc->mask_info.mask;
703 }
704
ED_space_clip_set_mask(bContext * C,SpaceClip * sc,Mask * mask)705 void ED_space_clip_set_mask(bContext *C, SpaceClip *sc, Mask *mask)
706 {
707 sc->mask_info.mask = mask;
708
709 id_us_ensure_real((ID *)sc->mask_info.mask);
710
711 if (C) {
712 WM_event_add_notifier(C, NC_MASK | NA_SELECTED, mask);
713 }
714 }
715
716 /* ******** pre-fetching functions ******** */
717
718 typedef struct PrefetchJob {
719 MovieClip *clip;
720 int start_frame, current_frame, end_frame;
721 short render_size, render_flag;
722 } PrefetchJob;
723
724 typedef struct PrefetchQueue {
725 int initial_frame, current_frame, start_frame, end_frame;
726 short render_size, render_flag;
727
728 /* If true pre-fetching goes forward in time,
729 * otherwise it goes backwards in time (starting from current frame).
730 */
731 bool forward;
732
733 SpinLock spin;
734
735 short *stop;
736 short *do_update;
737 float *progress;
738 } PrefetchQueue;
739
740 /* check whether pre-fetching is allowed */
check_prefetch_break(void)741 static bool check_prefetch_break(void)
742 {
743 return G.is_break;
744 }
745
746 /* read file for specified frame number to the memory */
prefetch_read_file_to_memory(MovieClip * clip,int current_frame,short render_size,short render_flag,size_t * r_size)747 static uchar *prefetch_read_file_to_memory(
748 MovieClip *clip, int current_frame, short render_size, short render_flag, size_t *r_size)
749 {
750 MovieClipUser user = {0};
751 user.framenr = current_frame;
752 user.render_size = render_size;
753 user.render_flag = render_flag;
754
755 char name[FILE_MAX];
756 BKE_movieclip_filename_for_frame(clip, &user, name);
757
758 int file = BLI_open(name, O_BINARY | O_RDONLY, 0);
759 if (file == -1) {
760 return NULL;
761 }
762
763 const size_t size = BLI_file_descriptor_size(file);
764 if (size < 1) {
765 close(file);
766 return NULL;
767 }
768
769 uchar *mem = MEM_mallocN(size, "movieclip prefetch memory file");
770 if (mem == NULL) {
771 close(file);
772 return NULL;
773 }
774
775 if (read(file, mem, size) != size) {
776 close(file);
777 MEM_freeN(mem);
778 return NULL;
779 }
780
781 *r_size = size;
782
783 close(file);
784
785 return mem;
786 }
787
788 /* find first uncached frame within prefetching frame range */
prefetch_find_uncached_frame(MovieClip * clip,int from_frame,int end_frame,short render_size,short render_flag,short direction)789 static int prefetch_find_uncached_frame(MovieClip *clip,
790 int from_frame,
791 int end_frame,
792 short render_size,
793 short render_flag,
794 short direction)
795 {
796 int current_frame;
797 MovieClipUser user = {0};
798
799 user.render_size = render_size;
800 user.render_flag = render_flag;
801
802 if (direction > 0) {
803 for (current_frame = from_frame; current_frame <= end_frame; current_frame++) {
804 user.framenr = current_frame;
805
806 if (!BKE_movieclip_has_cached_frame(clip, &user)) {
807 break;
808 }
809 }
810 }
811 else {
812 for (current_frame = from_frame; current_frame >= end_frame; current_frame--) {
813 user.framenr = current_frame;
814
815 if (!BKE_movieclip_has_cached_frame(clip, &user)) {
816 break;
817 }
818 }
819 }
820
821 return current_frame;
822 }
823
824 /* get memory buffer for first uncached frame within prefetch frame range */
prefetch_thread_next_frame(PrefetchQueue * queue,MovieClip * clip,size_t * r_size,int * r_current_frame)825 static uchar *prefetch_thread_next_frame(PrefetchQueue *queue,
826 MovieClip *clip,
827 size_t *r_size,
828 int *r_current_frame)
829 {
830 uchar *mem = NULL;
831
832 BLI_spin_lock(&queue->spin);
833 if (!*queue->stop && !check_prefetch_break() &&
834 IN_RANGE_INCL(queue->current_frame, queue->start_frame, queue->end_frame)) {
835 int current_frame;
836
837 if (queue->forward) {
838 current_frame = prefetch_find_uncached_frame(clip,
839 queue->current_frame + 1,
840 queue->end_frame,
841 queue->render_size,
842 queue->render_flag,
843 1);
844 /* switch direction if read frames from current up to scene end frames */
845 if (current_frame > queue->end_frame) {
846 queue->current_frame = queue->initial_frame;
847 queue->forward = false;
848 }
849 }
850
851 if (!queue->forward) {
852 current_frame = prefetch_find_uncached_frame(clip,
853 queue->current_frame - 1,
854 queue->start_frame,
855 queue->render_size,
856 queue->render_flag,
857 -1);
858 }
859
860 if (IN_RANGE_INCL(current_frame, queue->start_frame, queue->end_frame)) {
861 int frames_processed;
862
863 mem = prefetch_read_file_to_memory(
864 clip, current_frame, queue->render_size, queue->render_flag, r_size);
865
866 *r_current_frame = current_frame;
867
868 queue->current_frame = current_frame;
869
870 if (queue->forward) {
871 frames_processed = queue->current_frame - queue->initial_frame;
872 }
873 else {
874 frames_processed = (queue->end_frame - queue->initial_frame) +
875 (queue->initial_frame - queue->current_frame);
876 }
877
878 *queue->do_update = 1;
879 *queue->progress = (float)frames_processed / (queue->end_frame - queue->start_frame);
880 }
881 }
882 BLI_spin_unlock(&queue->spin);
883
884 return mem;
885 }
886
prefetch_task_func(TaskPool * __restrict pool,void * task_data)887 static void prefetch_task_func(TaskPool *__restrict pool, void *task_data)
888 {
889 PrefetchQueue *queue = (PrefetchQueue *)BLI_task_pool_user_data(pool);
890 MovieClip *clip = (MovieClip *)task_data;
891 uchar *mem;
892 size_t size;
893 int current_frame;
894
895 while ((mem = prefetch_thread_next_frame(queue, clip, &size, ¤t_frame))) {
896 ImBuf *ibuf;
897 MovieClipUser user = {0};
898 int flag = IB_rect | IB_multilayer | IB_alphamode_detect | IB_metadata;
899 int result;
900 char *colorspace_name = NULL;
901 const bool use_proxy = (clip->flag & MCLIP_USE_PROXY) &&
902 (queue->render_size != MCLIP_PROXY_RENDER_SIZE_FULL);
903
904 user.framenr = current_frame;
905 user.render_size = queue->render_size;
906 user.render_flag = queue->render_flag;
907
908 /* Proxies are stored in the display space. */
909 if (!use_proxy) {
910 colorspace_name = clip->colorspace_settings.name;
911 }
912
913 ibuf = IMB_ibImageFromMemory(mem, size, flag, colorspace_name, "prefetch frame");
914 if (ibuf == NULL) {
915 continue;
916 }
917 BKE_movieclip_convert_multilayer_ibuf(ibuf);
918
919 result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
920
921 IMB_freeImBuf(ibuf);
922
923 MEM_freeN(mem);
924
925 if (!result) {
926 /* no more space in the cache, stop reading frames */
927 *queue->stop = 1;
928 break;
929 }
930 }
931 }
932
start_prefetch_threads(MovieClip * clip,int start_frame,int current_frame,int end_frame,short render_size,short render_flag,short * stop,short * do_update,float * progress)933 static void start_prefetch_threads(MovieClip *clip,
934 int start_frame,
935 int current_frame,
936 int end_frame,
937 short render_size,
938 short render_flag,
939 short *stop,
940 short *do_update,
941 float *progress)
942 {
943 int tot_thread = BLI_task_scheduler_num_threads();
944
945 /* initialize queue */
946 PrefetchQueue queue;
947 BLI_spin_init(&queue.spin);
948
949 queue.current_frame = current_frame;
950 queue.initial_frame = current_frame;
951 queue.start_frame = start_frame;
952 queue.end_frame = end_frame;
953 queue.render_size = render_size;
954 queue.render_flag = render_flag;
955 queue.forward = 1;
956
957 queue.stop = stop;
958 queue.do_update = do_update;
959 queue.progress = progress;
960
961 TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
962 for (int i = 0; i < tot_thread; i++) {
963 BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL);
964 }
965 BLI_task_pool_work_and_wait(task_pool);
966 BLI_task_pool_free(task_pool);
967
968 BLI_spin_end(&queue.spin);
969 }
970
prefetch_movie_frame(MovieClip * clip,int frame,short render_size,short render_flag,short * stop)971 static bool prefetch_movie_frame(
972 MovieClip *clip, int frame, short render_size, short render_flag, short *stop)
973 {
974 MovieClipUser user = {0};
975 ImBuf *ibuf;
976
977 if (check_prefetch_break() || *stop) {
978 return false;
979 }
980
981 user.framenr = frame;
982 user.render_size = render_size;
983 user.render_flag = render_flag;
984
985 if (!BKE_movieclip_has_cached_frame(clip, &user)) {
986 ibuf = BKE_movieclip_anim_ibuf_for_frame(clip, &user);
987
988 if (ibuf) {
989 int result;
990
991 result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
992
993 if (!result) {
994 /* no more space in the cache, we could stop prefetching here */
995 *stop = 1;
996 }
997
998 IMB_freeImBuf(ibuf);
999 }
1000 else {
1001 /* error reading frame, fair enough stop attempting further reading */
1002 *stop = 1;
1003 }
1004 }
1005
1006 return true;
1007 }
1008
do_prefetch_movie(MovieClip * clip,int start_frame,int current_frame,int end_frame,short render_size,short render_flag,short * stop,short * do_update,float * progress)1009 static void do_prefetch_movie(MovieClip *clip,
1010 int start_frame,
1011 int current_frame,
1012 int end_frame,
1013 short render_size,
1014 short render_flag,
1015 short *stop,
1016 short *do_update,
1017 float *progress)
1018 {
1019 int frame;
1020 int frames_processed = 0;
1021
1022 /* read frames starting from current frame up to scene end frame */
1023 for (frame = current_frame; frame <= end_frame; frame++) {
1024 if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop)) {
1025 return;
1026 }
1027
1028 frames_processed++;
1029
1030 *do_update = 1;
1031 *progress = (float)frames_processed / (end_frame - start_frame);
1032 }
1033
1034 /* read frames starting from current frame up to scene start frame */
1035 for (frame = current_frame; frame >= start_frame; frame--) {
1036 if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop)) {
1037 return;
1038 }
1039
1040 frames_processed++;
1041
1042 *do_update = 1;
1043 *progress = (float)frames_processed / (end_frame - start_frame);
1044 }
1045 }
1046
prefetch_startjob(void * pjv,short * stop,short * do_update,float * progress)1047 static void prefetch_startjob(void *pjv, short *stop, short *do_update, float *progress)
1048 {
1049 PrefetchJob *pj = pjv;
1050
1051 if (pj->clip->source == MCLIP_SRC_SEQUENCE) {
1052 /* read sequence files in multiple threads */
1053 start_prefetch_threads(pj->clip,
1054 pj->start_frame,
1055 pj->current_frame,
1056 pj->end_frame,
1057 pj->render_size,
1058 pj->render_flag,
1059 stop,
1060 do_update,
1061 progress);
1062 }
1063 else if (pj->clip->source == MCLIP_SRC_MOVIE) {
1064 /* read movie in a single thread */
1065 do_prefetch_movie(pj->clip,
1066 pj->start_frame,
1067 pj->current_frame,
1068 pj->end_frame,
1069 pj->render_size,
1070 pj->render_flag,
1071 stop,
1072 do_update,
1073 progress);
1074 }
1075 else {
1076 BLI_assert(!"Unknown movie clip source when prefetching frames");
1077 }
1078 }
1079
prefetch_freejob(void * pjv)1080 static void prefetch_freejob(void *pjv)
1081 {
1082 PrefetchJob *pj = pjv;
1083
1084 MEM_freeN(pj);
1085 }
1086
prefetch_get_start_frame(const bContext * C)1087 static int prefetch_get_start_frame(const bContext *C)
1088 {
1089 Scene *scene = CTX_data_scene(C);
1090
1091 return SFRA;
1092 }
1093
prefetch_get_final_frame(const bContext * C)1094 static int prefetch_get_final_frame(const bContext *C)
1095 {
1096 Scene *scene = CTX_data_scene(C);
1097 SpaceClip *sc = CTX_wm_space_clip(C);
1098 MovieClip *clip = ED_space_clip_get_clip(sc);
1099 int end_frame;
1100
1101 /* check whether all the frames from prefetch range are cached */
1102 end_frame = EFRA;
1103
1104 if (clip->len) {
1105 end_frame = min_ii(end_frame, SFRA + clip->len - 1);
1106 }
1107
1108 return end_frame;
1109 }
1110
1111 /* returns true if early out is possible */
prefetch_check_early_out(const bContext * C)1112 static bool prefetch_check_early_out(const bContext *C)
1113 {
1114 SpaceClip *sc = CTX_wm_space_clip(C);
1115 MovieClip *clip = ED_space_clip_get_clip(sc);
1116 int first_uncached_frame, end_frame;
1117 int clip_len;
1118
1119 if (clip == NULL) {
1120 return true;
1121 }
1122
1123 clip_len = BKE_movieclip_get_duration(clip);
1124
1125 /* check whether all the frames from prefetch range are cached */
1126 end_frame = prefetch_get_final_frame(C);
1127
1128 first_uncached_frame = prefetch_find_uncached_frame(
1129 clip, sc->user.framenr, end_frame, sc->user.render_size, sc->user.render_flag, 1);
1130
1131 if (first_uncached_frame > end_frame || first_uncached_frame == clip_len) {
1132 int start_frame = prefetch_get_start_frame(C);
1133
1134 first_uncached_frame = prefetch_find_uncached_frame(
1135 clip, sc->user.framenr, start_frame, sc->user.render_size, sc->user.render_flag, -1);
1136
1137 if (first_uncached_frame < start_frame) {
1138 return true;
1139 }
1140 }
1141
1142 return false;
1143 }
1144
clip_start_prefetch_job(const bContext * C)1145 void clip_start_prefetch_job(const bContext *C)
1146 {
1147 wmJob *wm_job;
1148 PrefetchJob *pj;
1149 SpaceClip *sc = CTX_wm_space_clip(C);
1150
1151 if (prefetch_check_early_out(C)) {
1152 return;
1153 }
1154
1155 wm_job = WM_jobs_get(CTX_wm_manager(C),
1156 CTX_wm_window(C),
1157 CTX_data_scene(C),
1158 "Prefetching",
1159 WM_JOB_PROGRESS,
1160 WM_JOB_TYPE_CLIP_PREFETCH);
1161
1162 /* create new job */
1163 pj = MEM_callocN(sizeof(PrefetchJob), "prefetch job");
1164 pj->clip = ED_space_clip_get_clip(sc);
1165 pj->start_frame = prefetch_get_start_frame(C);
1166 pj->current_frame = sc->user.framenr;
1167 pj->end_frame = prefetch_get_final_frame(C);
1168 pj->render_size = sc->user.render_size;
1169 pj->render_flag = sc->user.render_flag;
1170
1171 WM_jobs_customdata_set(wm_job, pj, prefetch_freejob);
1172 WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1173 WM_jobs_callbacks(wm_job, prefetch_startjob, NULL, NULL, NULL);
1174
1175 G.is_break = false;
1176
1177 /* and finally start the job */
1178 WM_jobs_start(CTX_wm_manager(C), wm_job);
1179 }
1180