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