Fixed annoyance with opion "DispView" and Border render. It now displays
[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         /* crop option makes image smaller */
245         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
246                 rectx*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
247                 recty*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
248         }
249         
250         if(rectx>w || recty>h) {
251                 if(rectx-w > recty-h) rw->zoom= ((float)w)/((float)rectx);
252                 else rw->zoom= ((float)h)/((float)recty);
253         }
254         else rw->zoom= 1.0;
255
256         rw->zoomofs[0]= rw->zoomofs[1]= 0;
257         renderwin_queue_redraw(rw);
258 }
259
260 static void renderwin_draw_render_info(RenderWin *rw)
261 {
262         /* render text is added to top */
263         if(RW_HEADERY) {
264                 float colf[3];
265                 rcti rect;
266                 
267                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
268                 rect.xmin= 0;
269                 rect.ymin= rect.ymax-RW_HEADERY;
270                 glEnable(GL_SCISSOR_TEST);
271                 glaDefine2DArea(&rect);
272                 
273                 /* clear header rect */
274                 BIF_SetTheme(NULL);     // sets view3d theme by default
275                 BIF_GetThemeColor3fv(TH_HEADER, colf);
276                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
277                 glClear(GL_COLOR_BUFFER_BIT);
278                 
279                 if(rw->render_text) {
280                         BIF_ThemeColor(TH_TEXT);
281                         glRasterPos2i(12, 5);
282                         BMF_DrawString(G.fonts, rw->render_text);
283                 }
284         }       
285         
286 }
287
288 static void renderwin_draw(RenderWin *rw, int just_clear)
289 {
290         float disprect[2][2];
291         int set_back_mainwindow;
292         rcti rect;
293         
294         /* since renderwin uses callbacks (controlled by ghost) it can
295            mess up active window output with redraw events after a render. 
296            this is patchy, still WIP */
297         set_back_mainwindow = (winlay_get_active_window() != rw->win);
298         window_make_active(rw->win);
299         
300         rect.xmin= rect.ymin= 0;
301         window_get_size(rw->win, &rect.xmax, &rect.ymax);
302         rect.ymax-= RW_HEADERY;
303         
304         renderwin_get_disprect(rw, disprect);
305         
306         /* do this first, so window ends with correct scissor */
307         renderwin_draw_render_info(rw);
308         
309         glEnable(GL_SCISSOR_TEST);
310         glaDefine2DArea(&rect);
311         
312         glClearColor(.1875, .1875, .1875, 1.0); 
313         glClear(GL_COLOR_BUFFER_BIT);
314
315         if (just_clear || !R.rectot) {
316                 glColor3ub(0, 0, 0);
317                 glRectfv(disprect[0], disprect[1]);
318         } else {
319                 glPixelZoom(rw->zoom, rw->zoom);
320                 if(rw->flags & RW_FLAGS_ALPHA) {
321                         char *rect= (char *)R.rectot;
322                         
323                         glColorMask(1, 0, 0, 0);
324                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+3);
325                         glColorMask(0, 1, 0, 0);
326                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+2);
327                         glColorMask(0, 0, 1, 0);
328                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+1);
329                         glColorMask(1, 1, 1, 1);
330                         
331                 }
332                 else {
333                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, R.rectot);
334                 }
335                 glPixelZoom(1.0, 1.0);
336         }
337         
338         /* info text is overlayed on bottom */
339         if (rw->info_text) {
340                 float w;
341                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
342                 glEnable(GL_BLEND);
343                 w=186.0*strlen(rw->info_text)/30;
344                 glColor4f(.5,.5,.5,.25);
345                 glRectf(0.0,0.0,w,30.0);
346                 glDisable(GL_BLEND);
347                 glColor3ub(255, 255, 255);
348                 glRasterPos2i(10, 10);
349                 BMF_DrawString(G.font, rw->info_text);
350         }
351         
352         window_swap_buffers(rw->win);
353         
354         if (set_back_mainwindow) mainwindow_make_active();      
355 }
356
357 /* ------ interactivity calls for RenderWin ------------- */
358
359 static void renderwin_mouse_moved(RenderWin *rw)
360 {
361         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
362                 int imgco[2];
363                 char buf[64];
364
365                 if (R.rectot && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
366                         unsigned char *pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
367
368                         sprintf(buf, "R: %d, G: %d, B: %d, A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);
369                         renderwin_set_infotext(rw, buf);
370                         renderwin_queue_redraw(rw);
371                 } else {
372                         renderwin_set_infotext(rw, NULL);
373                         renderwin_queue_redraw(rw);
374                 }
375         } 
376         else if (rw->flags & RW_FLAGS_PANNING) {
377                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
378                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
379         
380                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
381                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
382                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -R.rectx/2, R.rectx/2);
383                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -R.recty/2, R.recty/2);
384
385                 renderwin_queue_redraw(rw);
386         } 
387         else if (rw->flags & RW_FLAGS_OLDZOOM) {
388                 float ndc[2];
389                 int w, h;
390
391                 window_get_size(rw->win, &w, &h);
392                 h-= RW_HEADERY;
393                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
394
395                 rw->zoomofs[0]= -0.5*ndc[0]*(w-R.rectx*rw->zoom)/rw->zoom;
396                 rw->zoomofs[1]= -0.5*ndc[1]*(h-R.recty*rw->zoom)/rw->zoom;
397
398                 renderwin_queue_redraw(rw);
399         }
400 }
401
402 static void renderwin_mousebut_changed(RenderWin *rw)
403 {
404         if (rw->mbut[0]) {
405                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
406         } 
407         else if (rw->mbut[1]) {
408                 rw->flags|= RW_FLAGS_PANNING;
409                 rw->pan_mouse_start[0]= rw->lmouse[0];
410                 rw->pan_mouse_start[1]= rw->lmouse[1];
411                 rw->pan_ofs_start[0]= rw->zoomofs[0];
412                 rw->pan_ofs_start[1]= rw->zoomofs[1];
413         } 
414         else {
415                 if (rw->flags & RW_FLAGS_PANNING) {
416                         rw->flags &= ~RW_FLAGS_PANNING;
417                         renderwin_queue_redraw(rw);
418                 }
419                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
420                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
421                         renderwin_set_infotext(rw, NULL);
422                         renderwin_queue_redraw(rw);
423                 }
424         }
425 }
426
427
428 /* handler for renderwin, passed on to Ghost */
429 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
430 {
431         RenderWin *rw= user_data;
432
433         // added this for safety, while render it's just creating bezerk results
434         if(R.flag & R_RENDERING) {
435                 if(evt==ESCKEY && val) 
436                         rw->flags|= RW_FLAGS_ESCAPE;
437                 return;
438         }
439         
440         if (evt==RESHAPE) {
441                 renderwin_reshape(rw);
442         } 
443         else if (evt==REDRAW) {
444                 renderwin_draw(rw, 0);
445         } 
446         else if (evt==WINCLOSE) {
447                 BIF_close_render_display();
448         } 
449         else if (evt==INPUTCHANGE) {
450                 rw->active= val;
451
452                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
453                         rw->flags&= ~RW_FLAGS_OLDZOOM;
454                         renderwin_reset_view(rw);
455                 }
456         } 
457         else if (ELEM(evt, MOUSEX, MOUSEY)) {
458                 rw->lmouse[evt==MOUSEY]= val;
459                 renderwin_mouse_moved(rw);
460         } 
461         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
462                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
463                 rw->mbut[which]= val;
464                 renderwin_mousebut_changed(rw);
465         } 
466         else if (val) {
467                 if (evt==ESCKEY) {
468                         if (rw->flags&RW_FLAGS_OLDZOOM) {
469                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
470                                 renderwin_reset_view(rw);
471                         } 
472                         else {
473                                 rw->flags|= RW_FLAGS_ESCAPE;
474                                 mainwindow_raise();
475                                 mainwindow_make_active();
476                                 rw->active= 0;
477                         }
478                 } 
479                 else if( evt==AKEY) {
480                         rw->flags ^= RW_FLAGS_ALPHA;
481                         renderwin_queue_redraw(render_win);
482                 }
483                 else if (evt==JKEY) {
484                         if(R.flag==0) BIF_swap_render_rects();
485                 } 
486                 else if (evt==ZKEY) {
487                         if (rw->flags&RW_FLAGS_OLDZOOM) {
488                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
489                                 renderwin_reset_view(rw);
490                         } else {
491                                 rw->zoom= 2.0;
492                                 rw->flags|= RW_FLAGS_OLDZOOM;
493                                 renderwin_mouse_moved(rw);
494                         }
495                 } 
496                 else if (evt==PADPLUSKEY) {
497                         if (rw->zoom<15.9) {
498                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
499                                 else rw->zoom*= 2.0;
500                                 renderwin_queue_redraw(rw);
501                         }
502                 } 
503                 else if (evt==PADMINUS) {
504                         if (rw->zoom>0.26) {
505                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
506                                 else rw->zoom*= 0.5;
507                                 renderwin_queue_redraw(rw);
508                         }
509                 } 
510                 else if (evt==PADENTER || evt==HOMEKEY) {
511                         if (rw->flags&RW_FLAGS_OLDZOOM) {
512                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
513                         }
514                         renderwin_reset_view(rw);
515                 } 
516                 else if (evt==F3KEY) {
517                         if(R.flag==0) {
518                                 mainwindow_raise();
519                                 mainwindow_make_active();
520                                 rw->active= 0;
521                                 areawinset(find_biggest_area()->win);
522                                 BIF_save_rendered_image();
523                         }
524                 } 
525                 else if (evt==F11KEY) {
526                         BIF_toggle_render_display();
527                 } 
528                 else if (evt==F12KEY) {
529                         /* if it's rendering, this flag is set */
530                         if(R.flag==0) BIF_do_render(0);
531                 }
532         }
533 }
534
535 static char *renderwin_get_title(int doswap)
536 {
537         static int swap= 0;
538         char *title="";
539         
540         swap+= doswap;
541         
542         if(swap & 1) {
543                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
544                 else title = "Blender:Render (spare)";
545         }
546         else {
547                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
548                 else title = "Blender:Render";
549         }
550
551         return title;
552 }
553
554 /* opens window and allocs struct */
555 static void open_renderwin(int winpos[2], int winsize[2])
556 {
557         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
558         Window *win;
559         char *title;
560         
561         title= renderwin_get_title(0);  /* 0 = no swap */
562         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
563
564         render_win= renderwin_alloc(win);
565
566         /* Ghost calls handler */
567         window_set_handler(win, renderwin_handler, render_win);
568
569         winlay_process_events(0);
570         window_make_active(render_win->win);
571         winlay_process_events(0);
572         
573         /* mywindow has to know about it too */
574         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
575         /* and we should be able to draw 3d in it */
576         init_gl_stuff();
577         
578         renderwin_draw(render_win, 1);
579         renderwin_draw(render_win, 1);
580 }
581
582 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
583
584 /* calculations for window size and position */
585 void calc_renderwin_rectangle(int posmask, int renderpos_r[2], int rendersize_r[2]) 
586 {
587         int scr_w, scr_h, x, y, div= 0;
588         float ndc_x= 0.0, ndc_y= 0.0;
589
590                 /* XXX, we temporarily hack the screen size and position so
591                  * the window is always 60 pixels away from a side, really need
592                  * a GHOST_GetMaxWindowBounds or so - zr
593                  */
594         winlay_get_screensize(&scr_w, &scr_h);
595         
596         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
597         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
598         
599         /* crop option makes image smaller */
600         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
601                 rendersize_r[0]*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
602                 rendersize_r[1]*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
603         }
604         
605         if(G.scene->r.mode & R_PANORAMA) {
606                 rendersize_r[0]*= G.scene->r.xparts;
607                 rendersize_r[1]*= G.scene->r.yparts;
608         }
609         /* increased size of clipping for OSX, should become an option instead */
610         rendersize_r[0]= CLAMPIS(rendersize_r[0], 100, scr_w-120);
611         rendersize_r[1]= CLAMPIS(rendersize_r[1], 100, scr_h-120);
612
613         for (y=-1; y<=1; y++) {
614                 for (x=-1; x<=1; x++) {
615                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
616                                 ndc_x+= x;
617                                 ndc_y+= y;
618                                 div++;
619                         }
620                 }
621         }
622
623         if (div) {
624                 ndc_x/= div;
625                 ndc_y/= div;
626         }
627                 
628         renderpos_r[0]= 60 + (scr_w-90-rendersize_r[0])*(ndc_x*0.5 + 0.5);
629         renderpos_r[1]= 30 + (scr_h-90-rendersize_r[1])*(ndc_y*0.5 + 0.5);
630 }
631         
632 /* init renderwin, alloc/open/resize */
633 static void renderwin_init_display_cb(void) 
634 {
635         if (G.afbreek == 0) {
636                 int rendersize[2], renderpos[2];
637
638                 calc_renderwin_rectangle(G.winpos, renderpos, rendersize);
639
640                 if (!render_win) {
641                         open_renderwin(renderpos, rendersize);
642                         renderwin_reset_view(render_win); // incl. autozoom for large images
643                 } else {
644                         int win_x, win_y;
645                         int win_w, win_h;
646
647                         window_get_position(render_win->win, &win_x, &win_y);
648                         window_get_size(render_win->win, &win_w, &win_h);
649                         win_h-= RW_HEADERY;
650
651                                 /* XXX, this is nasty and I guess bound to cause problems,
652                                  * but to ensure the window is at the user specified position
653                                  * and size we reopen the window all the time... we need
654                                  * a ghost _set_position to fix this -zr
655                                  */
656                                  
657                                  /* XXX, well... it is nasty yes, and reopens windows each time on
658                                     subsequent renders. Better rule is to make it reopen only only
659                                     size change, and use the preferred position only on open_renderwin
660                                         cases (ton)
661                                  */
662                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
663                                 BIF_close_render_display();
664                                 open_renderwin(renderpos, rendersize);
665                         }
666                         else {
667                                 window_raise(render_win->win);
668                                 window_make_active(render_win->win);
669                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes
670                         }
671
672                         renderwin_reset_view(render_win);
673                         render_win->flags&= ~RW_FLAGS_ESCAPE;
674                         render_win->active= 1;
675                 }
676                 /* make sure we are in normal draw again */
677                 render_win->flags &= ~RW_FLAGS_ALPHA;
678         }
679 }
680
681 /* callback for redraw render win */
682 static void renderwin_clear_display_cb(short ignore) 
683 {
684         if (render_win) {
685                 window_make_active(render_win->win);    
686                 renderwin_draw(render_win, 1);
687         }
688 }
689
690 /* XXX, this is not good, we do this without any regard to state
691 * ... better is to make this an optimization of a more clear
692 * implementation. the bug shows up when you do something like
693 * open the window, then draw part of the progress, then get
694 * a redraw event. whatever can go wrong will. -zr
695 *
696 * Note: blocked queue handling while rendering to prevent that (ton)
697 */
698
699 /* in render window; display a couple of scanlines of rendered image (see callback below) */
700 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
701 {
702         float disprect[2][2];
703         rcti win_rct;
704
705         win_rct.xmin= win_rct.ymin= 0;
706         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
707         win_rct.ymax-= RW_HEADERY;
708         
709         renderwin_get_disprect(rw, disprect);
710         
711         /* for efficiency & speed; not drawing in Blender UI while rendering */
712         //window_make_active(rw->win);
713
714         glEnable(GL_SCISSOR_TEST);
715         glaDefine2DArea(&win_rct);
716
717         glDrawBuffer(GL_FRONT);
718         glPixelZoom(rw->zoom, rw->zoom);
719         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
720         glPixelZoom(1.0, 1.0);
721         glFlush();
722         glDrawBuffer(GL_BACK);
723 }
724
725
726 /* in render window; display a couple of scanlines of rendered image */
727 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
728 {
729         if (render_win) {
730                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
731         }
732 }
733
734
735 /* -------------- callbacks for render loop: in View3D ----------------------- */
736
737
738 static View3D *render_view3d = NULL;
739
740 /* init Render view callback */
741 static void renderview_init_display_cb(void)
742 {
743         ScrArea *sa;
744
745                 /* Choose the first view with a persp camera,
746                  * if one doesn't exist we will get the first
747                  * View3D window.
748                  */ 
749         render_view3d= NULL;
750         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
751                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
752                         View3D *vd= sa->spacedata.first;
753                         
754                         if (vd->persp==2 && vd->camera==G.scene->camera) {
755                                 render_view3d= vd;
756                                 break;
757                         } else if (!render_view3d) {
758                                 render_view3d= vd;
759                         }
760                 }
761         }
762 }
763
764
765 /* in 3d view; display a couple of scanlines of rendered image */
766 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
767 {
768         if (render_view3d) {
769                 View3D *v3d= render_view3d;
770                 int nlines= y2-y1+1;
771                 float sx, sy, facx, facy;
772                 rcti win_rct, vb;
773
774                 calc_viewborder(v3d, &vb);
775                 
776                 /* if border render  */
777                 if(G.scene->r.mode & R_BORDER) { 
778                         
779                         /* but, if image is full (at end of border render, without crop) we don't */
780                         if(R.rectx != (G.scene->r.size*G.scene->r.xsch)/100 ||
781                            R.recty != (G.scene->r.size*G.scene->r.ysch)/100 ) {
782                         
783                                 facx= (float) (vb.xmax-vb.xmin);
784                                 facy= (float) (vb.ymax-vb.ymin);
785                                 
786                                 vb.xmax= vb.xmin + facx*G.scene->r.border.xmax;
787                                 vb.ymax= vb.ymin + facy*G.scene->r.border.ymax;
788                                 vb.xmin+= facx*G.scene->r.border.xmin;
789                                 vb.ymin+= facy*G.scene->r.border.ymin;
790                         }
791                 }
792                         
793                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
794                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
795
796                 bwin_get_rect(v3d->area->win, &win_rct);
797
798                 glaDefine2DArea(&win_rct);
799         
800                 glDrawBuffer(GL_FRONT);
801                 
802                 sx= vb.xmin;
803                 sy= vb.ymin + facy*y1;
804
805                 glPixelZoom(facx, facy);
806                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
807                 glPixelZoom(1.0, 1.0);
808
809                 glFlush();
810                 glDrawBuffer(GL_BACK);
811                 v3d->flag |= V3D_DISPIMAGE;
812                 v3d->area->win_swap= WIN_FRONT_OK;
813                 
814         }
815 }
816
817 /* in 3d view; display stats of rendered image */
818 static void renderview_draw_render_info(char *str)
819 {
820         if (render_view3d) {
821                 View3D *v3d= render_view3d;
822                 rcti vb, win_rct;
823                 
824                 calc_viewborder(v3d, &vb);
825                 
826                 bwin_get_rect(v3d->area->win, &win_rct);
827                 glaDefine2DArea(&win_rct);
828                 
829                 glDrawBuffer(GL_FRONT);
830                 
831                 /* clear header rect */
832                 BIF_ThemeColor(TH_HEADER);
833                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
834                 
835                 if(str) {
836                         BIF_ThemeColor(TH_TEXT);
837                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
838                         BMF_DrawString(G.fonts, str);
839                 }
840                 
841                 glFlush();
842                 glDrawBuffer(GL_BACK);
843
844                 v3d->area->win_swap= WIN_FRONT_OK;
845                 
846         }
847 }
848
849
850 /* -------------- callbacks for render loop: interactivity ----------------------- */
851
852
853 /* callback for print info in top header of renderwin */
854 /* time is only not zero on last call, we then don't update the other stats */ 
855 static void printrenderinfo_cb(double time, int sample)
856 {
857         extern int mem_in_use;
858         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
859         static float megs_used_memory=0.0;
860         char str[300], *spos= str;
861                 
862         if(time==0.0) {
863                 megs_used_memory= mem_in_use/(1024.0*1024.0);
864                 totvert= R.totvert;
865                 totvlak= R.totvlak;
866                 totlamp= R.totlamp;
867                 tothalo= R.tothalo;
868         }
869         
870         if(tothalo)
871                 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);
872         else 
873                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
874
875         if(time==0.0) {
876                 if (R.r.mode & R_FIELDS) {
877                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
878                 }
879                 if (sample!=-1) {
880                         spos+= sprintf(spos, "Sample: %d    ", sample);
881                 }
882         }
883         else {
884                 extern char info_time_str[32];  // header_info.c
885                 timestr(time, info_time_str);
886                 spos+= sprintf(spos, " Time:%s ", info_time_str);
887         }
888         
889         if(render_win) {
890                 if(render_win->render_text) MEM_freeN(render_win->render_text);
891                 render_win->render_text= BLI_strdup(str);
892                 glDrawBuffer(GL_FRONT);
893                 renderwin_draw_render_info(render_win);
894                 glFlush();
895                 glDrawBuffer(GL_BACK);
896         }
897         else renderview_draw_render_info(str);
898 }
899
900 /* -------------- callback system to allow ESC from rendering ----------------------- */
901
902 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
903 static int test_break(void)
904 {
905
906         if(G.afbreek==2) { /* code for testing queue */
907
908                 G.afbreek= 0;
909
910                 blender_test_break(); /* tests blender interface */
911
912                 if (G.afbreek==0 && render_win) { /* tests window */
913                         winlay_process_events(0);
914                         // render_win can be closed in winlay_process_events()
915                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
916                                 G.afbreek= 1;
917                         }
918                 }
919         }
920
921         if(G.afbreek==1) return 1;
922         else return 0;
923 }
924
925
926
927 #ifdef _WIN32
928 /* we use the multimedia time here */
929 static UINT uRenderTimerId;
930
931 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
932 {
933         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
934 }
935
936 /* WIN32: init SetTimer callback */
937 static void init_test_break_callback()
938 {
939         timeBeginPeriod(50);
940         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
941 }
942
943 /* WIN32: stop SetTimer callback */
944 static void end_test_break_callback()
945 {
946         timeEndPeriod(50);
947         timeKillEvent(uRenderTimerId);
948 }
949
950 #else
951 /* all other OS's support signal(SIGVTALRM) */
952
953 /* POSIX: this function goes in the signal() callback */
954 static void interruptESC(int sig)
955 {
956
957         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
958
959         /* call again, timer was reset */
960         signal(SIGVTALRM, interruptESC);
961 }
962
963 /* POSIX: initialize timer and signal */
964 static void init_test_break_callback()
965 {
966
967         struct itimerval tmevalue;
968
969         tmevalue.it_interval.tv_sec = 0;
970         tmevalue.it_interval.tv_usec = 250000;
971         /* wanneer de eerste ? */
972         tmevalue.it_value.tv_sec = 0;
973         tmevalue.it_value.tv_usec = 10000;
974
975         signal(SIGVTALRM, interruptESC);
976         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
977
978 }
979
980 /* POSIX: stop timer and callback */
981 static void end_test_break_callback()
982 {
983         struct itimerval tmevalue;
984
985         tmevalue.it_value.tv_sec = 0;
986         tmevalue.it_value.tv_usec = 0;
987         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
988         signal(SIGVTALRM, SIG_IGN);
989
990 }
991
992
993 #endif
994
995
996
997 /* -------------- callbacks for render loop: init & run! ----------------------- */
998
999
1000 /* - initialize displays
1001    - both opengl render as blender render
1002    - set callbacks
1003    - cleanup
1004 */
1005
1006 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
1007 {
1008         
1009         /* we set this flag to prevent renderwindow queue to execute another render */
1010         R.flag= R_RENDERING;
1011
1012         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
1013                 RE_set_initrenderdisplay_callback(NULL);
1014                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
1015                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
1016
1017                 renderwin_init_display_cb();
1018         } 
1019         else {
1020                 BIF_close_render_display();
1021                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
1022                 RE_set_clearrenderdisplay_callback(NULL);
1023                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
1024         }
1025
1026         init_test_break_callback();
1027         RE_set_test_break_callback(test_break);
1028         
1029         RE_set_timecursor_callback(set_timecursor);
1030         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1031         
1032         if (render_win) {
1033                 window_set_cursor(render_win->win, CURSOR_WAIT);
1034                 // when opening new window... not cross platform identical behaviour, so
1035                 // for now call it each time
1036                 // if(ogl_render_view3d) init_gl_stuff();
1037         }
1038         waitcursor(1);
1039
1040         G.afbreek= 0;
1041         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1042                 exit_editmode(0);       /* 0 = no free data */
1043         }
1044
1045         if(anim) {
1046                 RE_animrender(ogl_render_view3d);
1047         }
1048         else {
1049                 RE_initrender(ogl_render_view3d);
1050         }
1051
1052         if(anim) update_for_newframe_muted();  // only when anim, causes redraw event which frustrates dispview
1053         
1054         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1055
1056         free_filesel_spec(G.scene->r.pic);
1057
1058         G.afbreek= 0;
1059         end_test_break_callback();
1060         
1061         /* in dispiew it will destroy the image otherwise
1062            window_make_active() raises window at osx and sends redraws */
1063         if(G.displaymode==R_DISPLAYWIN) {
1064                 mainwindow_make_active();
1065         
1066                 /* after an envmap creation...  */
1067                 if(R.flag & R_REDRAW_PRV) {
1068                         BIF_all_preview_changed();
1069                 }
1070                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1071         }
1072         
1073         R.flag= 0;
1074         waitcursor(0);  // waitcursor checks rendering R.flag...
1075 }
1076
1077 /* finds area with a 'dispview' set */
1078 static ScrArea *find_dispimage_v3d(void)
1079 {
1080         ScrArea *sa;
1081         
1082         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1083                 if (sa->spacetype==SPACE_VIEW3D) {
1084                         View3D *vd= sa->spacedata.first;
1085                         if (vd->flag & V3D_DISPIMAGE)
1086                                 return sa;
1087                 }
1088         }
1089         
1090         return NULL;
1091 }
1092
1093 /* used for swapping with spare buffer, when images are different size */
1094 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1095 {
1096         unsigned int *rect, *newrect;
1097         int x, y;
1098         int ofsx, ofsy, stepx, stepy;
1099
1100         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1101         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1102         ofsy = 32768;
1103         newrect= rectn;
1104         
1105         for (y = newy; y > 0 ; y--){
1106                 rect = recto;
1107                 rect += (ofsy >> 16) * oldx;
1108                 ofsy += stepy;
1109                 ofsx = 32768;
1110                 for (x = newx ; x>0 ; x--){
1111                         *newrect++ = rect[ofsx >> 16];
1112                         ofsx += stepx;
1113                 }
1114         }
1115 }
1116
1117 /* -------------- API: externally called --------------- */
1118
1119 void BIF_renderwin_make_active(void)
1120 {
1121         if(render_win) {
1122                 window_make_active(render_win->win);
1123                 mywinset(2);
1124         }
1125 }
1126
1127 /* set up display, render an image or scene */
1128 void BIF_do_render(int anim)
1129 {
1130         /* if start render in 3d win, use layer from window (e.g also local view) */
1131         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1132                 int lay= G.scene->lay;
1133                 if(G.vd->lay & 0xFF000000)      // localview
1134                         G.scene->lay |= G.vd->lay;
1135                 else G.scene->lay= G.vd->lay;
1136                 
1137                 do_render(NULL, anim, 0);
1138                 
1139                 G.scene->lay= lay;
1140         }
1141         else do_render(NULL, anim, 0);
1142 }
1143
1144 /* set up display, render the current area view in an image */
1145 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1146 {
1147         G.scene->r.scemode |= R_OGL;
1148         do_render(ogl_render_view3d, anim, 1);
1149         G.scene->r.scemode &= ~R_OGL;
1150 }
1151
1152 void BIF_redraw_render_rect(void)
1153 {
1154         
1155         /* redraw */
1156         if (G.displaymode == R_DISPLAYWIN) {
1157                 // don't open render_win if rendering has been
1158             // canceled or the render_win has been actively closed
1159                 if (render_win) {
1160                         renderwin_queue_redraw(render_win);
1161                 }
1162         } else {
1163                 renderview_init_display_cb();
1164                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1165         }
1166 }       
1167
1168 void BIF_swap_render_rects(void)
1169 {
1170         unsigned int *temp;
1171
1172         if(R.rectspare==0) {
1173                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1174                 R.sparex= R.rectx;
1175                 R.sparey= R.recty;
1176         }
1177         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1178                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1179                                         
1180                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1181                 MEM_freeN(R.rectspare);
1182                 R.rectspare= temp;
1183                                         
1184                 R.sparex= R.rectx;
1185                 R.sparey= R.recty;
1186         }
1187         
1188         temp= R.rectot;
1189         R.rectot= R.rectspare;
1190         R.rectspare= temp;
1191         
1192         if (G.displaymode == R_DISPLAYWIN) {
1193                 if (render_win) {
1194                         char *tmp= render_win->render_text_spare;
1195                         render_win->render_text_spare= render_win->render_text;
1196                         render_win->render_text= tmp;
1197                         
1198                         window_set_title(render_win->win, renderwin_get_title(1));
1199                         
1200                 }
1201         }
1202
1203         /* redraw */
1204         BIF_redraw_render_rect();
1205 }                               
1206
1207 /* called from usiblender.c too, to free and close renderwin */
1208 void BIF_close_render_display(void)
1209 {
1210         if (render_win) {
1211
1212                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1213                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1214                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1215
1216                 window_destroy(render_win->win); /* ghost close window */
1217                 MEM_freeN(render_win);
1218
1219                 render_win= NULL;
1220         }
1221 }
1222
1223
1224 /* typical with F11 key, show image or hide/close */
1225 void BIF_toggle_render_display(void) 
1226 {
1227         ScrArea *sa= find_dispimage_v3d();
1228         
1229         if(R.rectot==NULL);             // do nothing
1230         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1231                 if(render_win->active) {
1232                         mainwindow_raise();
1233                         mainwindow_make_active();
1234                         render_win->active= 0;
1235                 }
1236                 else {
1237                         window_raise(render_win->win);
1238                         window_make_active(render_win->win);
1239                         render_win->active= 1;
1240                 }
1241         } 
1242         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1243                 View3D *vd= sa->spacedata.first;
1244                 vd->flag &= ~V3D_DISPIMAGE;
1245                 scrarea_queue_winredraw(sa);
1246         } 
1247         else {
1248                 if (G.displaymode == R_DISPLAYWIN) {
1249                         renderwin_init_display_cb();
1250                 } else {
1251                         if (render_win) {
1252                                 BIF_close_render_display();
1253                         }
1254                         renderview_init_display_cb();
1255                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1256                 }
1257         }
1258 }
1259
1260 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1261 {
1262         if (render_win) {
1263                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1264         }
1265 }