Color Management, Stage 2: Switch color pipeline to use OpenColorIO
[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         //if (ghostwin) {
718         //if (win) {
719         // GHOST_SetWindowUserData(ghostwin, win);
720         //} else {
721         //      GHOST_DisposeWindow(g_WS.ghost_system, ghostwin);
722         //}
723         //}
724 }
725
726
727 void WM_main_playanim(int argc, const char **argv)
728 {
729         struct ImBuf *ibuf = NULL;
730         char filepath[FILE_MAX];
731         GHOST_TUns32 maxwinx, maxwiny;
732         /* short c233 = FALSE, yuvx = FALSE; */ /* UNUSED */
733         int i;
734         /* This was done to disambiguate the name for use under c++. */
735         struct anim *anim = NULL;
736         int start_x = 0, start_y = 0;
737         int sfra = -1;
738         int efra = -1;
739         int totblock;
740
741         PlayState ps = {0};
742
743         /* ps.doubleb   = TRUE;*/ /* UNUSED */
744         ps.go        = TRUE;
745         ps.direction = TRUE;
746         ps.next      = TRUE;
747         ps.once      = FALSE;
748         ps.turbo     = FALSE;
749         ps.pingpong  = FALSE;
750         ps.noskip    = FALSE;
751         ps.sstep     = FALSE;
752         ps.pause     = FALSE;
753         ps.wait2     = FALSE;
754         ps.stopped   = FALSE;
755         ps.picture   = NULL;
756         /* resetmap = FALSE */
757
758         ps.fontid = -1;
759
760         while (argc > 1) {
761                 if (argv[1][0] == '-') {
762                         switch (argv[1][1]) {
763                                 case 'm':
764                                         fromdisk = TRUE;
765                                         break;
766                                 case 'p':
767                                         if (argc > 3) {
768                                                 start_x = atoi(argv[2]);
769                                                 start_y = atoi(argv[3]);
770                                                 argc -= 2;
771                                                 argv += 2;
772                                         }
773                                         else {
774                                                 printf("too few arguments for -p (need 2): skipping\n");
775                                         }
776                                         break;
777                                 case 'f':
778                                         if (argc > 3) {
779                                                 double fps = atof(argv[2]);
780                                                 double fps_base = atof(argv[3]);
781                                                 if (fps == 0.0) {
782                                                         fps = 1;
783                                                         printf("invalid fps,"
784                                                                "forcing 1\n");
785                                                 }
786                                                 swaptime = fps_base / fps;
787                                                 argc -= 2;
788                                                 argv += 2;
789                                         }
790                                         else {
791                                                 printf("too few arguments for -f (need 2): skipping\n");
792                                         }
793                                         break;
794                                 case 's':
795                                         sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
796                                         argc--;
797                                         argv++;
798                                         break;
799                                 case 'e':
800                                         efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
801                                         argc--;
802                                         argv++;
803                                         break;
804                                 case 'j':
805                                         fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2])));
806                                         swaptime *= fstep;
807                                         argc--;
808                                         argv++;
809                                         break;
810                                 default:
811                                         printf("unknown option '%c': skipping\n", argv[1][1]);
812                                         break;
813                         }
814                         argc--;
815                         argv++;
816                 }
817                 else {
818                         break;
819                 }
820         }
821
822         if (argc > 1) {
823                 BLI_strncpy(filepath, argv[1], sizeof(filepath));
824         }
825         else {
826                 BLI_current_working_dir(filepath, sizeof(filepath));
827                 BLI_add_slash(filepath);
828         }
829
830         if (IMB_isanim(filepath)) {
831                 /* OCIO_TODO: support different input color spaces */
832                 anim = IMB_open_anim(filepath, IB_rect, 0, NULL);
833                 if (anim) {
834                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
835                         IMB_close_anim(anim);
836                         anim = NULL;
837                 }
838         }
839         else if (!IMB_ispic(filepath)) {
840                 exit(1);
841         }
842
843         if (ibuf == NULL) {
844                 /* OCIO_TODO: support different input color space */
845                 ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
846         }
847
848         if (ibuf == NULL) {
849                 printf("couldn't open %s\n", filepath);
850                 exit(1);
851         }
852
853 #if 0 //XXX25
854         #if !defined(WIN32) && !defined(__APPLE__)
855         if (fork()) exit(0);
856         #endif
857 #endif //XXX25
858
859         /* XXX, fixme zr */
860         {
861 //              extern void add_to_mainqueue(wmWindow *win, void *user_data, short evt, short val, char ascii);
862
863                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
864
865                 g_WS.ghost_system = GHOST_CreateSystem();
866                 GHOST_AddEventConsumer(g_WS.ghost_system, consumer);
867
868
869
870                 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y, 0);
871 //XXX25         window_set_handler(g_WS.ghost_window, add_to_mainqueue, NULL);
872
873                 glMatrixMode(GL_PROJECTION);
874                 glLoadIdentity();
875                 glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
876                 glMatrixMode(GL_MODELVIEW);
877         }
878
879         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny);
880
881         //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
882
883         /* initialize the font */
884         BLF_init(11, 72);
885         ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
886         BLF_size(ps.fontid, 11, 72);
887
888         ps.ibufx = ibuf->x;
889         ps.ibufy = ibuf->y;
890
891         if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
892         if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
893
894         glClearColor(0.0, 0.0, 0.0, 0.0);
895         glClear(GL_COLOR_BUFFER_BIT);
896
897         GHOST_SwapWindowBuffers(g_WS.ghost_window);
898
899         if (sfra == -1 || efra == -1) {
900                 /* one of the frames was invalid, just use all images */
901                 sfra = 1;
902                 efra = MAXFRAME;
903         }
904
905         build_pict_list(filepath, (efra - sfra) + 1, fstep, ps.fontid);
906
907         for (i = 2; i < argc; i++) {
908                 BLI_strncpy(filepath, argv[i], sizeof(filepath));
909                 build_pict_list(filepath, (efra - sfra) + 1, fstep, ps.fontid);
910         }
911
912         IMB_freeImBuf(ibuf);
913         ibuf = NULL;
914
915         pupdate_time();
916         ptottime = 0;
917
918         /* newly added in 2.6x, without this images never get freed */
919 #define USE_IMB_CACHE
920
921         while (ps.go) {
922                 if (ps.pingpong)
923                         ps.direction = -ps.direction;
924
925                 if (ps.direction == 1) {
926                         ps.picture = picsbase.first;
927                 }
928                 else {
929                         ps.picture = picsbase.last;
930                 }
931
932                 if (ps.picture == NULL) {
933                         printf("couldn't find pictures\n");
934                         ps.go = FALSE;
935                 }
936                 if (ps.pingpong) {
937                         if (ps.direction == 1) {
938                                 ps.picture = ps.picture->next;
939                         }
940                         else {
941                                 ps.picture = ps.picture->prev;
942                         }
943                 }
944                 if (ptottime > 0.0) ptottime = 0.0;
945
946                 while (ps.picture) {
947 #ifndef USE_IMB_CACHE
948                         if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
949 #endif
950                         if (ps.picture->ibuf) {
951                                 ibuf = ps.picture->ibuf;
952                         }
953                         else if (ps.picture->anim) {
954                                 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
955                         }
956                         else if (ps.picture->mem) {
957                                 /* use correct colorspace here */
958                                 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
959                                                              ps.picture->IB_flags, NULL, ps.picture->name);
960                         }
961                         else {
962                                 /* use correct colorspace here */
963                                 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
964                         }
965
966                         if (ibuf) {
967
968 #ifdef USE_IMB_CACHE
969                                 ps.picture->ibuf = ibuf;
970 #endif
971
972                                 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
973
974                                 /* why only windows? (from 2.4x) - campbell */
975 #ifdef _WIN32
976                                 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
977 #endif
978
979                                 while (pupdate_time()) PIL_sleep_ms(1);
980                                 ptottime -= swaptime;
981                                 playanim_toscreen(ps.picture, ibuf, ps.fontid);
982                         } /* else deleten */
983                         else {
984                                 printf("error: can't play this image type\n");
985                                 exit(0);
986                         }
987
988                         if (ps.once) {
989                                 if (ps.picture->next == NULL) {
990                                         ps.wait2 = TRUE;
991                                 }
992                                 else if (ps.picture->prev == NULL) {
993                                         ps.wait2 = TRUE;
994                                 }
995                         }
996
997                         ps.next = ps.direction;
998
999
1000                         {
1001                                 int hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0);
1002                                 if (hasevent) {
1003                                         GHOST_DispatchEvents(g_WS.ghost_system);
1004                                 }
1005                         }
1006
1007                         /* XXX25 - we should not have to do this, but it makes scrubbing functional! */
1008                         if (g_WS.qual & WS_QUAL_LMOUSE) {
1009                                 ps.next = 0;
1010                         }
1011                         else {
1012                                 ps.sstep = 0;
1013                         }
1014
1015                         ps.wait2 = ps.sstep;
1016
1017                         if (ps.wait2 == 0 && ps.stopped == 0) {
1018                                 ps.stopped = TRUE;
1019                         }
1020
1021                         pupdate_time();
1022
1023                         if (ps.picture && ps.next) {
1024                                 /* always at least set one step */
1025                                 while (ps.picture) {
1026                                         if (ps.next < 0) {
1027                                                 ps.picture = ps.picture->prev;
1028                                         }
1029                                         else {
1030                                                 ps.picture = ps.picture->next;
1031                                         }
1032
1033                                         if (ps.once && ps.picture != NULL) {
1034                                                 if (ps.picture->next == NULL) {
1035                                                         ps.wait2 = TRUE;
1036                                                 }
1037                                                 else if (ps.picture->prev == NULL) {
1038                                                         ps.wait2 = TRUE;
1039                                                 }
1040                                         }
1041
1042                                         if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1043                                         ptottime -= swaptime;
1044                                 }
1045                                 if (ps.picture == NULL && ps.sstep) {
1046                                         if (ps.next < 0) {
1047                                                 ps.picture = picsbase.last;
1048                                         }
1049                                         else if (ps.next > 0) {
1050                                                 ps.picture = picsbase.first;
1051                                         }
1052                                 }
1053                         }
1054                         if (ps.go == FALSE) {
1055                                 break;
1056                         }
1057                 }
1058         }
1059         ps.picture = picsbase.first;
1060         anim = NULL;
1061         while (ps.picture) {
1062                 if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) {
1063                         // to prevent divx crashes
1064                         anim = ps.picture->anim;
1065                         IMB_close_anim(anim);
1066                 }
1067
1068                 if (ps.picture->ibuf) {
1069                         IMB_freeImBuf(ps.picture->ibuf);
1070                 }
1071                 if (ps.picture->mem) {
1072                         MEM_freeN(ps.picture->mem);
1073                 }
1074
1075                 ps.picture = ps.picture->next;
1076         }
1077
1078         /* cleanup */
1079 #ifndef USE_IMB_CACHE
1080         if (ibuf) IMB_freeImBuf(ibuf);
1081 #endif
1082
1083         BLI_freelistN(&picsbase);
1084 #if 0 // XXX25
1085         free_blender();
1086 #else
1087         /* we still miss freeing a lot!,
1088          * but many areas could skip initialization too for anim play */
1089         IMB_exit();
1090         BLF_exit();
1091 #endif
1092         GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
1093
1094         totblock = MEM_get_memory_blocks_in_use();
1095         if (totblock != 0) {
1096                 /* prints many bAKey, bArgument's which are tricky to fix */
1097 #if 0
1098                 printf("Error Totblock: %d\n", totblock);
1099                 MEM_printmemlist();
1100 #endif
1101         }
1102 }