Draw a checker board as a background for animation player
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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_fileops.h"
53 #include "BLI_listbase.h"
54 #include "BLI_path_util.h"
55 #include "BLI_rect.h"
56 #include "BLI_string.h"
57 #include "BLI_utildefines.h"
58
59 #include "IMB_imbuf_types.h"
60 #include "IMB_imbuf.h"
61
62 #include "BKE_blender.h"
63 #include "BKE_global.h"
64 #include "BKE_image.h"
65
66 #include "BIF_gl.h"
67 #include "BIF_glutil.h"
68
69 #include "DNA_scene_types.h"
70 #include "ED_datafiles.h" /* for fonts */
71 #include "GHOST_C-api.h"
72 #include "BLF_api.h"
73
74 #include "wm_event_types.h"
75
76 #include "WM_api.h"  /* only for WM_main_playanim */
77
78 struct PlayState;
79 static void playanim_window_zoom(const struct PlayState *ps, const float zoom_offset);
80
81 typedef struct PlayState {
82
83         /* playback state */
84         short direction;
85         short next_frame;
86         short once;
87         short turbo;
88         short pingpong;
89         short noskip;
90         short sstep;
91         short wait2;
92         short stopped;
93         short go;
94
95         int fstep;
96
97         /* current picture */
98         struct PlayAnimPict *picture;
99
100         /* set once at the start */
101         int ibufx, ibufy;
102         int fontid;
103
104         /* saves passing args */
105         struct ImBuf *curframe_ibuf;
106 } PlayState;
107
108 /* for debugging */
109 #if 0
110 void print_ps(PlayState *ps)
111 {
112         printf("ps:\n");
113         printf("    direction=%d,\n", (int)ps->direction);
114         printf("    next=%d,\n", ps->next);
115         printf("    once=%d,\n", ps->once);
116         printf("    turbo=%d,\n", ps->turbo);
117         printf("    pingpong=%d,\n", ps->pingpong);
118         printf("    noskip=%d,\n", ps->noskip);
119         printf("    sstep=%d,\n", ps->sstep);
120         printf("    pause=%d,\n", ps->pause);
121         printf("    wait2=%d,\n", ps->wait2);
122         printf("    stopped=%d,\n", ps->stopped);
123         printf("    go=%d,\n\n", ps->go);
124         fflush(stdout);
125 }
126 #endif
127
128 /* global for window and events */
129 typedef enum eWS_Qual {
130         WS_QUAL_LSHIFT  = (1 << 0),
131         WS_QUAL_RSHIFT  = (1 << 1),
132         WS_QUAL_SHIFT   = (WS_QUAL_LSHIFT | WS_QUAL_RSHIFT),
133         WS_QUAL_LALT    = (1 << 2),
134         WS_QUAL_RALT    = (1 << 3),
135         WS_QUAL_ALT     = (WS_QUAL_LALT | WS_QUAL_RALT),
136         WS_QUAL_LCTRL   = (1 << 4),
137         WS_QUAL_RCTRL   = (1 << 5),
138         WS_QUAL_CTRL    = (WS_QUAL_LCTRL | WS_QUAL_RCTRL),
139         WS_QUAL_LMOUSE  = (1 << 16),
140         WS_QUAL_MMOUSE  = (1 << 17),
141         WS_QUAL_RMOUSE  = (1 << 18),
142         WS_QUAL_MOUSE   = (WS_QUAL_LMOUSE | WS_QUAL_MMOUSE | WS_QUAL_RMOUSE)
143 } eWS_Qual;
144
145 static struct WindowStateGlobal {
146         GHOST_SystemHandle ghost_system;
147         void *ghost_window;
148
149         /* events */
150         eWS_Qual qual;
151 } g_WS = {NULL};
152
153 static void playanim_window_get_size(int *width_r, int *height_r)
154 {
155         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(g_WS.ghost_window);
156         *width_r = GHOST_GetWidthRectangle(bounds);
157         *height_r = GHOST_GetHeightRectangle(bounds);
158         GHOST_DisposeRectangle(bounds);
159 }
160
161 /* implementation */
162 static void playanim_event_qual_update(void)
163 {
164         int val;
165
166         /* Shift */
167         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftShift, &val);
168         if (val) g_WS.qual |=  WS_QUAL_LSHIFT;
169         else     g_WS.qual &= ~WS_QUAL_LSHIFT;
170
171         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightShift, &val);
172         if (val) g_WS.qual |=  WS_QUAL_RSHIFT;
173         else     g_WS.qual &= ~WS_QUAL_RSHIFT;
174
175         /* Control */
176         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftControl, &val);
177         if (val) g_WS.qual |=  WS_QUAL_LCTRL;
178         else     g_WS.qual &= ~WS_QUAL_LCTRL;
179
180         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightControl, &val);
181         if (val) g_WS.qual |=  WS_QUAL_RCTRL;
182         else     g_WS.qual &= ~WS_QUAL_RCTRL;
183
184         /* Alt */
185         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftAlt, &val);
186         if (val) g_WS.qual |=  WS_QUAL_LALT;
187         else     g_WS.qual &= ~WS_QUAL_LALT;
188
189         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightAlt, &val);
190         if (val) g_WS.qual |=  WS_QUAL_RALT;
191         else     g_WS.qual &= ~WS_QUAL_RALT;
192 }
193
194 typedef struct PlayAnimPict {
195         struct PlayAnimPict *next, *prev;
196         char *mem;
197         int size;
198         char *name;
199         struct ImBuf *ibuf;
200         struct anim *anim;
201         int frame;
202         int IB_flags;
203 } PlayAnimPict;
204
205 static struct ListBase picsbase = {NULL, NULL};
206 static int fromdisk = FALSE;
207 static float zoomx = 1.0, zoomy = 1.0;
208 static double ptottime = 0.0, swaptime = 0.04;
209
210 static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
211 {
212         if (step > 0) {
213                 while (step-- && playanim) {
214                         playanim = playanim->next;
215                 }
216         }
217         else if (step < 0) {
218                 while (step++ && playanim) {
219                         playanim = playanim->prev;
220                 }
221         }
222         return playanim;
223 }
224
225 static int pupdate_time(void)
226 {
227         static double ltime;
228         double time;
229
230         time = PIL_check_seconds_timer();
231
232         ptottime += (time - ltime);
233         ltime = time;
234         return (ptottime < 0);
235 }
236
237 static void playanim_toscreen(PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
238 {
239
240         if (ibuf == NULL) {
241                 printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
242                 return;
243         }
244         if (ibuf->rect == NULL && ibuf->rect_float) {
245                 IMB_rect_from_float(ibuf);
246                 imb_freerectfloatImBuf(ibuf);
247         }
248         if (ibuf->rect == NULL)
249                 return;
250
251         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
252
253         glRasterPos2f(0.0f, 0.0f);
254
255         glEnable(GL_BLEND);
256         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
257
258         fdrawcheckerboard(0.0f, 0.0f, ibuf->x, ibuf->y);
259
260         glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
261
262         glDisable(GL_BLEND);
263
264         pupdate_time();
265
266         if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) {
267                 int sizex, sizey;
268                 float fsizex_inv, fsizey_inv;
269                 char str[32 + FILE_MAX];
270                 cpack(-1);
271                 BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
272
273                 playanim_window_get_size(&sizex, &sizey);
274                 fsizex_inv = 1.0f / sizex;
275                 fsizey_inv = 1.0f / sizey;
276
277                 BLF_enable(fontid, BLF_ASPECT);
278                 BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f);
279                 BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f);
280                 BLF_draw(fontid, str, sizeof(str));
281         }
282
283         GHOST_SwapWindowBuffers(g_WS.ghost_window);
284 }
285
286 static void build_pict_list(char *first, int totframes, int fstep, int fontid)
287 {
288         char *mem, filepath[FILE_MAX];
289 //      short val;
290         PlayAnimPict *picture = NULL;
291         struct ImBuf *ibuf = NULL;
292         char str[32 + FILE_MAX];
293         struct anim *anim;
294
295         if (IMB_isanim(first)) {
296                 /* OCIO_TODO: support different input color space */
297                 anim = IMB_open_anim(first, IB_rect, 0, NULL);
298                 if (anim) {
299                         int pic;
300                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
301                         if (ibuf) {
302                                 playanim_toscreen(NULL, ibuf, fontid, fstep);
303                                 IMB_freeImBuf(ibuf);
304                         }
305
306                         for (pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
307                                 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "Pict");
308                                 picture->anim = anim;
309                                 picture->frame = pic;
310                                 picture->IB_flags = IB_rect;
311                                 BLI_snprintf(str, sizeof(str), "%s : %4.d", first, pic + 1);
312                                 picture->name = strdup(str);
313                                 BLI_addtail(&picsbase, picture);
314                         }
315                 }
316                 else {
317                         printf("couldn't open anim %s\n", first);
318                 }
319         }
320         else {
321                 int count = 0;
322
323                 BLI_strncpy(filepath, first, sizeof(filepath));
324
325                 pupdate_time();
326                 ptottime = 1.0;
327
328                 /* O_DIRECT
329                  *
330                  * If set, all reads and writes on the resulting file descriptor will
331                  * be performed directly to or from the user program buffer, provided
332                  * appropriate size and alignment restrictions are met.  Refer to the
333                  * F_SETFL and F_DIOINFO commands in the fcntl(2) manual entry for
334                  * information about how to determine the alignment constraints.
335                  * O_DIRECT is a Silicon Graphics extension and is only supported on
336                  * local EFS and XFS file systems.
337                  */
338
339                 while (IMB_ispic(filepath) && totframes) {
340                         size_t size;
341                         int file;
342
343                         file = open(filepath, O_BINARY | O_RDONLY, 0);
344                         if (file < 0) {
345                                 /* print errno? */
346                                 return;
347                         }
348
349                         picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "picture");
350                         if (picture == NULL) {
351                                 printf("Not enough memory for pict struct '%s'\n", filepath);
352                                 close(file);
353                                 return;
354                         }
355                         size = BLI_file_descriptor_size(file);
356
357                         if (size < 1) {
358                                 close(file);
359                                 MEM_freeN(picture);
360                                 return;
361                         }
362
363                         picture->size = size;
364                         picture->IB_flags = IB_rect;
365
366                         if (fromdisk == FALSE) {
367                                 mem = (char *)MEM_mallocN(size, "build pic list");
368                                 if (mem == NULL) {
369                                         printf("Couldn't get memory\n");
370                                         close(file);
371                                         MEM_freeN(picture);
372                                         return;
373                                 }
374
375                                 if (read(file, mem, size) != size) {
376                                         printf("Error while reading %s\n", filepath);
377                                         close(file);
378                                         MEM_freeN(picture);
379                                         MEM_freeN(mem);
380                                         return;
381                                 }
382                         }
383                         else {
384                                 mem = NULL;
385                         }
386
387                         picture->mem = mem;
388                         picture->name = strdup(filepath);
389                         close(file);
390                         BLI_addtail(&picsbase, picture);
391                         count++;
392
393                         pupdate_time();
394
395                         if (ptottime > 1.0) {
396                                 /* OCIO_TODO: support different input color space */
397                                 if (picture->mem) {
398                                         ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size,
399                                                                      picture->IB_flags, NULL, picture->name);
400                                 }
401                                 else {
402                                         ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
403                                 }
404                                 if (ibuf) {
405                                         playanim_toscreen(picture, ibuf, fontid, fstep);
406                                         IMB_freeImBuf(ibuf);
407                                 }
408                                 pupdate_time();
409                                 ptottime = 0.0;
410                         }
411
412                         BLI_newname(filepath, +fstep);
413
414 #if 0 // XXX25
415                         while (qtest()) {
416                                 switch (qreadN(&val)) {
417                                         case ESCKEY:
418                                                 if (val) return;
419                                                 break;
420                                 }
421                         }
422 #endif
423                         totframes--;
424                 }
425         }
426         return;
427 }
428
429 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
430 {
431         PlayState *ps = (PlayState *)ps_void;
432         GHOST_TEventType type = GHOST_GetEventType(evt);
433         int val;
434
435         // print_ps(ps);
436
437         playanim_event_qual_update();
438
439         /* convert ghost event into value keyboard or mouse */
440         val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
441
442         if (ps->wait2 && ps->stopped) {
443                 ps->stopped = FALSE;
444         }
445
446         if (ps->wait2) {
447                 pupdate_time();
448                 ptottime = 0;
449         }
450
451         switch (type) {
452                 case GHOST_kEventKeyDown:
453                 case GHOST_kEventKeyUp:
454                 {
455                         GHOST_TEventKeyData *key_data;
456
457                         key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
458                         switch (key_data->key) {
459                                 case GHOST_kKeyA:
460                                         if (val) ps->noskip = !ps->noskip;
461                                         break;
462                                 case GHOST_kKeyP:
463                                         if (val) ps->pingpong = !ps->pingpong;
464                                         break;
465                                 case GHOST_kKey1:
466                                 case GHOST_kKeyNumpad1:
467                                         if (val) swaptime = ps->fstep / 60.0;
468                                         break;
469                                 case GHOST_kKey2:
470                                 case GHOST_kKeyNumpad2:
471                                         if (val) swaptime = ps->fstep / 50.0;
472                                         break;
473                                 case GHOST_kKey3:
474                                 case GHOST_kKeyNumpad3:
475                                         if (val) swaptime = ps->fstep / 30.0;
476                                         break;
477                                 case GHOST_kKey4:
478                                 case GHOST_kKeyNumpad4:
479                                         if (g_WS.qual & WS_QUAL_SHIFT)
480                                                 swaptime = ps->fstep / 24.0;
481                                         else
482                                                 swaptime = ps->fstep / 25.0;
483                                         break;
484                                 case GHOST_kKey5:
485                                 case GHOST_kKeyNumpad5:
486                                         if (val) swaptime = ps->fstep / 20.0;
487                                         break;
488                                 case GHOST_kKey6:
489                                 case GHOST_kKeyNumpad6:
490                                         if (val) swaptime = ps->fstep / 15.0;
491                                         break;
492                                 case GHOST_kKey7:
493                                 case GHOST_kKeyNumpad7:
494                                         if (val) swaptime = ps->fstep / 12.0;
495                                         break;
496                                 case GHOST_kKey8:
497                                 case GHOST_kKeyNumpad8:
498                                         if (val) swaptime = ps->fstep / 10.0;
499                                         break;
500                                 case GHOST_kKey9:
501                                 case GHOST_kKeyNumpad9:
502                                         if (val) swaptime = ps->fstep / 6.0;
503                                         break;
504                                 case GHOST_kKeyLeftArrow:
505                                         if (val) {
506                                                 ps->sstep = TRUE;
507                                                 ps->wait2 = FALSE;
508                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
509                                                         ps->picture = picsbase.first;
510                                                         ps->next_frame = 0;
511                                                 }
512                                                 else {
513                                                         ps->next_frame = -1;
514                                                 }
515                                         }
516                                         break;
517                                 case GHOST_kKeyDownArrow:
518                                         if (val) {
519                                                 ps->wait2 = FALSE;
520                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
521                                                         ps->next_frame = ps->direction = -1;
522                                                 }
523                                                 else {
524                                                         ps->next_frame = -10;
525                                                         ps->sstep = TRUE;
526                                                 }
527                                         }
528                                         break;
529                                 case GHOST_kKeyRightArrow:
530                                         if (val) {
531                                                 ps->sstep = TRUE;
532                                                 ps->wait2 = FALSE;
533                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
534                                                         ps->picture = picsbase.last;
535                                                         ps->next_frame = 0;
536                                                 }
537                                                 else {
538                                                         ps->next_frame = 1;
539                                                 }
540                                         }
541                                         break;
542                                 case GHOST_kKeyUpArrow:
543                                         if (val) {
544                                                 ps->wait2 = FALSE;
545                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
546                                                         ps->next_frame = ps->direction = 1;
547                                                 }
548                                                 else {
549                                                         ps->next_frame = 10;
550                                                         ps->sstep = TRUE;
551                                                 }
552                                         }
553                                         break;
554
555                                 case GHOST_kKeySlash:
556                                 case GHOST_kKeyNumpadSlash:
557                                         if (val) {
558                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
559                                                         if (ps->curframe_ibuf)
560                                                                 printf(" Name: %s | Speed: %.2f frames/s\n",
561                                                                        ps->curframe_ibuf->name, ps->fstep / swaptime);
562                                                 }
563                                                 else {
564                                                         swaptime = ps->fstep / 5.0;
565                                                 }
566                                         }
567                                         break;
568                                 case GHOST_kKey0:
569                                 case GHOST_kKeyNumpad0:
570                                         if (val) {
571                                                 if (ps->once) {
572                                                         ps->once = ps->wait2 = FALSE;
573                                                 }
574                                                 else {
575                                                         ps->picture = NULL;
576                                                         ps->once = TRUE;
577                                                         ps->wait2 = FALSE;
578                                                 }
579                                         }
580                                         break;
581                                 case GHOST_kKeyEnter:
582                                 case GHOST_kKeyNumpadEnter:
583                                         if (val) {
584                                                 ps->wait2 = ps->sstep = FALSE;
585                                         }
586                                         break;
587                                 case GHOST_kKeyPeriod:
588                                 case GHOST_kKeyNumpadPeriod:
589                                         if (val) {
590                                                 if (ps->sstep) ps->wait2 = FALSE;
591                                                 else {
592                                                         ps->sstep = TRUE;
593                                                         ps->wait2 = !ps->wait2;
594                                                 }
595                                         }
596                                         break;
597                                 case GHOST_kKeyEqual:
598                                 case GHOST_kKeyNumpadPlus:
599                                 {
600                                         if (val == 0) break;
601                                         if (g_WS.qual & WS_QUAL_CTRL) {
602                                                 playanim_window_zoom(ps, 1.0f);
603                                         }
604                                         else {
605                                                 swaptime /= 1.1;
606                                         }
607                                         break;
608                                 }
609                                 case GHOST_kKeyMinus:
610                                 case GHOST_kKeyNumpadMinus:
611                                 {
612                                         if (val == 0) break;
613                                         if (g_WS.qual & WS_QUAL_CTRL) {
614                                                 playanim_window_zoom(ps, -1.0f);
615                                         }
616                                         else {
617                                                 swaptime *= 1.1;
618                                         }
619                                         break;
620                                 }
621                                 case GHOST_kKeyEsc:
622                                         ps->go = FALSE;
623                                         break;
624                                 default:
625                                         break;
626                         }
627                         break;
628                 }
629                 case GHOST_kEventButtonDown:
630                 case GHOST_kEventButtonUp:
631                 {
632                         GHOST_TEventButtonData *bd = GHOST_GetEventData(evt);
633                         int cx, cy, sizex, sizey, inside_window;
634                         
635                         GHOST_GetCursorPosition(g_WS.ghost_system, &cx, &cy);
636                         GHOST_ScreenToClient(g_WS.ghost_window, cx, cy, &cx, &cy);
637                         playanim_window_get_size(&sizex, &sizey);
638
639                         inside_window = (cx >= 0 && cx < sizex && cy >= 0 && cy <= sizey);
640                         
641                         if (bd->button == GHOST_kButtonMaskLeft) {
642                                 if (type == GHOST_kEventButtonDown) {
643                                         if (inside_window)
644                                                 g_WS.qual |= WS_QUAL_LMOUSE;
645                                 }
646                                 else
647                                         g_WS.qual &= ~WS_QUAL_LMOUSE;
648                         }
649                         else if (bd->button == GHOST_kButtonMaskMiddle) {
650                                 if (type == GHOST_kEventButtonDown) {
651                                         if (inside_window)
652                                                 g_WS.qual |= WS_QUAL_MMOUSE;
653                                 }
654                                 else
655                                         g_WS.qual &= ~WS_QUAL_MMOUSE;
656                         }
657                         else if (bd->button == GHOST_kButtonMaskRight) {
658                                 if (type == GHOST_kEventButtonDown) {
659                                         if (inside_window)
660                                                 g_WS.qual |= WS_QUAL_RMOUSE;
661                                 }
662                                 else
663                                         g_WS.qual &= ~WS_QUAL_RMOUSE;
664                         }
665                         break;
666                 }
667                 case GHOST_kEventCursorMove:
668                 {
669                         if (g_WS.qual & WS_QUAL_LMOUSE) {
670                                 int sizex, sizey;
671                                 int i;
672
673                                 GHOST_TEventCursorData *cd = GHOST_GetEventData(evt);
674                                 int cx, cy;
675
676                                 GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy);
677
678                                 playanim_window_get_size(&sizex, &sizey);
679                                 ps->picture = picsbase.first;
680                                 /* TODO - store in ps direct? */
681                                 i = 0;
682                                 while (ps->picture) {
683                                         i++;
684                                         ps->picture = ps->picture->next;
685                                 }
686                                 i = (i * cx) / sizex;
687                                 ps->picture = picsbase.first;
688                                 for (; i > 0; i--) {
689                                         if (ps->picture->next == NULL) break;
690                                         ps->picture = ps->picture->next;
691                                 }
692                                 ps->sstep = TRUE;
693                                 ps->wait2 = FALSE;
694                                 ps->next_frame = 0;
695                         }
696                         break;
697                 }
698                 case GHOST_kEventWindowActivate:
699                 case GHOST_kEventWindowDeactivate:
700                 {
701                         g_WS.qual &= ~WS_QUAL_MOUSE;
702                         break;
703                 }
704                 case GHOST_kEventWindowSize:
705                 case GHOST_kEventWindowMove:
706                 {
707                         int sizex, sizey;
708
709                         playanim_window_get_size(&sizex, &sizey);
710                         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
711
712                         glViewport(0, 0, sizex, sizey);
713                         glScissor(0, 0, sizex, sizey);
714
715                         zoomx = (float) sizex / ps->ibufx;
716                         zoomy = (float) sizey / ps->ibufy;
717                         zoomx = floor(zoomx + 0.5f);
718                         zoomy = floor(zoomy + 0.5f);
719                         if (zoomx < 1.0f) zoomx = 1.0f;
720                         if (zoomy < 1.0f) zoomy = 1.0f;
721
722                         sizex = zoomx * ps->ibufx;
723                         sizey = zoomy * ps->ibufy;
724
725                         glPixelZoom(zoomx, zoomy);
726                         glEnable(GL_DITHER);
727                         ptottime = 0.0;
728                         playanim_toscreen(ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
729
730                         break;
731                 }
732                 case GHOST_kEventQuit:
733                 case GHOST_kEventWindowClose:
734                 {
735                         ps->go = FALSE;
736                         break;
737                 }
738                 default:
739                         /* quiet warnings */
740                         break;
741         }
742
743         return 1;
744 }
745
746 static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey, int start_maximized)
747 {
748         GHOST_TWindowState inital_state;
749         GHOST_TUns32 scr_w, scr_h;
750
751         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &scr_w, &scr_h);
752
753         posy = (scr_h - posy - sizey);
754
755         if (start_maximized == G_WINDOWSTATE_FULLSCREEN)
756                 inital_state = start_maximized ? GHOST_kWindowStateFullScreen : GHOST_kWindowStateNormal;
757         else
758                 inital_state = start_maximized ? GHOST_kWindowStateMaximized : GHOST_kWindowStateNormal;
759
760         g_WS.ghost_window = GHOST_CreateWindow(g_WS.ghost_system,
761                                                title,
762                                                posx, posy, sizex, sizey,
763                                                inital_state,
764                                                GHOST_kDrawingContextTypeOpenGL,
765                                                FALSE /* no stereo */, FALSE);
766 }
767
768 static void playanim_window_zoom(const PlayState *ps, const float zoom_offset)
769 {
770         int sizex, sizey;
771         /* int ofsx, ofsy; */ /* UNUSED */
772
773         if (zoomx + zoom_offset > 0.0f) zoomx += zoom_offset;
774         if (zoomy + zoom_offset > 0.0f) zoomy += zoom_offset;
775
776         // playanim_window_get_position(&ofsx, &ofsy);
777         playanim_window_get_size(&sizex, &sizey);
778         /* ofsx += sizex / 2; */ /* UNUSED */
779         /* ofsy += sizey / 2; */ /* UNUSED */
780         sizex = zoomx * ps->ibufx;
781         sizey = zoomy * ps->ibufy;
782         /* ofsx -= sizex / 2; */ /* UNUSED */
783         /* ofsy -= sizey / 2; */ /* UNUSED */
784         // window_set_position(g_WS.ghost_window,sizex,sizey);
785         GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
786 }
787
788 void WM_main_playanim(int argc, const char **argv)
789 {
790         struct ImBuf *ibuf = NULL;
791         char filepath[FILE_MAX];
792         GHOST_TUns32 maxwinx, maxwiny;
793         /* short c233 = FALSE, yuvx = FALSE; */ /* UNUSED */
794         int i;
795         /* This was done to disambiguate the name for use under c++. */
796         struct anim *anim = NULL;
797         int start_x = 0, start_y = 0;
798         int sfra = -1;
799         int efra = -1;
800         int totblock;
801
802         PlayState ps = {0};
803
804         /* ps.doubleb   = TRUE;*/ /* UNUSED */
805         ps.go        = TRUE;
806         ps.direction = TRUE;
807         ps.next_frame = 1;
808         ps.once      = FALSE;
809         ps.turbo     = FALSE;
810         ps.pingpong  = FALSE;
811         ps.noskip    = FALSE;
812         ps.sstep     = FALSE;
813         ps.wait2     = FALSE;
814         ps.stopped   = FALSE;
815         ps.picture   = NULL;
816         /* resetmap = FALSE */
817
818         ps.fstep     = 1;
819
820         ps.fontid = -1;
821
822         while (argc > 1) {
823                 if (argv[1][0] == '-') {
824                         switch (argv[1][1]) {
825                                 case 'm':
826                                         fromdisk = TRUE;
827                                         break;
828                                 case 'p':
829                                         if (argc > 3) {
830                                                 start_x = atoi(argv[2]);
831                                                 start_y = atoi(argv[3]);
832                                                 argc -= 2;
833                                                 argv += 2;
834                                         }
835                                         else {
836                                                 printf("too few arguments for -p (need 2): skipping\n");
837                                         }
838                                         break;
839                                 case 'f':
840                                         if (argc > 3) {
841                                                 double fps = atof(argv[2]);
842                                                 double fps_base = atof(argv[3]);
843                                                 if (fps == 0.0) {
844                                                         fps = 1;
845                                                         printf("invalid fps,"
846                                                                "forcing 1\n");
847                                                 }
848                                                 swaptime = fps_base / fps;
849                                                 argc -= 2;
850                                                 argv += 2;
851                                         }
852                                         else {
853                                                 printf("too few arguments for -f (need 2): skipping\n");
854                                         }
855                                         break;
856                                 case 's':
857                                         sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
858                                         argc--;
859                                         argv++;
860                                         break;
861                                 case 'e':
862                                         efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
863                                         argc--;
864                                         argv++;
865                                         break;
866                                 case 'j':
867                                         ps.fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2])));
868                                         swaptime *= ps.fstep;
869                                         argc--;
870                                         argv++;
871                                         break;
872                                 default:
873                                         printf("unknown option '%c': skipping\n", argv[1][1]);
874                                         break;
875                         }
876                         argc--;
877                         argv++;
878                 }
879                 else {
880                         break;
881                 }
882         }
883
884         if (argc > 1) {
885                 BLI_strncpy(filepath, argv[1], sizeof(filepath));
886         }
887         else {
888                 BLI_current_working_dir(filepath, sizeof(filepath));
889                 BLI_add_slash(filepath);
890         }
891
892         if (IMB_isanim(filepath)) {
893                 /* OCIO_TODO: support different input color spaces */
894                 anim = IMB_open_anim(filepath, IB_rect, 0, NULL);
895                 if (anim) {
896                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
897                         IMB_close_anim(anim);
898                         anim = NULL;
899                 }
900         }
901         else if (!IMB_ispic(filepath)) {
902                 exit(1);
903         }
904
905         if (ibuf == NULL) {
906                 /* OCIO_TODO: support different input color space */
907                 ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
908         }
909
910         if (ibuf == NULL) {
911                 printf("couldn't open %s\n", filepath);
912                 exit(1);
913         }
914
915 #if 0 //XXX25
916         #if !defined(WIN32) && !defined(__APPLE__)
917         if (fork()) exit(0);
918         #endif
919 #endif //XXX25
920
921         /* XXX, fixme zr */
922         {
923 //              extern void add_to_mainqueue(wmWindow *win, void *user_data, short evt, short val, char ascii);
924
925                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
926
927                 g_WS.ghost_system = GHOST_CreateSystem();
928                 GHOST_AddEventConsumer(g_WS.ghost_system, consumer);
929
930
931
932                 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y, 0);
933 //XXX25         window_set_handler(g_WS.ghost_window, add_to_mainqueue, NULL);
934
935                 glMatrixMode(GL_PROJECTION);
936                 glLoadIdentity();
937                 glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
938                 glMatrixMode(GL_MODELVIEW);
939         }
940
941         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny);
942
943         //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
944
945         /* initialize the font */
946         BLF_init(11, 72);
947         ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
948         BLF_size(ps.fontid, 11, 72);
949
950         ps.ibufx = ibuf->x;
951         ps.ibufy = ibuf->y;
952
953         if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
954         if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
955
956         glClearColor(0.0, 0.0, 0.0, 0.0);
957         glClear(GL_COLOR_BUFFER_BIT);
958
959         GHOST_SwapWindowBuffers(g_WS.ghost_window);
960
961         if (sfra == -1 || efra == -1) {
962                 /* one of the frames was invalid, just use all images */
963                 sfra = 1;
964                 efra = MAXFRAME;
965         }
966
967         build_pict_list(filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
968
969         for (i = 2; i < argc; i++) {
970                 BLI_strncpy(filepath, argv[i], sizeof(filepath));
971                 build_pict_list(filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
972         }
973
974         IMB_freeImBuf(ibuf);
975         ibuf = NULL;
976
977         pupdate_time();
978         ptottime = 0;
979
980         /* newly added in 2.6x, without this images never get freed */
981 #define USE_IMB_CACHE
982
983         while (ps.go) {
984                 if (ps.pingpong)
985                         ps.direction = -ps.direction;
986
987                 if (ps.direction == 1) {
988                         ps.picture = picsbase.first;
989                 }
990                 else {
991                         ps.picture = picsbase.last;
992                 }
993
994                 if (ps.picture == NULL) {
995                         printf("couldn't find pictures\n");
996                         ps.go = FALSE;
997                 }
998                 if (ps.pingpong) {
999                         if (ps.direction == 1) {
1000                                 ps.picture = ps.picture->next;
1001                         }
1002                         else {
1003                                 ps.picture = ps.picture->prev;
1004                         }
1005                 }
1006                 if (ptottime > 0.0) ptottime = 0.0;
1007
1008                 while (ps.picture) {
1009                         int hasevent;
1010 #ifndef USE_IMB_CACHE
1011                         if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
1012 #endif
1013                         if (ps.picture->ibuf) {
1014                                 ibuf = ps.picture->ibuf;
1015                         }
1016                         else if (ps.picture->anim) {
1017                                 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
1018                         }
1019                         else if (ps.picture->mem) {
1020                                 /* use correct colorspace here */
1021                                 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
1022                                                              ps.picture->IB_flags, NULL, ps.picture->name);
1023                         }
1024                         else {
1025                                 /* use correct colorspace here */
1026                                 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
1027                         }
1028
1029                         if (ibuf) {
1030
1031 #ifdef USE_IMB_CACHE
1032                                 ps.picture->ibuf = ibuf;
1033 #endif
1034
1035                                 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
1036
1037                                 /* why only windows? (from 2.4x) - campbell */
1038 #ifdef _WIN32
1039                                 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
1040 #endif
1041
1042                                 while (pupdate_time()) PIL_sleep_ms(1);
1043                                 ptottime -= swaptime;
1044                                 playanim_toscreen(ps.picture, ibuf, ps.fontid, ps.fstep);
1045                         } /* else deleten */
1046                         else {
1047                                 printf("error: can't play this image type\n");
1048                                 exit(0);
1049                         }
1050
1051                         if (ps.once) {
1052                                 if (ps.picture->next == NULL) {
1053                                         ps.wait2 = TRUE;
1054                                 }
1055                                 else if (ps.picture->prev == NULL) {
1056                                         ps.wait2 = TRUE;
1057                                 }
1058                         }
1059
1060                         ps.next_frame = ps.direction;
1061
1062
1063                         while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0) || ps.wait2 != 0)) {
1064                                 if (hasevent) {
1065                                         GHOST_DispatchEvents(g_WS.ghost_system);
1066                                 }
1067                                 if (ps.wait2) {
1068                                         if (hasevent) {
1069                                                 if (ibuf) {
1070                                                         while (pupdate_time()) PIL_sleep_ms(1);
1071                                                         ptottime -= swaptime;
1072                                                         playanim_toscreen(ps.picture, ibuf, ps.fontid, ps.fstep);
1073                                                 }
1074                                         }
1075                                 }
1076                                 if (!ps.go) {
1077                                         break;
1078                                 }
1079                         }
1080
1081                         ps.wait2 = ps.sstep;
1082
1083                         if (ps.wait2 == 0 && ps.stopped == 0) {
1084                                 ps.stopped = TRUE;
1085                         }
1086
1087                         pupdate_time();
1088
1089                         if (ps.picture && ps.next_frame) {
1090                                 /* always at least set one step */
1091                                 while (ps.picture) {
1092                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1093
1094                                         if (ps.once && ps.picture != NULL) {
1095                                                 if (ps.picture->next == NULL) {
1096                                                         ps.wait2 = TRUE;
1097                                                 }
1098                                                 else if (ps.picture->prev == NULL) {
1099                                                         ps.wait2 = TRUE;
1100                                                 }
1101                                         }
1102
1103                                         if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1104                                         ptottime -= swaptime;
1105                                 }
1106                                 if (ps.picture == NULL && ps.sstep) {
1107                                         ps.picture = playanim_step(ps.picture, ps.next_frame);
1108                                 }
1109                         }
1110                         if (ps.go == FALSE) {
1111                                 break;
1112                         }
1113                 }
1114         }
1115         ps.picture = picsbase.first;
1116         anim = NULL;
1117         while (ps.picture) {
1118                 if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) {
1119                         // to prevent divx crashes
1120                         anim = ps.picture->anim;
1121                         IMB_close_anim(anim);
1122                 }
1123
1124                 if (ps.picture->ibuf) {
1125                         IMB_freeImBuf(ps.picture->ibuf);
1126                 }
1127                 if (ps.picture->mem) {
1128                         MEM_freeN(ps.picture->mem);
1129                 }
1130
1131                 ps.picture = ps.picture->next;
1132         }
1133
1134         /* cleanup */
1135 #ifndef USE_IMB_CACHE
1136         if (ibuf) IMB_freeImBuf(ibuf);
1137 #endif
1138
1139         BLI_freelistN(&picsbase);
1140 #if 0 // XXX25
1141         free_blender();
1142 #else
1143         /* we still miss freeing a lot!,
1144          * but many areas could skip initialization too for anim play */
1145         IMB_exit();
1146         BKE_images_exit();
1147         BLF_exit();
1148 #endif
1149         GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
1150
1151         totblock = MEM_get_memory_blocks_in_use();
1152         if (totblock != 0) {
1153                 /* prints many bAKey, bArgument's which are tricky to fix */
1154 #if 0
1155                 printf("Error Totblock: %d\n", totblock);
1156                 MEM_printmemlist();
1157 #endif
1158         }
1159 }