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