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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/render/render_internal.c
36 #include "MEM_guardedalloc.h"
38 #include "BLI_blenlib.h"
40 #include "BLI_threads.h"
42 #include "BLI_utildefines.h"
44 #include "DNA_scene_types.h"
46 #include "BKE_blender.h"
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_image.h"
50 #include "BKE_library.h"
53 #include "BKE_multires.h"
54 #include "BKE_report.h"
55 #include "BKE_sequencer.h"
56 #include "BKE_screen.h"
57 #include "BKE_scene.h"
62 #include "ED_screen.h"
63 #include "ED_object.h"
65 #include "RE_pipeline.h"
66 #include "IMB_imbuf.h"
67 #include "IMB_imbuf_types.h"
69 #include "RNA_access.h"
70 #include "RNA_define.h"
72 #include "wm_window.h"
74 #include "render_intern.h"
76 static ScrArea *biggest_area(bContext *C);
77 static ScrArea *biggest_non_image_area(bContext *C);
78 static ScrArea *find_area_showing_r_result(bContext *C, wmWindow **win);
79 static ScrArea *find_area_image_empty(bContext *C);
81 /* called inside thread! */
82 void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
84 float x1, y1, *rectf= NULL;
85 int ymin, ymax, xmin, xmax;
89 /* if renrect argument, we only refresh scanlines */
91 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
92 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
95 /* xmin here is first subrect x coord, xmax defines subrect width */
96 xmin = renrect->xmin + rr->crop;
97 xmax = renrect->xmax - xmin + rr->crop;
100 ymin= renrect->ymin + rr->crop;
101 ymax= renrect->ymax - ymin + rr->crop;
104 renrect->ymin= renrect->ymax;
108 xmin = ymin = rr->crop;
109 xmax = rr->rectx - 2*rr->crop;
110 ymax = rr->recty - 2*rr->crop;
113 /* xmin ymin is in tile coords. transform to ibuf */
114 rxmin= rr->tilerect.xmin + xmin;
115 if(rxmin >= ibuf->x) return;
116 rymin= rr->tilerect.ymin + ymin;
117 if(rymin >= ibuf->y) return;
119 if(rxmin + xmax > ibuf->x)
120 xmax= ibuf->x - rxmin;
121 if(rymin + ymax > ibuf->y)
122 ymax= ibuf->y - rymin;
124 if(xmax < 1 || ymax < 1) return;
126 /* find current float rect for display, first case is after composit... still weak */
133 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
134 rectf= rr->renlay->rectf;
137 if(rectf==NULL) return;
140 imb_addrectImBuf(ibuf);
142 rectf+= 4*(rr->rectx*ymin + xmin);
143 rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
145 /* XXX make nice consistent functions for this */
146 if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
147 for(y1= 0; y1<ymax; y1++) {
151 const float dither = ibuf->dither / 255.0f;
153 /* XXX temp. because crop offset */
154 if( rectc >= (char *)(ibuf->rect)) {
155 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
156 const float d = (BLI_frand()-0.5f)*dither;
157 srgb[0]= d + linearrgb_to_srgb(rf[0]);
158 srgb[1]= d + linearrgb_to_srgb(rf[1]);
159 srgb[2]= d + linearrgb_to_srgb(rf[2]);
161 rc[0]= FTOCHAR(srgb[0]);
162 rc[1]= FTOCHAR(srgb[1]);
163 rc[2]= FTOCHAR(srgb[2]);
164 rc[3]= FTOCHAR(rf[3]);
167 rectf += 4*rr->rectx;
171 for(y1= 0; y1<ymax; y1++) {
175 const float dither = ibuf->dither / 255.0f;
177 /* XXX temp. because crop offset */
178 if( rectc >= (char *)(ibuf->rect)) {
179 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
180 const float d = (BLI_frand()-0.5f)*dither;
186 rc[0]= FTOCHAR(rgb[0]);
187 rc[1]= FTOCHAR(rgb[1]);
188 rc[2]= FTOCHAR(rgb[2]);
189 rc[3]= FTOCHAR(rf[3]);
192 rectf += 4*rr->rectx;
198 /* new window uses x,y to set position */
199 void screen_set_image_output(bContext *C, int mx, int my)
201 wmWindow *win= CTX_wm_window(C);
202 Scene *scene= CTX_data_scene(C);
205 int area_was_image=0;
207 if(scene->r.displaymode==R_OUTPUT_NONE)
210 if(scene->r.displaymode==R_OUTPUT_WINDOW) {
214 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
215 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
217 /* arbitrary... miniature image window views don't make much sense */
218 if(sizex < 320) sizex= 320;
219 if(sizey < 256) sizey= 256;
221 /* XXX some magic to calculate postition */
222 rect.xmin= mx + win->posx - sizex/2;
223 rect.ymin= my + win->posy - sizey/2;
224 rect.xmax= rect.xmin + sizex;
225 rect.ymax= rect.ymin + sizey;
227 /* changes context! */
228 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
232 else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
233 if (CTX_wm_area(C) && CTX_wm_area(C)->spacetype == SPACE_IMAGE)
236 /* this function returns with changed context */
237 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
242 sa= find_area_showing_r_result(C, &win);
244 sa= find_area_image_empty(C);
246 /* if area found in other window, we make that one show in front */
247 if(win && win!=CTX_wm_window(C))
248 wm_window_raise(win);
251 /* find largest open non-image area */
252 sa= biggest_non_image_area(C);
254 ED_area_newspace(C, sa, SPACE_IMAGE);
255 sima= sa->spacedata.first;
257 /* makes ESC go back to prev space */
258 sima->flag |= SI_PREVSPACE;
261 /* use any area of decent size */
263 if(sa->spacetype!=SPACE_IMAGE) {
264 // XXX newspace(sa, SPACE_IMAGE);
265 sima= sa->spacedata.first;
267 /* makes ESC go back to prev space */
268 sima->flag |= SI_PREVSPACE;
273 sima= sa->spacedata.first;
275 /* get the correct image, and scale it */
276 sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
279 /* if we're rendering to full screen, set appropriate hints on image editor
280 * so it can restore properly on pressing esc */
282 sima->flag |= SI_FULLWINDOW;
284 /* Tell the image editor to revert to previous space in space list on close
285 * _only_ if it wasn't already an image editor when the render was invoked */
286 if (area_was_image == 0)
287 sima->flag |= SI_PREVSPACE;
289 /* Leave it alone so the image editor will just go back from
290 * full screen to the original tiled setup */
299 /* ****************************** render invoking ***************** */
301 /* set callbacks, exported to sequence render too.
302 Only call in foreground (UI) renders. */
304 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
305 /* window as the last possible alternative. */
306 static ScrArea *biggest_non_image_area(bContext *C)
308 bScreen *sc= CTX_wm_screen(C);
309 ScrArea *sa, *big= NULL;
310 int size, maxsize= 0, bwmaxsize= 0;
313 for(sa= sc->areabase.first; sa; sa= sa->next) {
314 if(sa->winx > 30 && sa->winy > 30) {
315 size= sa->winx*sa->winy;
316 if(sa->spacetype == SPACE_BUTS) {
317 if(foundwin == 0 && size > bwmaxsize) {
322 else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
333 static ScrArea *biggest_area(bContext *C)
335 bScreen *sc= CTX_wm_screen(C);
336 ScrArea *sa, *big= NULL;
337 int size, maxsize= 0;
339 for(sa= sc->areabase.first; sa; sa= sa->next) {
340 size= sa->winx*sa->winy;
350 static ScrArea *find_area_showing_r_result(bContext *C, wmWindow **win)
352 wmWindowManager *wm= CTX_wm_manager(C);
356 /* find an imagewindow showing render result */
357 for(*win=wm->windows.first; *win; *win= (*win)->next) {
358 for(sa= (*win)->screen->areabase.first; sa; sa= sa->next) {
359 if(sa->spacetype==SPACE_IMAGE) {
360 sima= sa->spacedata.first;
361 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
372 static ScrArea *find_area_image_empty(bContext *C)
374 bScreen *sc= CTX_wm_screen(C);
378 /* find an imagewindow showing render result */
379 for(sa=sc->areabase.first; sa; sa= sa->next) {
380 if(sa->spacetype==SPACE_IMAGE) {
381 sima= sa->spacedata.first;
389 #if 0 // XXX not used
390 static ScrArea *find_empty_image_area(bContext *C)
392 bScreen *sc= CTX_wm_screen(C);
396 /* find an imagewindow showing render result */
397 for(sa=sc->areabase.first; sa; sa= sa->next) {
398 if(sa->spacetype==SPACE_IMAGE) {
399 sima= sa->spacedata.first;
406 #endif // XXX not used
408 static void render_error_reports(void *reports, const char *str)
410 BKE_report(reports, RPT_ERROR, str);
413 /* executes blocking render */
414 static int screen_render_exec(bContext *C, wmOperator *op)
416 Scene *scene= CTX_data_scene(C);
417 Render *re= RE_NewRender(scene->id.name);
419 View3D *v3d= CTX_wm_view3d(C);
420 Main *mainp= CTX_data_main(C);
421 unsigned int lay= (v3d)? v3d->lay: scene->lay;
422 const short is_animation= RNA_boolean_get(op->ptr, "animation");
423 const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
424 struct Object *camera_override= v3d ? V3D_CAMERA_LOCAL(v3d) : NULL;
426 if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.imtype)) {
427 BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected.");
428 return OPERATOR_CANCELLED;
432 re= RE_NewRender(scene->id.name);
436 RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
437 RE_error_cb(re, op->reports, render_error_reports);
439 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
440 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
441 BKE_image_backup_render(scene, ima);
443 /* cleanup sequencer caches before starting user triggered render.
444 otherwise, invalidated cache entries can make their way into
445 the output rendering. We can't put that into RE_BlenderFrame,
446 since sequence rendering can call that recursively... (peter) */
447 seq_stripelem_cache_cleanup();
450 RE_BlenderAnim(re, mainp, scene, camera_override, lay, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
452 RE_BlenderFrame(re, mainp, scene, NULL, camera_override, lay, scene->r.cfra, is_write_still);
454 // no redraw needed, we leave state as we entered it
455 ED_update_for_newframe(mainp, scene, CTX_wm_screen(C), 1);
457 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
459 return OPERATOR_FINISHED;
462 typedef struct RenderJob {
467 SceneRenderLayer *srl;
468 struct Object *camera_override;
470 short anim, write_still;
479 static void render_freejob(void *rjv)
486 /* str is IMA_MAX_RENDER_TEXT in size */
487 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
489 char info_time_str[32]; // used to be extern to header_info.c
490 uintptr_t mem_in_use, mmap_in_use, peak_memory;
491 float megs_used_memory, mmap_used_memory, megs_peak_memory;
494 mem_in_use= MEM_get_memory_in_use();
495 mmap_in_use= MEM_get_mapped_memory_in_use();
496 peak_memory = MEM_get_peak_memory();
498 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
499 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
500 megs_peak_memory = (peak_memory)/(1024.0*1024.0);
502 if(scene->lay & 0xFF000000)
503 spos+= sprintf(spos, "Localview | ");
504 else if(scene->r.scemode & R_SINGLE_LAYER)
505 spos+= sprintf(spos, "Single Layer | ");
508 spos+= sprintf(spos, "%s ", rs->statstr);
511 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
512 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
513 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
514 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM, peak %.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory, megs_peak_memory);
517 spos+= sprintf(spos, "Field %d ", rs->curfield);
519 spos+= sprintf(spos, "Blur %d ", rs->curblur);
522 BLI_timestr(rs->lastframetime, info_time_str);
523 spos+= sprintf(spos, "Time:%s ", info_time_str);
525 if(rs->infostr && rs->infostr[0])
526 spos+= sprintf(spos, "| %s ", rs->infostr);
528 /* very weak... but 512 characters is quite safe */
529 if(spos >= str+IMA_MAX_RENDER_TEXT)
531 printf("WARNING! renderwin text beyond limit \n");
535 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
540 rr= RE_AcquireResultRead(rj->re);
543 /* malloc OK here, stats_draw is not in tile threads */
545 rr->text= MEM_callocN(IMA_MAX_RENDER_TEXT, "rendertext");
547 make_renderinfo_string(rs, rj->scene, rr->text);
550 RE_ReleaseResult(rj->re);
552 /* make jobs timer to send notifier */
557 static void render_progress_update(void *rjv, float progress)
562 *rj->progress = progress;
565 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
568 Image *ima= rj->image;
572 /* only update if we are displaying the slot being rendered */
573 if(ima->render_slot != ima->last_render_slot)
576 ibuf= BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
578 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
580 /* make jobs timer to send notifier */
583 BKE_image_release_ibuf(ima, lock);
586 static void render_startjob(void *rjv, short *stop, short *do_update, float *progress)
591 rj->do_update= do_update;
592 rj->progress= progress;
595 RE_BlenderAnim(rj->re, rj->main, rj->scene, rj->camera_override, rj->lay, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
597 RE_BlenderFrame(rj->re, rj->main, rj->scene, rj->srl, rj->camera_override, rj->lay, rj->scene->r.cfra, rj->write_still);
600 static void render_endjob(void *rjv)
604 /* this render may be used again by the sequencer without the active 'Render' where the callbacks
605 * would be re-assigned. assign dummy callbacks to avoid referencing freed renderjobs bug [#24508] */
606 RE_InitRenderCB(rj->re);
608 if(rj->main != G.main)
611 /* else the frame will not update for the original value */
612 if(!(rj->scene->r.scemode & R_NO_FRAME_UPDATE))
613 ED_update_for_newframe(G.main, rj->scene, rj->win->screen, 1);
615 /* XXX above function sets all tags in nodes */
616 ntreeClearTags(rj->scene->nodetree);
618 /* potentially set by caller */
619 rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE;
622 NodeTagIDChanged(rj->scene->nodetree, &rj->scene->id);
623 WM_main_add_notifier(NC_NODE|NA_EDITED, rj->scene);
626 /* XXX render stability hack */
628 WM_main_add_notifier(NC_WINDOW, NULL);
631 /* called by render, check job 'stop' value or the global */
632 static int render_breakjob(void *rjv)
638 if(rj->stop && *(rj->stop))
643 /* runs in thread, no cursor setting here works. careful with notifiers too (malloc conflicts) */
644 /* maybe need a way to get job send notifer? */
645 static void render_drawlock(void *UNUSED(rjv), int lock)
647 BKE_spacedata_draw_locks(lock);
652 static int screen_render_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
654 /* no running blender, remove handler and pass through */
655 if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C))) {
656 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
660 switch (event->type) {
662 return OPERATOR_RUNNING_MODAL;
665 return OPERATOR_PASS_THROUGH;
668 /* using context, starts job */
669 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
671 /* new render clears all callbacks */
673 Scene *scene= CTX_data_scene(C);
674 SceneRenderLayer *srl=NULL;
675 bScreen *screen= CTX_wm_screen(C);
676 View3D *v3d= CTX_wm_view3d(C);
682 const short is_animation= RNA_boolean_get(op->ptr, "animation");
683 const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
684 struct Object *camera_override= v3d ? V3D_CAMERA_LOCAL(v3d) : NULL;
686 /* only one render job at a time */
687 if(WM_jobs_test(CTX_wm_manager(C), scene))
688 return OPERATOR_CANCELLED;
690 if(!RE_is_rendering_allowed(scene, camera_override, op->reports, render_error_reports)) {
691 return OPERATOR_CANCELLED;
694 if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.imtype)) {
695 BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected.");
696 return OPERATOR_CANCELLED;
699 /* stop all running jobs, currently previews frustrate Render */
700 WM_jobs_stop_all(CTX_wm_manager(C));
704 /* thread-safety experiment, copy main from the undo buffer */
705 mainp= BKE_undo_get_main(&scene);
708 mainp= CTX_data_main(C);
710 /* cancel animation playback */
711 if (screen->animtimer)
712 ED_screen_animation_play(C, 0, 0);
714 /* handle UI stuff */
717 /* flush multires changes (for sculpt) */
718 multires_force_render_update(CTX_data_active_object(C));
720 /* cleanup sequencer caches before starting user triggered render.
721 otherwise, invalidated cache entries can make their way into
722 the output rendering. We can't put that into RE_BlenderFrame,
723 since sequence rendering can call that recursively... (peter) */
724 seq_stripelem_cache_cleanup();
726 /* get editmode results */
727 ED_object_exit_editmode(C, 0); /* 0 = does not exit editmode */
730 // get view3d layer, local layer, make this nice api call to render
733 /* ensure at least 1 area shows result */
734 screen_set_image_output(C, event->x, event->y);
736 jobflag= WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS;
738 /* single layer re-render */
739 if(RNA_property_is_set(op->ptr, "layer")) {
740 SceneRenderLayer *rl;
742 char scene_name[MAX_ID_NAME-2], rl_name[RE_MAXNAME];
744 RNA_string_get(op->ptr, "layer", rl_name);
745 RNA_string_get(op->ptr, "scene", scene_name);
747 scn = (Scene *)BLI_findstring(&mainp->scene, scene_name, offsetof(ID, name) + 2);
748 rl = (SceneRenderLayer *)BLI_findstring(&scene->r.layers, rl_name, offsetof(SceneRenderLayer, name));
751 /* camera switch wont have updated */
752 scn->r.cfra= scene->r.cfra;
753 scene_camera_switch_update(scn);
758 jobflag |= WM_JOB_SUSPEND;
761 /* job custom data */
762 rj= MEM_callocN(sizeof(RenderJob), "render job");
765 rj->win= CTX_wm_window(C);
767 rj->camera_override = camera_override;
768 rj->lay = (v3d)? v3d->lay: scene->lay;
769 rj->anim= is_animation;
770 rj->write_still= is_write_still && !is_animation;
771 rj->iuser.scene= scene;
773 rj->reports= op->reports;
776 steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Render", jobflag);
777 WM_jobs_customdata(steve, rj, render_freejob);
778 WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
779 WM_jobs_callbacks(steve, render_startjob, NULL, NULL, render_endjob);
781 /* get a render result image, and make sure it is empty */
782 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
783 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
784 BKE_image_backup_render(rj->scene, ima);
787 /* setup new render */
788 re= RE_NewRender(scene->id.name);
789 RE_test_break_cb(re, rj, render_breakjob);
790 RE_draw_lock_cb(re, rj, render_drawlock);
791 RE_display_draw_cb(re, rj, image_rect_update);
792 RE_stats_draw_cb(re, rj, image_renderinfo_cb);
793 RE_progress_cb(re, rj, render_progress_update);
798 RE_error_cb(re, op->reports, render_error_reports);
800 WM_jobs_start(CTX_wm_manager(C), steve);
803 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
805 /* we set G.rendering here already instead of only in the job, this ensure
806 main loop or other scene updates are disabled in time, since they may
807 have started before the job thread */
810 /* add modal handler for ESC */
811 WM_event_add_modal_handler(C, op);
813 return OPERATOR_RUNNING_MODAL;
817 /* contextual render, using current scene, view3d? */
818 void RENDER_OT_render(wmOperatorType *ot)
822 ot->description= "Render active scene";
823 ot->idname= "RENDER_OT_render";
826 ot->invoke= screen_render_invoke;
827 ot->modal= screen_render_modal;
828 ot->exec= screen_render_exec;
830 /*ot->poll= ED_operator_screenactive;*/ /* this isnt needed, causes failer in background mode */
832 RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
833 RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)");
834 RNA_def_string(ot->srna, "layer", "", RE_MAXNAME, "Render Layer", "Single render layer to re-render");
835 RNA_def_string(ot->srna, "scene", "", MAX_ID_NAME-2, "Scene", "Re-render single layer in this scene");
838 /* ****************************** opengl render *************************** */
841 /* *********************** cancel render viewer *************** */
843 static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
845 wmWindow *win= CTX_wm_window(C);
846 ScrArea *sa= CTX_wm_area(C);
847 SpaceImage *sima= sa->spacedata.first;
849 /* test if we have a temp screen in front */
850 if(CTX_wm_window(C)->screen->temp) {
851 wm_window_lower(CTX_wm_window(C));
852 return OPERATOR_FINISHED;
854 /* determine if render already shows */
855 else if(sima->flag & SI_PREVSPACE) {
856 sima->flag &= ~SI_PREVSPACE;
858 if(sima->flag & SI_FULLWINDOW) {
859 sima->flag &= ~SI_FULLWINDOW;
860 ED_screen_full_prevspace(C, sa);
863 ED_area_prevspace(C, sa);
865 return OPERATOR_FINISHED;
867 else if(sima->flag & SI_FULLWINDOW) {
868 sima->flag &= ~SI_FULLWINDOW;
869 ED_screen_full_toggle(C, win, sa);
870 return OPERATOR_FINISHED;
873 return OPERATOR_PASS_THROUGH;
876 void RENDER_OT_view_cancel(struct wmOperatorType *ot)
879 ot->name= "Cancel Render View";
880 ot->description= "Cancel show render view";
881 ot->idname= "RENDER_OT_view_cancel";
884 ot->exec= render_view_cancel_exec;
885 ot->poll= ED_operator_image_active;
888 /* *********************** show render viewer *************** */
890 static int render_view_show_invoke(bContext *C, wmOperator *UNUSED(unused), wmEvent *event)
892 wmWindow *wincur = CTX_wm_window(C);
894 /* test if we have currently a temp screen active */
895 if(wincur->screen->temp) {
896 wm_window_lower(wincur);
899 wmWindow *win, *winshow;
900 ScrArea *sa= find_area_showing_r_result(C, &winshow);
902 /* is there another window showing result? */
903 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
904 if(win->screen->temp || (win==winshow && winshow!=wincur)) {
905 wm_window_raise(win);
906 return OPERATOR_FINISHED;
910 /* determine if render already shows */
912 /* but don't close it when rendering */
914 SpaceImage *sima= sa->spacedata.first;
916 if(sima->flag & SI_PREVSPACE) {
917 sima->flag &= ~SI_PREVSPACE;
919 if(sima->flag & SI_FULLWINDOW) {
920 sima->flag &= ~SI_FULLWINDOW;
921 ED_screen_full_prevspace(C, sa);
923 else if(sima->next) {
924 /* workaround for case of double prevspace, render window
925 with a file browser on top of it (same as in ED_area_prevspace) */
926 if(sima->next->spacetype == SPACE_FILE && sima->next->next)
927 ED_area_newspace(C, sa, sima->next->next->spacetype);
929 ED_area_newspace(C, sa, sima->next->spacetype);
930 ED_area_tag_redraw(sa);
936 screen_set_image_output(C, event->x, event->y);
940 return OPERATOR_FINISHED;
943 void RENDER_OT_view_show(struct wmOperatorType *ot)
946 ot->name= "Show/Hide Render View";
947 ot->description= "Toggle show render view";
948 ot->idname= "RENDER_OT_view_show";
951 ot->invoke= render_view_show_invoke;
952 ot->poll= ED_operator_screenactive;