Border for compositor viewer node feature
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 7 Mar 2013 17:47:30 +0000 (17:47 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 7 Mar 2013 17:47:30 +0000 (17:47 +0000)
This adds border option to compositor, which affects on
a backdrop and viewer nodes, which is useful for faster
previews and tweaks.

Final compositing still happens for the whole frame, but
if it'll be needed it's not so difficult to support it
as well.

To use border there's Ctrl-B shortcut in the compositor
editor, which i used to define region you want to restrict
compositing to. There's also "Viewer Border" option in
the N-panel in case you'll want to disable border
compositing.

Some areas could be cleaned a bit, like ideally it shall
not be viewer image clearing in viewer_border_update RNA
callback, but currently it's not so much clear how to
make it the same fast as simple memset and glue it
somehow to compositor. Will think of nicer solution a
bit later.

release/scripts/startup/bl_ui/space_node.py
source/blender/compositor/intern/COM_ExecutionGroup.cpp
source/blender/compositor/intern/COM_ExecutionGroup.h
source/blender/compositor/intern/COM_ExecutionSystem.cpp
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_ops.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/windowmanager/intern/wm_operators.c

index 104c15007568cc8c52ae7d61c2e176fbd9b29d23..1865b049a03c70965d8260f3ab39df2cc7bd1c73 100644 (file)
@@ -254,6 +254,7 @@ class NODE_PT_quality(bpy.types.Panel):
         col.prop(tree, "use_opencl")
         col.prop(tree, "use_groupnode_buffer")
         col.prop(tree, "two_pass")
+        col.prop(tree, "use_viewer_border")
         col.prop(snode, "show_highlight")
         col.prop(snode, "use_hidden_preview")
 
index ffc36281874712584646fa89f731b4e1b7ea72c6..0868fccceaa2e35c35d5cd377fbda41407f60fe2 100644 (file)
@@ -60,6 +60,7 @@ ExecutionGroup::ExecutionGroup()
        this->m_openCL = false;
        this->m_singleThreaded = false;
        this->m_chunksFinished = 0;
+       BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
 }
 
 CompositorPriority ExecutionGroup::getRenderPriotrity()
@@ -196,6 +197,7 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2])
        resolution[0] = operation->getWidth();
        resolution[1] = operation->getHeight();
        this->setResolution(resolution);
+       BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
 }
 
 void ExecutionGroup::determineNumberOfChunks()
@@ -207,8 +209,10 @@ void ExecutionGroup::determineNumberOfChunks()
        }
        else {
                const float chunkSizef = this->m_chunkSize;
-               this->m_numberOfXChunks = ceil(this->m_width / chunkSizef);
-               this->m_numberOfYChunks = ceil(this->m_height / chunkSizef);
+               const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
+               const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
+               this->m_numberOfXChunks = ceil(border_width / chunkSizef);
+               this->m_numberOfYChunks = ceil(border_height / chunkSizef);
                this->m_numberOfChunks = this->m_numberOfXChunks * this->m_numberOfYChunks;
        }
 }
@@ -245,6 +249,9 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
                chunkorder = viewer->getChunkOrder();
        }
 
