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(&region->winrct) + 1) /
158            (BLI_rctf_size_x(&region->v2d.cur) * width);
159   *zoomy = (float)(BLI_rcti_size_y(&region->winrct) + 1) /
160            (BLI_rctf_size_y(&region->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(&region->winrct) + 1;
431     height = BLI_rcti_size_y(&region->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(&region->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(&region->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, &current_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