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