Fix proposed by Brecht; on redraw events to renderwindow, it sets back
[blender.git] / source / blender / src / renderwin.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #ifdef WIN32
34 /* for the multimedia timer */
35 #include <windows.h>
36 #include <mmsystem.h>
37 #endif
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <math.h>
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #ifdef WIN32
48 #include "BLI_winstuff.h"
49 #else
50  /* for signal callback, not (fully) supported at windows */
51 #include <sys/time.h>
52 #include <signal.h>
53
54 #endif
55
56 #include "BLI_blenlib.h"
57
58 #include "MEM_guardedalloc.h"
59
60 #include "BMF_Api.h"
61
62 #include "DNA_view3d_types.h"
63 #include "DNA_screen_types.h"
64 #include "DNA_vec_types.h"
65
66 #include "BKE_global.h"
67 #include "BKE_utildefines.h"
68
69 #include "BIF_gl.h"
70 #include "BIF_glutil.h"
71 #include "BIF_graphics.h"
72 #include "BIF_screen.h"
73 #include "BIF_space.h"
74 #include "BIF_mywindow.h"
75 #include "BIF_previewrender.h"
76 #include "BIF_renderwin.h"
77 #include "BIF_resources.h"
78 #include "BIF_toets.h"
79
80 #include "BDR_editobject.h"
81
82 #include "BSE_view.h"
83 #include "BSE_drawview.h"
84 #include "BSE_filesel.h"
85 #include "BSE_headerbuttons.h"
86
87 #include "blendef.h"
88 #include "mydevice.h"
89 #include "winlay.h"
90 #include "render.h"
91
92 /* ------------ renderwin struct, to prevent too much global vars --------- */
93 /* ------------ only used for display in a 2nd window  --------- */
94
95
96 /* flags escape presses during event handling
97 * so we can test for user break later.
98 */
99 #define RW_FLAGS_ESCAPE         (1<<0)
100 /* old zoom style (2x, locked to mouse, exits
101 * when mouse leaves window), to be removed
102 * at some point.
103 */
104 #define RW_FLAGS_OLDZOOM                (1<<1)
105 /* on when image is being panned with middlemouse
106 */
107 #define RW_FLAGS_PANNING                (1<<2)
108 /* on when the mouse is dragging over the image
109 * to examine pixel values.
110 */
111 #define RW_FLAGS_PIXEL_EXAMINING        (1<<3)
112
113 /* forces draw of alpha */
114 #define RW_FLAGS_ALPHA          (1<<4)
115
116 /* space for info text */
117 #define RW_HEADERY              18
118
119 typedef struct {
120         Window *win;
121
122         float zoom, zoomofs[2];
123         int active;
124         
125         int mbut[3];
126         int lmouse[2];
127         
128         unsigned int flags;
129         
130         float pan_mouse_start[2], pan_ofs_start[2];
131
132         char *info_text;
133         char *render_text, *render_text_spare;
134         
135 } RenderWin;
136
137 static RenderWin *render_win= NULL;
138
139 /* --------------- help functions for RenderWin struct ---------------------------- */
140
141
142 /* only called in function open_renderwin */
143 static RenderWin *renderwin_alloc(Window *win)
144 {
145         RenderWin *rw= MEM_mallocN(sizeof(*rw), "RenderWin");
146         rw->win= win;
147         rw->zoom= 1.0;
148         rw->active= 0;
149         rw->flags= 0;
150         rw->zoomofs[0]= rw->zoomofs[1]= 0;
151         rw->info_text= NULL;
152         rw->render_text= rw->render_text_spare= NULL;
153
154         rw->lmouse[0]= rw->lmouse[1]= 0;
155         rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= 0;
156
157         return rw;
158 }
159
160
161 static void renderwin_queue_redraw(RenderWin *rw)
162 {
163         window_queue_redraw(rw->win); // to ghost
164 }
165
166 static void renderwin_reshape(RenderWin *rw)
167 {
168         ;
169 }
170
171 static void renderwin_get_disprect(RenderWin *rw, float disprect_r[2][2])
172 {
173         float display_w, display_h;
174         float cent_x, cent_y;
175         int w, h;
176
177         window_get_size(rw->win, &w, &h);
178         h-= RW_HEADERY;
179
180         display_w= R.rectx*rw->zoom;
181         display_h= R.recty*rw->zoom;
182         cent_x= (rw->zoomofs[0] + R.rectx/2)*rw->zoom;
183         cent_y= (rw->zoomofs[1] + R.recty/2)*rw->zoom;
184         
185         disprect_r[0][0]= w/2 - cent_x;
186         disprect_r[0][1]= h/2 - cent_y;
187         disprect_r[1][0]= disprect_r[0][0] + display_w;
188         disprect_r[1][1]= disprect_r[0][1] + display_h;
189 }
190
191         /** 
192          * Project window coordinate to image pixel coordinate.
193          * Returns true if resulting coordinate is within image.
194          */
195 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
196 {
197         float disprect[2][2];
198         
199         renderwin_get_disprect(rw, disprect);
200         
201         imgco_r[0]= (int) ((winco[0]-disprect[0][0])/rw->zoom);
202         imgco_r[1]= (int) ((winco[1]-disprect[0][1])/rw->zoom);
203         
204         return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<R.rectx && imgco_r[1]<R.recty);
205 }
206
207         /**
208          * Project window coordinates to normalized device coordinates
209          * Returns true if resulting coordinate is within window.
210          */
211 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
212 {
213         int w, h;
214
215         window_get_size(rw->win, &w, &h);
216         h-= RW_HEADERY;
217
218         ndc_r[0]=  ((float)(win_co[0]*2)/(w-1) - 1.0);
219         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0);
220
221         return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
222 }
223
224 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
225 {
226         if (rw->info_text) MEM_freeN(rw->info_text);
227         rw->info_text= info_text?BLI_strdup(info_text):NULL;
228 }
229
230 static void renderwin_reset_view(RenderWin *rw)
231 {
232         int w, h, rectx, recty;
233
234         if (rw->info_text) renderwin_set_infotext(rw, NULL);
235
236         /* now calculate a zoom for when image is larger than window */
237         window_get_size(rw->win, &w, &h);
238         h-= RW_HEADERY;
239         
240                 /* at this point the r.rectx/y values are not correct yet */
241         rectx= (G.scene->r.size*G.scene->r.xsch)/100;
242         recty= (G.scene->r.size*G.scene->r.ysch)/100;
243
244         if(rectx>w || recty>h) {
245                 if(rectx-w > recty-h) rw->zoom= ((float)w)/((float)rectx);
246                 else rw->zoom= ((float)h)/((float)recty);
247         }
248         else rw->zoom= 1.0;
249
250         rw->zoomofs[0]= rw->zoomofs[1]= 0;
251         renderwin_queue_redraw(rw);
252 }
253
254 static void renderwin_draw_render_info(RenderWin *rw)
255 {
256         /* render text is added to top */
257         if(RW_HEADERY) {
258                 float colf[3];
259                 rcti rect;
260                 
261                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
262                 rect.xmin= 0;
263                 rect.ymin= rect.ymax-RW_HEADERY;
264                 glEnable(GL_SCISSOR_TEST);
265                 glaDefine2DArea(&rect);
266                 
267                 /* clear header rect */
268                 BIF_SetTheme(NULL);     // sets view3d theme by default
269                 BIF_GetThemeColor3fv(TH_HEADER, colf);
270                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
271                 glClear(GL_COLOR_BUFFER_BIT);
272                 
273                 if(rw->render_text) {
274                         BIF_ThemeColor(TH_TEXT);
275                         glRasterPos2i(12, 5);
276                         BMF_DrawString(G.fonts, rw->render_text);
277                 }
278         }       
279         
280 }
281
282 static void renderwin_draw(RenderWin *rw, int just_clear)
283 {
284         float disprect[2][2];
285         int set_back_mainwindow;
286         rcti rect;
287         
288         /* since renderwin uses callbacks (controlled by ghost) it can
289            mess up active window output with redraw events after a render. 
290            this is patchy, still WIP */
291         set_back_mainwindow = (winlay_get_active_window() != rw->win);
292         window_make_active(rw->win);
293         
294         rect.xmin= rect.ymin= 0;
295         window_get_size(rw->win, &rect.xmax, &rect.ymax);
296         rect.ymax-= RW_HEADERY;
297         
298         renderwin_get_disprect(rw, disprect);
299         
300         /* do this first, so window ends with correct scissor */
301         renderwin_draw_render_info(rw);
302         
303         glEnable(GL_SCISSOR_TEST);
304         glaDefine2DArea(&rect);
305         
306         glClearColor(.1875, .1875, .1875, 1.0); 
307         glClear(GL_COLOR_BUFFER_BIT);
308
309         if (just_clear || !R.rectot) {
310                 glColor3ub(0, 0, 0);
311                 glRectfv(disprect[0], disprect[1]);
312         } else {
313                 glPixelZoom(rw->zoom, rw->zoom);
314                 if(rw->flags & RW_FLAGS_ALPHA) {
315                         char *rect= (char *)R.rectot;
316                         
317                         glColorMask(1, 0, 0, 0);
318                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+3);
319                         glColorMask(0, 1, 0, 0);
320                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+2);
321                         glColorMask(0, 0, 1, 0);
322                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+1);
323                         glColorMask(1, 1, 1, 1);
324                         
325                 }
326                 else {
327                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, R.rectot);
328                 }
329                 glPixelZoom(1.0, 1.0);
330         }
331         
332         /* info text is overlayed on bottom */
333         if (rw->info_text) {
334                 float w;
335                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
336                 glEnable(GL_BLEND);
337                 w=186.0*strlen(rw->info_text)/30;
338                 glColor4f(.5,.5,.5,.25);
339                 glRectf(0.0,0.0,w,30.0);
340                 glDisable(GL_BLEND);
341                 glColor3ub(255, 255, 255);
342                 glRasterPos2i(10, 10);
343                 BMF_DrawString(G.font, rw->info_text);
344         }
345         
346         window_swap_buffers(rw->win);
347         
348         if (set_back_mainwindow) mainwindow_make_active();      
349 }
350
351 /* ------ interactivity calls for RenderWin ------------- */
352
353 static void renderwin_mouse_moved(RenderWin *rw)
354 {
355         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
356                 int imgco[2];
357                 char buf[64];
358
359                 if (R.rectot && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
360                         unsigned char *pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
361
362                         sprintf(buf, "R: %d, G: %d, B: %d, A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);
363                         renderwin_set_infotext(rw, buf);
364                         renderwin_queue_redraw(rw);
365                 } else {
366                         renderwin_set_infotext(rw, NULL);
367                         renderwin_queue_redraw(rw);
368                 }
369         } 
370         else if (rw->flags & RW_FLAGS_PANNING) {
371                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
372                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
373         
374                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
375                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
376                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -R.rectx/2, R.rectx/2);
377                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -R.recty/2, R.recty/2);
378
379                 renderwin_queue_redraw(rw);
380         } 
381         else if (rw->flags & RW_FLAGS_OLDZOOM) {
382                 float ndc[2];
383                 int w, h;
384
385                 window_get_size(rw->win, &w, &h);
386                 h-= RW_HEADERY;
387                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
388
389                 rw->zoomofs[0]= -0.5*ndc[0]*(w-R.rectx*rw->zoom)/rw->zoom;
390                 rw->zoomofs[1]= -0.5*ndc[1]*(h-R.recty*rw->zoom)/rw->zoom;
391
392                 renderwin_queue_redraw(rw);
393         }
394 }
395
396 static void renderwin_mousebut_changed(RenderWin *rw)
397 {
398         if (rw->mbut[0]) {
399                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
400         } 
401         else if (rw->mbut[1]) {
402                 rw->flags|= RW_FLAGS_PANNING;
403                 rw->pan_mouse_start[0]= rw->lmouse[0];
404                 rw->pan_mouse_start[1]= rw->lmouse[1];
405                 rw->pan_ofs_start[0]= rw->zoomofs[0];
406                 rw->pan_ofs_start[1]= rw->zoomofs[1];
407         } 
408         else {
409                 if (rw->flags & RW_FLAGS_PANNING) {
410                         rw->flags &= ~RW_FLAGS_PANNING;
411                         renderwin_queue_redraw(rw);
412                 }
413                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
414                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
415                         renderwin_set_infotext(rw, NULL);
416                         renderwin_queue_redraw(rw);
417                 }
418         }
419 }
420
421
422 /* handler for renderwin, passed on to Ghost */
423 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
424 {
425         RenderWin *rw= user_data;
426
427         // added this for safety, while render it's just creating bezerk results
428         if(R.flag & R_RENDERING) {
429                 if(evt==ESCKEY && val) 
430                         rw->flags|= RW_FLAGS_ESCAPE;
431                 return;
432         }
433         
434         if (evt==RESHAPE) {
435                 renderwin_reshape(rw);
436         } 
437         else if (evt==REDRAW) {
438                 renderwin_draw(rw, 0);
439         } 
440         else if (evt==WINCLOSE) {
441                 BIF_close_render_display();
442         } 
443         else if (evt==INPUTCHANGE) {
444                 rw->active= val;
445
446                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
447                         rw->flags&= ~RW_FLAGS_OLDZOOM;
448                         renderwin_reset_view(rw);
449                 }
450         } 
451         else if (ELEM(evt, MOUSEX, MOUSEY)) {
452                 rw->lmouse[evt==MOUSEY]= val;
453                 renderwin_mouse_moved(rw);
454         } 
455         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
456                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
457                 rw->mbut[which]= val;
458                 renderwin_mousebut_changed(rw);
459         } 
460         else if (val) {
461                 if (evt==ESCKEY) {
462                         if (rw->flags&RW_FLAGS_OLDZOOM) {
463                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
464                                 renderwin_reset_view(rw);
465                         } 
466                         else {
467                                 rw->flags|= RW_FLAGS_ESCAPE;
468                                 mainwindow_raise();
469                                 mainwindow_make_active();
470                                 rw->active= 0;
471                         }
472                 } 
473                 else if( evt==AKEY) {
474                         rw->flags ^= RW_FLAGS_ALPHA;
475                         renderwin_queue_redraw(render_win);
476                 }
477                 else if (evt==JKEY) {
478                         if(R.flag==0) BIF_swap_render_rects();
479                 } 
480                 else if (evt==ZKEY) {
481                         if (rw->flags&RW_FLAGS_OLDZOOM) {
482                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
483                                 renderwin_reset_view(rw);
484                         } else {
485                                 rw->zoom= 2.0;
486                                 rw->flags|= RW_FLAGS_OLDZOOM;
487                                 renderwin_mouse_moved(rw);
488                         }
489                 } 
490                 else if (evt==PADPLUSKEY) {
491                         if (rw->zoom<15.9) {
492                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
493                                 else rw->zoom*= 2.0;
494                                 renderwin_queue_redraw(rw);
495                         }
496                 } 
497                 else if (evt==PADMINUS) {
498                         if (rw->zoom>0.26) {
499                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
500                                 else rw->zoom*= 0.5;
501                                 renderwin_queue_redraw(rw);
502                         }
503                 } 
504                 else if (evt==PADENTER || evt==HOMEKEY) {
505                         if (rw->flags&RW_FLAGS_OLDZOOM) {
506                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
507                         }
508                         renderwin_reset_view(rw);
509                 } 
510                 else if (evt==F3KEY) {
511                         if(R.flag==0) {
512                                 mainwindow_raise();
513                                 mainwindow_make_active();
514                                 rw->active= 0;
515                                 areawinset(find_biggest_area()->win);
516                                 BIF_save_rendered_image();
517                         }
518                 } 
519                 else if (evt==F11KEY) {
520                         BIF_toggle_render_display();
521                 } 
522                 else if (evt==F12KEY) {
523                         /* if it's rendering, this flag is set */
524                         if(R.flag==0) BIF_do_render(0);
525                 }
526         }
527 }
528
529 static char *renderwin_get_title(int doswap)
530 {
531         static int swap= 0;
532         char *title="";
533         
534         swap+= doswap;
535         
536         if(swap & 1) {
537                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
538                 else title = "Blender:Render (spare)";
539         }
540         else {
541                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
542                 else title = "Blender:Render";
543         }
544
545         return title;
546 }
547
548 /* opens window and allocs struct */
549 static void open_renderwin(int winpos[2], int winsize[2])
550 {
551         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
552         Window *win;
553         char *title;
554         
555         title= renderwin_get_title(0);  /* 0 = no swap */
556         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
557
558         render_win= renderwin_alloc(win);
559
560         /* Ghost calls handler */
561         window_set_handler(win, renderwin_handler, render_win);
562
563         winlay_process_events(0);
564         window_make_active(render_win->win);
565         winlay_process_events(0);
566         
567         /* mywindow has to know about it too */
568         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
569         /* and we should be able to draw 3d in it */
570         init_gl_stuff();
571         
572         renderwin_draw(render_win, 1);
573         renderwin_draw(render_win, 1);
574 }
575
576 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
577
578 /* calculations for window size and position */
579 void calc_renderwin_rectangle(int posmask, int renderpos_r[2], int rendersize_r[2]) 
580 {
581         int scr_w, scr_h, x, y, div= 0;
582         float ndc_x= 0.0, ndc_y= 0.0;
583
584                 /* XXX, we temporarily hack the screen size and position so
585                  * the window is always 60 pixels away from a side, really need
586                  * a GHOST_GetMaxWindowBounds or so - zr
587                  */
588         winlay_get_screensize(&scr_w, &scr_h);
589         
590         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
591         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
592         if(G.scene->r.mode & R_PANORAMA) {
593                 rendersize_r[0]*= G.scene->r.xparts;
594                 rendersize_r[1]*= G.scene->r.yparts;
595         }
596         /* increased size of clipping for OSX, should become an option instead */
597         rendersize_r[0]= CLAMPIS(rendersize_r[0], 100, scr_w-120);
598         rendersize_r[1]= CLAMPIS(rendersize_r[1], 100, scr_h-120);
599
600         for (y=-1; y<=1; y++) {
601                 for (x=-1; x<=1; x++) {
602                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
603                                 ndc_x+= x;
604                                 ndc_y+= y;
605                                 div++;
606                         }
607                 }
608         }
609
610         if (div) {
611                 ndc_x/= div;
612                 ndc_y/= div;
613         }
614                 
615         renderpos_r[0]= 60 + (scr_w-90-rendersize_r[0])*(ndc_x*0.5 + 0.5);
616         renderpos_r[1]= 30 + (scr_h-90-rendersize_r[1])*(ndc_y*0.5 + 0.5);
617 }
618         
619 /* init renderwin, alloc/open/resize */
620 static void renderwin_init_display_cb(void) 
621 {
622         if (G.afbreek == 0) {
623                 int rendersize[2], renderpos[2];
624
625                 calc_renderwin_rectangle(G.winpos, renderpos, rendersize);
626
627                 if (!render_win) {
628                         open_renderwin(renderpos, rendersize);
629                         renderwin_reset_view(render_win); // incl. autozoom for large images
630                 } else {
631                         int win_x, win_y;
632                         int win_w, win_h;
633
634                         window_get_position(render_win->win, &win_x, &win_y);
635                         window_get_size(render_win->win, &win_w, &win_h);
636                         win_h-= RW_HEADERY;
637
638                                 /* XXX, this is nasty and I guess bound to cause problems,
639                                  * but to ensure the window is at the user specified position
640                                  * and size we reopen the window all the time... we need
641                                  * a ghost _set_position to fix this -zr
642                                  */
643                                  
644                                  /* XXX, well... it is nasty yes, and reopens windows each time on
645                                     subsequent renders. Better rule is to make it reopen only only
646                                     size change, and use the preferred position only on open_renderwin
647                                         cases (ton)
648                                  */
649                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
650                                 BIF_close_render_display();
651                                 open_renderwin(renderpos, rendersize);
652                         }
653                         else {
654                                 window_raise(render_win->win);
655                                 window_make_active(render_win->win);
656                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes
657                         }
658
659                         renderwin_reset_view(render_win);
660                         render_win->flags&= ~RW_FLAGS_ESCAPE;
661                         render_win->active= 1;
662                 }
663                 /* make sure we are in normal draw again */
664                 render_win->flags &= ~RW_FLAGS_ALPHA;
665         }
666 }
667
668 /* callback for redraw render win */
669 static void renderwin_clear_display_cb(short ignore) 
670 {
671         if (render_win) {
672                 window_make_active(render_win->win);    
673                 renderwin_draw(render_win, 1);
674         }
675 }
676
677 /* XXX, this is not good, we do this without any regard to state
678 * ... better is to make this an optimization of a more clear
679 * implementation. the bug shows up when you do something like
680 * open the window, then draw part of the progress, then get
681 * a redraw event. whatever can go wrong will. -zr
682 *
683 * Note: blocked queue handling while rendering to prevent that (ton)
684 */
685
686 /* in render window; display a couple of scanlines of rendered image (see callback below) */
687 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
688 {
689         float disprect[2][2];
690         rcti win_rct;
691
692         win_rct.xmin= win_rct.ymin= 0;
693         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
694         win_rct.ymax-= RW_HEADERY;
695         
696         renderwin_get_disprect(rw, disprect);
697         
698         /* for efficiency & speed; not drawing in Blender UI while rendering */
699         //window_make_active(rw->win);
700
701         glEnable(GL_SCISSOR_TEST);
702         glaDefine2DArea(&win_rct);
703
704         glDrawBuffer(GL_FRONT);
705         glPixelZoom(rw->zoom, rw->zoom);
706         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
707         glPixelZoom(1.0, 1.0);
708         glFlush();
709         glDrawBuffer(GL_BACK);
710 }
711
712
713 /* in render window; display a couple of scanlines of rendered image */
714 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
715 {
716         if (render_win) {
717                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
718         }
719 }
720
721
722 /* -------------- callbacks for render loop: in View3D ----------------------- */
723
724
725 static View3D *render_view3d = NULL;
726
727 /* init Render view callback */
728 static void renderview_init_display_cb(void)
729 {
730         ScrArea *sa;
731
732                 /* Choose the first view with a persp camera,
733                  * if one doesn't exist we will get the first
734                  * View3D window.
735                  */ 
736         render_view3d= NULL;
737         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
738                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
739                         View3D *vd= sa->spacedata.first;
740                         
741                         if (vd->persp==2 && vd->camera==G.scene->camera) {
742                                 render_view3d= vd;
743                                 break;
744                         } else if (!render_view3d) {
745                                 render_view3d= vd;
746                         }
747                 }
748         }
749 }
750
751
752 /* in 3d view; display a couple of scanlines of rendered image */
753 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
754 {
755         if (render_view3d) {
756                 View3D *v3d= render_view3d;
757                 int nlines= y2-y1+1;
758                 float sx, sy, facx, facy;
759                 rcti win_rct, vb;
760
761                 calc_viewborder(v3d, &vb);
762
763                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
764                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
765
766                 bwin_get_rect(v3d->area->win, &win_rct);
767
768                 glaDefine2DArea(&win_rct);
769         
770                 glDrawBuffer(GL_FRONT);
771                 
772                 sx= vb.xmin;
773                 sy= vb.ymin + facy*y1;
774
775                 glPixelZoom(facx, facy);
776                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
777                 glPixelZoom(1.0, 1.0);
778
779                 glFlush();
780                 glDrawBuffer(GL_BACK);
781                 v3d->flag |= V3D_DISPIMAGE;
782                 v3d->area->win_swap= WIN_FRONT_OK;
783                 
784         }
785 }
786
787 /* in 3d view; display stats of rendered image */
788 static void renderview_draw_render_info(char *str)
789 {
790         if (render_view3d) {
791                 View3D *v3d= render_view3d;
792                 rcti vb, win_rct;
793                 
794                 calc_viewborder(v3d, &vb);
795                 
796                 bwin_get_rect(v3d->area->win, &win_rct);
797                 glaDefine2DArea(&win_rct);
798                 
799                 glDrawBuffer(GL_FRONT);
800                 
801                 /* clear header rect */
802                 BIF_ThemeColor(TH_HEADER);
803                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
804                 
805                 if(str) {
806                         BIF_ThemeColor(TH_TEXT);
807                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
808                         BMF_DrawString(G.fonts, str);
809                 }
810                 
811                 glFlush();
812                 glDrawBuffer(GL_BACK);
813
814                 v3d->area->win_swap= WIN_FRONT_OK;
815                 
816         }
817 }
818
819
820 /* -------------- callbacks for render loop: interactivity ----------------------- */
821
822
823 /* callback for print info in top header of renderwin */
824 /* time is only not zero on last call, we then don't update the other stats */ 
825 static void printrenderinfo_cb(double time, int sample)
826 {
827         extern int mem_in_use;
828         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
829         static float megs_used_memory=0.0;
830         char str[300], *spos= str;
831                 
832         if(time==0.0) {
833                 megs_used_memory= mem_in_use/(1024.0*1024.0);
834                 totvert= R.totvert;
835                 totvlak= R.totvlak;
836                 totlamp= R.totlamp;
837                 tothalo= R.tothalo;
838         }
839         
840         if(tothalo)
841                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, tothalo, totlamp, megs_used_memory);
842         else 
843                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
844
845         if(time==0.0) {
846                 if (R.r.mode & R_FIELDS) {
847                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
848                 }
849                 if (sample!=-1) {
850                         spos+= sprintf(spos, "Sample: %d    ", sample);
851                 }
852         }
853         else {
854                 extern char info_time_str[32];  // header_info.c
855                 timestr(time, info_time_str);
856                 spos+= sprintf(spos, " Time:%s ", info_time_str);
857         }
858         
859         if(render_win) {
860                 if(render_win->render_text) MEM_freeN(render_win->render_text);
861                 render_win->render_text= BLI_strdup(str);
862                 glDrawBuffer(GL_FRONT);
863                 renderwin_draw_render_info(render_win);
864                 glFlush();
865                 glDrawBuffer(GL_BACK);
866         }
867         else renderview_draw_render_info(str);
868 }
869
870 /* -------------- callback system to allow ESC from rendering ----------------------- */
871
872 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
873 static int test_break(void)
874 {
875
876         if(G.afbreek==2) { /* code for testing queue */
877
878                 G.afbreek= 0;
879
880                 blender_test_break(); /* tests blender interface */
881
882                 if (G.afbreek==0 && render_win) { /* tests window */
883                         winlay_process_events(0);
884                         // render_win can be closed in winlay_process_events()
885                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
886                                 G.afbreek= 1;
887                         }
888                 }
889         }
890
891         if(G.afbreek==1) return 1;
892         else return 0;
893 }
894
895
896
897 #ifdef _WIN32
898 /* we use the multimedia time here */
899 static UINT uRenderTimerId;
900
901 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
902 {
903         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
904 }
905
906 /* WIN32: init SetTimer callback */
907 static void init_test_break_callback()
908 {
909         timeBeginPeriod(50);
910         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
911 }
912
913 /* WIN32: stop SetTimer callback */
914 static void end_test_break_callback()
915 {
916         timeEndPeriod(50);
917         timeKillEvent(uRenderTimerId);
918 }
919
920 #else
921 /* all other OS's support signal(SIGVTALRM) */
922
923 /* POSIX: this function goes in the signal() callback */
924 static void interruptESC(int sig)
925 {
926
927         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
928
929         /* call again, timer was reset */
930         signal(SIGVTALRM, interruptESC);
931 }
932
933 /* POSIX: initialize timer and signal */
934 static void init_test_break_callback()
935 {
936
937         struct itimerval tmevalue;
938
939         tmevalue.it_interval.tv_sec = 0;
940         tmevalue.it_interval.tv_usec = 250000;
941         /* wanneer de eerste ? */
942         tmevalue.it_value.tv_sec = 0;
943         tmevalue.it_value.tv_usec = 10000;
944
945         signal(SIGVTALRM, interruptESC);
946         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
947
948 }
949
950 /* POSIX: stop timer and callback */
951 static void end_test_break_callback()
952 {
953         struct itimerval tmevalue;
954
955         tmevalue.it_value.tv_sec = 0;
956         tmevalue.it_value.tv_usec = 0;
957         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
958         signal(SIGVTALRM, SIG_IGN);
959
960 }
961
962
963 #endif
964
965
966
967 /* -------------- callbacks for render loop: init & run! ----------------------- */
968
969
970 /* - initialize displays
971    - both opengl render as blender render
972    - set callbacks
973    - cleanup
974 */
975
976 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
977 {
978         
979         /* we set this flag to prevent renderwindow queue to execute another render */
980         R.flag= R_RENDERING;
981
982         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
983                 RE_set_initrenderdisplay_callback(NULL);
984                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
985                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
986
987                 renderwin_init_display_cb();
988         } 
989         else {
990                 BIF_close_render_display();
991                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
992                 RE_set_clearrenderdisplay_callback(NULL);
993                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
994         }
995
996         init_test_break_callback();
997         RE_set_test_break_callback(test_break);
998         
999         RE_set_timecursor_callback(set_timecursor);
1000         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1001         
1002         if (render_win) {
1003                 window_set_cursor(render_win->win, CURSOR_WAIT);
1004                 // when opening new window... not cross platform identical behaviour, so
1005                 // for now call it each time
1006                 // if(ogl_render_view3d) init_gl_stuff();
1007         }
1008         waitcursor(1);
1009
1010         G.afbreek= 0;
1011         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1012                 exit_editmode(0);       /* 0 = no free data */
1013         }
1014
1015         if(anim) {
1016                 RE_animrender(ogl_render_view3d);
1017         }
1018         else {
1019                 RE_initrender(ogl_render_view3d);
1020         }
1021
1022         if(anim) update_for_newframe_muted();  // only when anim, causes redraw event which frustrates dispview
1023         
1024         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1025
1026         free_filesel_spec(G.scene->r.pic);
1027
1028         G.afbreek= 0;
1029         end_test_break_callback();
1030         
1031         /* in dispiew it will destroy the image otherwise
1032            window_make_active() raises window at osx and sends redraws */
1033         if(G.displaymode==R_DISPLAYWIN) {
1034                 mainwindow_make_active();
1035         
1036                 /* after an envmap creation...  */
1037                 if(R.flag & R_REDRAW_PRV) {
1038                         BIF_all_preview_changed();
1039                 }
1040                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1041         }
1042         
1043         R.flag= 0;
1044         waitcursor(0);  // waitcursor checks rendering R.flag...
1045 }
1046
1047 /* finds area with a 'dispview' set */
1048 static ScrArea *find_dispimage_v3d(void)
1049 {
1050         ScrArea *sa;
1051         
1052         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1053                 if (sa->spacetype==SPACE_VIEW3D) {
1054                         View3D *vd= sa->spacedata.first;
1055                         if (vd->flag & V3D_DISPIMAGE)
1056                                 return sa;
1057                 }
1058         }
1059         
1060         return NULL;
1061 }
1062
1063 /* used for swapping with spare buffer, when images are different size */
1064 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1065 {
1066         unsigned int *rect, *newrect;
1067         int x, y;
1068         int ofsx, ofsy, stepx, stepy;
1069
1070         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1071         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1072         ofsy = 32768;
1073         newrect= rectn;
1074         
1075         for (y = newy; y > 0 ; y--){
1076                 rect = recto;
1077                 rect += (ofsy >> 16) * oldx;
1078                 ofsy += stepy;
1079                 ofsx = 32768;
1080                 for (x = newx ; x>0 ; x--){
1081                         *newrect++ = rect[ofsx >> 16];
1082                         ofsx += stepx;
1083                 }
1084         }
1085 }
1086
1087 /* -------------- API: externally called --------------- */
1088
1089 void BIF_renderwin_make_active(void)
1090 {
1091         if(render_win) {
1092                 window_make_active(render_win->win);
1093                 mywinset(2);
1094         }
1095 }
1096
1097 /* set up display, render an image or scene */
1098 void BIF_do_render(int anim)
1099 {
1100         /* if start render in 3d win, use layer from window (e.g also local view) */
1101         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1102                 int lay= G.scene->lay;
1103                 if(G.vd->lay & 0xFF000000)      // localview
1104                         G.scene->lay |= G.vd->lay;
1105                 else G.scene->lay= G.vd->lay;
1106                 
1107                 do_render(NULL, anim, 0);
1108                 
1109                 G.scene->lay= lay;
1110         }
1111         else do_render(NULL, anim, 0);
1112 }
1113
1114 /* set up display, render the current area view in an image */
1115 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1116 {
1117         G.scene->r.scemode |= R_OGL;
1118         do_render(ogl_render_view3d, anim, 1);
1119         G.scene->r.scemode &= ~R_OGL;
1120 }
1121
1122 void BIF_redraw_render_rect(void)
1123 {
1124         
1125         /* redraw */
1126         if (G.displaymode == R_DISPLAYWIN) {
1127                 // don't open render_win if rendering has been
1128             // canceled or the render_win has been actively closed
1129                 if (render_win) {
1130                         renderwin_queue_redraw(render_win);
1131                 }
1132         } else {
1133                 renderview_init_display_cb();
1134                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1135         }
1136 }       
1137
1138 void BIF_swap_render_rects(void)
1139 {
1140         unsigned int *temp;
1141
1142         if(R.rectspare==0) {
1143                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1144                 R.sparex= R.rectx;
1145                 R.sparey= R.recty;
1146         }
1147         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1148                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1149                                         
1150                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1151                 MEM_freeN(R.rectspare);
1152                 R.rectspare= temp;
1153                                         
1154                 R.sparex= R.rectx;
1155                 R.sparey= R.recty;
1156         }
1157         
1158         temp= R.rectot;
1159         R.rectot= R.rectspare;
1160         R.rectspare= temp;
1161         
1162         if (G.displaymode == R_DISPLAYWIN) {
1163                 if (render_win) {
1164                         char *tmp= render_win->render_text_spare;
1165                         render_win->render_text_spare= render_win->render_text;
1166                         render_win->render_text= tmp;
1167                         
1168                         window_set_title(render_win->win, renderwin_get_title(1));
1169                         
1170                 }
1171         }
1172
1173         /* redraw */
1174         BIF_redraw_render_rect();
1175 }                               
1176
1177 /* called from usiblender.c too, to free and close renderwin */
1178 void BIF_close_render_display(void)
1179 {
1180         if (render_win) {
1181
1182                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1183                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1184                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1185
1186                 window_destroy(render_win->win); /* ghost close window */
1187                 MEM_freeN(render_win);
1188
1189                 render_win= NULL;
1190         }
1191 }
1192
1193
1194 /* typical with F11 key, show image or hide/close */
1195 void BIF_toggle_render_display(void) 
1196 {
1197         ScrArea *sa= find_dispimage_v3d();
1198         
1199         if(R.rectot==NULL);             // do nothing
1200         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1201                 if(render_win->active) {
1202                         mainwindow_raise();
1203                         mainwindow_make_active();
1204                         render_win->active= 0;
1205                 }
1206                 else {
1207                         window_raise(render_win->win);
1208                         window_make_active(render_win->win);
1209                         render_win->active= 1;
1210                 }
1211         } 
1212         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1213                 View3D *vd= sa->spacedata.first;
1214                 vd->flag &= ~V3D_DISPIMAGE;
1215                 scrarea_queue_winredraw(sa);
1216         } 
1217         else {
1218                 if (G.displaymode == R_DISPLAYWIN) {
1219                         renderwin_init_display_cb();
1220                 } else {
1221                         if (render_win) {
1222                                 BIF_close_render_display();
1223                         }
1224                         renderview_init_display_cb();
1225                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1226                 }
1227         }
1228 }
1229
1230 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1231 {
1232         if (render_win) {
1233                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1234         }
1235 }