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