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