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