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