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