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