+       const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
+       const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
+
        switch (chunkorder) {
                case COM_TO_RANDOM:
                        for (index = 0; index < 2 * this->m_numberOfChunks; index++) {
@@ -258,14 +265,14 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
                case COM_TO_CENTER_OUT:
                {
                        ChunkOrderHotspot *hotspots[1];
-                       hotspots[0] = new ChunkOrderHotspot(this->m_width * centerX, this->m_height * centerY, 0.0f);
+                       hotspots[0] = new ChunkOrderHotspot(border_width * centerX, border_height * centerY, 0.0f);
                        rcti rect;
                        ChunkOrder *chunkOrders = (ChunkOrder *)MEM_mallocN(sizeof(ChunkOrder) * this->m_numberOfChunks, __func__);
                        for (index = 0; index < this->m_numberOfChunks; index++) {
                                determineChunkRect(&rect, index);
                                chunkOrders[index].setChunkNumber(index);
-                               chunkOrders[index].setX(rect.xmin);
-                               chunkOrders[index].setY(rect.ymin);
+                               chunkOrders[index].setX(rect.xmin - this->m_viewerBorder.xmin);
+                               chunkOrders[index].setY(rect.ymin - this->m_viewerBorder.ymin);
                                chunkOrders[index].determineDistance(hotspots, 1);
                        }
 
@@ -281,10 +288,10 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
                case COM_TO_RULE_OF_THIRDS:
                {
                        ChunkOrderHotspot *hotspots[9];
-                       unsigned int tx = this->m_width / 6;
-                       unsigned int ty = this->m_height / 6;
-                       unsigned int mx = this->m_width / 2;
-                       unsigned int my = this->m_height / 2;
+                       unsigned int tx = border_width / 6;
+                       unsigned int ty = border_height / 6;
+                       unsigned int mx = border_width / 2;
+                       unsigned int my = border_height / 2;
                        unsigned int bx = mx + 2 * tx;
                        unsigned int by = my + 2 * ty;
 
@@ -303,8 +310,8 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
                        for (index = 0; index < this->m_numberOfChunks; index++) {
                                determineChunkRect(&rect, index);
                                chunkOrders[index].setChunkNumber(index);
-                               chunkOrders[index].setX(rect.xmin);
-                               chunkOrders[index].setY(rect.ymin);
+                               chunkOrders[index].setX(rect.xmin - this->m_viewerBorder.xmin);
+                               chunkOrders[index].setY(rect.ymin - this->m_viewerBorder.ymin);
                                chunkOrders[index].determineDistance(hotspots, 9);
                        }
 
@@ -431,13 +438,18 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
 
 inline void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const
 {
+       const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
+       const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
+
        if (this->m_singleThreaded) {
-               BLI_rcti_init(rect, 0, this->m_width, 0, this->m_height);
+               BLI_rcti_init(rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
        }
        else {
-               const unsigned int minx = xChunk * this->m_chunkSize;
-               const unsigned int miny = yChunk * this->m_chunkSize;
-               BLI_rcti_init(rect, minx, min(minx + this->m_chunkSize, this->m_width), miny, min(miny + this->m_chunkSize, this->m_height));
+               const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
+               const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
+               const unsigned int width = min((unsigned int) this->m_viewerBorder.xmax, this->m_width);
+               const unsigned int height = min((unsigned int) this->m_viewerBorder.ymax, this->m_height);
+               BLI_rcti_init(rect, min(minx, this->m_width), min(minx + this->m_chunkSize, width), min(miny, this->m_height), min(miny + this->m_chunkSize, height));
        }
 }
 
@@ -472,9 +484,9 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
        float chunkSizef = this->m_chunkSize;
 
        int indexx, indexy;
-       int minxchunk = floor(area->xmin / chunkSizef);
+       int minxchunk = floor((area->xmin - this->m_viewerBorder.xmin) / chunkSizef);
        int maxxchunk = ceil((area->xmax - 1) / chunkSizef);
-       int minychunk = floor(area->ymin / chunkSizef);
+       int minychunk = floor((area->ymin - this->m_viewerBorder.ymin) / chunkSizef);
        int maxychunk = ceil((area->ymax - 1) / chunkSizef);
        minxchunk = max(minxchunk, 0);
        minychunk = max(minychunk, 0);
@@ -574,3 +586,13 @@ bool ExecutionGroup::isOpenCL()
 {
        return this->m_openCL;
 }
+
+void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
+{
+       NodeOperation *operation = this->getOutputNodeOperation();
+
+       if (operation->isViewerOperation()) {
+               BLI_rcti_init(&this->m_viewerBorder, xmin * this->m_width, xmax * this->m_width,
+                             ymin * this->m_height, ymax * this->m_height);
+       }
+}
index 00104c24194e6b5e9cc2db0555b5da9490facafd..52f5bae4be9a60fb55060dc3c390516af116303d 100644 (file)
@@ -161,6 +161,12 @@ private:
         * @see openCL
         */
        bool m_initialized;
+
+       /**
+        * @brief denotes boundary for border compositing
+        * @note measured in pixel space
+        */
+       rcti m_viewerBorder;
        
        // methods
        /**
@@ -395,6 +401,12 @@ public:
         */
        CompositorPriority getRenderPriotrity();
 
+       /**
+        * @brief set border for viewer operation
+        * @note all the coordinates are assumed to be in normalized space
+        */
+       void setViewerBorder(float xmin, float xmax, float ymin, float ymax);
+
 #ifdef WITH_CXX_GUARDEDALLOC
        MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionGroup")
 #endif
index 1ec4ac7699bd0d493b78314d871fde30324b225a..2b2af73d0c85682c2301539f5e897948b47823ed 100644 (file)
@@ -78,11 +78,22 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, bNodeTree *editingtree, bool re
        this->groupOperations(); /* group operations in ExecutionGroups */
        unsigned int index;
        unsigned int resolution[2];
+
+       rctf *viewer_border = &editingtree->viewer_border;
+       bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) &&
+                                viewer_border->xmin < viewer_border->xmax &&
+                                viewer_border->ymin < viewer_border->ymax;
+
        for (index = 0; index < this->m_groups.size(); index++) {
                resolution[0] = 0;
                resolution[1] = 0;
                ExecutionGroup *executionGroup = this->m_groups[index];
                executionGroup->determineResolution(resolution);
+
+               if (use_viewer_border) {
+                       executionGroup->setViewerBorder(viewer_border->xmin, viewer_border->xmax,
+                                                       viewer_border->ymin, viewer_border->ymax);
+               }
        }
 
 #ifdef COM_DEBUG
index 8a2e03f266031a8d0631c8ac8101d4447d397eb6..097469f396f9950ae45762aed619797a4a171432 100644 (file)
@@ -3320,6 +3320,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode)
                        /** @note draw selected info on backdrop */
                        if (snode->edittree) {
                                bNode *node = snode->edittree->nodes.first;
+                               rctf *viewer_border = &snode->edittree->viewer_border;
                                while (node) {
                                        if (node->flag & NODE_SELECT) {
                                                if (node->typeinfo->uibackdropfunc) {
@@ -3328,6 +3329,23 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode)
                                        }
                                        node = node->next;
                                }
+
+                               if ((snode->edittree->flag & NTREE_VIEWER_BORDER) &&
+                                       viewer_border->xmin < viewer_border->xmax &&
+                                   viewer_border->ymin < viewer_border->ymax)
+                               {
+                                       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                                       setlinestyle(3);
+                                       cpack(0x4040FF);
+
+                                       glRectf(x + snode->zoom * viewer_border->xmin * ibuf->x,
+                                                       y + snode->zoom * viewer_border->ymin * ibuf->y,
+                                               x + snode->zoom * viewer_border->xmax * ibuf->x,
+                                                       y + snode->zoom * viewer_border->ymax * ibuf->y);
+
+                                       setlinestyle(0);
+                                       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                               }
                        }
                        
                        glMatrixMode(GL_PROJECTION);
index fb4e4f62e52823867e2a3f9ceda1f34012e81883..ad863fca5a522da6308bd7dabb89ebd64fbd7342 100644 (file)
@@ -73,6 +73,8 @@
 
 #include "GPU_material.h"
 
+#include "IMB_imbuf_types.h"
+
 #include "node_intern.h"  /* own include */
 
 #define USE_ESC_COMPO
@@ -2281,3 +2283,105 @@ void NODE_OT_shader_script_update(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/* ********************** Viewer border ******************/
+
+static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *ar, int x, int y,
+                                             int backdrop_width, int backdrop_height,
+                                             float *fx, float *fy)
+{
+       float bufx, bufy;
+
+       bufx = backdrop_width * snode->zoom;
+       bufy = backdrop_height * snode->zoom;
+
+       *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
+       *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+}
+
+static int viewer_border_exec(bContext *C, wmOperator *op)
+{
+       Image *ima;
+       void *lock;
+       ImBuf *ibuf;
+
+       ED_preview_kill_jobs(C);
+
+       ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
+       ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+
+       if (ibuf) {
+               ARegion *ar = CTX_wm_region(C);
+               SpaceNode *snode = CTX_wm_space_node(C);
+               bNodeTree *btree = snode->edittree;
+               rcti rect;
+               rctf rectf;
+
+               /* get border from operator */
+               WM_operator_properties_border_to_rcti(op, &rect);
+
+               /* convert border to unified space within backdrop image */
+               viewer_border_corner_to_backdrop(snode, ar, rect.xmin, rect.ymin, ibuf->x, ibuf->y,
+                                                &rectf.xmin, &rectf.ymin);
+
+               viewer_border_corner_to_backdrop(snode, ar, rect.xmax, rect.ymax, ibuf->x, ibuf->y,
+                                                &rectf.xmax, &rectf.ymax);
+
+               /* clamp coordinates */
+               rectf.xmin = max_ff(rectf.xmin, 0.0f);
+               rectf.ymin = max_ff(rectf.ymin, 0.0f);
+               rectf.xmax = min_ff(rectf.xmax, 1.0f);
+               rectf.ymax = min_ff(rectf.ymax, 1.0f);
+
+               if (rectf.xmin < rectf.xmax && rectf.ymin < rectf.ymax) {
+                       btree->viewer_border = rectf;
+
+                       if (rectf.xmin == 0.0f && rectf.ymin == 0.0f &&
+                           rectf.xmax == 1.0f && rectf.ymax == 1.0f)
+                       {
+                               btree->flag &= ~NTREE_VIEWER_BORDER;
+                       }
+                       else {
+                               if (ibuf->rect)
+                                       memset(ibuf->rect, 0, 4 * ibuf->x * ibuf->y);
+
+                               if (ibuf->rect_float)
+                                       memset(ibuf->rect_float, 0, 4 * ibuf->x * ibuf->y * sizeof(float));
+
+                               ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+
+                               btree->flag |= NTREE_VIEWER_BORDER;
+                       }
+
+                       snode_notify(C, snode);
+                       WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+               }
+               else {
+                       btree->flag &= ~NTREE_VIEWER_BORDER;
+               }
+       }
+
+       BKE_image_release_ibuf(ima, ibuf, lock);
+
+       return OPERATOR_FINISHED;
+}
+
+void NODE_OT_viewer_border(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Viewer Border";
+       ot->description = "Set the boundaries for viewer operations";
+       ot->idname = "NODE_OT_viewer_border";
+
+       /* api callbacks */
+       ot->invoke = WM_border_select_invoke;
+       ot->exec = viewer_border_exec;
+       ot->modal = WM_border_select_modal;
+       ot->cancel = WM_border_select_cancel;
+       ot->poll = composite_node_active;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       WM_operator_properties_gesture_border(ot, TRUE);
+}
index e8dd1cf1528c2228d0ad85abfe4aa60953e0e4d5..cbf7101a10183de96fc7c2539d9bcf2f8e47b41d 100644 (file)
@@ -217,6 +217,8 @@ void NODE_OT_clipboard_paste(struct wmOperatorType *ot);
 
 void NODE_OT_shader_script_update(struct wmOperatorType *ot);
 
+void NODE_OT_viewer_border(struct wmOperatorType *ot);
+
 extern const char *node_context_dir[];
 
 // XXXXXX
index 8adccd9e6c4a2d789a7a868c8a73e03a072076e4..d16c6627d3f7cf05011358cf702437c1f16d7d6a 100644 (file)
@@ -120,6 +120,8 @@ void node_operatortypes(void)
        WM_operatortype_append(NODE_OT_clipboard_paste);
        
        WM_operatortype_append(NODE_OT_shader_script_update);
+
+       WM_operatortype_append(NODE_OT_viewer_border);
 }
 
 void ED_operatormacros_node(void)
@@ -298,5 +300,7 @@ void node_keymap(struct wmKeyConfig *keyconf)
        WM_keymap_add_item(keymap, "NODE_OT_clipboard_copy", CKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "NODE_OT_clipboard_paste", VKEY, KM_PRESS, KM_CTRL, 0);
        
+       WM_keymap_add_item(keymap, "NODE_OT_viewer_border", BKEY, KM_PRESS, KM_CTRL, 0);
+
        transform_keymap_for_space(keyconf, keymap, SPACE_NODE);
 }
index 62c997b72c678d40b9bd7f41af1edeb01ae6ada3..b2b23828ceedf5d4302eab184a66a8eaf367c553 100644 (file)
@@ -279,6 +279,8 @@ typedef struct bNodeTree {
        short render_quality;                           /* Quality setting when rendering */
        int chunksize;                                  /* tile size for compositor engine */
        
+       rctf viewer_border;
+       
        ListBase inputs, outputs;               /* external sockets for group nodes */
        
        /* execution data */
@@ -313,6 +315,7 @@ typedef struct bNodeTree {
 #define NTREE_COM_OPENCL                       2       /* use opencl */
 #define NTREE_TWO_PASS                         4       /* two pass */
 #define NTREE_COM_GROUPNODE_BUFFER     8       /* use groupnode buffers */
+#define NTREE_VIEWER_BORDER            16      /* use a border for viewer nodes */
 
 /* XXX not nice, but needed as a temporary flags
  * for group updates after library linking.
index c13481bcb9acbafe7121b4d4ecfa4ec9b7d473cd..6022929badc9bd051d4a4a0a67992eee8e8cad04 100644 (file)
@@ -55,6 +55,7 @@
 #include "rna_internal_types.h"
 
 #include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
 
 #include "WM_types.h"
 
@@ -521,6 +522,38 @@ static void rna_Node_material_update(Main *bmain, Scene *scene, PointerRNA *ptr)
        node_update(bmain, scene, ntree, node);
 }
 
+static void rna_NodeTree_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+       bNodeTree *ntree = (bNodeTree *)ptr->id.data;
+
+       /* when using border, make it so no old data from outside of
+        * border is hanging around
+        * ideally shouldn't be in RNA callback, but how to teach
+        * compo to only clear frame when border usage is actually
+        * toggling
+        */
+       if (ntree->flag & NTREE_VIEWER_BORDER) {
+               Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
+               void *lock;
+               ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+
+               if (ibuf) {
+                       if (ibuf->rect)
+                               memset(ibuf->rect, 0, 4 * ibuf->x * ibuf->y);
+
+                       if (ibuf->rect_float)
+                               memset(ibuf->rect_float, 0, 4 * ibuf->x * ibuf->y * sizeof(float));
+
+                       ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+               }
+
+               BKE_image_release_ibuf(ima, ibuf, lock);
+       }
+
+       WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
+       WM_main_add_notifier(NC_SCENE | ND_NODES, &ntree->id);
+}
+
 static void rna_NodeGroup_update(Main *bmain, Scene *scene, PointerRNA *ptr)
 {
        bNodeTree *ntree = (bNodeTree *)ptr->id.data;
@@ -4985,6 +5018,11 @@ static void rna_def_composite_nodetree(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", NTREE_TWO_PASS);
        RNA_def_property_ui_text(prop, "Two Pass", "Use two pass execution during editing: first calculate fast nodes, "
                                                   "second pass calculate all nodes");
+
+       prop = RNA_def_property(srna, "use_viewer_border", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", NTREE_VIEWER_BORDER);
+       RNA_def_property_ui_text(prop, "Viewer Border", "Use boundaries for viewer nodes and composite backdrop");
+       RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
 }
 
 static void rna_def_shader_nodetree(BlenderRNA *brna)
index 77dc467a4e304ba0f3d7437334159d53f103e657..374c31820469ecaf63b12bc4e55478ac270f7061 100644 (file)
@@ -4197,6 +4197,7 @@ static void gesture_border_modal_keymap(wmKeyConfig *keyconf)
        WM_modalkeymap_assign(keymap, "MARKER_OT_select_border");
        WM_modalkeymap_assign(keymap, "NLA_OT_select_border");
        WM_modalkeymap_assign(keymap, "NODE_OT_select_border");
+       WM_modalkeymap_assign(keymap, "NODE_OT_viewer_border");
        WM_modalkeymap_assign(keymap, "PAINT_OT_hide_show");
        WM_modalkeymap_assign(keymap, "OUTLINER_OT_select_border");
 //     WM_modalkeymap_assign(keymap, "SCREEN_OT_border_select"); // template