Cleanup: spelling
[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 the display space, matching ED_space_image_color_sample(). */
263 bool ED_space_clip_color_sample(Scene *scene, SpaceClip *sc, ARegion *ar, int mval[2], float r_col[3])
264 {
265         const char *display_device = scene->display_settings.display_device;
266         struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
267         ImBuf *ibuf;
268         float fx, fy, co[2];
269         bool ret = false;
270
271         ibuf = ED_space_clip_get_buffer(sc);
272         if (!ibuf) {
273                 return false;
274         }
275
276         /* map the mouse coords to the backdrop image space */
277         ED_clip_mouse_pos(sc, ar, mval, co);
278
279         fx = co[0];
280         fy = co[1];
281
282         if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
283                 const float *fp;
284                 unsigned char *cp;
285                 int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
286
287                 CLAMP(x, 0, ibuf->x - 1);
288                 CLAMP(y, 0, ibuf->y - 1);
289
290                 if (ibuf->rect_float) {
291                         fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
292                         copy_v3_v3(r_col, fp);
293                         ret = true;
294                 }
295                 else if (ibuf->rect) {
296                         cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x);
297                         rgb_uchar_to_float(r_col, cp);
298                         IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace);
299                         ret = true;
300                 }
301         }
302
303         if (ret) {
304                 IMB_colormanagement_scene_linear_to_display_v3(r_col, display);
305         }
306
307         IMB_freeImBuf(ibuf);
308
309         return ret;
310 }
311
312 void ED_clip_update_frame(const Main *mainp, int cfra)
313 {
314         wmWindowManager *wm;
315         wmWindow *win;
316
317         /* image window, compo node users */
318         for (wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */
319                 for (win = wm->windows.first; win; win = win->next) {
320                         ScrArea *sa;
321
322                         for (sa = win->screen->areabase.first; sa; sa = sa->next) {
323                                 if (sa->spacetype == SPACE_CLIP) {
324                                         SpaceClip *sc = sa->spacedata.first;
325
326                                         sc->scopes.ok = false;
327
328                                         BKE_movieclip_user_set_frame(&sc->user, cfra);
329                                 }
330                         }
331                 }
332         }
333 }
334
335 static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2])
336 {
337         MovieClip *clip = ED_space_clip_get_clip(sc);
338         MovieTrackingTrack *track;
339         int width, height;
340         bool ok = false;
341         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
342         int framenr = ED_space_clip_get_clip_frame_number(sc);
343
344         INIT_MINMAX2(min, max);
345
346         ED_space_clip_get_size(sc, &width, &height);
347
348         track = tracksbase->first;
349         while (track) {
350                 if (TRACK_VIEW_SELECTED(sc, track)) {
351                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
352
353                         if (marker) {
354                                 float pos[3];
355
356                                 pos[0] = marker->pos[0] + track->offset[0];
357                                 pos[1] = marker->pos[1] + track->offset[1];
358                                 pos[2] = 0.0f;
359
360                                 /* undistortion happens for normalized coords */
361                                 if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
362                                         /* undistortion happens for normalized coords */
363                                         ED_clip_point_undistorted_pos(sc, pos, pos);
364                                 }
365
366                                 pos[0] *= width;
367                                 pos[1] *= height;
368
369                                 mul_v3_m4v3(pos, sc->stabmat, pos);
370
371                                 minmax_v2v2_v2(min, max, pos);
372
373                                 ok = true;
374                         }
375                 }
376
377                 track = track->next;
378         }
379
380         return ok;
381 }
382
383 static bool selected_boundbox(const bContext *C, float min[2], float max[2])
384 {
385         SpaceClip *sc = CTX_wm_space_clip(C);
386         if (sc->mode == SC_MODE_TRACKING) {
387                 return selected_tracking_boundbox(sc, min, max);
388         }
389         else {
390                 if (ED_mask_selected_minmax(C, min, max)) {
391                         MovieClip *clip = ED_space_clip_get_clip(sc);
392                         int width, height;
393                         ED_space_clip_get_size(sc, &width, &height);
394                         BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
395                         BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
396                         min[0] *= width;
397                         min[1] *= height;
398                         max[0] *= width;
399                         max[1] *= height;
400                         return true;
401                 }
402                 return false;
403         }
404 }
405
406 bool ED_clip_view_selection(const bContext *C, ARegion *ar, bool fit)
407 {
408         SpaceClip *sc = CTX_wm_space_clip(C);
409         int w, h, frame_width, frame_height;
410         float min[2], max[2];
411
412         ED_space_clip_get_size(sc, &frame_width, &frame_height);
413
414         if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL))
415                 return false;
416
417         if (!selected_boundbox(C, min, max))
418                 return false;
419
420         /* center view */
421         clip_view_center_to_point(sc, (max[0] + min[0]) / (2 * frame_width),
422                                       (max[1] + min[1]) / (2 * frame_height));
423
424         w = max[0] - min[0];
425         h = max[1] - min[1];
426
427         /* set zoom to see all selection */
428         if (w > 0 && h > 0) {
429                 int width, height;
430                 float zoomx, zoomy, newzoom, aspx, aspy;
431
432                 ED_space_clip_get_aspect(sc, &aspx, &aspy);
433
434                 width  = BLI_rcti_size_x(&ar->winrct) + 1;
435                 height = BLI_rcti_size_y(&ar->winrct) + 1;
436
437                 zoomx = (float)width / w / aspx;
438                 zoomy = (float)height / h / aspy;
439
440                 newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy));
441
442                 if (fit || sc->zoom > newzoom)
443                         sc->zoom = newzoom;
444         }
445
446         return true;
447 }
448
449 void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[2])
450 {
451         copy_v2_v2(r_co, co);
452
453         if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
454                 MovieClip *clip = ED_space_clip_get_clip(sc);
455                 float aspy = 1.0f / clip->tracking.camera.pixel_aspect;
456                 int width, height;
457
458                 BKE_movieclip_get_size(sc->clip, &sc->user, &width, &height);
459
460                 r_co[0] *= width;
461                 r_co[1] *= height * aspy;
462
463                 BKE_tracking_undistort_v2(&clip->tracking, r_co, r_co);
464
465                 r_co[0] /= width;
466                 r_co[1] /= height * aspy;
467         }
468 }
469
470 void ED_clip_point_stable_pos(SpaceClip *sc, ARegion *ar, float x, float y, float *xr, float *yr)
471 {
472         int sx, sy, width, height;
473         float zoomx, zoomy, pos[3], imat[4][4];
474
475         ED_space_clip_get_zoom(sc, ar, &zoomx, &zoomy);
476         ED_space_clip_get_size(sc, &width, &height);
477
478         UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &sx, &sy);
479
480         pos[0] = (x - sx) / zoomx;
481         pos[1] = (y - sy) / zoomy;
482         pos[2] = 0.0f;
483
484         invert_m4_m4(imat, sc->stabmat);
485         mul_v3_m4v3(pos, imat, pos);
486
487         *xr = pos[0] / width;
488         *yr = pos[1] / height;
489
490         if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
491                 MovieClip *clip = ED_space_clip_get_clip(sc);
492                 MovieTracking *tracking = &clip->tracking;
493                 float aspy = 1.0f / tracking->camera.pixel_aspect;
494                 float tmp[2] = {*xr * width, *yr * height * aspy};
495
496                 BKE_tracking_distort_v2(tracking, tmp, tmp);
497
498                 *xr = tmp[0] / width;
499                 *yr = tmp[1] / (height * aspy);
500         }
501 }
502
503 /**
504  * \brief the reverse of ED_clip_point_stable_pos(), gets the marker region coords.
505  * better name here? view_to_track / track_to_view or so?
506  */
507 void ED_clip_point_stable_pos__reverse(SpaceClip *sc, ARegion *ar, const float co[2], float r_co[2])
508 {
509         float zoomx, zoomy;
510         float pos[3];
511         int width, height;
512         int sx, sy;
513
514         UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &sx, &sy);
515         ED_space_clip_get_size(sc, &width, &height);
516         ED_space_clip_get_zoom(sc, ar, &zoomx, &zoomy);
517
518         ED_clip_point_undistorted_pos(sc, co, pos);
519         pos[2] = 0.0f;
520
521         /* untested */
522         mul_v3_m4v3(pos, sc->stabmat, pos);
523
524         r_co[0] = (pos[0] * width  * zoomx) + (float)sx;
525         r_co[1] = (pos[1] * height * zoomy) + (float)sy;
526 }
527
528 /* takes event->mval */
529 void ED_clip_mouse_pos(SpaceClip *sc, ARegion *ar, const int mval[2], float co[2])
530 {
531         ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &co[0], &co[1]);
532 }
533
534 bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
535 {
536         if (sc) {
537                 return sc->mode == SC_MODE_TRACKING;
538         }
539
540         return false;
541 }
542
543 bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
544 {
545         if (sc) {
546                 return sc->mode == SC_MODE_MASKEDIT;
547         }
548
549         return false;
550 }
551
552 /* ******** clip editing functions ******** */
553
554 MovieClip *ED_space_clip_get_clip(SpaceClip *sc)
555 {
556         return sc->clip;
557 }
558
559 void ED_space_clip_set_clip(bContext *C, bScreen *screen, SpaceClip *sc, MovieClip *clip)
560 {
561         MovieClip *old_clip;
562         bool old_clip_visible = false;
563
564         if (!screen && C)
565                 screen = CTX_wm_screen(C);
566
567         old_clip = sc->clip;
568         sc->clip = clip;
569
570         id_us_ensure_real((ID *)sc->clip);
571
572         if (screen && sc->view == SC_VIEW_CLIP) {
573                 ScrArea *area;
574                 SpaceLink *sl;
575
576                 for (area = screen->areabase.first; area; area = area->next) {
577                         for (sl = area->spacedata.first; sl; sl = sl->next) {
578                                 if (sl->spacetype == SPACE_CLIP) {
579                                         SpaceClip *cur_sc = (SpaceClip *) sl;
580
581                                         if (cur_sc != sc) {
582                                                 if (cur_sc->view == SC_VIEW_CLIP) {
583                                                         if (cur_sc->clip == old_clip)
584                                                                 old_clip_visible = true;
585                                                 }
586                                                 else {
587                                                         if (cur_sc->clip == old_clip || cur_sc->clip == NULL) {
588                                                                 cur_sc->clip = clip;
589                                                         }
590                                                 }
591                                         }
592                                 }
593                         }
594                 }
595         }
596
597         /* If clip is no longer visible on screen, free memory used by it's cache */
598         if (old_clip && old_clip != clip && !old_clip_visible) {
599                 BKE_movieclip_clear_cache(old_clip);
600         }
601
602         if (C)
603                 WM_event_add_notifier(C, NC_MOVIECLIP | NA_SELECTED, sc->clip);
604 }
605
606 /* ******** masking editing functions ******** */
607
608 Mask *ED_space_clip_get_mask(SpaceClip *sc)
609 {
610         return sc->mask_info.mask;
611 }
612
613 void ED_space_clip_set_mask(bContext *C, SpaceClip *sc, Mask *mask)
614 {
615         sc->mask_info.mask = mask;
616
617         id_us_ensure_real((ID *)sc->mask_info.mask);
618
619         if (C) {
620                 WM_event_add_notifier(C, NC_MASK | NA_SELECTED, mask);
621         }
622 }
623
624 /* ******** pre-fetching functions ******** */
625
626 typedef struct PrefetchJob {
627         MovieClip *clip;
628         int start_frame, current_frame, end_frame;
629         short render_size, render_flag;
630 } PrefetchJob;
631
632 typedef struct PrefetchQueue {
633         int initial_frame, current_frame, start_frame, end_frame;
634         short render_size, render_flag;
635
636         /* If true prefecthing goes forward in time,
637          * otherwise it goes backwards in time (starting from current frame).
638          */
639         bool forward;
640
641         SpinLock spin;
642
643         short *stop;
644         short *do_update;
645         float *progress;
646 } PrefetchQueue;
647
648 /* check whether pre-fetching is allowed */
649 static bool check_prefetch_break(void)
650 {
651         return G.is_break;
652 }
653
654 /* read file for specified frame number to the memory */
655 static unsigned char *prefetch_read_file_to_memory(
656         MovieClip *clip, int current_frame, short render_size, short render_flag,
657         size_t *r_size)
658 {
659         MovieClipUser user = {0};
660         char name[FILE_MAX];
661         size_t size;
662         int file;
663         unsigned char *mem;
664
665         user.framenr = current_frame;
666         user.render_size = render_size;
667         user.render_flag = render_flag;
668
669         BKE_movieclip_filename_for_frame(clip, &user, name);
670
671         file = BLI_open(name, O_BINARY | O_RDONLY, 0);
672         if (file == -1) {
673                 return NULL;
674         }
675
676         size = BLI_file_descriptor_size(file);
677         if (size < 1) {
678                 close(file);
679                 return NULL;
680         }
681
682         mem = MEM_mallocN(size, "movieclip prefetch memory file");
683
684         if (read(file, mem, size) != size) {
685                 close(file);
686                 MEM_freeN(mem);
687                 return NULL;
688         }
689
690         *r_size = size;
691
692         close(file);
693
694         return mem;
695 }
696
697 /* find first uncached frame within prefetching frame range */
698 static int prefetch_find_uncached_frame(MovieClip *clip, int from_frame, int end_frame,
699                                         short render_size, short render_flag, short direction)
700 {
701         int current_frame;
702         MovieClipUser user = {0};
703
704         user.render_size = render_size;
705         user.render_flag = render_flag;
706
707         if (direction > 0) {
708                 for (current_frame = from_frame; current_frame <= end_frame; current_frame++) {
709                         user.framenr = current_frame;
710
711                         if (!BKE_movieclip_has_cached_frame(clip, &user))
712                                 break;
713                 }
714         }
715         else {
716                 for (current_frame = from_frame; current_frame >= end_frame; current_frame--) {
717                         user.framenr = current_frame;
718
719                         if (!BKE_movieclip_has_cached_frame(clip, &user))
720                                 break;
721                 }
722         }
723
724         return current_frame;
725 }
726
727 /* get memory buffer for first uncached frame within prefetch frame range */
728 static unsigned char *prefetch_thread_next_frame(
729         PrefetchQueue *queue, MovieClip *clip,
730         size_t *r_size, int *r_current_frame)
731 {
732         unsigned char *mem = NULL;
733
734         BLI_spin_lock(&queue->spin);
735         if (!*queue->stop && !check_prefetch_break() &&
736             IN_RANGE_INCL(queue->current_frame, queue->start_frame, queue->end_frame))
737         {
738                 int current_frame;
739
740                 if (queue->forward) {
741                         current_frame = prefetch_find_uncached_frame(clip, queue->current_frame + 1, queue->end_frame,
742                                                                      queue->render_size, queue->render_flag, 1);
743                         /* switch direction if read frames from current up to scene end frames */
744                         if (current_frame > queue->end_frame) {
745                                 queue->current_frame = queue->initial_frame;
746                                 queue->forward = false;
747                         }
748                 }
749
750                 if (!queue->forward) {
751                         current_frame = prefetch_find_uncached_frame(clip, queue->current_frame - 1, queue->start_frame,
752                                                                      queue->render_size, queue->render_flag, -1);
753                 }
754
755                 if (IN_RANGE_INCL(current_frame, queue->start_frame, queue->end_frame)) {
756                         int frames_processed;
757
758                         mem = prefetch_read_file_to_memory(clip, current_frame, queue->render_size,
759                                                            queue->render_flag, r_size);
760
761                         *r_current_frame = current_frame;
762
763                         queue->current_frame = current_frame;
764
765                         if (queue->forward) {
766                                 frames_processed = queue->current_frame - queue->initial_frame;
767                         }
768                         else {
769                                 frames_processed = (queue->end_frame - queue->initial_frame) +
770                                                    (queue->initial_frame - queue->current_frame);
771                         }
772
773                         *queue->do_update = 1;
774                         *queue->progress = (float)frames_processed / (queue->end_frame - queue->start_frame);
775                 }
776         }
777         BLI_spin_unlock(&queue->spin);
778
779         return mem;
780 }
781
782 static void prefetch_task_func(TaskPool * __restrict pool, void *task_data, int UNUSED(threadid))
783 {
784         PrefetchQueue *queue = (PrefetchQueue *)BLI_task_pool_userdata(pool);
785         MovieClip *clip = (MovieClip *)task_data;
786         unsigned char *mem;
787         size_t size;
788         int current_frame;
789
790         while ((mem = prefetch_thread_next_frame(queue, clip, &size, &current_frame))) {
791                 ImBuf *ibuf;
792                 MovieClipUser user = {0};
793                 int flag = IB_rect | IB_alphamode_detect;
794                 int result;
795                 char *colorspace_name = NULL;
796                 const bool use_proxy = (clip->flag & MCLIP_USE_PROXY) &&
797                                        (queue->render_size != MCLIP_PROXY_RENDER_SIZE_FULL);
798
799                 user.framenr = current_frame;
800                 user.render_size = queue->render_size;
801                 user.render_flag = queue->render_flag;
802
803                 /* Proxies are stored in the display space. */
804                 if (!use_proxy) {
805                         colorspace_name = clip->colorspace_settings.name;
806                 }
807
808                 ibuf = IMB_ibImageFromMemory(mem, size, flag, colorspace_name, "prefetch frame");
809
810                 result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
811
812                 IMB_freeImBuf(ibuf);
813
814                 MEM_freeN(mem);
815
816                 if (!result) {
817                         /* no more space in the cache, stop reading frames */
818                         *queue->stop = 1;
819                         break;
820                 }
821         }
822 }
823
824 static void start_prefetch_threads(MovieClip *clip, int start_frame, int current_frame, int end_frame,
825                                    short render_size, short render_flag, short *stop, short *do_update,
826                                    float *progress)
827 {
828         PrefetchQueue queue;
829         TaskScheduler *task_scheduler = BLI_task_scheduler_get();
830         TaskPool *task_pool;
831         int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler);
832
833         /* initialize queue */
834         BLI_spin_init(&queue.spin);
835
836         queue.current_frame = current_frame;
837         queue.initial_frame = current_frame;
838         queue.start_frame = start_frame;
839         queue.end_frame = end_frame;
840         queue.render_size = render_size;
841         queue.render_flag = render_flag;
842         queue.forward = 1;
843
844         queue.stop = stop;
845         queue.do_update = do_update;
846         queue.progress = progress;
847
848         task_pool = BLI_task_pool_create(task_scheduler, &queue);
849         for (i = 0; i < tot_thread; i++) {
850                 BLI_task_pool_push(task_pool,
851                                    prefetch_task_func,
852                                    clip,
853                                    false,
854                                    TASK_PRIORITY_LOW);
855         }
856         BLI_task_pool_work_and_wait(task_pool);
857         BLI_task_pool_free(task_pool);
858
859         BLI_spin_end(&queue.spin);
860 }
861
862 static bool prefetch_movie_frame(MovieClip *clip, int frame, short render_size,
863                                  short render_flag, short *stop)
864 {
865         MovieClipUser user = {0};
866         ImBuf *ibuf;
867
868         if (check_prefetch_break() || *stop)
869                 return false;
870
871         user.framenr = frame;
872         user.render_size = render_size;
873         user.render_flag = render_flag;
874
875         if (!BKE_movieclip_has_cached_frame(clip, &user)) {
876                 ibuf = BKE_movieclip_anim_ibuf_for_frame(clip, &user);
877
878                 if (ibuf) {
879                         int result;
880
881                         result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
882
883                         if (!result) {
884                                 /* no more space in the cache, we could stop prefetching here */
885                                 *stop = 1;
886                         }
887
888                         IMB_freeImBuf(ibuf);
889                 }
890                 else {
891                         /* error reading frame, fair enough stop attempting further reading */
892                         *stop = 1;
893                 }
894         }
895
896         return true;
897 }
898
899 static void do_prefetch_movie(MovieClip *clip, int start_frame, int current_frame, int end_frame,
900                               short render_size, short render_flag, short *stop, short *do_update,
901                               float *progress)
902 {
903         int frame;
904         int frames_processed = 0;
905
906         /* read frames starting from current frame up to scene end frame */
907         for (frame = current_frame; frame <= end_frame; frame++) {
908                 if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop))
909                         return;
910
911                 frames_processed++;
912
913                 *do_update = 1;
914                 *progress = (float) frames_processed / (end_frame - start_frame);
915         }
916
917         /* read frames starting from current frame up to scene start frame */
918         for (frame = current_frame; frame >= start_frame; frame--) {
919                 if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop))
920                         return;
921
922                 frames_processed++;
923
924                 *do_update = 1;
925                 *progress = (float) frames_processed / (end_frame - start_frame);
926         }
927 }
928
929 static void prefetch_startjob(void *pjv, short *stop, short *do_update, float *progress)
930 {
931         PrefetchJob *pj = pjv;
932
933         if (pj->clip->source == MCLIP_SRC_SEQUENCE) {
934                 /* read sequence files in multiple threads */
935                 start_prefetch_threads(pj->clip, pj->start_frame, pj->current_frame, pj->end_frame,
936                                        pj->render_size, pj->render_flag,
937                                        stop, do_update, progress);
938         }
939         else if (pj->clip->source == MCLIP_SRC_MOVIE) {
940                 /* read movie in a single thread */
941                 do_prefetch_movie(pj->clip, pj->start_frame, pj->current_frame, pj->end_frame,
942                                   pj->render_size, pj->render_flag,
943                                   stop, do_update, progress);
944         }
945         else {
946                 BLI_assert(!"Unknown movie clip source when prefetching frames");
947         }
948 }
949
950 static void prefetch_freejob(void *pjv)
951 {
952         PrefetchJob *pj = pjv;
953
954         MEM_freeN(pj);
955 }
956
957 static int prefetch_get_start_frame(const bContext *C)
958 {
959         Scene *scene = CTX_data_scene(C);
960
961         return SFRA;
962 }
963
964 static int prefetch_get_final_frame(const bContext *C)
965 {
966         Scene *scene = CTX_data_scene(C);
967         SpaceClip *sc = CTX_wm_space_clip(C);
968         MovieClip *clip = ED_space_clip_get_clip(sc);
969         int end_frame;
970
971         /* check whether all the frames from prefetch range are cached */
972         end_frame = EFRA;
973
974         if (clip->len)
975                 end_frame = min_ii(end_frame, clip->len);
976
977         return end_frame;
978 }
979
980 /* returns true if early out is possible */
981 static bool prefetch_check_early_out(const bContext *C)
982 {
983         SpaceClip *sc = CTX_wm_space_clip(C);
984         MovieClip *clip = ED_space_clip_get_clip(sc);
985         int first_uncached_frame, end_frame;
986         int clip_len;
987
988         if (clip == NULL) {
989                 return true;
990         }
991
992         clip_len = BKE_movieclip_get_duration(clip);
993
994         /* check whether all the frames from prefetch range are cached */
995         end_frame = prefetch_get_final_frame(C);
996
997         first_uncached_frame =
998                 prefetch_find_uncached_frame(clip, sc->user.framenr, end_frame,
999                                              sc->user.render_size, sc->user.render_flag, 1);
1000
1001         if (first_uncached_frame > end_frame || first_uncached_frame == clip_len) {
1002                 int start_frame = prefetch_get_start_frame(C);
1003
1004                 first_uncached_frame =
1005                         prefetch_find_uncached_frame(clip, sc->user.framenr, start_frame,
1006                                                      sc->user.render_size, sc->user.render_flag, -1);
1007
1008                 if (first_uncached_frame < start_frame)
1009                         return true;
1010         }
1011
1012         return false;
1013 }
1014
1015 void clip_start_prefetch_job(const bContext *C)
1016 {
1017         wmJob *wm_job;
1018         PrefetchJob *pj;
1019         SpaceClip *sc = CTX_wm_space_clip(C);
1020
1021         if (prefetch_check_early_out(C))
1022                 return;
1023
1024         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_area(C), "Prefetching",
1025                              WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_PREFETCH);
1026
1027         /* create new job */
1028         pj = MEM_callocN(sizeof(PrefetchJob), "prefetch job");
1029         pj->clip = ED_space_clip_get_clip(sc);
1030         pj->start_frame = prefetch_get_start_frame(C);
1031         pj->current_frame = sc->user.framenr;
1032         pj->end_frame = prefetch_get_final_frame(C);
1033         pj->render_size = sc->user.render_size;
1034         pj->render_flag = sc->user.render_flag;
1035
1036         WM_jobs_customdata_set(wm_job, pj, prefetch_freejob);
1037         WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
1038         WM_jobs_callbacks(wm_job, prefetch_startjob, NULL, NULL, NULL);
1039
1040         G.is_break = false;
1041
1042         /* and finally start the job */
1043         WM_jobs_start(CTX_wm_manager(C), wm_job);
1044 }