Fix T55796: Motion Paths are not being drawn as overlay
[blender.git] / source / blender / windowmanager / intern / wm_playanim.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Campbell Barton
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/windowmanager/intern/wm_playanim.c
29  *  \ingroup wm
30  *
31  * \note This file uses ghost directly and none of the WM definitions.
32  *       this could be made into its own module, alongside creator/
33  */
34
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40
41 #ifndef WIN32
42 #  include <unistd.h>
43 #  include <sys/times.h>
44 #  include <sys/wait.h>
45 #else
46 #  include <io.h>
47 #endif
48 #include "MEM_guardedalloc.h"
49
50 #include "PIL_time.h"
51
52 #include "BLI_utildefines.h"
53 #include "BLI_fileops.h"
54 #include "BLI_listbase.h"
55 #include "BLI_path_util.h"
56 #include "BLI_string.h"
57
58 #include "IMB_imbuf_types.h"
59 #include "IMB_imbuf.h"
60
61 #include "BKE_image.h"
62
63 #include "BIF_gl.h"
64 #include "BIF_glutil.h"
65
66 #include "GPU_matrix.h"
67 #include "GPU_immediate.h"
68 #include "GPU_immediate_util.h"
69 #include "GPU_batch.h"
70 #include "GPU_init_exit.h"
71
72 #include "DNA_scene_types.h"
73 #include "ED_datafiles.h" /* for fonts */
74 #include "GHOST_C-api.h"
75 #include "BLF_api.h"
76
77 #include "DEG_depsgraph.h"
78
79 #include "WM_api.h"  /* only for WM_main_playanim */
80
81 #ifdef WITH_AUDASPACE
82 #  include <AUD_Device.h>
83 #  include <AUD_Handle.h>
84 #  include <AUD_Sound.h>
85 #  include <AUD_Special.h>
86
87 static AUD_Sound *source = NULL;
88 static AUD_Handle *playback_handle = NULL;
89 static AUD_Handle *scrub_handle = NULL;
90 static AUD_Device *audio_device = NULL;
91 #endif
92
93 /* simple limiter to avoid flooding memory */
94 #define USE_FRAME_CACHE_LIMIT
95 #ifdef USE_FRAME_CACHE_LIMIT
96 #  define PLAY_FRAME_CACHE_MAX 30
97 #endif
98
99 struct PlayState;
100 static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
101
102 typedef struct PlayState {
103
104         /* window and viewport size */
105         int win_x, win_y;
106
107         /* current zoom level */
108         float zoom;
109
110         /* playback state */
111         short direction;
112         short next_frame;
113
114         bool  once;
115         bool  turbo;
116         bool  pingpong;
117         bool  noskip;
118         bool  indicator;
119         bool  sstep;
120         bool  wait2;
121         bool  stopped;
122         bool  go;
123         /* waiting for images to load */
124         bool  loading;
125         /* x/y image flip */
126         bool draw_flip[2];
127
128         int fstep;
129
130         /* current picture */
131         struct PlayAnimPict *picture;
132
133         /* set once at the start */
134         int ibufx, ibufy;
135         int fontid;
136
137         /* saves passing args */
138         struct ImBuf *curframe_ibuf;
139
140         /* restarts player for file drop */
141         char dropped_file[FILE_MAX];
142 } PlayState;
143
144 /* for debugging */
145 #if 0
146 void print_ps(PlayState *ps)
147 {
148         printf("ps:\n");
149         printf("    direction=%d,\n", (int)ps->direction);
150         printf("    next=%d,\n", ps->next);
151         printf("    once=%d,\n", ps->once);
152         printf("    turbo=%d,\n", ps->turbo);
153         printf("    pingpong=%d,\n", ps->pingpong);
154         printf("    noskip=%d,\n", ps->noskip);
155         printf("    sstep=%d,\n", ps->sstep);
156         printf("    pause=%d,\n", ps->pause);
157         printf("    wait2=%d,\n", ps->wait2);
158         printf("    stopped=%d,\n", ps->stopped);
159         printf("    go=%d,\n\n", ps->go);
160         fflush(stdout);
161 }
162 #endif
163
164 /* global for window and events */
165 typedef enum eWS_Qual {
166         WS_QUAL_LSHIFT  = (1 << 0),
167         WS_QUAL_RSHIFT  = (1 << 1),
168         WS_QUAL_SHIFT   = (WS_QUAL_LSHIFT | WS_QUAL_RSHIFT),
169         WS_QUAL_LALT    = (1 << 2),
170         WS_QUAL_RALT    = (1 << 3),
171         WS_QUAL_ALT     = (WS_QUAL_LALT | WS_QUAL_RALT),
172         WS_QUAL_LCTRL   = (1 << 4),
173         WS_QUAL_RCTRL   = (1 << 5),
174         WS_QUAL_CTRL    = (WS_QUAL_LCTRL | WS_QUAL_RCTRL),
175         WS_QUAL_LMOUSE  = (1 << 16),
176         WS_QUAL_MMOUSE  = (1 << 17),
177         WS_QUAL_RMOUSE  = (1 << 18),
178         WS_QUAL_MOUSE   = (WS_QUAL_LMOUSE | WS_QUAL_MMOUSE | WS_QUAL_RMOUSE)
179 } eWS_Qual;
180
181 static struct WindowStateGlobal {
182         GHOST_SystemHandle ghost_system;
183         void *ghost_window;
184         Gwn_Context *gwn_context;
185
186         /* events */
187         eWS_Qual qual;
188 } g_WS = {NULL};
189
190 static void playanim_window_get_size(int *r_width, int *r_height)
191 {
192         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(g_WS.ghost_window);
193         *r_width = GHOST_GetWidthRectangle(bounds);
194         *r_height = GHOST_GetHeightRectangle(bounds);
195         GHOST_DisposeRectangle(bounds);
196 }
197
198 static void playanim_gl_matrix(void)
199 {
200         /* unified matrix, note it affects offset for drawing */
201         /* note! cannot use gpuOrtho2D here because shader ignores. */
202         gpuOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0, 1.0f);
203 }
204
205 /* implementation */
206 static void playanim_event_qual_update(void)
207 {
208         int val;
209
210         /* Shift */
211         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftShift, &val);
212         if (val) g_WS.qual |=  WS_QUAL_LSHIFT;
213         else     g_WS.qual &= ~WS_QUAL_LSHIFT;
214
215         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightShift, &val);
216         if (val) g_WS.qual |=  WS_QUAL_RSHIFT;
217         else     g_WS.qual &= ~WS_QUAL_RSHIFT;
218
219         /* Control */
220         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftControl, &val);
221         if (val) g_WS.qual |=  WS_QUAL_LCTRL;
222         else     g_WS.qual &= ~WS_QUAL_LCTRL;
223
224         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightControl, &val);
225         if (val) g_WS.qual |=  WS_QUAL_RCTRL;
226         else     g_WS.qual &= ~WS_QUAL_RCTRL;
227
228         /* Alt */
229         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftAlt, &val);
230         if (val) g_WS.qual |=  WS_QUAL_LALT;
231         else     g_WS.qual &= ~WS_QUAL_LALT;
232
233         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightAlt, &val);
234         if (val) g_WS.qual |=  WS_QUAL_RALT;
235         else     g_WS.qual &= ~WS_QUAL_RALT;
236 }
237
238 typedef struct PlayAnimPict {
239         struct PlayAnimPict *next, *prev;
240         char *mem;
241         int size;
242         const char *name;
243         struct ImBuf *ibuf;
244         struct anim *anim;
245         int frame;
246         int IB_flags;
247 } PlayAnimPict;
248
249 static struct ListBase picsbase = {NULL, NULL};
250 /* frames in memory - store them here to for easy deallocation later */
251 static bool fromdisk = false;
252 static double ptottime = 0.0, swaptime = 0.04;
253 #ifdef WITH_AUDASPACE
254 static double fps_movie;
255 #endif
256
257 #ifdef USE_FRAME_CACHE_LIMIT
258 static struct ListBase inmempicsbase = {NULL, NULL};
259 static int added_images = 0;
260 #endif
261
262 static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
263 {
264         if (step > 0) {
265                 while (step-- && playanim) {
266                         playanim = playanim->next;
267                 }
268         }
269         else if (step < 0) {
270                 while (step++ && playanim) {
271                         playanim = playanim->prev;
272                 }
273         }
274         return playanim;
275 }
276
277 static int pupdate_time(void)
278 {
279         static double ltime;
280         double time;
281
282         time = PIL_check_seconds_timer();
283
284         ptottime += (time - ltime);
285         ltime = time;
286         return (ptottime < 0);
287 }
288
289 static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
290 {
291         float offs_x, offs_y;
292         float span_x, span_y;
293
294         if (ibuf == NULL) {
295                 printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
296                 return;
297         }
298         if (ibuf->rect == NULL && ibuf->rect_float) {
299                 IMB_rect_from_float(ibuf);
300                 imb_freerectfloatImBuf(ibuf);
301         }
302         if (ibuf->rect == NULL)
303                 return;
304
305         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
306
307         /* size within window */
308         span_x = (ps->zoom * ibuf->x) / (float)ps->win_x;
309         span_y = (ps->zoom * ibuf->y) / (float)ps->win_y;
310
311         /* offset within window */
312         offs_x = 0.5f * (1.0f - span_x);
313         offs_y = 0.5f * (1.0f - span_y);
314
315         CLAMP(offs_x, 0.0f, 1.0f);
316         CLAMP(offs_y, 0.0f, 1.0f);
317
318         glClearColor(0.1, 0.1, 0.1, 0.0);
319         glClear(GL_COLOR_BUFFER_BIT);
320
321         /* checkerboard for case alpha */
322         if (ibuf->planes == 32) {
323                 glEnable(GL_BLEND);
324                 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
325
326                 imm_draw_box_checker_2d(offs_x, offs_y, offs_x + span_x, offs_y + span_y);
327         }
328
329         IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
330
331         immDrawPixelsTex(
332                 &state,
333                 offs_x + (ps->draw_flip[0] ? span_x : 0.0f),
334                 offs_y + (ps->draw_flip[1] ? span_y : 0.0f),
335                 ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST,
336                 ibuf->rect,
337                 ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x),
338                 ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
339                 NULL);
340
341         glDisable(GL_BLEND);
342
343         pupdate_time();
344
345         if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) {
346                 int sizex, sizey;
347                 float fsizex_inv, fsizey_inv;
348                 char str[32 + FILE_MAX];
349                 BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
350
351                 playanim_window_get_size(&sizex, &sizey);
352                 fsizex_inv = 1.0f / sizex;
353                 fsizey_inv = 1.0f / sizey;
354
355                 BLF_color4f(fontid, 1.0, 1.0, 1.0, 1.0);
356                 BLF_enable(fontid, BLF_ASPECT);
357                 BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f);
358                 BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f);
359                 BLF_draw(fontid, str, sizeof(str));
360         }
361
362         if (ps->indicator) {
363                 float fac = ps->picture->frame / (double)(((PlayAnimPict *)picsbase.last)->frame - ((PlayAnimPict *)picsbase.first)->frame);
364
365                 fac = 2.0f * fac - 1.0f;
366                 gpuPushProjectionMatrix();
367                 gpuLoadIdentityProjectionMatrix();
368                 gpuPushMatrix();
369                 gpuLoadIdentity();
370
371                 unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
372
373                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
374                 immUniformColor3ub(0, 255, 0);
375
376                 immBegin(GWN_PRIM_LINES, 2);
377                 immVertex2f(pos, fac, -1.0f);
378                 immVertex2f(pos, fac,  1.0f);
379                 immEnd();
380
381                 immUnbindProgram();
382
383                 gpuPopMatrix();
384                 gpuPopProjectionMatrix();
385         }
386
387         GHOST_SwapWindowBuffers(g_WS.ghost_window);
388 }
389
390 static void build_pict_list_ex(PlayState *ps, const char *first, int totframes, int fstep, int fontid)
391 {
392         char *mem, filepath[FILE_MAX];
393 //      short val;
394         PlayAnimPict *picture = NULL;
395         struct ImBuf *ibuf = NULL;
396         struct anim *anim;
397
398         if (IMB_isanim(first)) {
399                 /* OCIO_TODO: support different input color space */
400                 anim = IMB_open_anim(first, IB_rect, 0, NULL);
401                 if (anim) {
402                         int pic;
403                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
404                         if (ibuf) {
405                                 playanim_toscreen(ps, NULL, ibuf, fontid, fstep);
406                                 IMB_freeImBuf(ibuf);
407                         }
408
409                         for (pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
410                                 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "Pict");
411                                 picture->anim = anim;
412                                 picture->frame = pic;
413                                 picture->IB_flags = IB_rect;
414                                 picture->name = BLI_sprintfN("%s : %4.d", first, pic + 1);
415                                 BLI_addtail(&picsbase, picture);
416                         }
417                 }
418                 else {
419                         printf("couldn't open anim %s\n", first);
420                 }
421         }
422         else {
423                 int count = 0;
424
425                 int fp_framenr;
426                 struct {
427                         char head[FILE_MAX], tail[FILE_MAX];
428                         unsigned short digits;
429                 } fp_decoded;
430
431                 BLI_strncpy(filepath, first, sizeof(filepath));
432                 fp_framenr = BLI_stringdec(filepath, fp_decoded.head, fp_decoded.tail, &fp_decoded.digits);
433
434                 pupdate_time();
435                 ptottime = 1.0;
436
437                 /* O_DIRECT
438                  *
439                  * If set, all reads and writes on the resulting file descriptor will
440                  * be performed directly to or from the user program buffer, provided
441                  * appropriate size and alignment restrictions are met.  Refer to the
442                  * F_SETFL and F_DIOINFO commands in the fcntl(2) manual entry for
443                  * information about how to determine the alignment constraints.
444                  * O_DIRECT is a Silicon Graphics extension and is only supported on
445                  * local EFS and XFS file systems.
446                  */
447
448                 while (IMB_ispic(filepath) && totframes) {
449                         bool hasevent;
450                         size_t size;
451                         int file;
452
453                         file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
454                         if (file < 0) {
455                                 /* print errno? */
456                                 return;
457                         }
458
459                         picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "picture");
460                         if (picture == NULL) {
461                                 printf("Not enough memory for pict struct '%s'\n", filepath);
462                                 close(file);
463                                 return;
464                         }
465                         size = BLI_file_descriptor_size(file);
466
467                         if (size < 1) {
468                                 close(file);
469                                 MEM_freeN(picture);
470                                 return;
471                         }
472
473                         picture->size = size;
474                         picture->IB_flags = IB_rect;
475
476                         if (fromdisk == false) {
477                                 mem = (char *)MEM_mallocN(size, "build pic list");
478                                 if (mem == NULL) {
479                                         printf("Couldn't get memory\n");
480                                         close(file);
481                                         MEM_freeN(picture);
482                                         return;
483                                 }
484
485                                 if (read(file, mem, size) != size) {
486                                         printf("Error while reading %s\n", filepath);
487                                         close(file);
488                                         MEM_freeN(picture);
489                                         MEM_freeN(mem);
490                                         return;
491                                 }
492                         }
493                         else {
494                                 mem = NULL;
495                         }
496
497                         picture->mem = mem;
498                         picture->name = BLI_strdup(filepath);
499                         picture->frame = count;
500                         close(file);
501                         BLI_addtail(&picsbase, picture);
502                         count++;
503
504                         pupdate_time();
505
506                         if (ptottime > 1.0) {
507                                 /* OCIO_TODO: support different input color space */
508                                 if (picture->mem) {
509                                         ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size,
510                                                                      picture->IB_flags, NULL, picture->name);
511                                 }
512                                 else {
513                                         ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
514                                 }
515                                 if (ibuf) {
516                                         playanim_toscreen(ps, picture, ibuf, fontid, fstep);
517                                         IMB_freeImBuf(ibuf);
518                                 }
519                                 pupdate_time();
520                                 ptottime = 0.0;
521                         }
522
523                         /* create a new filepath each time */
524                         fp_framenr += fstep;
525                         BLI_stringenc(filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
526
527                         while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
528                                 if (hasevent) {
529                                         GHOST_DispatchEvents(g_WS.ghost_system);
530                                 }
531                                 if (ps->loading == false) {
532                                         return;
533                                 }
534                         }
535
536                         totframes--;
537                 }
538         }
539         return;
540 }
541
542 static void build_pict_list(PlayState *ps, const char *first, int totframes, int fstep, int fontid)
543 {
544         ps->loading = true;
545         build_pict_list_ex(ps, first, totframes, fstep, fontid);
546         ps->loading = false;
547 }
548
549 static void update_sound_fps(void)
550 {
551 #ifdef WITH_AUDASPACE
552         if (playback_handle) {
553                 /* swaptime stores the 1.0/fps ratio */
554                 double speed = 1.0 / (swaptime * fps_movie);
555
556                 AUD_Handle_setPitch(playback_handle, speed);
557         }
558 #endif
559 }
560
561 static void change_frame(PlayState *ps, int cx)
562 {
563         int sizex, sizey;
564         int i, i_last;
565
566         if (BLI_listbase_is_empty(&picsbase)) {
567                 return;
568         }
569
570         playanim_window_get_size(&sizex, &sizey);
571         i_last = ((struct PlayAnimPict *)picsbase.last)->frame;
572         i = (i_last * cx) / sizex;
573         CLAMP(i, 0, i_last);
574
575 #ifdef WITH_AUDASPACE
576         if (scrub_handle) {
577                 AUD_Handle_stop(scrub_handle);
578                 scrub_handle = NULL;
579         }
580
581         if (playback_handle) {
582                 AUD_Status status = AUD_Handle_getStatus(playback_handle);
583                 if (status != AUD_STATUS_PLAYING) {
584                         AUD_Handle_stop(playback_handle);
585                         playback_handle = AUD_Device_play(audio_device, source, 1);
586                         if (playback_handle) {
587                                 AUD_Handle_setPosition(playback_handle, i / fps_movie);
588                                 scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie);
589                         }
590                         update_sound_fps();
591                 }
592                 else {
593                         AUD_Handle_setPosition(playback_handle, i / fps_movie);
594                         scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie);
595                 }
596         }
597         else if (source) {
598                 playback_handle = AUD_Device_play(audio_device, source, 1);
599                 if (playback_handle) {
600                         AUD_Handle_setPosition(playback_handle, i / fps_movie);
601                         scrub_handle = AUD_pauseAfter(playback_handle, 1 / fps_movie);
602                 }
603                 update_sound_fps();
604         }
605 #endif
606
607         ps->picture = BLI_findlink(&picsbase, i);
608         BLI_assert(ps->picture != NULL);
609
610         ps->sstep = true;
611         ps->wait2 = false;
612         ps->next_frame = 0;
613 }
614
615 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
616 {
617         PlayState *ps = (PlayState *)ps_void;
618         GHOST_TEventType type = GHOST_GetEventType(evt);
619         int val;
620
621         // print_ps(ps);
622
623         playanim_event_qual_update();
624
625         /* convert ghost event into value keyboard or mouse */
626         val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
627
628         /* first check if we're busy loading files */
629         if (ps->loading) {
630                 switch (type) {
631                         case GHOST_kEventKeyDown:
632                         case GHOST_kEventKeyUp:
633                         {
634                                 GHOST_TEventKeyData *key_data;
635
636                                 key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
637                                 switch (key_data->key) {
638                                         case GHOST_kKeyEsc:
639                                                 ps->loading = false;
640                                                 break;
641                                         default:
642                                                 break;
643                                 }
644                                 break;
645                         }
646                         default:
647                                 break;
648                 }
649                 return 1;
650         }
651
652
653         if (ps->wait2 && ps->stopped) {
654                 ps->stopped = false;
655         }
656
657         if (ps->wait2) {
658                 pupdate_time();
659                 ptottime = 0;
660         }
661
662         switch (type) {
663                 case GHOST_kEventKeyDown:
664                 case GHOST_kEventKeyUp:
665                 {
666                         GHOST_TEventKeyData *key_data;
667
668                         key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
669                         switch (key_data->key) {
670                                 case GHOST_kKeyA:
671                                         if (val) ps->noskip = !ps->noskip;
672                                         break;
673                                 case GHOST_kKeyI:
674                                         if (val) ps->indicator = !ps->indicator;
675                                         break;
676                                 case GHOST_kKeyP:
677                                         if (val) ps->pingpong = !ps->pingpong;
678                                         break;
679                                 case GHOST_kKeyF:
680                                 {
681                                         if (val) {
682                                                 int axis = (g_WS.qual & WS_QUAL_SHIFT) ? 1 : 0;
683                                                 ps->draw_flip[axis] = !ps->draw_flip[axis];
684                                         }
685                                         break;
686                                 }
687                                 case GHOST_kKey1:
688                                 case GHOST_kKeyNumpad1:
689                                         if (val) {
690                                                 swaptime = ps->fstep / 60.0;
691                                                 update_sound_fps();
692                                         }
693                                         break;
694                                 case GHOST_kKey2:
695                                 case GHOST_kKeyNumpad2:
696                                         if (val) {
697                                                 swaptime = ps->fstep / 50.0;
698                                                 update_sound_fps();
699                                         }
700                                         break;
701                                 case GHOST_kKey3:
702                                 case GHOST_kKeyNumpad3:
703                                         if (val) {
704                                                 swaptime = ps->fstep / 30.0;
705                                                 update_sound_fps();
706                                         }
707                                         break;
708                                 case GHOST_kKey4:
709                                 case GHOST_kKeyNumpad4:
710                                         if (g_WS.qual & WS_QUAL_SHIFT) {
711                                                 swaptime = ps->fstep / 24.0;
712                                                 update_sound_fps();
713                                         }
714                                         else {
715                                                 swaptime = ps->fstep / 25.0;
716                                                 update_sound_fps();
717                                         }
718                                         break;
719                                 case GHOST_kKey5:
720                                 case GHOST_kKeyNumpad5:
721                                         if (val) {
722                                                 swaptime = ps->fstep / 20.0;
723                                                 update_sound_fps();
724                                         }
725                                         break;
726                                 case GHOST_kKey6:
727                                 case GHOST_kKeyNumpad6:
728                                         if (val) {
729                                                 swaptime = ps->fstep / 15.0;
730                                                 update_sound_fps();
731                                         }
732                                         break;
733                                 case GHOST_kKey7:
734                                 case GHOST_kKeyNumpad7:
735                                         if (val) {
736                                                 swaptime = ps->fstep / 12.0;
737                                                 update_sound_fps();
738                                         }
739                                         break;
740                                 case GHOST_kKey8:
741                                 case GHOST_kKeyNumpad8:
742                                         if (val) {
743                                                 swaptime = ps->fstep / 10.0;
744                                                 update_sound_fps();
745                                         }
746                                         break;
747                                 case GHOST_kKey9:
748                                 case GHOST_kKeyNumpad9:
749                                         if (val) {
750                                                 swaptime = ps->fstep / 6.0;
751                                                 update_sound_fps();
752                                         }
753                                         break;
754                                 case GHOST_kKeyLeftArrow:
755                                         if (val) {
756                                                 ps->sstep = true;
757                                                 ps->wait2 = false;
758                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
759                                                         ps->picture = picsbase.first;
760                                                         ps->next_frame = 0;
761                                                 }
762                                                 else {
763                                                         ps->next_frame = -1;
764                                                 }
765                                         }
766                                         break;
767                                 case GHOST_kKeyDownArrow:
768                                         if (val) {
769                                                 ps->wait2 = false;
770                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
771                                                         ps->next_frame = ps->direction = -1;
772                                                 }
773                                                 else {
774                                                         ps->next_frame = -10;
775                                                         ps->sstep = true;
776                                                 }
777                                         }
778                                         break;
779                                 case GHOST_kKeyRightArrow:
780                                         if (val) {
781                                                 ps->sstep = true;
782                                                 ps->wait2 = false;
783                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
784                                                         ps->picture = picsbase.last;
785                                                         ps->next_frame = 0;
786                                                 }
787                                                 else {
788                                                         ps->next_frame = 1;
789                                                 }
790                                         }
791                                         break;
792                                 case GHOST_kKeyUpArrow:
793                                         if (val) {
794                                                 ps->wait2 = false;
795                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
796                                                         ps->next_frame = ps->direction = 1;
797                                                 }
798                                                 else {
799                                                         ps->next_frame = 10;
800                                                         ps->sstep = true;
801                                                 }
802                                         }
803                                         break;
804
805                                 case GHOST_kKeySlash:
806                                 case GHOST_kKeyNumpadSlash:
807                                         if (val) {
808                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
809                                                         if (ps->curframe_ibuf)
810                                                                 printf(" Name: %s | Speed: %.2f frames/s\n",
811                                                                        ps->curframe_ibuf->name, ps->fstep / swaptime);
812                                                 }
813                                                 else {
814                                                         swaptime = ps->fstep / 5.0;
815                                                         update_sound_fps();
816                                                 }
817                                         }
818                                         break;
819                                 case GHOST_kKey0:
820                                 case GHOST_kKeyNumpad0:
821                                         if (val) {
822                                                 if (ps->once) {
823                                                         ps->once = ps->wait2 = false;
824                                                 }
825                                                 else {
826                                                         ps->picture = NULL;
827                                                         ps->once = true;
828                                                         ps->wait2 = false;
829                                                 }
830                                         }
831                                         break;
832
833                                 case GHOST_kKeySpace:
834                                         if (val) {
835                                                 if (ps->wait2 || ps->sstep) {
836                                                         ps->wait2 = ps->sstep = false;
837 #ifdef WITH_AUDASPACE
838                                                         {
839                                                                 PlayAnimPict *picture = picsbase.first;
840                                                                 /* TODO - store in ps direct? */
841                                                                 int i = 0;
842
843                                                                 while (picture && picture != ps->picture) {
844                                                                         i++;
845                                                                         picture = picture->next;
846                                                                 }
847                                                                 if (playback_handle)
848                                                                         AUD_Handle_stop(playback_handle);
849                                                                 playback_handle = AUD_Device_play(audio_device, source, 1);
850                                                                 if (playback_handle)
851                                                                         AUD_Handle_setPosition(playback_handle, i / fps_movie);
852                                                                 update_sound_fps();
853                                                         }
854 #endif
855                                                 }
856                                                 else {
857                                                         ps->sstep = true;
858                                                         ps->wait2 = true;
859 #ifdef WITH_AUDASPACE
860                                                         if (playback_handle) {
861                                                                 AUD_Handle_stop(playback_handle);
862                                                                 playback_handle = NULL;
863                                                         }
864 #endif
865                                                 }
866                                         }
867                                         break;
868                                 case GHOST_kKeyEnter:
869                                 case GHOST_kKeyNumpadEnter:
870                                         if (val) {
871                                                 ps->wait2 = ps->sstep = false;
872 #ifdef WITH_AUDASPACE
873                                                 {
874                                                         PlayAnimPict *picture = picsbase.first;
875                                                         /* TODO - store in ps direct? */
876                                                         int i = 0;
877                                                         while (picture && picture != ps->picture) {
878                                                                 i++;
879                                                                 picture = picture->next;
880                                                         }
881                                                         if (playback_handle)
882                                                                 AUD_Handle_stop(playback_handle);
883                                                         playback_handle = AUD_Device_play(audio_device, source, 1);
884                                                         if (playback_handle)
885                                                                 AUD_Handle_setPosition(playback_handle, i / fps_movie);
886                                                         update_sound_fps();
887                                                 }
888 #endif
889                                         }
890                                         break;
891                                 case GHOST_kKeyPeriod:
892                                 case GHOST_kKeyNumpadPeriod:
893                                         if (val) {
894                                                 if (ps->sstep) {
895                                                         ps->wait2 = false;
896                                                 }
897                                                 else {
898                                                         ps->sstep = true;
899                                                         ps->wait2 = !ps->wait2;
900 #ifdef WITH_AUDASPACE
901                                                         if (playback_handle) {
902                                                                 AUD_Handle_stop(playback_handle);
903                                                                 playback_handle = NULL;
904                                                         }
905 #endif
906                                                 }
907                                         }
908                                         break;
909                                 case GHOST_kKeyEqual:
910                                 case GHOST_kKeyPlus:
911                                 case GHOST_kKeyNumpadPlus:
912                                 {
913                                         if (val == 0) break;
914                                         if (g_WS.qual & WS_QUAL_CTRL) {
915                                                 playanim_window_zoom(ps, 1.0f);
916                                         }
917                                         else {
918                                                 if (swaptime > ps->fstep / 60.0) {
919                                                         swaptime /= 1.1;
920                                                         update_sound_fps();
921                                                 }
922                                         }
923                                         break;
924                                 }
925                                 case GHOST_kKeyMinus:
926                                 case GHOST_kKeyNumpadMinus:
927                                 {
928                                         if (val == 0) break;
929                                         if (g_WS.qual & WS_QUAL_CTRL) {
930                                                 playanim_window_zoom(ps, -1.0f);
931                                         }
932                                         else {
933                                                 if (swaptime < ps->fstep / 5.0) {
934                                                         swaptime *= 1.1;
935                                                         update_sound_fps();
936                                                 }
937                                         }
938                                         break;
939                                 }
940                                 case GHOST_kKeyEsc:
941                                         ps->go = false;
942                                         break;
943                                 default:
944                                         break;
945                         }
946                         break;
947                 }
948                 case GHOST_kEventButtonDown:
949                 case GHOST_kEventButtonUp:
950                 {
951                         GHOST_TEventButtonData *bd = GHOST_GetEventData(evt);
952                         int cx, cy, sizex, sizey, inside_window;
953
954                         GHOST_GetCursorPosition(g_WS.ghost_system, &cx, &cy);
955                         GHOST_ScreenToClient(g_WS.ghost_window, cx, cy, &cx, &cy);
956                         playanim_window_get_size(&sizex, &sizey);
957
958                         inside_window = (cx >= 0 && cx < sizex && cy >= 0 && cy <= sizey);
959
960                         if (bd->button == GHOST_kButtonMaskLeft) {
961                                 if (type == GHOST_kEventButtonDown) {
962                                         if (inside_window) {
963                                                 g_WS.qual |= WS_QUAL_LMOUSE;
964                                                 change_frame(ps, cx);
965                                         }
966                                 }
967                                 else
968                                         g_WS.qual &= ~WS_QUAL_LMOUSE;
969                         }
970                         else if (bd->button == GHOST_kButtonMaskMiddle) {
971                                 if (type == GHOST_kEventButtonDown) {
972                                         if (inside_window)
973                                                 g_WS.qual |= WS_QUAL_MMOUSE;
974                                 }
975                                 else
976                                         g_WS.qual &= ~WS_QUAL_MMOUSE;
977                         }
978                         else if (bd->button == GHOST_kButtonMaskRight) {
979                                 if (type == GHOST_kEventButtonDown) {
980                                         if (inside_window)
981                                                 g_WS.qual |= WS_QUAL_RMOUSE;
982                                 }
983                                 else
984                                         g_WS.qual &= ~WS_QUAL_RMOUSE;
985                         }
986                         break;
987                 }
988                 case GHOST_kEventCursorMove:
989                 {
990                         if (g_WS.qual & WS_QUAL_LMOUSE) {
991                                 GHOST_TEventCursorData *cd = GHOST_GetEventData(evt);
992                                 int cx, cy;
993
994                                 /* Ignore 'in-between' events, since they can make scrubbing lag.
995                                  *
996                                  * Ideally we would keep into the event queue and see if this is the last motion event.
997                                  * however the API currently doesn't support this. */
998                                 {
999                                         int x_test, y_test;
1000                                         GHOST_GetCursorPosition(g_WS.ghost_system, &x_test, &y_test);
1001                                         if (x_test != cd->x || y_test != cd->y) {
1002                                                 /* we're not the last event... skipping */
1003                                                 break;
1004                                         }
1005                                 }
1006
1007                                 GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy);
1008
1009                                 change_frame(ps, cx);
1010                         }
1011                         break;
1012                 }
1013                 case GHOST_kEventWindowActivate:
1014                 case GHOST_kEventWindowDeactivate:
1015                 {
1016                         g_WS.qual &= ~WS_QUAL_MOUSE;
1017                         break;
1018                 }
1019                 case GHOST_kEventWindowSize:
1020                 case GHOST_kEventWindowMove:
1021                 {
1022                         float zoomx, zoomy;
1023
1024                         playanim_window_get_size(&ps->win_x, &ps->win_y);
1025                         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
1026
1027                         zoomx = (float) ps->win_x / ps->ibufx;
1028                         zoomy = (float) ps->win_y / ps->ibufy;
1029
1030                         /* zoom always show entire image */
1031                         ps->zoom = MIN2(zoomx, zoomy);
1032
1033                         /* zoom steps of 2 for speed */
1034                         ps->zoom = floor(ps->zoom + 0.5f);
1035                         if (ps->zoom < 1.0f) ps->zoom = 1.0f;
1036
1037                         glViewport(0, 0, ps->win_x, ps->win_y);
1038                         glScissor(0, 0, ps->win_x, ps->win_y);
1039
1040                         playanim_gl_matrix();
1041
1042                         ptottime = 0.0;
1043                         playanim_toscreen(ps, ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
1044
1045                         break;
1046                 }
1047                 case GHOST_kEventQuit:
1048                 case GHOST_kEventWindowClose:
1049                 {
1050                         ps->go = false;
1051                         break;
1052                 }
1053                 case GHOST_kEventDraggingDropDone:
1054                 {
1055                         GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
1056
1057                         if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
1058                                 GHOST_TStringArray *stra = ddd->data;
1059                                 int a;
1060
1061                                 for (a = 0; a < stra->count; a++) {
1062                                         BLI_strncpy(ps->dropped_file, (char *)stra->strings[a], sizeof(ps->dropped_file));
1063                                         ps->go = false;
1064                                         printf("drop file %s\n", stra->strings[a]);
1065                                         break; /* only one drop element supported now */
1066                                 }
1067                         }
1068                         break;
1069                 }
1070                 default:
1071                         /* quiet warnings */
1072                         break;
1073         }
1074
1075         return 1;
1076 }
1077
1078 static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey)
1079 {
1080         GHOST_GLSettings glsettings = {0};
1081         GHOST_TUns32 scr_w, scr_h;
1082
1083         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &scr_w, &scr_h);
1084
1085         posy = (scr_h - posy - sizey);
1086
1087         g_WS.ghost_window = GHOST_CreateWindow(g_WS.ghost_system,
1088                                                title,
1089                                                posx, posy, sizex, sizey,
1090                                                /* could optionally start fullscreen */
1091                                                GHOST_kWindowStateNormal,
1092                                                GHOST_kDrawingContextTypeOpenGL,
1093                                                glsettings);
1094 }
1095
1096 static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
1097 {
1098         int sizex, sizey;
1099         /* int ofsx, ofsy; */ /* UNUSED */
1100
1101         if (ps->zoom + zoom_offset > 0.0f) ps->zoom += zoom_offset;
1102
1103         // playanim_window_get_position(&ofsx, &ofsy);
1104         playanim_window_get_size(&sizex, &sizey);
1105         /* ofsx += sizex / 2; */ /* UNUSED */
1106         /* ofsy += sizey / 2; */ /* UNUSED */
1107         sizex = ps->zoom * ps->ibufx;
1108         sizey = ps->zoom * ps->ibufy;
1109         /* ofsx -= sizex / 2; */ /* UNUSED */
1110         /* ofsy -= sizey / 2; */ /* UNUSED */
1111         // window_set_position(g_WS.ghost_window, sizex, sizey);
1112         GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
1113 }
1114
1115 /* return path for restart */
1116 static char *wm_main_playanim_intern(int argc, const char **argv)
1117 {
1118         struct ImBuf *ibuf = NULL;
1119         static char filepath[FILE_MAX]; /* abused to return dropped file path */
1120         GHOST_TUns32 maxwinx, maxwiny;
1121         int i;
1122         /* This was done to disambiguate the name for use under c++. */
1123         int start_x = 0, start_y = 0;
1124         int sfra = -1;
1125         int efra = -1;
1126         int totblock;
1127
1128         PlayState ps = {0};
1129
1130         /* ps.doubleb   = true;*/ /* UNUSED */
1131         ps.go        = true;
1132         ps.direction = true;
1133         ps.next_frame = 1;
1134         ps.once      = false;
1135         ps.turbo     = false;
1136         ps.pingpong  = false;
1137         ps.noskip    = false;
1138         ps.sstep     = false;
1139         ps.wait2     = false;
1140         ps.stopped   = false;
1141         ps.loading   = false;
1142         ps.picture   = NULL;
1143         ps.indicator = false;
1144         ps.dropped_file[0] = 0;
1145         ps.zoom      = 1.0f;
1146         /* resetmap = false */
1147         ps.draw_flip[0] = false;
1148         ps.draw_flip[1] = false;
1149
1150         ps.fstep     = 1;
1151
1152         ps.fontid = -1;
1153
1154         while (argc > 1) {
1155                 if (argv[1][0] == '-') {
1156                         switch (argv[1][1]) {
1157                                 case 'm':
1158                                         fromdisk = true;
1159                                         break;
1160                                 case 'p':
1161                                         if (argc > 3) {
1162                                                 start_x = atoi(argv[2]);
1163                                                 start_y = atoi(argv[3]);
1164                                                 argc -= 2;
1165                                                 argv += 2;
1166                                         }
1167                                         else {
1168                                                 printf("too few arguments for -p (need 2): skipping\n");
1169                                         }
1170                                         break;
1171                                 case 'f':
1172                                         if (argc > 3) {
1173                                                 double fps = atof(argv[2]);
1174                                                 double fps_base = atof(argv[3]);
1175                                                 if (fps == 0.0) {
1176                                                         fps = 1;
1177                                                         printf("invalid fps,"
1178                                                                "forcing 1\n");
1179                                                 }
1180                                                 swaptime = fps_base / fps;
1181                                                 argc -= 2;
1182                                                 argv += 2;
1183                                         }
1184                                         else {
1185                                                 printf("too few arguments for -f (need 2): skipping\n");
1186                                         }
1187                                         break;
1188                                 case 's':
1189                                         sfra = atoi(argv[2]);
1190                                         CLAMP(sfra, 1, MAXFRAME);
1191                                         argc--;
1192                                         argv++;
1193                                         break;
1194                                 case 'e':
1195                                         efra = atoi(argv[2]);
1196                                         CLAMP(efra, 1, MAXFRAME);
1197                                         argc--;
1198                                         argv++;
1199                                         break;
1200                                 case 'j':
1201                                         ps.fstep = atoi(argv[2]);
1202                                         CLAMP(ps.fstep, 1, MAXFRAME);
1203                                         swaptime *= ps.fstep;
1204                                         argc--;
1205                                         argv++;
1206                                         break;
1207                                 default:
1208                                         printf("unknown option '%c': skipping\n", argv[1][1]);
1209                                         break;
1210                         }
1211                         argc--;
1212                         argv++;
1213                 }
1214                 else {
1215                         break;
1216                 }
1217         }
1218
1219         if (argc > 1) {
1220                 BLI_strncpy(filepath, argv[1], sizeof(filepath));
1221         }
1222         else {
1223                 printf("%s: no filepath argument given\n", __func__);
1224                 exit(1);
1225         }
1226
1227         if (IMB_isanim(filepath)) {
1228                 /* OCIO_TODO: support different input color spaces */
1229                 struct anim *anim;
1230                 anim = IMB_open_anim(filepath, IB_rect, 0, NULL);
1231                 if (anim) {
1232                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
1233                         IMB_close_anim(anim);
1234                         anim = NULL;
1235                 }
1236         }
1237         else if (!IMB_ispic(filepath)) {
1238                 printf("%s: '%s' not an image file\n", __func__, filepath);
1239                 exit(1);
1240         }
1241
1242         if (ibuf == NULL) {
1243                 /* OCIO_TODO: support different input color space */
1244                 ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
1245         }
1246
1247         if (ibuf == NULL) {
1248                 printf("%s: '%s' couldn't open\n", __func__, filepath);
1249                 exit(1);
1250         }
1251
1252         {
1253
1254                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
1255
1256                 g_WS.ghost_system = GHOST_CreateSystem();
1257                 GHOST_AddEventConsumer(g_WS.ghost_system, consumer);
1258
1259                 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y);
1260
1261                 playanim_gl_matrix();
1262         }
1263
1264         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny);
1265
1266         //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
1267
1268         /* initialize OpenGL immediate mode */
1269         g_WS.gwn_context =  GWN_context_create();
1270         GPU_init();
1271
1272         /* initialize the font */
1273         BLF_init();
1274         ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
1275         BLF_size(ps.fontid, 11, 72);
1276
1277         ps.ibufx = ibuf->x;
1278         ps.ibufy = ibuf->y;
1279
1280         ps.win_x = ps.ibufx;
1281         ps.win_y = ps.ibufy;
1282
1283         if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
1284         if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
1285
1286
1287         glClearColor(0.1, 0.1, 0.1, 0.0);
1288         glClear(GL_COLOR_BUFFER_BIT);
1289
1290         GHOST_SwapWindowBuffers(g_WS.ghost_window);
1291
1292         if (sfra == -1 || efra == -1) {
1293                 /* one of the frames was invalid, just use all images */
1294                 sfra = 1;
1295                 efra = MAXFRAME;
1296         }
1297
1298         build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
1299
1300 #ifdef WITH_AUDASPACE
1301         source = AUD_Sound_file(filepath);
1302         {
1303                 struct anim *anim_movie = ((struct PlayAnimPict *)picsbase.first)->anim;
1304                 if (anim_movie) {
1305                         short frs_sec = 25;
1306                         float frs_sec_base = 1.0;
1307
1308                         IMB_anim_get_fps(anim_movie, &frs_sec, &frs_sec_base, true);
1309
1310                         fps_movie = (double) frs_sec / (double) frs_sec_base;
1311                         /* enforce same fps for movie as sound */
1312                         swaptime = ps.fstep / fps_movie;
1313                 }
1314         }
1315 #endif
1316
1317         for (i = 2; i < argc; i++) {
1318                 BLI_strncpy(filepath, argv[i], sizeof(filepath));
1319                 build_pict_list(&ps, filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
1320         }
1321
1322         IMB_freeImBuf(ibuf);
1323         ibuf = NULL;
1324
1325         pupdate_time();
1326         ptottime = 0;
1327
1328         /* newly added in 2.6x, without this images never get freed */
1329 #define USE_IMB_CACHE
1330
1331         while (ps.go) {
1332                 if (ps.pingpong)
1333                         ps.direction = -ps.direction;
1334
1335                 if (ps.direction == 1) {
1336                         ps.picture = picsbase.first;
1337                 }
1338                 else {
1339                         ps.picture = picsbase.last;
1340                 }
1341
1342                 if (ps.picture == NULL) {
1343                         printf("couldn't find pictures\n");
1344                         ps.go = false;
1345                 }
1346                 if (ps.pingpong) {
1347                         if (ps.direction == 1) {
1348                                 ps.picture = ps.picture->next;
1349                         }
1350                         else {
1351                                 ps.picture = ps.picture->prev;
1352                         }
1353                 }
1354                 if (ptottime > 0.0) ptottime = 0.0;
1355
1356 #ifdef WITH_AUDASPACE
1357                 if (playback_handle)
1358                         AUD_Handle_stop(playback_handle);
1359                 playback_handle = AUD_Device_play(audio_device, source, 1);
1360                 update_sound_fps();
1361 #endif
1362
1363                 while (ps.picture) {
1364                         int hasevent;
1365 #ifndef USE_IMB_CACHE
1366                         if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
1367 #endif
1368                         if (ps.picture->ibuf) {
1369                                 ibuf = ps.picture->ibuf;
1370                         }
1371                         else if (ps.picture->anim) {
1372                                 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
1373                         }
1374                         else if (ps.picture->mem) {
1375                                 /* use correct colorspace here */
1376                                 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
1377                                                              ps.picture->IB_flags, NULL, ps.picture->name);
1378                         }
1379                         else {
1380                                 /* use correct colorspace here */
1381                                 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
1382                         }
1383
1384                         if (ibuf) {
1385 #ifdef USE_FRAME_CACHE_LIMIT
1386                                 LinkData *node;
1387 #endif
1388
1389 #ifdef USE_IMB_CACHE
1390                                 ps.picture->ibuf = ibuf;
1391 #endif
1392
1393 #ifdef USE_FRAME_CACHE_LIMIT
1394                                 /* really basic memory conservation scheme. Keep frames in a fifo queue */
1395                                 node = inmempicsbase.last;
1396
1397                                 while (node && added_images > PLAY_FRAME_CACHE_MAX) {
1398                                         PlayAnimPict *pic = node->data;
1399
1400                                         if (pic->ibuf && pic->ibuf != ibuf) {
1401                                                 LinkData *node_tmp;
1402                                                 IMB_freeImBuf(pic->ibuf);
1403                                                 pic->ibuf = NULL;
1404                                                 node_tmp = node->prev;
1405                                                 BLI_freelinkN(&inmempicsbase, node);
1406                                                 added_images--;
1407                                                 node = node_tmp;
1408                                         }
1409                                         else {
1410                                                 node = node->prev;
1411                                         }
1412                                 }
1413
1414                                 BLI_addhead(&inmempicsbase, BLI_genericNodeN(ps.picture));
1415                                 added_images++;
1416 #endif  /* USE_FRAME_CACHE_LIMIT */
1417
1418                                 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
1419
1420                                 /* why only windows? (from 2.4x) - campbell */
1421 #ifdef _WIN32
1422                                 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
1423 #endif
1424
1425                                 while (pupdate_time()) PIL_sleep_ms(1);
1426                                 ptottime -= swaptime;
1427                                 playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep);
1428                         } /* else delete */
1429                         else {
1430                                 printf("error: can't play this image type\n");
1431                                 exit(0);
1432                         }
1433
1434                         if (ps.once) {
1435                                 if (ps.picture->next == NULL) {
1436                                         ps.wait2 = true;
1437                                 }
1438                                 else if (ps.picture->prev == NULL) {
1439                                         ps.wait2 = true;
1440                                 }
1441                         }
1442
1443                         ps.next_frame = ps.direction;
1444
1445                         while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, ps.wait2))) {
1446                                 if (hasevent) {
1447                                         GHOST_DispatchEvents(g_WS.ghost_system);
1448                                 }
1449                                 /* Note, this still draws for mousemoves on pause */
1450                                 if (ps.wait2) {
1451                                         if (hasevent) {
1452                                                 if (ibuf) {
1453                                                         while (pupdate_time()) PIL_sleep_ms(1);
1454                                                         ptottime -= swaptime;
1455                                                         playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep);
1456                                                 }
1457                                         }
1458                                 }
1459                                 if (ps.go == false) {
1460                                         break;
1461                                 }
1462                         }
1463
1464                         ps.wait2 = ps.sstep;
1465
1466                         if (ps.wait2 == false && ps.stopped == false) {
1467                                 ps.stopped = true;
1468                         }
1469
1470                         pupdate_time();
1471
1472                         if (ps.picture && ps.next_frame) {
1473                                 /* always at least set one step */
1474                                 while (ps.picture) {
1475                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1476
1477                                         if (ps.once && ps.picture != NULL) {
1478                                                 if (ps.picture->next == NULL) {
1479                                                         ps.wait2 = true;
1480                                                 }
1481                                                 else if (ps.picture->prev == NULL) {
1482                                                         ps.wait2 = true;
1483                                                 }
1484                                         }
1485
1486                                         if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1487                                         ptottime -= swaptime;
1488                                 }
1489                                 if (ps.picture == NULL && ps.sstep) {
1490                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1491                                 }
1492                         }
1493                         if (ps.go == false) {
1494                                 break;
1495                         }
1496                 }
1497         }
1498         while ((ps.picture = BLI_pophead(&picsbase))) {
1499                 if (ps.picture->anim) {
1500                         if ((ps.picture->next == NULL) ||
1501                             (ps.picture->next->anim != ps.picture->anim))
1502                         {
1503                                 IMB_close_anim(ps.picture->anim);
1504                         }
1505                 }
1506
1507                 if (ps.picture->ibuf) {
1508                         IMB_freeImBuf(ps.picture->ibuf);
1509                 }
1510                 if (ps.picture->mem) {
1511                         MEM_freeN(ps.picture->mem);
1512                 }
1513
1514                 MEM_freeN((void *)ps.picture->name);
1515                 MEM_freeN(ps.picture);
1516         }
1517
1518         /* cleanup */
1519 #ifndef USE_IMB_CACHE
1520         if (ibuf) IMB_freeImBuf(ibuf);
1521 #endif
1522
1523         BLI_freelistN(&picsbase);
1524         BLI_freelistN(&inmempicsbase);
1525         added_images = 0;
1526
1527 #ifdef WITH_AUDASPACE
1528         if (playback_handle) {
1529                 AUD_Handle_stop(playback_handle);
1530                 playback_handle = NULL;
1531         }
1532         if (scrub_handle) {
1533                 AUD_Handle_stop(scrub_handle);
1534                 scrub_handle = NULL;
1535         }
1536         AUD_Sound_free(source);
1537         source = NULL;
1538 #endif
1539         /* we still miss freeing a lot!,
1540          * but many areas could skip initialization too for anim play */
1541
1542         GPU_shader_free_builtin_shaders();
1543
1544         GPU_exit();
1545
1546         if (g_WS.gwn_context) {
1547                 GWN_context_active_set(g_WS.gwn_context);
1548                 GWN_context_discard(g_WS.gwn_context);
1549                 g_WS.gwn_context = NULL;
1550         }
1551
1552         BLF_exit();
1553
1554         GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
1555
1556         /* early exit, IMB and BKE should be exited only in end */
1557         if (ps.dropped_file[0]) {
1558                 BLI_strncpy(filepath, ps.dropped_file, sizeof(filepath));
1559                 return filepath;
1560         }
1561
1562         IMB_exit();
1563         BKE_images_exit();
1564         DEG_free_node_types();
1565
1566         totblock = MEM_get_memory_blocks_in_use();
1567         if (totblock != 0) {
1568                 /* prints many bAKey, bArgument's which are tricky to fix */
1569 #if 0
1570                 printf("Error Totblock: %d\n", totblock);
1571                 MEM_printmemlist();
1572 #endif
1573         }
1574
1575         return NULL;
1576 }
1577
1578
1579 void WM_main_playanim(int argc, const char **argv)
1580 {
1581         const char *argv_next[2];
1582         bool looping = true;
1583
1584 #ifdef WITH_AUDASPACE
1585         {
1586                 AUD_DeviceSpecs specs;
1587
1588                 specs.rate = AUD_RATE_48000;
1589                 specs.format = AUD_FORMAT_S16;
1590                 specs.channels = AUD_CHANNELS_STEREO;
1591
1592                 AUD_initOnce();
1593
1594                 if (!(audio_device = AUD_init("OpenAL", specs, 1024, "Blender"))) {
1595                         audio_device = AUD_init("Null", specs, 0, "Blender");
1596                 }
1597         }
1598 #endif
1599
1600         while (looping) {
1601                 const char *filepath = wm_main_playanim_intern(argc, argv);
1602
1603                 if (filepath) { /* use simple args */
1604                         argv_next[0] = argv[0];
1605                         argv_next[1] = filepath;
1606                         argc = 2;
1607
1608                         /* continue with new args */
1609                         argv = argv_next;
1610                 }
1611                 else {
1612                         looping = false;
1613                 }
1614         }
1615
1616 #ifdef WITH_AUDASPACE
1617         AUD_exit(audio_device);
1618         AUD_exitOnce();
1619 #endif
1620 }