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