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