a9fb0f352f2bbf124842c4954fb1b80fcd975fda
[blender.git] / source / blender / src / renderwin.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #ifdef WIN32
34 /* for the multimedia timer */
35 #include <windows.h>
36 #include <mmsystem.h>
37 #endif
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <math.h>
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #ifdef WIN32
48 #include "BLI_winstuff.h"
49 #else
50  /* for signal callback, not (fully) supported at windows */
51 #include <sys/time.h>
52 #include <signal.h>
53
54 #endif
55
56 #include <limits.h>
57
58 #include "BLI_blenlib.h"
59 #include "BLI_threads.h"
60
61 #include "MEM_guardedalloc.h"
62
63 #include "BMF_Api.h"
64
65 #include "DNA_view3d_types.h"
66 #include "DNA_screen_types.h"
67 #include "DNA_scene_types.h"
68 #include "DNA_vec_types.h"
69
70 #include "BKE_global.h"
71 #include "BKE_image.h"
72 #include "BKE_scene.h"
73 #include "BKE_utildefines.h"
74 #include "BKE_writeavi.h"       /* movie handle */
75
76 #include "BIF_drawimage.h"
77 #include "BIF_gl.h"
78 #include "BIF_glutil.h"
79 #include "BIF_graphics.h"
80 #include "BIF_screen.h"
81 #include "BIF_space.h"
82 #include "BIF_mywindow.h"
83 #include "BIF_renderwin.h"
84 #include "BIF_resources.h"
85 #include "BIF_toets.h"
86 #include "BIF_toolbox.h"
87 #include "BIF_writeimage.h"
88
89 #include "BDR_editobject.h"
90 #include "BPY_extern.h" /* for BPY_do_all_scripts */
91
92 #include "BSE_view.h"
93 #include "BSE_drawview.h"
94 #include "BSE_filesel.h"
95 #include "BSE_headerbuttons.h"
96
97 #include "RE_pipeline.h"
98
99 #include "IMB_imbuf.h"
100 #include "IMB_imbuf_types.h"
101
102 #include "blendef.h"
103 #include "mydevice.h"
104 #include "winlay.h"
105
106 /* ------------ renderwin struct, to prevent too much global vars --------- */
107 /* ------------ only used for display in a 2nd window  --------- */
108
109
110 /* flags escape presses during event handling
111 * so we can test for user break later.
112 */
113 #define RW_FLAGS_ESCAPE         (1<<0)
114 /* old zoom style (2x, locked to mouse, exits
115 * when mouse leaves window), to be removed
116 * at some point.
117 */
118 #define RW_FLAGS_OLDZOOM                (1<<1)
119 /* on when image is being panned with middlemouse
120 */
121 #define RW_FLAGS_PANNING                (1<<2)
122 /* on when the mouse is dragging over the image
123 * to examine pixel values.
124 */
125 #define RW_FLAGS_PIXEL_EXAMINING        (1<<3)
126
127 /* forces draw of alpha */
128 #define RW_FLAGS_ALPHA          (1<<4)
129
130
131 typedef struct {
132         Window *win;
133
134         int rectx, recty;       /* size of image */
135         
136         int sparex, sparey;     /* spare rect size */
137         unsigned int *rectspare;
138         float *rectsparef;
139         
140         float zoom, zoomofs[2];
141         int active;
142         short storespare, showspare;
143         
144         int mbut[3];
145         int lmouse[2];
146         
147         unsigned int flags;
148         
149         float pan_mouse_start[2], pan_ofs_start[2];
150
151         char *info_text;
152         char *render_text, *render_text_spare;
153         
154 } RenderWin;
155
156 static RenderWin *render_win= NULL;
157
158 /* --------------- help functions for RenderWin struct ---------------------------- */
159
160
161 /* only called in function open_renderwin */
162 static RenderWin *renderwin_alloc(Window *win)
163 {
164         RenderWin *rw= MEM_callocN(sizeof(*rw), "RenderWin");
165         rw->win= win;
166         rw->zoom= 1.0;
167         rw->active= 0;
168         rw->flags= 0;
169         rw->zoomofs[0]= rw->zoomofs[1]= 0;
170         rw->info_text= NULL;
171         rw->render_text= MEM_callocN(RW_MAXTEXT, "rendertext");
172         rw->render_text_spare= MEM_callocN(RW_MAXTEXT, "rendertext spare");
173
174         rw->lmouse[0]= rw->lmouse[1]= 0;
175         rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= 0;
176
177         return rw;
178 }
179
180
181 static void renderwin_queue_redraw(RenderWin *rw)
182 {
183         window_queue_redraw(rw->win); // to ghost
184 }
185
186 static void renderwin_reshape(RenderWin *rw)
187 {
188         ;
189 }
190
191 static void renderwin_get_fullrect(RenderWin *rw, float fullrect_r[2][2])
192 {
193         float display_w, display_h;
194         float cent_x, cent_y;
195         int w, h;
196
197         window_get_size(rw->win, &w, &h);
198         h-= RW_HEADERY;
199
200         display_w= rw->rectx*rw->zoom;
201         display_h= rw->recty*rw->zoom;
202         cent_x= (rw->zoomofs[0] + rw->rectx/2)*rw->zoom;
203         cent_y= (rw->zoomofs[1] + rw->recty/2)*rw->zoom;
204         
205         fullrect_r[0][0]= w/2 - cent_x;
206         fullrect_r[0][1]= h/2 - cent_y;
207         fullrect_r[1][0]= fullrect_r[0][0] + display_w;
208         fullrect_r[1][1]= fullrect_r[0][1] + display_h;
209 }
210
211         /** 
212          * Project window coordinate to image pixel coordinate.
213          * Returns true if resulting coordinate is within image.
214          */
215 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
216 {
217         float fullrect[2][2];
218         
219         renderwin_get_fullrect(rw, fullrect);
220         
221         imgco_r[0]= (int) ((winco[0]-fullrect[0][0])/rw->zoom);
222         imgco_r[1]= (int) ((winco[1]-fullrect[0][1])/rw->zoom);
223         
224         return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<rw->rectx && imgco_r[1]<rw->recty);
225 }
226
227         /**
228          * Project window coordinates to normalized device coordinates
229          * Returns true if resulting coordinate is within window.
230          */
231 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
232 {
233         int w, h;
234
235         window_get_size(rw->win, &w, &h);
236         h-= RW_HEADERY;
237
238         ndc_r[0]=  ((float)(win_co[0]*2)/(w-1) - 1.0f);
239         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0f);
240
241         return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
242 }
243
244 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
245 {
246         if (rw->info_text) MEM_freeN(rw->info_text);
247         rw->info_text= info_text?BLI_strdup(info_text):NULL;
248 }
249
250 static void renderwin_reset_view(RenderWin *rw)
251 {
252         int w, h;
253
254         if (rw->info_text) renderwin_set_infotext(rw, NULL);
255
256         /* now calculate a zoom for when image is larger than window */
257         window_get_size(rw->win, &w, &h);
258         h-= RW_HEADERY;
259
260         if(rw->rectx>w || rw->recty>h) {
261                 if(rw->rectx-w > rw->recty-h) rw->zoom= ((float)w)/((float)rw->rectx);
262                 else rw->zoom= ((float)h)/((float)rw->recty);
263         }
264         else rw->zoom= 1.0;
265
266         rw->zoomofs[0]= rw->zoomofs[1]= 0;
267         renderwin_queue_redraw(rw);
268 }
269
270 static void renderwin_draw_render_info(RenderWin *rw)
271 {
272         /* render text is added to top */
273         if(RW_HEADERY) {
274                 float colf[3];
275                 rcti rect;
276                 char *str;
277                 
278                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
279                 rect.xmin= 0;
280                 rect.ymin= rect.ymax-RW_HEADERY;
281                 glEnable(GL_SCISSOR_TEST);
282                 glaDefine2DArea(&rect);
283                 
284                 /* clear header rect */
285                 BIF_SetTheme(NULL);     // sets view3d theme by default
286                 BIF_GetThemeColor3fv(TH_HEADER, colf);
287                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
288                 glClear(GL_COLOR_BUFFER_BIT);
289                 
290                 if(rw->showspare)
291                         str= rw->render_text_spare;
292                 else
293                         str= rw->render_text;
294                 
295                 if(str) {
296                         BIF_ThemeColor(TH_TEXT);
297                         glRasterPos2i(12, 5);
298                         BMF_DrawString(G.fonts, str);
299                 }
300                 
301                 BIF_SetTheme(curarea);  // restore theme
302         }       
303         
304 }
305
306 static void renderwin_draw(RenderWin *rw, int just_clear)
307 {
308         float fullrect[2][2];
309         int set_back_mainwindow;
310         rcti rect;
311
312         /* since renderwin uses callbacks (controlled by ghost) it can
313                 mess up active window output with redraw events after a render. 
314                 this is patchy, still WIP */
315         set_back_mainwindow = (winlay_get_active_window() != rw->win);
316         window_make_active(rw->win);
317         
318         rect.xmin= rect.ymin= 0;
319         window_get_size(rw->win, &rect.xmax, &rect.ymax);
320         rect.ymax-= RW_HEADERY;
321         
322         renderwin_get_fullrect(rw, fullrect);
323         
324         /* do this first, so window ends with correct scissor */
325         renderwin_draw_render_info(rw);
326         
327         glEnable(GL_SCISSOR_TEST);
328         glaDefine2DArea(&rect);
329         
330         glClearColor(.1875, .1875, .1875, 1.0); 
331         glClear(GL_COLOR_BUFFER_BIT);
332
333         if (just_clear) {
334                 glColor3ub(0, 0, 0);
335                 glRectfv(fullrect[0], fullrect[1]);
336         } else {
337                 RenderResult rres;
338                 
339                 if(rw->showspare) {
340                         rres.rectx= rw->sparex;
341                         rres.recty= rw->sparey;
342                         rres.rect32= rw->rectspare;
343                         rres.rectf= rw->rectsparef;
344                 }
345                 else
346                         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
347                 
348                 if(rres.rectf || rres.rect32) {
349                         
350                         glPixelZoom(rw->zoom, rw->zoom);
351                         if(rw->flags & RW_FLAGS_ALPHA) {
352                                 if(rres.rect32) {
353                                         /* swap bytes, so alpha is most significant one, then just draw it as luminance int */
354                                         if(G.order==B_ENDIAN)
355                                                 glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
356                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_LUMINANCE, GL_UNSIGNED_INT, rres.rect32);
357                                         glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
358                                 }
359                                 else {
360                                         float *trectf= MEM_mallocN(rres.rectx*rres.recty*4, "temp");
361                                         int a, b;
362                                         
363                                         for(a= rres.rectx*rres.recty -1, b= 4*a+3; a>=0; a--, b-=4)
364                                                 trectf[a]= rres.rectf[b];
365                                         
366                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_LUMINANCE, GL_FLOAT, trectf);
367                                         MEM_freeN(trectf);
368                                 }
369                         }
370                         else {
371                                 if(rres.rect32)
372                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rres.rect32);
373                                 else if(rres.rectf)
374                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_FLOAT, rres.rectf);
375                         }
376                         glPixelZoom(1.0, 1.0);
377                 }
378         }
379         
380         /* info text is overlayed on bottom */
381         if (rw->info_text) {
382                 float w;
383                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
384                 glEnable(GL_BLEND);
385                 w=186.0*strlen(rw->info_text)/30;
386                 glColor4f(.5,.5,.5,.25);
387                 glRectf(0.0,0.0,w,30.0);
388                 glDisable(GL_BLEND);
389                 glColor3ub(255, 255, 255);
390                 glRasterPos2i(10, 10);
391                 BMF_DrawString(G.font, rw->info_text);
392         }
393         
394         window_swap_buffers(rw->win);
395         
396         if (set_back_mainwindow) mainwindow_make_active();      
397 }
398
399 /* ------ interactivity calls for RenderWin ------------- */
400
401 static void renderwin_mouse_moved(RenderWin *rw)
402 {
403         RenderResult rres;
404         
405         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
406
407         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
408                 int imgco[2], ofs=0;
409                 char buf[128];
410                 char *pxl;
411
412                 if (renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
413                         if (rres.rect32) {
414                                 pxl= (char*) &rres.rect32[rres.rectx*imgco[1] + imgco[0]];
415                                 ofs= sprintf(buf, "R: %d G: %d B: %d A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);   
416                         }
417                         if (rres.rectf) {
418                                 float *pxlf= rres.rectf + 4*(rres.rectx*imgco[1] + imgco[0]);
419                                 ofs+= sprintf(buf+ofs, " | R: %.3f G: %.3f B: %.3f A: %.3f ", pxlf[0], pxlf[1], pxlf[2], pxlf[3]);
420                         }
421                         if (rres.rectz) {
422                                 float *pxlz= &rres.rectz[rres.rectx*imgco[1] + imgco[0]];                       
423                                 sprintf(buf+ofs, "| Z: %.3f", *pxlz );
424                         }
425
426                         renderwin_set_infotext(rw, buf);
427                         renderwin_queue_redraw(rw);
428                 } else {
429                         renderwin_set_infotext(rw, NULL);
430                         renderwin_queue_redraw(rw);
431                 }
432         } 
433         else if (rw->flags & RW_FLAGS_PANNING) {
434                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
435                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
436         
437                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
438                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
439                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -rres.rectx/2, rres.rectx/2);
440                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -rres.recty/2, rres.recty/2);
441
442                 renderwin_queue_redraw(rw);
443         } 
444         else if (rw->flags & RW_FLAGS_OLDZOOM) {
445                 float ndc[2];
446                 int w, h;
447
448                 window_get_size(rw->win, &w, &h);
449                 h-= RW_HEADERY;
450                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
451
452                 rw->zoomofs[0]= -0.5*ndc[0]*(w-rres.rectx*rw->zoom)/rw->zoom;
453                 rw->zoomofs[1]= -0.5*ndc[1]*(h-rres.recty*rw->zoom)/rw->zoom;
454
455                 renderwin_queue_redraw(rw);
456         }
457 }
458
459 static void renderwin_mousebut_changed(RenderWin *rw)
460 {
461         if (rw->mbut[0]) {
462                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
463         } 
464         else if (rw->mbut[1]) {
465                 rw->flags|= RW_FLAGS_PANNING;
466                 rw->pan_mouse_start[0]= rw->lmouse[0];
467                 rw->pan_mouse_start[1]= rw->lmouse[1];
468                 rw->pan_ofs_start[0]= rw->zoomofs[0];
469                 rw->pan_ofs_start[1]= rw->zoomofs[1];
470         } 
471         else {
472                 if (rw->flags & RW_FLAGS_PANNING) {
473                         rw->flags &= ~RW_FLAGS_PANNING;
474                         renderwin_queue_redraw(rw);
475                 }
476                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
477                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
478                         renderwin_set_infotext(rw, NULL);
479                         renderwin_queue_redraw(rw);
480                 }
481         }
482 }
483
484
485 /* handler for renderwin, passed on to Ghost */
486 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
487 {
488         RenderWin *rw= user_data;
489
490         // added this for safety, while render it's just creating bezerk results
491         if(G.rendering) {
492                 if(evt==ESCKEY && val) 
493                         rw->flags|= RW_FLAGS_ESCAPE;
494                 return;
495         }
496         
497         if (evt==RESHAPE) {
498                 renderwin_reshape(rw);
499         } 
500         else if (evt==REDRAW) {
501                 renderwin_draw(rw, 0);
502         } 
503         else if (evt==WINCLOSE) {
504                 BIF_close_render_display();
505         } 
506         else if (evt==INPUTCHANGE) {
507                 rw->active= val;
508
509                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
510                         rw->flags&= ~RW_FLAGS_OLDZOOM;
511                         renderwin_reset_view(rw);
512                 }
513         } 
514         else if (ELEM(evt, MOUSEX, MOUSEY)) {
515                 rw->lmouse[evt==MOUSEY]= val;
516                 renderwin_mouse_moved(rw);
517         } 
518         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
519                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
520                 rw->mbut[which]= val;
521                 renderwin_mousebut_changed(rw);
522         } 
523         else if (val) {
524                 if (evt==ESCKEY) {
525                         if (rw->flags&RW_FLAGS_OLDZOOM) {
526                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
527                                 renderwin_reset_view(rw);
528                         } 
529                         else {
530                                 rw->flags|= RW_FLAGS_ESCAPE;
531                                 mainwindow_raise();
532                                 mainwindow_make_active();
533                                 rw->active= 0;
534                         }
535                 } 
536                 else if( evt==AKEY) {
537                         rw->flags ^= RW_FLAGS_ALPHA;
538                         renderwin_queue_redraw(render_win);
539                 }
540                 else if (evt==JKEY) {
541                         if(G.rendering==0) BIF_swap_render_rects();
542                 } 
543                 else if (evt==ZKEY) {
544                         if (rw->flags&RW_FLAGS_OLDZOOM) {
545                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
546                                 renderwin_reset_view(rw);
547                         } else {
548                                 rw->zoom= 2.0;
549                                 rw->flags|= RW_FLAGS_OLDZOOM;
550                                 renderwin_mouse_moved(rw);
551                         }
552                 } 
553                 else if (evt==PADPLUSKEY) {
554                         if (rw->zoom<15.9) {
555                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
556                                 else rw->zoom*= 2.0;
557                                 renderwin_queue_redraw(rw);
558                         }
559                 } 
560                 else if (evt==PADMINUS) {
561                         if (rw->zoom>0.26) {
562                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
563                                 else rw->zoom*= 0.5;
564                                 renderwin_queue_redraw(rw);
565                         }
566                 } 
567                 else if (evt==PADENTER || evt==HOMEKEY) {
568                         if (rw->flags&RW_FLAGS_OLDZOOM) {
569                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
570                         }
571                         renderwin_reset_view(rw);
572                 } 
573                 else if (evt==F3KEY) {
574                         if(G.rendering==0) {
575                                 mainwindow_raise();
576                                 mainwindow_make_active();
577                                 rw->active= 0;
578                                 areawinset(find_biggest_area()->win);
579                                 BIF_save_rendered_image_fs(0);
580                         }
581                 } 
582                 else if (evt==F11KEY) {
583                         BIF_toggle_render_display();
584                 } 
585                 else if (evt==F12KEY) {
586                         if(G.rendering==0) 
587                                 BIF_do_render(0);
588                 }
589         }
590 }
591
592 static char *renderwin_get_title(int doswap)
593 {
594         static int swap= 0;
595         char *title="";
596         
597         swap+= doswap;
598         
599         if(swap & 1) {
600                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (previous)";
601                 else title = "Blender:Render (previous)";
602         }
603         else {
604                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
605                 else title = "Blender:Render";
606         }
607
608         return title;
609 }
610
611 /* opens window and allocs struct */
612 static void open_renderwin(int winpos[2], int winsize[2], int imagesize[2])
613 {
614         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
615         Window *win;
616         char *title;
617         
618         title= renderwin_get_title(0);  /* 0 = no swap */
619         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
620
621         render_win= renderwin_alloc(win);
622         render_win->rectx= imagesize[0];
623         render_win->recty= imagesize[1];
624         
625         /* Ghost calls handler */
626         window_set_handler(win, renderwin_handler, render_win);
627
628         winlay_process_events(0);
629         window_make_active(render_win->win);
630         winlay_process_events(0);
631         
632         /* mywindow has to know about it too */
633         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
634         /* and we should be able to draw 3d in it */
635         init_gl_stuff();
636         
637         renderwin_draw(render_win, 1);
638         renderwin_draw(render_win, 1);
639 }
640
641 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
642
643 /* calculations for window size and position */
644 void calc_renderwin_rectangle(int rectx, int recty, int posmask, int renderpos_r[2], int rendersize_r[2]) 
645 {
646         int scr_w, scr_h, x, y, div= 0;
647         float ndc_x= 0.0, ndc_y= 0.0;
648
649         winlay_get_screensize(&scr_w, &scr_h);
650
651         rendersize_r[0]= rectx;
652         rendersize_r[1]= recty;
653
654         rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);     
655         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);  
656         
657         for (y=-1; y<=1; y++) {
658                 for (x=-1; x<=1; x++) {
659                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
660                                 ndc_x+= x;
661                                 ndc_y+= y;
662                                 div++;
663                         }
664                 }
665         }
666
667         if (div) {
668                 ndc_x/= div;
669                 ndc_y/= div;
670         }
671         
672         renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
673 #ifdef __APPLE__
674         /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
675         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);       
676         renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
677 #else
678         renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
679 #endif
680 }
681         
682 /* init renderwin, alloc/open/resize */
683 static void renderwin_init_display_cb(RenderResult *rr) 
684 {
685         if (G.afbreek != 1) {
686                 int rendersize[2], renderpos[2], imagesize[2];
687
688                 calc_renderwin_rectangle(rr->rectx, rr->recty, G.winpos, renderpos, rendersize);
689                 
690                 imagesize[0]= rr->rectx;
691                 imagesize[1]= rr->recty;
692
693                 if (!render_win) {
694                         open_renderwin(renderpos, rendersize, imagesize);
695                         renderwin_reset_view(render_win); // incl. autozoom for large images
696                 } else {
697                         int win_x, win_y;
698                         int win_w, win_h;
699
700                         window_get_position(render_win->win, &win_x, &win_y);
701                         window_get_size(render_win->win, &win_w, &win_h);
702                         win_h-= RW_HEADERY;
703
704                                 /* XXX, this is nasty and I guess bound to cause problems,
705                                  * but to ensure the window is at the user specified position
706                                  * and size we reopen the window all the time... we need
707                                  * a ghost _set_position to fix this -zr
708                                  */
709                                  
710                                  /* XXX, well... it is nasty yes, and reopens windows each time on
711                                     subsequent renders. Better rule is to make it reopen only only
712                                     size change, and use the preferred position only on open_renderwin
713                                         cases (ton)
714                                  */
715                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
716                                 BIF_close_render_display();
717                                 open_renderwin(renderpos, rendersize, imagesize);
718                         }
719                         else {
720                                 window_raise(render_win->win);
721                                 window_make_active(render_win->win);
722                                 
723                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
724                                 {
725                                         rcti win_rct;
726                                         win_rct.xmin= win_rct.ymin= 0;
727                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
728                                         win_rct.ymax-= RW_HEADERY;
729                                         glaDefine2DArea(&win_rct);
730                                 }
731                         }
732
733                         renderwin_reset_view(render_win);
734                         render_win->flags&= ~RW_FLAGS_ESCAPE;
735                         render_win->active= 1;
736                 }
737                 /* make sure we are in normal draw again */
738                 render_win->flags &= ~RW_FLAGS_ALPHA;
739                 
740                 glFinish();
741         }
742 }
743
744 /* callback for redraw render win */
745 static void renderwin_clear_display_cb(RenderResult *rr) 
746 {
747         if (render_win) {
748                 window_make_active(render_win->win);
749                 renderwin_draw(render_win, 1);
750         }
751 }
752
753 #define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val))
754 static void glaDrawPixelsSafe_to32(float fx, float fy, int img_w, int img_h, int row_w, int format, int type, float *rectf)
755 {
756         float *rf;
757         int x, y;
758         char *rect32, *rc;
759         
760         /* copy imgw-imgh to a temporal 32 bits rect */
761         if(img_w<1 || img_h<1) return;
762         
763         /* happens during threaded render... */
764         rc= rect32= MEM_mallocT(img_w*img_h*sizeof(int), "temp 32 bits");
765         
766         for(y=0; y<img_h; y++) {
767                 rf= rectf;
768                 for(x=0; x<img_w; x++, rf+=4, rc+=4) {
769                         rc[0]= FTOCHAR(rf[0]);
770                         rc[1]= FTOCHAR(rf[1]);
771                         rc[2]= FTOCHAR(rf[2]);
772                         rc[3]= FTOCHAR(rf[3]);
773                 }
774                 rectf+= 4*row_w;
775         }
776         
777         glaDrawPixelsSafe(fx, fy, img_w, img_h, img_w, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
778                 
779         MEM_freeT(rect32);
780 }
781
782 /* XXX, this is not good, we do this without any regard to state
783 * ... better is to make this an optimization of a more clear
784 * implementation. the bug shows up when you do something like
785 * open the window, then draw part of the progress, then get
786 * a redraw event. whatever can go wrong will. -zr
787 *
788 * Note: blocked queue handling while rendering to prevent that (ton)
789 */
790
791 /* can get as well the full picture, as the parts while rendering */
792 static void renderwin_progress(RenderWin *rw, RenderResult *rr, volatile rcti *renrect)
793 {
794         rcti win_rct;
795         float *rectf= NULL, fullrect[2][2];
796         unsigned int *rect32= NULL;
797         int ymin, ymax, xmin, xmax;
798         
799         /* if renrect argument, we only display scanlines */
800         if(renrect) {
801                  /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
802                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
803                         return;
804                 
805                 /* xmin here is first subrect x coord, xmax defines subrect width */
806                 xmin = renrect->xmin;
807                 xmax = renrect->xmax - xmin;
808                 if (xmax<2) return;
809                 
810                 ymin= renrect->ymin;
811                 ymax= renrect->ymax - ymin;
812                 if(ymax<2)
813                         return;
814                 renrect->ymin= renrect->ymax;
815         }
816         else {
817                 xmin = ymin = 0;
818                 xmax = rr->rectx - 2*rr->crop;
819                 ymax = rr->recty - 2*rr->crop;
820         }
821         
822         /* renderwindow cruft */
823         win_rct.xmin= win_rct.ymin= 0;
824         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
825         win_rct.ymax-= RW_HEADERY;
826         renderwin_get_fullrect(rw, fullrect);
827         
828         /* find current float rect for display, first case is after composit... still weak */
829         if(rr->rectf)
830                 rectf= rr->rectf;
831         else {
832                 if(rr->rect32)
833                         rect32= rr->rect32;
834                 else {
835                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
836                         rectf= rr->renlay->rectf;
837                 }
838         }
839         if(rectf) {
840                 /* if scanline updates... */
841                 rectf+= 4*(rr->rectx*ymin + xmin);
842         
843                 /* when rendering more pixels than needed, we crop away cruft */
844                 if(rr->crop)
845                         rectf+= 4*(rr->crop*rr->rectx + rr->crop);
846         }
847         
848         /* tilerect defines drawing offset from (0,0) */
849         /* however, tilerect (xmin, ymin) is first pixel */
850         fullrect[0][0] += (rr->tilerect.xmin + rr->crop + xmin)*rw->zoom;
851         fullrect[0][1] += (rr->tilerect.ymin + rr->crop + ymin)*rw->zoom;
852
853         glEnable(GL_SCISSOR_TEST);
854         glaDefine2DArea(&win_rct);
855
856 #ifdef __APPLE__
857 #else
858         glDrawBuffer(GL_FRONT);
859 #endif
860         glPixelZoom(rw->zoom, rw->zoom);
861
862         if(rect32)
863                 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
864         else
865                 glaDrawPixelsSafe_to32(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, GL_RGBA, GL_FLOAT, rectf);
866         
867         glPixelZoom(1.0, 1.0);
868         
869 #ifdef __APPLE__
870         window_swap_buffers(render_win->win);
871 #else
872         /* no glFlush(); here... threads render hates it! */
873         glFinish();
874         glDrawBuffer(GL_BACK);
875 #endif  
876 }
877
878
879 /* in render window; display a couple of scanlines of rendered image */
880 static void renderwin_progress_display_cb(RenderResult *rr, volatile rcti *rect)
881 {
882         if (render_win) {
883                 renderwin_progress(render_win, rr, rect);
884         }
885 }
886
887 /* -------------- callbacks for render loop: interactivity ----------------------- */
888
889 /* string is RW_MAXTEXT chars min */
890 void make_renderinfo_string(RenderStats *rs, char *str)
891 {
892         extern char info_time_str[32];  // header_info.c
893         extern unsigned long mem_in_use, mmap_in_use;
894         static float megs_used_memory, mmap_used_memory;
895         char *spos= str;
896         
897         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
898         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
899         
900         if(G.scene->lay & 0xFF000000)
901                 spos+= sprintf(spos, "Localview | ");
902         else if(G.scene->r.scemode & R_SINGLE_LAYER)
903                 spos+= sprintf(spos, "Single Layer | ");
904         
905         if(rs->tothalo)
906                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM (%.2fM)", (G.scene->r.cfra), rs->totvert, rs->totface, rs->tothalo, rs->totlamp, megs_used_memory, mmap_used_memory);
907         else 
908                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM (%.2fM)", (G.scene->r.cfra), rs->totvert, rs->totface, rs->totlamp, megs_used_memory, mmap_used_memory);
909         
910         BLI_timestr(rs->lastframetime, info_time_str);
911         spos+= sprintf(spos, " Time:%s ", info_time_str);
912         
913         if(rs->infostr)
914                 spos+= sprintf(spos, " | %s", rs->infostr);
915         
916         /* very weak... but 512 characters is quite safe... we cannot malloc during thread render */
917         if(spos >= str+RW_MAXTEXT)
918                 printf("WARNING! renderwin text beyond limit \n");
919         
920         /* temporal render debug printing, needed for testing orange renders atm... will be gone soon (or option) */
921         if(G.rt==7 && rs->convertdone) {
922                 char str[256];
923                 
924                 spos= str;
925                 spos+= sprintf(spos, "Fra:%d Mem:%.2fM (%.2fM)", G.scene->r.cfra, megs_used_memory, mmap_used_memory);
926                 
927                 if(rs->infostr) {
928                         spos+= sprintf(spos, " | %s", rs->infostr);
929                 }
930                 else {
931                         if(rs->tothalo)
932                                 spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d Ha:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->tothalo, rs->totlamp);
933                         else 
934                                 spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->totlamp);
935                 }
936                 printf(str); printf("\n");
937         }       
938         
939         
940 }
941
942 /* callback for print info in top header of renderwin */
943 static void renderwin_renderinfo_cb(RenderStats *rs)
944 {
945         
946         if(render_win) {
947                 
948                 make_renderinfo_string(rs, render_win->render_text);
949                 
950 #ifdef __APPLE__
951 #else
952                 glDrawBuffer(GL_FRONT);
953 #endif
954                 renderwin_draw_render_info(render_win);
955                 
956 #ifdef __APPLE__
957                 window_swap_buffers(render_win->win);
958 #else
959                 /* no glFlush(); here... threads render hates it! */
960                 glFinish();
961                 glDrawBuffer(GL_BACK);
962 #endif
963         }
964
965 }
966
967 /* -------------- callback system to allow ESC from rendering ----------------------- */
968
969 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
970 static int test_break(void)
971 {
972
973         if(G.afbreek==2) { /* code for testing queue */
974
975                 G.afbreek= 0;
976
977                 blender_test_break(); /* tests blender interface */
978
979                 if (G.afbreek==0 && render_win) { /* tests window */
980                         winlay_process_events(0);
981                         // render_win can be closed in winlay_process_events()
982                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
983                                 G.afbreek= 1;
984                         }
985                 }
986         }
987
988         if(G.afbreek==1) return 1;
989         else return 0;
990 }
991
992
993
994 #ifdef _WIN32
995 /* we use the multimedia time here */
996 static UINT uRenderTimerId;
997
998 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
999 {
1000         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
1001 }
1002
1003 /* WIN32: init SetTimer callback */
1004 static void init_test_break_callback()
1005 {
1006         timeBeginPeriod(50);
1007         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
1008 }
1009
1010 /* WIN32: stop SetTimer callback */
1011 static void end_test_break_callback()
1012 {
1013         timeEndPeriod(50);
1014         timeKillEvent(uRenderTimerId);
1015 }
1016
1017 #else
1018 /* all other OS's support signal(SIGVTALRM) */
1019
1020 /* XXX The ESC problem: some unix users reported that ESC doesn't cancel
1021  * renders anymore. Most complaints came from linux, but it's not
1022  * general, not all linux users have the problem.
1023  *
1024  * From tests, the systems that do have it are not signalling SIGVTALRM
1025  * interrupts (an issue with signals and threads). Using SIGALRM instead
1026  * fixes the problem, at least while we investigate better.
1027  *
1028  * ITIMER_REAL (SIGALRM): timer that counts real system time
1029  * ITIMER_VIRTUAL (SIGVTALRM): only counts time spent in its owner process */
1030
1031 /* POSIX: this function goes in the signal() callback */
1032 static void interruptESC(int sig)
1033 {
1034
1035         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
1036
1037         /* call again, timer was reset */
1038         signal(SIGALRM, interruptESC);
1039 }
1040
1041 /* POSIX: initialize timer and signal */
1042 static void init_test_break_callback()
1043 {
1044
1045         struct itimerval tmevalue;
1046
1047         tmevalue.it_interval.tv_sec = 0;
1048         tmevalue.it_interval.tv_usec = 250000;
1049         /* when the first ? */
1050         tmevalue.it_value.tv_sec = 0;
1051         tmevalue.it_value.tv_usec = 10000;
1052
1053         signal(SIGALRM, interruptESC);
1054         setitimer(ITIMER_REAL, &tmevalue, 0);
1055 }
1056
1057 /* POSIX: stop timer and callback */
1058 static void end_test_break_callback()
1059 {
1060         struct itimerval tmevalue;
1061
1062         tmevalue.it_value.tv_sec = 0;
1063         tmevalue.it_value.tv_usec = 0;
1064
1065         setitimer(ITIMER_REAL, &tmevalue, 0);
1066         signal(SIGALRM, SIG_IGN);
1067
1068 }
1069
1070
1071 #endif
1072
1073
1074
1075 /* -------------- callbacks for render loop: init & run! ----------------------- */
1076
1077
1078 /* - initialize displays
1079    - set callbacks
1080    - cleanup
1081 */
1082 static void error_cb(char *str){error(str);}
1083
1084 static void do_render(int anim)
1085 {
1086         Render *re= RE_NewRender(G.scene->id.name);
1087         unsigned int lay= G.scene->lay;
1088         int scemode= G.scene->r.scemode;
1089         
1090         /* UGLY! we set this flag to prevent renderwindow queue to execute another render */
1091         /* is reset in RE_BlenderFrame */
1092         G.rendering= 1;
1093         G.afbreek= 0;
1094
1095         /* set callbacks */
1096         if(G.displaymode!=R_DISPLAYWIN) {
1097                 if(render_win)
1098                         BIF_close_render_display();
1099                 imagewindow_render_callbacks(re);
1100         }
1101         else {
1102                 RE_display_init_cb(re, renderwin_init_display_cb);
1103                 RE_display_draw_cb(re, renderwin_progress_display_cb);
1104                 RE_display_clear_cb(re, renderwin_clear_display_cb);
1105                 RE_stats_draw_cb(re, renderwin_renderinfo_cb);
1106         }
1107         RE_error_cb(re, error_cb);
1108         init_test_break_callback();
1109         RE_test_break_cb(re, test_break);
1110         RE_timecursor_cb(re, set_timecursor);
1111         
1112         waitcursor(1);
1113         if(render_win) 
1114                 window_set_cursor(render_win->win, CURSOR_WAIT);
1115         
1116         if(G.obedit)
1117                 exit_editmode(0);       /* 0 = no free data */
1118
1119         /* allow localview render for objects with lights in normal layers */
1120         if(curarea->spacetype==SPACE_VIEW3D) {
1121                 if(G.vd->lay & 0xFF000000) {
1122                         G.scene->lay |= G.vd->lay;
1123                         G.scene->r.scemode |= R_SINGLE_LAYER;
1124                 }
1125                 else G.scene->lay= G.vd->lay;
1126         }
1127         
1128         if(anim)
1129                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
1130         else
1131                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
1132
1133         /* restore local view exception */
1134         G.scene->lay= lay;
1135         G.scene->r.scemode= scemode;
1136         
1137         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
1138         
1139         free_filesel_spec(G.scene->r.pic);
1140
1141         G.afbreek= 0;
1142         end_test_break_callback();
1143         
1144         mainwindow_make_active();
1145         
1146         /* after an envmap creation...  */
1147 //              if(R.flag & R_REDRAW_PRV) {
1148 //                      BIF_preview_changed(ID_TE);
1149 //              }
1150         allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1151                 
1152         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1153         
1154         waitcursor(0);
1155 }
1156
1157 #if 0
1158 /* used for swapping with spare buffer, when images are different size */
1159 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1160 {
1161         unsigned int *rect, *newrect;
1162         int x, y;
1163         int ofsx, ofsy, stepx, stepy;
1164
1165         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1166         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1167         ofsy = 32768;
1168         newrect= rectn;
1169         
1170         for (y = newy; y > 0 ; y--){
1171                 rect = recto;
1172                 rect += (ofsy >> 16) * oldx;
1173                 ofsy += stepy;
1174                 ofsx = 32768;
1175                 for (x = newx ; x>0 ; x--){
1176                         *newrect++ = rect[ofsx >> 16];
1177                         ofsx += stepx;
1178                 }
1179         }
1180 }
1181 #endif
1182
1183 static void renderwin_store_spare(void)
1184 {
1185         RenderResult rres;
1186         
1187         if(render_win==0 || render_win->storespare==0)
1188                 return;
1189
1190         if(render_win->showspare) {
1191                 render_win->showspare= 0;
1192                 window_set_title(render_win->win, renderwin_get_title(1));
1193         }
1194         
1195         BLI_strncpy(render_win->render_text_spare, render_win->render_text, RW_MAXTEXT);
1196         
1197         if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1198         render_win->rectspare= NULL;
1199         if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1200         render_win->rectsparef= NULL;
1201         
1202         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1203         
1204         if(rres.rect32)
1205                 render_win->rectspare= MEM_dupallocN(rres.rect32);
1206         else if(rres.rectf)
1207                 render_win->rectsparef= MEM_dupallocN(rres.rectf);
1208
1209         render_win->sparex= rres.rectx;
1210         render_win->sparey= rres.recty;
1211 }
1212
1213 /* -------------- API: externally called --------------- */
1214
1215 /* not used anywhere ??? */
1216 #if 0
1217 void BIF_renderwin_make_active(void)
1218 {
1219         if(render_win) {
1220                 window_make_active(render_win->win);
1221                 mywinset(2);
1222         }
1223 }
1224 #endif
1225
1226
1227
1228 /* set up display, render an image or scene */
1229 void BIF_do_render(int anim)
1230 {
1231         int slink_flag = 0;
1232
1233         if (G.f & G_DOSCRIPTLINKS) {
1234                 BPY_do_all_scripts(SCRIPT_RENDER);
1235                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1236                         G.f &= ~G_DOSCRIPTLINKS;
1237                         slink_flag = 1;
1238                 }
1239         }
1240         
1241         if(render_win && render_win->showspare)
1242                 renderwin_store_spare();
1243
1244         do_render(anim);
1245
1246         if(G.scene->use_nodes) {
1247                 allqueue(REDRAWNODE, 1);
1248                 allqueue(REDRAWIMAGE, 1);
1249         }
1250         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1251         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1252 }
1253
1254 /* set up display, render the current area view in an image */
1255 /* the RE_Render is only used to make sure we got the picture in the result */
1256 void BIF_do_ogl_render(View3D *v3d, int anim)
1257 {
1258         Render *re= RE_NewRender(G.scene->id.name);
1259         RenderResult *rr;
1260         int winx, winy;
1261         
1262         G.afbreek= 0;
1263         init_test_break_callback();
1264         
1265         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1266         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1267         
1268         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1269         
1270         /* for now, result is defaulting to floats still... */
1271         rr= RE_GetResult(re);
1272         if(rr->rect32==NULL)
1273                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1274         
1275         /* open window */
1276         renderwin_init_display_cb(rr);
1277         init_gl_stuff();
1278         
1279         waitcursor(1);
1280
1281         if(anim) {
1282                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1283                 int cfrao= CFRA;
1284                 
1285                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1286                         mh->start_movie(&G.scene->r, winx, winy);
1287                 
1288                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1289                         drawview3d_render(v3d, winx, winy);
1290                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1291                         window_swap_buffers(render_win->win);
1292                         
1293                         if(BKE_imtype_is_movie(G.scene->r.imtype)) {
1294                                 mh->append_movie(CFRA, rr->rect32, winx, winy);
1295                                 printf("Append frame %d", G.scene->r.cfra);
1296                         }
1297                         else {
1298                                 ImBuf *ibuf= IMB_allocImBuf(winx, winy, G.scene->r.planes, 0, 0);
1299                                 char name[FILE_MAXDIR+FILE_MAXFILE];
1300                                 int ok;
1301                                 
1302                                 BKE_makepicstring(name, (G.scene->r.cfra));
1303
1304                                 ibuf->rect= rr->rect32;    
1305                                 ok= BKE_write_ibuf(ibuf, name, G.scene->r.imtype, G.scene->r.subimtype, G.scene->r.quality);
1306                                 
1307                                 if(ok==0) {
1308                                         printf("Write error: cannot save %s\n", name);
1309                                         break;
1310                                 }
1311                                 else printf("Saved: %s", name);
1312                                 
1313                 /* imbuf knows which rects are not part of ibuf */
1314                                 IMB_freeImBuf(ibuf);    
1315                         }
1316                         /* movie stats prints have no line break */
1317                         printf("\n");
1318                         
1319                         if(test_break()) break;
1320                 }
1321                 
1322                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1323                         mh->end_movie();
1324                 
1325                 CFRA= cfrao;
1326         }
1327         else {
1328                 drawview3d_render(v3d, winx, winy);
1329                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1330                 window_swap_buffers(render_win->win);
1331         }
1332         
1333         renderwin_draw(render_win, 0);
1334
1335         mainwindow_make_active();
1336         
1337         if(anim)
1338                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1339         
1340         end_test_break_callback();
1341         waitcursor(0);
1342 }
1343
1344 void BIF_redraw_render_rect(void)
1345 {
1346         /* redraw */
1347         if (render_win) {
1348                 renderwin_queue_redraw(render_win);
1349         }
1350 }       
1351
1352 void BIF_swap_render_rects(void)
1353 {
1354         RenderResult rres;
1355         
1356         if (render_win==NULL) return;
1357         
1358         render_win->storespare= 1;
1359         render_win->showspare ^= 1;
1360                 
1361         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1362                 
1363         if(render_win->sparex!=rres.rectx || render_win->sparey!=rres.recty) {
1364                 if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1365                 render_win->rectspare= NULL;
1366                 if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1367                 render_win->rectsparef= NULL;
1368         }
1369         
1370         window_set_title(render_win->win, renderwin_get_title(1));
1371
1372         /* redraw */
1373         BIF_redraw_render_rect();
1374
1375 }                               
1376
1377 /* called from usiblender.c too, to free and close renderwin */
1378 void BIF_close_render_display(void)
1379 {
1380         if (render_win) {
1381                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1382                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1383                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1384                 if (render_win->rectspare) MEM_freeN(render_win->rectspare);
1385                 if (render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1386                         
1387                 window_destroy(render_win->win); /* ghost close window */
1388                 MEM_freeN(render_win);
1389
1390                 render_win= NULL;
1391         }
1392 }
1393
1394
1395 /* typical with F11 key, show image or hide/close */
1396 void BIF_toggle_render_display(void) 
1397 {
1398         
1399         if (G.displaymode!=R_DISPLAYWIN) {
1400                 imagewindow_toggle_render();
1401         }
1402         else {
1403                 if (render_win) {
1404                         if(render_win->active) {
1405                                 mainwindow_raise();
1406                                 mainwindow_make_active();
1407                                 render_win->active= 0;
1408                         }
1409                         else {
1410                                 window_raise(render_win->win);
1411                                 window_make_active(render_win->win);
1412                                 render_win->active= 1;
1413                         }
1414                 } 
1415                 else {
1416                         RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name));
1417                         if(rr) renderwin_init_display_cb(rr);
1418                 }
1419         }
1420 }
1421
1422 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1423 {
1424         if (render_win) {
1425                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1426         }
1427 }