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