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