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