4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
31 /* for the multimedia timer */
45 #include "BLI_winstuff.h"
47 /* for signal callback, not (fully) supported at windows */
55 #include "BLI_blenlib.h"
56 #include "BLI_threads.h"
58 #include "MEM_guardedalloc.h"
62 #include "DNA_image_types.h"
63 #include "DNA_space_types.h"
64 #include "DNA_screen_types.h"
65 #include "DNA_scene_types.h"
66 #include "DNA_view3d_types.h"
67 #include "DNA_vec_types.h"
69 #include "BKE_global.h"
70 #include "BKE_image.h"
71 #include "BKE_library.h"
72 #include "BKE_scene.h"
73 #include "BKE_utildefines.h"
74 #include "BKE_writeavi.h" /* movie handle */
76 #include "BIF_drawimage.h"
78 #include "BIF_glutil.h"
79 #include "BIF_graphics.h"
80 #include "BIF_screen.h"
81 #include "BIF_space.h"
82 #include "BIF_mywindow.h"
83 #include "BIF_renderwin.h"
84 #include "BIF_resources.h"
85 #include "BIF_toets.h"
86 #include "BIF_toolbox.h"
87 #include "BIF_writeimage.h"
89 #include "BDR_sculptmode.h"
90 #include "BDR_editobject.h"
92 #ifndef DISABLE_PYTHON
93 #include "BPY_extern.h" /* for BPY_do_all_scripts */
97 #include "BSE_drawview.h"
98 #include "BSE_filesel.h"
99 #include "BSE_headerbuttons.h"
101 #include "RE_pipeline.h"
103 #include "IMB_imbuf.h"
104 #include "IMB_imbuf_types.h"
106 #include "GPU_draw.h"
109 #include "mydevice.h"
112 /* ------------ renderwin struct, to prevent too much global vars --------- */
113 /* ------------ only used for display in a 2nd window --------- */
116 /* flags escape presses during event handling
117 * so we can test for user break later.
119 #define RW_FLAGS_ESCAPE (1<<0)
120 /* old zoom style (2x, locked to mouse, exits
121 * when mouse leaves window), to be removed
124 #define RW_FLAGS_OLDZOOM (1<<1)
125 /* on when image is being panned with middlemouse
127 #define RW_FLAGS_PANNING (1<<2)
128 /* on when the mouse is dragging over the image
129 * to examine pixel values.
131 #define RW_FLAGS_PIXEL_EXAMINING (1<<3)
133 /* forces draw of alpha */
134 #define RW_FLAGS_ALPHA (1<<4)
140 int rectx, recty; /* size of image */
142 float zoom, zoomofs[2];
150 float pan_mouse_start[2], pan_ofs_start[2];
156 typedef struct RenderSpare {
159 short storespare, showspare;
160 char *render_text_spare;
163 static RenderWin *render_win= NULL;
164 static RenderSpare *render_spare= NULL;
165 static char *render_text= NULL;
167 /* --------------- help functions for RenderWin struct ---------------------------- */
169 static RenderSpare *renderspare_alloc()
171 RenderSpare *rspare= MEM_callocN(sizeof(*rspare), "RenderSpare");
172 rspare->render_text_spare= MEM_callocN(RW_MAXTEXT, "rendertext spare");
177 /* only called in function open_renderwin */
178 static RenderWin *renderwin_alloc(Window *win)
180 RenderWin *rw= MEM_callocN(sizeof(*rw), "RenderWin");
185 rw->zoomofs[0]= rw->zoomofs[1]= 0;
188 rw->lmouse[0]= rw->lmouse[1]= 0;
189 rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= rw->mbut[3] = rw->mbut[4] = 0;
195 static void renderwin_queue_redraw(RenderWin *rw)
197 window_queue_redraw(rw->win); // to ghost
200 static void renderwin_reshape(RenderWin *rw)
205 static void renderwin_get_fullrect(RenderWin *rw, float fullrect_r[2][2])
207 float display_w, display_h;
208 float cent_x, cent_y;
211 window_get_size(rw->win, &w, &h);
214 display_w= rw->rectx*rw->zoom;
215 display_h= rw->recty*rw->zoom;
216 cent_x= (rw->zoomofs[0] + rw->rectx/2)*rw->zoom;
217 cent_y= (rw->zoomofs[1] + rw->recty/2)*rw->zoom;
219 fullrect_r[0][0]= w/2 - cent_x;
220 fullrect_r[0][1]= h/2 - cent_y;
221 fullrect_r[1][0]= fullrect_r[0][0] + display_w;
222 fullrect_r[1][1]= fullrect_r[0][1] + display_h;
226 * Project window coordinate to image pixel coordinate.
227 * Returns true if resulting coordinate is within image.
229 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
231 float fullrect[2][2];
233 renderwin_get_fullrect(rw, fullrect);
235 imgco_r[0]= (int) ((winco[0]-fullrect[0][0])/rw->zoom);
236 imgco_r[1]= (int) ((winco[1]-fullrect[0][1])/rw->zoom);
238 return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<rw->rectx && imgco_r[1]<rw->recty);
242 * Project window coordinates to normalized device coordinates
243 * Returns true if resulting coordinate is within window.
245 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
249 window_get_size(rw->win, &w, &h);
252 ndc_r[0]= ((float)(win_co[0]*2)/(w-1) - 1.0f);
253 ndc_r[1]= ((float)(win_co[1]*2)/(h-1) - 1.0f);
255 return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
258 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
260 if (rw->info_text) MEM_freeN(rw->info_text);
261 rw->info_text= info_text?BLI_strdup(info_text):NULL;
264 static void renderwin_reset_view(RenderWin *rw)
268 if (rw->info_text) renderwin_set_infotext(rw, NULL);
270 /* now calculate a zoom for when image is larger than window */
271 window_get_size(rw->win, &w, &h);
274 if(rw->rectx>w || rw->recty>h) {
275 if(rw->rectx-w > rw->recty-h) rw->zoom= ((float)w)/((float)rw->rectx);
276 else rw->zoom= ((float)h)/((float)rw->recty);
280 rw->zoomofs[0]= rw->zoomofs[1]= 0;
281 renderwin_queue_redraw(rw);
284 static void renderwin_draw_render_info(RenderWin *rw)
286 /* render text is added to top */
292 window_get_size(rw->win, &rect.xmax, &rect.ymax);
294 rect.ymin= rect.ymax-RW_HEADERY;
295 glEnable(GL_SCISSOR_TEST);
296 glaDefine2DArea(&rect);
298 /* clear header rect */
299 BIF_SetTheme(NULL); // sets view3d theme by default
300 BIF_GetThemeColor3fv(TH_HEADER, colf);
301 glClearColor(colf[0], colf[1], colf[2], 1.0);
302 glClear(GL_COLOR_BUFFER_BIT);
304 str= BIF_render_text();
307 BIF_ThemeColor(TH_TEXT);
308 glRasterPos2i(12, 5);
309 BMF_DrawString(G.fonts, str);
312 BIF_SetTheme(curarea); // restore theme
317 static void renderwin_draw(RenderWin *rw, int just_clear)
321 float fullrect[2][2];
322 int set_back_mainwindow;
325 /* since renderwin uses callbacks (controlled by ghost) it can
326 mess up active window output with redraw events after a render.
327 this is patchy, still WIP */
328 set_back_mainwindow = (winlay_get_active_window() != rw->win);
329 window_make_active(rw->win);
331 rect.xmin= rect.ymin= 0;
332 window_get_size(rw->win, &rect.xmax, &rect.ymax);
333 rect.ymax-= RW_HEADERY;
335 renderwin_get_fullrect(rw, fullrect);
337 /* do this first, so window ends with correct scissor */
338 renderwin_draw_render_info(rw);
340 glEnable(GL_SCISSOR_TEST);
341 glaDefine2DArea(&rect);
343 glClearColor(.1875, .1875, .1875, 1.0);
344 glClear(GL_COLOR_BUFFER_BIT);
348 glRectfv(fullrect[0], fullrect[1]);
350 RenderSpare *rspare= render_spare;
352 if(rspare && rspare->showspare) {
356 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
357 ibuf= BKE_image_get_ibuf(ima, NULL);
362 IMB_rect_from_float(ibuf);
364 glPixelZoom(rw->zoom, rw->zoom);
365 if(rw->flags & RW_FLAGS_ALPHA) {
367 /* swap bytes, so alpha is most significant one, then just draw it as luminance int */
368 if(G.order==B_ENDIAN)
369 glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
370 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_UNSIGNED_INT, ibuf->rect);
371 glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
374 float *trectf= MEM_mallocN(ibuf->x*ibuf->y*4, "temp");
377 for(a= ibuf->x*ibuf->y -1, b= 4*a+3; a>=0; a--, b-=4)
378 trectf[a]= ibuf->rect_float[b];
380 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_FLOAT, trectf);
386 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
387 else if(ibuf->rect_float)
388 glaDrawPixelsSafe_to32(fullrect[0][0], fullrect[0][1], ibuf->x, ibuf->y, ibuf->x, ibuf->rect_float);
390 glPixelZoom(1.0, 1.0);
394 /* info text is overlayed on bottom */
397 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
399 w=186.0*strlen(rw->info_text)/30;
400 glColor4f(.5,.5,.5,.25);
401 glRectf(0.0,0.0,w,30.0);
403 glColor3ub(255, 255, 255);
404 glRasterPos2i(10, 10);
405 BMF_DrawString(G.font, rw->info_text);
408 window_swap_buffers(rw->win);
410 if (set_back_mainwindow) mainwindow_make_active();
414 /* ------ interactivity calls for RenderWin ------------- */
415 static void renderwin_zoom(RenderWin *rw, int ZoomIn) {
418 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
423 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
427 if (rw->zoom>1.0) rw->flags |= RW_FLAGS_OLDZOOM;
428 if (rw->zoom==1.0) rw->flags &= ~RW_FLAGS_OLDZOOM;
429 renderwin_queue_redraw(rw);
433 static void renderwin_mouse_moved(RenderWin *rw)
437 RenderSpare *rspare= render_spare;
439 if(rspare && rspare->showspare) {
443 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
444 ibuf= BKE_image_get_ibuf(ima, NULL);
450 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
455 if (renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
456 ofs= sprintf(buf, "X: %d Y: %d ", imgco[0], imgco[1]);
458 pxl= (char*) &ibuf->rect[ibuf->x*imgco[1] + imgco[0]];
459 ofs+= sprintf(buf+ofs, " | R: %d G: %d B: %d A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);
461 if (ibuf->rect_float) {
462 float *pxlf= ibuf->rect_float + 4*(ibuf->x*imgco[1] + imgco[0]);
464 ofs+= sprintf(buf+ofs, " | R: %d G: %d B: %d A: %d", FTOCHAR(pxlf[0]), FTOCHAR(pxlf[1]), FTOCHAR(pxlf[2]), FTOCHAR(pxlf[3]));
466 ofs+= sprintf(buf+ofs, " | R: %.3f G: %.3f B: %.3f A: %.3f ", pxlf[0], pxlf[1], pxlf[2], pxlf[3]);
468 if (ibuf->zbuf_float) {
469 float *pxlz= &ibuf->zbuf_float[ibuf->x*imgco[1] + imgco[0]];
470 sprintf(buf+ofs, "| Z: %.3f", *pxlz );
473 renderwin_set_infotext(rw, buf);
474 renderwin_queue_redraw(rw);
476 renderwin_set_infotext(rw, NULL);
477 renderwin_queue_redraw(rw);
480 else if (rw->flags & RW_FLAGS_PANNING) {
481 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
482 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
484 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
485 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
486 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -ibuf->x/2, ibuf->x/2);
487 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -ibuf->y/2, ibuf->y/2);
489 renderwin_queue_redraw(rw);
491 else if (rw->flags & RW_FLAGS_OLDZOOM) {
495 window_get_size(rw->win, &w, &h);
497 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
499 rw->zoomofs[0]= -0.5*ndc[0]*(w-ibuf->x*rw->zoom)/rw->zoom;
500 rw->zoomofs[1]= -0.5*ndc[1]*(h-ibuf->y*rw->zoom)/rw->zoom;
502 renderwin_queue_redraw(rw);
506 static void renderwin_mousebut_changed(RenderWin *rw)
509 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
511 else if (rw->mbut[1]) {
512 rw->flags|= RW_FLAGS_PANNING;
513 rw->pan_mouse_start[0]= rw->lmouse[0];
514 rw->pan_mouse_start[1]= rw->lmouse[1];
515 rw->pan_ofs_start[0]= rw->zoomofs[0];
516 rw->pan_ofs_start[1]= rw->zoomofs[1];
517 } else if (rw->mbut[3]) {
518 renderwin_zoom(rw, 0);
520 } else if (rw->mbut[4]) {
521 renderwin_zoom(rw, 1);
524 if (rw->flags & RW_FLAGS_PANNING) {
525 rw->flags &= ~RW_FLAGS_PANNING;
526 renderwin_queue_redraw(rw);
528 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
529 rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
530 renderwin_set_infotext(rw, NULL);
531 renderwin_queue_redraw(rw);
537 /* handler for renderwin, passed on to Ghost */
538 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
540 RenderWin *rw= user_data;
542 // added this for safety, while render it's just creating bezerk results
544 if(evt==ESCKEY && val)
545 rw->flags|= RW_FLAGS_ESCAPE;
550 renderwin_reshape(rw);
552 else if (evt==REDRAW) {
553 renderwin_draw(rw, 0);
555 else if (evt==WINCLOSE) {
556 BIF_close_render_display();
558 else if (evt==INPUTCHANGE) {
561 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
562 rw->flags&= ~RW_FLAGS_OLDZOOM;
563 renderwin_reset_view(rw);
566 else if (ELEM(evt, MOUSEX, MOUSEY)) {
567 rw->lmouse[evt==MOUSEY]= val;
568 renderwin_mouse_moved(rw);
570 else if (ELEM(evt, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
571 int which=(evt==WHEELUPMOUSE?3:4);
573 renderwin_mousebut_changed(rw);
575 else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
576 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
577 rw->mbut[which]= val;
578 renderwin_mousebut_changed(rw);
582 if (rw->flags&RW_FLAGS_OLDZOOM) {
583 rw->flags&= ~RW_FLAGS_OLDZOOM;
584 renderwin_reset_view(rw);
587 rw->flags|= RW_FLAGS_ESCAPE;
589 mainwindow_make_active();
593 else if( evt==AKEY) {
594 rw->flags ^= RW_FLAGS_ALPHA;
595 renderwin_queue_redraw(render_win);
597 else if (evt==JKEY) {
598 if(G.rendering==0) BIF_swap_render_rects();
600 else if (evt==ZKEY) {
601 if (rw->flags&RW_FLAGS_OLDZOOM) {
602 rw->flags&= ~RW_FLAGS_OLDZOOM;
603 renderwin_reset_view(rw);
606 rw->flags|= RW_FLAGS_OLDZOOM;
607 renderwin_mouse_moved(rw);
610 else if (ELEM(evt,PADPLUSKEY,PAGEUPKEY)) {
611 renderwin_zoom(rw, 0);
613 else if (ELEM(evt,PADMINUS,PAGEDOWNKEY)) {
614 renderwin_zoom(rw, 1);
616 else if (evt==PADENTER || evt==HOMEKEY) {
617 if (rw->flags&RW_FLAGS_OLDZOOM) {
618 rw->flags&= ~RW_FLAGS_OLDZOOM;
620 renderwin_reset_view(rw);
622 else if (evt==F3KEY) {
625 mainwindow_make_active();
627 areawinset(find_biggest_area()->win);
628 BIF_save_rendered_image_fs();
631 else if (evt==F11KEY) {
632 BIF_toggle_render_display();
634 else if (evt==F12KEY) {
641 static char *renderwin_get_title()
645 if(BIF_show_render_spare()) {
646 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (previous)";
647 else title = "Blender:Render (previous)";
650 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
651 else title = "Blender:Render";
657 /* opens window and allocs struct */
658 static void open_renderwin(int winpos[2], int winsize[2], int imagesize[2])
660 extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
664 title= renderwin_get_title();
665 win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
667 render_win= renderwin_alloc(win);
668 render_win->rectx= imagesize[0];
669 render_win->recty= imagesize[1];
671 /* Ghost calls handler */
672 window_set_handler(win, renderwin_handler, render_win);
674 winlay_process_events(0);
675 window_make_active(render_win->win);
676 winlay_process_events(0);
678 /* mywindow has to know about it too */
679 mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
680 /* and we should be able to draw 3d in it */
683 renderwin_draw(render_win, 1);
684 renderwin_draw(render_win, 1);
687 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
689 /* calculations for window size and position */
690 void calc_renderwin_rectangle(int rectx, int recty, int posmask, int renderpos_r[2], int rendersize_r[2])
692 int scr_w, scr_h, x, y, div= 0;
693 float ndc_x= 0.0, ndc_y= 0.0;
695 winlay_get_screensize(&scr_w, &scr_h);
697 rendersize_r[0]= rectx;
698 rendersize_r[1]= recty;
700 rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);
701 rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);
703 for (y=-1; y<=1; y++) {
704 for (x=-1; x<=1; x++) {
705 if (posmask & (1<<((y+1)*3 + (x+1)))) {
718 renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
720 /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
721 rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);
722 renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
724 renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
728 /* init renderwin, alloc/open/resize */
729 static void renderwin_init_display_cb(RenderResult *rr)
731 if (G.afbreek != 1) {
732 int rendersize[2], renderpos[2], imagesize[2];
734 calc_renderwin_rectangle(rr->rectx, rr->recty, G.winpos, renderpos, rendersize);
736 imagesize[0]= rr->rectx;
737 imagesize[1]= rr->recty;
740 open_renderwin(renderpos, rendersize, imagesize);
741 renderwin_reset_view(render_win); // incl. autozoom for large images
746 window_get_position(render_win->win, &win_x, &win_y);
747 window_get_size(render_win->win, &win_w, &win_h);
750 /* XXX, this is nasty and I guess bound to cause problems,
751 * but to ensure the window is at the user specified position
752 * and size we reopen the window all the time... we need
753 * a ghost _set_position to fix this -zr
756 /* XXX, well... it is nasty yes, and reopens windows each time on
757 subsequent renders. Better rule is to make it reopen only only
758 size change, and use the preferred position only on open_renderwin
761 if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
762 BIF_close_render_display();
763 open_renderwin(renderpos, rendersize, imagesize);
766 window_raise(render_win->win);
767 window_make_active(render_win->win);
769 mywinset(2); // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
772 win_rct.xmin= win_rct.ymin= 0;
773 window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
774 win_rct.ymax-= RW_HEADERY;
775 glaDefine2DArea(&win_rct);
779 renderwin_reset_view(render_win);
780 render_win->active= 1;
782 /* make sure we are in normal draw again */
783 render_win->flags &= ~RW_FLAGS_ALPHA;
789 /* callback for redraw render win */
790 static void renderwin_clear_display_cb(RenderResult *rr)
793 window_make_active(render_win->win);
794 renderwin_draw(render_win, 1);
798 /* XXX, this is not good, we do this without any regard to state
799 * ... better is to make this an optimization of a more clear
800 * implementation. the bug shows up when you do something like
801 * open the window, then draw part of the progress, then get
802 * a redraw event. whatever can go wrong will. -zr
804 * Note: blocked queue handling while rendering to prevent that (ton)
807 /* can get as well the full picture, as the parts while rendering */
808 static void renderwin_progress(RenderWin *rw, RenderResult *rr, volatile rcti *renrect)
811 float *rectf= NULL, fullrect[2][2];
812 unsigned int *rect32= NULL;
813 int ymin, ymax, xmin, xmax;
815 /* if renrect argument, we only display scanlines */
817 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
818 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
821 /* xmin here is first subrect x coord, xmax defines subrect width */
822 xmin = renrect->xmin;
823 xmax = renrect->xmax - xmin;
827 ymax= renrect->ymax - ymin;
830 renrect->ymin= renrect->ymax;
834 xmax = rr->rectx - 2*rr->crop;
835 ymax = rr->recty - 2*rr->crop;
838 /* renderwindow cruft */
839 win_rct.xmin= win_rct.ymin= 0;
840 window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
841 win_rct.ymax-= RW_HEADERY;
842 renderwin_get_fullrect(rw, fullrect);
844 /* find current float rect for display, first case is after composit... still weak */
849 rect32= (unsigned int *)rr->rect32;
851 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
852 rectf= rr->renlay->rectf;
856 /* if scanline updates... */
857 rectf+= 4*(rr->rectx*ymin + xmin);
859 /* when rendering more pixels than needed, we crop away cruft */
861 rectf+= 4*(rr->crop*rr->rectx + rr->crop);
864 /* tilerect defines drawing offset from (0,0) */
865 /* however, tilerect (xmin, ymin) is first pixel */
866 fullrect[0][0] += (rr->tilerect.xmin + rr->crop + xmin)*rw->zoom;
867 fullrect[0][1] += (rr->tilerect.ymin + rr->crop + ymin)*rw->zoom;
869 glEnable(GL_SCISSOR_TEST);
870 glaDefine2DArea(&win_rct);
874 glDrawBuffer(GL_FRONT);
876 glPixelZoom(rw->zoom, rw->zoom);
879 glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32);
881 glaDrawPixelsSafe_to32(fullrect[0][0], fullrect[0][1], xmax, ymax, rr->rectx, rectf);
883 glPixelZoom(1.0, 1.0);
886 window_swap_buffers(render_win->win);
889 glDrawBuffer(GL_BACK);
894 /* in render window; display a couple of scanlines of rendered image */
895 static void renderwin_progress_display_cb(RenderResult *rr, volatile rcti *rect)
898 renderwin_progress(render_win, rr, rect);
902 /* -------------- callbacks for render loop: interactivity ----------------------- */
904 /* string is RW_MAXTEXT chars min */
905 void make_renderinfo_string(RenderStats *rs, char *str)
907 extern char info_time_str[32]; // header_info.c
908 uintptr_t mem_in_use, mmap_in_use;
909 float megs_used_memory, mmap_used_memory;
912 mem_in_use= MEM_get_memory_in_use();
913 mmap_in_use= MEM_get_mapped_memory_in_use();
915 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
916 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
918 if(G.scene->lay & 0xFF000000)
919 spos+= sprintf(spos, "Localview | ");
920 else if(G.scene->r.scemode & R_SINGLE_LAYER)
921 spos+= sprintf(spos, "Single Layer | ");
923 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (G.scene->r.cfra), rs->totvert, rs->totface);
924 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
925 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
926 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory);
929 spos+= sprintf(spos, "Field %d ", rs->curfield);
931 spos+= sprintf(spos, "Blur %d ", rs->curblur);
933 BLI_timestr(rs->lastframetime, info_time_str);
934 spos+= sprintf(spos, "Time:%s ", info_time_str);
937 spos+= sprintf(spos, "| %s ", rs->infostr);
939 /* very weak... but 512 characters is quite safe... we cannot malloc during thread render */
940 if(spos >= str+RW_MAXTEXT)
941 printf("WARNING! renderwin text beyond limit \n");
945 /* callback for print info in top header of renderwin */
946 static void renderwin_renderinfo_cb(RenderStats *rs)
951 BIF_make_render_text(rs);
955 glDrawBuffer(GL_FRONT);
957 renderwin_draw_render_info(render_win);
960 window_swap_buffers(render_win->win);
963 glDrawBuffer(GL_BACK);
969 /* -------------- callback system to allow ESC from rendering ----------------------- */
971 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
972 static int test_break(void)
975 if(G.afbreek==2) { /* code for testing queue */
979 blender_test_break(); /* tests blender interface */
981 if (G.afbreek==0 && render_win) { /* tests window */
982 winlay_process_events(0);
983 // render_win can be closed in winlay_process_events()
984 if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
990 if(G.afbreek==1) return 1;
997 /* we use the multimedia time here */
998 static UINT uRenderTimerId;
1000 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
1002 if(G.afbreek==0) G.afbreek= 2; /* code for read queue */
1005 /* WIN32: init SetTimer callback */
1006 static void init_test_break_callback()
1008 timeBeginPeriod(50);
1009 uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
1012 /* WIN32: stop SetTimer callback */
1013 static void end_test_break_callback()
1016 timeKillEvent(uRenderTimerId);
1020 /* all other OS's support signal(SIGVTALRM/SIGALRM) */
1022 /* XXX The ESC problem: some unix users reported that ESC doesn't cancel
1023 * renders anymore. Most complaints came from linux, but it's not
1024 * general, not all linux users have the problem.
1026 * From tests, the systems that do have it are not signalling SIGVTALRM
1027 * interrupts (an issue with signals and threads). Using SIGALRM instead
1028 * fixes the problem, at least while we investigate better.
1030 * ITIMER_REAL (SIGALRM): timer that counts real system time
1031 * ITIMER_VIRTUAL (SIGVTALRM): only counts time spent in its owner process
1033 * Addendum: now SIGVTALRM is used on Solaris again, because SIGALRM can
1034 * kill the process there! */
1036 /* POSIX: this function goes in the signal() callback */
1037 static void interruptESC(int sig)
1040 if(G.afbreek==0) G.afbreek= 2; /* code for read queue */
1042 /* call again, timer was reset */
1044 signal(SIGVTALRM, interruptESC);
1046 signal(SIGALRM, interruptESC);
1050 /* POSIX: initialize timer and signal */
1051 static void init_test_break_callback()
1054 struct itimerval tmevalue;
1056 tmevalue.it_interval.tv_sec = 0;
1057 tmevalue.it_interval.tv_usec = 250000;
1058 /* when the first ? */
1059 tmevalue.it_value.tv_sec = 0;
1060 tmevalue.it_value.tv_usec = 10000;
1063 signal(SIGVTALRM, interruptESC);
1064 setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1066 signal(SIGALRM, interruptESC);
1067 setitimer(ITIMER_REAL, &tmevalue, 0);
1071 /* POSIX: stop timer and callback */
1072 static void end_test_break_callback()
1074 struct itimerval tmevalue;
1076 memset(&tmevalue, 0, sizeof(struct itimerval));
1079 setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1080 signal(SIGVTALRM, SIG_IGN);
1082 setitimer(ITIMER_REAL, &tmevalue, 0);
1083 signal(SIGALRM, SIG_IGN);
1092 /* -------------- callbacks for render loop: init & run! ----------------------- */
1095 /* - initialize displays
1100 static void do_render(int anim)
1103 Render *re= RE_NewRender(G.scene->id.name);
1104 unsigned int lay= G.scene->lay;
1105 int scemode= G.scene->r.scemode;
1106 int sculptmode= G.f & G_SCULPTMODE;
1108 /* UGLY! we set this flag to prevent renderwindow queue to execute another render */
1109 /* is reset in RE_BlenderFrame */
1112 /* set render callbacks, also starts ESC timer */
1113 BIF_init_render_callbacks(re, 1);
1117 window_set_cursor(render_win->win, CURSOR_WAIT);
1120 exit_editmode(0); /* 0 = no free data */
1122 if(sculptmode) set_sculptmode();
1124 /* allow localview render for objects with lights in normal layers */
1125 if(curarea->spacetype==SPACE_VIEW3D) {
1126 /* if view is defined (might not be if called from script), check and set layers. */
1128 if(G.vd->lay & 0xFF000000) {
1129 G.scene->lay |= G.vd->lay;
1130 G.scene->r.scemode |= R_SINGLE_LAYER;
1132 else G.scene->lay= G.vd->lay;
1137 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra, G.scene->frame_step);
1139 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
1141 /* restore local view exception */
1143 G.scene->r.scemode= scemode;
1145 if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
1147 free_filesel_spec(G.scene->r.pic);
1150 BIF_end_render_callbacks();
1152 /* after an envmap creation... */
1153 // if(R.flag & R_REDRAW_PRV) {
1154 // BIF_preview_changed(ID_TE);
1157 scene_update_for_newframe(G.scene, G.scene->lay); // no redraw needed, this restores to view as we left it
1159 /* get a render result image, and make sure it is clean */
1160 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
1161 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
1163 if(sculptmode) set_sculptmode();
1168 /* called before render, store old render in spare buffer */
1169 static int render_store_spare(void)
1172 RenderSpare *rspare= render_spare;
1174 if(rspare==0 || rspare->storespare==0)
1177 /* only store when it does not show spare */
1178 if(rspare->showspare==0)
1181 rspare->showspare= 0;
1184 IMB_freeImBuf(rspare->ibuf);
1188 RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1190 rspare->ibuf= IMB_allocImBuf(rres.rectx, rres.recty, 32, 0, 0);
1191 rspare->ibuf->dither= G.scene->r.dither_intensity;
1194 rspare->ibuf->rect= MEM_dupallocN(rres.rect32);
1195 rspare->ibuf->flags |= IB_rect;
1196 rspare->ibuf->mall |= IB_rect;
1199 rspare->ibuf->rect_float= MEM_dupallocN(rres.rectf);
1200 rspare->ibuf->flags |= IB_rectfloat;
1201 rspare->ibuf->mall |= IB_rectfloat;
1204 rspare->ibuf->zbuf_float= MEM_dupallocN(rres.rectz);
1205 rspare->ibuf->flags |= IB_zbuffloat;
1206 rspare->ibuf->mall |= IB_zbuffloat;
1212 /* -------------- API: externally called --------------- */
1214 static void error_cb(char *str){error(str);}
1215 static int esc_timer_set= 0;
1217 /* set callbacks, exported to sequence render too.
1218 Only call in foreground (UI) renders. */
1220 void BIF_init_render_callbacks(Render *re, int do_display)
1223 if(G.displaymode!=R_DISPLAYWIN) {
1225 BIF_close_render_display();
1226 imagewindow_render_callbacks(re);
1229 RE_display_init_cb(re, renderwin_init_display_cb);
1230 RE_display_draw_cb(re, renderwin_progress_display_cb);
1231 RE_display_clear_cb(re, renderwin_clear_display_cb);
1232 RE_stats_draw_cb(re, renderwin_renderinfo_cb);
1236 RE_error_cb(re, error_cb);
1240 render_win->flags &= ~RW_FLAGS_ESCAPE;
1242 /* start esc timer. ensure it happens once only */
1243 if(esc_timer_set==0)
1244 init_test_break_callback();
1247 RE_test_break_cb(re, test_break);
1248 RE_timecursor_cb(re, set_timecursor);
1252 /* the init/end callbacks can be called multiple times (sequence render) */
1253 void BIF_end_render_callbacks(void)
1256 if(esc_timer_set==0) {
1257 end_test_break_callback();
1260 mainwindow_make_active();
1264 void BIF_store_spare(void)
1266 if(render_store_spare()) {
1268 BLI_strncpy(render_spare->render_text_spare, render_text, RW_MAXTEXT);
1271 window_set_title(render_win->win, renderwin_get_title());
1272 allqueue(REDRAWIMAGE, 0);
1276 /* set up display, render an image or scene */
1277 void BIF_do_render(int anim)
1279 #ifndef DISABLE_PYTHON
1280 if (G.f & G_DOSCRIPTLINKS)
1281 BPY_do_all_scripts(SCRIPT_RENDER, anim);
1288 if(G.scene->use_nodes) {
1289 allqueue(REDRAWNODE, 1);
1290 allqueue(REDRAWIMAGE, 1);
1292 if(G.scene->r.dither_intensity != 0.0f)
1293 BIF_redraw_render_rect();
1294 #ifndef DISABLE_PYTHON
1295 if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER, anim);
1299 void do_ogl_view3d_render(Render *re, View3D *v3d, int winx, int winy)
1303 update_for_newframe_muted(); /* here, since camera can be animated */
1305 if(v3d->persp==V3D_CAMOB && v3d->camera) {
1306 /* in camera view, use actual render winmat */
1307 RE_GetCameraWindow(re, v3d->camera, CFRA, winmat);
1308 drawview3d_render(v3d, NULL, winx, winy, winmat, 0);
1311 drawview3d_render(v3d, NULL, winx, winy, NULL, 0);
1314 /* set up display, render the current area view in an image */
1315 /* the RE_Render is only used to make sure we got the picture in the result */
1316 void BIF_do_ogl_render(View3D *v3d, int anim)
1318 Render *re= RE_NewRender(G.scene->id.name);
1323 init_test_break_callback();
1325 winx= (G.scene->r.size*G.scene->r.xsch)/100;
1326 winy= (G.scene->r.size*G.scene->r.ysch)/100;
1328 RE_InitState(re, NULL, &G.scene->r, winx, winy, NULL);
1330 /* for now, result is defaulting to floats still... */
1331 rr= RE_GetResult(re);
1332 if(rr->rect32==NULL)
1333 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1336 renderwin_init_display_cb(rr);
1338 render_win->flags &= ~RW_FLAGS_ESCAPE;
1345 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1350 if(BKE_imtype_is_movie(G.scene->r.imtype))
1351 mh->start_movie(&G.scene->r, winx, winy);
1353 for(nfra= SFRA, CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1354 /* user event can close window */
1355 if(render_win==NULL)
1359 if(G.scene->lay & 0xFF000000)
1360 lay= G.scene->lay & 0xFF000000;
1364 scene_update_for_newframe(G.scene, lay);
1368 do_ogl_view3d_render(re, v3d, winx, winy);
1369 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1370 if((G.scene->r.scemode & R_STAMP_INFO) && (G.scene->r.stamp & R_STAMP_DRAW)) {
1371 BKE_stamp_buf((unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
1373 window_swap_buffers(render_win->win);
1375 if(BKE_imtype_is_movie(G.scene->r.imtype)) {
1376 mh->append_movie(CFRA, rr->rect32, winx, winy);
1377 printf("Append frame %d", G.scene->r.cfra);
1380 ImBuf *ibuf= IMB_allocImBuf(winx, winy, G.scene->r.planes, 0, 0);
1381 char name[FILE_MAXDIR+FILE_MAXFILE];
1384 BKE_makepicstring(name, G.scene->r.pic, G.scene->r.cfra, G.scene->r.imtype);
1386 ibuf->rect= (unsigned int *)rr->rect32;
1387 ok= BKE_write_ibuf(ibuf, name, G.scene->r.imtype, G.scene->r.subimtype, G.scene->r.quality);
1390 printf("Write error: cannot save %s\n", name);
1393 else printf("Saved: %s", name);
1395 /* imbuf knows which rects are not part of ibuf */
1396 IMB_freeImBuf(ibuf);
1398 /* movie stats prints have no line break */
1401 if(test_break()) break;
1405 if(BKE_imtype_is_movie(G.scene->r.imtype))
1411 do_ogl_view3d_render(re, v3d, winx, winy);
1412 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1413 if((G.scene->r.scemode & R_STAMP_INFO) && (G.scene->r.stamp & R_STAMP_DRAW)) {
1414 BKE_stamp_buf((unsigned char *)rr->rect32, rr->rectf, rr->rectx, rr->recty, 3);
1416 window_swap_buffers(render_win->win);
1420 renderwin_draw(render_win, 0);
1422 mainwindow_make_active();
1425 scene_update_for_newframe(G.scene, G.scene->lay); // no redraw needed, this restores to view as we left it
1427 end_test_break_callback();
1431 void BIF_redraw_render_rect(void)
1435 renderwin_queue_redraw(render_win);
1436 allqueue(REDRAWIMAGE, 0);
1439 void BIF_swap_render_rects(void)
1442 RenderSpare *rspare;
1446 render_spare= renderspare_alloc();
1448 rspare= render_spare;
1449 rspare->storespare= 1;
1450 rspare->showspare ^= 1;
1452 RE_GetResultImage(RE_GetRender(G.scene->id.name), &rres);
1455 if(ibuf && (ibuf->x!=rres.rectx || ibuf->y!=rres.recty)) {
1456 IMB_freeImBuf(ibuf);
1461 window_set_title(render_win->win, renderwin_get_title());
1464 BIF_redraw_render_rect();
1467 ImBuf *BIF_render_spare_imbuf()
1469 return (render_spare)? render_spare->ibuf: NULL;
1472 int BIF_show_render_spare()
1474 return (render_spare && render_spare->showspare);
1477 char *BIF_render_text()
1479 if(render_spare && render_spare->showspare)
1480 return render_spare->render_text_spare;
1485 void BIF_make_render_text(RenderStats *rs)
1488 render_text= MEM_callocN(RW_MAXTEXT, "rendertext");
1489 make_renderinfo_string(rs, render_text);
1492 /* called from usiblender.c too, to free and close renderwin */
1494 void BIF_free_render_spare()
1496 RenderSpare *rspare= render_spare;
1499 MEM_freeN(render_text);
1504 if (rspare->render_text_spare) MEM_freeN(rspare->render_text_spare);
1505 if (rspare->ibuf) IMB_freeImBuf(rspare->ibuf);
1512 void BIF_close_render_display(void)
1515 if (render_win->info_text) MEM_freeN(render_win->info_text);
1516 window_destroy(render_win->win); /* ghost close window */
1517 MEM_freeN(render_win);
1524 /* typical with F11 key, show image or hide/close */
1525 void BIF_toggle_render_display(void)
1528 if (G.displaymode!=R_DISPLAYWIN) {
1529 imagewindow_toggle_render();
1533 if(render_win->active) {
1535 mainwindow_make_active();
1536 render_win->active= 0;
1539 window_raise(render_win->win);
1540 window_make_active(render_win->win);
1541 render_win->active= 1;
1545 RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name));
1546 if(rr) renderwin_init_display_cb(rr);
1551 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1554 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);