(no commit message)
authorPeter Larabell <xgl.asyliax@gmail.com>
Tue, 10 Jan 2012 19:08:08 +0000 (19:08 +0000)
committerPeter Larabell <xgl.asyliax@gmail.com>
Tue, 10 Jan 2012 19:08:08 +0000 (19:08 +0000)
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/editors/space_node/drawnode.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_nodetree_types.h
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c [new file with mode: 0644]

index 8e3dc896cd41b011cde6772aad0d2bbacb9af067..8229978454feba74194d1b52ae3ad5bf460b9e92 100644 (file)
@@ -642,6 +642,7 @@ void                        ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat);
 #define CMP_NODE_STABILIZE2D   263
 #define CMP_NODE_TRANSFORM     264
 #define CMP_NODE_MOVIEDISTORTION       265
+#define CMP_NODE_DOUBLEEDGEMASK    266
 
 #define CMP_NODE_GLARE         301
 #define CMP_NODE_TONEMAP       302
index d273d3ff63cb504aea2e0136083817584bd834d0..7a4b19915c3d4dce8d7e520d5b442bb96d2ac7b8 100644 (file)
@@ -1839,6 +1839,7 @@ static void registerCompositNodes(bNodeTreeType *ttype)
        register_node_type_cmp_vecblur(ttype);
        register_node_type_cmp_dilateerode(ttype);
        register_node_type_cmp_defocus(ttype);
+       register_node_type_cmp_doubleedgemask(ttype);
        
        register_node_type_cmp_valtorgb(ttype);
        register_node_type_cmp_rgbtobw(ttype);
index bd5292e5154fb011320485a1b4eaceabf9871b43..6c0cc15559cdcfa43702ad38989c87a76025737c 100644 (file)
@@ -1472,6 +1472,18 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
        uiItemR(col, ptr, "factor", 0, NULL, ICON_NONE);
 }
 
+static void node_composit_buts_double_edge_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+       uiLayout *col;
+
+       col= uiLayoutColumn(layout, 0);
+
+       uiItemL(col, "Inner Edge:", ICON_NONE);
+       uiItemR(col, ptr, "inner_mode", 0, "", ICON_NONE);
+       uiItemL(col, "Buffer Edge:", ICON_NONE);
+       uiItemR(col, ptr, "edge_mode", 0, "", ICON_NONE);
+}
+
 static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
 {
        uiLayout *sub, *col;
@@ -1926,6 +1938,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
                case CMP_NODE_ID_MASK:
                        ntype->uifunc= node_composit_buts_id_mask;
                        break;
+               case CMP_NODE_DOUBLEEDGEMASK:
+                       ntype->uifunc= node_composit_buts_double_edge_mask;
+                       break;
                case CMP_NODE_MATH:
                        ntype->uifunc= node_buts_math;
                        break;
index 9e708a0a5d7a2dffd6bb15b46e8c8793b1c21a9e..4497f4573bd9a5c1ad70a6e79112d5ed76f78cb3 100644 (file)
@@ -136,6 +136,7 @@ extern StructRNA RNA_CompositorNodeGamma;
 extern StructRNA RNA_CompositorNodeGlare;
 extern StructRNA RNA_CompositorNodeHueSat;
 extern StructRNA RNA_CompositorNodeIDMask;
+extern StructRNA RNA_CompositorNodeDoubleEdgeMask;
 extern StructRNA RNA_CompositorNodeImage;
 extern StructRNA RNA_CompositorNodeInvert;
 extern StructRNA RNA_CompositorNodeLensdist;
index 4b1dc3e9fa0c0f26d382343edf793c9a6af2dcfa..05b33877f10f76fdc1b8bad4f79347aca6e47f76 100644 (file)
@@ -2125,6 +2125,35 @@ static void def_cmp_id_mask(StructRNA *srna)
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 }
 
