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