Bugfix #4351
[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 /* XXX, this is not good, we do this without any regard to state
754 * ... better is to make this an optimization of a more clear
755 * implementation. the bug shows up when you do something like
756 * open the window, then draw part of the progress, then get
757 * a redraw event. whatever can go wrong will. -zr
758 *
759 * Note: blocked queue handling while rendering to prevent that (ton)
760 */
761
762 /* can get as well the full picture, as the parts while rendering */
763 static void renderwin_progress(RenderWin *rw, RenderResult *rr, volatile rcti *renrect)
764 {
765         rcti win_rct;
766         float *rectf= NULL, fullrect[2][2];
767         unsigned int *rect32= NULL;
768         int ymin, ymax, xmin, xmax;
769         
770         /* if renrect argument, we only display scanlines */
771         if(renrect) {
772                  /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
773                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
774                         return;
775                 
776                 /* xmin here is first subrect x coord, xmax defines subrect width */
777                 xmin = renrect->xmin;
778                 xmax = renrect->xmax - xmin;
779                 if (xmax<2) return;
780                 
781                 ymin= renrect->ymin;
782                 ymax= renrect->ymax - ymin;
783                 if(ymax<2)
784                         return;
785                 renrect->ymin= renrect->ymax;
786         }
787         else {
788                 xmin = ymin = 0;
789                 xmax = rr->rectx - 2*rr->crop;
790                 ymax = rr->recty - 2*rr->crop;
791         }
792         
793         /* renderwindow cruft */
794         win_rct.xmin= win_rct.ymin= 0;
795         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
796         win_rct.ymax-= RW_HEADERY;
797         renderwin_get_fullrect(rw, fullrect);
798         
799         /* find current float rect for display, first case is after composit... still weak */
800         if(rr->rectf)
801                 rectf= rr->rectf;
802         else {
803                 if(rr->rect32)
804                         rect32= rr->rect32;
805                 else {
806                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
807                         rectf= rr->renlay->rectf;
808                 }
809         }
810         if(rectf) {
811                 /* if scanline updates... */
812                 rectf+= 4*(rr->rectx*ymin + xmin);
813         
814                 /* when rendering more pixels than needed, we crop away cruft */
815                 if(rr->crop)
816                         rectf+= 4*(rr->crop*rr->rectx + rr->crop);
817         }
818         
819         /* tilerect defines drawing offset from (0,0) */
820         /* however, tilerect (xmin, ymin) is first pixel */
821         fullrect[0][0] += (rr->tilerect.xmin + rr->crop + xmin)*rw->zoom;
822         fullrect[0][1] += (rr->tilerect.ymin + rr->crop + ymin)*rw->zoom;
823
824         glEnable(GL_SCISSOR_TEST);
825         glaDefine2DArea(&win_rct);
826
827 #ifdef __APPLE__
828 #else
829         glDrawBuffer(GL_FRONT);
830 #endif
831         glPixelZoom(rw->zoom, rw->zoom);
832
833         if(rect32)
834                 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
835         else
836                 glaDrawPixelsSafe_to32(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, rectf);
837         
838         glPixelZoom(1.0, 1.0);
839         
840 #ifdef __APPLE__
841         window_swap_buffers(render_win->win);
842 #else
843         /* no glFlush(); here... threads render hates it! */
844         glFinish();
845         glDrawBuffer(GL_BACK);
846 #endif  
847 }
848
849
850 /* in render window; display a couple of scanlines of rendered image */
851 static void renderwin_progress_display_cb(RenderResult *rr, volatile rcti *rect)
852 {
853         if (render_win) {
854                 renderwin_progress(render_win, rr, rect);
855         }
856 }
857
858 /* -------------- callbacks for render loop: interactivity ----------------------- */
859
860 /* string is RW_MAXTEXT chars min */
861 void make_renderinfo_string(RenderStats *rs, char *str)
862 {
863         extern char info_time_str[32];  // header_info.c
864         extern unsigned long mem_in_use, mmap_in_use;
865         static float megs_used_memory, mmap_used_memory;
866         char *spos= str;
867         
868         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
869         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
870         
871         if(G.scene->lay & 0xFF000000)
872                 spos+= sprintf(spos, "Localview | ");
873         else if(G.scene->r.scemode & R_SINGLE_LAYER)
874                 spos+= sprintf(spos, "Single Layer | ");
875         
876         if(rs->tothalo)
877                 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);
878         else 
879                 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);
880         
881         BLI_timestr(rs->lastframetime, info_time_str);
882         spos+= sprintf(spos, " Time:%s ", info_time_str);
883         
884         if(rs->infostr)
885                 spos+= sprintf(spos, " | %s", rs->infostr);
886         
887         /* very weak... but 512 characters is quite safe... we cannot malloc during thread render */
888         if(spos >= str+RW_MAXTEXT)
889                 printf("WARNING! renderwin text beyond limit \n");
890         
891         /* temporal render debug printing, needed for testing orange renders atm... will be gone soon (or option) */
892         if(G.rt==7 && rs->convertdone) {
893                 char str[256];
894                 
895                 spos= str;
896                 spos+= sprintf(spos, "Fra:%d Mem:%.2fM (%.2fM)", G.scene->r.cfra, megs_used_memory, mmap_used_memory);
897                 
898                 if(rs->infostr) {
899                         spos+= sprintf(spos, " | %s", rs->infostr);
900                 }
901                 else {
902                         if(rs->tothalo)
903                                 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);
904                         else 
905                                 spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->totlamp);
906                 }
907                 printf(str); printf("\n");
908         }       
909         
910         
911 }
912
913 /* callback for print info in top header of renderwin */
914 static void renderwin_renderinfo_cb(RenderStats *rs)
915 {
916         
917         if(render_win) {
918                 
919                 make_renderinfo_string(rs, render_win->render_text);
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 }
937
938 /* -------------- callback system to allow ESC from rendering ----------------------- */
939
940 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
941 static int test_break(void)
942 {
943
944         if(G.afbreek==2) { /* code for testing queue */
945
946                 G.afbreek= 0;
947
948                 blender_test_break(); /* tests blender interface */
949
950                 if (G.afbreek==0 && render_win) { /* tests window */
951                         winlay_process_events(0);
952                         // render_win can be closed in winlay_process_events()
953                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
954                                 G.afbreek= 1;
955                         }
956                 }
957         }
958
959         if(G.afbreek==1) return 1;
960         else return 0;
961 }
962
963
964
965 #ifdef _WIN32
966 /* we use the multimedia time here */
967 static UINT uRenderTimerId;
968
969 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
970 {
971         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
972 }
973
974 /* WIN32: init SetTimer callback */
975 static void init_test_break_callback()
976 {
977         timeBeginPeriod(50);
978         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
979 }
980
981 /* WIN32: stop SetTimer callback */
982 static void end_test_break_callback()
983 {
984         timeEndPeriod(50);
985         timeKillEvent(uRenderTimerId);
986 }
987
988 #else
989 /* all other OS's support signal(SIGVTALRM) */
990
991 /* XXX The ESC problem: some unix users reported that ESC doesn't cancel
992  * renders anymore. Most complaints came from linux, but it's not
993  * general, not all linux users have the problem.
994  *
995  * From tests, the systems that do have it are not signalling SIGVTALRM
996  * interrupts (an issue with signals and threads). Using SIGALRM instead
997  * fixes the problem, at least while we investigate better.
998  *
999  * ITIMER_REAL (SIGALRM): timer that counts real system time
1000  * ITIMER_VIRTUAL (SIGVTALRM): only counts time spent in its owner process */
1001
1002 /* POSIX: this function goes in the signal() callback */
1003 static void interruptESC(int sig)
1004 {
1005
1006         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
1007
1008         /* call again, timer was reset */
1009         signal(SIGALRM, interruptESC);
1010 }
1011
1012 /* POSIX: initialize timer and signal */
1013 static void init_test_break_callback()
1014 {
1015
1016         struct itimerval tmevalue;
1017
1018         tmevalue.it_interval.tv_sec = 0;
1019         tmevalue.it_interval.tv_usec = 250000;
1020         /* when the first ? */
1021         tmevalue.it_value.tv_sec = 0;
1022         tmevalue.it_value.tv_usec = 10000;
1023
1024         signal(SIGALRM, interruptESC);
1025         setitimer(ITIMER_REAL, &tmevalue, 0);
1026 }
1027
1028 /* POSIX: stop timer and callback */
1029 static void end_test_break_callback()
1030 {
1031         struct itimerval tmevalue;
1032
1033         tmevalue.it_value.tv_sec = 0;
1034         tmevalue.it_value.tv_usec = 0;
1035
1036         setitimer(ITIMER_REAL, &tmevalue, 0);
1037         signal(SIGALRM, SIG_IGN);
1038
1039 }
1040
1041
1042 #endif
1043
1044
1045
1046 /* -------------- callbacks for render loop: init & run! ----------------------- */
1047
1048
1049 /* - initialize displays
1050    - set callbacks
1051    - cleanup
1052 */
1053
1054 static void do_render(int anim)
1055 {
1056         Render *re= RE_NewRender(G.scene->id.name);
1057         unsigned int lay= G.scene->lay;
1058         int scemode= G.scene->r.scemode;
1059         
1060         /* UGLY! we set this flag to prevent renderwindow queue to execute another render */
1061         /* is reset in RE_BlenderFrame */
1062         G.rendering= 1;
1063
1064         /* set render callbacks, also starts ESC timer */
1065         BIF_init_render_callbacks(re);
1066         
1067         waitcursor(1);
1068         if(render_win) 
1069                 window_set_cursor(render_win->win, CURSOR_WAIT);
1070         
1071         if(G.obedit)
1072                 exit_editmode(0);       /* 0 = no free data */
1073
1074         /* allow localview render for objects with lights in normal layers */
1075         if(curarea->spacetype==SPACE_VIEW3D) {
1076                 if(G.vd->lay & 0xFF000000) {
1077                         G.scene->lay |= G.vd->lay;
1078                         G.scene->r.scemode |= R_SINGLE_LAYER;
1079                 }
1080                 else G.scene->lay= G.vd->lay;
1081         }
1082         
1083         if(anim)
1084                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
1085         else
1086                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
1087
1088         /* restore local view exception */
1089         G.scene->lay= lay;
1090         G.scene->r.scemode= scemode;
1091         
1092         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
1093         
1094         free_filesel_spec(G.scene->r.pic);
1095
1096         G.afbreek= 0;
1097         BIF_end_render_callbacks();
1098         
1099         /* after an envmap creation...  */
1100 //              if(R.flag & R_REDRAW_PRV) {
1101 //                      BIF_preview_changed(ID_TE);
1102 //              }
1103                 
1104         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1105         
1106         waitcursor(0);
1107 }
1108
1109 static void renderwin_store_spare(void)
1110 {
1111         RenderResult rres;
1112         
1113         if(render_win==0 || render_win->storespare==0)
1114                 return;
1115
1116         if(render_win->showspare) {
1117                 render_win->showspare= 0;
1118                 window_set_title(render_win->win, renderwin_get_title(1));
1119         }
1120         
1121         BLI_strncpy(render_win->render_text_spare, render_win->render_text, RW_MAXTEXT);
1122         
1123         if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1124         render_win->rectspare= NULL;
1125         if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1126         render_win->rectsparef= NULL;
1127         
1128         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1129         
1130         if(rres.rect32)
1131                 render_win->rectspare= MEM_dupallocN(rres.rect32);
1132         else if(rres.rectf)
1133                 render_win->rectsparef= MEM_dupallocN(rres.rectf);
1134
1135         render_win->sparex= rres.rectx;
1136         render_win->sparey= rres.recty;
1137 }
1138
1139 /* -------------- API: externally called --------------- */
1140
1141 static void error_cb(char *str){error(str);}
1142 static int esc_timer_set= 0;
1143
1144 /* set callbacks, exported to sequence render too. 
1145    Only call in foreground (UI) renders. */
1146
1147 void BIF_init_render_callbacks(Render *re)
1148 {
1149         
1150         if(G.displaymode!=R_DISPLAYWIN) {
1151                 if(render_win)
1152                         BIF_close_render_display();
1153                 imagewindow_render_callbacks(re);
1154         }
1155         else {
1156                 RE_display_init_cb(re, renderwin_init_display_cb);
1157                 RE_display_draw_cb(re, renderwin_progress_display_cb);
1158                 RE_display_clear_cb(re, renderwin_clear_display_cb);
1159                 RE_stats_draw_cb(re, renderwin_renderinfo_cb);
1160         }
1161         
1162         RE_error_cb(re, error_cb);
1163         
1164         G.afbreek= 0;
1165         /* start esc timer. ensure it happens once only */
1166         if(esc_timer_set==0)
1167                 init_test_break_callback();
1168         esc_timer_set++;
1169         
1170         RE_test_break_cb(re, test_break);
1171         RE_timecursor_cb(re, set_timecursor);
1172         
1173 }
1174
1175 /* the init/end callbacks can be called multiple times (sequence render) */
1176 void BIF_end_render_callbacks(void)
1177 {
1178         esc_timer_set--;
1179         if(esc_timer_set==0)
1180                 end_test_break_callback();
1181         
1182         if(render_win)
1183                 mainwindow_make_active();
1184
1185 }
1186
1187 /* set up display, render an image or scene */
1188 void BIF_do_render(int anim)
1189 {
1190         int slink_flag = 0;
1191
1192         if (G.f & G_DOSCRIPTLINKS) {
1193                 BPY_do_all_scripts(SCRIPT_RENDER);
1194                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1195                         G.f &= ~G_DOSCRIPTLINKS;
1196                         slink_flag = 1;
1197                 }
1198         }
1199         
1200         if(render_win && render_win->showspare)
1201                 renderwin_store_spare();
1202
1203         do_render(anim);
1204
1205         if(G.scene->use_nodes) {
1206                 allqueue(REDRAWNODE, 1);
1207                 allqueue(REDRAWIMAGE, 1);
1208         }
1209         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1210         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1211 }
1212
1213 /* set up display, render the current area view in an image */
1214 /* the RE_Render is only used to make sure we got the picture in the result */
1215 void BIF_do_ogl_render(View3D *v3d, int anim)
1216 {
1217         Render *re= RE_NewRender(G.scene->id.name);
1218         RenderResult *rr;
1219         int winx, winy;
1220         
1221         G.afbreek= 0;
1222         init_test_break_callback();
1223         
1224         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1225         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1226         
1227         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1228         
1229         /* for now, result is defaulting to floats still... */
1230         rr= RE_GetResult(re);
1231         if(rr->rect32==NULL)
1232                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1233         
1234         /* open window */
1235         renderwin_init_display_cb(rr);
1236         init_gl_stuff();
1237         
1238         waitcursor(1);
1239
1240         if(anim) {
1241                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1242                 int cfrao= CFRA;
1243                 
1244                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1245                         mh->start_movie(&G.scene->r, winx, winy);
1246                 
1247                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1248                         drawview3d_render(v3d, winx, winy);
1249                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1250                         window_swap_buffers(render_win->win);
1251                         
1252                         if(BKE_imtype_is_movie(G.scene->r.imtype)) {
1253                                 mh->append_movie(CFRA, rr->rect32, winx, winy);
1254                                 printf("Append frame %d", G.scene->r.cfra);
1255                         }
1256                         else {
1257                                 ImBuf *ibuf= IMB_allocImBuf(winx, winy, G.scene->r.planes, 0, 0);
1258                                 char name[FILE_MAXDIR+FILE_MAXFILE];
1259                                 int ok;
1260                                 
1261                                 BKE_makepicstring(name, (G.scene->r.cfra));
1262
1263                                 ibuf->rect= rr->rect32;    
1264                                 ok= BKE_write_ibuf(ibuf, name, G.scene->r.imtype, G.scene->r.subimtype, G.scene->r.quality);
1265                                 
1266                                 if(ok==0) {
1267                                         printf("Write error: cannot save %s\n", name);
1268                                         break;
1269                                 }
1270                                 else printf("Saved: %s", name);
1271                                 
1272                 /* imbuf knows which rects are not part of ibuf */
1273                                 IMB_freeImBuf(ibuf);    
1274                         }
1275                         /* movie stats prints have no line break */
1276                         printf("\n");
1277                         
1278                         if(test_break()) break;
1279                 }
1280                 
1281                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1282                         mh->end_movie();
1283                 
1284                 CFRA= cfrao;
1285         }
1286         else {
1287                 drawview3d_render(v3d, winx, winy);
1288                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1289                 window_swap_buffers(render_win->win);
1290         }
1291         
1292         renderwin_draw(render_win, 0);
1293
1294         mainwindow_make_active();
1295         
1296         if(anim)
1297                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1298         
1299         end_test_break_callback();
1300         waitcursor(0);
1301 }
1302
1303 void BIF_redraw_render_rect(void)
1304 {
1305         /* redraw */
1306         if (render_win) {
1307                 renderwin_queue_redraw(render_win);
1308         }
1309 }       
1310
1311 void BIF_swap_render_rects(void)
1312 {
1313         RenderResult rres;
1314         
1315         if (render_win==NULL) return;
1316         
1317         render_win->storespare= 1;
1318         render_win->showspare ^= 1;
1319                 
1320         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1321                 
1322         if(render_win->sparex!=rres.rectx || render_win->sparey!=rres.recty) {
1323                 if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1324                 render_win->rectspare= NULL;
1325                 if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1326                 render_win->rectsparef= NULL;
1327         }
1328         
1329         window_set_title(render_win->win, renderwin_get_title(1));
1330
1331         /* redraw */
1332         BIF_redraw_render_rect();
1333
1334 }                               
1335
1336 /* called from usiblender.c too, to free and close renderwin */
1337 void BIF_close_render_display(void)
1338 {
1339         if (render_win) {
1340                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1341                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1342                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1343                 if (render_win->rectspare) MEM_freeN(render_win->rectspare);
1344                 if (render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1345                         
1346                 window_destroy(render_win->win); /* ghost close window */
1347                 MEM_freeN(render_win);
1348
1349                 render_win= NULL;
1350         }
1351 }
1352
1353
1354 /* typical with F11 key, show image or hide/close */
1355 void BIF_toggle_render_display(void) 
1356 {
1357         
1358         if (G.displaymode!=R_DISPLAYWIN) {
1359                 imagewindow_toggle_render();
1360         }
1361         else {
1362                 if (render_win) {
1363                         if(render_win->active) {
1364                                 mainwindow_raise();
1365                                 mainwindow_make_active();
1366                                 render_win->active= 0;
1367                         }
1368                         else {
1369                                 window_raise(render_win->win);
1370                                 window_make_active(render_win->win);
1371                                 render_win->active= 1;
1372                         }
1373                 } 
1374                 else {
1375                         RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name));
1376                         if(rr) renderwin_init_display_cb(rr);
1377                 }
1378         }
1379 }
1380
1381 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1382 {
1383         if (render_win) {
1384                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1385         }
1386 }