+static void def_cmp_double_edge_mask(StructRNA * srna)
+{
+       PropertyRNA *prop;
+
+       static EnumPropertyItem BufEdgeMode_items[] = {
+               {0, "BLEED_OUT",  0, "Bleed Out",     "Allow mask pixels to bleed along edges"},
+               {1, "KEEP_IN",  0, "Keep In",     "Restrict mask pixels from touching edges"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       static EnumPropertyItem InnerEdgeMode_items[] = {
+               {0, "ALL", 0, "All", "All pixels on inner mask edge are considered during mask calculation"},
+               {1, "ADJACENT_ONLY", 0, "Adjacent Only", "Only inner mask pixels adjacent to outer mask pixels are considered during mask calculation"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       prop = RNA_def_property(srna, "inner_mode", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "custom2");
+       RNA_def_property_enum_items(prop, InnerEdgeMode_items);
+       RNA_def_property_ui_text(prop, "Inner Edge Mode", "");
+       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+       prop = RNA_def_property(srna, "edge_mode", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "custom1");
+       RNA_def_property_enum_items(prop, BufEdgeMode_items);
+       RNA_def_property_ui_text(prop, "Buffer Edge Mode", "");
+       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+}
+
 static void def_cmp_map_uv(StructRNA *srna)
 {
        PropertyRNA *prop;
@@ -3375,4 +3404,3 @@ void RNA_def_nodetree(BlenderRNA *brna)
 #undef NODE_DEFINE_SUBTYPES
 
 #endif
-
index e63e13f89b2279814775954b292d1db510f697d8..5ab907c87dc0afd32d693037cb7e6fbc271ac68b 100644 (file)
@@ -130,6 +130,7 @@ DefNode( CompositorNode, CMP_NODE_FLIP,           def_cmp_flip,           "FLIP"
 DefNode( CompositorNode, CMP_NODE_SPLITVIEWER,    def_cmp_splitviewer,    "SPLITVIEWER",    SplitViewer,      "Split Viewer",      ""              )
 DefNode( CompositorNode, CMP_NODE_MAP_UV,         def_cmp_map_uv,         "MAP_UV",         MapUV,            "Map UV",            ""              )
 DefNode( CompositorNode, CMP_NODE_ID_MASK,        def_cmp_id_mask,        "ID_MASK",        IDMask,           "ID Mask",           ""              )
+DefNode( CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask",   ""              )
 DefNode( CompositorNode, CMP_NODE_DEFOCUS,        def_cmp_defocus,        "DEFOCUS",        Defocus,          "Defocus",           ""              )
 DefNode( CompositorNode, CMP_NODE_DISPLACE,       0,                      "DISPLACE",       Displace,         "Displace",          ""              )
 DefNode( CompositorNode, CMP_NODE_COMBHSVA,       0,                      "COMBHSVA",       CombHSVA,         "Combine HSVA",      ""              )
index 21493cd67503be40de305083a8833cf408b6fa37..6d5c282d308d61ad64cd4131a0bb45c2338bb160 100644 (file)
@@ -59,6 +59,7 @@ set(SRC
        composite/nodes/node_composite_composite.c
        composite/nodes/node_composite_crop.c
        composite/nodes/node_composite_curves.c
+       composite/nodes/node_composite_doubleEdgeMask.c
        composite/nodes/node_composite_defocus.c
        composite/nodes/node_composite_diffMatte.c
        composite/nodes/node_composite_dilate.c
index 97a5afec7dabcf524fa7a3d35caffef780736ecb..1cc9390b8beaffa0a3bceffc83e698cd4c9a3ebc 100644 (file)
@@ -80,6 +80,7 @@ void register_node_type_cmp_bilateralblur(struct bNodeTreeType *ttype);
 void register_node_type_cmp_vecblur(struct bNodeTreeType *ttype);
 void register_node_type_cmp_dilateerode(struct bNodeTreeType *ttype);
 void register_node_type_cmp_defocus(struct bNodeTreeType *ttype);
+void register_node_type_cmp_doubleedgemask(struct bNodeTreeType *ttype);
 
 void register_node_type_cmp_valtorgb(struct bNodeTreeType *ttype);
 void register_node_type_cmp_rgbtobw(struct bNodeTreeType *ttype);
diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c
new file mode 100644 (file)
index 0000000..cc1a32f
--- /dev/null
@@ -0,0 +1,1183 @@
+/*
+ * $Id: node_composite_doubleEdgeMask.c 35237 2012-01-01 03:06:22Z xgl.asyliax $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2011 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Peter Larabell.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c
+ *  \ingroup cmpnodes
+ */
+#include "node_composite_util.h"
+/* **************** DblEdgMatte  ******************** */
+
+
+static bNodeSocketTemplate cmp_node_doubleedgemask_in[]= {
+    { SOCK_FLOAT, 1, "Inner Mask", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f, PROP_NONE},  // inner mask socket definition
+    { SOCK_FLOAT, 1, "Outer Mask", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f, PROP_NONE},  // outer mask socket definition
+    { -1, 0, ""        }                                                                   // input socket array terminator
+};
+static bNodeSocketTemplate cmp_node_doubleedgemask_out[]= {
+    { SOCK_FLOAT, 0, "Mask"},          // output socket definition
+    { -1, 0, "" }                      // output socket array terminator
+};
+
+static void do_adjacentKeepBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize){
+    int x;
+    unsigned int isz=0; // inner edge size
+    unsigned int osz=0; // outer edge size
+    unsigned int gsz=0; // gradient fill area size
+    /* Test the four corners */
+    /* upper left corner */
+    x=t-rw+1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel underneath, or to the right, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+1] && lomask[x+1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* upper right corner */
+    x=t;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel underneath, or to the left, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x-rw] && lomask[x-rw]) || (!limask[x-1] && lomask[x-1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* lower left corner */
+    x=0;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel above, or to the right, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x+rw] && lomask[x+rw]) || (!limask[x+1] && lomask[x+1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* lower right corner */
+    x=rw-1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel above, or to the left, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x+rw] && lomask[x+rw]) || (!limask[x-1] && lomask[x-1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+
+    /* Test the TOP row of pixels in buffer, except corners */
+    for(x= t-1; x>=(t-rw)+2; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel to the right, or to the left, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    /* Test the BOTTOM row of pixels in buffer, except corners */
+    for(x= rw-2; x; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel to the right, or to the left, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+    /* Test the LEFT edge of pixels in buffer, except corners */
+    for(x= t-(rw<<1)+1; x>=rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel underneath, or above, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    /* Test the RIGHT edge of pixels in buffer, except corners */
+    for(x= t-rw; x>rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel underneath, or above, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    rsize[0]=isz;  // fill in our return sizes for edges + fill
+    rsize[1]=osz;
+    rsize[2]=gsz;
+}
+
+static void do_adjacentBleedBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize){
+    int x;
+    unsigned int isz=0; // inner edge size
+    unsigned int osz=0; // outer edge size
+    unsigned int gsz=0; // gradient fill area size
+    /* Test the four corners */
+    /* upper left corner */
+    x=t-rw+1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel underneath, or to the right, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+1] && lomask[x+1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x-rw] || !lomask[x+1]) {      // test if outer mask is empty underneath or to the right
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* upper right corner */
+    x=t;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel underneath, or to the left, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x-rw] && lomask[x-rw]) || (!limask[x-1] && lomask[x-1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x-rw] || !lomask[x-1]) {      // test if outer mask is empty underneath or to the left
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* lower left corner */
+    x=0;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel above, or to the right, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x+rw] && lomask[x+rw]) || (!limask[x+1] && lomask[x+1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x+rw] || !lomask[x+1]) {      // test if outer mask is empty above or to the right
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* lower right corner */
+    x=rw-1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if pixel above, or to the left, are empty in the inner mask,
+        // but filled in the outer mask
+        if((!limask[x+rw] && lomask[x+rw]) || (!limask[x-1] && lomask[x-1])){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x+rw] || !lomask[x-1]) {      // test if outer mask is empty above or to the left
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* Test the TOP row of pixels in buffer, except corners */
+    for(x= t-1; x>=(t-rw)+2; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel to the left, or to the right, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-1] || !lomask[x+1]) {   // test if outer mask is empty to the left or to the right
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    /* Test the BOTTOM row of pixels in buffer, except corners */
+    for(x= rw-2; x; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel to the left, or to the right, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-1] && lomask[x-1]) || (!limask[x+1] && lomask[x+1])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-1] || !lomask[x+1]) {   // test if outer mask is empty to the left or to the right
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+    /* Test the LEFT edge of pixels in buffer, except corners */
+    for(x= t-(rw<<1)+1; x>=rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel underneath, or above, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    /* Test the RIGHT edge of pixels in buffer, except corners */
+    for(x= t-rw; x>rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if pixel underneath, or above, are empty in the inner mask,
+            // but filled in the outer mask
+            if((!limask[x-rw] && lomask[x-rw]) || (!limask[x+rw] && lomask[x+rw])) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    rsize[0]=isz;  // fill in our return sizes for edges + fill
+    rsize[1]=osz;
+    rsize[2]=gsz;
+}
+
+static void do_allKeepBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize){
+    int x;
+    unsigned int isz=0; // inner edge size
+    unsigned int osz=0; // outer edge size
+    unsigned int gsz=0; // gradient fill area size
+    /* Test the four corners */
+    /* upper left corner */
+    x=t-rw+1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if the inner mask is empty underneath or to the right
+        if(!limask[x-rw] || !limask[x+1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* upper right corner */
+    x=t;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if the inner mask is empty underneath or to the left
+        if(!limask[x-rw] || !limask[x-1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* lower left corner */
+    x=0;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if inner mask is empty above or to the right
+        if(!limask[x+rw] || !limask[x+1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+    /* lower right corner */
+    x=rw-1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if inner mask is empty above or to the left
+        if(!limask[x+rw] || !limask[x-1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        osz++;                                   // increment outer edge size
+        lres[x]=3;                               // flag pixel as outer edge
+    }
+
+    /* Test the TOP row of pixels in buffer, except corners */
+    for(x= t-1; x>=(t-rw)+2; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty to the left or to the right
+            if(!limask[x-1] || !limask[x+1]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    /* Test the BOTTOM row of pixels in buffer, except corners */
+    for(x= rw-2; x; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty to the left or to the right
+            if(!limask[x-1] || !limask[x+1]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+    /* Test the LEFT edge of pixels in buffer, except corners */
+    for(x= t-(rw<<1)+1; x>=rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty underneath or above
+            if(!limask[x-rw] || !limask[x+rw]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    /* Test the RIGHT edge of pixels in buffer, except corners */
+    for(x= t-rw; x>rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty underneath or above
+            if(!limask[x-rw] || !limask[x+rw]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        }
+    }
+
+    rsize[0]=isz;  // fill in our return sizes for edges + fill
+    rsize[1]=osz;
+    rsize[2]=gsz;
+}
+
+static void do_allBleedBorders(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize){
+    int x;
+    unsigned int isz=0; // inner edge size
+    unsigned int osz=0; // outer edge size
+    unsigned int gsz=0; // gradient fill area size
+    /* Test the four corners */
+    /* upper left corner */
+    x=t-rw+1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if the inner mask is empty underneath or to the right
+        if(!limask[x-rw] || !limask[x+1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x-rw] || !lomask[x+1]) {      // test if outer mask is empty underneath or to the right
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* upper right corner */
+    x=t;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if the inner mask is empty underneath or to the left
+        if(!limask[x-rw] || !limask[x-1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x-rw] || !lomask[x-1]) {      // test if outer mask is empty above or to the left
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* lower left corner */
+    x=0;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if inner mask is empty above or to the right
+        if(!limask[x+rw] || !limask[x+1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x+rw] || !lomask[x+1]) {      // test if outer mask is empty underneath or to the right
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* lower right corner */
+    x=rw-1;
+    // test if inner mask is filled
+    if(limask[x]){
+        // test if inner mask is empty above or to the left
+        if(!limask[x+rw] || !limask[x-1]){
+            isz++;                               // increment inner edge size
+            lres[x]=4;                           // flag pixel as inner edge
+        } else {
+            res[x]=1.0f;                         // pixel is just part of inner mask, and it's not an edge
+        }
+    } else if(lomask[x]){                        // inner mask was empty, test if outer mask is filled
+        if(!lomask[x+rw] || !lomask[x-1]) {      // test if outer mask is empty underneath or to the left
+            osz++;                               // increment outer edge size
+            lres[x]=3;                           // flag pixel as outer edge
+        } else {
+            gsz++;                               // increment the gradient pixel count
+            lres[x]=2;                           // flag pixel as gradient
+        }
+    }
+    /* Test the TOP row of pixels in buffer, except corners */
+    for(x= t-1; x>=(t-rw)+2; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty to the left or to the right
+            if(!limask[x-1] || !limask[x+1]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-1] || !lomask[x+1]) {   // test if outer mask is empty to the left or to the right
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    /* Test the BOTTOM row of pixels in buffer, except corners */
+    for(x= rw-2; x; x--) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty to the left or to the right
+            if(!limask[x-1] || !limask[x+1]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-1] || !lomask[x+1]) {   // test if outer mask is empty to the left or to the right
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+    /* Test the LEFT edge of pixels in buffer, except corners */
+    for(x= t-(rw<<1)+1; x>=rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty underneath or above
+            if(!limask[x-rw] || !limask[x+rw]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    /* Test the RIGHT edge of pixels in buffer, except corners */
+    for(x= t-rw; x>rw; x-=rw) {
+        // test if inner mask is filled
+        if(limask[x]) {
+            // test if inner mask is empty underneath or above
+            if(!limask[x-rw] || !limask[x+rw]) {
+                isz++;                           // increment inner edge size
+                lres[x]=4;                       // flag pixel as inner edge
+            } else {
+                res[x]=1.0f;                     // pixel is just part of inner mask, and it's not an edge
+            }
+        } else if(lomask[x]) {                   // inner mask was empty, test if outer mask is filled
+            if(!lomask[x-rw] || !lomask[x+rw]) { // test if outer mask is empty underneath or above
+                osz++;                           // increment outer edge size
+                lres[x]=3;                       // flag pixel as outer edge
+            } else {
+                gsz++;                           // increment the gradient pixel count
+                lres[x]=2;                       // flag pixel as gradient
+            }
+        }
+    }
+
+    rsize[0]=isz;  // fill in our return sizes for edges + fill
+    rsize[1]=osz;
+    rsize[2]=gsz;
+}
+
+static void do_allEdgeDetection(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz){
+    int x;                             // x = pixel loop counter
+    int a;                             // a = pixel loop counter
+    int dx;                            // dx = delta x
+    int pix_prevRow;                   // pix_prevRow = pixel one row behind the one we are testing in a loop
+    int pix_nextRow;                   // pix_nextRow = pixel one row in front of the one we are testing in a loop
+    int pix_prevCol;                   // pix_prevCol = pixel one column behind the one we are testing in a loop
+    int pix_nextCol;                   // pix_nextCol = pixel one column in front of the one we are testing in a loop
+    /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
+    for(x= (t-rw)+1, dx=x-(rw-2); dx>rw; x-=rw,dx-=rw) {
+        a=x-2;
+        pix_prevRow=a+rw;
+        pix_nextRow=a-rw;
+        pix_prevCol=a+1;
+        pix_nextCol=a-1;
+        while(a>dx-2) {
+            if(!limask[a]) {             // if the inner mask is empty
+                if(lomask[a]) {          // if the outer mask is full
+                    /*
+                      Next we test all 4 directions around the current pixel: next/prev/up/down
+                      The test ensures that the outer mask is empty and that the inner mask
+                      is also empty. If both conditions are true for any one of the 4 adjacent pixels
+                      then the current pixel is counted as being a true outer edge pixel.
+                     */
+                    if((!lomask[pix_nextCol] && !limask[pix_nextCol]) || \
+                       (!lomask[pix_prevCol] && !limask[pix_prevCol]) || \
+                       (!lomask[pix_nextRow] && !limask[pix_nextRow]) || \
+                       (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
+                        in_osz++;           // increment the outer boundary pixel count
+                        lres[a]=3;       // flag pixel as part of outer edge
+                    } else {             // it's not a boundary pixel, but it is a gradient pixel
+                        in_gsz++;           // increment the gradient pixel count
+                        lres[a]=2;       // flag pixel as gradient
+                    }
+                }
+
+            } else {
+                if(!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] || !limask[pix_prevRow]) {
+                    in_isz++;               // increment the inner boundary pixel count
+                    lres[a]=4;           // flag pixel as part of inner edge
+                } else {
+                    res[a]=1.0f;         // pixel is part of inner mask, but not at an edge
+                }
+            }
+            a--;
+            pix_prevRow--;
+            pix_nextRow--;
+            pix_prevCol--;
+            pix_nextCol--;
+        }
+    }
+
+    rsize[0]=in_isz;  // fill in our return sizes for edges + fill
+    rsize[1]=in_osz;
+    rsize[2]=in_gsz;
+}
+
+static void do_adjacentEdgeDetection(unsigned int t, unsigned int rw, unsigned int *limask, unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, unsigned int in_isz, unsigned int in_osz, unsigned int in_gsz){
+    int x;                             // x = pixel loop counter
+    int a;                             // a = pixel loop counter
+    int dx;                            // dx = delta x
+    int pix_prevRow;                   // pix_prevRow = pixel one row behind the one we are testing in a loop
+    int pix_nextRow;                   // pix_nextRow = pixel one row in front of the one we are testing in a loop
+    int pix_prevCol;                   // pix_prevCol = pixel one column behind the one we are testing in a loop
+    int pix_nextCol;                   // pix_nextCol = pixel one column in front of the one we are testing in a loop
+    /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
+    for(x= (t-rw)+1, dx=x-(rw-2); dx>rw; x-=rw,dx-=rw) {
+        a=x-2;
+        pix_prevRow=a+rw;
+        pix_nextRow=a-rw;
+        pix_prevCol=a+1;
+        pix_nextCol=a-1;
+        while(a>dx-2) {
+            if(!limask[a]) {             // if the inner mask is empty
+                if(lomask[a]) {          // if the outer mask is full
+                    /*
+                      Next we test all 4 directions around the current pixel: next/prev/up/down
+                      The test ensures that the outer mask is empty and that the inner mask
+                      is also empty. If both conditions are true for any one of the 4 adjacent pixels
+                      then the current pixel is counted as being a true outer edge pixel.
+                     */
+                    if((!lomask[pix_nextCol] && !limask[pix_nextCol]) || \
+                       (!lomask[pix_prevCol] && !limask[pix_prevCol]) || \
+                       (!lomask[pix_nextRow] && !limask[pix_nextRow]) || \
+                       (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
+                        in_osz++;           // increment the outer boundary pixel count
+                        lres[a]=3;       // flag pixel as part of outer edge
+                    } else {             // it's not a boundary pixel, but it is a gradient pixel
+                        in_gsz++;           // increment the gradient pixel count
+                        lres[a]=2;       // flag pixel as gradient
+                    }
+                }
+
+            } else {
+                if((!limask[pix_nextCol] && lomask[pix_nextCol]) || \
+                   (!limask[pix_prevCol] && lomask[pix_prevCol]) || \
+                   (!limask[pix_nextRow] && lomask[pix_nextRow]) || \
+                   (!limask[pix_prevRow] && lomask[pix_prevRow])) {
+                    in_isz++;               // increment the inner boundary pixel count
+                    lres[a]=4;           // flag pixel as part of inner edge
+                } else {
+                    res[a]=1.0f;         // pixel is part of inner mask, but not at an edge
+                }
+            }
+            a--;
+            pix_prevRow--;              // advance all four "surrounding" pixel pointers
+            pix_nextRow--;
+            pix_prevCol--;
+            pix_nextCol--;
+        }
+    }
+
+    rsize[0]=in_isz;  // fill in our return sizes for edges + fill
+    rsize[1]=in_osz;
+    rsize[2]=in_gsz;
+}
+
+static void do_createEdgeLocationBuffer(unsigned int t, unsigned int rw, unsigned int *lres, float *res, unsigned short *gbuf, unsigned int *innerEdgeOffset, unsigned int *outerEdgeOffset, unsigned int isz, unsigned int gsz){
+    int x;                             // x = pixel loop counter
+    int a;                             // a = temporary pixel index buffer loop counter
+    unsigned int ud;                   // ud = unscaled edge distance
+    unsigned int dmin;                 // dmin = minimun edge distance
+
+    unsigned int rsl;                  // long used for finding fast 1.0/sqrt
+    unsigned int gradientFillOffset;
+    unsigned int innerAccum=0;         // for looping inner edge pixel indexes, represents current position from offset
+    unsigned int outerAccum=0;         // for looping outer edge pixel indexes, represents current position from offset
+    unsigned int gradientAccum=0;      // for looping gradient pixel indexes, represents current position from offset
+    /*
+    Here we compute the size of buffer needed to hold (row,col) coordinates
+    for each pixel previously determined to be either gradient, inner edge,
+    or outer edge.
+
+    Allocation is done by requesting 4 bytes "sizeof(int)" per pixel, even
+    though gbuf[] is declared as unsigned short* (2 bytes) because we don't
+    store the pixel indexes, we only store x,y location of pixel in buffer.
+
+    This does make the assumption that x and y can fit in 16 unsigned bits
+    so if Blender starts doing renders greater than 65536 in either direction
+    this will need to allocate gbuf[] as unsigned int* and allocate 8 bytes
+    per flagged pixel.
+
+    In general, the buffer on-screen:
+
+    Example:  9 by 9 pixel block
+
+    . = pixel non-white in both outer and inner mask
+    o = pixel white in outer, but not inner mask, adjacent to "." pixel
+    g = pixel white in outer, but not inner mask, not adjacent to "." pixel
+    i = pixel white in inner mask, adjacent to "g" or "." pixel
+    F = pixel white in inner mask, only adjacent to other pixels white in the inner mask
+
+
+                     .........   <----- pixel #80
+                     ..oooo...
+                     .oggggo..
+                     .oggiggo.
+                     .ogiFigo.
+                     .oggiggo.
+                     .oggggo..
+                     ..oooo...
+    pixel #00 -----> .........
+
+    gsz = 18   (18 "g" pixels above)
+    isz = 4    (4 "i" pixels above)
+    osz = 18   (18 "o" pixels above)
+
+
+    The memory in gbuf[] after filling will look like this:
+
+     gradientFillOffset (0 pixels)                   innerEdgeOffset (18 pixels)    outerEdgeOffset (22 pixels)
+    /                                               /                              /
+    /                                               /                              /
+    |X   Y   X   Y   X   Y   X   Y   >     <X   Y   X   Y   >     <X   Y   X   Y   X   Y   >     <X   Y   X   Y   | <- (x,y)
+    +-------------------------------->     <---------------->     <------------------------>     <----------------+
+    |0   2   4   6   8   10  12  14  > ... <68  70  72  74  > ... <80  82  84  86  88  90  > ... <152 154 156 158 | <- bytes
+    +-------------------------------->     <---------------->     <------------------------>     <----------------+
+    |g0  g0  g1  g1  g2  g2  g3  g3  >     <g17 g17 i0  i0  >     <i2  i2  i3  i3  o0  o0  >     <o16 o16 o17 o17 | <- pixel
+                                                 /                              /                              /
+                                                /                              /                              /
+                                               /                              /                              /
+      +---------- gradientAccum (18) ---------+      +--- innerAccum (22) ---+      +--- outerAccum (40) ---+
+
+
+    Ultimately we do need the pixel's memory buffer index to set the output
+    pixel color, but it's faster to reconstruct the memory buffer location
+    each iteration of the final gradient calculation than it is to deconstruct
+    a memory location into x,y pairs each round.
+*/
+
+
+    gradientFillOffset=0;                              // since there are likely "more" of these, put it first. :)
+    *innerEdgeOffset=gradientFillOffset+gsz;           // set start of inner edge indexes
+    *outerEdgeOffset=(*innerEdgeOffset)+isz;           // set start of outer edge indexes
+    /* set the accumulators to correct positions */    // set up some accumulator variables for loops
+    gradientAccum = gradientFillOffset;                // each accumulator variable starts at its respective
+    innerAccum = *innerEdgeOffset;                     // section's offset so when we start filling, each
+    outerAccum = *outerEdgeOffset;                     // section fills up it's allocated space in gbuf
+    //uses dmin=row, rsl=col
+    for(x=0,dmin=0; x<t; x+=rw,dmin++) {
+        for(rsl=0; rsl<rw; rsl++) {
+            a=x+rsl;
+            if(lres[a]==2) {           // it is a gradient pixel flagged by 2
+                ud=gradientAccum<<1;   // double the index to reach correct unsigned short location
+                gbuf[ud]=dmin;         // insert pixel's row into gradient pixel location buffer
+                gbuf[ud+1]=rsl;        // insert pixel's column into gradient pixel location buffer
+                gradientAccum++;       // increment gradient index buffer pointer
+            } else if(lres[a]==3) {    // it is an outer edge pixel flagged by 3
+                ud=outerAccum<<1;      // double the index to reach correct unsigned short location
+                gbuf[ud]=dmin;         // insert pixel's row into outer edge pixel location buffer
+                gbuf[ud+1]=rsl;        // insert pixel's column into outer edge pixel location buffer
+                outerAccum++;          // increment outer edge index buffer pointer
+                res[a]=0.0f;           // set output pixel intensity now since it won't change later
+            } else if(lres[a]==4) {    // it is an inner edge pixel flagged by 4
+                ud=innerAccum<<1;      // double int index to reach correct unsigned short location
+                gbuf[ud]=dmin;         // insert pixel's row into inner edge pixel location buffer
+                gbuf[ud+1]=rsl;        // insert pixel's column into inner edge pixel location buffer
+                innerAccum++;          // increment inner edge index buffer pointer
+                res[a]=1.0f;           // set output pixel intensity now since it won't change later
+            }
+        }
+    }
+
+}
+
+static void do_fillGradientBuffer(unsigned int rw, float *res, unsigned short *gbuf, unsigned int isz, unsigned int osz, unsigned int gsz, unsigned int innerEdgeOffset, unsigned int outerEdgeOffset){
+    int x;                             // x = pixel loop counter
+    int a;                             // a = temporary pixel index buffer loop counter
+    int fsz;                           // size of the frame
+    unsigned int rsl;                  // long used for finding fast 1.0/sqrt
+    float rsf;                         // float used for finding fast 1.0/sqrt
+    const float rsopf = 1.5f;          // constant float used for finding fast 1.0/sqrt
+
+    unsigned int gradientFillOffset;
+    unsigned int t;
+    unsigned int ud;                   // ud = unscaled edge distance
+    unsigned int dmin;                 // dmin = minimun edge distance
+    float odist;                       // odist = current outer edge distance
+    float idist;                       // idist = current inner edge distance
+    int dx;                            // dx = X-delta (used for distance proportion calculation)
+    int dy;                            // dy = Y-delta (used for distance proportion calculation)
+
+    /*
+     The general algorithm used to color each gradient pixel is:
+
+     1.) Loop through all gradient pixels.
+        A.) For each gradient pixel:
+            a.) Loop though all outside edge pixels, looking for closest one
+                to the gradient pixel we are in.
+            b.) Loop through all inside edge pixels, looking for closest one
+                to the gradient pixel we are in.
+            c.) Find proportion of distance from gradient pixel to inside edge
+                pixel compared to sum of distance to inside edge and distance to
+                outside edge.
+
+                In an image where:
+                . = blank (black) pixels, not covered by inner mask or outer mask
+                + = desired gradient pixels, covered only by outer mask
+                * = white full mask pixels, covered by at least inner mask
+
+                ...............................
+                ...............+++++++++++.....
+                ...+O++++++..++++++++++++++....
+                ..+++\++++++++++++++++++++.....
+                .+++++G+++++++++*******+++.....
+                .+++++|+++++++*********+++.....
+                .++***I****************+++.....
+                .++*******************+++......
+                .+++*****************+++.......
+                ..+++***************+++........
+                ....+++**********+++...........
+                ......++++++++++++.............
+                ...............................
+
+                    O = outside edge pixel
+                     \
+                      G = gradient pixel
+                      |
+                      I = inside edge pixel
+
+                                 __
+                      *note that IO does not need to be a straight line, in fact
+                       many cases can arise where straight lines do not work
+                       correctly.
+
+                                            __       __     __
+            d.) Pixel color is assigned as |GO| / ( |GI| + |GO| )
+
+     The implementation does not compute distance, but the reciprocal of the
+     distance. This is done to avoid having to compute a square root, as a
+     reciprocal square root can be computed faster. Therefore, the code computes
+     pixel color as |GI| / (|GI| + |GO|). Since these are reciprocals, GI serves the
+     purpose of GO for the proportion calculation.
+
+     For the purposes of the minimun distance comparisons, we only check
+     the sums-of-squares against eachother, since they are in the same
+     mathematical sort-order as if we did go ahead and take square roots
+
+     Loop through all gradient pixels.
+     */
+
+    for(x= gsz-1; x>=0; x--) {
+        gradientFillOffset=x<<1;
+        t=gbuf[gradientFillOffset];         // calculate column of pixel indexed by gbuf[x]
+        fsz=gbuf[gradientFillOffset+1];     // calculate row of pixel indexed by gbuf[x]
+        dmin=0xffffffff;                    // reset min distance to edge pixel
+        for(a=outerEdgeOffset+osz-1; a>=outerEdgeOffset; a--) {   // loop through all outer edge buffer pixels
+            ud=a<<1;
+            dy=t-gbuf[ud];                  // set dx to gradient pixel column - outer edge pixel row
+            dx=fsz-gbuf[ud+1];              // set dy to gradient pixel row - outer edge pixel column
+            ud=dx*dx+dy*dy;                 // compute sum of squares
+            if(ud<dmin) {                   // if our new sum of squares is less than the current minimum
+                dmin=ud;                    // set a new minimum equal to the new lower value
+            }
+        }
+        odist=(float)(dmin);                     // cast outer min to a float
+        rsf=odist*0.5f;                          //
+        rsl=*(unsigned int*)&odist;              // use some peculiar properties of the way bits are stored
+        rsl=0x5f3759df-(rsl>>1);                 // in floats vs. unsigned ints to compute an approximate
+        odist=*(float*)&rsl;                     // reciprocal square root
+        odist=odist*(rsopf-(rsf*odist*odist));   // -- ** this line can be iterated for more accuracy ** --
+        dmin=0xffffffff;                         // reset min distance to edge pixel
+        for(a= innerEdgeOffset+isz-1; a>=innerEdgeOffset; a--) {   // loop through all inside edge pixels
+            ud=a<<1;
+            dy=t-gbuf[ud];         // compute delta in Y from gradient pixel to inside edge pixel
+            dx=fsz-gbuf[ud+1];     // compute delta in X from gradient pixel to inside edge pixel
+            ud=dx*dx+dy*dy;        // compute sum of squares
+            if(ud<dmin) {          // if our new sum of squares is less than the current minimum we've found
+                dmin=ud;           // set a new minimum equal to the new lower value
+            }
+        }
+        idist=(float)(dmin);                     // cast inner min to a float
+        rsf=idist*0.5f;                          //
+        rsl=*(unsigned int*)&idist;              //
+        rsl=0x5f3759df-(rsl>>1);                 // see notes above
+        idist=*(float*)&rsl;                     //
+        idist=idist*(rsopf-(rsf*idist*idist));   //
+        /*
+         Note once again that since we are using reciprocals of distance values our
+         proportion is already the correct intensity, and does not need to be
+         subracted from 1.0 like it would have if we used real distances.
+         */
+
+        /*
+         Here we reconstruct the pixel's memory location in the CompBuf by
+         Pixel Index = Pixel Column + ( Pixel Row * Row Width )
+         */
+        res[gbuf[gradientFillOffset+1]+(gbuf[gradientFillOffset]*rw)]=(idist/(idist+odist));   //set intensity
+    }
+
+}
+
+
+static void node_composit_exec_doubleedgemask(void *UNUSED(data), bNode *node, bNodeStack **in, bNodeStack **out) {
+
+    float *imask;                      // imask = pointer to inner mask pixel buffer
+    float *omask;                      // omask = pointer to outer mask pixel buffer
+    float *res;                        // res = pointer to output mask
+
+    unsigned int *lres;                // lres = unsigned int pointer to output pixel buffer (for bit operations)
+    unsigned int *limask;              // limask = unsigned int pointer to inner mask (for bit operations)
+    unsigned int *lomask;              // lomask = unsigned int pointer to outer mask (for bit operations)
+
+    int rw;                            // rw = pixel row width
+    int t;                             // t = total number of pixels in buffer - 1 (used for loop starts)
+    int fsz;                           // size of the frame
+
+    unsigned int isz=0;                // size (in pixels) of inside edge pixel index buffer
+    unsigned int osz=0;                // size (in pixels) of outside edge pixel index buffer
+    unsigned int gsz=0;                // size (in pixels) of gradient pixel index buffer
+    unsigned int rsize[3];             // size storage to pass to helper functions
+    unsigned int innerEdgeOffset=0;    // offset into final buffer where inner edge pixel indexes start
+    unsigned int outerEdgeOffset=0;    // offset into final buffer where outer edge pixel indexes start
+
+    unsigned short *gbuf;              // gradient/inner/outer pixel location index buffer
+
+    CompBuf *cbuf;                     // pointer, will be set to inner mask data
+    CompBuf *dbuf;                     // pointer, will be set to outer mask data
+    CompBuf *stackbuf;                 // pointer, will get allocated as output buffer
+
+    if(out[0]->hasoutput==0) {         // if the node's output socket is not connected to anything...
+        return;                        //     do not execute any further, just exit the node immediately
+    }
+
+    if(in[0]->data && in[1]->data) {                    // if both input sockets have some data coming in...
+        cbuf= in[0]->data;                              //     get a pointer to the inner mask data
+        dbuf= in[1]->data;                              //     get a pointer to the outer mask data
+        if(cbuf->type!=CB_VAL || dbuf->type!=CB_VAL) {  // if either input socket has an incorrect data type coming in
+            return;                                     //     exit the node immediately
+        }
+
+        t=(cbuf->x*cbuf->y)-1;                                // determine size of the frame
+
+        stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); // allocate the output buffer
+
+        imask= cbuf->rect;             // set the inner mask
+        omask= dbuf->rect;             // set the outer mask
+        res= stackbuf->rect;           // set output pointer
+        lres= (unsigned int*)res;      // unsigned int pointer to output buffer (for bit level ops)
+        limask=(unsigned int*)imask;   // unsigned int pointer to input mask (for bit level ops)
+        lomask=(unsigned int*)omask;   // unsigned int pointer to output mask (for bit level ops)
+        rw= cbuf->x;                   // width of a row of pixels
+
+
+        /*
+         The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the
+         LEFT and RIGHT edges (excluding the corner pixels), and all OTHER rows.
+         This allows for quick computation of outer edge pixels where
+         a screen edge pixel is marked to be gradient.
+
+         The pixel type (gradient vs inner-edge vs outer-edge) tests change
+         depending on the user selected "Inner Edge Mode" and the user selected
+         "Buffer Edge Mode" on the node's GUI. There are 4 sets of basically the
+         same algorithm:
+
+         1.) Inner Edge -> Adjacent Only
+             Buffer Edge -> Keep Inside
+
+         2.) Inner Edge -> Adjacent Only
+             Buffer Edge -> Bleed Out
+
+         3.) Inner Edge -> All
+             Buffer Edge -> Keep Inside
+
+         4.) Inner Edge -> All
+             Buffer Edge -> Bleed Out
+
+         Each version has slightly different criteria for detecting an edge pixel.
+         */
+        if(node->custom2) {            // if "adjacent only" inner edge mode is turned on
+            if(node->custom1) {        // if "keep inside" buffer edge mode is turned on
+                do_adjacentKeepBorders(t,rw,limask,lomask,lres,res,rsize);
+            }else{                     // "bleed out" buffer edge mode is turned on
+                do_adjacentBleedBorders(t,rw,limask,lomask,lres,res,rsize);
+            }
+            isz=rsize[0];              // set up inner edge, outer edge, and gradient buffer sizes after border pass
+            osz=rsize[1];
+            gsz=rsize[2];
+            // detect edges in all non-border pixels in the buffer
+            do_adjacentEdgeDetection(t,rw,limask,lomask,lres,res,rsize,isz,osz,gsz);
+        }else{                         // "all" inner edge mode is turned on
+            if(node->custom1) {        // if "keep inside" buffer edge mode is turned on
+                do_allKeepBorders(t,rw,limask,lomask,lres,res,rsize);
+            }else{                     // "bleed out" buffer edge mode is turned on
+                do_allBleedBorders(t,rw,limask,lomask,lres,res,rsize);
+            }
+            isz=rsize[0];              // set up inner edge, outer edge, and gradient buffer sizes after border pass
+            osz=rsize[1];
+            gsz=rsize[2];
+            // detect edges in all non-border pixels in the buffer
+            do_allEdgeDetection(t,rw,limask,lomask,lres,res,rsize,isz,osz,gsz);
+        }
+
+        isz=rsize[0];                  // set edge and gradient buffer sizes once again...
+        osz=rsize[1];                  // the sizes in rsize[] have been modified (always increased)
+        gsz=rsize[2];                  // by the do_*EdgeDetection() function.
+
+        // quick check for existance of edges in the buffer...
+        // if we don't have any one of the three sizes, the other two make no difference visually,
+        // so we can just pass the inner input buffer back as output.
+        if(!gsz || !isz || !osz) {
+            out[0]->data= stackbuf;    // point the node output buffer to our filled buffer
+            return;
+        }
+
+
+        fsz=gsz+isz+osz;                                   // calculate size of pixel index buffer needed
+        gbuf= MEM_mallocN(fsz*sizeof(int), "grd buf");     // allocate edge/gradient pixel index buffer
+
+        do_createEdgeLocationBuffer(t,rw,lres,res,gbuf,&innerEdgeOffset,&outerEdgeOffset,isz,gsz);
+        do_fillGradientBuffer(rw,res,gbuf,isz,osz,gsz,innerEdgeOffset,outerEdgeOffset);
+
+        MEM_freeN(gbuf);          // free the gradient index buffer
+        out[0]->data= stackbuf;   // point the node output buffer to our filled buffer
+    }
+}
+
+void register_node_type_cmp_doubleedgemask(bNodeTreeType *ttype) {
+    static bNodeType ntype;      // allocate a node type data structure
+
+    node_type_base(ttype, &ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_OP_FILTER, NODE_OPTIONS);
+    node_type_socket_templates(&ntype, cmp_node_doubleedgemask_in, cmp_node_doubleedgemask_out);
+    node_type_size(&ntype, 210, 210, 210);
+    node_type_exec(&ntype, node_composit_exec_doubleedgemask);
+
+    nodeRegisterType(ttype, &ntype);
+}