/* * Copyright 2011, Blender Foundation. * * 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. * * Contributor: * Jeroen Bakker * Monique Dewanchand */ #include "COM_DoubleEdgeMaskOperation.h" #include "BLI_math.h" #include "DNA_node_types.h" #include "MEM_guardedalloc.h" // this part has been copied from the double edge mask // Contributor(s): Peter Larabell. 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 > <----------------> <------------------------> <----------------+ |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 > = 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 } } // end of copy void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res) { 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 if (true) { // if both input sockets have some data coming in... t = (this->getWidth() * this->getHeight()) - 1; // determine size of the frame 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 = this->getWidth(); // 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 (this->adjecentOnly) { // if "adjacent only" inner edge mode is turned on if (this->keepInside) { // 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 (this->keepInside) { // 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[] may have been modified gsz = rsize[2]; // by the do_*EdgeDetection() function. fsz = gsz + isz + osz; // calculate size of pixel index buffer needed gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM"); // 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 } } DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() : NodeOperation() { this->addInputSocket(COM_DT_VALUE); this->addInputSocket(COM_DT_VALUE); this->addOutputSocket(COM_DT_VALUE); this->inputInnerMask = NULL; this->inputOuterMask = NULL; this->adjecentOnly = false; this->keepInside = false; this->setComplex(true); } bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) { if (this->cachedInstance == NULL) { rcti newInput; newInput.xmax = this->getWidth(); newInput.xmin = 0; newInput.ymax = this->getHeight(); newInput.ymin = 0; return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } else { return false; } } void DoubleEdgeMaskOperation::initExecution() { this->inputInnerMask = this->getInputSocketReader(0); this->inputOuterMask = this->getInputSocketReader(1); initMutex(); this->cachedInstance = NULL; } void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers) { if (this->cachedInstance) return this->cachedInstance; lockMutex(); if (this->cachedInstance == NULL) { MemoryBuffer *innerMask = (MemoryBuffer *)inputInnerMask->initializeTileData(rect, memoryBuffers); MemoryBuffer *outerMask = (MemoryBuffer *)inputOuterMask->initializeTileData(rect, memoryBuffers); float *data = new float[this->getWidth() * this->getHeight()]; float *imask = innerMask->convertToValueBuffer(); float *omask = outerMask->convertToValueBuffer(); doDoubleEdgeMask(imask, omask, data); delete imask; delete omask; this->cachedInstance = data; } unlockMutex(); return this->cachedInstance; } void DoubleEdgeMaskOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data) { float *buffer = (float *) data; int index = (y * this->getWidth() + x); color[0] = buffer[index]; color[1] = buffer[index + 1]; color[2] = buffer[index + 2]; color[3] = buffer[index + 3]; } void DoubleEdgeMaskOperation::deinitExecution() { this->inputInnerMask = NULL; this->inputOuterMask = NULL; deinitMutex(); if (this->cachedInstance) { delete cachedInstance; this->cachedInstance = NULL; } }