/* * 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 = minimum 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 * subtracted 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->m_adjecentOnly) { // if "adjacent only" inner edge mode is turned on if (this->m_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->m_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->m_inputInnerMask = NULL; this->m_inputOuterMask = NULL; this->m_adjecentOnly = false; this->m_keepInside = false; this->setComplex(true); } bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) { if (this->m_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->m_inputInnerMask = this->getInputSocketReader(0); this->m_inputOuterMask = this->getInputSocketReader(1); initMutex(); this->m_cachedInstance = NULL; } void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) { if (this->m_cachedInstance) return this->m_cachedInstance; lockMutex(); if (this->m_cachedInstance == NULL) { MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect); MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect); float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), __func__); float *imask = innerMask->getBuffer(); float *omask = outerMask->getBuffer(); doDoubleEdgeMask(imask, omask, data); this->m_cachedInstance = data; } unlockMutex(); return this->m_cachedInstance; } void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data) { float *buffer = (float *)data; int index = (y * this->getWidth() + x); output[0] = buffer[index]; } void DoubleEdgeMaskOperation::deinitExecution() { this->m_inputInnerMask = NULL; this->m_inputOuterMask = NULL; deinitMutex(); if (this->m_cachedInstance) { MEM_freeN(this->m_cachedInstance); this->m_cachedInstance = NULL; } }