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