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