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