3 * ***** BEGIN GPL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 * The Original Code is Copyright (C) 2006 Blender Foundation.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
34 #include "DNA_group_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
38 #include "BKE_global.h"
39 #include "BKE_image.h"
41 #include "BKE_scene.h"
42 #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */
44 #include "MEM_guardedalloc.h"
46 #include "BLI_arithb.h"
47 #include "BLI_blenlib.h"
50 #include "IMB_imbuf.h"
51 #include "IMB_imbuf_types.h"
53 #include "RE_pipeline.h"
56 #include "BSE_sequence.h" /* <----------------- bad!!! */
59 #include "render_types.h"
60 #include "renderpipeline.h"
61 #include "renderdatabase.h"
62 #include "rendercore.h"
64 #include "initrender.h"
68 #include "SDL_thread.h"
69 #include "SDL_mutex.h"
75 - movie/image file init
76 - everything that doesn't change during animation
79 - camera, world, matrices
80 - make render verts, faces, halos, strands
81 - everything can change per frame/field
86 - layers/tiles optionally to disk or directly in Render Result
88 4) Composit Render Result
89 - also read external files etc
92 - save file or append in movie
97 /* ********* globals ******** */
99 /* here we store all renders */
100 static struct ListBase RenderList= {NULL, NULL};
102 /* hardcopy of current render, used while rendering for speed */
105 /* ********* alloc and free ******** */
108 SDL_mutex *malloc_lock= NULL;
110 void *RE_mallocN(int len, char *name)
113 if(malloc_lock) SDL_mutexP(malloc_lock);
114 mem= MEM_mallocN(len, name);
115 if(malloc_lock) SDL_mutexV(malloc_lock);
118 void *RE_callocN(int len, char *name)
121 if(malloc_lock) SDL_mutexP(malloc_lock);
122 mem= MEM_callocN(len, name);
123 if(malloc_lock) SDL_mutexV(malloc_lock);
126 void RE_freeN(void *poin)
128 if(malloc_lock) SDL_mutexP(malloc_lock);
130 if(malloc_lock) SDL_mutexV(malloc_lock);
133 /* ********************** */
136 /* default callbacks, set in each new render */
137 static void result_nothing(RenderResult *rr) {}
138 static void result_rcti_nothing(RenderResult *rr, rcti *rect) {}
139 static void stats_nothing(RenderStats *rs) {}
140 static void int_nothing(int val) {}
141 static int void_nothing(void) {return 0;}
142 static void print_error(const char *str) {printf("ERROR: %s\n", str);}
144 static void free_render_result(RenderResult *res)
146 if(res==NULL) return;
148 while(res->layers.first) {
149 RenderLayer *rl= res->layers.first;
150 if(rl->rectf) RE_freeN(rl->rectf);
151 if(rl->rectz) RE_freeN(rl->rectz);
152 BLI_remlink(&res->layers, rl);
157 RE_freeN(res->rect32);
159 RE_freeN(res->rectz);
161 RE_freeN(res->rectf);
166 /* called by main render as well for parts */
167 /* will read info from Render *re to define layers */
168 /* called in threads */
169 /* winrct is coordinate rect of entire image, partrct the part within */
170 static RenderResult *new_render_result(Render *re, rcti *partrct, int crop)
174 SceneRenderLayer *srl;
177 rectx= partrct->xmax - partrct->xmin;
178 recty= partrct->ymax - partrct->ymin;
180 if(rectx<=0 || recty<=0)
183 rr= RE_callocN(sizeof(RenderResult), "new render result");
186 /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */
189 /* tilerect is relative coordinates within render disprect. do not subtract crop yet */
190 rr->tilerect.xmin= partrct->xmin - re->disprect.xmin;
191 rr->tilerect.xmax= partrct->xmax - re->disprect.xmax;
192 rr->tilerect.ymin= partrct->ymin - re->disprect.ymin;
193 rr->tilerect.ymax= partrct->ymax - re->disprect.ymax;
195 /* copy, so display callbacks can find out too */
196 rr->actlay= re->r.actlay;
198 /* check renderdata for amount of layers */
199 for(srl= re->r.layers.first; srl; srl= srl->next) {
200 rl= RE_callocN(sizeof(RenderLayer), "new render layer");
201 BLI_addtail(&rr->layers, rl);
203 strcpy(rl->name, srl->name);
205 rl->layflag= srl->layflag;
206 rl->passflag= srl->passflag;
208 rl->rectf= RE_callocN(rectx*recty*sizeof(float)*4, "layer float rgba");
209 if(srl->passflag & SCE_PASS_Z)
210 rl->rectz= RE_callocN(rectx*recty*sizeof(float), "layer float Z");
213 /* previewrender and envmap don't do layers, so we make a default one */
214 if(rr->layers.first==NULL) {
215 rl= RE_callocN(sizeof(RenderLayer), "new render layer");
216 BLI_addtail(&rr->layers, rl);
218 rl->rectf= RE_callocN(rectx*recty*sizeof(float)*4, "prev/env float rgba");
219 rl->rectz= RE_callocN(rectx*recty*sizeof(float), "prev/env float Z");
221 /* note, this has to be in sync with scene.c */
223 rl->layflag= 0x7FFF; /* solid ztra halo strand */
224 rl->passflag= SCE_PASS_COMBINED|SCE_PASS_Z;
232 /* used when rendering to a full buffer, or when reading the exr part-layer-pass file */
233 /* no test happens here if it fits... we also assume layers are in sync */
234 /* is used within threads */
235 static void merge_render_result(RenderResult *rr, RenderResult *rrpart)
237 RenderLayer *rl, *rlp;
240 int y, height, len, copylen;
242 for(rl= rr->layers.first, rlp= rrpart->layers.first; rl && rlp; rl= rl->next, rlp= rlp->next) {
244 /* first combined and z pass */
245 if(rl->rectf && rlp->rectf) {
251 copylen=len= rrpart->rectx;
252 height= rrpart->recty;
254 if(rrpart->crop) { /* filters add pixel extra */
256 if(rzp) rzp+= rrpart->crop + rrpart->crop*len;
257 if(rfp) rfp+= 4*(rrpart->crop + rrpart->crop*len);
259 copylen= len-2*rrpart->crop;
260 height -= 2*rrpart->crop;
262 ofs= (rrpart->tilerect.ymin + rrpart->crop)*rr->rectx + (rrpart->tilerect.xmin+rrpart->crop);
264 rf= rl->rectf+ 4*ofs;
267 ofs= (rrpart->tilerect.ymin*rr->rectx + rrpart->tilerect.xmin);
269 rf= rl->rectf+ 4*ofs;
272 for(y=0; y<height; y++) {
274 memcpy(rz, rzp, 4*copylen);
279 memcpy(rf, rfp, 16*copylen);
289 /* *************************************************** */
291 Render *RE_GetRender(const char *name)
295 /* search for existing renders */
296 for(re= RenderList.first; re; re= re->next) {
297 if(strncmp(re->name, name, RE_MAXNAME)==0) {
304 /* if you want to know exactly what has been done */
305 RenderResult *RE_GetResult(Render *re)
312 /* fill provided result struct with what's currently active or done */
313 void RE_GetResultImage(Render *re, RenderResult *rr)
315 memset(rr, sizeof(RenderResult), 0);
317 if(re && re->result) {
320 rr->rectx= re->result->rectx;
321 rr->recty= re->result->recty;
323 rr->rectf= re->result->rectf;
324 rr->rectz= re->result->rectz;
325 rr->rect32= re->result->rect32;
328 rl= BLI_findlink(&re->result->layers, re->r.actlay);
331 rr->rectf= rl->rectf;
333 rr->rectz= rl->rectz;
338 #define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val))
339 /* caller is responsible for allocating rect in correct size! */
340 void RE_ResultGet32(Render *re, unsigned int *rect)
344 RE_GetResultImage(re, &rres);
346 memcpy(rect, rres.rect32, sizeof(int)*rres.rectx*rres.recty);
347 else if(rres.rectf) {
348 float *fp= rres.rectf;
349 int tot= rres.rectx*rres.recty;
350 char *cp= (char *)rect;
352 for(;tot>0; tot--, cp+=4, fp+=4) {
353 cp[0] = FTOCHAR(fp[0]);
354 cp[1] = FTOCHAR(fp[1]);
355 cp[2] = FTOCHAR(fp[2]);
356 cp[3] = FTOCHAR(fp[3]);
360 /* else fill with black */
361 memset(rect, sizeof(int)*re->rectx*re->recty, 0);
365 RenderStats *RE_GetStats(Render *re)
370 Render *RE_NewRender(const char *name)
374 /* only one render per name exists */
375 re= RE_GetRender(name);
377 BLI_remlink(&RenderList, re);
381 /* new render data struct */
382 re= RE_callocN(sizeof(Render), "new render");
383 BLI_addtail(&RenderList, re);
384 strncpy(re->name, name, RE_MAXNAME);
386 /* set default empty callbacks */
387 re->display_init= result_nothing;
388 re->display_clear= result_nothing;
389 re->display_draw= result_rcti_nothing;
390 re->timecursor= int_nothing;
391 re->test_break= void_nothing;
392 re->test_return= void_nothing;
393 re->error= print_error;
394 re->stats_draw= stats_nothing;
396 /* init some variables */
402 /* only call this while you know it will remove the link too */
403 void RE_FreeRender(Render *re)
406 free_renderdata_tables(re);
407 free_sample_tables(re);
409 free_render_result(re->result);
411 BLI_remlink(&RenderList, re);
416 void RE_FreeAllRender(void)
418 while(RenderList.first) {
419 RE_FreeRender(RenderList.first);
423 /* ********* initialize state ******** */
426 /* what doesn't change during entire render sequence */
427 /* disprect is optional, if NULL it assumes full window render */
428 void RE_InitState(Render *re, RenderData *rd, int winx, int winy, rcti *disprect)
430 re->ok= TRUE; /* maybe flag */
432 re->i.starttime= PIL_check_seconds_timer();
433 re->r= *rd; /* hardcopy */
438 re->disprect= *disprect;
439 re->rectx= disprect->xmax-disprect->xmin;
440 re->recty= disprect->ymax-disprect->ymin;
443 re->disprect.xmin= re->disprect.xmax= 0;
444 re->disprect.xmax= winx;
445 re->disprect.ymax= winy;
450 if(re->rectx < 2 || re->recty < 2) {
451 re->error("Image too small");
455 /* check state variables, osa? */
456 if(re->r.mode & (R_OSA|R_MBLUR)) {
458 if(re->osa>16) re->osa= 16;
462 /* always call, checks for gamma, gamma tables and jitter too */
463 make_sample_tables(re);
465 /* initialize render result */
466 free_render_result(re->result);
467 re->result= new_render_result(re, &re->disprect, 0);
472 void RE_SetDispRect (struct Render *re, rcti *disprect)
474 re->disprect= *disprect;
475 re->rectx= disprect->xmax-disprect->xmin;
476 re->recty= disprect->ymax-disprect->ymin;
478 /* initialize render result */
479 free_render_result(re->result);
480 re->result= new_render_result(re, &re->disprect, 0);
483 void RE_SetWindow(Render *re, rctf *viewplane, float clipsta, float clipend)
487 re->viewplane= *viewplane;
488 re->clipsta= clipsta;
489 re->clipend= clipend;
491 i_window(re->viewplane.xmin, re->viewplane.xmax, re->viewplane.ymin, re->viewplane.ymax, re->clipsta, re->clipend, re->winmat);
494 void RE_SetOrtho(Render *re, rctf *viewplane, float clipsta, float clipend)
498 re->viewplane= *viewplane;
499 re->clipsta= clipsta;
500 re->clipend= clipend;
501 re->r.mode |= R_ORTHO;
503 i_ortho(re->viewplane.xmin, re->viewplane.xmax, re->viewplane.ymin, re->viewplane.ymax, re->clipsta, re->clipend, re->winmat);
506 void RE_SetView(Render *re, float mat[][4])
509 Mat4CpyMat4(re->viewmat, mat);
510 Mat4Invert(re->viewinv, re->viewmat);
513 /* image and movie output has to move to either imbuf or kernel */
515 void RE_display_init_cb(Render *re, void (*f)(RenderResult *rr))
519 void RE_display_clear_cb(Render *re, void (*f)(RenderResult *rr))
521 re->display_clear= f;
523 void RE_display_draw_cb(Render *re, void (*f)(RenderResult *rr, rcti *rect))
528 void RE_stats_draw_cb(Render *re, void (*f)(RenderStats *rs))
532 void RE_timecursor_cb(Render *re, void (*f)(int))
537 void RE_test_break_cb(Render *re, int (*f)(void))
541 void RE_test_return_cb(Render *re, int (*f)(void))
545 void RE_error_cb(Render *re, void (*f)(const char *str))
551 /* ********* add object data (later) ******** */
553 /* object is considered fully prepared on correct time etc */
554 /* includes lights */
555 void RE_AddObject(Render *re, Object *ob)
560 /* ********** basic thread control API ************ */
562 #define RE_MAX_THREAD 4
564 typedef struct ThreadSlot {
566 SDL_Thread *sdlthread;
570 static ThreadSlot threadslots[RE_MAX_THREAD];
572 static void init_threadslots(int tot)
576 if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
577 else if(tot<1) tot= 1;
579 for(a=0; a< RE_MAX_THREAD; a++) {
580 threadslots[a].part= NULL;
581 threadslots[a].sdlthread= NULL;
583 threadslots[a].avail= 1;
585 threadslots[a].avail= 0;
589 static int available_threadslots(void)
592 for(a=0; a< RE_MAX_THREAD; a++)
593 if(threadslots[a].avail)
598 /* prototype for functional below */
599 static int do_part_thread(void *pa_v);
601 static void insert_threadslot(RenderPart *pa)
604 for(a=0; a< RE_MAX_THREAD; a++) {
605 if(threadslots[a].avail) {
607 threadslots[a].avail= 0;
608 threadslots[a].part= pa;
609 threadslots[a].sdlthread= SDL_CreateThread(do_part_thread, pa);
615 static void remove_threadslot(RenderPart *pa)
618 for(a=0; a< RE_MAX_THREAD; a++) {
619 if(threadslots[a].part==pa) {
620 threadslots[a].avail= 1;
621 threadslots[a].part= NULL;
622 SDL_WaitThread(threadslots[a].sdlthread, NULL);
623 threadslots[a].sdlthread= NULL;
628 /* ********** basic thread control API ************ */
630 static int do_part_thread(void *pa_v)
632 RenderPart *pa= pa_v;
634 /* need to return nicely all parts on esc */
635 if(R.test_break()==0) {
637 pa->result= new_render_result(&R, &pa->disprect, pa->crop);
640 zbufshadeDA_tile(pa);
645 merge_render_result(R.result, pa->result);
653 /* returns with render result filled, not threaded */
654 static void render_tile_processor(Render *re)
661 re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime;
662 re->stats_draw(&re->i);
663 re->i.starttime= PIL_check_seconds_timer();
670 /* assuming no new data gets added to dbase... */
673 for(pa= re->parts.first; pa; pa= pa->next) {
677 if(!re->test_break()) {
678 re->display_draw(pa->result, NULL);
681 free_render_result(pa->result);
688 re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime;
689 re->stats_draw(&re->i);
694 static RenderPart *find_nicest_part(Render *re)
696 RenderPart *pa, *best= NULL;
697 int centx=re->winx/2, centy=re->winy/2, tot=1;
698 int mindist, distx, disty;
700 /* find center of rendered parts, image center counts for 1 too */
701 for(pa= re->parts.first; pa; pa= pa->next) {
703 centx+= (pa->disprect.xmin+pa->disprect.xmax)/2;
704 centy+= (pa->disprect.ymin+pa->disprect.ymax)/2;
711 /* closest of the non-rendering parts */
712 mindist= re->winx*re->winy;
713 for(pa= re->parts.first; pa; pa= pa->next) {
714 if(pa->ready==0 && pa->nr==0) {
715 distx= centx - (pa->disprect.xmin+pa->disprect.xmax)/2;
716 disty= centy - (pa->disprect.ymin+pa->disprect.ymax)/2;
717 distx= (int)sqrt(distx*distx + disty*disty);
727 static void threaded_tile_processor(Render *re)
730 int maxthreads=2, rendering=1, counter= 1;
738 init_threadslots(maxthreads);
740 /* assuming no new data gets added to dbase... */
743 malloc_lock = SDL_CreateMutex();
747 /* I noted that test_break() in a thread doesn't make ghost send ESC */
748 if(available_threadslots() && !re->test_break()) {
749 pa= find_nicest_part(re);
751 pa->nr= counter++; /* only for stats */
752 insert_threadslot(pa);
758 /* check for ready ones to display, and if we need to continue */
760 for(pa= re->parts.first; pa; pa= pa->next) {
763 remove_threadslot(pa); /* do it here, not in thread */
764 re->display_draw(pa->result, NULL);
765 free_render_result(pa->result);
773 /* on break, wait for all slots to get freed */
774 if(re->test_break() && available_threadslots()==maxthreads)
779 if(malloc_lock) SDL_DestroyMutex(malloc_lock); malloc_lock= NULL;
784 void RE_TileProcessor(Render *re)
786 if(re->r.mode & R_THREADS)
787 threaded_tile_processor(re);
789 render_tile_processor(re);
793 /* ************ This part uses API, for rendering Blender scenes ********** */
795 void render_one_frame(Render *re)
798 // re->cfra= cfra; /* <- unused! */
800 /* make render verts/faces/halos/lamps */
801 RE_Database_FromScene(re, re->scene, 1);
803 RE_TileProcessor(re);
805 /* free all render verts etc */
806 RE_Database_Free(re);
809 /* accumulates osa frames */
810 static void do_render_blurred(Render *re, float frame)
815 /* interleaves 2 frames */
816 static void do_render_fields(Render *re)
821 static void do_render_final(Render *re, Scene *scene)
823 /* we set start time here, for main Blender loops */
824 re->i.starttime= PIL_check_seconds_timer();
826 if(re->r.scemode & R_DOSEQ) {
827 re->result->rect32= MEM_callocN(sizeof(int)*re->rectx*re->recty, "rectot");
828 if(!re->test_break())
829 do_render_seq(re->result);
832 /* first check if theres nodetree with render result */
833 int do_render= ntreeCompositNeedsRender(scene->nodetree);
834 /* but.. do we use nodes? */
835 if(scene->use_nodes==0) do_render= 1;
840 /* now use renderdata and camera to set viewplane */
841 RE_SetCamera(re, re->scene->camera);
843 if(re->r.mode & R_FIELDS)
844 do_render_fields(re);
845 else if(re->r.mode & R_MBLUR)
846 do_render_blurred(re, re->scene->r.cfra);
848 render_one_frame(re);
851 ntreeCompositTagRender(scene->nodetree);
853 if(re->r.scemode & R_DOCOMP)
854 ntreeCompositExecTree(scene->nodetree, &re->r, 0);
858 re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime;
859 re->stats_draw(&re->i);
861 re->display_draw(re->result, NULL);
866 static int is_rendering_allowed(Render *re)
869 /* forbidden combinations */
870 if(re->r.mode & R_PANORAMA) {
871 if(re->r.mode & R_BORDER) {
872 re->error("No border supported for Panorama");
876 re->error("No Y-Parts supported for Panorama");
879 if(re->r.mode & R_ORTHO) {
880 re->error("No Ortho render possible for Panorama");
885 if(re->r.mode & R_BORDER) {
886 if(re->r.border.xmax <= re->r.border.xmin ||
887 re->r.border.ymax <= re->r.border.ymin) {
888 re->error("No border area selected.");
893 if(re->r.xparts*re->r.yparts>=2 && (re->r.mode & R_MOVIECROP) && (re->r.mode & R_BORDER)) {
894 re->error("Combination of border, crop and parts not allowed");
898 if(re->r.xparts*re->r.yparts>64) {
899 re->error("No more than 64 parts supported");
903 if(re->r.yparts>1 && (re->r.mode & R_PANORAMA)) {
904 re->error("No Y-Parts supported for Panorama");
908 /* check valid camera */
909 if(re->scene->camera==NULL)
910 re->scene->camera= scene_find_camera(re->scene);
911 if(re->scene->camera==NULL) {
912 re->error("No camera");
920 /* evaluating scene options for general Blender render */
921 static int render_initialize_from_scene(Render *re, Scene *scene)
926 /* r.xsch and r.ysch has the actual view window size
927 r.border is the clipping rect */
929 /* calculate actual render result and display size */
930 winx= (scene->r.size*scene->r.xsch)/100;
931 winy= (scene->r.size*scene->r.ysch)/100;
932 // if(scene->r.mode & R_PANORAMA)
933 // winx*= scene->r.xparts;
935 /* only in movie case we render smaller part */
936 if(scene->r.mode & R_BORDER) {
937 disprect.xmin= scene->r.border.xmin*winx;
938 disprect.xmax= scene->r.border.xmax*winx;
940 disprect.ymin= scene->r.border.ymin*winy;
941 disprect.ymax= scene->r.border.ymax*winy;
944 disprect.xmin= disprect.ymin= 0;
949 RE_InitState(re, &scene->r, winx, winy, &disprect);
952 if(!is_rendering_allowed(re))
955 re->display_init(re->result);
956 re->display_clear(re->result);
961 /* general Blender frame render call */
962 /* should return 1 when all is OK, otherwise it throws up errors */
963 void RE_BlenderFrame(Render *re, Scene *scene, int frame)
965 /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */
966 /* is also set by caller renderwin.c */
969 if(render_initialize_from_scene(re, scene)) {
970 do_render_final(re, scene);
975 /* saves images to disk */
976 void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra)
978 bMovieHandle *mh= BKE_get_movie_handle(scene->r.imtype);
979 int cfrao= scene->r.cfra;
980 char name[FILE_MAXDIR+FILE_MAXFILE];
982 /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */
983 /* is also set by caller renderwin.c */
986 if(!render_initialize_from_scene(re, scene))
989 /* confusing... scene->r or re->r? make a decision once! */
990 if(BKE_imtype_is_movie(scene->r.imtype))
991 mh->start_movie(&scene->r, re->rectx, re->recty);
993 for(scene->r.cfra= sfra; scene->r.cfra<=efra; scene->r.cfra++) {
994 re->r.cfra= scene->r.cfra; /* weak.... */
996 do_render_final(re, scene);
998 /* write image or movie */
999 if(re->test_break()==0) {
1002 RE_GetResultImage(re, &rres);
1004 /* write movie or image */
1005 if(BKE_imtype_is_movie(scene->r.imtype)) {
1006 /* note; the way it gets 32 bits rects is weak... */
1008 if(rres.rect32==NULL) {
1009 rres.rect32= MEM_mallocN(sizeof(int)*rres.rectx*rres.recty, "temp 32 bits rect");
1012 RE_ResultGet32(re, rres.rect32);
1013 mh->append_movie(scene->r.cfra, rres.rect32, rres.rectx, rres.recty);
1014 if(dofree) MEM_freeN(rres.rect32);
1015 printf("Append frame %d", scene->r.cfra);
1018 ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.planes, 0, 0);
1021 BKE_makepicstring(name, (scene->r.cfra));
1022 ibuf->rect= rres.rect32; /* if not exists, BKE_write_ibuf makes one */
1023 ibuf->rect_float= rres.rectf;
1024 ibuf->zbuf_float= rres.rectz;
1025 ok= BKE_write_ibuf(ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality);
1026 IMB_freeImBuf(ibuf); /* imbuf knows which rects are not part of ibuf */
1029 printf("Render error: cannot save %s\n", name);
1032 else printf("Saved: %s", name);
1035 BLI_timestr(re->i.lastframetime, name);
1036 printf(" Time: %s\n", name);
1037 fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */
1040 if(G.afbreek==1) break;
1044 if(BKE_imtype_is_movie(scene->r.imtype))
1047 scene->r.cfra= cfrao;