2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Campbell Barton
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/windowmanager/intern/wm_playanim.c
31 * \note This file uses ghost directly and none of the WM definitions.
32 * this could be made into its own module, alongside creator/
35 #include <sys/types.h>
42 # include <sys/times.h>
43 # include <sys/wait.h>
47 #include "MEM_guardedalloc.h"
53 #include "BLI_blenlib.h"
56 #include "IMB_imbuf_types.h"
57 #include "IMB_imbuf.h"
59 #include "BKE_blender.h"
60 #include "BKE_global.h"
61 #include "BKE_utildefines.h"
64 #include "BIF_glutil.h"
70 # elif defined(__APPLE__)
71 # include <QuickTime/Movies.h>
72 # endif /* __APPLE__ */
73 #endif /* WITH_QUICKTIME */
75 #include "DNA_scene_types.h"
76 #include "BLI_utildefines.h"
77 #include "ED_datafiles.h" /* for fonts */
78 #include "wm_event_types.h"
79 #include "GHOST_C-api.h"
88 typedef struct PlayState {
101 struct PlayAnimPict *picture;
103 /* set once at the start */
107 /* saves passing args */
108 struct ImBuf *curframe_ibuf;
111 /* ***************** gl_util.c ****************** */
113 static GHOST_SystemHandle g_system = NULL;
114 static void *g_window = NULL;
116 static int qualN = 0;
118 #define LSHIFT (1 << 0)
119 #define RSHIFT (1 << 1)
120 #define SHIFT (LSHIFT | RSHIFT)
121 #define LALT (1 << 2)
122 #define RALT (1 << 3)
123 #define ALT (LALT | RALT)
124 #define LCTRL (1 << 4)
125 #define RCTRL (1 << 5)
126 #define LMOUSE (1 << 16)
127 #define MMOUSE (1 << 17)
128 #define RMOUSE (1 << 18)
129 #define MOUSE (LMOUSE | MMOUSE | RMOUSE)
131 unsigned short screen_qread(short *val, char *ascii);
133 void playanim_window_get_size(int *width_r, int *height_r)
135 GHOST_RectangleHandle bounds = GHOST_GetClientBounds(g_window);
136 *width_r = GHOST_GetWidthRectangle(bounds);
137 *height_r = GHOST_GetHeightRectangle(bounds);
138 GHOST_DisposeRectangle(bounds);
142 static void playanim_event_qual_update(void)
147 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyLeftShift, &val);
148 if (val) qualN |= LSHIFT;
149 else qualN &= ~LSHIFT;
151 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyRightShift, &val);
152 if (val) qualN |= RSHIFT;
153 else qualN &= ~RSHIFT;
156 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyLeftControl, &val);
157 if (val) qualN |= LCTRL;
158 else qualN &= ~LCTRL;
160 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyRightControl, &val);
161 if (val) qualN |= RCTRL;
162 else qualN &= ~RCTRL;
165 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyLeftAlt, &val);
166 if (val) qualN |= LCTRL;
167 else qualN &= ~LCTRL;
169 GHOST_GetModifierKeyState(g_system, GHOST_kModifierKeyRightAlt, &val);
170 if (val) qualN |= RCTRL;
171 else qualN &= ~RCTRL;
174 GHOST_GetButtonState(g_system, GHOST_kButtonMaskLeft, &val);
175 if (val) qualN |= LMOUSE;
176 else qualN &= ~LMOUSE;
179 GHOST_GetButtonState(g_system, GHOST_kButtonMaskMiddle, &val);
180 if (val) qualN |= MMOUSE;
181 else qualN &= ~MMOUSE;
184 GHOST_GetButtonState(g_system, GHOST_kButtonMaskRight, &val);
185 if (val) qualN |= RMOUSE;
186 else qualN &= ~RMOUSE;
189 typedef struct PlayAnimPict {
190 struct PlayAnimPict *next, *prev;
200 static struct ListBase _picsbase = {NULL, NULL};
201 static struct ListBase *picsbase = &_picsbase;
202 static int fromdisk = FALSE;
203 static int fstep = 1;
204 static float zoomx = 1.0, zoomy = 1.0;
205 static double ptottime = 0.0, swaptime = 0.04;
207 static int pupdate_time(void)
212 time = PIL_check_seconds_timer();
214 ptottime += (time - ltime);
216 return (ptottime < 0);
219 static void toscreen(PlayAnimPict *picture, struct ImBuf *ibuf, int fontid)
223 printf("no ibuf !\n");
226 if (ibuf->rect == NULL && ibuf->rect_float) {
227 IMB_rect_from_float(ibuf);
228 imb_freerectfloatImBuf(ibuf);
230 if (ibuf->rect == NULL)
233 GHOST_ActivateWindowDrawingContext(g_window);
235 glRasterPos2f(0.0f, 0.0f);
237 glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
241 if (picture && (qualN & (SHIFT | LMOUSE)) && (fontid != -1)) {
242 char str[32 + FILE_MAX];
244 // glRasterPos2f(0.02f, 0.03f);
245 BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
246 // BMF_DrawString(font, str);
248 BLF_enable(fontid, BLF_ASPECT);
249 BLF_aspect(fontid, 1.0f / ibuf->x, 1.0f / ibuf->y, 1.0f);
250 BLF_position(fontid, 0.02f, 0.03f, 0.0f);
251 BLF_draw(fontid, str, 256); // XXX
252 printf("Drawing text '%s'\n", str);
255 GHOST_SwapWindowBuffers(g_window);
258 static void build_pict_list(char *first, int totframes, int fstep, int fontid)
260 char *mem, filepath[FILE_MAX];
262 PlayAnimPict *picture = NULL;
263 struct ImBuf *ibuf = NULL;
264 char str[32 + FILE_MAX];
267 if (IMB_isanim(first)) {
268 anim = IMB_open_anim(first, IB_rect, 0);
271 ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
273 toscreen(NULL, ibuf, fontid);
277 for (pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
278 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "Pict");
279 picture->anim = anim;
280 picture->frame = pic;
281 picture->IB_flags = IB_rect;
282 BLI_snprintf(str, sizeof(str), "%s : %d", first, pic + 1);
283 picture->name = strdup(str);
284 BLI_addtail(picsbase, picture);
288 printf("couldn't open anim %s\n", first);
294 BLI_strncpy(filepath, first, sizeof(filepath));
301 * If set, all reads and writes on the resulting file descriptor will
302 * be performed directly to or from the user program buffer, provided
303 * appropriate size and alignment restrictions are met. Refer to the
304 * F_SETFL and F_DIOINFO commands in the fcntl(2) manual entry for
305 * information about how to determine the alignment constraints.
306 * O_DIRECT is a Silicon Graphics extension and is only supported on
307 * local EFS and XFS file systems.
310 while (IMB_ispic(filepath) && totframes) {
314 file = open(filepath, O_BINARY | O_RDONLY, 0);
315 if (file < 0) return;
316 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "picture");
317 if (picture == NULL) {
318 printf("Not enough memory for pict struct \n");
322 size = BLI_file_descriptor_size(file);
330 picture->size = size;
331 picture->IB_flags = IB_rect;
333 if (fromdisk == FALSE) {
334 mem = (char *)MEM_mallocN(size, "build pic list");
336 printf("Couldn't get memory\n");
342 if (read(file, mem, size) != size) {
343 printf("Error while reading %s\n", filepath);
355 picture->name = strdup(filepath);
357 BLI_addtail(picsbase, picture);
362 if (ptottime > 1.0) {
364 ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size,
365 picture->IB_flags, picture->name);
368 ibuf = IMB_loadiffname(picture->name, picture->IB_flags);
371 toscreen(picture, ibuf, fontid);
378 BLI_newname(filepath, +fstep);
382 switch (qreadN(&val)) {
395 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
397 PlayState *ps = (PlayState *)ps_void;
399 GHOST_TEventType type = GHOST_GetEventType(evt);
402 playanim_event_qual_update();
404 /* convert ghost event into value keyboard or mouse */
405 val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
407 if (ps->wait2 && ps->stopped) {
412 case GHOST_kEventKeyDown:
413 case GHOST_kEventKeyUp:
415 GHOST_TEventKeyData *key_data;
417 key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
418 switch (key_data->key) {
420 if (val) ps->noskip = !ps->noskip;
423 if (val) ps->pingpong = !ps->pingpong;
425 case GHOST_kKeyNumpad1:
426 if (val) swaptime = fstep / 60.0;
428 case GHOST_kKeyNumpad2:
429 if (val) swaptime = fstep / 50.0;
431 case GHOST_kKeyNumpad3:
432 if (val) swaptime = fstep / 30.0;
434 case GHOST_kKeyNumpad4:
436 swaptime = fstep / 24.0;
438 swaptime = fstep / 25.0;
440 case GHOST_kKeyNumpad5:
441 if (val) swaptime = fstep / 20.0;
443 case GHOST_kKeyNumpad6:
444 if (val) swaptime = fstep / 15.0;
446 case GHOST_kKeyNumpad7:
447 if (val) swaptime = fstep / 12.0;
449 case GHOST_kKeyNumpad8:
450 if (val) swaptime = fstep / 10.0;
452 case GHOST_kKeyNumpad9:
453 if (val) swaptime = fstep / 6.0;
455 case GHOST_kKeyLeftArrow:
460 ps->picture = picsbase->first;
468 case GHOST_kKeyDownArrow:
472 ps->next = ps->direction = -1;
480 case GHOST_kKeyRightArrow:
485 ps->picture = picsbase->last;
493 case GHOST_kKeyUpArrow:
497 ps->next = ps->direction = 1;
506 case GHOST_kKeySlash:
507 case GHOST_kKeyNumpadSlash:
510 if (ps->curframe_ibuf)
511 printf(" Name: %s | Speed: %.2f frames/s\n", ps->curframe_ibuf->name, fstep / swaptime);
514 swaptime = fstep / 5.0;
521 if (qualN & LMOUSE) {
524 playanim_window_get_size(&sizex, &sizey);
525 ps->picture = picsbase->first;
527 while (ps->picture) {
529 ps->picture = ps->picture->next;
531 i = (i * val) / sizex;
532 ps->picture = picsbase->first;
534 if (ps->picture->next == NULL) break;
535 ps->picture = ps->picture->next;
543 case GHOST_kKeyEqual:
547 printf("pause:%d\n", ps->pause);
554 case GHOST_kKeyMinus:
558 printf("pause:%d\n", ps->pause);
565 case GHOST_kKeyNumpad0:
568 ps->once = ps->wait2 = FALSE;
577 case GHOST_kKeyEnter:
578 case GHOST_kKeyNumpadEnter:
580 ps->wait2 = ps->sstep = FALSE;
583 case GHOST_kKeyNumpadPeriod:
585 if (ps->sstep) ps->wait2 = FALSE;
588 ps->wait2 = !ps->wait2;
592 case GHOST_kKeyNumpadPlus:
596 /* no break??? - is this intentional? - campbell XXX25 */
597 case GHOST_kKeyNumpadMinus:
600 /* int ofsx, ofsy; */ /* UNUSED */
603 if (zoomx > 1.0) zoomx -= 1.0;
604 if (zoomy > 1.0) zoomy -= 1.0;
605 // playanim_window_get_position(&ofsx, &ofsy);
606 playanim_window_get_size(&sizex, &sizey);
607 /* ofsx += sizex / 2; */ /* UNUSED */
608 /* ofsy += sizey / 2; */ /* UNUSED */
609 sizex = zoomx * ps->ibufx;
610 sizey = zoomy * ps->ibufy;
611 /* ofsx -= sizex / 2; */ /* UNUSED */
612 /* ofsy -= sizey / 2; */ /* UNUSED */
613 // window_set_position(g_window,sizex,sizey);
614 GHOST_SetClientSize(g_window, sizex, sizey);
625 case GHOST_kEventWindowSize:
626 case GHOST_kEventWindowMove:
630 playanim_window_get_size(&sizex, &sizey);
631 GHOST_ActivateWindowDrawingContext(g_window);
633 glViewport(0, 0, sizex, sizey);
634 glScissor(0, 0, sizex, sizey);
636 zoomx = (float) sizex / ps->ibufx;
637 zoomy = (float) sizey / ps->ibufy;
638 zoomx = floor(zoomx + 0.5);
639 zoomy = floor(zoomy + 0.5);
640 if (zoomx < 1.0) zoomx = 1.0;
641 if (zoomy < 1.0) zoomy = 1.0;
643 sizex = zoomx * ps->ibufx;
644 sizey = zoomy * ps->ibufy;
646 glPixelZoom(zoomx, zoomy);
649 toscreen(ps->picture, ps->curframe_ibuf, ps->fontid);
650 //XXX25 while (qtest()) qreadN(&val);
654 case GHOST_kEventQuit:
655 case GHOST_kEventWindowClose:
668 void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey, int start_maximized)
670 GHOST_TWindowState inital_state;
671 GHOST_TUns32 scr_w, scr_h;
673 GHOST_GetMainDisplayDimensions(g_system, &scr_w, &scr_h);
675 posy = (scr_h - posy - sizey);
677 if (start_maximized == G_WINDOWSTATE_FULLSCREEN)
678 inital_state = start_maximized ? GHOST_kWindowStateFullScreen : GHOST_kWindowStateNormal;
680 inital_state = start_maximized ? GHOST_kWindowStateMaximized : GHOST_kWindowStateNormal;
681 #if defined(__APPLE__) && !defined(GHOST_COCOA)
683 extern int macPrefState; /* creator.c */
684 initial_state += macPrefState;
688 g_window = GHOST_CreateWindow(g_system,
690 posx, posy, sizex, sizey,
692 GHOST_kDrawingContextTypeOpenGL,
693 FALSE /* no stereo */, FALSE);
697 // GHOST_SetWindowUserData(ghostwin, win);
699 // GHOST_DisposeWindow(g_system, ghostwin);
705 void playanim(int argc, const char **argv)
707 struct ImBuf *ibuf = NULL;
708 char filepath[FILE_MAX];
709 GHOST_TUns32 maxwinx, maxwiny;
710 /* short c233 = FALSE, yuvx = FALSE; */ /* UNUSED */
712 /* This was done to disambiguate the name for use under c++. */
713 struct anim *anim = NULL;
714 int start_x = 0, start_y = 0;
721 /* ps.doubleb = TRUE;*/ /* UNUSED */
734 /* resetmap = FALSE */
739 if (argv[1][0] == '-') {
740 switch (argv[1][1]) {
746 start_x = atoi(argv[2]);
747 start_y = atoi(argv[3]);
752 printf("too few arguments for -p (need 2): skipping\n");
757 double fps = atof(argv[2]);
758 double fps_base = atof(argv[3]);
761 printf("invalid fps,"
764 swaptime = fps_base / fps;
769 printf("too few arguments for -f (need 2): skipping\n");
773 sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
778 efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
783 fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2])));
789 printf("unknown option '%c': skipping\n", argv[1][1]);
800 #ifdef WITH_QUICKTIME
801 #if defined(_WIN32) || defined(__APPLE__) && !defined(GHOST_COCOA)
802 /* Initialize QuickTime */
808 if (InitializeQTML(0) != noErr)
809 G.have_quicktime = FALSE;
811 G.have_quicktime = TRUE;
813 if (EnterMovies() != noErr)
814 G.have_quicktime = FALSE;
816 #endif /* _WIN32 || __APPLE__ && !defined(GHOST_COCOA)*/
817 G.have_quicktime = TRUE;
818 #endif /* WITH_QUICKTIME */
821 BLI_strncpy(filepath, argv[1], sizeof(filepath));
824 BLI_current_working_dir(filepath, sizeof(filepath));
825 BLI_add_slash(filepath);
828 if (IMB_isanim(filepath)) {
829 anim = IMB_open_anim(filepath, IB_rect, 0);
831 ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
832 IMB_close_anim(anim);
836 else if (!IMB_ispic(filepath)) {
841 ibuf = IMB_loadiffname(filepath, IB_rect);
845 printf("couldn't open %s\n", filepath);
850 #if !defined(WIN32) && !defined(__APPLE__)
857 // extern void add_to_mainqueue(wmWindow *win, void *user_data, short evt, short val, char ascii);
859 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
861 g_system = GHOST_CreateSystem();
862 GHOST_AddEventConsumer(g_system, consumer);
866 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y, 0);
867 //XXX25 window_set_handler(g_window, add_to_mainqueue, NULL);
869 glMatrixMode(GL_PROJECTION);
871 glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
872 glMatrixMode(GL_MODELVIEW);
875 GHOST_GetMainDisplayDimensions(g_system, &maxwinx, &maxwiny);
877 //GHOST_ActivateWindowDrawingContext(g_window);
879 /* initialize the font */
881 ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
882 BLF_size(ps.fontid, 11, 72);
887 if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
888 if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
890 glClearColor(0.0, 0.0, 0.0, 0.0);
891 glClear(GL_COLOR_BUFFER_BIT);
893 GHOST_SwapWindowBuffers(g_window);
895 if (sfra == -1 || efra == -1) {
896 /* one of the frames was invalid, just use all images */
901 build_pict_list(filepath, (efra - sfra) + 1, fstep, ps.fontid);
903 for (i = 2; i < argc; i++) {
904 BLI_strncpy(filepath, argv[i], sizeof(filepath));
905 build_pict_list(filepath, (efra - sfra) + 1, fstep, ps.fontid);
916 ps.direction = -ps.direction;
918 if (ps.direction == 1) {
919 ps.picture = picsbase->first;
922 ps.picture = picsbase->last;
925 if (ps.picture == NULL) {
926 printf("couldn't find pictures\n");
930 if (ps.direction == 1) {
931 ps.picture = ps.picture->next;
934 ps.picture = ps.picture->prev;
937 if (ptottime > 0.0) ptottime = 0.0;
940 if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
942 if (ps.picture->ibuf) {
943 ibuf = ps.picture->ibuf;
945 else if (ps.picture->anim) {
946 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
948 else if (ps.picture->mem) {
949 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
950 ps.picture->IB_flags, ps.picture->name);
953 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags);
957 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
960 GHOST_SetTitle(g_window, picture->name);
963 while (pupdate_time()) PIL_sleep_ms(1);
964 ptottime -= swaptime;
965 toscreen(ps.picture, ibuf, ps.fontid);
968 printf("error: can't play this image type\n");
973 if (ps.picture->next == NULL) {
976 else if (ps.picture->prev == NULL) {
981 ps.next = ps.direction;
985 int hasevent = GHOST_ProcessEvents(g_system, 0);
987 GHOST_DispatchEvents(g_system);
993 if (ps.wait2 == 0 && ps.stopped == 0) {
999 if (ps.picture && ps.next) {
1000 /* always at least set one step */
1001 while (ps.picture) {
1003 ps.picture = ps.picture->prev;
1006 ps.picture = ps.picture->next;
1009 if (ps.once && ps.picture != NULL) {
1010 if (ps.picture->next == NULL) {
1013 else if (ps.picture->prev == NULL) {
1018 if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1019 ptottime -= swaptime;
1021 if (ps.picture == NULL && ps.sstep) {
1023 ps.picture = picsbase->last;
1025 else if (ps.next > 0) {
1026 ps.picture = picsbase->first;
1030 if (ps.go == FALSE) {
1035 ps.picture = picsbase->first;
1037 while (ps.picture) {
1038 if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) {
1039 // to prevent divx crashes
1040 anim = ps.picture->anim;
1041 IMB_close_anim(anim);
1044 if (ps.picture->ibuf) {
1045 IMB_freeImBuf(ps.picture->ibuf);
1047 if (ps.picture->mem) {
1048 MEM_freeN(ps.picture->mem);
1051 ps.picture = ps.picture->next;
1053 #ifdef WITH_QUICKTIME
1054 #if defined(_WIN32) || defined(__APPLE__) && !defined(GHOST_COCOA)
1055 if (G.have_quicktime) {
1061 #endif /* _WIN32 || __APPLE__ && !defined(GHOST_COCOA) */
1062 #endif /* WITH_QUICKTIME */
1065 if (ibuf) IMB_freeImBuf(ibuf);
1066 BLI_freelistN(picsbase);
1070 /* we still miss freeing a lot!,
1071 * but many areas could skip initialization too for anim play */
1075 GHOST_DisposeWindow(g_system, g_window);
1077 totblock = MEM_get_memory_blocks_in_use();
1078 if (totblock != 0) {
1079 printf("Error Totblock: %d\n", totblock);