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