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