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