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 *****
31 #include "MEM_guardedalloc.h"
33 #include "BLI_blenlib.h"
35 #include "BLI_threads.h"
38 #include "DNA_scene_types.h"
40 #include "BKE_blender.h"
41 #include "BKE_context.h"
42 #include "BKE_global.h"
43 #include "BKE_image.h"
44 #include "BKE_library.h"
46 #include "BKE_multires.h"
47 #include "BKE_report.h"
48 #include "BKE_sequencer.h"
53 #include "ED_screen.h"
54 #include "ED_object.h"
56 #include "RE_pipeline.h"
57 #include "IMB_imbuf.h"
58 #include "IMB_imbuf_types.h"
60 #include "RNA_access.h"
61 #include "RNA_define.h"
63 #include "wm_window.h"
65 #include "render_intern.h"
67 static ScrArea *biggest_area(bContext *C);
68 static ScrArea *biggest_non_image_area(bContext *C);
69 static ScrArea *find_area_showing_r_result(bContext *C);
70 static ScrArea *find_area_image_empty(bContext *C);
72 /* called inside thread! */
73 void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
75 float x1, y1, *rectf= NULL;
76 int ymin, ymax, xmin, xmax;
80 /* if renrect argument, we only refresh scanlines */
82 /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
83 if(rr->renlay==NULL || renrect->ymax>=rr->recty)
86 /* xmin here is first subrect x coord, xmax defines subrect width */
87 xmin = renrect->xmin + rr->crop;
88 xmax = renrect->xmax - xmin + rr->crop;
91 ymin= renrect->ymin + rr->crop;
92 ymax= renrect->ymax - ymin + rr->crop;
95 renrect->ymin= renrect->ymax;
99 xmin = ymin = rr->crop;
100 xmax = rr->rectx - 2*rr->crop;
101 ymax = rr->recty - 2*rr->crop;
104 /* xmin ymin is in tile coords. transform to ibuf */
105 rxmin= rr->tilerect.xmin + xmin;
106 if(rxmin >= ibuf->x) return;
107 rymin= rr->tilerect.ymin + ymin;
108 if(rymin >= ibuf->y) return;
110 if(rxmin + xmax > ibuf->x)
111 xmax= ibuf->x - rxmin;
112 if(rymin + ymax > ibuf->y)
113 ymax= ibuf->y - rymin;
115 if(xmax < 1 || ymax < 1) return;
117 /* find current float rect for display, first case is after composit... still weak */
124 if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
125 rectf= rr->renlay->rectf;
128 if(rectf==NULL) return;
131 imb_addrectImBuf(ibuf);
133 rectf+= 4*(rr->rectx*ymin + xmin);
134 rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin);
136 /* XXX make nice consistent functions for this */
137 if (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
138 for(y1= 0; y1<ymax; y1++) {
142 const float dither = ibuf->dither / 255.0;
144 /* XXX temp. because crop offset */
145 if( rectc >= (char *)(ibuf->rect)) {
146 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
147 const float d = (BLI_frand()-0.5)*dither;
148 srgb[0]= d + linearrgb_to_srgb(rf[0]);
149 srgb[1]= d + linearrgb_to_srgb(rf[1]);
150 srgb[2]= d + linearrgb_to_srgb(rf[2]);
152 rc[0]= FTOCHAR(srgb[0]);
153 rc[1]= FTOCHAR(srgb[1]);
154 rc[2]= FTOCHAR(srgb[2]);
155 rc[3]= FTOCHAR(rf[3]);
158 rectf += 4*rr->rectx;
162 for(y1= 0; y1<ymax; y1++) {
166 const float dither = ibuf->dither / 255.0;
168 /* XXX temp. because crop offset */
169 if( rectc >= (char *)(ibuf->rect)) {
170 for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) {
171 const float d = (BLI_frand()-0.5)*dither;
177 rc[0]= FTOCHAR(rgb[0]);
178 rc[1]= FTOCHAR(rgb[1]);
179 rc[2]= FTOCHAR(rgb[2]);
180 rc[3]= FTOCHAR(rf[3]);
183 rectf += 4*rr->rectx;
189 /* new window uses x,y to set position */
190 void screen_set_image_output(bContext *C, int mx, int my)
192 wmWindow *win= CTX_wm_window(C);
193 Scene *scene= CTX_data_scene(C);
196 int area_was_image=0;
198 if(scene->r.displaymode==R_OUTPUT_WINDOW) {
202 sizex= 10 + (scene->r.xsch*scene->r.size)/100;
203 sizey= 40 + (scene->r.ysch*scene->r.size)/100;
205 /* arbitrary... miniature image window views don't make much sense */
206 if(sizex < 320) sizex= 320;
207 if(sizey < 256) sizey= 256;
209 /* XXX some magic to calculate postition */
210 rect.xmin= mx + win->posx - sizex/2;
211 rect.ymin= my + win->posy - sizey/2;
212 rect.xmax= rect.xmin + sizex;
213 rect.ymax= rect.ymin + sizey;
215 /* changes context! */
216 WM_window_open_temp(C, &rect, WM_WINDOW_RENDER);
220 else if(scene->r.displaymode==R_OUTPUT_SCREEN) {
221 if (CTX_wm_area(C) && CTX_wm_area(C)->spacetype == SPACE_IMAGE)
224 /* this function returns with changed context */
225 ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_IMAGE);
230 sa= find_area_showing_r_result(C);
232 sa= find_area_image_empty(C);
235 /* find largest open non-image area */
236 sa= biggest_non_image_area(C);
238 ED_area_newspace(C, sa, SPACE_IMAGE);
239 sima= sa->spacedata.first;
241 /* makes ESC go back to prev space */
242 sima->flag |= SI_PREVSPACE;
245 /* use any area of decent size */
247 if(sa->spacetype!=SPACE_IMAGE) {
248 // XXX newspace(sa, SPACE_IMAGE);
249 sima= sa->spacedata.first;
251 /* makes ESC go back to prev space */
252 sima->flag |= SI_PREVSPACE;
257 sima= sa->spacedata.first;
259 /* get the correct image, and scale it */
260 sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
263 /* if we're rendering to full screen, set appropriate hints on image editor
264 * so it can restore properly on pressing esc */
266 sima->flag |= SI_FULLWINDOW;
268 /* Tell the image editor to revert to previous space in space list on close
269 * _only_ if it wasn't already an image editor when the render was invoked */
270 if (area_was_image == 0)
271 sima->flag |= SI_PREVSPACE;
273 /* Leave it alone so the image editor will just go back from
274 * full screen to the original tiled setup */
283 /* ****************************** render invoking ***************** */
285 /* set callbacks, exported to sequence render too.
286 Only call in foreground (UI) renders. */
288 /* returns biggest area that is not uv/image editor. Note that it uses buttons */
289 /* window as the last possible alternative. */
290 static ScrArea *biggest_non_image_area(bContext *C)
292 bScreen *sc= CTX_wm_screen(C);
293 ScrArea *sa, *big= NULL;
294 int size, maxsize= 0, bwmaxsize= 0;
297 for(sa= sc->areabase.first; sa; sa= sa->next) {
298 if(sa->winx > 30 && sa->winy > 30) {
299 size= sa->winx*sa->winy;
300 if(sa->spacetype == SPACE_BUTS) {
301 if(foundwin == 0 && size > bwmaxsize) {
306 else if(sa->spacetype != SPACE_IMAGE && size > maxsize) {
317 static ScrArea *biggest_area(bContext *C)
319 bScreen *sc= CTX_wm_screen(C);
320 ScrArea *sa, *big= NULL;
321 int size, maxsize= 0;
323 for(sa= sc->areabase.first; sa; sa= sa->next) {
324 size= sa->winx*sa->winy;
334 static ScrArea *find_area_showing_r_result(bContext *C)
336 wmWindowManager *wm= CTX_wm_manager(C);
341 /* find an imagewindow showing render result */
342 for(win=wm->windows.first; win; win=win->next) {
343 for(sa=win->screen->areabase.first; sa; sa= sa->next) {
344 if(sa->spacetype==SPACE_IMAGE) {
345 sima= sa->spacedata.first;
346 if(sima->image && sima->image->type==IMA_TYPE_R_RESULT)
355 static ScrArea *find_area_image_empty(bContext *C)
357 bScreen *sc= CTX_wm_screen(C);
361 /* find an imagewindow showing render result */
362 for(sa=sc->areabase.first; sa; sa= sa->next) {
363 if(sa->spacetype==SPACE_IMAGE) {
364 sima= sa->spacedata.first;
372 #if 0 // XXX not used
373 static ScrArea *find_empty_image_area(bContext *C)
375 bScreen *sc= CTX_wm_screen(C);
379 /* find an imagewindow showing render result */
380 for(sa=sc->areabase.first; sa; sa= sa->next) {
381 if(sa->spacetype==SPACE_IMAGE) {
382 sima= sa->spacedata.first;
389 #endif // XXX not used
391 static void render_error_reports(void *reports, char *str)
393 BKE_report(reports, RPT_ERROR, str);
396 /* executes blocking render */
397 static int screen_render_exec(bContext *C, wmOperator *op)
399 Scene *scene= CTX_data_scene(C);
400 Render *re= RE_NewRender(scene->id.name);
402 View3D *v3d= CTX_wm_view3d(C);
403 Main *mainp= CTX_data_main(C);
404 unsigned int lay= (v3d)? v3d->lay: scene->lay;
405 const short is_animation= RNA_boolean_get(op->ptr, "animation");
406 const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
408 if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.imtype)) {
409 BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected.");
410 return OPERATOR_CANCELLED;
414 re= RE_NewRender(scene->id.name);
418 RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
419 RE_error_cb(re, op->reports, render_error_reports);
421 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
422 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
423 BKE_image_backup_render(scene, ima);
425 /* cleanup sequencer caches before starting user triggered render.
426 otherwise, invalidated cache entries can make their way into
427 the output rendering. We can't put that into RE_BlenderFrame,
428 since sequence rendering can call that recursively... (peter) */
429 seq_stripelem_cache_cleanup();
432 RE_BlenderAnim(re, mainp, scene, lay, scene->r.sfra, scene->r.efra, scene->r.frame_step, op->reports);
434 RE_BlenderFrame(re, mainp, scene, NULL, lay, scene->r.cfra, is_write_still);
436 // no redraw needed, we leave state as we entered it
437 ED_update_for_newframe(mainp, scene, CTX_wm_screen(C), 1);
439 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
441 return OPERATOR_FINISHED;
444 typedef struct RenderJob {
449 SceneRenderLayer *srl;
451 short anim, write_still;
460 static void render_freejob(void *rjv)
467 /* str is IMA_MAX_RENDER_TEXT in size */
468 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
470 char info_time_str[32]; // used to be extern to header_info.c
471 uintptr_t mem_in_use, mmap_in_use, peak_memory;
472 float megs_used_memory, mmap_used_memory, megs_peak_memory;
475 mem_in_use= MEM_get_memory_in_use();
476 mmap_in_use= MEM_get_mapped_memory_in_use();
477 peak_memory = MEM_get_peak_memory();
479 megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
480 mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
481 megs_peak_memory = (peak_memory)/(1024.0*1024.0);
483 if(scene->lay & 0xFF000000)
484 spos+= sprintf(spos, "Localview | ");
485 else if(scene->r.scemode & R_SINGLE_LAYER)
486 spos+= sprintf(spos, "Single Layer | ");
489 spos+= sprintf(spos, "%s ", rs->statstr);
492 spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface);
493 if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
494 if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
495 spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM, peak %.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory, megs_peak_memory);
498 spos+= sprintf(spos, "Field %d ", rs->curfield);
500 spos+= sprintf(spos, "Blur %d ", rs->curblur);
503 BLI_timestr(rs->lastframetime, info_time_str);
504 spos+= sprintf(spos, "Time:%s ", info_time_str);
506 if(rs->infostr && rs->infostr[0])
507 spos+= sprintf(spos, "| %s ", rs->infostr);
509 /* very weak... but 512 characters is quite safe */
510 if(spos >= str+IMA_MAX_RENDER_TEXT)
512 printf("WARNING! renderwin text beyond limit \n");
516 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
521 rr= RE_AcquireResultRead(rj->re);
524 /* malloc OK here, stats_draw is not in tile threads */
526 rr->text= MEM_callocN(IMA_MAX_RENDER_TEXT, "rendertext");
528 make_renderinfo_string(rs, rj->scene, rr->text);
531 RE_ReleaseResult(rj->re);
533 /* make jobs timer to send notifier */
538 static void render_progress_update(void *rjv, float progress)
543 *rj->progress = progress;
546 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
549 Image *ima= rj->image;
553 /* only update if we are displaying the slot being rendered */
554 if(ima->render_slot != ima->last_render_slot)
557 ibuf= BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
559 image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
561 /* make jobs timer to send notifier */
564 BKE_image_release_ibuf(ima, lock);
567 static void render_startjob(void *rjv, short *stop, short *do_update, float *progress)
572 rj->do_update= do_update;
573 rj->progress= progress;
576 RE_BlenderAnim(rj->re, rj->main, rj->scene, rj->lay, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step, rj->reports);
578 RE_BlenderFrame(rj->re, rj->main, rj->scene, rj->srl, rj->lay, rj->scene->r.cfra, rj->write_still);
581 static void render_endjob(void *rjv)
585 /* this render may be used again by the sequencer without the active 'Render' where the callbacks
586 * would be re-assigned. assign dummy callbacks to avoid referencing freed renderjobs bug [#24508] */
587 RE_InitRenderCB(rj->re);
589 if(rj->main != G.main)
592 /* else the frame will not update for the original value */
593 ED_update_for_newframe(G.main, rj->scene, rj->win->screen, 1);
595 /* XXX render stability hack */
597 WM_main_add_notifier(NC_WINDOW, NULL);
600 /* called by render, check job 'stop' value or the global */
601 static int render_breakjob(void *rjv)
607 if(rj->stop && *(rj->stop))
613 static int screen_render_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
615 /* no running blender, remove handler and pass through */
616 if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C))) {
617 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
621 switch (event->type) {
623 return OPERATOR_RUNNING_MODAL;
626 return OPERATOR_PASS_THROUGH;
629 /* using context, starts job */
630 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
632 /* new render clears all callbacks */
634 Scene *scene= CTX_data_scene(C);
635 SceneRenderLayer *srl=NULL;
636 bScreen *screen= CTX_wm_screen(C);
637 View3D *v3d= CTX_wm_view3d(C);
642 const short is_animation= RNA_boolean_get(op->ptr, "animation");
643 const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
645 /* only one render job at a time */
646 if(WM_jobs_test(CTX_wm_manager(C), scene))
647 return OPERATOR_CANCELLED;
649 if(!RE_is_rendering_allowed(scene, op->reports, render_error_reports)) {
650 return OPERATOR_CANCELLED;
653 if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.imtype)) {
654 BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected.");
655 return OPERATOR_CANCELLED;
658 /* stop all running jobs, currently previews frustrate Render */
659 WM_jobs_stop_all(CTX_wm_manager(C));
663 /* thread-safety experiment, copy main from the undo buffer */
664 mainp= BKE_undo_get_main(&scene);
667 mainp= CTX_data_main(C);
669 /* cancel animation playback */
670 if (screen->animtimer)
671 ED_screen_animation_play(C, 0, 0);
673 /* handle UI stuff */
676 /* flush multires changes (for sculpt) */
677 multires_force_render_update(CTX_data_active_object(C));
679 /* cleanup sequencer caches before starting user triggered render.
680 otherwise, invalidated cache entries can make their way into
681 the output rendering. We can't put that into RE_BlenderFrame,
682 since sequence rendering can call that recursively... (peter) */
683 seq_stripelem_cache_cleanup();
685 /* get editmode results */
686 ED_object_exit_editmode(C, 0); /* 0 = does not exit editmode */
689 // get view3d layer, local layer, make this nice api call to render
692 /* ensure at least 1 area shows result */
693 screen_set_image_output(C, event->x, event->y);
695 /* single layer re-render */
696 if(RNA_property_is_set(op->ptr, "layer")) {
697 SceneRenderLayer *rl;
699 char scene_name[19], rl_name[RE_MAXNAME];
701 RNA_string_get(op->ptr, "layer", rl_name);
702 RNA_string_get(op->ptr, "scene", scene_name);
704 scn = (Scene *)BLI_findstring(&mainp->scene, scene_name, offsetof(ID, name) + 2);
705 rl = (SceneRenderLayer *)BLI_findstring(&scene->r.layers, rl_name, offsetof(SceneRenderLayer, name));
713 /* job custom data */
714 rj= MEM_callocN(sizeof(RenderJob), "render job");
717 rj->win= CTX_wm_window(C);
719 rj->lay = (v3d)? v3d->lay: scene->lay;
720 rj->anim= is_animation;
721 rj->write_still= is_write_still;
722 rj->iuser.scene= scene;
724 rj->reports= op->reports;
727 steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Render", WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS);
728 WM_jobs_customdata(steve, rj, render_freejob);
729 WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
730 WM_jobs_callbacks(steve, render_startjob, NULL, NULL, render_endjob);
732 /* get a render result image, and make sure it is empty */
733 ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
734 BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
735 BKE_image_backup_render(rj->scene, ima);
738 /* setup new render */
739 re= RE_NewRender(scene->id.name);
740 RE_test_break_cb(re, rj, render_breakjob);
741 RE_display_draw_cb(re, rj, image_rect_update);
742 RE_stats_draw_cb(re, rj, image_renderinfo_cb);
743 RE_progress_cb(re, rj, render_progress_update);
748 RE_error_cb(re, op->reports, render_error_reports);
750 WM_jobs_start(CTX_wm_manager(C), steve);
753 WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
755 /* we set G.rendering here already instead of only in the job, this ensure
756 main loop or other scene updates are disabled in time, since they may
757 have started before the job thread */
760 /* add modal handler for ESC */
761 WM_event_add_modal_handler(C, op);
763 return OPERATOR_RUNNING_MODAL;
767 /* contextual render, using current scene, view3d? */
768 void RENDER_OT_render(wmOperatorType *ot)
772 ot->description= "Render active scene";
773 ot->idname= "RENDER_OT_render";
776 ot->invoke= screen_render_invoke;
777 ot->modal= screen_render_modal;
778 ot->exec= screen_render_exec;
780 ot->poll= ED_operator_screenactive;
782 RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
783 RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)");
784 RNA_def_string(ot->srna, "layer", "", RE_MAXNAME, "Render Layer", "Single render layer to re-render");
785 RNA_def_string(ot->srna, "scene", "", 19, "Scene", "Re-render single layer in this scene");
788 /* ****************************** opengl render *************************** */
791 /* *********************** cancel render viewer *************** */
793 static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
795 wmWindow *win= CTX_wm_window(C);
796 ScrArea *sa= CTX_wm_area(C);
797 SpaceImage *sima= sa->spacedata.first;
799 /* test if we have a temp screen in front */
800 if(CTX_wm_window(C)->screen->temp) {
801 wm_window_lower(CTX_wm_window(C));
802 return OPERATOR_FINISHED;
804 /* determine if render already shows */
805 else if(sima->flag & SI_PREVSPACE) {
806 sima->flag &= ~SI_PREVSPACE;
808 if(sima->flag & SI_FULLWINDOW) {
809 sima->flag &= ~SI_FULLWINDOW;
810 ED_screen_full_prevspace(C, sa);
813 ED_area_prevspace(C, sa);
815 return OPERATOR_FINISHED;
817 else if(sima->flag & SI_FULLWINDOW) {
818 sima->flag &= ~SI_FULLWINDOW;
819 ED_screen_full_toggle(C, win, sa);
820 return OPERATOR_FINISHED;
823 return OPERATOR_PASS_THROUGH;
826 void RENDER_OT_view_cancel(struct wmOperatorType *ot)
829 ot->name= "Cancel Render View";
830 ot->description= "Cancel show render view";
831 ot->idname= "RENDER_OT_view_cancel";
834 ot->exec= render_view_cancel_exec;
835 ot->poll= ED_operator_image_active;
838 /* *********************** show render viewer *************** */
840 static int render_view_show_invoke(bContext *C, wmOperator *UNUSED(unused), wmEvent *event)
842 ScrArea *sa= find_area_showing_r_result(C);
844 /* test if we have a temp screen in front */
845 if(CTX_wm_window(C)->screen->temp) {
846 wm_window_lower(CTX_wm_window(C));
848 /* determine if render already shows */
850 SpaceImage *sima= sa->spacedata.first;
852 if(sima->flag & SI_PREVSPACE) {
853 sima->flag &= ~SI_PREVSPACE;
855 if(sima->flag & SI_FULLWINDOW) {
856 sima->flag &= ~SI_FULLWINDOW;
857 ED_screen_full_prevspace(C, sa);
859 else if(sima->next) {
860 /* workaround for case of double prevspace, render window
861 with a file browser on top of it (same as in ED_area_prevspace) */
862 if(sima->next->spacetype == SPACE_FILE && sima->next->next)
863 ED_area_newspace(C, sa, sima->next->next->spacetype);
865 ED_area_newspace(C, sa, sima->next->spacetype);
866 ED_area_tag_redraw(sa);
871 screen_set_image_output(C, event->x, event->y);
874 return OPERATOR_FINISHED;
877 void RENDER_OT_view_show(struct wmOperatorType *ot)
880 ot->name= "Show/Hide Render View";
881 ot->description= "Toggle show render view";
882 ot->idname= "RENDER_OT_view_show";
885 ot->invoke= render_view_show_invoke;
886 ot->poll= ED_operator_screenactive;