Second part of bugfix #4715
[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[3];
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]= 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 /* ------ interactivity calls for RenderWin ------------- */
402
403 static void renderwin_mouse_moved(RenderWin *rw)
404 {
405         RenderResult rres;
406         
407         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
408
409         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
410                 int imgco[2], ofs=0;
411                 char buf[128];
412                 char *pxl;
413
414                 if (renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
415                         if (rres.rect32) {
416                                 pxl= (char*) &rres.rect32[rres.rectx*imgco[1] + imgco[0]];
417                                 ofs= sprintf(buf, "R: %d G: %d B: %d A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);   
418                         }
419                         if (rres.rectf) {
420                                 float *pxlf= rres.rectf + 4*(rres.rectx*imgco[1] + imgco[0]);
421                                 ofs+= sprintf(buf+ofs, " | R: %.3f G: %.3f B: %.3f A: %.3f ", pxlf[0], pxlf[1], pxlf[2], pxlf[3]);
422                         }
423                         if (rres.rectz) {
424                                 float *pxlz= &rres.rectz[rres.rectx*imgco[1] + imgco[0]];                       
425                                 sprintf(buf+ofs, "| Z: %.3f", *pxlz );
426                         }
427
428                         renderwin_set_infotext(rw, buf);
429                         renderwin_queue_redraw(rw);
430                 } else {
431                         renderwin_set_infotext(rw, NULL);
432                         renderwin_queue_redraw(rw);
433                 }
434         } 
435         else if (rw->flags & RW_FLAGS_PANNING) {
436                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
437                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
438         
439                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
440                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
441                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -rres.rectx/2, rres.rectx/2);
442                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -rres.recty/2, rres.recty/2);
443
444                 renderwin_queue_redraw(rw);
445         } 
446         else if (rw->flags & RW_FLAGS_OLDZOOM) {
447                 float ndc[2];
448                 int w, h;
449
450                 window_get_size(rw->win, &w, &h);
451                 h-= RW_HEADERY;
452                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
453
454                 rw->zoomofs[0]= -0.5*ndc[0]*(w-rres.rectx*rw->zoom)/rw->zoom;
455                 rw->zoomofs[1]= -0.5*ndc[1]*(h-rres.recty*rw->zoom)/rw->zoom;
456
457                 renderwin_queue_redraw(rw);
458         }
459 }
460
461 static void renderwin_mousebut_changed(RenderWin *rw)
462 {
463         if (rw->mbut[0]) {
464                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
465         } 
466         else if (rw->mbut[1]) {
467                 rw->flags|= RW_FLAGS_PANNING;
468                 rw->pan_mouse_start[0]= rw->lmouse[0];
469                 rw->pan_mouse_start[1]= rw->lmouse[1];
470                 rw->pan_ofs_start[0]= rw->zoomofs[0];
471                 rw->pan_ofs_start[1]= rw->zoomofs[1];
472         } 
473         else {
474                 if (rw->flags & RW_FLAGS_PANNING) {
475                         rw->flags &= ~RW_FLAGS_PANNING;
476                         renderwin_queue_redraw(rw);
477                 }
478                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
479                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
480                         renderwin_set_infotext(rw, NULL);
481                         renderwin_queue_redraw(rw);
482                 }
483         }
484 }
485
486
487 /* handler for renderwin, passed on to Ghost */
488 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
489 {
490         RenderWin *rw= user_data;
491
492         // added this for safety, while render it's just creating bezerk results
493         if(G.rendering) {
494                 if(evt==ESCKEY && val) 
495                         rw->flags|= RW_FLAGS_ESCAPE;
496                 return;
497         }
498         
499         if (evt==RESHAPE) {
500                 renderwin_reshape(rw);
501         } 
502         else if (evt==REDRAW) {
503                 renderwin_draw(rw, 0);
504         } 
505         else if (evt==WINCLOSE) {
506                 BIF_close_render_display();
507         } 
508         else if (evt==INPUTCHANGE) {
509                 rw->active= val;
510
511                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
512                         rw->flags&= ~RW_FLAGS_OLDZOOM;
513                         renderwin_reset_view(rw);
514                 }
515         } 
516         else if (ELEM(evt, MOUSEX, MOUSEY)) {
517                 rw->lmouse[evt==MOUSEY]= val;
518                 renderwin_mouse_moved(rw);
519         } 
520         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
521                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
522                 rw->mbut[which]= val;
523                 renderwin_mousebut_changed(rw);
524         } 
525         else if (val) {
526                 if (evt==ESCKEY) {
527                         if (rw->flags&RW_FLAGS_OLDZOOM) {
528                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
529                                 renderwin_reset_view(rw);
530                         } 
531                         else {
532                                 rw->flags|= RW_FLAGS_ESCAPE;
533                                 mainwindow_raise();
534                                 mainwindow_make_active();
535                                 rw->active= 0;
536                         }
537                 } 
538                 else if( evt==AKEY) {
539                         rw->flags ^= RW_FLAGS_ALPHA;
540                         renderwin_queue_redraw(render_win);
541                 }
542                 else if (evt==JKEY) {
543                         if(G.rendering==0) BIF_swap_render_rects();
544                 } 
545                 else if (evt==ZKEY) {
546                         if (rw->flags&RW_FLAGS_OLDZOOM) {
547                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
548                                 renderwin_reset_view(rw);
549                         } else {
550                                 rw->zoom= 2.0;
551                                 rw->flags|= RW_FLAGS_OLDZOOM;
552                                 renderwin_mouse_moved(rw);
553                         }
554                 } 
555                 else if (evt==PADPLUSKEY) {
556                         if (rw->zoom<15.9) {
557                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
558                                 else rw->zoom*= 2.0;
559                                 renderwin_queue_redraw(rw);
560                         }
561                 } 
562                 else if (evt==PADMINUS) {
563                         if (rw->zoom>0.26) {
564                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
565                                 else rw->zoom*= 0.5;
566                                 renderwin_queue_redraw(rw);
567                         }
568                 } 
569                 else if (evt==PADENTER || evt==HOMEKEY) {
570                         if (rw->flags&RW_FLAGS_OLDZOOM) {
571                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
572                         }
573                         renderwin_reset_view(rw);
574                 } 
575                 else if (evt==F3KEY) {
576                         if(G.rendering==0) {
577                                 mainwindow_raise();
578                                 mainwindow_make_active();
579                                 rw->active= 0;
580                                 areawinset(find_biggest_area()->win);
581                                 BIF_save_rendered_image_fs(0);
582                         }
583                 } 
584                 else if (evt==F11KEY) {
585                         BIF_toggle_render_display();
586                 } 
587                 else if (evt==F12KEY) {
588                         if(G.rendering==0) 
589                                 BIF_do_render(0);
590                 }
591         }
592 }
593
594 static char *renderwin_get_title(int doswap)
595 {
596         static int swap= 0;
597         char *title="";
598         
599         swap+= doswap;
600         
601         if(swap & 1) {
602                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (previous)";
603                 else title = "Blender:Render (previous)";
604         }
605         else {
606                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
607                 else title = "Blender:Render";
608         }
609
610         return title;
611 }
612
613 /* opens window and allocs struct */
614 static void open_renderwin(int winpos[2], int winsize[2], int imagesize[2])
615 {
616         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
617         Window *win;
618         char *title;
619         
620         title= renderwin_get_title(0);  /* 0 = no swap */
621         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
622
623         render_win= renderwin_alloc(win);
624         render_win->rectx= imagesize[0];
625         render_win->recty= imagesize[1];
626         
627         /* Ghost calls handler */
628         window_set_handler(win, renderwin_handler, render_win);
629
630         winlay_process_events(0);
631         window_make_active(render_win->win);
632         winlay_process_events(0);
633         
634         /* mywindow has to know about it too */
635         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
636         /* and we should be able to draw 3d in it */
637         init_gl_stuff();
638         
639         renderwin_draw(render_win, 1);
640         renderwin_draw(render_win, 1);
641 }
642
643 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
644
645 /* calculations for window size and position */
646 void calc_renderwin_rectangle(int rectx, int recty, int posmask, int renderpos_r[2], int rendersize_r[2]) 
647 {
648         int scr_w, scr_h, x, y, div= 0;
649         float ndc_x= 0.0, ndc_y= 0.0;
650
651         winlay_get_screensize(&scr_w, &scr_h);
652
653         rendersize_r[0]= rectx;
654         rendersize_r[1]= recty;
655
656         rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);     
657         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);  
658         
659         for (y=-1; y<=1; y++) {
660                 for (x=-1; x<=1; x++) {
661                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
662                                 ndc_x+= x;
663                                 ndc_y+= y;
664                                 div++;
665                         }
666                 }
667         }
668
669         if (div) {
670                 ndc_x/= div;
671                 ndc_y/= div;
672         }
673         
674         renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
675 #ifdef __APPLE__
676         /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
677         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);       
678         renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
679 #else
680         renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
681 #endif
682 }
683         
684 /* init renderwin, alloc/open/resize */
685 static void renderwin_init_display_cb(RenderResult *rr) 
686 {
687         if (G.afbreek != 1) {
688                 int rendersize[2], renderpos[2], imagesize[2];
689
690                 calc_renderwin_rectangle(rr->rectx, rr->recty, G.winpos, renderpos, rendersize);
691                 
692                 imagesize[0]= rr->rectx;
693                 imagesize[1]= rr->recty;
694
695                 if (!render_win) {
696                         open_renderwin(renderpos, rendersize, imagesize);
697                         renderwin_reset_view(render_win); // incl. autozoom for large images
698                 } else {
699                         int win_x, win_y;
700                         int win_w, win_h;
701
702                         window_get_position(render_win->win, &win_x, &win_y);
703                         window_get_size(render_win->win, &win_w, &win_h);
704                         win_h-= RW_HEADERY;
705
706                                 /* XXX, this is nasty and I guess bound to cause problems,
707                                  * but to ensure the window is at the user specified position
708                                  * and size we reopen the window all the time... we need
709                                  * a ghost _set_position to fix this -zr
710                                  */
711                                  
712                                  /* XXX, well... it is nasty yes, and reopens windows each time on
713                                     subsequent renders. Better rule is to make it reopen only only
714                                     size change, and use the preferred position only on open_renderwin
715                                         cases (ton)
716                                  */
717                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
718                                 BIF_close_render_display();
719                                 open_renderwin(renderpos, rendersize, imagesize);
720                         }
721                         else {
722                                 window_raise(render_win->win);
723                                 window_make_active(render_win->win);
724                                 
725                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
726                                 {
727                                         rcti win_rct;
728                                         win_rct.xmin= win_rct.ymin= 0;
729                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
730                                         win_rct.ymax-= RW_HEADERY;
731                                         glaDefine2DArea(&win_rct);
732                                 }
733                         }
734
735                         renderwin_reset_view(render_win);
736                         render_win->active= 1;
737                 }
738                 /* make sure we are in normal draw again */
739                 render_win->flags &= ~RW_FLAGS_ALPHA;
740                 
741                 glFinish();
742         }
743 }
744
745 /* callback for redraw render win */
746 static void renderwin_clear_display_cb(RenderResult *rr) 
747 {
748         if (render_win) {
749                 window_make_active(render_win->win);
750                 renderwin_draw(render_win, 1);
751         }
752 }
753
754 /* XXX, this is not good, we do this without any regard to state
755 * ... better is to make this an optimization of a more clear
756 * implementation. the bug shows up when you do something like
757 * open the window, then draw part of the progress, then get
758 * a redraw event. whatever can go wrong will. -zr
759 *
760 * Note: blocked queue handling while rendering to prevent that (ton)
761 */
762
763 /* can get as well the full picture, as the parts while rendering */
764 static void renderwin_progress(RenderWin *rw, RenderResult *rr, volatile rcti *renrect)
765 {
766         rcti win_rct;
767         float *rectf= NULL, fullrect[2][2];
768         unsigned int *rect32= NULL;
769         int ymin, ymax, xmin, xmax;
770         
771         /* if renrect argument, we only display scanlines */
772         if(renrect) {
773                  /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
774                 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
775                         return;
776                 
777                 /* xmin here is first subrect x coord, xmax defines subrect width */
778                 xmin = renrect->xmin;
779                 xmax = renrect->xmax - xmin;
780                 if (xmax<2) return;
781                 
782                 ymin= renrect->ymin;
783                 ymax= renrect->ymax - ymin;
784                 if(ymax<2)
785                         return;
786                 renrect->ymin= renrect->ymax;
787         }
788         else {
789                 xmin = ymin = 0;
790                 xmax = rr->rectx - 2*rr->crop;
791                 ymax = rr->recty - 2*rr->crop;
792         }
793         
794         /* renderwindow cruft */
795         win_rct.xmin= win_rct.ymin= 0;
796         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
797         win_rct.ymax-= RW_HEADERY;
798         renderwin_get_fullrect(rw, fullrect);
799         
800         /* find current float rect for display, first case is after composit... still weak */
801         if(rr->rectf)
802                 rectf= rr->rectf;
803         else {
804                 if(rr->rect32)
805                         rect32= rr->rect32;
806                 else {
807                         if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
808                         rectf= rr->renlay->rectf;
809                 }
810         }
811         if(rectf) {
812                 /* if scanline updates... */
813                 rectf+= 4*(rr->rectx*ymin + xmin);
814         
815                 /* when rendering more pixels than needed, we crop away cruft */
816                 if(rr->crop)
817                         rectf+= 4*(rr->crop*rr->rectx + rr->crop);
818         }
819         
820         /* tilerect defines drawing offset from (0,0) */
821         /* however, tilerect (xmin, ymin) is first pixel */
822         fullrect[0][0] += (rr->tilerect.xmin + rr->crop + xmin)*rw->zoom;
823         fullrect[0][1] += (rr->tilerect.ymin + rr->crop + ymin)*rw->zoom;
824
825         glEnable(GL_SCISSOR_TEST);
826         glaDefine2DArea(&win_rct);
827
828 #ifdef __APPLE__
829 #else
830         glDrawBuffer(GL_FRONT);
831 #endif
832         glPixelZoom(rw->zoom, rw->zoom);
833
834         if(rect32)
835                 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
836         else
837                 glaDrawPixelsSafe_to32(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, rectf);
838         
839         glPixelZoom(1.0, 1.0);
840         
841 #ifdef __APPLE__
842         window_swap_buffers(render_win->win);
843 #else
844         glFlush();
845         glDrawBuffer(GL_BACK);
846 #endif  
847 }
848
849
850 /* in render window; display a couple of scanlines of rendered image */
851 static void renderwin_progress_display_cb(RenderResult *rr, volatile rcti *rect)
852 {
853         if (render_win) {
854                 renderwin_progress(render_win, rr, rect);
855         }
856 }
857
858 /* -------------- callbacks for render loop: interactivity ----------------------- */
859
860 /* string is RW_MAXTEXT chars min */
861 void make_renderinfo_string(RenderStats *rs, char *str)
862 {
863         extern char info_time_str[32];  // header_info.c
864         extern unsigned long mem_in_use, mmap_in_use;
865         float megs_used_memory, mmap_used_memory;
866         char *spos= str;
867         
868         megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
869         mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
870         
871         if(G.scene->lay & 0xFF000000)
872                 spos+= sprintf(spos, "Localview | ");
873         else if(G.scene->r.scemode & R_SINGLE_LAYER)
874                 spos+= sprintf(spos, "Single Layer | ");
875         
876         if(rs->tothalo)
877                 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);
878         else 
879                 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);
880         
881         if(rs->curfield)
882                 spos+= sprintf(spos, "Field %d ", rs->curfield);
883         if(rs->curblur)
884                 spos+= sprintf(spos, "Blur %d ", rs->curblur);
885         
886         BLI_timestr(rs->lastframetime, info_time_str);
887         spos+= sprintf(spos, "Time:%s ", info_time_str);
888         
889         if(rs->infostr)
890                 spos+= sprintf(spos, "| %s ", rs->infostr);
891         
892         /* very weak... but 512 characters is quite safe... we cannot malloc during thread render */
893         if(spos >= str+RW_MAXTEXT)
894                 printf("WARNING! renderwin text beyond limit \n");
895         
896 }
897
898 /* callback for print info in top header of renderwin */
899 static void renderwin_renderinfo_cb(RenderStats *rs)
900 {
901         
902         if(render_win) {
903                 
904                 make_renderinfo_string(rs, render_win->render_text);
905                 
906 #ifdef __APPLE__
907 #else
908                 glDrawBuffer(GL_FRONT);
909 #endif
910                 renderwin_draw_render_info(render_win);
911                 
912 #ifdef __APPLE__
913                 window_swap_buffers(render_win->win);
914 #else
915                 glFlush();
916                 glDrawBuffer(GL_BACK);
917 #endif
918         }
919
920 }
921
922 /* -------------- callback system to allow ESC from rendering ----------------------- */
923
924 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
925 static int test_break(void)
926 {
927
928         if(G.afbreek==2) { /* code for testing queue */
929
930                 G.afbreek= 0;
931
932                 blender_test_break(); /* tests blender interface */
933
934                 if (G.afbreek==0 && render_win) { /* tests window */
935                         winlay_process_events(0);
936                         // render_win can be closed in winlay_process_events()
937                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
938                                 G.afbreek= 1;
939                         }
940                 }
941         }
942
943         if(G.afbreek==1) return 1;
944         else return 0;
945 }
946
947
948
949 #ifdef _WIN32
950 /* we use the multimedia time here */
951 static UINT uRenderTimerId;
952
953 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
954 {
955         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
956 }
957
958 /* WIN32: init SetTimer callback */
959 static void init_test_break_callback()
960 {
961         timeBeginPeriod(50);
962         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
963 }
964
965 /* WIN32: stop SetTimer callback */
966 static void end_test_break_callback()
967 {
968         timeEndPeriod(50);
969         timeKillEvent(uRenderTimerId);
970 }
971
972 #else
973 /* all other OS's support signal(SIGVTALRM) */
974
975 /* XXX The ESC problem: some unix users reported that ESC doesn't cancel
976  * renders anymore. Most complaints came from linux, but it's not
977  * general, not all linux users have the problem.
978  *
979  * From tests, the systems that do have it are not signalling SIGVTALRM
980  * interrupts (an issue with signals and threads). Using SIGALRM instead
981  * fixes the problem, at least while we investigate better.
982  *
983  * ITIMER_REAL (SIGALRM): timer that counts real system time
984  * ITIMER_VIRTUAL (SIGVTALRM): only counts time spent in its owner process */
985
986 /* POSIX: this function goes in the signal() callback */
987 static void interruptESC(int sig)
988 {
989
990         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
991
992         /* call again, timer was reset */
993         signal(SIGALRM, interruptESC);
994 }
995
996 /* POSIX: initialize timer and signal */
997 static void init_test_break_callback()
998 {
999
1000         struct itimerval tmevalue;
1001
1002         tmevalue.it_interval.tv_sec = 0;
1003         tmevalue.it_interval.tv_usec = 250000;
1004         /* when the first ? */
1005         tmevalue.it_value.tv_sec = 0;
1006         tmevalue.it_value.tv_usec = 10000;
1007
1008         signal(SIGALRM, interruptESC);
1009         setitimer(ITIMER_REAL, &tmevalue, 0);
1010 }
1011
1012 /* POSIX: stop timer and callback */
1013 static void end_test_break_callback()
1014 {
1015         struct itimerval tmevalue;
1016
1017         memset(&tmevalue, 0, sizeof(struct itimerval));
1018
1019         setitimer(ITIMER_REAL, &tmevalue, 0);
1020         signal(SIGALRM, SIG_IGN);
1021
1022 }
1023
1024
1025 #endif
1026
1027
1028
1029 /* -------------- callbacks for render loop: init & run! ----------------------- */
1030
1031
1032 /* - initialize displays
1033    - set callbacks
1034    - cleanup
1035 */
1036
1037 static void do_render(int anim)
1038 {
1039         Render *re= RE_NewRender(G.scene->id.name);
1040         unsigned int lay= G.scene->lay;
1041         int scemode= G.scene->r.scemode;
1042         
1043         /* UGLY! we set this flag to prevent renderwindow queue to execute another render */
1044         /* is reset in RE_BlenderFrame */
1045         G.rendering= 1;
1046
1047         /* set render callbacks, also starts ESC timer */
1048         BIF_init_render_callbacks(re, 1);
1049         
1050         waitcursor(1);
1051         if(render_win) 
1052                 window_set_cursor(render_win->win, CURSOR_WAIT);
1053         
1054         if(G.obedit)
1055                 exit_editmode(0);       /* 0 = no free data */
1056
1057         /* allow localview render for objects with lights in normal layers */
1058         if(curarea->spacetype==SPACE_VIEW3D) {
1059                 if(G.vd->lay & 0xFF000000) {
1060                         G.scene->lay |= G.vd->lay;
1061                         G.scene->r.scemode |= R_SINGLE_LAYER;
1062                 }
1063                 else G.scene->lay= G.vd->lay;
1064         }
1065         
1066         if(anim)
1067                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
1068         else
1069                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
1070
1071         /* restore local view exception */
1072         G.scene->lay= lay;
1073         G.scene->r.scemode= scemode;
1074         
1075         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
1076         
1077         free_filesel_spec(G.scene->r.pic);
1078
1079         G.afbreek= 0;
1080         BIF_end_render_callbacks();
1081         
1082         /* after an envmap creation...  */
1083 //              if(R.flag & R_REDRAW_PRV) {
1084 //                      BIF_preview_changed(ID_TE);
1085 //              }
1086                 
1087         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1088         
1089         waitcursor(0);
1090 }
1091
1092 static void renderwin_store_spare(void)
1093 {
1094         RenderResult rres;
1095         
1096         if(render_win==0 || render_win->storespare==0)
1097                 return;
1098
1099         if(render_win->showspare) {
1100                 render_win->showspare= 0;
1101                 window_set_title(render_win->win, renderwin_get_title(1));
1102         }
1103         
1104         BLI_strncpy(render_win->render_text_spare, render_win->render_text, RW_MAXTEXT);
1105         
1106         if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1107         render_win->rectspare= NULL;
1108         if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1109         render_win->rectsparef= NULL;
1110         
1111         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1112         
1113         if(rres.rect32)
1114                 render_win->rectspare= MEM_dupallocN(rres.rect32);
1115         else if(rres.rectf)
1116                 render_win->rectsparef= MEM_dupallocN(rres.rectf);
1117
1118         render_win->sparex= rres.rectx;
1119         render_win->sparey= rres.recty;
1120 }
1121
1122 /* -------------- API: externally called --------------- */
1123
1124 static void error_cb(char *str){error(str);}
1125 static int esc_timer_set= 0;
1126
1127 /* set callbacks, exported to sequence render too. 
1128    Only call in foreground (UI) renders. */
1129
1130 void BIF_init_render_callbacks(Render *re, int do_display)
1131 {
1132         if(do_display) {
1133                 if(G.displaymode!=R_DISPLAYWIN) {
1134                         if(render_win)
1135                                 BIF_close_render_display();
1136                         imagewindow_render_callbacks(re);
1137                 }
1138                 else {
1139                         RE_display_init_cb(re, renderwin_init_display_cb);
1140                         RE_display_draw_cb(re, renderwin_progress_display_cb);
1141                         RE_display_clear_cb(re, renderwin_clear_display_cb);
1142                         RE_stats_draw_cb(re, renderwin_renderinfo_cb);
1143                 }
1144         }
1145         
1146         RE_error_cb(re, error_cb);
1147         
1148         G.afbreek= 0;
1149         if(render_win)
1150                 render_win->flags &= ~RW_FLAGS_ESCAPE;
1151
1152         /* start esc timer. ensure it happens once only */
1153         if(esc_timer_set==0)
1154                 init_test_break_callback();
1155         esc_timer_set++;
1156         
1157         RE_test_break_cb(re, test_break);
1158         RE_timecursor_cb(re, set_timecursor);
1159         
1160 }
1161
1162 /* the init/end callbacks can be called multiple times (sequence render) */
1163 void BIF_end_render_callbacks(void)
1164 {
1165         esc_timer_set--;
1166         if(esc_timer_set==0) {
1167                 end_test_break_callback();
1168                 
1169                 if(render_win)
1170                         mainwindow_make_active();
1171         }
1172 }
1173
1174 /* set up display, render an image or scene */
1175 void BIF_do_render(int anim)
1176 {
1177         int slink_flag = 0;
1178
1179         if (G.f & G_DOSCRIPTLINKS) {
1180                 BPY_do_all_scripts(SCRIPT_RENDER);
1181                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1182                         G.f &= ~G_DOSCRIPTLINKS;
1183                         slink_flag = 1;
1184                 }
1185         }
1186         
1187         if(render_win && render_win->showspare)
1188                 renderwin_store_spare();
1189
1190         do_render(anim);
1191
1192         if(G.scene->use_nodes) {
1193                 allqueue(REDRAWNODE, 1);
1194                 allqueue(REDRAWIMAGE, 1);
1195         }
1196         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1197         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1198 }
1199
1200 /* set up display, render the current area view in an image */
1201 /* the RE_Render is only used to make sure we got the picture in the result */
1202 void BIF_do_ogl_render(View3D *v3d, int anim)
1203 {
1204         Render *re= RE_NewRender(G.scene->id.name);
1205         RenderResult *rr;
1206         int winx, winy;
1207         
1208         G.afbreek= 0;
1209         init_test_break_callback();
1210         
1211         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1212         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1213         
1214         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1215         
1216         /* for now, result is defaulting to floats still... */
1217         rr= RE_GetResult(re);
1218         if(rr->rect32==NULL)
1219                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1220         
1221         /* open window */
1222         renderwin_init_display_cb(rr);
1223         if(render_win)
1224                 render_win->flags &= ~RW_FLAGS_ESCAPE;
1225
1226         init_gl_stuff();
1227         
1228         waitcursor(1);
1229
1230         if(anim) {
1231                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1232                 int cfrao= CFRA;
1233                 
1234                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1235                         mh->start_movie(&G.scene->r, winx, winy);
1236                 
1237                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1238                         /* user event can close window */
1239                         if(render_win==NULL)
1240                                 break;
1241                         drawview3d_render(v3d, winx, winy);
1242                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1243                         window_swap_buffers(render_win->win);
1244                         
1245                         if(BKE_imtype_is_movie(G.scene->r.imtype)) {
1246                                 mh->append_movie(CFRA, rr->rect32, winx, winy);
1247                                 printf("Append frame %d", G.scene->r.cfra);
1248                         }
1249                         else {
1250                                 ImBuf *ibuf= IMB_allocImBuf(winx, winy, G.scene->r.planes, 0, 0);
1251                                 char name[FILE_MAXDIR+FILE_MAXFILE];
1252                                 int ok;
1253                                 
1254                                 BKE_makepicstring(name, (G.scene->r.cfra));
1255
1256                                 ibuf->rect= rr->rect32;    
1257                                 ok= BKE_write_ibuf(ibuf, name, G.scene->r.imtype, G.scene->r.subimtype, G.scene->r.quality);
1258                                 
1259                                 if(ok==0) {
1260                                         printf("Write error: cannot save %s\n", name);
1261                                         break;
1262                                 }
1263                                 else printf("Saved: %s", name);
1264                                 
1265                 /* imbuf knows which rects are not part of ibuf */
1266                                 IMB_freeImBuf(ibuf);    
1267                         }
1268                         /* movie stats prints have no line break */
1269                         printf("\n");
1270                         
1271                         if(test_break()) break;
1272                 }
1273                 
1274                 if(BKE_imtype_is_movie(G.scene->r.imtype))
1275                         mh->end_movie();
1276                 
1277                 CFRA= cfrao;
1278         }
1279         else {
1280                 drawview3d_render(v3d, winx, winy);
1281                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1282                 window_swap_buffers(render_win->win);
1283         }
1284         
1285         if(render_win)
1286                 renderwin_draw(render_win, 0);
1287
1288         mainwindow_make_active();
1289         
1290         if(anim)
1291                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1292         
1293         end_test_break_callback();
1294         waitcursor(0);
1295 }
1296
1297 void BIF_redraw_render_rect(void)
1298 {
1299         /* redraw */
1300         if (render_win) {
1301                 renderwin_queue_redraw(render_win);
1302         }
1303         else {
1304                 Image *ima = (Image *)find_id("IM", "Render Result");
1305                 if(ima && ima->ibuf) {
1306                         IMB_freeImBuf(ima->ibuf);
1307                         ima->ibuf= NULL;
1308                         allqueue(REDRAWIMAGE, 0);
1309                 }
1310         }
1311 }       
1312
1313 void BIF_swap_render_rects(void)
1314 {
1315         RenderResult rres;
1316         
1317         if (render_win==NULL) return;
1318         
1319         render_win->storespare= 1;
1320         render_win->showspare ^= 1;
1321                 
1322         RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1323                 
1324         if(render_win->sparex!=rres.rectx || render_win->sparey!=rres.recty) {
1325                 if(render_win->rectspare) MEM_freeN(render_win->rectspare);
1326                 render_win->rectspare= NULL;
1327                 if(render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1328                 render_win->rectsparef= NULL;
1329         }
1330         
1331         window_set_title(render_win->win, renderwin_get_title(1));
1332
1333         /* redraw */
1334         BIF_redraw_render_rect();
1335
1336 }                               
1337
1338 /* called from usiblender.c too, to free and close renderwin */
1339 void BIF_close_render_display(void)
1340 {
1341         if (render_win) {
1342                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1343                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1344                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1345                 if (render_win->rectspare) MEM_freeN(render_win->rectspare);
1346                 if (render_win->rectsparef) MEM_freeN(render_win->rectsparef);
1347                         
1348                 window_destroy(render_win->win); /* ghost close window */
1349                 MEM_freeN(render_win);
1350
1351                 render_win= NULL;
1352         }
1353 }
1354
1355
1356 /* typical with F11 key, show image or hide/close */
1357 void BIF_toggle_render_display(void) 
1358 {
1359         
1360         if (G.displaymode!=R_DISPLAYWIN) {
1361                 imagewindow_toggle_render();
1362         }
1363         else {
1364                 if (render_win) {
1365                         if(render_win->active) {
1366                                 mainwindow_raise();
1367                                 mainwindow_make_active();
1368                                 render_win->active= 0;
1369                         }
1370                         else {
1371                                 window_raise(render_win->win);
1372                                 window_make_active(render_win->win);
1373                                 render_win->active= 1;
1374                         }
1375                 } 
1376                 else {
1377                         RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name));
1378                         if(rr) renderwin_init_display_cb(rr);
1379                 }
1380         }
1381 }
1382
1383 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1384 {
1385         if (render_win) {
1386                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1387         }
1388 }