Two bugfixes for Image commit:
[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         Render *re= RE_NewRender(G.scene->id.name);
1061         unsigned int lay= G.scene->lay;
1062         int scemode= G.scene->r.scemode;
1063         int sculptmode= G.f & G_SCULPTMODE;
1064         
1065         /* UGLY! we set this flag to prevent renderwindow queue to execute another render */
1066         /* is reset in RE_BlenderFrame */
1067         G.rendering= 1;
1068
1069         /* set render callbacks, also starts ESC timer */
1070         BIF_init_render_callbacks(re, 1);
1071         
1072         waitcursor(1);
1073         if(render_win) 
1074                 window_set_cursor(render_win->win, CURSOR_WAIT);
1075         
1076         if(G.obedit)
1077                 exit_editmode(0);       /* 0 = no free data */
1078
1079         if(sculptmode) set_sculptmode();
1080
1081         /* allow localview render for objects with lights in normal layers */
1082         if(curarea->spacetype==SPACE_VIEW3D) {
1083                 if(G.vd->lay & 0xFF000000) {
1084                         G.scene->lay |= G.vd->lay;
1085                         G.scene->r.scemode |= R_SINGLE_LAYER;
1086                 }
1087                 else G.scene->lay= G.vd->lay;
1088         }
1089         
1090         if(anim)
1091                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
1092         else
1093                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
1094
1095         /* restore local view exception */
1096         G.scene->lay= lay;
1097         G.scene->r.scemode= scemode;
1098         
1099         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
1100         
1101         free_filesel_spec(G.scene->r.pic);
1102
1103         G.afbreek= 0;
1104         BIF_end_render_callbacks();
1105         
1106         /* after an envmap creation...  */
1107 //              if(R.flag & R_REDRAW_PRV) {
1108 //                      BIF_preview_changed(ID_TE);
1109 //              }
1110                 
1111         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1112
1113         if(sculptmode) set_sculptmode();
1114         
1115         waitcursor(0);
1116 }
1117
1118 /* called before render, store old render in spare buffer */
1119 static void renderwin_store_spare(void)
1120 {
1121         RenderResult rres;
1122         
1123         if(render_win==0 || render_win->storespare==0)
1124                 return;
1125         
1126         /* only store when it does not show spare */
1127         if(render_win->showspare==0)
1128                 return;
1129         
1130         render_win->showspare= 0;
1131         window_set_title(render_win->win, renderwin_get_title(1));
1132         
1133         BLI_strncpy(render_win->render_text_spare, render_win->render_text, RW_MAXTEXT);
1134         
1135         if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1136         render_win->rectspare= NULL;
1137         if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1138         render_win->rectsparef= NULL;
1139         
1140         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1141         
1142         if(rres.rect32)
1143                 render_win->rectspare= MEM_dupallocN(rres.rect32);
1144         else if(rres.rectf)
1145                 render_win->rectsparef= MEM_dupallocN(rres.rectf);
1146
1147         render_win->sparex= rres.rectx;
1148         render_win->sparey= rres.recty;
1149 }
1150
1151 /* -------------- API: externally called --------------- */
1152
1153 static void error_cb(char *str){error(str);}
1154 static int esc_timer_set= 0;
1155
1156 /* set callbacks, exported to sequence render too. 
1157    Only call in foreground (UI) renders. */
1158
1159 void BIF_init_render_callbacks(Render *re, int do_display)
1160 {
1161         if(do_display) {
1162                 if(G.displaymode!=R_DISPLAYWIN) {
1163                         if(render_win)
1164                                 BIF_close_render_display();
1165                         imagewindow_render_callbacks(re);
1166                 }
1167                 else {
1168                         RE_display_init_cb(re, renderwin_init_display_cb);
1169                         RE_display_draw_cb(re, renderwin_progress_display_cb);
1170                         RE_display_clear_cb(re, renderwin_clear_display_cb);
1171                         RE_stats_draw_cb(re, renderwin_renderinfo_cb);
1172                 }
1173         }
1174         
1175         RE_error_cb(re, error_cb);
1176         
1177         G.afbreek= 0;
1178         if(render_win)
1179                 render_win->flags &= ~RW_FLAGS_ESCAPE;
1180
1181         /* start esc timer. ensure it happens once only */
1182         if(esc_timer_set==0)
1183                 init_test_break_callback();
1184         esc_timer_set++;
1185         
1186         RE_test_break_cb(re, test_break);
1187         RE_timecursor_cb(re, set_timecursor);
1188         
1189 }
1190
1191 /* the init/end callbacks can be called multiple times (sequence render) */
1192 void BIF_end_render_callbacks(void)
1193 {
1194         esc_timer_set--;
1195         if(esc_timer_set==0) {
1196                 end_test_break_callback();
1197                 
1198                 if(render_win)
1199                         mainwindow_make_active();
1200         }
1201 }
1202
1203 void BIF_store_spare(void)
1204 {
1205         if(render_win)
1206                 renderwin_store_spare();
1207         else
1208                 imagewin_store_spare();
1209 }
1210
1211 /* set up display, render an image or scene */
1212 void BIF_do_render(int anim)
1213 {
1214         int slink_flag = 0;
1215
1216         if (G.f & G_DOSCRIPTLINKS) {
1217                 BPY_do_all_scripts(SCRIPT_RENDER);
1218                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1219                         G.f &= ~G_DOSCRIPTLINKS;
1220                         slink_flag = 1;
1221                 }
1222         }
1223         
1224         BIF_store_spare();
1225
1226         do_render(anim);
1227
1228         if(G.scene->use_nodes) {
1229                 allqueue(REDRAWNODE, 1);
1230                 allqueue(REDRAWIMAGE, 1);
1231         }
1232         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1233         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1234 }
1235
1236 /* set up display, render the current area view in an image */
1237 /* the RE_Render is only used to make sure we got the picture in the result */
1238 void BIF_do_ogl_render(View3D *v3d, int anim)
1239 {
1240         Render *re= RE_NewRender(G.scene->id.name);
1241         RenderResult *rr;
1242         int winx, winy;
1243         
1244         G.afbreek= 0;
1245         init_test_break_callback();
1246         
1247         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1248         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1249         
1250         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1251         
1252         /* for now, result is defaulting to floats still... */
1253         rr= RE_GetResult(re);
1254         if(rr->rect32==NULL)
1255                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1256         
1257         /* open window */
1258         renderwin_init_display_cb(rr);
1259         if(render_win)
1260                 render_win->flags &= ~RW_FLAGS_ESCAPE;
1261
1262         init_gl_stuff();
1263         
1264         waitcursor(1);
1265
1266         if(anim) {
1267                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1268                 int cfrao= CFRA;
1269                 
1270                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1271                         mh->start_movie(&G.scene->r, winx, winy);
1272                 
1273                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1274                         /* user event can close window */
1275                         if(render_win==NULL)
1276                                 break;
1277                         drawview3d_render(v3d, winx, winy);
1278                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1279                         window_swap_buffers(render_win->win);
1280                         
1281                         if(BKE_imtype_is_movie(G.scene->r.imtype)) {
1282                                 mh->append_movie(CFRA, rr->rect32, winx, winy);
1283                                 printf("Append frame %d", G.scene->r.cfra);
1284                         }
1285                         else {
1286                                 ImBuf *ibuf= IMB_allocImBuf(winx, winy, G.scene->r.planes, 0, 0);
1287                                 char name[FILE_MAXDIR+FILE_MAXFILE];
1288                                 int ok;
1289                                 
1290                                 BKE_makepicstring(name, G.scene->r.pic, G.scene->r.cfra, G.scene->r.imtype);
1291
1292                                 ibuf->rect= rr->rect32;    
1293                                 ok= BKE_write_ibuf(ibuf, name, G.scene->r.imtype, G.scene->r.subimtype, G.scene->r.quality);
1294                                 
1295                                 if(ok==0) {
1296                                         printf("Write error: cannot save %s\n", name);
1297                                         break;
1298                                 }
1299                                 else printf("Saved: %s", name);
1300                                 
1301                 /* imbuf knows which rects are not part of ibuf */
1302                                 IMB_freeImBuf(ibuf);    
1303                         }
1304                         /* movie stats prints have no line break */
1305                         printf("\n");
1306                         
1307                         if(test_break()) break;
1308                 }
1309                 
1310                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1311                         mh->end_movie();
1312                 
1313                 CFRA= cfrao;
1314         }
1315         else {
1316                 drawview3d_render(v3d, winx, winy);
1317                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1318                 window_swap_buffers(render_win->win);
1319         }
1320         
1321         if(render_win)
1322                 renderwin_draw(render_win, 0);
1323
1324         mainwindow_make_active();
1325         
1326         if(anim)
1327                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1328         
1329         end_test_break_callback();
1330         waitcursor(0);
1331 }
1332
1333 void BIF_redraw_render_rect(void)
1334 {
1335         /* redraw */
1336         if (render_win) {
1337                 renderwin_queue_redraw(render_win);
1338         }
1339         else {
1340                 allqueue(REDRAWIMAGE, 0);
1341         }
1342 }       
1343
1344 void BIF_swap_render_rects(void)
1345 {
1346         RenderResult rres;
1347         
1348         if(G.displaymode!=R_DISPLAYWIN) {
1349                 imagewindow_swap_render_rects();
1350         }
1351         else if(render_win) {
1352                 
1353                 render_win->storespare= 1;
1354                 render_win->showspare ^= 1;
1355                         
1356                 RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1357                         
1358                 if(render_win->sparex!=rres.rectx || render_win->sparey!=rres.recty) {
1359                         if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1360                         render_win->rectspare= NULL;
1361                         if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1362                         render_win->rectsparef= NULL;
1363                 }
1364                 
1365                 window_set_title(render_win->win, renderwin_get_title(1));
1366         }
1367         
1368         /* redraw */
1369         BIF_redraw_render_rect();
1370
1371 }                               
1372
1373 /* called from usiblender.c too, to free and close renderwin */
1374 void BIF_close_render_display(void)
1375 {
1376         if (render_win) {
1377                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1378                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1379                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1380                 if (render_win->rectspare) MEM_freeN(render_win->rectspare);
1381                 if (render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1382                         
1383                 window_destroy(render_win->win); /* ghost close window */
1384                 MEM_freeN(render_win);
1385
1386                 render_win= NULL;
1387         }
1388 }
1389
1390
1391 /* typical with F11 key, show image or hide/close */
1392 void BIF_toggle_render_display(void) 
1393 {
1394         
1395         if (G.displaymode!=R_DISPLAYWIN) {
1396                 imagewindow_toggle_render();
1397         }
1398         else {
1399                 if (render_win) {
1400                         if(render_win->active) {
1401                                 mainwindow_raise();
1402                                 mainwindow_make_active();
1403                                 render_win->active= 0;
1404                         }
1405                         else {
1406                                 window_raise(render_win->win);
1407                                 window_make_active(render_win->win);
1408                                 render_win->active= 1;
1409                         }
1410                 } 
1411                 else {
1412                         RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name));
1413                         if(rr) renderwin_init_display_cb(rr);
1414                 }
1415         }
1416 }
1417
1418 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1419 {
1420         if (render_win) {
1421                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1422         }
1423 }