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