This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc

1386 lines
55 KiB
C++

/*
* 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.
*
* Copyright 2011, Blender Foundation.
*/
#include <cstdlib>
#include "BLI_math.h"
#include "COM_DoubleEdgeMaskOperation.h"
#include "DNA_node_types.h"
#include "MEM_guardedalloc.h"
namespace blender::compositor {
// this part has been copied from the double edge mask
static void do_adjacentKeepBorders(unsigned int t,
unsigned int rw,
const unsigned int *limask,
const 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,
const unsigned int *limask,
const 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,
const unsigned int *limask,
const 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,
const unsigned int *limask,
const 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,
const unsigned int *limask,
const 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,
const unsigned int *limask,
const 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,
const 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;
/* For looping inner edge pixel indexes, represents current position from offset. */
unsigned int innerAccum = 0;
/* For looping outer edge pixel indexes, represents current position from offset. */
unsigned int outerAccum = 0;
/* For looping gradient pixel indexes, represents current position from offset. */
unsigned int gradientAccum = 0;
/* Disable clang-format to prevent line-wrapping. */
/* clang-format off */
/*
* 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.
*/
/* clang-format on */
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 its 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,
const 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 = minimum 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 through 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 minimum distance comparisons, we only check
* the sums-of-squares against each other, 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
* 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...
rw = this->getWidth(); // width of a row of pixels
t = (rw * this->getHeight()) - 1; // determine size of the frame
memset(res,
0,
sizeof(float) * (t + 1)); // clear output buffer (not all pixels will be written later)
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)
/*
* 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_adjacentOnly) { // 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);
}
// set up inner edge, outer edge, and gradient buffer sizes after border pass
isz = rsize[0];
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);
}
// set up inner edge, outer edge, and gradient buffer sizes after border pass
isz = rsize[0];
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);
}
// set edge and gradient buffer sizes once again...
// the sizes in rsize[] may have been modified
// by the do_*EdgeDetection() function.
isz = rsize[0];
osz = rsize[1];
gsz = rsize[2];
// calculate size of pixel index buffer needed
fsz = gsz + isz + osz;
// allocate edge/gradient pixel index buffer
gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM");
do_createEdgeLocationBuffer(
t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz);
do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset);
// free the gradient index buffer
MEM_freeN(gbuf);
}
}
DoubleEdgeMaskOperation::DoubleEdgeMaskOperation()
{
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
this->m_inputInnerMask = nullptr;
this->m_inputOuterMask = nullptr;
this->m_adjacentOnly = false;
this->m_keepInside = false;
this->flags.complex = true;
}
bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
ReadBufferOperation *readOperation,
rcti *output)
{
if (this->m_cachedInstance == nullptr) {
rcti newInput;
newInput.xmax = this->getWidth();
newInput.xmin = 0;
newInput.ymax = this->getHeight();
newInput.ymin = 0;
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
return false;
}
void DoubleEdgeMaskOperation::initExecution()
{
this->m_inputInnerMask = this->getInputSocketReader(0);
this->m_inputOuterMask = this->getInputSocketReader(1);
initMutex();
this->m_cachedInstance = nullptr;
}
void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect)
{
if (this->m_cachedInstance) {
return this->m_cachedInstance;
}
lockMutex();
if (this->m_cachedInstance == nullptr) {
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 = nullptr;
this->m_inputOuterMask = nullptr;
deinitMutex();
if (this->m_cachedInstance) {
MEM_freeN(this->m_cachedInstance);
this->m_cachedInstance = nullptr;
}
}
} // namespace blender::compositor