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